changeset 6710:6db6881c1270

add Virtualizable and VirtualizerTool, refactor PEA to use it
author Lukas Stadler <lukas.stadler@jku.at>
date Mon, 12 Nov 2012 17:49:06 +0100
parents 55afed7bc209
children ae13cc658b80
files graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/CyclicMaterializeStoreNode.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 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java
diffstat 23 files changed, 702 insertions(+), 365 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -25,13 +25,14 @@
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * A node that changes the type of its input, usually narrowing it.
  * For example, a PI node refines the type of a receiver during
  * type-guarded inlining to be the type tested by the guard.
  */
-public class PiNode extends FloatingNode implements LIRLowerable {
+public class PiNode extends FloatingNode implements LIRLowerable, Virtualizable {
 
     @Input private ValueNode object;
     @Input(notDataflow = true) private final FixedNode anchor;
@@ -64,4 +65,12 @@
         }
         return updateStamp(stamp().join(object().stamp()));
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            tool.replaceWithVirtual(virtual);
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -26,14 +26,16 @@
 import com.oracle.graal.graph.Node.ValueNumberable;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * A value proxy that is inserted in the frame state of a loop exit for any value that is
  * created inside the loop (i.e. was not live on entry to the loop) and is (potentially)
  * used after the loop.
  */
-public class ValueProxyNode extends FloatingNode implements Node.IterableNodeType, ValueNumberable {
+public class ValueProxyNode extends FloatingNode implements Node.IterableNodeType, ValueNumberable, Canonicalizable, Virtualizable {
     @Input(notDataflow = true) private BeginNode proxyPoint;
     @Input private ValueNode value;
     private final PhiType type;
@@ -74,4 +76,20 @@
         assert proxyPoint != null;
         return super.verify();
     }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        if (value.isConstant()) {
+            return value;
+        }
+        return this;
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(value());
+        if (virtual != null) {
+            tool.replaceWithVirtual(virtual);
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -30,7 +30,7 @@
 /**
  * An IsNullNode will be true if the supplied value is null, and false if it is non-null.
  */
-public final class IsNullNode extends BooleanNode implements Canonicalizable, LIRLowerable {
+public final class IsNullNode extends BooleanNode implements Canonicalizable, LIRLowerable, Virtualizable {
 
     @Input private ValueNode object;
 
@@ -73,4 +73,11 @@
         }
         return this;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        if (tool.getVirtualState(object()) != null || tool.getMaterializedValue(object()) != null) {
+            tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -26,9 +26,10 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 @NodeInfo(shortName = "==")
-public final class ObjectEqualsNode extends CompareNode {
+public final class ObjectEqualsNode extends CompareNode implements Virtualizable {
 
     /**
      * Constructs a new object equality comparison node.
@@ -69,4 +70,20 @@
 
         return super.canonical(tool);
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtualX = tool.getVirtualState(x());
+        VirtualObjectNode virtualY = tool.getVirtualState(y());
+        boolean xVirtual = virtualX != null;
+        boolean yVirtual = virtualY != null;
+
+        if (xVirtual ^ yVirtual) {
+            // one of them is virtual: they can never be the same objects
+            tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
+        } else if (xVirtual && yVirtual) {
+            // both are virtual: check if they refer to the same object
+            tool.replaceWithValue(ConstantNode.forBoolean(virtualX == virtualY, graph()));
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -27,11 +27,12 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Loads an object's {@linkplain Representation#ObjectHub hub}, null-checking the object first.
  */
-public final class LoadHubNode extends FixedWithNextNode implements Lowerable, Canonicalizable {
+public final class LoadHubNode extends FixedWithNextNode implements Lowerable, Canonicalizable, Virtualizable {
     @Input private ValueNode object;
 
     public ValueNode object() {
@@ -75,4 +76,12 @@
 
     @NodeIntrinsic
     public static native Object loadHub(Object object);
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            tool.replaceWithValue(ConstantNode.forConstant(virtual.type().getEncoding(Representation.ObjectHub), tool.getMetaAccessProvider(), graph()));
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -28,13 +28,14 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 
 /**
  * The ValueAnchor instruction keeps non-CFG (floating) nodes above a certain point in the graph.
  */
-public final class ValueAnchorNode extends FixedWithNextNode implements Canonicalizable, LIRLowerable, Node.IterableNodeType {
+public final class ValueAnchorNode extends FixedWithNextNode implements Canonicalizable, LIRLowerable, Node.IterableNodeType, Virtualizable {
 
     public ValueAnchorNode(ValueNode... values) {
         super(StampFactory.dependency(), values);
@@ -84,4 +85,21 @@
         }
         return this;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        // don't process this node if it is anchoring the return value
+        if (next() instanceof MonitorExitNode) {
+            MonitorExitNode monitorExit = (MonitorExitNode) next();
+            if (monitorExit.stateAfter() != null && monitorExit.stateAfter().bci == FrameState.AFTER_BCI && monitorExit.next() instanceof ReturnNode) {
+                return;
+            }
+        }
+        for (ValueNode node : dependencies().nonNull().and(isNotA(BeginNode.class))) {
+            if (tool.getVirtualState(node) == null) {
+                return;
+            }
+        }
+        tool.delete();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -23,9 +23,12 @@
 package com.oracle.graal.nodes.java;
 
 import com.oracle.graal.api.code.*;
+import com.oracle.graal.debug.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code AccessMonitorNode} is the base class of both monitor acquisition and release.
@@ -38,12 +41,9 @@
  * locking hierarchy.
  * <br>
  * The Java bytecode specification allows non-balanced locking. Graal does not handle such cases and throws a
- * {@link BailoutException} instead. Detecting non-balanced monitors during bytecode parsing is difficult, since the
- * node flowing into the {@link MonitorExitNode} can be a phi function hiding the node that was flowing into the
- * {@link MonitorEnterNode}. Optimization phases are free to throw {@link BailoutException} if they detect such cases.
- * Otherwise, they are detected during LIR construction.
+ * {@link BailoutException} instead during graph building.
  */
-public abstract class AccessMonitorNode extends AbstractStateSplit implements StateSplit, MemoryCheckpoint {
+public abstract class AccessMonitorNode extends AbstractStateSplit implements StateSplit, MemoryCheckpoint, Virtualizable {
 
     @Input private ValueNode object;
     private boolean eliminated;
@@ -69,4 +69,21 @@
         super(StampFactory.forVoid());
         this.object = object;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            Debug.log("monitor operation %s on %s\n", this, virtual);
+            int newLockCount = tool.getVirtualLockCount(virtual) + (this instanceof MonitorEnterNode ? 1 : -1);
+            tool.setVirtualLockCount(virtual,  newLockCount);
+            tool.replaceFirstInput(object(), virtual);
+            tool.customAction(new Runnable() {
+                @Override
+                public void run() {
+                    eliminate();
+                }
+            });
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -26,11 +26,12 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code ArrayLength} instruction gets the length of an array.
  */
-public final class ArrayLengthNode extends FixedWithNextNode implements Canonicalizable, Lowerable {
+public final class ArrayLengthNode extends FixedWithNextNode implements Canonicalizable, Lowerable, Virtualizable {
 
     @Input private ValueNode array;
 
@@ -67,4 +68,13 @@
 
     @NodeIntrinsic
     public static native int arrayLength(Object array);
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(array());
+        if (virtual != null) {
+            assert virtual instanceof VirtualArrayNode : virtual;
+            tool.replaceWithValue(ConstantNode.forInt(virtual.entryCount(), graph()));
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -27,11 +27,12 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Implements a type check against a compile-time known type.
  */
-public final class CheckCastNode extends FixedWithNextNode implements Canonicalizable, Lowerable, Node.IterableNodeType {
+public final class CheckCastNode extends FixedWithNextNode implements Canonicalizable, Lowerable, Node.IterableNodeType, Virtualizable {
 
     @Input private ValueNode object;
     private final ResolvedJavaType type;
@@ -97,4 +98,12 @@
     public JavaTypeProfile profile() {
         return profile;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null && virtual.type().isSubtypeOf(type())) {
+            tool.replaceWithVirtual(virtual);
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -27,12 +27,13 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code LoadFieldNode} represents a read of a static or instance field.
  */
 @NodeInfo(nameTemplate = "LoadField#{p#field/s}")
-public final class LoadFieldNode extends AccessFieldNode implements Canonicalizable, Node.IterableNodeType {
+public final class LoadFieldNode extends AccessFieldNode implements Canonicalizable, Node.IterableNodeType, Virtualizable {
 
     /**
      * Creates a new LoadFieldNode instance.
@@ -69,4 +70,21 @@
         }
         return this;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            int fieldIndex = ((VirtualInstanceNode) virtual).fieldIndex(field());
+            if (fieldIndex != -1) {
+                ValueNode result = tool.getVirtualEntry(virtual, fieldIndex);
+                VirtualObjectNode virtualResult = tool.getVirtualState(result);
+                if (virtualResult != null) {
+                    tool.replaceWithVirtual(virtualResult);
+                } else {
+                    tool.replaceWithValue(result);
+                }
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -29,11 +29,12 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code LoadIndexedNode} represents a read from an element of an array.
  */
-public final class LoadIndexedNode extends AccessIndexedNode implements Canonicalizable, Node.IterableNodeType {
+public final class LoadIndexedNode extends AccessIndexedNode implements Canonicalizable, Node.IterableNodeType, Virtualizable {
 
     /**
      * Creates a new LoadIndexedNode.
@@ -70,4 +71,22 @@
         }
         return this;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtualArray = tool.getVirtualState(array());
+        if (virtualArray != null) {
+            ValueNode indexValue = tool.getReplacedValue(index());
+            int index = indexValue.isConstant() ? indexValue.asConstant().asInt() : -1;
+            if (index >= 0 && index < virtualArray.entryCount()) {
+                ValueNode result = tool.getVirtualEntry(virtualArray, index);
+                VirtualObjectNode virtualResult = tool.getVirtualState(result);
+                if (virtualResult != null) {
+                    tool.replaceWithVirtual(virtualResult);
+                } else {
+                    tool.replaceWithValue(result);
+                }
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -25,13 +25,15 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code StoreFieldNode} represents a write to a static or instance field.
  */
 @NodeInfo(nameTemplate = "StoreField#{p#field/s}")
-public final class StoreFieldNode extends AccessFieldNode implements StateSplit {
+public final class StoreFieldNode extends AccessFieldNode implements StateSplit, Virtualizable {
 
     @Input private ValueNode value;
     @Input(notDataflow = true) private FrameState stateAfter;
@@ -64,4 +66,16 @@
         super(StampFactory.forVoid(), object, field, leafGraphId);
         this.value = value;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            int fieldIndex = ((VirtualInstanceNode) virtual).fieldIndex(field());
+            if (fieldIndex != -1) {
+                tool.setVirtualEntry(virtual, fieldIndex, value());
+                tool.delete();
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -26,11 +26,12 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code StoreIndexedNode} represents a write to an array element.
  */
-public final class StoreIndexedNode extends AccessIndexedNode implements StateSplit, Lowerable {
+public final class StoreIndexedNode extends AccessIndexedNode implements StateSplit, Lowerable, Virtualizable {
 
     @Input private ValueNode value;
     @Input(notDataflow = true) private FrameState stateAfter;
@@ -64,4 +65,17 @@
         super(StampFactory.forVoid(), array, index, elementKind, leafGraphId);
         this.value = value;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtualArray = tool.getVirtualState(array());
+        if (virtualArray != null) {
+            ValueNode indexValue = tool.getReplacedValue(index());
+            int index = indexValue.isConstant() ? indexValue.asConstant().asInt() : -1;
+            if (index >= 0 && index < virtualArray.entryCount()) {
+                tool.setVirtualEntry(virtualArray, index, value());
+                tool.delete();
+            }
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Mon Nov 12 17:49:06 2012 +0100
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.spi;
+
+/**
+ * This interface allows a node to convey information about what its effect would be if some of its inputs were
+ * virtualized.
+ */
+public interface Virtualizable {
+
+    /**
+     * A node class can implement this method to convey information about what its effect would be if some of its inputs
+     * were virtualized. All modifications must be made through the supplied tool, and not directly on the node, because
+     * by the time this method is called the virtualized/non-virtualized state is still speculative and might not hold
+     * because of loops, etc.
+     *
+     * @param tool the tool used to describe the effects of this node
+     */
+    void virtualize(VirtualizerTool tool);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Mon Nov 12 17:49:06 2012 +0100
@@ -0,0 +1,145 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.spi;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.virtual.*;
+
+/**
+ * This tool can be used to query the current state (normal/virtualized/re-materialized) of values and to describe the
+ * actions that would be taken for this state.
+ *
+ * See also {@link Virtualizable}.
+ */
+public interface VirtualizerTool {
+
+    /**
+     * @return the {@link MetaAccessProvider} associated with the current compilation, which might be required for
+     *         creating constants, etc.
+     */
+    MetaAccessProvider getMetaAccessProvider();
+
+    // methods working on virtualized/materialized objects
+
+    /**
+     * Queries the current state of the given value: if it is virtualized (thread-local and the compiler knows all
+     * entries) or not.
+     *
+     * @param value the value whose state should be queried.
+     * @return the {@link VirtualObjectNode} representing the value if it is virtualized, null otherwise.
+     */
+    VirtualObjectNode getVirtualState(ValueNode value);
+
+    /**
+     * Retrieves the entry (field or array element) with the given index in the virtualized object.
+     *
+     * @param virtual the virtualized object
+     * @param index the index to be queried.
+     * @return the entry at the given index.
+     */
+    ValueNode getVirtualEntry(VirtualObjectNode virtual, int index);
+
+    /**
+     * Sets the entry (field or array element) with the given index in the virtualized object.
+     *
+     * @param virtual the virtualized object.
+     * @param index the index to be set.
+     * @param value the new value for the given index.
+     */
+    void setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value);
+
+    /**
+     * Retrieves the lock count of the given virtualized object.
+     *
+     * @param virtual the virtualized object.
+     * @return the number of locks.
+     */
+    int getVirtualLockCount(VirtualObjectNode virtual);
+
+    /**
+     * Sets the lock count of the given virtualized object.
+     *
+     * @param virtual the virtualized object.
+     * @param lockCount the new lock count.
+     */
+    void setVirtualLockCount(VirtualObjectNode virtual, int lockCount);
+
+    /**
+     * Queries the current state of the given value: if it was materialized or not.
+     *
+     * @param value the value whose state should be queried.
+     * @return the materialized value (usually a MaterializeObjectNode or a {@link PhiNode}) if it was materialized,
+     *         null otherwise.
+     */
+    ValueNode getMaterializedValue(ValueNode value);
+
+    // scalar replacement
+
+    /**
+     * Replacements via {@link #replaceWithValue(ValueNode)} are not immediately committed. This method can be used to
+     * determine if a value was replaced by another one (e.g., a load field by the loaded value).
+     *
+     * @param original the original input value.
+     * @return the replacement value, or the original value if there is no replacement.
+     */
+    ValueNode getReplacedValue(ValueNode original);
+
+    // operations on the current node
+
+    /**
+     * Deletes the current node and replaces it with the given virtualized object.
+     *
+     * @param virtual the virtualized object that should replace the current node.
+     */
+    void replaceWithVirtual(VirtualObjectNode virtual);
+
+    /**
+     * Deletes the current node and replaces it with the given value.
+     *
+     * @param replacement the value that should replace the current node.
+     */
+    void replaceWithValue(ValueNode replacement);
+
+    /**
+     * Deletes the current node.
+     */
+    void delete();
+
+    /**
+     * Replaces an input of the current node.
+     *
+     * @param oldInput the old input value.
+     * @param replacement the new input value.
+     */
+    void replaceFirstInput(Node oldInput, Node replacement);
+
+    /**
+     * Performs a custom action on the current node. This action will only be performed when, and if, the changes are
+     * committed. Custom actions must not modify inputs of nodes.
+     *
+     * @param action the custom action.
+     */
+    void customAction(Runnable action);
+}
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Mon Nov 12 17:49:06 2012 +0100
@@ -68,6 +68,7 @@
 
     // escape analysis settings
     public static boolean PartialEscapeAnalysis              = true;
+    public static boolean EscapeAnalysisHistogram            = ____;
     public static int     EscapeAnalysisIterations           = 2;
     public static String  EscapeAnalyzeOnly                  = null;
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/CyclicMaterializeStoreNode.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/CyclicMaterializeStoreNode.java	Mon Nov 12 17:49:06 2012 +0100
@@ -28,12 +28,13 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code StoreFieldNode} represents a write to a static or instance field.
  */
 @NodeInfo(nameTemplate = "MaterializeStore#{p#target/s}")
-public final class CyclicMaterializeStoreNode extends FixedWithNextNode implements Lowerable {
+public final class CyclicMaterializeStoreNode extends FixedWithNextNode implements Lowerable, Virtualizable {
 
     @Input private ValueNode object;
     @Input private ValueNode value;
@@ -82,4 +83,17 @@
         }
         graph.replaceFixedWithFixed(this, store);
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        VirtualObjectNode virtual = tool.getVirtualState(object());
+        if (virtual != null) {
+            if (virtual instanceof VirtualArrayNode) {
+                tool.setVirtualEntry(virtual, targetIndex(), value());
+            } else {
+                tool.setVirtualEntry(virtual, ((VirtualInstanceNode) virtual).fieldIndex(targetField()), value());
+            }
+            tool.delete();
+        }
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Mon Nov 12 17:49:06 2012 +0100
@@ -30,6 +30,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.MergeableBlockState;
 import com.oracle.graal.virtual.nodes.*;
@@ -88,41 +89,71 @@
             throw new BailoutException("array materialized with lock");
         }
 
-        MaterializeObjectNode materialize = new MaterializeObjectNode(virtual, obj.getLockCount());
-        ValueNode[] values = new ValueNode[obj.getEntries().length];
-        materialize.setProbability(fixed.probability());
         ValueNode[] fieldState = obj.getEntries();
-        obj.setMaterializedValue(materialize);
-        deferred.add(virtual);
+
+        // determine if all entries are default constants
+        boolean allDefault = true;
         for (int i = 0; i < fieldState.length; i++) {
-            ObjectState valueObj = getObjectState(fieldState[i]);
-            if (valueObj != null) {
-                if (valueObj.isVirtual()) {
-                    materializeChangedBefore(fixed, valueObj.virtual, deferred, deferredStores, materializeEffects);
-                }
-                if (deferred.contains(valueObj.virtual)) {
-                    Kind fieldKind;
-                    CyclicMaterializeStoreNode store;
-                    if (virtual instanceof VirtualArrayNode) {
-                        store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), i);
-                        fieldKind = ((VirtualArrayNode) virtual).componentType().getKind();
-                    } else {
-                        VirtualInstanceNode instanceObject = (VirtualInstanceNode) virtual;
-                        store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), instanceObject.field(i));
-                        fieldKind = instanceObject.field(i).getType().getKind();
-                    }
-                    deferredStores.addFixedNodeBefore(store, fixed);
-                    values[i] = ConstantNode.defaultForKind(fieldKind, fixed.graph());
-                } else {
-                    values[i] = valueObj.getMaterializedValue();
-                }
-            } else {
-                values[i] = fieldState[i];
+            if (!fieldState[i].isConstant() || !fieldState[i].asConstant().isDefaultForKind()) {
+                allDefault = false;
+                break;
             }
         }
-        deferred.remove(virtual);
 
-        materializeEffects.addMaterialization(materialize, fixed, values);
+        if (allDefault && obj.getLockCount() == 0) {
+            // create an ordinary NewInstance/NewArray node if all entries are default constants
+            FixedWithNextNode newObject;
+            if (virtual instanceof VirtualInstanceNode) {
+                newObject = new NewInstanceNode(virtual.type(), true, false);
+            } else {
+                assert virtual instanceof VirtualArrayNode;
+                ResolvedJavaType element = ((VirtualArrayNode) virtual).componentType();
+                if (element.getKind() == Kind.Object) {
+                    newObject = new NewObjectArrayNode(element, ConstantNode.forInt(virtual.entryCount(), fixed.graph()), true, false);
+                } else {
+                    newObject = new NewPrimitiveArrayNode(element, ConstantNode.forInt(virtual.entryCount(), fixed.graph()), true, false);
+                }
+            }
+            newObject.setProbability(fixed.probability());
+            obj.setMaterializedValue(newObject);
+            materializeEffects.addFixedNodeBefore(newObject, fixed);
+        } else {
+            // some entries are not default constants - do the materialization
+            MaterializeObjectNode materialize = new MaterializeObjectNode(virtual, obj.getLockCount());
+            ValueNode[] values = new ValueNode[obj.getEntries().length];
+            materialize.setProbability(fixed.probability());
+            obj.setMaterializedValue(materialize);
+            deferred.add(virtual);
+            for (int i = 0; i < fieldState.length; i++) {
+                ObjectState valueObj = getObjectState(fieldState[i]);
+                if (valueObj != null) {
+                    if (valueObj.isVirtual()) {
+                        materializeChangedBefore(fixed, valueObj.virtual, deferred, deferredStores, materializeEffects);
+                    }
+                    if (deferred.contains(valueObj.virtual)) {
+                        Kind fieldKind;
+                        CyclicMaterializeStoreNode store;
+                        if (virtual instanceof VirtualArrayNode) {
+                            store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), i);
+                            fieldKind = ((VirtualArrayNode) virtual).componentType().getKind();
+                        } else {
+                            VirtualInstanceNode instanceObject = (VirtualInstanceNode) virtual;
+                            store = new CyclicMaterializeStoreNode(materialize, valueObj.getMaterializedValue(), instanceObject.field(i));
+                            fieldKind = instanceObject.field(i).getType().getKind();
+                        }
+                        deferredStores.addFixedNodeBefore(store, fixed);
+                        values[i] = ConstantNode.defaultForKind(fieldKind, fixed.graph());
+                    } else {
+                        values[i] = valueObj.getMaterializedValue();
+                    }
+                } else {
+                    values[i] = fieldState[i];
+                }
+            }
+            deferred.remove(virtual);
+
+            materializeEffects.addMaterialization(materialize, fixed, values);
+        }
     }
 
     void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node, NodeBitMap usages) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Mon Nov 12 17:49:06 2012 +0100
@@ -27,8 +27,8 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.common.*;
 import com.oracle.graal.virtual.nodes.*;
 
 public class GraphEffectList extends EffectList {
@@ -214,31 +214,13 @@
     }
 
     /**
-     * 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() {
-
-            @Override
-            public String name() {
-                return "eliminateMonitor";
-            }
-
-            @Override
-            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
-                assert node.isAlive() && node.object().isAlive() && (node.object() instanceof VirtualObjectNode);
-                node.eliminate();
-            }
-        });
-    }
-
-    /**
-     * Replaces the given node at its usages without deleting it.
+     * Replaces the given node at its usages without deleting it. If the current node is a fixed node it will be
+     * disconnected from the control flow, so that it will be deleted by a subsequent {@link DeadCodeEliminationPhase}
      *
      * @param node The node to be replaced.
-     * @param replacement The node that should replace the original value.
+     * @param replacement The node that should replace the original value. If the replacement is a non-connected
+     *            {@link FixedWithNextNode} it will be added to the control flow.
+     *
      */
     public void replaceAtUsages(final ValueNode node, final ValueNode replacement) {
         add(new Effect() {
@@ -251,7 +233,17 @@
             @Override
             public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
                 assert node.isAlive() && replacement.isAlive();
+                if (replacement instanceof FixedWithNextNode && ((FixedWithNextNode) replacement).next() == null) {
+                    assert node instanceof FixedNode;
+                    graph.addBeforeFixed((FixedNode) node, (FixedWithNextNode) replacement);
+                }
                 node.replaceAtUsages(replacement);
+                if (node instanceof FixedWithNextNode) {
+                    FixedNode next = ((FixedWithNextNode) node).next();
+                    ((FixedWithNextNode) node).setNext(null);
+                    node.replaceAtPredecessor(next);
+                    obsoleteNodes.add(node);
+                }
             }
         });
     }
@@ -263,7 +255,7 @@
      * @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) {
+    public void replaceFirstInput(final Node node, final Node oldInput, final Node newInput) {
         add(new Effect() {
 
             @Override
@@ -283,4 +275,24 @@
             }
         });
     }
+
+    /**
+     * Performs a custom action.
+     *
+     * @param action The action that should be performed when the effects are applied.
+     */
+    public void customAction(final Runnable action) {
+        add(new Effect() {
+
+            @Override
+            public String name() {
+                return "customAction";
+            }
+
+            @Override
+            public void apply(StructuredGraph graph, ArrayList<Node> obsoleteNodes) {
+                action.run();
+            }
+        });
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Mon Nov 12 17:49:06 2012 +0100
@@ -100,12 +100,8 @@
         return lockCount;
     }
 
-    public void incLockCount() {
-        lockCount++;
-    }
-
-    public void decLockCount() {
-        lockCount--;
+    public void setLockCount(int lockCount) {
+        this.lockCount = lockCount;
     }
 
     @Override
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Mon Nov 12 17:49:06 2012 +0100
@@ -157,10 +157,10 @@
         boolean success = true;
         for (Node node : obsoleteNodes) {
             if (flood.isMarked(node)) {
-                trace("offending node path:");
+                error("offending node path:");
                 Node current = node;
                 while (current != null) {
-                    trace(current.toString());
+                    error(current.toString());
                     current = path.get(current);
                     if (current != null && current instanceof FixedNode && !obsoleteNodes.contains(current)) {
                         break;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Mon Nov 12 17:48:51 2012 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Mon Nov 12 17:49:06 2012 +0100
@@ -25,20 +25,19 @@
 import static com.oracle.graal.virtual.phases.ea.PartialEscapeAnalysisPhase.*;
 
 import java.util.*;
+import java.util.concurrent.*;
+import java.util.concurrent.atomic.*;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.meta.ResolvedJavaType.Representation;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
-import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.cfg.*;
-import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.LoopInfo;
@@ -47,21 +46,36 @@
 
 class PartialEscapeClosure extends BlockIteratorClosure<BlockState> {
 
+    public static final ConcurrentHashMap<Class<? extends Node>, AtomicLong> materializeReasons = new ConcurrentHashMap<>();
+
+    static {
+        if (GraalOptions.EscapeAnalysisHistogram) {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    for (Map.Entry<Class<? extends Node>, AtomicLong> entry : materializeReasons.entrySet()) {
+                        System.out.println(entry.getKey() + ": " + entry.getValue());
+                    }
+                }
+            });
+        }
+    }
 
     private static final DebugMetric metricAllocationRemoved = Debug.metric("AllocationRemoved ");
 
+    private final NodeBitMap usages;
+    private final SchedulePhase schedule;
+
     private final GraphEffectList effects = new GraphEffectList();
     private final HashSet<VirtualObjectNode> reusedVirtualObjects = new HashSet<>();
     private int virtualIds = 0;
 
-    private final NodeBitMap usages;
-    private final SchedulePhase schedule;
-    private final MetaAccessProvider runtime;
+    private final VirtualizerToolImpl tool;
 
-    public PartialEscapeClosure(NodeBitMap usages, SchedulePhase schedule, MetaAccessProvider runtime) {
+    public PartialEscapeClosure(NodeBitMap usages, SchedulePhase schedule, MetaAccessProvider metaAccess) {
         this.usages = usages;
         this.schedule = schedule;
-        this.runtime = runtime;
+        tool = new VirtualizerToolImpl(effects, usages, metaAccess);
     }
 
     public GraphEffectList getEffects() {
@@ -118,265 +132,16 @@
         trace(")\n    end state: %s\n", state);
     }
 
+
     private void processNode(final ValueNode node, FixedNode insertBefore, final BlockState state) {
-        boolean usageFound = false;
-        if (node instanceof PiNode || node instanceof ValueProxyNode) {
-            ValueNode value = node instanceof PiNode ? ((PiNode) node).object() : ((ValueProxyNode) node).value();
-            ObjectState obj = state.getObjectState(value);
-            if (obj != null) {
-                if (obj.isVirtual()) {
-                    state.addAndMarkAlias(obj.virtual, node, usages);
-                } else {
-                    effects.replaceFirstInput(node, value, obj.getMaterializedValue());
-                }
-                usageFound = true;
-            }
-        } else if (node instanceof CheckCastNode) {
-            CheckCastNode x = (CheckCastNode) node;
-            ObjectState obj = state.getObjectState(x.object());
-            if (obj != null) {
-                if (obj.isVirtual()) {
-                    if (obj.virtual.type().isSubtypeOf(x.type())) {
-                        state.addAndMarkAlias(obj.virtual, x, usages);
-                        effects.deleteFixedNode(x);
-                    } else {
-                        replaceWithMaterialized(x.object(), x, state, obj);
-                    }
-                } else {
-                    effects.replaceFirstInput(x, x.object(), obj.getMaterializedValue());
-                }
-                usageFound = true;
-            }
-        } else if (node instanceof IsNullNode) {
-            IsNullNode x = (IsNullNode) node;
-            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.getObjectState(x.object());
-            if (obj != null) {
-                Debug.log("monitor operation %s on %s\n", x, obj.virtual);
-                if (node instanceof MonitorEnterNode) {
-                    obj.incLockCount();
-                } else {
-                    assert node instanceof MonitorExitNode;
-                    obj.decLockCount();
-                }
-                if (obj.isVirtual()) {
-                    effects.replaceFirstInput(node, x.object(), obj.virtual);
-                    effects.eliminateMonitor(x);
-                } else {
-                    effects.replaceFirstInput(node, x.object(), obj.getMaterializedValue());
-                }
-                usageFound = true;
-            }
-        } else if (node instanceof CyclicMaterializeStoreNode) {
-            CyclicMaterializeStoreNode x = (CyclicMaterializeStoreNode) node;
-            ObjectState obj = state.getObjectState(x.object());
-            if (obj != null) {
-                if (obj.virtual instanceof VirtualArrayNode) {
-                    obj.setEntry(x.targetIndex(), x.value());
-                } else {
-                    VirtualInstanceNode instance = (VirtualInstanceNode) obj.virtual;
-                    obj.setEntry(instance.fieldIndex(x.targetField()), x.value());
-                }
-                effects.deleteFixedNode(x);
-                usageFound = true;
-            }
-        } else if (node instanceof LoadFieldNode) {
-            LoadFieldNode x = (LoadFieldNode) node;
-            ObjectState obj = state.getObjectState(x.object());
-            if (obj != null) {
-                VirtualInstanceNode virtual = (VirtualInstanceNode) obj.virtual;
-                int fieldIndex = virtual.fieldIndex(x.field());
-                if (fieldIndex == -1) {
-                    // the field does not exist in the virtual object
-                    ensureMaterialized(state, obj, x);
-                }
-                if (obj.isVirtual()) {
-                    ValueNode result = obj.getEntry(fieldIndex);
-                    ObjectState resultObj = state.getObjectState(result);
-                    if (resultObj != null) {
-                        state.addAndMarkAlias(resultObj.virtual, x, usages);
-                    } else {
-                        replaceAtUsages(state, x, result);
-                    }
-                    effects.deleteFixedNode(x);
-                } else {
-                    effects.replaceFirstInput(x, x.object(), obj.getMaterializedValue());
-                }
-                usageFound = true;
-            }
-        } else if (node instanceof StoreFieldNode) {
-            StoreFieldNode x = (StoreFieldNode) node;
-            ValueNode object = x.object();
-            ValueNode value = x.value();
-            ObjectState obj = state.getObjectState(object);
-            if (obj != null) {
-                VirtualInstanceNode virtual = (VirtualInstanceNode) obj.virtual;
-                int fieldIndex = virtual.fieldIndex(x.field());
-                if (fieldIndex == -1) {
-                    // the field does not exist in the virtual object
-                    ensureMaterialized(state, obj, x);
-                }
-                if (obj.isVirtual()) {
-                    obj.setEntry(fieldIndex, state.getScalarAlias(value));
-                    effects.deleteFixedNode(x);
-                } else {
-                    effects.replaceFirstInput(x, object, obj.getMaterializedValue());
-                    ObjectState valueObj = state.getObjectState(value);
-                    if (valueObj != null) {
-                        replaceWithMaterialized(value, x, state, valueObj);
-                    }
-                }
-                usageFound = true;
-            } else {
-                ObjectState valueObj = state.getObjectState(value);
-                if (valueObj != null) {
-                    replaceWithMaterialized(value, x, state, valueObj);
-                    usageFound = true;
-                }
-            }
-        } else if (node instanceof LoadIndexedNode) {
-            LoadIndexedNode x = (LoadIndexedNode) node;
-            ValueNode array = x.array();
-            ObjectState arrayObj = state.getObjectState(array);
-            if (arrayObj != null) {
-                if (arrayObj.isVirtual()) {
-                    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.getObjectState(result);
-                        if (resultObj != null) {
-                            state.addAndMarkAlias(resultObj.virtual, x, usages);
-                        } else {
-                            replaceAtUsages(state, x, result);
-                        }
-                        effects.deleteFixedNode(x);
-                    }
-                } else {
-                    effects.replaceFirstInput(x, array, arrayObj.getMaterializedValue());
-                }
-                usageFound = true;
-            }
-        } else if (node instanceof StoreIndexedNode) {
-            StoreIndexedNode x = (StoreIndexedNode) node;
-            ValueNode array = x.array();
-            ValueNode value = x.value();
-            ObjectState arrayObj = state.getObjectState(array);
-            ObjectState valueObj = state.getObjectState(value);
-
-            if (arrayObj != null) {
-                if (arrayObj.isVirtual()) {
-                    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);
-                        if (valueObj != null) {
-                            replaceWithMaterialized(value, x, state, valueObj);
-                        }
-                    } else {
-                        arrayObj.setEntry(index, state.getScalarAlias(value));
-                        effects.deleteFixedNode(x);
-                    }
-                } else {
-                    effects.replaceFirstInput(x, array, arrayObj.getMaterializedValue());
-                    if (valueObj != null) {
-                        replaceWithMaterialized(value, x, state, valueObj);
-                    }
-                }
-                usageFound = true;
-            } else {
-                if (valueObj != null) {
-                    replaceWithMaterialized(value, x, state, valueObj);
-                    usageFound = true;
-                }
-            }
-        } else if (node instanceof RegisterFinalizerNode) {
-            RegisterFinalizerNode x = (RegisterFinalizerNode) node;
-            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.getObjectState(x.array());
-            if (obj != null) {
-                replaceAtUsages(state, x, ConstantNode.forInt(((VirtualArrayNode) obj.virtual).entryCount(), node.graph()));
-                effects.deleteFixedNode(x);
-                usageFound = true;
-            }
-        } else if (node instanceof LoadHubNode) {
-            LoadHubNode x = (LoadHubNode) node;
-            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);
-                usageFound = true;
-            }
-        } else if (node instanceof ReturnNode) {
-            ReturnNode x = (ReturnNode) node;
-            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.getObjectState(argument);
-                if (obj != null) {
-                    replaceWithMaterialized(argument, node, insertBefore, state, obj);
-                    usageFound = true;
-                }
-            }
-        } else if (node instanceof ObjectEqualsNode) {
-            ObjectEqualsNode x = (ObjectEqualsNode) node;
-            ObjectState xObj = state.getObjectState(x.x());
-            ObjectState yObj = state.getObjectState(x.y());
-            boolean xVirtual = xObj != null && xObj.isVirtual();
-            boolean yVirtual = yObj != null && yObj.isVirtual();
-
-            if (xVirtual ^ yVirtual) {
-                // one of them is virtual: they can never be the same objects
-                replaceAtUsages(state, x, ConstantNode.forBoolean(false, node.graph()));
-                usageFound = true;
-            } else if (xVirtual && yVirtual) {
-                // both are virtual: check if they refer to the same object
-                replaceAtUsages(state, x, ConstantNode.forBoolean(xObj == yObj, node.graph()));
-                usageFound = true;
-            } else {
-                if (xObj != null || yObj != null) {
-                    if (xObj != null) {
-                        assert !xObj.isVirtual();
-                        effects.replaceFirstInput(x, x.x(), xObj.getMaterializedValue());
-                    }
-                    if (yObj != null) {
-                        assert !yObj.isVirtual();
-                        effects.replaceFirstInput(x, x.y(), yObj.getMaterializedValue());
-                    }
-                    usageFound = true;
-                }
-            }
-        } else if (node instanceof MergeNode) {
-            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.getObjectState(input);
-                if (obj != null) {
-                    replaceWithMaterialized(input, node, insertBefore, state, obj);
-                    usageFound = true;
-                }
-            }
+        tool.reset(state, node);
+        if (node instanceof Virtualizable) {
+            ((Virtualizable) node).virtualize(tool);
         }
-        if (node.isAlive() && node instanceof StateSplit) {
+        if (tool.isDeleted()) {
+            return;
+        }
+        if (node instanceof StateSplit) {
             StateSplit split = (StateSplit) node;
             FrameState stateAfter = split.stateAfter();
             if (stateAfter != null) {
@@ -449,25 +214,27 @@
                     effects.addVirtualMapping(stateAfter, v, reusedVirtualObjects);
                 }
             }
-            usageFound = true;
+        }
+        if (tool.isCustomAction()) {
+            return;
         }
-        if (!usageFound) {
-            for (ValueNode input : node.inputs().filter(ValueNode.class)) {
-                ObjectState obj = state.getObjectState(input);
-                if (obj != null) {
-                    replaceWithMaterialized(input, node, insertBefore, state, obj);
-                    usageFound = true;
+        for (ValueNode input : node.inputs().filter(ValueNode.class)) {
+            ObjectState obj = state.getObjectState(input);
+            if (obj != null) {
+                trace("replacing input %s at %s: %s", input, node, obj);
+                if (GraalOptions.EscapeAnalysisHistogram && obj.isVirtual()) {
+                    AtomicLong counter = materializeReasons.get(node.getClass());
+                    if (counter == null) {
+                        counter = new AtomicLong();
+                        materializeReasons.put(node.getClass(), counter);
+                    }
+                    counter.incrementAndGet();
                 }
+                replaceWithMaterialized(input, node, insertBefore, state, obj);
             }
-            Debug.log("unexpected usage of %s: %s\n", node, node.inputs().snapshot());
         }
     }
 
-    private void replaceAtUsages(final BlockState state, ValueNode x, ValueNode value) {
-        effects.replaceAtUsages(x, value);
-        state.addScalarAlias(x, value);
-    }
-
     private void ensureMaterialized(BlockState state, ObjectState obj, FixedNode materializeBefore) {
         assert obj != null;
         if (obj.isVirtual()) {
@@ -476,11 +243,6 @@
         assert !obj.isVirtual();
     }
 
-    private void replaceWithMaterialized(ValueNode value, FixedNode usage, BlockState state, ObjectState obj) {
-        ensureMaterialized(state, obj, usage);
-        effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
-    }
-
     private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockState state, ObjectState obj) {
         ensureMaterialized(state, obj, materializeBefore);
         effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Mon Nov 12 17:49:06 2012 +0100
@@ -0,0 +1,157 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.virtual.phases.ea;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
+
+class VirtualizerToolImpl implements VirtualizerTool {
+
+    private final GraphEffectList effects;
+    private final NodeBitMap usages;
+    private final MetaAccessProvider metaAccess;
+
+    VirtualizerToolImpl(GraphEffectList effects, NodeBitMap usages, MetaAccessProvider metaAccess) {
+        this.effects = effects;
+        this.usages = usages;
+        this.metaAccess = metaAccess;
+    }
+
+    private boolean deleted;
+    private boolean customAction;
+    private BlockState state;
+    private ValueNode current;
+
+    @Override
+    public MetaAccessProvider getMetaAccessProvider() {
+        return metaAccess;
+    }
+
+    public void reset(BlockState newState, ValueNode newCurrent) {
+        deleted = false;
+        customAction = false;
+        this.state = newState;
+        this.current = newCurrent;
+    }
+
+    public boolean isDeleted() {
+        return deleted;
+    }
+
+    public boolean isCustomAction() {
+        return customAction;
+    }
+
+    @Override
+    public VirtualObjectNode getVirtualState(ValueNode value) {
+        ObjectState obj = state.getObjectState(value);
+        return obj != null && obj.isVirtual() ? obj.virtual : null;
+    }
+
+    @Override
+    public ValueNode getVirtualEntry(VirtualObjectNode virtual, int index) {
+        ObjectState obj = state.getObjectState(virtual);
+        assert obj != null && obj.isVirtual() : "not virtual: " + obj;
+        ValueNode result = obj.getEntry(index);
+        ValueNode materialized = getMaterializedValue(result);
+        return materialized != null ? materialized : result;
+    }
+
+    @Override
+    public void setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value) {
+        ObjectState obj = state.getObjectState(virtual);
+        assert obj != null && obj.isVirtual() : "not virtual: " + obj;
+        if (getVirtualState(value) == null) {
+            ValueNode materialized = getMaterializedValue(value);
+            if (materialized != null) {
+                obj.setEntry(index, materialized);
+            } else {
+                obj.setEntry(index, getReplacedValue(value));
+            }
+        } else {
+            obj.setEntry(index, value);
+        }
+    }
+
+    @Override
+    public int getVirtualLockCount(VirtualObjectNode virtual) {
+        ObjectState obj = state.getObjectState(virtual);
+        assert obj != null && obj.isVirtual() : "not virtual: " + obj;
+        return obj.getLockCount();
+    }
+
+    @Override
+    public void setVirtualLockCount(VirtualObjectNode virtual, int lockCount) {
+        ObjectState obj = state.getObjectState(virtual);
+        assert obj != null && obj.isVirtual() : "not virtual: " + obj;
+        obj.setLockCount(lockCount);
+    }
+
+    @Override
+    public ValueNode getMaterializedValue(ValueNode value) {
+        ObjectState obj = state.getObjectState(value);
+        return obj != null && !obj.isVirtual() ? obj.getMaterializedValue() : null;
+    }
+
+    @Override
+    public ValueNode getReplacedValue(ValueNode original) {
+        return state.getScalarAlias(original);
+    }
+
+    @Override
+    public void replaceWithVirtual(VirtualObjectNode virtual) {
+        state.addAndMarkAlias(virtual, current, usages);
+        if (current instanceof FixedWithNextNode) {
+            effects.deleteFixedNode((FixedWithNextNode) current);
+        }
+        deleted = true;
+    }
+
+    @Override
+    public void replaceWithValue(ValueNode replacement) {
+        effects.replaceAtUsages(current, state.getScalarAlias(replacement));
+        state.addScalarAlias(current, replacement);
+        deleted = true;
+    }
+
+    @Override
+    public void delete() {
+        assert current instanceof FixedWithNextNode;
+        effects.deleteFixedNode((FixedWithNextNode) current);
+        deleted = true;
+    }
+
+    @Override
+    public void replaceFirstInput(Node oldInput, Node replacement) {
+        effects.replaceFirstInput(current, oldInput, replacement);
+    }
+
+    @Override
+    public void customAction(Runnable action) {
+        effects.customAction(action);
+        customAction = true;
+    }
+}