changeset 7394:94f032472c28

changed PEA interface: * prepare for more fine-grained object states (thread local, ...) * merge Virtualizable and EscapeAnalyzable interfaces * make MaterializeObjectNode an ArrayLengthProvider
author Lukas Stadler <lukas.stadler@jku.at>
date Wed, 16 Jan 2013 15:15:32 +0100
parents 5f00bf5a530d
children 921bde171d1d
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/BoxNode.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/UnsafeLoadNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.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/InstanceOfNode.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/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/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/EscapeAnalyzable.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/VirtualizableAllocation.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/BoxedVirtualObjectNode.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.common/src/com/oracle/graal/phases/common/BoxingEliminationPhase.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/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 graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java
diffstat 37 files changed, 357 insertions(+), 336 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -25,7 +25,6 @@
 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.
@@ -68,9 +67,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
-            tool.replaceWithVirtual(virtual);
+        State state = tool.getObjectState(object);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            tool.replaceWithVirtual(state.getVirtualObject());
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -28,7 +28,6 @@
 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
@@ -87,9 +86,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(value());
-        if (virtual != null) {
-            tool.replaceWithVirtual(virtual);
+        State state = tool.getObjectState(value);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            tool.replaceWithVirtual(state.getVirtualObject());
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -76,7 +76,7 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        if (tool.getVirtualState(object()) != null || tool.getMaterializedValue(object()) != null) {
+        if (tool.getObjectState(object) != null) {
             tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -26,7 +26,6 @@
 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 implements Virtualizable {
@@ -73,17 +72,17 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtualX = tool.getVirtualState(x());
-        VirtualObjectNode virtualY = tool.getVirtualState(y());
-        boolean xVirtual = virtualX != null;
-        boolean yVirtual = virtualY != null;
+        State stateX = tool.getObjectState(x());
+        State stateY = tool.getObjectState(y());
+        boolean xVirtual = stateX != null && stateX.getState() == EscapeState.Virtual;
+        boolean yVirtual = stateY != null && stateY.getState() == EscapeState.Virtual;
 
         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()));
+            tool.replaceWithValue(ConstantNode.forBoolean(stateX == stateY, graph()));
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -34,8 +34,8 @@
 public final class BoxNode extends AbstractStateSplit implements StateSplit, Node.IterableNodeType, Canonicalizable {
 
     @Input private ValueNode source;
-    private int bci;
-    private Kind sourceKind;
+    private final int bci;
+    private final Kind sourceKind;
 
     public BoxNode(ValueNode value, ResolvedJavaType type, Kind sourceKind, int bci) {
         super(StampFactory.exactNonNull(type));
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -27,7 +27,6 @@
 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.
@@ -76,9 +75,10 @@
 
     @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()));
+        State state = tool.getObjectState(object);
+        if (state != null) {
+            Constant constantHub = state.getVirtualObject().type().getEncoding(Representation.ObjectHub);
+            tool.replaceWithValue(ConstantNode.forConstant(constantHub, tool.getMetaAccessProvider(), graph()));
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeLoadNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeLoadNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -28,7 +28,6 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Load of a value from a location specified as an offset relative to an object.
@@ -55,19 +54,14 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual) {
             ValueNode indexValue = tool.getReplacedValue(offset());
             if (indexValue.isConstant()) {
-                int fieldIndex = virtual.fieldIndexForOffset(indexValue.asConstant().asLong());
-                if (fieldIndex != -1) {
-                    ValueNode result = tool.getVirtualEntry(virtual, fieldIndex);
-                    VirtualObjectNode virtualResult = tool.getVirtualState(result);
-                    if (virtualResult != null) {
-                        tool.replaceWithVirtual(virtualResult);
-                    } else {
-                        tool.replaceWithValue(result);
-                    }
+                long offset = indexValue.asConstant().asLong() + displacement();
+                int entryIndex = state.getVirtualObject().entryIndexForOffset(offset);
+                if (entryIndex != -1 && state.getVirtualObject().entryKind(entryIndex) == accessKind()) {
+                    tool.replaceWith(state.getEntry(entryIndex));
                 }
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -28,7 +28,6 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Store of a value at a location specified as an offset relative to an object.
@@ -74,13 +73,14 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual) {
             ValueNode indexValue = tool.getReplacedValue(offset());
             if (indexValue.isConstant()) {
-                int fieldIndex = virtual.fieldIndexForOffset(indexValue.asConstant().asLong());
-                if (fieldIndex != -1) {
-                    tool.setVirtualEntry(virtual, fieldIndex, value());
+                long offset = indexValue.asConstant().asLong() + displacement();
+                int entryIndex = state.getVirtualObject().entryIndexForOffset(offset);
+                if (entryIndex != -1 && state.getVirtualObject().entryKind(entryIndex) == accessKind()) {
+                    tool.setVirtualEntry(state, entryIndex, value());
                     tool.delete();
                 }
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -96,7 +96,8 @@
             }
         }
         for (ValueNode node : dependencies().nonNull().and(isNotA(BeginNode.class))) {
-            if (tool.getVirtualState(node) == null) {
+            State state = tool.getObjectState(node);
+            if (state == null || state.getState() != EscapeState.Virtual) {
                 return;
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -28,7 +28,6 @@
 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.
@@ -72,12 +71,12 @@
 
     @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);
+        State state = tool.getObjectState(object);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            Debug.log("monitor operation %s on %s\n", this, state);
+            int newLockCount = state.getLockCount() + (this instanceof MonitorEnterNode ? 1 : -1);
+            state.setLockCount(newLockCount);
+            tool.replaceFirstInput(object(), state.getVirtualObject());
             tool.customAction(new Runnable() {
                 @Override
                 public void run() {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -26,7 +26,6 @@
 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.
@@ -71,10 +70,10 @@
 
     @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()));
+        State state = tool.getObjectState(array());
+        if (state != null) {
+            assert state.getVirtualObject().type().isArray();
+            tool.replaceWithValue(ConstantNode.forInt(state.getVirtualObject().entryCount(), graph()));
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -27,7 +27,6 @@
 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.
@@ -100,9 +99,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null && type().isAssignableFrom(virtual.type())) {
-            tool.replaceWithVirtual(virtual);
+        State state = tool.getObjectState(object);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            tool.replaceWithVirtual(state.getVirtualObject());
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -28,7 +28,6 @@
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code InstanceOfNode} represents an instanceof test.
@@ -129,9 +128,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
-            tool.replaceWithValue(ConstantNode.forBoolean(type().isAssignableFrom(virtual.type()), graph()));
+        State state = tool.getObjectState(object);
+        if (state != null) {
+            tool.replaceWithValue(ConstantNode.forBoolean(type().isAssignableFrom(state.getVirtualObject().type()), graph()));
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -73,17 +73,11 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
-            int fieldIndex = ((VirtualInstanceNode) virtual).fieldIndex(field());
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            int fieldIndex = ((VirtualInstanceNode) state.getVirtualObject()).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);
-                }
+                tool.replaceWith(state.getEntry(fieldIndex));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -27,7 +27,6 @@
 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.
@@ -54,18 +53,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtualArray = tool.getVirtualState(array());
-        if (virtualArray != null) {
+        State arrayState = tool.getObjectState(array());
+        if (arrayState != null && arrayState.getState() == EscapeState.Virtual) {
             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);
-                }
+            if (index >= 0 && index < arrayState.getVirtualObject().entryCount()) {
+                tool.replaceWith(arrayState.getEntry(index));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -31,13 +31,12 @@
 /**
  * The {@code NewArrayNode} class is the base of all instructions that allocate arrays.
  */
-public abstract class NewArrayNode extends FixedWithNextNode implements Lowerable, EscapeAnalyzable, ArrayLengthProvider {
+public abstract class NewArrayNode extends FixedWithNextNode implements Lowerable, VirtualizableAllocation, ArrayLengthProvider {
 
     @Input private ValueNode length;
     private final ResolvedJavaType elementType;
     private final boolean fillContents;
 
-    public static final int MaximumEscapeAnalysisArrayLength = 32;
     private final boolean locked;
 
     @Override
@@ -105,19 +104,19 @@
     }
 
     @Override
-    public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) {
+    public void virtualize(VirtualizerTool tool) {
         if (length().asConstant() != null) {
             final int constantLength = length().asConstant().asInt();
-            if (constantLength >= 0 && constantLength < MaximumEscapeAnalysisArrayLength) {
+            if (constantLength >= 0 && constantLength < tool.getMaximumEntryCount()) {
                 ValueNode[] state = new ValueNode[constantLength];
                 ConstantNode defaultForKind = constantLength == 0 ? null : ConstantNode.defaultForKind(elementType().getKind(), graph());
                 for (int i = 0; i < constantLength; i++) {
                     state[i] = defaultForKind;
                 }
-                VirtualObjectNode virtualObject = new VirtualArrayNode(nextVirtualId, elementType, constantLength);
-                return new ObjectDesc[]{new ObjectDesc(virtualObject, state, 0)};
+                VirtualObjectNode virtualObject = new VirtualArrayNode(tool.getNextVirtualId(), elementType, constantLength);
+                tool.createVirtualObject(virtualObject, state, 0);
+                tool.replaceWithVirtual(virtualObject);
             }
         }
-        return null;
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -33,7 +33,7 @@
  * The {@code NewInstanceNode} represents the allocation of an instance class object.
  */
 @NodeInfo(nameTemplate = "New {p#instanceClass/s}")
-public final class NewInstanceNode extends FixedWithNextNode implements EscapeAnalyzable, Lowerable, Node.IterableNodeType {
+public final class NewInstanceNode extends FixedWithNextNode implements VirtualizableAllocation, Lowerable, Node.IterableNodeType {
 
     private final ResolvedJavaType instanceClass;
     private final boolean fillContents;
@@ -82,7 +82,7 @@
     }
 
     @Override
-    public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) {
+    public void virtualize(VirtualizerTool tool) {
         if (instanceClass != null) {
             assert !instanceClass().isArray();
             ResolvedJavaField[] fields = instanceClass().getInstanceFields(true);
@@ -90,9 +90,9 @@
             for (int i = 0; i < state.length; i++) {
                 state[i] = ConstantNode.defaultForKind(fields[i].getType().getKind(), graph());
             }
-            VirtualObjectNode virtualObject = new VirtualInstanceNode(nextVirtualId, instanceClass(), fields);
-            return new ObjectDesc[]{new ObjectDesc(virtualObject, state, 0)};
+            VirtualObjectNode virtualObject = new VirtualInstanceNode(tool.getNextVirtualId(), instanceClass(), fields);
+            tool.createVirtualObject(virtualObject, state, 0);
+            tool.replaceWithVirtual(virtualObject);
         }
-        return null;
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -69,11 +69,11 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
-            int fieldIndex = ((VirtualInstanceNode) virtual).fieldIndex(field());
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            int fieldIndex = ((VirtualInstanceNode) state.getVirtualObject()).fieldIndex(field());
             if (fieldIndex != -1) {
-                tool.setVirtualEntry(virtual, fieldIndex, value());
+                tool.setVirtualEntry(state, fieldIndex, value());
                 tool.delete();
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -26,7 +26,6 @@
 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.
@@ -68,12 +67,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtualArray = tool.getVirtualState(array());
-        if (virtualArray != null) {
+        State arrayState = tool.getObjectState(array());
+        if (arrayState != null && arrayState.getState() == EscapeState.Virtual) {
             ValueNode indexValue = tool.getReplacedValue(index());
             int index = indexValue.isConstant() ? indexValue.asConstant().asInt() : -1;
-            if (index >= 0 && index < virtualArray.entryCount()) {
-                tool.setVirtualEntry(virtualArray, index, value());
+            if (index >= 0 && index < arrayState.getVirtualObject().entryCount()) {
+                tool.setVirtualEntry(arrayState, index, value());
                 tool.delete();
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/EscapeAnalyzable.java	Wed Jan 16 10:19:09 2013 +0100
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,45 +0,0 @@
-/*
- * 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.nodes.*;
-import com.oracle.graal.nodes.virtual.*;
-
-public interface EscapeAnalyzable {
-
-    public static class ObjectDesc {
-
-        public final VirtualObjectNode virtualObject;
-        public final ValueNode[] entryState;
-        public final int lockCount;
-
-        public ObjectDesc(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount) {
-            this.virtualObject = virtualObject;
-            this.entryState = entryState;
-            this.lockCount = lockCount;
-        }
-    }
-
-    ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess);
-}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Wed Jan 16 15:15:32 2013 +0100
@@ -22,12 +22,37 @@
  */
 package com.oracle.graal.nodes.spi;
 
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.virtual.*;
+
 /**
  * This interface allows a node to convey information about what its effect would be if some of its inputs were
- * virtualized.
+ * virtualized. The {@link #virtualize(VirtualizerTool)} method will only be called for nodes that have some interaction
+ * with virtualized nodes. However, the virtualized nodes might have been re-materialized in the meantime.
  */
 public interface Virtualizable {
 
+    public static enum EscapeState {
+        Virtual, ThreadLocal, Global
+    }
+
+    public abstract static class State {
+
+        public abstract EscapeState getState();
+
+        public abstract VirtualObjectNode getVirtualObject();
+
+        public abstract void setEntry(int index, ValueNode value);
+
+        public abstract ValueNode getEntry(int index);
+
+        public abstract int getLockCount();
+
+        public abstract void setLockCount(int lockCount);
+
+        public abstract ValueNode getMaterializedValue();
+    }
+
     /**
      * 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
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizableAllocation.java	Wed Jan 16 15:15:32 2013 +0100
@@ -0,0 +1,36 @@
+/*
+ * 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.
+ *
+ * The difference to {@link Virtualizable} is that the {@link #virtualize(VirtualizerTool)} method will be called
+ * regardless of whether this node had any interaction with virtualized nodes. This interface can therefore be used for
+ * object allocations, for which virtualization introduces new virtualized objects.
+ *
+ */
+public interface VirtualizableAllocation extends Virtualizable {
+
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Wed Jan 16 15:15:32 2013 +0100
@@ -25,6 +25,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.Virtualizable.State;
 import com.oracle.graal.nodes.virtual.*;
 
 /**
@@ -41,50 +42,46 @@
      */
     MetaAccessProvider getMetaAccessProvider();
 
+    /**
+     * This method should be used to query the maximum size of virtualized objects before attempting virtualization.
+     *
+     * @return the maximum number of entries for virtualized objects.
+     */
+    int getMaximumEntryCount();
+
+    /**
+     * @return the next id for virtual objects (can be used for the VirtualObject constructor).
+     */
+    int getNextVirtualId();
+
     // methods working on virtualized/materialized objects
 
     /**
+     * Introduces a new virtual object to the current state.
+     *
+     * @param virtualObject the new virtual object.
+     * @param entryState the initial state of the virtual object's fields.
+     * @param lockCount the initial locking depth.
+     */
+    void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount);
+
+    /**
      * 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.
+     * @return the {@link State} representing the value if it has been virtualized at some point, 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);
+    State getObjectState(ValueNode value);
 
     /**
      * Sets the entry (field or array element) with the given index in the virtualized object.
      *
-     * @param virtual the virtualized object.
+     * @param state the state.
      * @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);
+    void setVirtualEntry(State state, int index, ValueNode value);
 
     /**
      * Queries the current state of the given value: if it was materialized or not.
@@ -142,4 +139,12 @@
      * @param action the custom action.
      */
     void customAction(Runnable action);
+
+    /**
+     * This method performs either {@link #replaceWithValue(ValueNode)} or
+     * {@link #replaceWithVirtual(VirtualObjectNode)}, depending on the given value.
+     *
+     * @param value the replacement value
+     */
+    void replaceWith(ValueNode value);
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/BoxedVirtualObjectNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/BoxedVirtualObjectNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -32,10 +32,12 @@
 
     @Input ValueNode unboxedValue;
     private final ResolvedJavaType type;
+    private final Kind kind;
 
-    public BoxedVirtualObjectNode(int virtualId, ResolvedJavaType type, ValueNode unboxedValue) {
+    public BoxedVirtualObjectNode(int virtualId, ResolvedJavaType type, Kind kind, ValueNode unboxedValue) {
         super(virtualId);
         this.type = type;
+        this.kind = kind;
         this.unboxedValue = unboxedValue;
     }
 
@@ -60,8 +62,13 @@
     }
 
     @Override
-    public int fieldIndexForOffset(long constantOffset) {
+    public int entryIndexForOffset(long constantOffset) {
         // (lstadler) unsafe access to a newly created boxing object should only ever touch the value field
         return 0;
     }
+
+    @Override
+    public Kind entryKind(int index) {
+        return kind;
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -74,7 +74,7 @@
     }
 
     @Override
-    public int fieldIndexForOffset(long constantOffset) {
+    public int entryIndexForOffset(long constantOffset) {
         int baseOffset;
         int indexScale;
         switch (componentType.getKind()) {
@@ -127,4 +127,10 @@
         }
         return (int) elementIndex;
     }
+
+    @Override
+    public Kind entryKind(int index) {
+        assert index >= 0 && index < length;
+        return componentType.getKind();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -77,7 +77,13 @@
     }
 
     @Override
-    public int fieldIndexForOffset(long constantOffset) {
+    public int entryIndexForOffset(long constantOffset) {
         return fieldIndex(type.findInstanceFieldWithOffset(constantOffset));
     }
+
+    @Override
+    public Kind entryKind(int index) {
+        assert index >= 0 && index < fields.length;
+        return fields[index].getKind();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -58,5 +58,7 @@
         // nothing to do in here - this method allows subclasses to respond to materialization
     }
 
-    public abstract int fieldIndexForOffset(long constantOffset);
+    public abstract int entryIndexForOffset(long constantOffset);
+
+    public abstract Kind entryKind(int index);
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/BoxingEliminationPhase.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/BoxingEliminationPhase.java	Wed Jan 16 15:15:32 2013 +0100
@@ -80,7 +80,7 @@
                     StructuredGraph graph = (StructuredGraph) phiNode.graph();
                     result = graph.add(new PhiNode(kind, phiNode.merge()));
                     phiReplacements.put(phiNode, result);
-                    virtualizeUsages(phiNode, result, type);
+                    virtualizeUsages(phiNode, result, type, kind);
                     int i = 0;
                     for (ValueNode n : phiNode.values()) {
                         ValueNode unboxedValue = unboxedValue(n, kind, phiReplacements);
@@ -116,7 +116,7 @@
     private void tryEliminate(BoxNode boxNode) {
 
         assert boxNode.objectStamp().isExactType();
-        virtualizeUsages(boxNode, boxNode.source(), boxNode.objectStamp().type());
+        virtualizeUsages(boxNode, boxNode.source(), boxNode.objectStamp().type(), boxNode.getSourceKind());
 
         if (boxNode.usages().filter(isNotA(VirtualState.class)).isNotEmpty()) {
             // Elimination failed, because boxing object escapes.
@@ -130,12 +130,12 @@
         ((StructuredGraph) boxNode.graph()).removeFixed(boxNode);
     }
 
-    private void virtualizeUsages(ValueNode boxNode, ValueNode replacement, ResolvedJavaType exactType) {
+    private void virtualizeUsages(ValueNode boxNode, ValueNode replacement, ResolvedJavaType exactType, Kind sourceKind) {
         ValueNode virtualValueNode = null;
         VirtualObjectNode virtualObjectNode = null;
         for (Node n : boxNode.usages().filter(NodePredicates.isA(VirtualState.class)).snapshot()) {
             if (virtualValueNode == null) {
-                virtualObjectNode = n.graph().unique(new BoxedVirtualObjectNode(virtualIds++, exactType, replacement));
+                virtualObjectNode = n.graph().unique(new BoxedVirtualObjectNode(virtualIds++, exactType, sourceKind, replacement));
             }
             n.replaceFirstInput(boxNode, virtualObjectNode);
         }
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Wed Jan 16 15:15:32 2013 +0100
@@ -74,6 +74,7 @@
     public static boolean EscapeAnalysisHistogram            = ____;
     public static int     EscapeAnalysisIterations           = 2;
     public static String  EscapeAnalyzeOnly                  = null;
+    public static int     MaximumEscapeAnalysisArrayLength   = 32;
 
     public static double  TailDuplicationProbability         = 0.5;
     public static int     TailDuplicationTrivialSize         = 1;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/CyclicMaterializeStoreNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/CyclicMaterializeStoreNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -86,12 +86,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        VirtualObjectNode virtual = tool.getVirtualState(object());
-        if (virtual != null) {
-            if (virtual instanceof VirtualArrayNode) {
-                tool.setVirtualEntry(virtual, targetIndex(), value());
+        State state = tool.getObjectState(object);
+        if (state != null && state.getState() == EscapeState.Virtual) {
+            if (state.getVirtualObject() instanceof VirtualArrayNode) {
+                tool.setVirtualEntry(state, targetIndex(), value());
             } else {
-                tool.setVirtualEntry(virtual, ((VirtualInstanceNode) virtual).fieldIndex(targetField()), value());
+                tool.setVirtualEntry(state, ((VirtualInstanceNode) state.getVirtualObject()).fieldIndex(targetField()), value());
             }
             tool.delete();
         }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/nodes/MaterializeObjectNode.java	Wed Jan 16 15:15:32 2013 +0100
@@ -31,7 +31,7 @@
 import com.oracle.graal.nodes.virtual.*;
 
 @NodeInfo(nameTemplate = "Materialize {i#virtualObject}")
-public final class MaterializeObjectNode extends FixedWithNextNode implements EscapeAnalyzable, Lowerable, Node.IterableNodeType, Canonicalizable {
+public final class MaterializeObjectNode extends FixedWithNextNode implements VirtualizableAllocation, Lowerable, Node.IterableNodeType, Canonicalizable, ArrayLengthProvider {
 
     @Input private final NodeInputList<ValueNode> values;
     @Input private final VirtualObjectNode virtualObject;
@@ -49,6 +49,12 @@
     }
 
     @Override
+    public ValueNode length() {
+        assert virtualObject.type().isArray();
+        return ConstantNode.forInt(values.size(), graph());
+    }
+
+    @Override
     public void lower(LoweringTool tool) {
         StructuredGraph graph = (StructuredGraph) graph();
         if (virtualObject instanceof VirtualInstanceNode) {
@@ -101,7 +107,8 @@
     }
 
     @Override
-    public ObjectDesc[] getAllocations(long nextVirtualId, MetaAccessProvider metaAccess) {
-        return new ObjectDesc[] {new ObjectDesc(virtualObject, values.toArray(new ValueNode[values.size()]), lockCount)};
+    public void virtualize(VirtualizerTool tool) {
+        tool.createVirtualObject(virtualObject, values.toArray(new ValueNode[values.size()]), lockCount);
+        tool.replaceWithVirtual(virtualObject);
     }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Wed Jan 16 15:15:32 2013 +0100
@@ -31,6 +31,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.spi.Virtualizable.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.MergeableBlockState;
 import com.oracle.graal.virtual.nodes.*;
@@ -75,14 +76,15 @@
         return new BlockState(this);
     }
 
-    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
+    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, GraphEffectList materializeEffects) {
+        PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
         HashSet<VirtualObjectNode> deferred = new HashSet<>();
         GraphEffectList deferredStores = new GraphEffectList();
-        materializeChangedBefore(fixed, virtual, deferred, deferredStores, materializeEffects);
+        materializeChangedBefore(fixed, virtual, state, deferred, deferredStores, materializeEffects);
         materializeEffects.addAll(deferredStores);
     }
 
-    private void materializeChangedBefore(FixedNode fixed, VirtualObjectNode virtual, HashSet<VirtualObjectNode> deferred, GraphEffectList deferredStores, GraphEffectList materializeEffects) {
+    private void materializeChangedBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, HashSet<VirtualObjectNode> deferred, GraphEffectList deferredStores, GraphEffectList materializeEffects) {
         trace("materializing %s at %s", virtual, fixed);
         ObjectState obj = getObjectState(virtual);
         if (obj.getLockCount() > 0 && obj.virtual.type().isArray()) {
@@ -115,7 +117,7 @@
                 }
             }
             newObject.setProbability(fixed.probability());
-            obj.setMaterializedValue(newObject);
+            obj.escape(newObject, state);
             materializeEffects.addFixedNodeBefore(newObject, fixed);
         } else {
             // some entries are not default constants - do the materialization
@@ -123,13 +125,13 @@
             MaterializeObjectNode materialize = new MaterializeObjectNode(virtual, obj.getLockCount());
             ValueNode[] values = new ValueNode[obj.getEntries().length];
             materialize.setProbability(fixed.probability());
-            obj.setMaterializedValue(materialize);
+            obj.escape(materialize, state);
             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);
+                        materializeChangedBefore(fixed, valueObj.virtual, state, deferred, deferredStores, materializeEffects);
                     }
                     if (deferred.contains(valueObj.virtual)) {
                         Kind fieldKind;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Wed Jan 16 15:15:32 2013 +0100
@@ -156,9 +156,8 @@
      *
      * @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) {
+    public void addVirtualMapping(final FrameState node, final EscapeObjectState state) {
         add(new Effect() {
 
             @Override
@@ -172,11 +171,7 @@
                 FrameState stateAfter = node;
                 for (int i = 0; i < stateAfter.virtualObjectMappingCount(); i++) {
                     if (stateAfter.virtualObjectMappingAt(i).object() == state.object()) {
-                        if (reusedVirtualObjects.contains(state.object())) {
-                            stateAfter.virtualObjectMappings().remove(i);
-                        } else {
-                            throw new GraalInternalError("unexpected duplicate virtual state at: %s for %s", stateAfter, state.object());
-                        }
+                        stateAfter.virtualObjectMappings().remove(i);
                     }
                 }
                 stateAfter.addVirtualObjectMapping(graph.add(state));
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Wed Jan 16 15:15:32 2013 +0100
@@ -23,6 +23,8 @@
 package com.oracle.graal.virtual.phases.ea;
 
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 
 /**
@@ -30,22 +32,26 @@
  * It describes the fields or array elements (called "entries") and the lock count if the object is still virtual.
  * If the object was materialized, it contains the current materialized value.
  */
-class ObjectState {
+class ObjectState extends Virtualizable.State {
 
     public final VirtualObjectNode virtual;
+
+    private EscapeState state;
     private ValueNode[] entries;
     private ValueNode materializedValue;
     private int lockCount;
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, int lockCount) {
+    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, EscapeState state, int lockCount) {
         this.virtual = virtual;
         this.entries = entries;
+        this.state = state;
         this.lockCount = lockCount;
     }
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode materializedValue, int lockCount) {
+    public ObjectState(VirtualObjectNode virtual, ValueNode materializedValue, EscapeState state, int lockCount) {
         this.virtual = virtual;
         this.materializedValue = materializedValue;
+        this.state = state;
         this.lockCount = lockCount;
     }
 
@@ -54,41 +60,56 @@
         entries = other.entries == null ? null : other.entries.clone();
         materializedValue = other.materializedValue;
         lockCount = other.lockCount;
+        state = other.state;
     }
 
     public ObjectState cloneState() {
         return new ObjectState(this);
     }
 
+    @Override
+    public EscapeState getState() {
+        return state;
+    }
+
+    @Override
+    public VirtualObjectNode getVirtualObject() {
+        return virtual;
+    }
+
     public boolean isVirtual() {
-        assert (entries == null) ^ (materializedValue == null);
-        return materializedValue == null;
+        return state == EscapeState.Virtual;
     }
 
     public ValueNode[] getEntries() {
-        assert isVirtual();
+        assert isVirtual() && entries != null;
         return entries;
     }
 
+    @Override
     public ValueNode getEntry(int index) {
         assert isVirtual();
         return entries[index];
     }
 
+    @Override
     public void setEntry(int index, ValueNode value) {
         assert isVirtual();
         entries[index] = value;
     }
 
-    public ValueNode getMaterializedValue() {
+    public void escape(ValueNode materialized, EscapeState newState) {
+        assert state == EscapeState.Virtual || (state == EscapeState.ThreadLocal && newState == EscapeState.Global);
+        state = newState;
+        materializedValue = materialized;
+        entries = null;
         assert !isVirtual();
-        return materializedValue;
     }
 
-    public void setMaterializedValue(ValueNode value) {
-        assert isVirtual();
-        materializedValue = value;
-        entries = null;
+    @Override
+    public ValueNode getMaterializedValue() {
+        assert state == EscapeState.ThreadLocal || state == EscapeState.Global;
+        return materializedValue;
     }
 
     public void updateMaterializedValue(ValueNode value) {
@@ -96,10 +117,12 @@
         materializedValue = value;
     }
 
+    @Override
     public int getLockCount() {
         return lockCount;
     }
 
+    @Override
     public void setLockCount(int lockCount) {
         this.lockCount = lockCount;
     }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeAnalysisPhase.java	Wed Jan 16 15:15:32 2013 +0100
@@ -33,7 +33,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
-import com.oracle.graal.phases.common.CanonicalizerPhase.*;
+import com.oracle.graal.phases.common.CanonicalizerPhase.CustomCanonicalizer;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
 import com.oracle.graal.virtual.phases.ea.EffectList.Effect;
@@ -75,7 +75,7 @@
 
         boolean analyzableNodes = false;
         for (Node node : graph.getNodes()) {
-            if (node instanceof EscapeAnalyzable) {
+            if (node instanceof VirtualizableAllocation) {
                 analyzableNodes = true;
                 break;
             }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Wed Jan 16 15:15:32 2013 +0100
@@ -25,8 +25,6 @@
 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.debug.*;
@@ -36,9 +34,8 @@
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.spi.EscapeAnalyzable.ObjectDesc;
+import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 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,37 +44,24 @@
 
 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 ");
+    public static final DebugMetric METRIC_MATERIALIZATIONS = Debug.metric("Materializations");
+    public static final DebugMetric METRIC_MATERIALIZATIONS_PHI = Debug.metric("MaterializationsPhi");
+    public static final DebugMetric METRIC_MATERIALIZATIONS_MERGE = Debug.metric("MaterializationsMerge");
+    public static final DebugMetric METRIC_MATERIALIZATIONS_UNHANDLED = Debug.metric("MaterializationsUnhandled");
+    public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_REITERATION = Debug.metric("MaterializationsLoopReiteration");
+    public static final DebugMetric METRIC_MATERIALIZATIONS_LOOP_END = Debug.metric("MaterializationsLoopEnd");
+    public static final DebugMetric METRIC_ALLOCATION_REMOVED = 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 VirtualizerToolImpl tool;
-    private final MetaAccessProvider metaAccess;
 
     public PartialEscapeClosure(NodeBitMap usages, SchedulePhase schedule, MetaAccessProvider metaAccess) {
         this.usages = usages;
         this.schedule = schedule;
-        this.metaAccess = metaAccess;
         tool = new VirtualizerToolImpl(effects, usages, metaAccess);
     }
 
@@ -86,7 +70,7 @@
     }
 
     public int getVirtualIdCount() {
-        return virtualIds;
+        return tool.getNextVirtualId();
     }
 
     @Override
@@ -96,39 +80,11 @@
 
         FixedWithNextNode lastFixedNode = null;
         for (Node node : nodeList) {
-            ObjectDesc[] newAllocations = null;
-            if (node instanceof EscapeAnalyzable) {
-                newAllocations = ((EscapeAnalyzable) node).getAllocations(virtualIds, metaAccess);
-            }
-
-            if (newAllocations != null) {
-                trace("{{%s}} ", node);
-                for (ObjectDesc desc : newAllocations) {
-                    VirtualObjectNode virtualObject = desc.virtualObject;
-                    if (virtualObject.isAlive()) {
-                        reusedVirtualObjects.add(virtualObject);
-                        state.addAndMarkAlias(virtualObject, virtualObject, usages);
-                    } else {
-                        effects.addFloatingNode(virtualObject);
-                    }
-                    ValueNode[] fieldState = desc.entryState;
-                    for (int i = 0; i < fieldState.length; i++) {
-                        fieldState[i] = state.getScalarAlias(fieldState[i]);
-                    }
-                    state.addObject(virtualObject, new ObjectState(virtualObject, fieldState, desc.lockCount));
-                    state.addAndMarkAlias(virtualObject, virtualObject, usages);
-                }
-                state.addAndMarkAlias(newAllocations[0].virtualObject, (ValueNode) node, usages);
-                effects.deleteFixedNode((FixedWithNextNode) node);
-                metricAllocationRemoved.add(newAllocations.length);
-                virtualIds += newAllocations.length;
+            if (usages.isMarked(node) || node instanceof VirtualizableAllocation) {
+                trace("[[%s]] ", node);
+                processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state);
             } else {
-                if (usages.isMarked(node)) {
-                    trace("[[%s]] ", node);
-                    processNode((ValueNode) node, lastFixedNode == null ? null : lastFixedNode.next(), state);
-                } else {
-                    trace("%s ", node);
-                }
+                trace("%s ", node);
             }
 
             if (node instanceof FixedWithNextNode && node.isAlive()) {
@@ -138,7 +94,6 @@
         trace(")\n    end state: %s\n", state);
     }
 
-
     private void processNode(final ValueNode node, FixedNode insertBefore, final BlockState state) {
         tool.reset(state, node);
         if (node instanceof Virtualizable) {
@@ -217,7 +172,7 @@
                     } else {
                         v = new MaterializedObjectState(obj.virtual, obj.getMaterializedValue());
                     }
-                    effects.addVirtualMapping(stateAfter, v, reusedVirtualObjects);
+                    effects.addVirtualMapping(stateAfter, v);
                 }
             }
         }
@@ -228,29 +183,24 @@
             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);
+                replaceWithMaterialized(input, node, insertBefore, state, obj, METRIC_MATERIALIZATIONS_UNHANDLED);
             }
         }
     }
 
-    private void ensureMaterialized(BlockState state, ObjectState obj, FixedNode materializeBefore) {
+    private void ensureMaterialized(BlockState state, ObjectState obj, FixedNode materializeBefore, DebugMetric metric) {
         assert obj != null;
-        if (obj.isVirtual()) {
-            state.materializeBefore(materializeBefore, obj.virtual, effects);
+        if (obj.getState() == EscapeState.Virtual) {
+            metric.increment();
+            state.materializeBefore(materializeBefore, obj.virtual, EscapeState.Global, effects);
+        } else {
+            assert obj.getState() == EscapeState.Global;
         }
         assert !obj.isVirtual();
     }
 
-    private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockState state, ObjectState obj) {
-        ensureMaterialized(state, obj, materializeBefore);
+    private void replaceWithMaterialized(ValueNode value, Node usage, FixedNode materializeBefore, BlockState state, ObjectState obj, DebugMetric metric) {
+        ensureMaterialized(state, obj, materializeBefore, metric);
         effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
     }
 
@@ -298,12 +248,12 @@
                                 BlockState state = states.get(i);
                                 ObjectState obj = state.getObjectState(object);
                                 materialized |= obj.isVirtual();
-                                ensureMaterialized(state, obj, merge.forwardEndAt(i));
+                                ensureMaterialized(state, obj, merge.forwardEndAt(i), METRIC_MATERIALIZATIONS_MERGE);
                                 effects.addPhiInput(materializedValuePhi, obj.getMaterializedValue());
                             }
-                            newState.addObject(object, new ObjectState(object, materializedValuePhi, lockCount));
+                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Global, lockCount));
                         } else {
-                            newState.addObject(object, new ObjectState(object, singleValue, lockCount));
+                            newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Global, lockCount));
                         }
                     } else {
                         assert virtual == states.size();
@@ -330,7 +280,7 @@
                                         ObjectState obj = state.getObjectState(fields[index]);
                                         if (obj != null) {
                                             materialized |= obj.isVirtual();
-                                            ensureMaterialized(state, obj, merge.forwardEndAt(i));
+                                            ensureMaterialized(state, obj, merge.forwardEndAt(i), METRIC_MATERIALIZATIONS_MERGE);
                                             fields[index] = obj.getMaterializedValue();
                                         }
                                         effects.addPhiInput(phis[index], fields[index]);
@@ -343,7 +293,7 @@
                                 }
                             }
                         }
-                        newState.addObject(object, new ObjectState(object, values, lockCount));
+                        newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, lockCount));
                     }
                 }
             }
@@ -413,7 +363,7 @@
                 ObjectState obj = states.get(i).getObjectState(value);
                 if (obj != null) {
                     materialized |= obj.isVirtual();
-                    replaceWithMaterialized(value, phi, merge.forwardEndAt(i), states.get(i), obj);
+                    replaceWithMaterialized(value, phi, merge.forwardEndAt(i), states.get(i), obj, METRIC_MATERIALIZATIONS_PHI);
                 }
             }
         }
@@ -492,7 +442,10 @@
                 for (VirtualObjectNode virtualObject : additionalMaterializations) {
                     ObjectState obj = initialState.getObjectState(virtualObject);
                     if (obj.isVirtual()) {
-                        initialState.materializeBefore(loop.loopBegin().forwardEnd(), virtualObject, effects);
+                        METRIC_MATERIALIZATIONS_LOOP_REITERATION.increment();
+                        initialState.materializeBefore(loop.loopBegin().forwardEnd(), virtualObject, EscapeState.Global, effects);
+                    } else {
+                        assert obj.getState() == EscapeState.Global;
                     }
                 }
             }
@@ -604,7 +557,8 @@
                                 if (value instanceof PhiNode && ((PhiNode) value).merge() == loopBegin) {
                                     if (endValueObj != null) {
                                         if (endValueObj.isVirtual()) {
-                                            loopEndState.materializeBefore(loopEnd, endValueObj.virtual, successEffects);
+                                            METRIC_MATERIALIZATIONS_LOOP_END.increment();
+                                            loopEndState.materializeBefore(loopEnd, endValueObj.virtual, EscapeState.Global, successEffects);
                                             materialized = true;
                                         }
                                     }
@@ -631,7 +585,7 @@
                         }
                     } else {
                         if (initialMaterialized) {
-                            loopEndState.materializeBefore(loopEnd, loopEndObj.virtual, successEffects);
+                            loopEndState.materializeBefore(loopEnd, loopEndObj.virtual, EscapeState.Global, successEffects);
                             materialized = true;
                         } else {
                             if (loopEndObj.virtual != initialObj.virtual) {
@@ -663,7 +617,8 @@
                                     // good.
                                 }
                             } else {
-                                if ((endValueObj != null && endValueObj.getMaterializedValue() != valueObj.getMaterializedValue()) || (endValueObj == null && valueObj.getMaterializedValue() != endValue)) {
+                                if ((endValueObj != null && endValueObj.getMaterializedValue() != valueObj.getMaterializedValue()) ||
+                                                (endValueObj == null && valueObj.getMaterializedValue() != endValue)) {
                                     phis.add(new PhiDesc(state.virtual, i));
                                 } else {
                                     // either endValue has the same materialized value as value or endValue is the
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Wed Jan 16 10:19:09 2013 +0100
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Wed Jan 16 15:15:32 2013 +0100
@@ -22,11 +22,16 @@
  */
 package com.oracle.graal.virtual.phases.ea;
 
+import static com.oracle.graal.virtual.phases.ea.PartialEscapeAnalysisPhase.*;
+
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
+import com.oracle.graal.nodes.spi.Virtualizable.State;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.phases.*;
 
 class VirtualizerToolImpl implements VirtualizerTool {
 
@@ -44,6 +49,7 @@
     private boolean customAction;
     private BlockState state;
     private ValueNode current;
+    private int virtualIds = 0;
 
     @Override
     public MetaAccessProvider getMetaAccessProvider() {
@@ -66,51 +72,32 @@
     }
 
     @Override
-    public VirtualObjectNode getVirtualState(ValueNode value) {
-        ObjectState obj = state.getObjectState(value);
-        return obj != null && obj.isVirtual() ? obj.virtual : null;
+    public int getNextVirtualId() {
+        return virtualIds;
     }
 
     @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;
+    public State getObjectState(ValueNode value) {
+        return state.getObjectState(value);
     }
 
     @Override
-    public void setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value) {
-        ObjectState obj = state.getObjectState(virtual);
+    public void setVirtualEntry(State objectState, int index, ValueNode value) {
+        ObjectState obj = (ObjectState) objectState;
         assert obj != null && obj.isVirtual() : "not virtual: " + obj;
-        if (getVirtualState(value) == null) {
-            ValueNode materialized = getMaterializedValue(value);
-            if (materialized != null) {
-                obj.setEntry(index, materialized);
+        ObjectState valueState = state.getObjectState(value);
+        if (valueState == null) {
+            obj.setEntry(index, getReplacedValue(value));
+        } else {
+            if (valueState.getState() == EscapeState.Virtual) {
+                obj.setEntry(index, value);
             } else {
-                obj.setEntry(index, getReplacedValue(value));
+                obj.setEntry(index, valueState.getMaterializedValue());
             }
-        } 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;
@@ -154,4 +141,40 @@
         effects.customAction(action);
         customAction = true;
     }
+
+    @Override
+    public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount) {
+        trace("{{%s}} ", current);
+        if (virtualObject.isAlive()) {
+            state.addAndMarkAlias(virtualObject, virtualObject, usages);
+        } else {
+            effects.addFloatingNode(virtualObject);
+        }
+        for (int i = 0; i < entryState.length; i++) {
+            entryState[i] = state.getScalarAlias(entryState[i]);
+        }
+        state.addObject(virtualObject, new ObjectState(virtualObject, entryState, EscapeState.Virtual, lockCount));
+        state.addAndMarkAlias(virtualObject, virtualObject, usages);
+        PartialEscapeClosure.METRIC_ALLOCATION_REMOVED.increment();
+        virtualIds++;
+    }
+
+    @Override
+    public int getMaximumEntryCount() {
+        return GraalOptions.MaximumEscapeAnalysisArrayLength;
+    }
+
+    @Override
+    public void replaceWith(ValueNode node) {
+        State resultState = getObjectState(node);
+        if (resultState == null) {
+            replaceWithValue(node);
+        } else {
+            if (resultState.getState() == EscapeState.Virtual) {
+                replaceWithVirtual(resultState.getVirtualObject());
+            } else {
+                replaceWithValue(resultState.getMaterializedValue());
+            }
+        }
+    }
 }