changeset 22353:681c04ce9db2

PEA refactoring: simplify VirtualizerTool, arrays instead of HashMaps, route all modifications through BlockState, copy-on-write in BlockState
author Lukas Stadler <lukas.stadler@oracle.com>
date Fri, 24 Jul 2015 16:20:56 +0200
parents 17b96d2fe8d6
children 06a9e6737dcf 143099e04cfa
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java 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/debug/WeakCounterNode.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/GetClassNode.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/UnboxNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeCastNode.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/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/MonitorEnterNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.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/RawMonitorEnterNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RegisterFinalizerNode.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/java/TypeCheckNode.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.nodes/src/com/oracle/graal/nodes/virtual/EnsureVirtualizedNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicArrayCopyNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicObjectCloneNode.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.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/PartialEscapeBlockState.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/PartialEscapePhase.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java
diffstat 43 files changed, 807 insertions(+), 607 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Fri Jul 24 16:20:56 2015 +0200
@@ -464,7 +464,7 @@
                         /*
                          * At least one array is of a known type requiring no store checks, so
                          * assume the other is of the same type. Generally this is working around
-                         * deficiencies in our propation of type information.
+                         * deficiencies in our propagation of type information.
                          */
                         componentKind = predictedKind;
                         if (predictedKind == Kind.Object) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GuardedValueNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * A node that changes the type of its input, usually narrowing it. For example, a GuardedValueNode
@@ -72,9 +73,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            tool.replaceWithVirtual(state.getVirtualObject());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            tool.replaceWithVirtual((VirtualObjectNode) alias);
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PiNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.graal.nodes.extended.*;
 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 {@link PiNode}
@@ -90,9 +91,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual && StampTool.typeOrNull(this) != null && StampTool.typeOrNull(this).isAssignableFrom(state.getVirtualObject().type())) {
-            tool.replaceWithVirtual(state.getVirtualObject());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (StampTool.typeOrNull(this) != null && StampTool.typeOrNull(this).isAssignableFrom(virtual.type())) {
+                tool.replaceWithVirtual(virtual);
+            }
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ValueProxyNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -26,6 +26,7 @@
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 @NodeInfo(nameTemplate = "Proxy({i#value})")
 public final class ValueProxyNode extends ProxyNode implements Canonicalizable, Virtualizable, ValueProxy {
@@ -58,9 +59,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(value);
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            tool.replaceWithVirtual(state.getVirtualObject());
+        ValueNode alias = tool.getAlias(value);
+        if (alias instanceof VirtualObjectNode) {
+            tool.replaceWithVirtual((VirtualObjectNode) alias);
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IsNullNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -68,8 +68,10 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        if (tool.getObjectState(getValue()) != null) {
-            tool.replaceWithValue(LogicConstantNode.contradiction(graph()));
+        ValueNode alias = tool.getAlias(getValue());
+        TriState fold = tryFold(alias.stamp());
+        if (fold != TriState.UNKNOWN) {
+            tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph()));
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ObjectEqualsNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -34,6 +34,7 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 @NodeInfo(shortName = "==")
 public final class ObjectEqualsNode extends PointerEqualsNode implements Virtualizable {
@@ -71,13 +72,13 @@
         return super.canonicalizeSymmetricConstant(tool, constant, nonConstant, mirrored);
     }
 
-    private void virtualizeNonVirtualComparison(State state, ValueNode other, VirtualizerTool tool) {
-        if (!state.getVirtualObject().hasIdentity() && state.getVirtualObject().entryKind(0) == Kind.Boolean) {
+    private void virtualizeNonVirtualComparison(VirtualObjectNode virtual, ValueNode other, VirtualizerTool tool) {
+        if (!virtual.hasIdentity() && virtual.entryKind(0) == Kind.Boolean) {
             if (other.isConstant()) {
                 JavaConstant otherUnboxed = tool.getConstantReflectionProvider().unboxPrimitive(other.asJavaConstant());
                 if (otherUnboxed != null && otherUnboxed.getKind() == Kind.Boolean) {
                     int expectedValue = otherUnboxed.asBoolean() ? 1 : 0;
-                    IntegerEqualsNode equals = new IntegerEqualsNode(state.getEntry(0), ConstantNode.forInt(expectedValue, graph()));
+                    IntegerEqualsNode equals = new IntegerEqualsNode(tool.getEntry(virtual, 0), ConstantNode.forInt(expectedValue, graph()));
                     tool.addNode(equals);
                     tool.replaceWithValue(equals);
                 } else {
@@ -92,43 +93,42 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State stateX = tool.getObjectState(getX());
-        State stateY = tool.getObjectState(getY());
-        boolean xVirtual = stateX != null && stateX.getState() == EscapeState.Virtual;
-        boolean yVirtual = stateY != null && stateY.getState() == EscapeState.Virtual;
+        ValueNode xAlias = tool.getAlias(getX());
+        ValueNode yAlias = tool.getAlias(getY());
 
-        if (xVirtual && !yVirtual) {
-            virtualizeNonVirtualComparison(stateX, stateY != null ? stateY.getMaterializedValue() : getY(), tool);
-        } else if (!xVirtual && yVirtual) {
-            virtualizeNonVirtualComparison(stateY, stateX != null ? stateX.getMaterializedValue() : getX(), tool);
-        } else if (xVirtual && yVirtual) {
-            boolean xIdentity = stateX.getVirtualObject().hasIdentity();
-            boolean yIdentity = stateY.getVirtualObject().hasIdentity();
-            if (xIdentity ^ yIdentity) {
+        VirtualObjectNode xVirtual = xAlias instanceof VirtualObjectNode ? (VirtualObjectNode) xAlias : null;
+        VirtualObjectNode yVirtual = yAlias instanceof VirtualObjectNode ? (VirtualObjectNode) yAlias : null;
+
+        if (xVirtual != null && yVirtual == null) {
+            virtualizeNonVirtualComparison(xVirtual, yAlias, tool);
+        } else if (xVirtual == null && yVirtual != null) {
+            virtualizeNonVirtualComparison(yVirtual, xAlias, tool);
+        } else if (xVirtual != null && yVirtual != null) {
+            if (xVirtual.hasIdentity() ^ yVirtual.hasIdentity()) {
                 /*
                  * One of the two objects has identity, the other doesn't. In code, this looks like
                  * "Integer.valueOf(a) == new Integer(b)", which is always false.
-                 *
+                 * 
                  * In other words: an object created via valueOf can never be equal to one created
                  * by new in the same compilation unit.
                  */
                 tool.replaceWithValue(LogicConstantNode.contradiction(graph()));
-            } else if (!xIdentity && !yIdentity) {
-                ResolvedJavaType type = stateX.getVirtualObject().type();
-                if (type.equals(stateY.getVirtualObject().type())) {
+            } else if (!xVirtual.hasIdentity() && !yVirtual.hasIdentity()) {
+                ResolvedJavaType type = xVirtual.type();
+                if (type.equals(yVirtual.type())) {
                     MetaAccessProvider metaAccess = tool.getMetaAccessProvider();
                     if (type.equals(metaAccess.lookupJavaType(Integer.class)) || type.equals(metaAccess.lookupJavaType(Long.class))) {
                         // both are virtual without identity: check contents
-                        assert stateX.getVirtualObject().entryCount() == 1 && stateY.getVirtualObject().entryCount() == 1;
-                        assert stateX.getVirtualObject().entryKind(0).getStackKind() == Kind.Int || stateX.getVirtualObject().entryKind(0) == Kind.Long;
-                        IntegerEqualsNode equals = new IntegerEqualsNode(stateX.getEntry(0), stateY.getEntry(0));
+                        assert xVirtual.entryCount() == 1 && yVirtual.entryCount() == 1;
+                        assert xVirtual.entryKind(0).getStackKind() == Kind.Int || xVirtual.entryKind(0) == Kind.Long;
+                        IntegerEqualsNode equals = new IntegerEqualsNode(tool.getEntry(xVirtual, 0), tool.getEntry(yVirtual, 0));
                         tool.addNode(equals);
                         tool.replaceWithValue(equals);
                     }
                 }
             } else {
                 // both are virtual with identity: check if they refer to the same object
-                tool.replaceWithValue(LogicConstantNode.forBoolean(stateX == stateY, graph()));
+                tool.replaceWithValue(LogicConstantNode.forBoolean(xVirtual == yVirtual, graph()));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/WeakCounterNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/debug/WeakCounterNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -28,6 +28,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * This is a special version of the dynamic counter node that removes itself as soon as it's the
@@ -55,8 +56,8 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(checkedValue);
-        if (state != null && state.getState() == EscapeState.Virtual) {
+        ValueNode alias = tool.getAlias(checkedValue);
+        if (alias instanceof VirtualObjectNode) {
             tool.delete();
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -76,13 +76,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        ValueNode v = tool.getReplacedValue(getValue());
+        ValueNode alias = tool.getAlias(getValue());
         ResolvedJavaType type = StampTool.typeOrNull(stamp());
 
         VirtualBoxingNode newVirtual = new VirtualBoxingNode(type, boxingKind);
         assert newVirtual.getFields().length == 1;
 
-        tool.createVirtualObject(newVirtual, new ValueNode[]{v}, Collections.<MonitorIdNode> emptyList(), false);
+        tool.createVirtualObject(newVirtual, new ValueNode[]{alias}, Collections.<MonitorIdNode> emptyList(), false);
         tool.replaceWithVirtual(newVirtual);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/GetClassNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/GetClassNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Loads an object's class (i.e., this node can be created for {@code object.getClass()}).
@@ -87,9 +88,10 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null) {
-            Constant javaClass = state.getVirtualObject().type().getJavaClass();
+        ValueNode alias = tool.getAlias(getObject());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            Constant javaClass = virtual.type().getJavaClass();
             tool.replaceWithValue(ConstantNode.forConstant(stamp(), javaClass, tool.getMetaAccessProvider(), graph()));
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/LoadHubNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -22,8 +22,8 @@
  */
 package com.oracle.graal.nodes.extended;
 
+import jdk.internal.jvmci.meta.Assumptions.AssumptionResult;
 import jdk.internal.jvmci.meta.*;
-import jdk.internal.jvmci.meta.Assumptions.*;
 
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
@@ -116,10 +116,10 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(value);
-        if (state != null) {
-            Constant constantHub = state.getVirtualObject().type().getObjectHub();
-            tool.replaceWithValue(ConstantNode.forConstant(stamp(), constantHub, tool.getMetaAccessProvider(), graph()));
+        ValueNode alias = tool.getAlias(getValue());
+        ResolvedJavaType type = findSynonymType(graph(), tool.getMetaAccessProvider(), alias);
+        if (type != null) {
+            tool.replaceWithValue(ConstantNode.forConstant(stamp(), type.getObjectHub(), tool.getMetaAccessProvider(), graph()));
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnboxNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnboxNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.nodes.virtual.*;
 
 @NodeInfo
 public final class UnboxNode extends FixedWithNextNode implements Virtualizable, Lowerable, Canonicalizable.Unary<ValueNode> {
@@ -68,12 +69,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(getValue());
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            ResolvedJavaType objectType = state.getVirtualObject().type();
+        ValueNode alias = tool.getAlias(getValue());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            ResolvedJavaType objectType = virtual.type();
             ResolvedJavaType expectedType = tool.getMetaAccessProvider().lookupJavaType(boxingKind.toBoxedJavaClass());
             if (objectType.equals(expectedType)) {
-                tool.replaceWithValue(state.getEntry(0));
+                tool.replaceWithValue(tool.getEntry(virtual, 0));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeCastNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeCastNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 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 UnsafeCastNode} produces the same value as its input, but with a different type. It
@@ -92,9 +93,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual && StampTool.typeOrNull(this) != null && StampTool.typeOrNull(this).isAssignableFrom(state.getVirtualObject().type())) {
-            tool.replaceWithVirtual(state.getVirtualObject());
+        ValueNode alias = tool.getAlias(object);
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (StampTool.typeOrNull(this) != null && StampTool.typeOrNull(this).isAssignableFrom(virtual.type())) {
+                tool.replaceWithVirtual(virtual);
+            }
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeLoadNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeLoadNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Load of a value from a location specified as an offset relative to an object. No null check is
@@ -60,16 +61,17 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            ValueNode offsetValue = tool.getReplacedValue(offset());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            ValueNode offsetValue = tool.getAlias(offset());
             if (offsetValue.isConstant()) {
                 long off = offsetValue.asJavaConstant().asLong();
-                int entryIndex = state.getVirtualObject().entryIndexForOffset(off, accessKind());
+                int entryIndex = virtual.entryIndexForOffset(off, accessKind());
 
                 if (entryIndex != -1) {
-                    ValueNode entry = state.getEntry(entryIndex);
-                    Kind entryKind = state.getVirtualObject().entryKind(entryIndex);
+                    ValueNode entry = tool.getEntry(virtual, entryIndex);
+                    Kind entryKind = virtual.entryKind(entryIndex);
                     if (entry.getKind() == getKind() || entryKind == accessKind()) {
                         tool.replaceWith(entry);
                     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeStoreNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * Store of a value at a location specified as an offset relative to an object. No null check is
@@ -79,26 +80,27 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            ValueNode indexValue = tool.getReplacedValue(offset());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            ValueNode indexValue = tool.getAlias(offset());
             if (indexValue.isConstant()) {
                 long off = indexValue.asJavaConstant().asLong();
-                int entryIndex = state.getVirtualObject().entryIndexForOffset(off, accessKind());
+                int entryIndex = virtual.entryIndexForOffset(off, accessKind());
                 if (entryIndex != -1) {
-                    Kind entryKind = state.getVirtualObject().entryKind(entryIndex);
-                    ValueNode entry = state.getEntry(entryIndex);
+                    Kind entryKind = virtual.entryKind(entryIndex);
+                    ValueNode entry = tool.getEntry(virtual, entryIndex);
                     if (entry.getKind() == value.getKind() || entryKind == accessKind()) {
-                        tool.setVirtualEntry(state, entryIndex, value(), true);
+                        tool.setVirtualEntry(virtual, entryIndex, value(), true);
                         tool.delete();
                     } else {
                         if ((accessKind() == Kind.Long || accessKind() == Kind.Double) && entryKind == Kind.Int) {
-                            int nextIndex = state.getVirtualObject().entryIndexForOffset(off + 4, entryKind);
+                            int nextIndex = virtual.entryIndexForOffset(off + 4, entryKind);
                             if (nextIndex != -1) {
-                                Kind nextKind = state.getVirtualObject().entryKind(nextIndex);
+                                Kind nextKind = virtual.entryKind(nextIndex);
                                 if (nextKind == Kind.Int) {
-                                    tool.setVirtualEntry(state, entryIndex, value(), true);
-                                    tool.setVirtualEntry(state, nextIndex, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()), true);
+                                    tool.setVirtualEntry(virtual, entryIndex, value(), true);
+                                    tool.setVirtualEntry(virtual, nextIndex, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()), true);
                                     tool.delete();
                                 }
                             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ValueAnchorNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The ValueAnchor instruction keeps non-CFG (floating) nodes above a certain point in the graph.
@@ -94,13 +95,14 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        if (anchored != null && !(anchored instanceof AbstractBeginNode)) {
-            State state = tool.getObjectState(anchored);
-            if (state == null || state.getState() != EscapeState.Virtual) {
-                return;
+        if (anchored == null || anchored instanceof AbstractBeginNode) {
+            tool.delete();
+        } else {
+            ValueNode alias = tool.getAlias(anchored);
+            if (alias instanceof VirtualObjectNode) {
+                tool.delete();
             }
         }
-        tool.delete();
     }
 
     public void removeAnchoredNode() {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/ArrayLengthNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -136,10 +136,14 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(array());
-        if (state != null) {
-            assert state.getVirtualObject().type().isArray();
-            tool.replaceWithValue(ConstantNode.forInt(state.getVirtualObject().entryCount(), graph()));
+        ValueNode alias = tool.getAlias(array());
+        ValueNode length = GraphUtil.arrayLength(alias);
+        if (length != null) {
+            ValueNode lengthAlias = tool.getAlias(length);
+            if (!lengthAlias.isAlive()) {
+                lengthAlias = graph().addOrUnique(lengthAlias);
+            }
+            tool.replaceWithValue(lengthAlias);
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CheckCastNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -240,11 +240,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            if (type.isAssignableFrom(state.getVirtualObject().type())) {
-                tool.replaceWithVirtual(state.getVirtualObject());
-            }
+        ValueNode alias = tool.getAlias(object);
+        if (tryFold(alias.stamp()) == TriState.TRUE) {
+            tool.replaceWith(alias);
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -164,9 +164,10 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(getValue());
-        if (state != null) {
-            tool.replaceWithValue(LogicConstantNode.forBoolean(type().isAssignableFrom(state.getVirtualObject().type()), graph()));
+        ValueNode alias = tool.getAlias(getValue());
+        TriState fold = tryFold(alias.stamp());
+        if (fold != TriState.UNKNOWN) {
+            tool.replaceWithValue(LogicConstantNode.forBoolean(fold.isTrue(), graph()));
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadFieldNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -121,11 +121,11 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            int fieldIndex = ((VirtualInstanceNode) state.getVirtualObject()).fieldIndex(field());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            int fieldIndex = ((VirtualInstanceNode) alias).fieldIndex(field());
             if (fieldIndex != -1) {
-                tool.replaceWith(state.getEntry(fieldIndex));
+                tool.replaceWith(tool.getEntry((VirtualObjectNode) alias, fieldIndex));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/LoadIndexedNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 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.
@@ -79,12 +80,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State arrayState = tool.getObjectState(array());
-        if (arrayState != null && arrayState.getState() == EscapeState.Virtual) {
-            ValueNode indexValue = tool.getReplacedValue(index());
+        ValueNode alias = tool.getAlias(array());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualArrayNode virtual = (VirtualArrayNode) alias;
+            ValueNode indexValue = tool.getAlias(index());
             int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1;
-            if (idx >= 0 && idx < arrayState.getVirtualObject().entryCount()) {
-                tool.replaceWith(arrayState.getEntry(idx));
+            if (idx >= 0 && idx < virtual.entryCount()) {
+                tool.replaceWith(tool.getEntry(virtual, idx));
             }
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -30,6 +30,7 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code MonitorEnterNode} represents the acquisition of a monitor.
@@ -55,10 +56,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
-            state.addLock(getMonitorId());
-            tool.delete();
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (virtual.hasIdentity()) {
+                tool.addLock(virtual, getMonitorId());
+                tool.delete();
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code MonitorExitNode} represents a monitor release. If it is the release of the monitor of
@@ -79,12 +80,15 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
         // the monitor exit for a synchronized method should never be virtualized
-        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
-            MonitorIdNode removedLock = state.removeLock();
-            assert removedLock == getMonitorId() : "mismatch at " + this + ": " + removedLock + " vs. " + getMonitorId();
-            tool.delete();
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (virtual.hasIdentity()) {
+                MonitorIdNode removedLock = tool.removeLock(virtual);
+                assert removedLock == getMonitorId() : "mismatch at " + this + ": " + removedLock + " vs. " + getMonitorId();
+                tool.delete();
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -74,9 +74,9 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        ValueNode replacedLength = tool.getReplacedValue(length());
-        if (replacedLength.asConstant() != null) {
-            final int constantLength = replacedLength.asJavaConstant().asInt();
+        ValueNode lengthAlias = tool.getAlias(length());
+        if (lengthAlias.asConstant() != null) {
+            int constantLength = lengthAlias.asJavaConstant().asInt();
             if (constantLength >= 0 && constantLength < tool.getMaximumEntryCount()) {
                 ValueNode[] state = new ValueNode[constantLength];
                 ConstantNode defaultForKind = constantLength == 0 ? null : defaultElementValue();
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RawMonitorEnterNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RawMonitorEnterNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code MonitorEnterNode} represents the acquisition of a monitor. The object needs already be
@@ -61,10 +62,13 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
-            state.addLock(getMonitorId());
-            tool.delete();
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (virtual.hasIdentity()) {
+                tool.addLock(virtual, getMonitorId());
+                tool.delete();
+            }
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RegisterFinalizerNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/RegisterFinalizerNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * This node is used to perform the finalizer registration at the end of the java.lang.Object
@@ -94,8 +95,8 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(getValue());
-        if (state != null && !state.getVirtualObject().type().hasFinalizer()) {
+        ValueNode alias = tool.getAlias(getValue());
+        if (alias instanceof VirtualObjectNode && !((VirtualObjectNode) alias).type().hasFinalizer()) {
             tool.delete();
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreFieldNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -72,11 +72,12 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object());
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            int fieldIndex = ((VirtualInstanceNode) state.getVirtualObject()).fieldIndex(field());
+        ValueNode alias = tool.getAlias(object());
+        if (alias instanceof VirtualObjectNode) {
+            VirtualInstanceNode virtual = (VirtualInstanceNode) alias;
+            int fieldIndex = virtual.fieldIndex(field());
             if (fieldIndex != -1) {
-                tool.setVirtualEntry(state, fieldIndex, value(), false);
+                tool.setVirtualEntry(virtual, fieldIndex, value(), false);
                 tool.delete();
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/StoreIndexedNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -30,6 +30,7 @@
 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.
@@ -66,15 +67,16 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State arrayState = tool.getObjectState(array());
-        if (arrayState != null && arrayState.getState() == EscapeState.Virtual) {
-            ValueNode indexValue = tool.getReplacedValue(index());
+        ValueNode alias = tool.getAlias(array());
+        if (alias instanceof VirtualObjectNode) {
+            ValueNode indexValue = tool.getAlias(index());
             int idx = indexValue.isConstant() ? indexValue.asJavaConstant().asInt() : -1;
-            if (idx >= 0 && idx < arrayState.getVirtualObject().entryCount()) {
-                ResolvedJavaType componentType = arrayState.getVirtualObject().type().getComponentType();
+            VirtualArrayNode virtual = (VirtualArrayNode) alias;
+            if (idx >= 0 && idx < virtual.entryCount()) {
+                ResolvedJavaType componentType = virtual.type().getComponentType();
                 if (componentType.isPrimitive() || StampTool.isPointerAlwaysNull(value) || componentType.getSuperclass() == null ||
                                 (StampTool.typeOrNull(value) != null && componentType.isAssignableFrom(StampTool.typeOrNull(value)))) {
-                    tool.setVirtualEntry(arrayState, idx, value(), false);
+                    tool.setVirtualEntry(virtual, idx, value(), false);
                     tool.delete();
                 }
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeCheckNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/TypeCheckNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -123,9 +123,10 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(getValue());
-        if (state != null) {
-            tool.replaceWithValue(LogicConstantNode.forBoolean(type().equals(state.getVirtualObject().type()), graph()));
+        ValueNode alias = tool.getAlias(getValue());
+        TriState state = tryFold(alias.stamp());
+        if (state != TriState.UNKNOWN) {
+            tool.replaceWithValue(LogicConstantNode.forBoolean(state.isTrue(), graph()));
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Fri Jul 24 16:20:56 2015 +0200
@@ -22,10 +22,6 @@
  */
 package com.oracle.graal.nodes.spi;
 
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-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. The {@link #virtualize(VirtualizerTool)} method will only be called for
@@ -34,30 +30,6 @@
  */
 public interface Virtualizable {
 
-    public static enum EscapeState {
-        Virtual,
-        Materialized
-    }
-
-    public abstract static class State {
-
-        public abstract EscapeState getState();
-
-        public abstract VirtualObjectNode getVirtualObject();
-
-        public abstract ValueNode getEntry(int index);
-
-        public abstract void addLock(MonitorIdNode monitorId);
-
-        public abstract MonitorIdNode removeLock();
-
-        public abstract ValueNode getMaterializedValue();
-
-        public abstract void setEnsureVirtualized(boolean ensureVirtualized);
-
-        public abstract boolean getEnsureVirtualized();
-    }
-
     /**
      * 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
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Fri Jul 24 16:20:56 2015 +0200
@@ -29,7 +29,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.Virtualizable.State;
 import com.oracle.graal.nodes.virtual.*;
 
 /**
@@ -72,45 +71,43 @@
     void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, List<MonitorIdNode> locks, boolean ensureVirtualized);
 
     /**
-     * Queries the current state of the given value: if it is virtualized (thread-local and the
-     * compiler knows all entries) or not.
+     * Returns a VirtualObjectNode if the given value is aliased with a virtual object that is still
+     * virtual, the materialized value of the given value is aliased with a virtual object that was
+     * materialized, the replacement if the give value was replaced, otherwise the given value.
      *
-     * @param value the value whose state should be queried.
-     * @return the {@link State} representing the value if it has been virtualized at some point,
-     *         null otherwise.
+     * 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).
      */
-    State getObjectState(ValueNode value);
+    ValueNode getAlias(ValueNode value);
 
     /**
      * Sets the entry (field or array element) with the given index in the virtualized object.
      *
-     * @param state the state.
      * @param index the index to be set.
      * @param value the new value for the given index.
      * @param unsafe if true, then mismatching value {@link Kind}s will be accepted.
      */
-    void setVirtualEntry(State state, int index, ValueNode value, boolean unsafe);
+    void setVirtualEntry(VirtualObjectNode virtualObject, int index, ValueNode value, boolean unsafe);
 
-    // scalar replacement
+    ValueNode getEntry(VirtualObjectNode virtualObject, int index);
 
-    /**
-     * 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);
+    void addLock(VirtualObjectNode virtualObject, MonitorIdNode monitorId);
+
+    MonitorIdNode removeLock(VirtualObjectNode virtualObject);
+
+    void setEnsureVirtualized(VirtualObjectNode virtualObject, boolean ensureVirtualized);
+
+    boolean getEnsureVirtualized(VirtualObjectNode virtualObject);
 
     // 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.
+     * @param virtualObject the virtualized object that should replace the current node.
      */
-    void replaceWithVirtual(VirtualObjectNode virtual);
+    void replaceWithVirtual(VirtualObjectNode virtualObject);
 
     /**
      * Deletes the current node and replaces it with the given value.
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/EnsureVirtualizedNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/EnsureVirtualizedNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -44,14 +44,15 @@
     }
 
     public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual) {
-            if (state.getVirtualObject() instanceof VirtualBoxingNode) {
-                Throwable exception = new VerificationError("ensureVirtual is not valid for boxing objects: %s", state.getVirtualObject().type().getName());
+        ValueNode alias = tool.getAlias(object);
+        if (alias instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual = (VirtualObjectNode) alias;
+            if (virtual instanceof VirtualBoxingNode) {
+                Throwable exception = new VerificationError("ensureVirtual is not valid for boxing objects: %s", virtual.type().getName());
                 throw GraphUtil.approxSourceException(this, exception);
             }
             if (!localOnly) {
-                state.setEnsureVirtualized(true);
+                tool.setEnsureVirtualized(virtual, true);
             }
             tool.delete();
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -35,12 +35,32 @@
 
     public static final NodeClass<VirtualObjectNode> TYPE = NodeClass.create(VirtualObjectNode.class);
     protected boolean hasIdentity;
+    private int objectId = -1;
 
     protected VirtualObjectNode(NodeClass<? extends VirtualObjectNode> c, ResolvedJavaType type, boolean hasIdentity) {
         super(c, StampFactory.exactNonNull(type));
         this.hasIdentity = hasIdentity;
     }
 
+    public final int getObjectId() {
+        return objectId;
+    }
+
+    public final void resetObjectId() {
+        this.objectId = -1;
+    }
+
+    public final void setObjectId(int objectId) {
+        assert objectId != -1;
+        this.objectId = objectId;
+    }
+
+    @Override
+    protected void afterClone(Node other) {
+        super.afterClone(other);
+        resetObjectId();
+    }
+
     /**
      * The type of object described by this {@link VirtualObjectNode}. In case of arrays, this is
      * the array type (and not the component type).
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ArrayEqualsNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.nodes.memory.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.nodes.virtual.*;
 
 // JaCoCo Exclude
 
@@ -76,32 +77,33 @@
     }
 
     public void virtualize(VirtualizerTool tool) {
-        State state1 = tool.getObjectState(array1);
-        if (state1 != null) {
-            State state2 = tool.getObjectState(array2);
-            if (state2 != null) {
-                if (state1.getVirtualObject() == state2.getVirtualObject()) {
-                    // the same virtual objects will always have the same contents
+        ValueNode alias1 = tool.getAlias(array1);
+        ValueNode alias2 = tool.getAlias(array2);
+        if (alias1 == alias2) {
+            // the same virtual objects will always have the same contents
+            tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
+        } else if (alias1 instanceof VirtualObjectNode && alias2 instanceof VirtualObjectNode) {
+            VirtualObjectNode virtual1 = (VirtualObjectNode) alias1;
+            VirtualObjectNode virtual2 = (VirtualObjectNode) alias2;
+
+            if (virtual1.entryCount() == virtual2.entryCount()) {
+                int entryCount = virtual1.entryCount();
+                boolean allEqual = true;
+                for (int i = 0; i < entryCount; i++) {
+                    ValueNode entry1 = tool.getEntry(virtual1, i);
+                    ValueNode entry2 = tool.getEntry(virtual2, i);
+                    if (entry1 != entry2) {
+                        // the contents might be different
+                        allEqual = false;
+                    }
+                    if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
+                        // the contents are different
+                        tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
+                        return;
+                    }
+                }
+                if (allEqual) {
                     tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
-                } else if (state1.getVirtualObject().entryCount() == state2.getVirtualObject().entryCount() && state1.getState() == EscapeState.Virtual && state2.getState() == EscapeState.Virtual) {
-                    int entryCount = state1.getVirtualObject().entryCount();
-                    boolean allEqual = true;
-                    for (int i = 0; i < entryCount; i++) {
-                        ValueNode entry1 = state1.getEntry(i);
-                        ValueNode entry2 = state2.getEntry(i);
-                        if (entry1 != entry2) {
-                            // the contents might be different
-                            allEqual = false;
-                        }
-                        if (entry1.stamp().alwaysDistinct(entry2.stamp())) {
-                            // the contents are different
-                            tool.replaceWithValue(ConstantNode.forBoolean(false, graph()));
-                            return;
-                        }
-                    }
-                    if (allEqual) {
-                        tool.replaceWithValue(ConstantNode.forBoolean(true, graph()));
-                    }
                 }
             }
         }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicArrayCopyNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicArrayCopyNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -131,21 +131,11 @@
         return position >= 0 && position + length <= virtualObject.entryCount();
     }
 
-    private static boolean checkEntryTypes(int srcPos, int length, State srcState, ResolvedJavaType destComponentType, VirtualizerTool tool) {
+    private static boolean checkEntryTypes(int srcPos, int length, VirtualObjectNode src, ResolvedJavaType destComponentType, VirtualizerTool tool) {
         if (destComponentType.getKind() == Kind.Object) {
             for (int i = 0; i < length; i++) {
-                ValueNode entry = srcState.getEntry(srcPos + i);
-                State state = tool.getObjectState(entry);
-                ResolvedJavaType type;
-                if (state != null) {
-                    if (state.getState() == EscapeState.Virtual) {
-                        type = state.getVirtualObject().type();
-                    } else {
-                        type = StampTool.typeOrNull(state.getMaterializedValue());
-                    }
-                } else {
-                    type = StampTool.typeOrNull(entry);
-                }
+                ValueNode entry = tool.getEntry(src, srcPos + i);
+                ResolvedJavaType type = StampTool.typeOrNull(entry);
                 if (type == null || !destComponentType.isAssignableFrom(type)) {
                     return false;
                 }
@@ -177,52 +167,49 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        ValueNode sourcePosition = tool.getReplacedValue(getSourcePosition());
-        ValueNode destinationPosition = tool.getReplacedValue(getDestinationPosition());
-        ValueNode replacedLength = tool.getReplacedValue(getLength());
+        ValueNode sourcePosition = tool.getAlias(getSourcePosition());
+        ValueNode destinationPosition = tool.getAlias(getDestinationPosition());
+        ValueNode replacedLength = tool.getAlias(getLength());
 
         if (sourcePosition.isConstant() && destinationPosition.isConstant() && replacedLength.isConstant()) {
             int srcPosInt = sourcePosition.asJavaConstant().asInt();
             int destPosInt = destinationPosition.asJavaConstant().asInt();
             int len = replacedLength.asJavaConstant().asInt();
-            State destState = tool.getObjectState(getDestination());
+            ValueNode destAlias = tool.getAlias(getDestination());
 
-            if (destState != null && destState.getState() == EscapeState.Virtual) {
-                VirtualObjectNode destVirtual = destState.getVirtualObject();
-                if (!(destVirtual instanceof VirtualArrayNode)) {
-                    return;
-                }
+            if (destAlias instanceof VirtualArrayNode) {
+                VirtualArrayNode destVirtual = (VirtualArrayNode) destAlias;
                 if (len < 0 || !checkBounds(destPosInt, len, destVirtual)) {
                     return;
                 }
-                State srcState = tool.getObjectState(getSource());
-                if (srcState != null && srcState.getState() == EscapeState.Virtual) {
-                    VirtualObjectNode srcVirtual = srcState.getVirtualObject();
-                    if (((VirtualArrayNode) destVirtual).componentType().getKind() != Kind.Object) {
+                ValueNode srcAlias = tool.getAlias(getSource());
+
+                if (srcAlias instanceof VirtualObjectNode) {
+                    if (!(srcAlias instanceof VirtualArrayNode)) {
                         return;
                     }
-                    if (!(srcVirtual instanceof VirtualArrayNode)) {
+                    VirtualArrayNode srcVirtual = (VirtualArrayNode) srcAlias;
+                    if (destVirtual.componentType().getKind() != Kind.Object) {
                         return;
                     }
-                    if (((VirtualArrayNode) srcVirtual).componentType().getKind() != Kind.Object) {
+                    if (srcVirtual.componentType().getKind() != Kind.Object) {
                         return;
                     }
                     if (!checkBounds(srcPosInt, len, srcVirtual)) {
                         return;
                     }
-                    if (!checkEntryTypes(srcPosInt, len, srcState, destVirtual.type().getComponentType(), tool)) {
+                    if (!checkEntryTypes(srcPosInt, len, srcVirtual, destVirtual.type().getComponentType(), tool)) {
                         return;
                     }
                     for (int i = 0; i < len; i++) {
-                        tool.setVirtualEntry(destState, destPosInt + i, srcState.getEntry(srcPosInt + i), false);
+                        tool.setVirtualEntry(destVirtual, destPosInt + i, tool.getEntry(srcVirtual, srcPosInt + i), false);
                     }
                     tool.delete();
                     if (Debug.isLogEnabled()) {
                         Debug.log("virtualized arraycopyf(%s, %d, %s, %d, %d)", getSource(), srcPosInt, getDestination(), destPosInt, len);
                     }
                 } else {
-                    ValueNode source = srcState == null ? tool.getReplacedValue(getSource()) : srcState.getMaterializedValue();
-                    ResolvedJavaType sourceType = StampTool.typeOrNull(source);
+                    ResolvedJavaType sourceType = StampTool.typeOrNull(srcAlias);
                     if (sourceType == null || !sourceType.isArray()) {
                         return;
                     }
@@ -232,9 +219,9 @@
                         return;
                     }
                     for (int i = 0; i < len; i++) {
-                        LoadIndexedNode load = new LoadIndexedNode(source, ConstantNode.forInt(i + srcPosInt, graph()), destComponentType.getKind());
+                        LoadIndexedNode load = new LoadIndexedNode(srcAlias, ConstantNode.forInt(i + srcPosInt, graph()), destComponentType.getKind());
                         tool.addNode(load);
-                        tool.setVirtualEntry(destState, destPosInt + i, load, false);
+                        tool.setVirtualEntry(destVirtual, destPosInt + i, load, false);
                     }
                     tool.delete();
                 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicObjectCloneNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/BasicObjectCloneNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -90,26 +90,20 @@
 
     @Override
     public void virtualize(VirtualizerTool tool) {
-        State originalState = tool.getObjectState(getObject());
-        if (originalState != null && originalState.getState() == EscapeState.Virtual) {
-            VirtualObjectNode originalVirtual = originalState.getVirtualObject();
+        ValueNode originalAlias = tool.getAlias(getObject());
+        if (originalAlias instanceof VirtualObjectNode) {
+            VirtualObjectNode originalVirtual = (VirtualObjectNode) originalAlias;
             if (isCloneableType(originalVirtual.type(), tool.getMetaAccessProvider())) {
                 ValueNode[] newEntryState = new ValueNode[originalVirtual.entryCount()];
                 for (int i = 0; i < newEntryState.length; i++) {
-                    newEntryState[i] = originalState.getEntry(i);
+                    newEntryState[i] = tool.getEntry(originalVirtual, i);
                 }
                 VirtualObjectNode newVirtual = originalVirtual.duplicate();
                 tool.createVirtualObject(newVirtual, newEntryState, Collections.<MonitorIdNode> emptyList(), false);
                 tool.replaceWithVirtual(newVirtual);
             }
         } else {
-            ValueNode obj;
-            if (originalState != null) {
-                obj = originalState.getMaterializedValue();
-            } else {
-                obj = tool.getReplacedValue(getObject());
-            }
-            ResolvedJavaType type = getConcreteType(obj.stamp(), graph().getAssumptions(), tool.getMetaAccessProvider());
+            ResolvedJavaType type = getConcreteType(originalAlias.stamp(), graph().getAssumptions(), tool.getMetaAccessProvider());
             if (type != null && !type.isArray()) {
                 VirtualInstanceNode newVirtual = createVirtualInstanceNode(type, true);
                 ResolvedJavaField[] fields = newVirtual.getFields();
@@ -117,7 +111,7 @@
                 ValueNode[] state = new ValueNode[fields.length];
                 final LoadFieldNode[] loads = new LoadFieldNode[fields.length];
                 for (int i = 0; i < fields.length; i++) {
-                    state[i] = loads[i] = new LoadFieldNode(obj, fields[i]);
+                    state[i] = loads[i] = new LoadFieldNode(originalAlias, fields[i]);
                     tool.addNode(loads[i]);
                 }
                 tool.createVirtualObject(newVirtual, state, Collections.<MonitorIdNode> emptyList(), false);
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.java	Fri Jul 24 16:20:56 2015 +0200
@@ -29,6 +29,7 @@
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 
 /**
  * A helper class to allow elimination of byte code instrumentation that could interfere with escape
@@ -46,8 +47,8 @@
     @Override
     public void virtualize(VirtualizerTool tool) {
         for (ValueNode arg : arguments) {
-            State state = tool.getObjectState(arg);
-            if (state != null && state.getState() == EscapeState.Virtual) {
+            ValueNode alias = tool.getAlias(arg);
+            if (alias instanceof VirtualObjectNode) {
                 tool.delete();
             }
         }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Fri Jul 24 16:20:56 2015 +0200
@@ -176,8 +176,9 @@
      *
      */
     public void replaceAtUsages(ValueNode node, ValueNode replacement) {
+        assert node != null && replacement != null : node + " " + replacement;
         add("replace at usages", (graph, obsoleteNodes) -> {
-            assert node.isAlive() && replacement.isAlive();
+            assert node.isAlive() && replacement.isAlive() : node + " " + replacement;
             if (replacement instanceof FixedWithNextNode && ((FixedWithNextNode) replacement).next() == null) {
                 assert node instanceof FixedNode;
                 graph.addBeforeFixed((FixedNode) node, (FixedWithNextNode) replacement);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Fri Jul 24 16:20:56 2015 +0200
@@ -28,8 +28,6 @@
 
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.virtual.nodes.*;
 
@@ -38,14 +36,11 @@
  * 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.
  */
-public class ObjectState extends Virtualizable.State {
+public class ObjectState {
 
     public static final DebugMetric CREATE_ESCAPED_OBJECT_STATE = Debug.metric("CreateEscapeObjectState");
     public static final DebugMetric GET_ESCAPED_OBJECT_STATE = Debug.metric("GetEscapeObjectState");
 
-    final VirtualObjectNode virtual;
-
-    private EscapeState state;
     private ValueNode[] entries;
     private ValueNode materializedValue;
     private LockState locks;
@@ -53,35 +48,32 @@
 
     private EscapeObjectState cachedState;
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, EscapeState state, List<MonitorIdNode> locks, boolean ensureVirtualized) {
-        this(virtual, entries, state, (LockState) null, ensureVirtualized);
+    boolean copyOnWrite;
+
+    public ObjectState(ValueNode[] entries, List<MonitorIdNode> locks, boolean ensureVirtualized) {
+        this(entries, (LockState) null, ensureVirtualized);
         for (int i = locks.size() - 1; i >= 0; i--) {
             this.locks = new LockState(locks.get(i), this.locks);
         }
     }
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, EscapeState state, LockState locks, boolean ensureVirtualized) {
-        this.virtual = virtual;
+    public ObjectState(ValueNode[] entries, LockState locks, boolean ensureVirtualized) {
         this.entries = entries;
-        this.state = state;
         this.locks = locks;
         this.ensureVirtualized = ensureVirtualized;
     }
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode materializedValue, EscapeState state, LockState locks, boolean ensureVirtualized) {
-        this.virtual = virtual;
+    public ObjectState(ValueNode materializedValue, LockState locks, boolean ensureVirtualized) {
+        assert materializedValue != null;
         this.materializedValue = materializedValue;
-        this.state = state;
         this.locks = locks;
         this.ensureVirtualized = ensureVirtualized;
     }
 
     private ObjectState(ObjectState other) {
-        virtual = other.virtual;
         entries = other.entries == null ? null : other.entries.clone();
         materializedValue = other.materializedValue;
         locks = other.locks;
-        state = other.state;
         cachedState = other.cachedState;
         ensureVirtualized = other.ensureVirtualized;
     }
@@ -90,7 +82,7 @@
         return new ObjectState(this);
     }
 
-    public EscapeObjectState createEscapeObjectState() {
+    public EscapeObjectState createEscapeObjectState(VirtualObjectNode virtual) {
         GET_ESCAPED_OBJECT_STATE.increment();
         if (cachedState == null) {
             CREATE_ESCAPED_OBJECT_STATE.increment();
@@ -100,68 +92,55 @@
 
     }
 
-    @Override
-    public EscapeState getState() {
-        return state;
+    public boolean isVirtual() {
+        assert materializedValue == null ^ entries == null;
+        return materializedValue == null;
     }
 
-    @Override
-    public VirtualObjectNode getVirtualObject() {
-        return virtual;
-    }
-
-    public boolean isVirtual() {
-        return state == EscapeState.Virtual;
-    }
-
+    /**
+     * Users of this method are not allowed to change the entries of the returned array.
+     */
     public ValueNode[] getEntries() {
-        assert isVirtual() && entries != null;
+        assert isVirtual();
         return entries;
     }
 
-    @Override
     public ValueNode getEntry(int index) {
         assert isVirtual();
         return entries[index];
     }
 
+    public ValueNode getMaterializedValue() {
+        assert !isVirtual();
+        return materializedValue;
+    }
+
     public void setEntry(int index, ValueNode value) {
         assert isVirtual();
-        if (entries[index] != value) {
-            cachedState = null;
-            entries[index] = value;
-        }
+        cachedState = null;
+        entries[index] = value;
     }
 
-    public void escape(ValueNode materialized, EscapeState newState) {
-        assert state == EscapeState.Virtual && newState == EscapeState.Materialized;
-        state = newState;
+    public void escape(ValueNode materialized) {
+        assert isVirtual();
+        assert materialized != null;
         materializedValue = materialized;
         entries = null;
         cachedState = null;
         assert !isVirtual();
     }
 
-    @Override
-    public ValueNode getMaterializedValue() {
-        assert state == EscapeState.Materialized;
-        return materializedValue;
+    public void updateMaterializedValue(ValueNode value) {
+        assert !isVirtual();
+        assert value != null;
+        cachedState = null;
+        materializedValue = value;
     }
 
-    public void updateMaterializedValue(ValueNode value) {
-        assert !isVirtual();
-        if (value != materializedValue) {
-            cachedState = null;
-            materializedValue = value;
-        }
-    }
-
-    @Override
     public void addLock(MonitorIdNode monitorId) {
         locks = new LockState(monitorId, locks);
     }
 
-    @Override
     public MonitorIdNode removeLock() {
         try {
             return locks.monitorId;
@@ -188,12 +167,10 @@
         return a == null && b == null;
     }
 
-    @Override
     public void setEnsureVirtualized(boolean ensureVirtualized) {
         this.ensureVirtualized = ensureVirtualized;
     }
 
-    @Override
     public boolean getEnsureVirtualized() {
         return ensureVirtualized;
     }
@@ -206,7 +183,7 @@
         }
         if (entries != null) {
             for (int i = 0; i < entries.length; i++) {
-                str.append(virtual.entryName(i)).append('=').append(entries[i]).append(' ');
+                str.append("entry").append(i).append('=').append(entries[i]).append(' ');
             }
         }
         if (materializedValue != null) {
@@ -223,8 +200,6 @@
         result = prime * result + Arrays.hashCode(entries);
         result = prime * result + (locks != null ? locks.monitorId.getLockDepth() : 0);
         result = prime * result + ((materializedValue == null) ? 0 : materializedValue.hashCode());
-        result = prime * result + ((state == null) ? 0 : state.hashCode());
-        result = prime * result + ((virtual == null) ? 0 : virtual.hashCode());
         return result;
     }
 
@@ -250,13 +225,11 @@
         } else if (!materializedValue.equals(other.materializedValue)) {
             return false;
         }
-        if (state != other.state) {
-            return false;
-        }
-        assert virtual != null && other.virtual != null;
-        if (!virtual.equals(other.virtual)) {
-            return false;
-        }
         return true;
     }
+
+    public ObjectState share() {
+        copyOnWrite = true;
+        return this;
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeBlockState.java	Fri Jul 24 16:20:56 2015 +0200
@@ -24,16 +24,22 @@
 
 import java.util.*;
 
-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.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 
 public abstract class PartialEscapeBlockState<T extends PartialEscapeBlockState<T>> extends EffectsBlockState<T> {
 
-    protected final Map<VirtualObjectNode, ObjectState> objectStates = Node.newIdentityMap();
+    private static final ObjectState[] EMPTY_ARRAY = new ObjectState[0];
+
+    private ObjectState[] objectStates;
+
+    private static class RefCount {
+        private int refCount = 1;
+    }
+
+    private RefCount arrayRefCount;
 
     /**
      * Final subclass of PartialEscapeBlockState, for performance and to make everything behave
@@ -50,32 +56,91 @@
     }
 
     protected PartialEscapeBlockState() {
+        objectStates = EMPTY_ARRAY;
+        arrayRefCount = new RefCount();
     }
 
     protected PartialEscapeBlockState(PartialEscapeBlockState<T> other) {
         super(other);
-        for (Map.Entry<VirtualObjectNode, ObjectState> entry : other.objectStates.entrySet()) {
-            objectStates.put(entry.getKey(), entry.getValue().cloneState());
+        adoptAddObjectStates(other);
+    }
+
+    public ObjectState getObjectState(int object) {
+        ObjectState state = objectStates[object];
+        assert state != null;
+        return state;
+    }
+
+    public ObjectState getObjectStateOptional(int object) {
+        return object >= objectStates.length ? null : objectStates[object];
+    }
+
+    public ObjectState getObjectState(VirtualObjectNode object) {
+        ObjectState state = objectStates[object.getObjectId()];
+        assert state != null;
+        return state;
+    }
+
+    public ObjectState getObjectStateOptional(VirtualObjectNode object) {
+        int id = object.getObjectId();
+        return id >= objectStates.length ? null : objectStates[id];
+    }
+
+    private ObjectState[] getObjectStateArrayForModification() {
+        if (arrayRefCount.refCount > 1) {
+            objectStates = objectStates.clone();
+            arrayRefCount = new RefCount();
+        }
+        return objectStates;
+    }
+
+    private ObjectState getObjectStateForModification(int object) {
+        ObjectState[] array = getObjectStateArrayForModification();
+        ObjectState objectState = array[object];
+        if (objectState.copyOnWrite) {
+            array[object] = objectState = objectState.cloneState();
+        }
+        return objectState;
+    }
+
+    public void setEntry(int object, int entryIndex, ValueNode value) {
+        if (objectStates[object].getEntry(entryIndex) != value) {
+            getObjectStateForModification(object).setEntry(entryIndex, value);
         }
     }
 
-    public ObjectState getObjectState(VirtualObjectNode object) {
-        assert objectStates.containsKey(object);
-        return objectStates.get(object);
+    public void escape(int object, ValueNode materialized) {
+        getObjectStateForModification(object).escape(materialized);
     }
 
-    public ObjectState getObjectStateOptional(VirtualObjectNode object) {
-        return objectStates.get(object);
+    public void addLock(int object, MonitorIdNode monitorId) {
+        getObjectStateForModification(object).addLock(monitorId);
     }
 
-    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, EscapeState state, GraphEffectList materializeEffects) {
+    public MonitorIdNode removeLock(int object) {
+        return getObjectStateForModification(object).removeLock();
+    }
+
+    public void setEnsureVirtualized(int object, boolean ensureVirtualized) {
+        if (objectStates[object].getEnsureVirtualized() != ensureVirtualized) {
+            getObjectStateForModification(object).setEnsureVirtualized(ensureVirtualized);
+        }
+    }
+
+    public void updateMaterializedValue(int object, ValueNode value) {
+        if (objectStates[object].getMaterializedValue() != value) {
+            getObjectStateForModification(object).updateMaterializedValue(value);
+        }
+    }
+
+    public void materializeBefore(FixedNode fixed, VirtualObjectNode virtual, GraphEffectList materializeEffects) {
         PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
         List<AllocatedObjectNode> objects = new ArrayList<>(2);
         List<ValueNode> values = new ArrayList<>(8);
         List<List<MonitorIdNode>> locks = new ArrayList<>(2);
         List<ValueNode> otherAllocations = new ArrayList<>(2);
         List<Boolean> ensureVirtual = new ArrayList<>(2);
-        materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations, state);
+        materializeWithCommit(fixed, virtual, objects, locks, values, ensureVirtual, otherAllocations);
 
         materializeEffects.add("materializeBefore", (graph, obsoleteNodes) -> {
             for (ValueNode otherAllocation : otherAllocations) {
@@ -117,13 +182,14 @@
     }
 
     private void materializeWithCommit(FixedNode fixed, VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<List<MonitorIdNode>> locks, List<ValueNode> values,
-                    List<Boolean> ensureVirtual, List<ValueNode> otherAllocations, EscapeState state) {
+                    List<Boolean> ensureVirtual, List<ValueNode> otherAllocations) {
         ObjectState obj = getObjectState(virtual);
 
         ValueNode[] entries = obj.getEntries();
         ValueNode representation = virtual.getMaterializedRepresentation(fixed, entries, obj.getLocks());
-        obj.escape(representation, state);
-        PartialEscapeClosure.updateStatesForMaterialized(this, obj);
+        escape(virtual.getObjectId(), representation);
+        obj = getObjectState(virtual);
+        PartialEscapeClosure.updateStatesForMaterialized(this, virtual, obj.getMaterializedValue());
         if (representation instanceof AllocatedObjectNode) {
             objects.add((AllocatedObjectNode) representation);
             locks.add(LockState.asList(obj.getLocks()));
@@ -134,9 +200,11 @@
             }
             for (int i = 0; i < entries.length; i++) {
                 if (entries[i] instanceof VirtualObjectNode) {
-                    ObjectState entryObj = getObjectState((VirtualObjectNode) entries[i]);
+                    VirtualObjectNode entryVirtual = (VirtualObjectNode) entries[i];
+                    ObjectState entryObj = getObjectState(entryVirtual);
                     if (entryObj.isVirtual()) {
-                        materializeWithCommit(fixed, entryObj.getVirtualObject(), objects, locks, values, ensureVirtual, otherAllocations, state);
+                        materializeWithCommit(fixed, entryVirtual, objects, locks, values, ensureVirtual, otherAllocations);
+                        entryObj = getObjectState(entryVirtual);
                     }
                     values.set(pos + i, entryObj.getMaterializedValue());
                 } else {
@@ -155,26 +223,46 @@
         VirtualUtil.trace("materialized %s as %s with values %s", virtual, representation, values);
     }
 
-    public void addObject(VirtualObjectNode virtual, ObjectState state) {
-        objectStates.put(virtual, state);
+    public void addObject(int virtual, ObjectState state) {
+        ensureSize(virtual)[virtual] = state;
     }
 
-    public Iterable<ObjectState> getStates() {
-        return objectStates.values();
+    private ObjectState[] ensureSize(int objectId) {
+        if (objectStates.length <= objectId) {
+            objectStates = Arrays.copyOf(objectStates, Math.max(objectId * 2, 4));
+            arrayRefCount.refCount--;
+            arrayRefCount = new RefCount();
+            return objectStates;
+        } else {
+            return getObjectStateArrayForModification();
+        }
     }
 
-    public Set<VirtualObjectNode> getVirtualObjects() {
-        return objectStates.keySet();
+    public int getStateCount() {
+        return objectStates.length;
     }
 
     @Override
     public String toString() {
-        return super.toString() + ", Object States: " + objectStates;
+        return super.toString() + ", Object States: " + Arrays.toString(objectStates);
     }
 
     @Override
     public boolean equivalentTo(T other) {
-        return compareMaps(objectStates, other.objectStates);
+        int length = Math.max(objectStates.length, other.getStateCount());
+        for (int i = 0; i < length; i++) {
+            ObjectState left = getObjectStateOptional(i);
+            ObjectState right = other.getObjectStateOptional(i);
+            if (left != right) {
+                if (left == null || right == null) {
+                    return false;
+                }
+                if (!left.equals(right)) {
+                    return false;
+                }
+            }
+        }
+        return true;
     }
 
     protected static <K, V> boolean compareMaps(Map<K, V> left, Map<K, V> right) {
@@ -212,4 +300,42 @@
         }
     }
 
+    public void resetObjectStates(int size) {
+        objectStates = new ObjectState[size];
+    }
+
+    public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states) {
+        for (int i = 1; i < states.length; i++) {
+            if (states[0].objectStates != states[i].objectStates) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public static boolean identicalObjectStates(PartialEscapeBlockState<?>[] states, int object) {
+        for (int i = 1; i < states.length; i++) {
+            if (states[0].objectStates[object] != states[i].objectStates[object]) {
+                return false;
+            }
+        }
+        return true;
+    }
+
+    public void adoptAddObjectStates(PartialEscapeBlockState<?> other) {
+        if (objectStates != null) {
+            arrayRefCount.refCount--;
+        }
+        objectStates = other.objectStates;
+        arrayRefCount = other.arrayRefCount;
+
+        if (arrayRefCount.refCount == 1) {
+            for (ObjectState state : objectStates) {
+                if (state != null) {
+                    state.share();
+                }
+            }
+        }
+        arrayRefCount.refCount++;
+    }
 }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Fri Jul 24 16:20:56 2015 +0200
@@ -23,6 +23,7 @@
 package com.oracle.graal.virtual.phases.ea;
 
 import java.util.*;
+import java.util.function.*;
 
 import com.oracle.graal.debug.*;
 import jdk.internal.jvmci.meta.*;
@@ -37,7 +38,6 @@
 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.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.schedule.*;
 
@@ -56,12 +56,14 @@
     private final NodeBitMap hasVirtualInputs;
     private final VirtualizerToolImpl tool;
 
+    public final ArrayList<VirtualObjectNode> virtualObjects = new ArrayList<>();
+
     private final class CollectVirtualObjectsClosure extends NodeClosure<ValueNode> {
-        private final Set<ObjectState> virtual;
+        private final Set<VirtualObjectNode> virtual;
         private final GraphEffectList effects;
         private final BlockT state;
 
-        private CollectVirtualObjectsClosure(Set<ObjectState> virtual, GraphEffectList effects, BlockT state) {
+        private CollectVirtualObjectsClosure(Set<VirtualObjectNode> virtual, GraphEffectList effects, BlockT state) {
             this.virtual = virtual;
             this.effects = effects;
             this.state = state;
@@ -69,20 +71,17 @@
 
         @Override
         public void apply(Node usage, ValueNode value) {
-            ObjectState valueObj = getObjectState(state, value);
-            if (valueObj != null) {
-                virtual.add(valueObj);
-                effects.replaceFirstInput(usage, value, valueObj.virtual);
-            } else if (value instanceof VirtualObjectNode) {
-                ObjectState virtualObj = null;
-                for (ObjectState obj : state.getStates()) {
-                    if (value == obj.virtual) {
-                        virtualObj = obj;
-                        break;
-                    }
+            if (value instanceof VirtualObjectNode) {
+                VirtualObjectNode object = (VirtualObjectNode) value;
+                if (object.getObjectId() != -1 && state.getObjectStateOptional(object) != null) {
+                    virtual.add(object);
                 }
-                if (virtualObj != null) {
-                    virtual.add(virtualObj);
+            } else {
+                ValueNode alias = getAlias(value);
+                if (alias instanceof VirtualObjectNode) {
+                    VirtualObjectNode object = (VirtualObjectNode) alias;
+                    virtual.add(object);
+                    effects.replaceFirstInput(usage, value, object);
                 }
             }
         }
@@ -196,17 +195,11 @@
         }
         if (canonicalizedValue != node && canonicalizedValue != null) {
             if (canonicalizedValue.isAlive()) {
-                ObjectState obj = getObjectState(state, canonicalizedValue);
-                if (obj != null) {
-                    if (obj.getState() == EscapeState.Virtual) {
-                        addAndMarkAlias(obj.virtual, node);
-                        effects.deleteNode(node);
-                    } else {
-                        effects.replaceAtUsages(node, obj.getMaterializedValue());
-                        addScalarAlias(node, obj.getMaterializedValue());
-                    }
+                ValueNode alias = getAliasAndResolve(state, canonicalizedValue);
+                if (alias instanceof VirtualObjectNode) {
+                    addAndMarkAlias((VirtualObjectNode) alias, node);
+                    effects.deleteNode(node);
                 } else {
-                    ValueNode alias = getScalarAlias(canonicalizedValue);
                     effects.replaceAtUsages(node, alias);
                     addScalarAlias(node, alias);
                 }
@@ -240,7 +233,7 @@
                 if (input.isAlive()) {
                     ObjectState obj = getObjectState(state, (ValueNode) input);
                     if (obj != null) {
-                        if (obj.getState() == EscapeState.Virtual) {
+                        if (obj.isVirtual()) {
                             return false;
                         } else {
                             pos.initialize(node, obj.getMaterializedValue());
@@ -262,10 +255,12 @@
         VirtualUtil.trace("processing nodewithstate: %s", node);
         for (Node input : node.inputs()) {
             if (input instanceof ValueNode) {
-                ObjectState obj = getObjectState(state, (ValueNode) input);
-                if (obj != null) {
-                    VirtualUtil.trace("replacing input %s at %s: %s", input, node, obj);
-                    replaceWithMaterialized(input, node, insertBefore, state, obj, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
+                ValueNode alias = getAlias((ValueNode) input);
+                if (alias instanceof VirtualObjectNode) {
+                    int id = ((VirtualObjectNode) alias).getObjectId();
+                    ensureMaterialized(state, id, insertBefore, effects, METRIC_MATERIALIZATIONS_UNHANDLED);
+                    effects.replaceFirstInput(node, input, state.getObjectState(id).getMaterializedValue());
+                    VirtualUtil.trace("replacing input %s at %s", input, node);
                 }
             }
         }
@@ -287,11 +282,11 @@
     private void processNodeWithState(NodeWithState nodeWithState, BlockT state, GraphEffectList effects) {
         for (FrameState fs : nodeWithState.states()) {
             FrameState frameState = getUniqueFramestate(nodeWithState, fs);
-            Set<ObjectState> virtual = new ArraySet<>();
+            Set<VirtualObjectNode> virtual = new ArraySet<>();
             frameState.applyToNonVirtual(new CollectVirtualObjectsClosure(virtual, effects, state));
             collectLockedVirtualObjects(state, virtual);
             collectReferencedVirtualObjects(state, virtual);
-            addVirtualMappings(effects, frameState, virtual);
+            addVirtualMappings(frameState, virtual, state, effects);
         }
     }
 
@@ -305,23 +300,27 @@
         return frameState;
     }
 
-    private static void addVirtualMappings(GraphEffectList effects, FrameState frameState, Set<ObjectState> virtual) {
-        for (ObjectState obj : virtual) {
-            effects.addVirtualMapping(frameState, obj.createEscapeObjectState());
+    private void addVirtualMappings(FrameState frameState, Set<VirtualObjectNode> virtual, BlockT state, GraphEffectList effects) {
+        for (VirtualObjectNode obj : virtual) {
+            effects.addVirtualMapping(frameState, state.getObjectState(obj).createEscapeObjectState(obj));
         }
     }
 
-    private void collectReferencedVirtualObjects(final BlockT state, final Set<ObjectState> virtual) {
-        ArrayDeque<ObjectState> queue = new ArrayDeque<>(virtual);
+    private void collectReferencedVirtualObjects(BlockT state, Set<VirtualObjectNode> virtual) {
+        ArrayDeque<VirtualObjectNode> queue = new ArrayDeque<>(virtual);
         while (!queue.isEmpty()) {
-            ObjectState obj = queue.removeLast();
-            if (obj.isVirtual()) {
-                for (ValueNode entry : obj.getEntries()) {
-                    if (entry instanceof VirtualObjectNode) {
-                        ObjectState fieldObj = state.getObjectState((VirtualObjectNode) entry);
-                        if (fieldObj.isVirtual() && !virtual.contains(fieldObj)) {
-                            virtual.add(fieldObj);
-                            queue.addLast(fieldObj);
+            VirtualObjectNode object = queue.removeLast();
+            int id = object.getObjectId();
+            if (id != -1) {
+                ObjectState objState = state.getObjectStateOptional(id);
+                if (objState != null && objState.isVirtual()) {
+                    for (ValueNode entry : objState.getEntries()) {
+                        if (entry instanceof VirtualObjectNode) {
+                            VirtualObjectNode entryVirtual = (VirtualObjectNode) entry;
+                            if (!virtual.contains(entryVirtual)) {
+                                virtual.add(entryVirtual);
+                                queue.addLast(entryVirtual);
+                            }
                         }
                     }
                 }
@@ -329,10 +328,11 @@
         }
     }
 
-    private void collectLockedVirtualObjects(final BlockT state, Set<ObjectState> virtual) {
-        for (ObjectState obj : state.getStates()) {
-            if (obj.isVirtual() && obj.hasLocks()) {
-                virtual.add(obj);
+    private void collectLockedVirtualObjects(BlockT state, Set<VirtualObjectNode> virtual) {
+        for (int i = 0; i < state.getStateCount(); i++) {
+            ObjectState objState = state.getObjectStateOptional(i);
+            if (objState != null && objState.isVirtual() && objState.hasLocks()) {
+                virtual.add(virtualObjects.get(i));
             }
         }
     }
@@ -340,49 +340,41 @@
     /**
      * @return true if materialization happened, false if not.
      */
-    private boolean ensureMaterialized(BlockT state, ObjectState obj, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) {
-        assert obj != null;
-        if (obj.getState() == EscapeState.Virtual) {
+    private boolean ensureMaterialized(PartialEscapeBlockState<?> state, int object, FixedNode materializeBefore, GraphEffectList effects, DebugMetric metric) {
+        if (state.getObjectState(object).isVirtual()) {
             metric.increment();
-            state.materializeBefore(materializeBefore, obj.virtual, EscapeState.Materialized, effects);
-            updateStatesForMaterialized(state, obj);
-            assert !obj.isVirtual();
+            VirtualObjectNode virtual = virtualObjects.get(object);
+            state.materializeBefore(materializeBefore, virtual, effects);
+            updateStatesForMaterialized(state, virtual, state.getObjectState(object).getMaterializedValue());
             return true;
         } else {
-            assert obj.getState() == EscapeState.Materialized;
             return false;
         }
     }
 
-    public static void updateStatesForMaterialized(PartialEscapeBlockState<?> state, ObjectState obj) {
+    public static void updateStatesForMaterialized(PartialEscapeBlockState<?> state, VirtualObjectNode virtual, ValueNode materializedValue) {
         // update all existing states with the newly materialized object
-        for (ObjectState objState : state.objectStates.values()) {
-            if (objState.isVirtual()) {
+        for (int i = 0; i < state.getStateCount(); i++) {
+            ObjectState objState = state.getObjectStateOptional(i);
+            if (objState != null && objState.isVirtual()) {
                 ValueNode[] entries = objState.getEntries();
-                for (int i = 0; i < entries.length; i++) {
-                    if (entries[i] == obj.virtual) {
-                        objState.setEntry(i, obj.getMaterializedValue());
+                for (int i2 = 0; i2 < entries.length; i2++) {
+                    if (entries[i2] == virtual) {
+                        state.setEntry(i, i2, materializedValue);
                     }
                 }
             }
         }
     }
 
-    private boolean replaceWithMaterialized(Node value, Node usage, FixedNode materializeBefore, BlockT state, ObjectState obj, GraphEffectList effects, DebugMetric metric) {
-        boolean materialized = ensureMaterialized(state, obj, materializeBefore, effects, metric);
-        effects.replaceFirstInput(usage, value, obj.getMaterializedValue());
-        return materialized;
-    }
-
     @Override
     protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) {
-        super.processInitialLoopState(loop, initialState);
-
         for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) {
             if (phi.valueAt(0) != null) {
-                ObjectState state = getObjectState(initialState, phi.valueAt(0));
-                if (state != null && state.isVirtual()) {
-                    addAndMarkAlias(state.virtual, phi);
+                ValueNode alias = getAliasAndResolve(initialState, phi.valueAt(0));
+                if (alias instanceof VirtualObjectNode) {
+                    VirtualObjectNode virtual = (VirtualObjectNode) alias;
+                    addAndMarkAlias(virtual, phi);
                 }
             }
         }
@@ -391,49 +383,55 @@
     @Override
     protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) {
         if (exitNode.graph().hasValueProxies()) {
-            Map<VirtualObjectNode, ProxyNode> proxies = Node.newMap();
+            Map<Integer, ProxyNode> proxies = new IdentityHashMap<>();
             for (ProxyNode proxy : exitNode.proxies()) {
-                ObjectState obj = getObjectState(exitState, proxy.value());
-                if (obj != null) {
-                    proxies.put(obj.virtual, proxy);
+                ValueNode alias = getAlias(proxy.value());
+                if (alias instanceof VirtualObjectNode) {
+                    VirtualObjectNode virtual = (VirtualObjectNode) alias;
+                    proxies.put(virtual.getObjectId(), proxy);
                 }
             }
-            for (ObjectState obj : exitState.getStates()) {
-                ObjectState initialObj = initialState.getObjectStateOptional(obj.virtual);
-                if (obj.isVirtual()) {
-                    processVirtualAtLoopExit(exitNode, effects, obj, initialObj);
-                } else {
-                    processMaterializedAtLoopExit(exitNode, effects, proxies, obj, initialObj);
+            for (int i = 0; i < exitState.getStateCount(); i++) {
+                ObjectState exitObjState = exitState.getObjectStateOptional(i);
+                if (exitObjState != null) {
+                    ObjectState initialObjState = initialState.getObjectStateOptional(i);
+
+                    if (exitObjState.isVirtual()) {
+                        processVirtualAtLoopExit(exitNode, effects, i, exitObjState, initialObjState, exitState);
+                    } else {
+                        processMaterializedAtLoopExit(exitNode, effects, proxies, i, exitObjState, initialObjState, exitState);
+                    }
                 }
             }
         }
     }
 
-    private static void processMaterializedAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, Map<VirtualObjectNode, ProxyNode> proxies, ObjectState obj, ObjectState initialObj) {
-        if (initialObj == null || initialObj.isVirtual()) {
-            ProxyNode proxy = proxies.get(obj.virtual);
+    private static void processMaterializedAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, Map<Integer, ProxyNode> proxies, int object, ObjectState exitObjState,
+                    ObjectState initialObjState, PartialEscapeBlockState<?> exitState) {
+        if (initialObjState == null || initialObjState.isVirtual()) {
+            ProxyNode proxy = proxies.get(object);
             if (proxy == null) {
-                proxy = new ValueProxyNode(obj.getMaterializedValue(), exitNode);
+                proxy = new ValueProxyNode(exitObjState.getMaterializedValue(), exitNode);
                 effects.addFloatingNode(proxy, "proxy");
             } else {
-                effects.replaceFirstInput(proxy, proxy.value(), obj.getMaterializedValue());
+                effects.replaceFirstInput(proxy, proxy.value(), exitObjState.getMaterializedValue());
                 // nothing to do - will be handled in processNode
             }
-            obj.updateMaterializedValue(proxy);
+            exitState.updateMaterializedValue(object, proxy);
         } else {
-            if (initialObj.getMaterializedValue() == obj.getMaterializedValue()) {
-                Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObj.getMaterializedValue(), obj.getMaterializedValue(), exitNode);
+            if (initialObjState.getMaterializedValue() != exitObjState.getMaterializedValue()) {
+                Debug.log("materialized value changes within loop: %s vs. %s at %s", initialObjState.getMaterializedValue(), exitObjState.getMaterializedValue(), exitNode);
             }
         }
     }
 
-    private static void processVirtualAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, ObjectState obj, ObjectState initialObj) {
-        for (int i = 0; i < obj.getEntries().length; i++) {
-            ValueNode value = obj.getEntry(i);
+    private static void processVirtualAtLoopExit(LoopExitNode exitNode, GraphEffectList effects, int object, ObjectState exitObjState, ObjectState initialObjState, PartialEscapeBlockState<?> exitState) {
+        for (int i = 0; i < exitObjState.getEntries().length; i++) {
+            ValueNode value = exitState.getObjectState(object).getEntry(i);
             if (!(value instanceof VirtualObjectNode || value.isConstant())) {
-                if (exitNode.loopBegin().isPhiAtMerge(value) || initialObj == null || !initialObj.isVirtual() || initialObj.getEntry(i) != value) {
+                if (exitNode.loopBegin().isPhiAtMerge(value) || initialObjState == null || !initialObjState.isVirtual() || initialObjState.getEntry(i) != value) {
                     ProxyNode proxy = new ValueProxyNode(value, exitNode);
-                    obj.setEntry(i, proxy);
+                    exitState.setEntry(object, i, proxy);
                     effects.addFloatingNode(proxy, "virtualProxy");
                 }
             }
@@ -525,56 +523,72 @@
          * reached. This method needs to be careful to place the effects of the merging operation
          * into the correct blocks.
          *
-         * @param states the predecessor block states of the merge
+         * @param statesList the predecessor block states of the merge
          */
         @Override
-        protected void merge(List<BlockT> states) {
-            super.merge(states);
+        protected void merge(List<BlockT> statesList) {
+            super.merge(statesList);
+
+            PartialEscapeBlockState<?>[] states = new PartialEscapeBlockState<?>[statesList.size()];
+            for (int i = 0; i < statesList.size(); i++) {
+                states[i] = statesList.get(i);
+            }
 
             // calculate the set of virtual objects that exist in all predecessors
-            Set<VirtualObjectNode> virtualObjTemp = intersectVirtualObjects(states);
+            int[] virtualObjTemp = intersectVirtualObjects(states);
 
-            ObjectState[] objStates = new ObjectState[states.size()];
             boolean materialized;
             do {
                 materialized = false;
-                for (VirtualObjectNode object : virtualObjTemp) {
-                    for (int i = 0; i < objStates.length; i++) {
-                        objStates[i] = states.get(i).getObjectState(object);
-                    }
 
-                    // determine if all inputs are virtual or the same materialized value
-                    int virtual = 0;
-                    ObjectState startObj = objStates[0];
-                    boolean locksMatch = true;
-                    ValueNode uniqueMaterializedValue = startObj.isVirtual() ? null : startObj.getMaterializedValue();
-                    for (ObjectState obj : objStates) {
-                        if (obj.isVirtual()) {
-                            virtual++;
-                            uniqueMaterializedValue = null;
-                            locksMatch &= obj.locksEqual(startObj);
-                        } else if (obj.getMaterializedValue() != uniqueMaterializedValue) {
-                            uniqueMaterializedValue = null;
+                if (PartialEscapeBlockState.identicalObjectStates(states)) {
+                    newState.adoptAddObjectStates(states[0]);
+                } else {
+
+                    for (int object : virtualObjTemp) {
+
+                        if (PartialEscapeBlockState.identicalObjectStates(states, object)) {
+                            newState.addObject(object, states[0].getObjectState(object).share());
+                            continue;
                         }
-                    }
 
-                    if (virtual == objStates.length && locksMatch) {
-                        materialized |= mergeObjectStates(object, objStates, states);
-                    } else {
-                        if (uniqueMaterializedValue != null) {
-                            newState.addObject(object, new ObjectState(object, uniqueMaterializedValue, EscapeState.Materialized, null, false));
+                        // determine if all inputs are virtual or the same materialized value
+                        int virtualCount = 0;
+                        ObjectState startObj = states[0].getObjectState(object);
+                        boolean locksMatch = true;
+                        boolean ensureVirtual = true;
+                        ValueNode uniqueMaterializedValue = startObj.isVirtual() ? null : startObj.getMaterializedValue();
+                        for (int i = 0; i < states.length; i++) {
+                            ObjectState obj = states[i].getObjectState(object);
+                            ensureVirtual &= obj.getEnsureVirtualized();
+                            if (obj.isVirtual()) {
+                                virtualCount++;
+                                uniqueMaterializedValue = null;
+                                locksMatch &= obj.locksEqual(startObj);
+                            } else if (obj.getMaterializedValue() != uniqueMaterializedValue) {
+                                uniqueMaterializedValue = null;
+                            }
+                        }
+
+                        if (virtualCount == states.length && locksMatch) {
+                            materialized |= mergeObjectStates(object, null, states);
                         } else {
-                            PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object));
-                            mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi");
-                            for (int i = 0; i < objStates.length; i++) {
-                                ObjectState obj = objStates[i];
-                                if (obj.isVirtual()) {
-                                    Block predecessor = getPredecessor(i);
-                                    materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                            if (uniqueMaterializedValue != null) {
+                                newState.addObject(object, new ObjectState(uniqueMaterializedValue, null, ensureVirtual));
+                            } else {
+                                PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object));
+                                mergeEffects.addFloatingNode(materializedValuePhi, "materializedPhi");
+                                for (int i = 0; i < states.length; i++) {
+                                    ObjectState obj = states[i].getObjectState(object);
+                                    if (obj.isVirtual()) {
+                                        Block predecessor = getPredecessor(i);
+                                        materialized |= ensureMaterialized(states[i], object, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                                        obj = states[i].getObjectState(object);
+                                    }
+                                    setPhiInput(materializedValuePhi, i, obj.getMaterializedValue());
                                 }
-                                setPhiInput(materializedValuePhi, i, obj.getMaterializedValue());
+                                newState.addObject(object, new ObjectState(materializedValuePhi, null, false));
                             }
-                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null, false));
                         }
                     }
                 }
@@ -585,19 +599,41 @@
                     }
                 }
                 if (materialized) {
-                    newState.objectStates.clear();
+                    newState.resetObjectStates(virtualObjects.size());
                     mergeEffects.clear();
                     afterMergeEffects.clear();
                 }
             } while (materialized);
         }
 
-        private Set<VirtualObjectNode> intersectVirtualObjects(List<BlockT> states) {
-            Set<VirtualObjectNode> virtualObjTemp = Node.newSet(states.get(0).getVirtualObjects());
-            for (int i = 1; i < states.size(); i++) {
-                virtualObjTemp.retainAll(states.get(i).getVirtualObjects());
+        private int[] intersectVirtualObjects(PartialEscapeBlockState<?>[] states) {
+            int length = states[0].getStateCount();
+            for (int i = 1; i < states.length; i++) {
+                length = Math.min(length, states[i].getStateCount());
             }
-            return virtualObjTemp;
+            boolean[] result = new boolean[length];
+            Arrays.fill(result, true);
+            int count = length;
+            for (int i = 0; i < states.length; i++) {
+                PartialEscapeBlockState<?> state = states[i];
+                for (int i2 = 0; i2 < length; i2++) {
+                    if (result[i2]) {
+                        if (state.getObjectStateOptional(i2) == null) {
+                            result[i2] = false;
+                            count--;
+                        }
+                    }
+                }
+            }
+            int[] resultInts = new int[count];
+            int index = 0;
+            for (int i = 0; i < length; i++) {
+                if (result[i]) {
+                    resultInts[index++] = i;
+                }
+            }
+            assert index == count;
+            return resultInts;
         }
 
         /**
@@ -608,28 +644,30 @@
          * can be incompatible if they contain {@code long} or {@code double} values occupying two
          * {@code int} slots in such a way that that their values cannot be merged using PhiNodes.
          *
-         * @param object the virtual object that should be associated with the merged object state
-         * @param objStates the incoming object states (all of which need to be virtual)
-         * @param blockStates the predecessor block states of the merge
+         * @param states the predecessor block states of the merge
          * @return true if materialization happened during the merge, false otherwise
          */
-        private boolean mergeObjectStates(VirtualObjectNode object, ObjectState[] objStates, List<BlockT> blockStates) {
+        private boolean mergeObjectStates(int resultObject, int[] sourceObjects, PartialEscapeBlockState<?>[] states) {
             boolean compatible = true;
             boolean ensureVirtual = true;
-            ValueNode[] values = objStates[0].getEntries().clone();
+            IntFunction<Integer> getObject = index -> sourceObjects == null ? resultObject : sourceObjects[index];
+
+            VirtualObjectNode virtual = virtualObjects.get(resultObject);
+            int entryCount = virtual.entryCount();
 
             // determine all entries that have a two-slot value
             Kind[] twoSlotKinds = null;
-            outer: for (int i = 0; i < objStates.length; i++) {
-                ValueNode[] entries = objStates[i].getEntries();
+            outer: for (int i = 0; i < states.length; i++) {
+                ObjectState objectState = states[i].getObjectState(getObject.apply(i));
+                ValueNode[] entries = objectState.getEntries();
                 int valueIndex = 0;
-                ensureVirtual &= objStates[i].getEnsureVirtualized();
-                while (valueIndex < values.length) {
+                ensureVirtual &= objectState.getEnsureVirtualized();
+                while (valueIndex < entryCount) {
                     Kind otherKind = entries[valueIndex].getKind();
-                    Kind entryKind = object.entryKind(valueIndex);
+                    Kind entryKind = virtual.entryKind(valueIndex);
                     if (entryKind == Kind.Int && otherKind.needsTwoSlots()) {
                         if (twoSlotKinds == null) {
-                            twoSlotKinds = new Kind[values.length];
+                            twoSlotKinds = new Kind[entryCount];
                         }
                         if (twoSlotKinds[valueIndex] != null && twoSlotKinds[valueIndex] != otherKind) {
                             compatible = false;
@@ -647,18 +685,20 @@
             }
             if (compatible && twoSlotKinds != null) {
                 // if there are two-slot values then make sure the incoming states can be merged
-                outer: for (int valueIndex = 0; valueIndex < values.length; valueIndex++) {
+                outer: for (int valueIndex = 0; valueIndex < entryCount; valueIndex++) {
                     if (twoSlotKinds[valueIndex] != null) {
-                        assert valueIndex < object.entryCount() - 1 && object.entryKind(valueIndex) == Kind.Int && object.entryKind(valueIndex + 1) == Kind.Int;
-                        for (int i = 0; i < objStates.length; i++) {
-                            ValueNode value = objStates[i].getEntry(valueIndex);
+                        assert valueIndex < virtual.entryCount() - 1 && virtual.entryKind(valueIndex) == Kind.Int && virtual.entryKind(valueIndex + 1) == Kind.Int;
+                        for (int i = 0; i < states.length; i++) {
+                            int object = getObject.apply(i);
+                            ObjectState objectState = states[i].getObjectState(object);
+                            ValueNode value = objectState.getEntry(valueIndex);
                             Kind valueKind = value.getKind();
                             if (valueKind != twoSlotKinds[valueIndex]) {
-                                ValueNode nextValue = objStates[i].getEntry(valueIndex + 1);
+                                ValueNode nextValue = objectState.getEntry(valueIndex + 1);
                                 if (value.isConstant() && value.asConstant().equals(JavaConstant.INT_0) && nextValue.isConstant() && nextValue.asConstant().equals(JavaConstant.INT_0)) {
                                     // rewrite to a zero constant of the larger kind
-                                    objStates[i].setEntry(valueIndex, ConstantNode.defaultForKind(twoSlotKinds[valueIndex], graph()));
-                                    objStates[i].setEntry(valueIndex + 1, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()));
+                                    states[i].setEntry(object, valueIndex, ConstantNode.defaultForKind(twoSlotKinds[valueIndex], graph()));
+                                    states[i].setEntry(object, valueIndex + 1, ConstantNode.forConstant(JavaConstant.forIllegal(), tool.getMetaAccessProvider(), graph()));
                                 } else {
                                     compatible = false;
                                     break outer;
@@ -671,12 +711,13 @@
 
             if (compatible) {
                 // virtual objects are compatible: create phis for all entries that need them
-                PhiNode[] phis = getValuePhis(object, object.entryCount());
+                ValueNode[] values = states[0].getObjectState(getObject.apply(0)).getEntries().clone();
+                PhiNode[] phis = getValuePhis(virtual, virtual.entryCount());
                 int valueIndex = 0;
                 while (valueIndex < values.length) {
-                    for (int i = 1; i < objStates.length; i++) {
+                    for (int i = 1; i < states.length; i++) {
                         if (phis[valueIndex] == null) {
-                            ValueNode field = objStates[i].getEntry(valueIndex);
+                            ValueNode field = states[i].getObjectState(getObject.apply(i)).getEntry(valueIndex);
                             if (values[valueIndex] != field) {
                                 phis[valueIndex] = createValuePhi(values[valueIndex].stamp().unrestricted());
                             }
@@ -699,26 +740,31 @@
                     PhiNode phi = phis[i];
                     if (phi != null) {
                         mergeEffects.addFloatingNode(phi, "virtualMergePhi");
-                        if (object.entryKind(i) == Kind.Object) {
-                            materialized |= mergeObjectEntry(objStates, blockStates, phi, i);
+                        if (virtual.entryKind(i) == Kind.Object) {
+                            materialized |= mergeObjectEntry(getObject, states, phi, i);
                         } else {
-                            mergePrimitiveEntry(objStates, phi, i);
+                            for (int i2 = 0; i2 < states.length; i2++) {
+                                ObjectState state = states[i2].getObjectState(getObject.apply(i2));
+                                if (!state.isVirtual()) {
+                                    break;
+                                }
+                                setPhiInput(phi, i2, state.getEntry(i));
+                            }
                         }
                         values[i] = phi;
                     }
                 }
-                newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, objStates[0].getLocks(), ensureVirtual));
+                newState.addObject(resultObject, new ObjectState(values, states[0].getObjectState(getObject.apply(0)).getLocks(), ensureVirtual));
                 return materialized;
             } else {
                 // not compatible: materialize in all predecessors
-                PhiNode materializedValuePhi = getPhi(object, StampFactory.forKind(Kind.Object));
-                for (int i = 0; i < blockStates.size(); i++) {
-                    ObjectState obj = objStates[i];
+                PhiNode materializedValuePhi = getPhi(resultObject, StampFactory.forKind(Kind.Object));
+                for (int i = 0; i < states.length; i++) {
                     Block predecessor = getPredecessor(i);
-                    ensureMaterialized(blockStates.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
-                    setPhiInput(materializedValuePhi, i, obj.getMaterializedValue());
+                    ensureMaterialized(states[i], getObject.apply(i), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                    setPhiInput(materializedValuePhi, i, states[i].getObjectState(getObject.apply(i)).getMaterializedValue());
                 }
-                newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Materialized, null, false));
+                newState.addObject(resultObject, new ObjectState(materializedValuePhi, null, ensureVirtual));
                 return true;
             }
         }
@@ -729,19 +775,22 @@
          *
          * @return true if materialization happened during the merge, false otherwise
          */
-        private boolean mergeObjectEntry(ObjectState[] objStates, List<BlockT> blockStates, PhiNode phi, int entryIndex) {
+        private boolean mergeObjectEntry(IntFunction<Integer> objectIdFunc, PartialEscapeBlockState<?>[] states, PhiNode phi, int entryIndex) {
             boolean materialized = false;
-            for (int i = 0; i < objStates.length; i++) {
-                if (!objStates[i].isVirtual()) {
+            for (int i = 0; i < states.length; i++) {
+                int object = objectIdFunc.apply(i);
+                ObjectState objectState = states[i].getObjectState(object);
+                if (!objectState.isVirtual()) {
                     break;
                 }
-                ValueNode entry = objStates[i].getEntry(entryIndex);
+                ValueNode entry = objectState.getEntry(entryIndex);
                 if (entry instanceof VirtualObjectNode) {
-                    ObjectState obj = blockStates.get(i).getObjectState((VirtualObjectNode) entry);
+                    VirtualObjectNode entryVirtual = (VirtualObjectNode) entry;
                     Block predecessor = getPredecessor(i);
-                    materialized |= ensureMaterialized(blockStates.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
-                    if (objStates[i].isVirtual()) {
-                        objStates[i].setEntry(entryIndex, entry = obj.getMaterializedValue());
+                    materialized |= ensureMaterialized(states[i], entryVirtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
+                    objectState = states[i].getObjectState(object);
+                    if (objectState.isVirtual()) {
+                        states[i].setEntry(object, entryIndex, entry = states[i].getObjectState(entryVirtual.getObjectId()).getMaterializedValue());
                     }
                 }
                 setPhiInput(phi, i, entry);
@@ -750,20 +799,6 @@
         }
 
         /**
-         * Fill the inputs of the PhiNode corresponding to one primitive entry in the virtual
-         * object.
-         */
-        private void mergePrimitiveEntry(ObjectState[] objStates, PhiNode phi, int entryIndex) {
-            for (int i = 0; i < objStates.length; i++) {
-                ObjectState state = objStates[i];
-                if (!state.isVirtual()) {
-                    break;
-                }
-                setPhiInput(phi, i, state.getEntry(entryIndex));
-            }
-        }
-
-        /**
          * Examine a PhiNode and try to replace it with merging of virtual objects if all its inputs
          * refer to virtual object states. In order for the merging to happen, all incoming object
          * states need to be compatible and without object identity (meaning that their object
@@ -775,40 +810,46 @@
          *            and therefore also exist in the merged state
          * @return true if materialization happened during the merge, false otherwise
          */
-        private boolean processPhi(ValuePhiNode phi, List<BlockT> states, Set<VirtualObjectNode> mergedVirtualObjects) {
+        private boolean processPhi(ValuePhiNode phi, PartialEscapeBlockState<?>[] states, int[] mergedVirtualObjects) {
             aliases.set(phi, null);
 
             // determine how many inputs are virtual and if they're all the same virtual object
             int virtualInputs = 0;
-            ObjectState[] objStates = new ObjectState[states.size()];
             boolean uniqueVirtualObject = true;
-            for (int i = 0; i < objStates.length; i++) {
-                ObjectState obj = objStates[i] = getObjectState(states.get(i), getPhiValueAt(phi, i));
-                if (obj != null) {
-                    if (obj.isVirtual()) {
-                        if (objStates[0] == null || objStates[0].virtual != obj.virtual) {
+            VirtualObjectNode[] virtualObjs = new VirtualObjectNode[states.length];
+            for (int i = 0; i < states.length; i++) {
+                ValueNode alias = getAlias(getPhiValueAt(phi, i));
+                if (alias instanceof VirtualObjectNode) {
+                    VirtualObjectNode virtual = (VirtualObjectNode) alias;
+                    virtualObjs[i] = virtual;
+                    if (states[i].getObjectState(virtual).isVirtual()) {
+                        if (virtualObjs[0] != alias) {
                             uniqueVirtualObject = false;
                         }
                         virtualInputs++;
                     }
                 }
             }
-            if (virtualInputs == objStates.length) {
+            if (virtualInputs == states.length) {
                 if (uniqueVirtualObject) {
                     // all inputs refer to the same object: just make the phi node an alias
-                    addAndMarkAlias(objStates[0].virtual, phi);
+                    addAndMarkAlias(virtualObjs[0], phi);
                     mergeEffects.deleteNode(phi);
                     return false;
                 } else {
                     // all inputs are virtual: check if they're compatible and without identity
                     boolean compatible = true;
                     boolean hasIdentity = false;
-                    ObjectState firstObj = objStates[0];
-                    for (int i = 0; i < objStates.length; i++) {
-                        ObjectState obj = objStates[i];
-                        hasIdentity |= obj.virtual.hasIdentity();
-                        boolean identitySurvives = obj.virtual.hasIdentity() && mergedVirtualObjects.contains(obj.virtual);
-                        if (identitySurvives || !firstObj.virtual.type().equals(obj.virtual.type()) || firstObj.virtual.entryCount() != obj.virtual.entryCount() || !firstObj.locksEqual(obj)) {
+                    VirtualObjectNode firstVirtual = virtualObjs[0];
+                    for (int i = 0; i < states.length; i++) {
+                        VirtualObjectNode virtual = virtualObjs[i];
+                        hasIdentity |= virtual.hasIdentity();
+                        boolean identitySurvives = virtual.hasIdentity() && Arrays.asList(mergedVirtualObjects).contains(virtual.getObjectId());
+                        if (identitySurvives || !firstVirtual.type().equals(virtual.type()) || firstVirtual.entryCount() != virtual.entryCount()) {
+                            compatible = false;
+                            break;
+                        }
+                        if (!states[0].getObjectState(firstVirtual).locksEqual(states[i].getObjectState(virtual))) {
                             compatible = false;
                             break;
                         }
@@ -816,22 +857,34 @@
                     if (compatible && hasIdentity) {
                         // we still need to check whether this value is referenced by any other phi
                         outer: for (PhiNode otherPhi : getPhis().filter(otherPhi -> otherPhi != phi)) {
-                            for (int i = 0; i < objStates.length; i++) {
-                                ObjectState otherPhiValueState = getObjectState(states.get(i), getPhiValueAt(otherPhi, i));
-                                if (Arrays.asList(objStates).contains(otherPhiValueState)) {
-                                    compatible = false;
-                                    break outer;
+                            for (int i = 0; i < states.length; i++) {
+                                ValueNode alias = getAliasAndResolve(states[i], getPhiValueAt(otherPhi, i));
+                                if (alias instanceof VirtualObjectNode) {
+                                    VirtualObjectNode phiValueVirtual = (VirtualObjectNode) alias;
+                                    if (Arrays.asList(virtualObjs).contains(phiValueVirtual)) {
+                                        compatible = false;
+                                        break outer;
+                                    }
                                 }
                             }
                         }
                     }
 
                     if (compatible) {
-                        VirtualObjectNode virtual = getValueObjectVirtual(phi, getObjectState(states.get(0), getPhiValueAt(phi, 0)).virtual);
+                        VirtualObjectNode virtual = getValueObjectVirtual(phi, virtualObjs[0]);
                         mergeEffects.addFloatingNode(virtual, "valueObjectNode");
                         mergeEffects.deleteNode(phi);
+                        if (virtual.getObjectId() == -1) {
+                            int id = virtualObjects.size();
+                            virtualObjects.add(virtual);
+                            virtual.setObjectId(id);
+                        }
 
-                        boolean materialized = mergeObjectStates(virtual, objStates, states);
+                        int[] virtualObjectIds = new int[states.length];
+                        for (int i = 0; i < states.length; i++) {
+                            virtualObjectIds[i] = virtualObjs[i].getObjectId();
+                        }
+                        boolean materialized = mergeObjectStates(virtual.getObjectId(), virtualObjectIds, states);
                         addAndMarkAlias(virtual, virtual);
                         addAndMarkAlias(virtual, phi);
                         return materialized;
@@ -841,12 +894,12 @@
 
             // otherwise: materialize all phi inputs
             boolean materialized = false;
-            for (int i = 0; i < objStates.length; i++) {
-                ObjectState obj = objStates[i];
-                if (obj != null) {
+            for (int i = 0; i < states.length; i++) {
+                VirtualObjectNode virtual = virtualObjs[i];
+                if (virtual != null) {
                     Block predecessor = getPredecessor(i);
-                    materialized |= ensureMaterialized(states.get(i), obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_PHI);
-                    setPhiInput(phi, i, obj.getMaterializedValue());
+                    materialized |= ensureMaterialized(states[i], virtual.getObjectId(), predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_PHI);
+                    setPhiInput(phi, i, getAliasAndResolve(states[i], virtual));
                 }
             }
             return materialized;
@@ -868,6 +921,29 @@
         }
     }
 
+    public ValueNode getAlias(ValueNode value) {
+        if (value != null && !(value instanceof VirtualObjectNode)) {
+            if (value.isAlive() && !aliases.isNew(value)) {
+                ValueNode result = aliases.get(value);
+                if (result != null) {
+                    return result;
+                }
+            }
+        }
+        return value;
+    }
+
+    public ValueNode getAliasAndResolve(PartialEscapeBlockState<?> state, ValueNode value) {
+        ValueNode result = getAlias(value);
+        if (result instanceof VirtualObjectNode) {
+            int id = ((VirtualObjectNode) result).getObjectId();
+            if (id != -1 && !state.getObjectState(id).isVirtual()) {
+                result = state.getObjectState(id).getMaterializedValue();
+            }
+        }
+        return result;
+    }
+
     void addAndMarkAlias(VirtualObjectNode virtual, ValueNode node) {
         if (node.isAlive()) {
             aliases.set(node, virtual);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapePhase.java	Fri Jul 24 16:20:56 2015 +0200
@@ -33,6 +33,7 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.schedule.*;
@@ -83,6 +84,9 @@
 
     @Override
     protected Closure<?> createEffectsClosure(PhaseContext context, SchedulePhase schedule, ControlFlowGraph cfg) {
+        for (VirtualObjectNode virtual : cfg.graph.getNodes(VirtualObjectNode.TYPE)) {
+            virtual.resetObjectId();
+        }
         assert schedule != null;
         if (readElimination) {
             return new PEReadEliminationClosure(schedule, context.getMetaAccess(), context.getConstantReflection());
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java	Fri Jul 24 16:20:56 2015 +0200
@@ -109,6 +109,7 @@
         }
         if (!success) {
             TTY.println();
+            Debug.dump(graph, "assertNonReachable");
         }
         return success;
     }
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Fri Jul 24 13:26:44 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Fri Jul 24 16:20:56 2015 +0200
@@ -33,8 +33,6 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.java.*;
-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.*;
 
@@ -78,33 +76,35 @@
     }
 
     @Override
-    public State getObjectState(ValueNode value) {
-        return closure.getObjectState(state, value);
+    public ValueNode getAlias(ValueNode value) {
+        return closure.getAliasAndResolve(state, value);
+    }
+
+    public ValueNode getEntry(VirtualObjectNode virtualObject, int index) {
+        return state.getObjectState(virtualObject).getEntry(index);
     }
 
     @Override
-    public void setVirtualEntry(State objectState, int index, ValueNode value, boolean unsafe) {
-        ObjectState obj = (ObjectState) objectState;
-        assert obj != null && obj.isVirtual() : "not virtual: " + obj;
+    public void setVirtualEntry(VirtualObjectNode virtual, int index, ValueNode value, boolean unsafe) {
+        ObjectState obj = state.getObjectState(virtual);
+        assert obj.isVirtual() : "not virtual: " + obj;
         ValueNode newValue;
         if (value == null) {
             newValue = null;
         } else {
-            ObjectState valueState = closure.getObjectState(state, value);
-            if (valueState == null) {
-                newValue = getReplacedValue(value);
-                assert unsafe || obj.getEntry(index) == null || obj.getEntry(index).getKind() == newValue.getKind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue));
-            } else {
-                if (valueState.getState() != EscapeState.Virtual) {
-                    newValue = valueState.getMaterializedValue();
-                    assert newValue.getKind() == Kind.Object;
-                } else {
-                    newValue = valueState.getVirtualObject();
-                }
-                assert obj.getEntry(index) == null || isObjectEntry(obj.getEntry(index));
-            }
+            newValue = closure.getAliasAndResolve(state, value);
+            assert unsafe || obj.getEntry(index) == null || obj.getEntry(index).getKind() == newValue.getKind() || (isObjectEntry(obj.getEntry(index)) && isObjectEntry(newValue));
         }
-        obj.setEntry(index, newValue);
+        state.setEntry(virtual.getObjectId(), index, newValue);
+    }
+
+    public void setEnsureVirtualized(VirtualObjectNode virtualObject, boolean ensureVirtualized) {
+        int id = virtualObject.getObjectId();
+        state.setEnsureVirtualized(id, ensureVirtualized);
+    }
+
+    public boolean getEnsureVirtualized(VirtualObjectNode virtualObject) {
+        return state.getObjectState(virtualObject).getEnsureVirtualized();
     }
 
     private static boolean isObjectEntry(ValueNode value) {
@@ -112,11 +112,6 @@
     }
 
     @Override
-    public ValueNode getReplacedValue(ValueNode original) {
-        return closure.getScalarAlias(original);
-    }
-
-    @Override
     public void replaceWithVirtual(VirtualObjectNode virtual) {
         closure.addAndMarkAlias(virtual, current);
         effects.deleteNode(current);
@@ -157,16 +152,16 @@
             effects.addFloatingNode(virtualObject, "newVirtualObject");
         }
         for (int i = 0; i < entryState.length; i++) {
-            if (!(entryState[i] instanceof VirtualObjectNode)) {
-                ObjectState v = closure.getObjectState(state, entryState[i]);
-                if (v != null) {
-                    entryState[i] = v.isVirtual() ? v.getVirtualObject() : v.getMaterializedValue();
-                } else {
-                    entryState[i] = closure.getScalarAlias(entryState[i]);
-                }
-            }
+            ValueNode entry = entryState[i];
+            entryState[i] = entry instanceof VirtualObjectNode ? entry : closure.getAliasAndResolve(state, entry);
         }
-        state.addObject(virtualObject, new ObjectState(virtualObject, entryState, EscapeState.Virtual, locks, ensureVirtualized));
+        int id = virtualObject.getObjectId();
+        if (id == -1) {
+            id = closure.virtualObjects.size();
+            closure.virtualObjects.add(virtualObject);
+            virtualObject.setObjectId(id);
+        }
+        state.addObject(id, new ObjectState(entryState, locks, ensureVirtualized));
         closure.addAndMarkAlias(virtualObject, virtualObject);
         PartialEscapeClosure.METRIC_ALLOCATION_REMOVED.increment();
     }
@@ -178,18 +173,23 @@
 
     @Override
     public void replaceWith(ValueNode node) {
-        State resultState = getObjectState(node);
-        if (resultState == null) {
+        if (node instanceof VirtualObjectNode) {
+            replaceWithVirtual((VirtualObjectNode) node);
+        } else {
             replaceWithValue(node);
-        } else {
-            if (resultState.getState() == EscapeState.Virtual) {
-                replaceWithVirtual(resultState.getVirtualObject());
-            } else {
-                replaceWithValue(resultState.getMaterializedValue());
-            }
         }
     }
 
+    public void addLock(VirtualObjectNode virtualObject, MonitorIdNode monitorId) {
+        int id = virtualObject.getObjectId();
+        state.addLock(id, monitorId);
+    }
+
+    public MonitorIdNode removeLock(VirtualObjectNode virtualObject) {
+        int id = virtualObject.getObjectId();
+        return state.removeLock(id);
+    }
+
     public MetaAccessProvider getMetaAccess() {
         return metaAccess;
     }