changeset 6657:768793150bcd

more PEA refactoring, multiple iterations
author Lukas Stadler <lukas.stadler@jku.at>
date Wed, 31 Oct 2012 18:21:19 +0100
parents 5bb9511a6764
children cc3fa26e7de7
files graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java
diffstat 17 files changed, 280 insertions(+), 130 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EscapeAnalysisTest.java	Wed Oct 31 18:21:19 2012 +0100
@@ -171,7 +171,7 @@
                 new InliningPhase(null, runtime(), null, null, null, getDefaultPhasePlan(), OptimisticOptimizations.ALL).apply(graph);
                 new DeadCodeEliminationPhase().apply(graph);
                 Debug.dump(graph, "Graph");
-                new PartialEscapeAnalysisPhase(null).apply(graph);
+                new PartialEscapeAnalysisPhase(null, runtime(), null).apply(graph);
                 new CullFrameStatesPhase().apply(graph);
                 new CanonicalizerPhase(null, runtime(), null).apply(graph);
                 Debug.dump(graph, "Graph");
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Wed Oct 31 18:21:19 2012 +0100
@@ -157,7 +157,7 @@
                 new DeadCodeEliminationPhase().apply(graph);
                 new CanonicalizerPhase(null, runtime(), null).apply(graph);
 //                TypeSystemTest.outputGraph(graph, "before EscapeAnalysis " + snippet);
-                new PartialEscapeAnalysisPhase(null).apply(graph);
+                new PartialEscapeAnalysisPhase(null, runtime(), null).apply(graph);
 //                TypeSystemTest.outputGraph(graph, "after EscapeAnalysis " + snippet);
                 new CullFrameStatesPhase().apply(graph);
                 new DeadCodeEliminationPhase().apply(graph);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Wed Oct 31 18:21:19 2012 +0100
@@ -156,7 +156,7 @@
         }
 
         if (GraalOptions.PartialEscapeAnalysis && !plan.isPhaseDisabled(PartialEscapeAnalysisPhase.class)) {
-            new PartialEscapeAnalysisPhase(runtime).apply(graph);
+            new PartialEscapeAnalysisPhase(target, runtime, assumptions).apply(graph);
         }
         if (GraalOptions.OptLoopTransform) {
             new LoopTransformHighPhase().apply(graph);
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed Oct 31 18:21:19 2012 +0100
@@ -410,11 +410,14 @@
         if (canGVN) {
             number = startGVNNumber;
             for (int i = 0; i < dataOffsets.length; ++i) {
-                Class<?> type = dataTypes[i];
+                Class< ? > type = dataTypes[i];
                 if (type.isPrimitive()) {
                     if (type == Integer.TYPE) {
                         int intValue = unsafe.getInt(n, dataOffsets[i]);
                         number += intValue;
+                    } else if (type == Long.TYPE) {
+                        long longValue = unsafe.getLong(n, dataOffsets[i]);
+                        number += longValue ^ (longValue >>> 32);
                     } else if (type == Boolean.TYPE) {
                         boolean booleanValue = unsafe.getBoolean(n, dataOffsets[i]);
                         if (booleanValue) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -121,7 +121,7 @@
                     }
 
                     @Override
-                    public VirtualObjectNode virtualObject(int virtualId) {
+                    public VirtualObjectNode virtualObject(long virtualId) {
                         return new VirtualArrayNode(virtualId, elementType, constantLength);
                     }
                 };
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -111,7 +111,7 @@
                 }
 
                 @Override
-                public VirtualObjectNode virtualObject(int virtualId) {
+                public VirtualObjectNode virtualObject(long virtualId) {
                     return new VirtualInstanceNode(virtualId, instanceClass(), fields);
                 }
             };
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeOp.java	Wed Oct 31 18:21:19 2012 +0100
@@ -32,6 +32,6 @@
      */
     public abstract ValueNode[] fieldState();
 
-    public abstract VirtualObjectNode virtualObject(int virtualId);
+    public abstract VirtualObjectNode virtualObject(long virtualId);
 
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -32,7 +32,7 @@
     private final ResolvedJavaType componentType;
     private final int length;
 
-    public VirtualArrayNode(int virtualId, ResolvedJavaType componentType, int length) {
+    public VirtualArrayNode(long virtualId, ResolvedJavaType componentType, int length) {
         super(virtualId);
         this.componentType = componentType;
         this.length = length;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -34,7 +34,7 @@
     private final ResolvedJavaField[] fields;
     private final HashMap<ResolvedJavaField, Integer> fieldMap = new HashMap<>();
 
-    public VirtualInstanceNode(int virtualId, ResolvedJavaType type, ResolvedJavaField[] fields) {
+    public VirtualInstanceNode(long virtualId, ResolvedJavaType type, ResolvedJavaField[] fields) {
         super(virtualId);
         this.type = type;
         this.fields = fields;
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -31,9 +31,9 @@
 @NodeInfo(nameTemplate = "VirtualObject {p#type}")
 public abstract class VirtualObjectNode extends FloatingNode implements LIRLowerable {
 
-    private final int virtualId;
+    private final long virtualId;
 
-    public VirtualObjectNode(int virtualId) {
+    public VirtualObjectNode(long virtualId) {
         super(StampFactory.virtual());
         this.virtualId = virtualId;
     }
@@ -42,7 +42,7 @@
 
     public abstract int entryCount();
 
-    public int virtualId() {
+    public long virtualId() {
         return virtualId;
     }
 
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Wed Oct 31 18:21:19 2012 +0100
@@ -69,9 +69,11 @@
 
     // escape analysis settings
     public static boolean PartialEscapeAnalysis              = true;
+    public static int     EscapeAnalysisIterations           = 2;
+    public static String  EscapeAnalyzeOnly                  = null;
 
-    public static double TailDuplicationProbability          = 0.5;
-    public static int    TailDuplicationTrivialSize          = 1;
+    public static double  TailDuplicationProbability         = 0.5;
+    public static int     TailDuplicationTrivialSize         = 1;
 
     // absolute probability analysis
     public static boolean ProbabilityAnalysis                = true;
@@ -129,12 +131,12 @@
     public static boolean PerThreadDebugValues               = ____;
     public static boolean SummarizeDebugValues               = ____;
     public static boolean SummarizePerPhase                  = ____;
-    public static String Dump                                = null;
-    public static String Meter                               = null;
-    public static String Time                                = null;
-    public static String Log                                 = null;
-    public static String LogFile                             = null;
-    public static String MethodFilter                        = null;
+    public static String  Dump                                = null;
+    public static String  Meter                               = null;
+    public static String  Time                                = null;
+    public static String  Log                                 = null;
+    public static String  LogFile                             = null;
+    public static String  MethodFilter                        = null;
     public static boolean DumpOnError                        = ____;
 
     // Ideal graph visualizer output settings
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java	Wed Oct 31 18:21:19 2012 +0100
@@ -102,6 +102,9 @@
 
     @Override
     public EscapeOp getEscapeOp() {
+        if (!shouldRevirtualize(this)) {
+            return null;
+        }
         return new EscapeOp() {
 
             @Override
@@ -110,9 +113,46 @@
             }
 
             @Override
-            public VirtualObjectNode virtualObject(int virtualId) {
+            public VirtualObjectNode virtualObject(long virtualId) {
                 return virtualObject;
             }
         };
     }
+
+    private boolean shouldRevirtualize(MaterializeObjectNode materializeObjectNode) {
+        FixedWithNextNode end = materializeObjectNode;
+        do {
+            Node next = end.next();
+            if (next instanceof MaterializeObjectNode) {
+                if (!shouldRevirtualize((MaterializeObjectNode) next)) {
+                    return false;
+                }
+                end = (FixedWithNextNode) next;
+            } else if (next instanceof CyclicMaterializeStoreNode) {
+                end = (FixedWithNextNode) next;
+            } else {
+                break;
+            }
+        } while (true);
+        FixedNode suffix = end.next();
+        if (suffix instanceof EndNode) {
+            for (PhiNode phi : ((EndNode) suffix).merge().phis()) {
+                int materialized = 0;
+                boolean used = false;
+                for (Node input : phi.inputs()) {
+                    if (input instanceof MaterializeObjectNode) {
+                        materialized++;
+                    }
+                    if (input == materializeObjectNode) {
+                        used = true;
+                    }
+                }
+                if (used && materialized != phi.valueCount()) {
+                    return false;
+                }
+            }
+        }
+
+        return true;
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Wed Oct 31 18:21:19 2012 +0100
@@ -45,7 +45,7 @@
 
     public BlockState(BlockState other) {
         for (Map.Entry<VirtualObjectNode, ObjectState> entry : other.objectStates.entrySet()) {
-            objectStates.put(entry.getKey(), entry.getValue().clone());
+            objectStates.put(entry.getKey(), entry.getValue().cloneState());
         }
         for (Map.Entry<ValueNode, VirtualObjectNode> entry : other.objectAliases.entrySet()) {
             objectAliases.put(entry.getKey(), entry.getValue());
@@ -55,18 +55,18 @@
         }
     }
 
-    public ObjectState objectState(VirtualObjectNode object) {
+    public ObjectState getObjectState(VirtualObjectNode object) {
         assert objectStates.containsKey(object);
         return objectStates.get(object);
     }
 
-    public ObjectState objectStateOptional(VirtualObjectNode object) {
+    public ObjectState getObjectStateOptional(VirtualObjectNode object) {
         return objectStates.get(object);
     }
 
-    public ObjectState objectState(ValueNode value) {
+    public ObjectState getObjectState(ValueNode value) {
         VirtualObjectNode object = objectAliases.get(value);
-        return object == null ? null : objectState(object);
+        return object == null ? null : getObjectState(object);
     }
 
     @Override
@@ -83,7 +83,7 @@
 
     private void materializeChangedBefore(FixedNode fixed, VirtualObjectNode virtual, HashSet<VirtualObjectNode> deferred, GraphEffectList deferredStores, GraphEffectList materializeEffects) {
         trace("materializing %s at %s", virtual, fixed);
-        ObjectState obj = objectState(virtual);
+        ObjectState obj = getObjectState(virtual);
         if (obj.getLockCount() > 0 && obj.virtual.type().isArrayClass()) {
             throw new BailoutException("array materialized with lock");
         }
@@ -95,7 +95,7 @@
         obj.setMaterializedValue(materialize);
         deferred.add(virtual);
         for (int i = 0; i < fieldState.length; i++) {
-            ObjectState valueObj = objectState(fieldState[i]);
+            ObjectState valueObj = getObjectState(fieldState[i]);
             if (valueObj != null) {
                 if (valueObj.isVirtual()) {
                     materializeChangedBefore(fixed, valueObj.virtual, deferred, deferredStores, materializeEffects);
@@ -151,16 +151,16 @@
         scalarAliases.put(alias, value);
     }
 
-    public ValueNode scalarAlias(ValueNode alias) {
+    public ValueNode getScalarAlias(ValueNode alias) {
         ValueNode result = scalarAliases.get(alias);
         return result == null ? alias : result;
     }
 
-    public Iterable<ObjectState> states() {
+    public Iterable<ObjectState> getStates() {
         return objectStates.values();
     }
 
-    public Iterable<VirtualObjectNode> virtualObjects() {
+    public Iterable<VirtualObjectNode> getVirtualObjects() {
         return objectAliases.values();
     }
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Oct 31 18:21:19 2012 +0100
@@ -33,6 +33,13 @@
 
 public class GraphEffectList extends EffectList {
 
+    /**
+     * Adds the given fixed node to the graph's control flow, before position (so that the original predecessor of
+     * position will then be node's predecessor).
+     *
+     * @param node The fixed node to be added to the graph.
+     * @param position The fixed node before which the node should be added.
+     */
     public void addFixedNodeBefore(final FixedWithNextNode node, final FixedNode position) {
         add(new Effect() {
 
@@ -50,6 +57,11 @@
         });
     }
 
+    /**
+     * Add the given floating node to the graph.
+     *
+     * @param node The floating node to be added.
+     */
     public void addFloatingNode(final FloatingNode node) {
         add(new Effect() {
 
@@ -66,6 +78,13 @@
         });
     }
 
+    /**
+     * Add the materialization node to the graph's control flow at the given position, and then sets its values.
+     *
+     * @param node The materialization node that should be added.
+     * @param position The fixed node before which the materialization node should be added.
+     * @param values The values for the materialization node's entries.
+     */
     public void addMaterialization(final MaterializeObjectNode node, final FixedNode position, final ValueNode[] values) {
         add(new Effect() {
 
@@ -86,6 +105,12 @@
         });
     }
 
+    /**
+     * Adds an value to the given phi node.
+     *
+     * @param node The phi node to which the value should be added.
+     * @param value The value that will be added to the phi node.
+     */
     public void addPhiInput(final PhiNode node, final ValueNode value) {
         add(new Effect() {
 
@@ -102,6 +127,37 @@
         });
     }
 
+    /**
+     * Sets the phi node's input at the given index to the given value.
+     *
+     * @param node The phi node whose input should be changed.
+     * @param index The index of the phi input to be changed.
+     * @param value The new value for the phi input.
+     */
+    public void setPhiInput(final PhiNode node, final int index, final ValueNode value) {
+        add(new Effect() {
+
+            @Override
+            public String name() {
+                return "setPhiInput";
+            }
+
+            @Override
+            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+                assert node.isAlive() && value.isAlive() && index >= 0;
+                node.setValueAt(index, value);
+            }
+        });
+    }
+
+    /**
+     * Adds a virtual object's state to the given frame state. If the given reusedVirtualObjects set contains the
+     * virtual object then old states for this object will be removed.
+     *
+     * @param node The frame state to which the state should be added.
+     * @param state The virtual object state to add.
+     * @param reusedVirtualObjects A set of all reused virtual objects.
+     */
     public void addVirtualMapping(final FrameState node, final EscapeObjectState state, final HashSet<VirtualObjectNode> reusedVirtualObjects) {
         add(new Effect() {
 
@@ -133,6 +189,11 @@
         });
     }
 
+    /**
+     * Removes the given fixed node from the control flow and deletes it.
+     *
+     * @param node The fixed node that should be deleted.
+     */
     public void deleteFixedNode(final FixedWithNextNode node) {
         add(new Effect() {
 
@@ -152,6 +213,11 @@
         });
     }
 
+    /**
+     * Virtualizes a monitor access by calling its {@link AccessMonitorNode#eliminate()} method.
+     *
+     * @param node The monitor access that should be virtualized.
+     */
     public void eliminateMonitor(final AccessMonitorNode node) {
         add(new Effect() {
 
@@ -168,6 +234,12 @@
         });
     }
 
+    /**
+     * Replaces the given node at its usages without deleting it.
+     *
+     * @param node The node to be replaced.
+     * @param replacement The node that should replace the original value.
+     */
     public void replaceAtUsages(final ValueNode node, final ValueNode replacement) {
         add(new Effect() {
 
@@ -184,6 +256,13 @@
         });
     }
 
+    /**
+     * Replaces the first occurrence of oldInput in node with newInput.
+     *
+     * @param node The node whose input should be changed.
+     * @param oldInput The value to look for.
+     * @param newInput The value to replace with.
+     */
     public void replaceFirstInput(final Node node, final ValueNode oldInput, final ValueNode newInput) {
         add(new Effect() {
 
@@ -204,20 +283,4 @@
             }
         });
     }
-
-    public void setPhiInput(final PhiNode node, final ValueNode value, final int index) {
-        add(new Effect() {
-
-            @Override
-            public String name() {
-                return "setPhiInput";
-            }
-
-            @Override
-            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
-                assert node.isAlive() && value.isAlive() && index >= 0;
-                node.setValueAt(index, value);
-            }
-        });
-    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Oct 31 18:21:19 2012 +0100
@@ -56,8 +56,7 @@
         lockCount = other.lockCount;
     }
 
-    @Override
-    public ObjectState clone() {
+    public ObjectState cloneState() {
         return new ObjectState(this);
     }
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Wed Oct 31 18:21:19 2012 +0100
@@ -24,10 +24,11 @@
 
 import java.util.*;
 
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.graph.*;
@@ -36,10 +37,14 @@
 
 public class PartialEscapeAnalysisPhase extends Phase {
 
-    private final GraalCodeCacheProvider runtime;
+    private final TargetDescription target;
+    private final MetaAccessProvider runtime;
+    private final Assumptions assumptions;
 
-    public PartialEscapeAnalysisPhase(GraalCodeCacheProvider runtime) {
+    public PartialEscapeAnalysisPhase(TargetDescription target, MetaAccessProvider runtime, Assumptions assumptions) {
+        this.target = target;
         this.runtime = runtime;
+        this.assumptions = assumptions;
     }
 
     public static final void trace(String format, Object... obj) {
@@ -53,23 +58,49 @@
     }
 
     @Override
-    protected void run(StructuredGraph graph) {
-        SchedulePhase schedule = new SchedulePhase();
-        schedule.apply(graph, false);
-        PartialEscapeClosure closure = new PartialEscapeClosure(graph.createNodeBitMap(), schedule, runtime);
-        ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock(), new BlockState(), null);
+    protected void run(final StructuredGraph graph) {
+        if (!matches(graph, GraalOptions.EscapeAnalyzeOnly)) {
+            return;
+        }
+
+        for (int iteration = 0; iteration < GraalOptions.EscapeAnalysisIterations; iteration++) {
+            Debug.scope("iteration " + iteration, new Runnable() {
+                @Override
+                public void run() {
+                    SchedulePhase schedule = new SchedulePhase();
+                    schedule.apply(graph, false);
+                    PartialEscapeClosure closure = new PartialEscapeClosure(graph.createNodeBitMap(), schedule, runtime);
+                    ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock(), new BlockState(), null);
+
+                    if (closure.getVirtualIdCount() == 0) {
+                        return;
+                    }
 
-        // apply the effects collected during the escape analysis iteration
-        ArrayList<Node> obsoleteNodes = new ArrayList<>();
-        for (Effect effect : closure.effects) {
-            effect.apply(graph, obsoleteNodes);
+                    // apply the effects collected during the escape analysis iteration
+                    ArrayList<Node> obsoleteNodes = new ArrayList<>();
+                    for (Effect effect : closure.getEffects()) {
+                        effect.apply(graph, obsoleteNodes);
+                    }
+                    trace("%s\n", closure.getEffects());
+
+                    Debug.dump(graph, "after PartialEscapeAnalysis");
+                    assert noObsoleteNodes(graph, obsoleteNodes);
+
+                    new DeadCodeEliminationPhase().apply(graph);
+                    if (GraalOptions.OptCanonicalizer) {
+                        new CanonicalizerPhase(target, runtime, assumptions).apply(graph);
+                    }
+                }
+            });
         }
-        trace("%s\n", closure.effects);
+    }
 
-        Debug.dump(graph, "after PartialEscapeAnalysis");
-        assert noObsoleteNodes(graph, obsoleteNodes);
-
-        new DeadCodeEliminationPhase().apply(graph);
+    private static boolean matches(StructuredGraph graph, String filter) {
+        if (filter != null) {
+            ResolvedJavaMethod method = graph.method();
+            return method != null && MetaUtil.format("%H.%n", method).contains(filter);
+        }
+        return true;
     }
 
     private static boolean noObsoleteNodes(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Oct 31 10:33:01 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Oct 31 18:21:19 2012 +0100
@@ -47,7 +47,10 @@
 
 class PartialEscapeClosure extends BlockIteratorClosure<BlockState> {
 
-    final GraphEffectList effects = new GraphEffectList();
+
+    private static final DebugMetric metricAllocationRemoved = Debug.metric("AllocationRemoved ");
+
+    private final GraphEffectList effects = new GraphEffectList();
     private final HashSet<VirtualObjectNode> reusedVirtualObjects = new HashSet<>();
     private int virtualIds = 0;
 
@@ -61,6 +64,14 @@
         this.runtime = runtime;
     }
 
+    public GraphEffectList getEffects() {
+        return effects;
+    }
+
+    public int getVirtualIdCount() {
+        return virtualIds;
+    }
+
     @Override
     protected void processBlock(Block block, BlockState state) {
         trace("\nBlock: %s (", block);
@@ -86,6 +97,7 @@
                 state.addAndMarkAlias(virtualObject, (ValueNode) node, usages);
                 effects.deleteFixedNode((FixedWithNextNode) node);
                 virtualIds++;
+                metricAllocationRemoved.increment();
             } else {
                 if (usages.isMarked(node)) {
                     trace("[[%s]] ", node);
@@ -106,7 +118,7 @@
         boolean usageFound = false;
         if (node instanceof PiNode || node instanceof ValueProxyNode) {
             ValueNode value = node instanceof PiNode ? ((PiNode) node).object() : ((ValueProxyNode) node).value();
-            ObjectState obj = state.objectState(value);
+            ObjectState obj = state.getObjectState(value);
             if (obj != null) {
                 if (obj.isVirtual()) {
                     state.addAndMarkAlias(obj.virtual, node, usages);
@@ -117,7 +129,7 @@
             }
         } else if (node instanceof CheckCastNode) {
             CheckCastNode x = (CheckCastNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 if (obj.isVirtual()) {
                     if (obj.virtual.type().isSubtypeOf(x.type())) {
@@ -133,13 +145,13 @@
             }
         } else if (node instanceof IsNullNode) {
             IsNullNode x = (IsNullNode) node;
-            if (state.objectState(x.object()) != null) {
+            if (state.getObjectState(x.object()) != null) {
                 replaceAtUsages(state, x, ConstantNode.forBoolean(false, node.graph()));
                 usageFound = true;
             }
         } else if (node instanceof AccessMonitorNode) {
             AccessMonitorNode x = (AccessMonitorNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 Debug.log("monitor operation %s on %s\n", x, obj.virtual);
                 if (node instanceof MonitorEnterNode) {
@@ -158,7 +170,7 @@
             }
         } else if (node instanceof CyclicMaterializeStoreNode) {
             CyclicMaterializeStoreNode x = (CyclicMaterializeStoreNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 if (obj.virtual instanceof VirtualArrayNode) {
                     obj.setEntry(x.targetIndex(), x.value());
@@ -171,7 +183,7 @@
             }
         } else if (node instanceof LoadFieldNode) {
             LoadFieldNode x = (LoadFieldNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 VirtualInstanceNode virtual = (VirtualInstanceNode) obj.virtual;
                 int fieldIndex = virtual.fieldIndex(x.field());
@@ -181,7 +193,7 @@
                 }
                 if (obj.isVirtual()) {
                     ValueNode result = obj.getEntry(fieldIndex);
-                    ObjectState resultObj = state.objectState(result);
+                    ObjectState resultObj = state.getObjectState(result);
                     if (resultObj != null) {
                         state.addAndMarkAlias(resultObj.virtual, x, usages);
                     } else {
@@ -197,7 +209,7 @@
             StoreFieldNode x = (StoreFieldNode) node;
             ValueNode object = x.object();
             ValueNode value = x.value();
-            ObjectState obj = state.objectState(object);
+            ObjectState obj = state.getObjectState(object);
             if (obj != null) {
                 VirtualInstanceNode virtual = (VirtualInstanceNode) obj.virtual;
                 int fieldIndex = virtual.fieldIndex(x.field());
@@ -206,18 +218,18 @@
                     ensureMaterialized(state, obj, x);
                 }
                 if (obj.isVirtual()) {
-                    obj.setEntry(fieldIndex, state.scalarAlias(value));
+                    obj.setEntry(fieldIndex, state.getScalarAlias(value));
                     effects.deleteFixedNode(x);
                 } else {
                     effects.replaceFirstInput(x, object, obj.getMaterializedValue());
-                    ObjectState valueObj = state.objectState(value);
+                    ObjectState valueObj = state.getObjectState(value);
                     if (valueObj != null) {
                         replaceWithMaterialized(value, x, state, valueObj);
                     }
                 }
                 usageFound = true;
             } else {
-                ObjectState valueObj = state.objectState(value);
+                ObjectState valueObj = state.getObjectState(value);
                 if (valueObj != null) {
                     replaceWithMaterialized(value, x, state, valueObj);
                     usageFound = true;
@@ -226,17 +238,17 @@
         } else if (node instanceof LoadIndexedNode) {
             LoadIndexedNode x = (LoadIndexedNode) node;
             ValueNode array = x.array();
-            ObjectState arrayObj = state.objectState(array);
+            ObjectState arrayObj = state.getObjectState(array);
             if (arrayObj != null) {
                 if (arrayObj.isVirtual()) {
-                    ValueNode indexValue = state.scalarAlias(x.index());
+                    ValueNode indexValue = state.getScalarAlias(x.index());
                     int index = indexValue.isConstant() ? indexValue.asConstant().asInt() : -1;
                     if (index < 0 || index >= arrayObj.getEntries().length) {
                         // out of bounds or not constant
                         replaceWithMaterialized(array, x, state, arrayObj);
                     } else {
                         ValueNode result = arrayObj.getEntry(index);
-                        ObjectState resultObj = state.objectState(result);
+                        ObjectState resultObj = state.getObjectState(result);
                         if (resultObj != null) {
                             state.addAndMarkAlias(resultObj.virtual, x, usages);
                         } else {
@@ -253,12 +265,12 @@
             StoreIndexedNode x = (StoreIndexedNode) node;
             ValueNode array = x.array();
             ValueNode value = x.value();
-            ObjectState arrayObj = state.objectState(array);
-            ObjectState valueObj = state.objectState(value);
+            ObjectState arrayObj = state.getObjectState(array);
+            ObjectState valueObj = state.getObjectState(value);
 
             if (arrayObj != null) {
                 if (arrayObj.isVirtual()) {
-                    ValueNode indexValue = state.scalarAlias(x.index());
+                    ValueNode indexValue = state.getScalarAlias(x.index());
                     int index = indexValue.isConstant() ? indexValue.asConstant().asInt() : -1;
                     if (index < 0 || index >= arrayObj.getEntries().length) {
                         // out of bounds or not constant
@@ -267,7 +279,7 @@
                             replaceWithMaterialized(value, x, state, valueObj);
                         }
                     } else {
-                        arrayObj.setEntry(index, state.scalarAlias(value));
+                        arrayObj.setEntry(index, state.getScalarAlias(value));
                         effects.deleteFixedNode(x);
                     }
                 } else {
@@ -285,14 +297,14 @@
             }
         } else if (node instanceof RegisterFinalizerNode) {
             RegisterFinalizerNode x = (RegisterFinalizerNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 replaceWithMaterialized(x.object(), x, state, obj);
                 usageFound = true;
             }
         } else if (node instanceof ArrayLengthNode) {
             ArrayLengthNode x = (ArrayLengthNode) node;
-            ObjectState obj = state.objectState(x.array());
+            ObjectState obj = state.getObjectState(x.array());
             if (obj != null) {
                 replaceAtUsages(state, x, ConstantNode.forInt(((VirtualArrayNode) obj.virtual).entryCount(), node.graph()));
                 effects.deleteFixedNode(x);
@@ -300,7 +312,7 @@
             }
         } else if (node instanceof LoadHubNode) {
             LoadHubNode x = (LoadHubNode) node;
-            ObjectState obj = state.objectState(x.object());
+            ObjectState obj = state.getObjectState(x.object());
             if (obj != null) {
                 replaceAtUsages(state, x, ConstantNode.forConstant(obj.virtual.type().getEncoding(Representation.ObjectHub), runtime, node.graph()));
                 effects.deleteFixedNode(x);
@@ -308,14 +320,14 @@
             }
         } else if (node instanceof ReturnNode) {
             ReturnNode x = (ReturnNode) node;
-            ObjectState obj = state.objectState(x.result());
+            ObjectState obj = state.getObjectState(x.result());
             if (obj != null) {
                 replaceWithMaterialized(x.result(), x, state, obj);
                 usageFound = true;
             }
         } else if (node instanceof MethodCallTargetNode) {
             for (ValueNode argument : ((MethodCallTargetNode) node).arguments()) {
-                ObjectState obj = state.objectState(argument);
+                ObjectState obj = state.getObjectState(argument);
                 if (obj != null) {
                     replaceWithMaterialized(argument, node, insertBefore, state, obj);
                     usageFound = true;
@@ -323,8 +335,8 @@
             }
         } else if (node instanceof ObjectEqualsNode) {
             ObjectEqualsNode x = (ObjectEqualsNode) node;
-            ObjectState xObj = state.objectState(x.x());
-            ObjectState yObj = state.objectState(x.y());
+            ObjectState xObj = state.getObjectState(x.x());
+            ObjectState yObj = state.getObjectState(x.y());
             boolean xVirtual = xObj != null && xObj.isVirtual();
             boolean yVirtual = yObj != null && yObj.isVirtual();
 
@@ -353,7 +365,7 @@
             usageFound = true;
         } else if (node instanceof UnsafeLoadNode || node instanceof UnsafeStoreNode || node instanceof CompareAndSwapNode || node instanceof SafeReadNode) {
             for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-                ObjectState obj = state.objectState(input);
+                ObjectState obj = state.getObjectState(input);
                 if (obj != null) {
                     replaceWithMaterialized(input, node, insertBefore, state, obj);
                     usageFound = true;
@@ -373,13 +385,13 @@
 
                     @Override
                     public void apply(Node usage, ValueNode value) {
-                        ObjectState valueObj = state.objectState(value);
+                        ObjectState valueObj = state.getObjectState(value);
                         if (valueObj != null) {
                             virtual.add(valueObj);
                             effects.replaceFirstInput(usage, value, valueObj.virtual);
                         } else if (value instanceof VirtualObjectNode) {
                             ObjectState virtualObj = null;
-                            for (ObjectState obj : state.states()) {
+                            for (ObjectState obj : state.getStates()) {
                                 if (value == obj.virtual) {
                                     virtualObj = obj;
                                     break;
@@ -391,7 +403,7 @@
                         }
                     }
                 });
-                for (ObjectState obj : state.states()) {
+                for (ObjectState obj : state.getStates()) {
                     if (obj.isVirtual() && obj.getLockCount() > 0) {
                         virtual.add(obj);
                     }
@@ -402,7 +414,7 @@
                     ObjectState obj = queue.removeLast();
                     if (obj.isVirtual()) {
                         for (ValueNode field : obj.getEntries()) {
-                            ObjectState fieldObj = state.objectState(field);
+                            ObjectState fieldObj = state.getObjectState(field);
                             if (fieldObj != null) {
                                 if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
                                     virtual.add(fieldObj);
@@ -417,7 +429,7 @@
                     if (obj.isVirtual()) {
                         ValueNode[] fieldState = obj.getEntries().clone();
                         for (int i = 0; i < fieldState.length; i++) {
-                            ObjectState valueObj = state.objectState(fieldState[i]);
+                            ObjectState valueObj = state.getObjectState(fieldState[i]);
                             if (valueObj != null) {
                                 if (valueObj.isVirtual()) {
                                     fieldState[i] = valueObj.virtual;
@@ -437,7 +449,7 @@
         }
         if (!usageFound) {
             for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-                ObjectState obj = state.objectState(input);
+                ObjectState obj = state.getObjectState(input);
                 if (obj != null) {
                     replaceWithMaterialized(input, node, insertBefore, state, obj);
                     usageFound = true;
@@ -483,15 +495,15 @@
         do {
             materialized = false;
             // use a hash set to make the values distinct...
-            for (VirtualObjectNode object : newState.virtualObjects()) {
-                ObjectState resultState = newState.objectStateOptional(object);
+            for (VirtualObjectNode object : newState.getVirtualObjects()) {
+                ObjectState resultState = newState.getObjectStateOptional(object);
                 if (resultState == null || resultState.isVirtual()) {
                     int virtual = 0;
-                    ObjectState startObj = states.get(0).objectState(object);
+                    ObjectState startObj = states.get(0).getObjectState(object);
                     int lockCount = startObj.getLockCount();
                     ValueNode singleValue = startObj.isVirtual() ? null : startObj.getMaterializedValue();
                     for (BlockState state : states) {
-                        ObjectState obj = state.objectState(object);
+                        ObjectState obj = state.getObjectState(object);
                         if (obj.isVirtual()) {
                             virtual++;
                             singleValue = null;
@@ -509,7 +521,7 @@
                             effects.addFloatingNode(materializedValuePhi);
                             for (int i = 0; i < states.size(); i++) {
                                 BlockState state = states.get(i);
-                                ObjectState obj = state.objectState(object);
+                                ObjectState obj = state.getObjectState(object);
                                 materialized |= obj.isVirtual();
                                 ensureMaterialized(state, obj, merge.forwardEndAt(i));
                                 effects.addPhiInput(materializedValuePhi, obj.getMaterializedValue());
@@ -525,7 +537,7 @@
                         int mismatch = 0;
                         for (int i = 1; i < states.size(); i++) {
                             BlockState state = states.get(i);
-                            ValueNode[] fields = state.objectState(object).getEntries();
+                            ValueNode[] fields = state.getObjectState(object).getEntries();
                             for (int index = 0; index < values.length; index++) {
                                 if (phis[index] == null && values[index] != fields[index]) {
                                     mismatch++;
@@ -537,10 +549,10 @@
                         if (mismatch > 0) {
                             for (int i = 0; i < states.size(); i++) {
                                 BlockState state = states.get(i);
-                                ValueNode[] fields = state.objectState(object).getEntries();
+                                ValueNode[] fields = state.getObjectState(object).getEntries();
                                 for (int index = 0; index < values.length; index++) {
                                     if (phis[index] != null) {
-                                        ObjectState obj = state.objectState(fields[index]);
+                                        ObjectState obj = state.getObjectState(fields[index]);
                                         if (obj != null) {
                                             materialized |= obj.isVirtual();
                                             ensureMaterialized(state, obj, merge.forwardEndAt(i));
@@ -580,7 +592,7 @@
         int sameEntryCount = -1;
         for (int i = 0; i < phi.valueCount(); i++) {
             ValueNode value = phi.valueAt(i);
-            ObjectState obj = states.get(i).objectState(value);
+            ObjectState obj = states.get(i).getObjectState(value);
             if (obj != null) {
                 if (obj.isVirtual()) {
                     virtualInputs++;
@@ -600,7 +612,7 @@
                         }
                     }
                 } else {
-                    effects.setPhiInput(phi, obj.getMaterializedValue(), i);
+                    effects.setPhiInput(phi, i, obj.getMaterializedValue());
                 }
             }
         }
@@ -623,7 +635,7 @@
         if (materialize) {
             for (int i = 0; i < phi.valueCount(); i++) {
                 ValueNode value = phi.valueAt(i);
-                ObjectState obj = states.get(i).objectState(value);
+                ObjectState obj = states.get(i).getObjectState(value);
                 if (obj != null) {
                     materialized |= obj.isVirtual();
                     replaceWithMaterialized(value, phi, merge.forwardEndAt(i), states.get(i), obj);
@@ -647,10 +659,10 @@
             int checkpoint = effects.checkpoint();
 
             for (PhiDesc desc : phis) {
-                ObjectState obj = state.objectState(desc.virtualObject);
+                ObjectState obj = state.getObjectState(desc.virtualObject);
                 if (obj.isVirtual()) {
                     ValueNode value = obj.getEntry(desc.fieldIndex);
-                    ObjectState valueObj = state.objectState(value);
+                    ObjectState valueObj = state.getObjectState(value);
                     if (valueObj != null) {
                         assert !valueObj.isVirtual();
                         value = valueObj.getMaterializedValue();
@@ -665,12 +677,12 @@
 
             for (PhiNode phi : loop.loopBegin().phis()) {
                 if (usages.isMarked(phi) && phi.type() == PhiType.Value) {
-                    ObjectState initialObj = initialState.objectState(phi.valueAt(0));
+                    ObjectState initialObj = initialState.getObjectState(phi.valueAt(0));
                     if (initialObj != null) {
                         if (initialObj.isVirtual()) {
                             state.addAndMarkAlias(initialObj.virtual, phi, usages);
                         } else {
-                            successEffects.setPhiInput(phi, initialObj.getMaterializedValue(), 0);
+                            successEffects.setPhiInput(phi, 0, initialObj.getMaterializedValue());
                         }
                     }
                 }
@@ -703,7 +715,7 @@
                 effects.backtrack(checkpoint);
                 effects.decLevel();
                 for (VirtualObjectNode virtualObject : additionalMaterializations) {
-                    ObjectState obj = initialState.objectState(virtualObject);
+                    ObjectState obj = initialState.getObjectState(virtualObject);
                     if (obj.isVirtual()) {
                         initialState.materializeBefore(loop.loopBegin().forwardEnd(), virtualObject, effects);
                     }
@@ -718,17 +730,17 @@
         HashMap<VirtualObjectNode, ValueProxyNode> proxies = new HashMap<>();
 
         for (ValueProxyNode proxy : exitNode.proxies()) {
-            ObjectState obj = exitState.objectState(proxy.value());
+            ObjectState obj = exitState.getObjectState(proxy.value());
             if (obj != null) {
                 proxies.put(obj.virtual, proxy);
             }
         }
-        for (ObjectState obj : exitState.states()) {
-            ObjectState initialObj = initialState.objectStateOptional(obj.virtual);
+        for (ObjectState obj : exitState.getStates()) {
+            ObjectState initialObj = initialState.getObjectStateOptional(obj.virtual);
             if (obj.isVirtual()) {
                 for (int i = 0; i < obj.getEntries().length; i++) {
                     ValueNode value = obj.getEntry(i);
-                    ObjectState valueObj = exitState.objectState(value);
+                    ObjectState valueObj = exitState.getObjectState(value);
                     if (valueObj == null) {
                         if ((value instanceof PhiNode && ((PhiNode) value).merge() == exitNode.loopBegin()) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
                             ValueProxyNode proxy = new ValueProxyNode(value, exitNode, PhiType.Value);
@@ -793,16 +805,16 @@
         boolean materialized;
         do {
             materialized = false;
-            for (ObjectState state : initialState.states()) {
-                ObjectState endState = loopEndState.objectState(state.virtual);
+            for (ObjectState state : initialState.getStates()) {
+                ObjectState endState = loopEndState.getObjectState(state.virtual);
                 if (state.isVirtual()) {
                     if (endState.isVirtual()) {
                         assert state.getEntries().length == endState.getEntries().length;
                         for (int i = 0; endState.isVirtual() && i < state.getEntries().length; i++) {
                             ValueNode value = state.getEntry(i);
                             ValueNode endValue = endState.getEntry(i);
-                            ObjectState valueObj = initialState.objectState(value);
-                            ObjectState endValueObj = loopEndState.objectState(endValue);
+                            ObjectState valueObj = initialState.getObjectState(value);
+                            ObjectState endValueObj = loopEndState.getObjectState(endValue);
 
                             if (valueObj != null) {
                                 if (valueObj.isVirtual()) {
@@ -831,13 +843,13 @@
             }
             for (PhiNode phi : loopBegin.phis().snapshot()) {
                 if (usages.isMarked(phi) && phi.type() == PhiType.Value) {
-                    ObjectState initialObj = initialState.objectState(phi.valueAt(0));
+                    ObjectState initialObj = initialState.getObjectState(phi.valueAt(0));
                     boolean initialMaterialized = initialObj == null || !initialObj.isVirtual();
 
-                    ObjectState loopEndObj = loopEndState.objectState(phi.valueAt(loopEnd));
+                    ObjectState loopEndObj = loopEndState.getObjectState(phi.valueAt(loopEnd));
                     if (loopEndObj == null || !loopEndObj.isVirtual()) {
                         if (loopEndObj != null) {
-                            successEffects.setPhiInput(phi, loopEndObj.getMaterializedValue(), loopBegin.phiPredecessorIndex(loopEnd));
+                            successEffects.setPhiInput(phi, loopBegin.phiPredecessorIndex(loopEnd), loopEndObj.getMaterializedValue());
                         }
                         if (!initialMaterialized) {
                             additionalMaterializations.add(initialObj.virtual);
@@ -856,16 +868,16 @@
             }
         } while (materialized);
 
-        for (ObjectState state : initialState.states()) {
-            ObjectState endState = loopEndState.objectState(state.virtual);
+        for (ObjectState state : initialState.getStates()) {
+            ObjectState endState = loopEndState.getObjectState(state.virtual);
             if (state.isVirtual()) {
                 if (endState.isVirtual()) {
                     assert state.getEntries().length == endState.getEntries().length;
                     for (int i = 0; i < state.getEntries().length; i++) {
                         ValueNode value = state.getEntry(i);
                         ValueNode endValue = endState.getEntry(i);
-                        ObjectState valueObj = initialState.objectState(value);
-                        ObjectState endValueObj = loopEndState.objectState(endValue);
+                        ObjectState valueObj = initialState.getObjectState(value);
+                        ObjectState endValueObj = loopEndState.getObjectState(endValue);
 
                         if (valueObj != null) {
                             if (valueObj.isVirtual()) {