changeset 9504:106f0a0acafa

refactored monitor handling in EA: MonitorEnter/Exit is removed, not eliminated
author Lukas Stadler <lukas.stadler@jku.at>
date Tue, 30 Apr 2013 17:29:33 +0200
parents f364c77077ff
children 2b7857aaa1c0 cf8104ed68ba 8b126a466917
files graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeArrayNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeObjectNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorExitStubCall.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneNode.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/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/NewInstanceNode.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/CommitAllocationNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java
diffstat 29 files changed, 224 insertions(+), 271 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Tue Apr 30 17:29:33 2013 +0200
@@ -777,34 +777,33 @@
         } else if (n instanceof CommitAllocationNode) {
             CommitAllocationNode commit = (CommitAllocationNode) n;
 
-            List<ValueNode> allocations = new ArrayList<>(commit.getVirtualObjects().size());
+            ValueNode[] allocations = new ValueNode[commit.getVirtualObjects().size()];
             for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
                 VirtualObjectNode virtual = commit.getVirtualObjects().get(objIndex);
-                int lockCount = commit.getLockCounts().get(objIndex);
                 int entryCount = virtual.entryCount();
 
                 FixedWithNextNode newObject;
                 if (virtual instanceof VirtualInstanceNode) {
-                    newObject = graph.add(new NewInstanceNode(virtual.type(), true, lockCount > 0));
+                    newObject = graph.add(new NewInstanceNode(virtual.type(), true));
                 } else {
                     ResolvedJavaType element = ((VirtualArrayNode) virtual).componentType();
-                    newObject = graph.add(new NewArrayNode(element, ConstantNode.forInt(entryCount, graph), true, lockCount > 0));
+                    newObject = graph.add(new NewArrayNode(element, ConstantNode.forInt(entryCount, graph), true));
                 }
                 graph.addBeforeFixed(commit, newObject);
-                allocations.add(newObject);
+                allocations[objIndex] = newObject;
             }
             int valuePos = 0;
             for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
                 VirtualObjectNode virtual = commit.getVirtualObjects().get(objIndex);
                 int entryCount = virtual.entryCount();
 
-                ValueNode newObject = allocations.get(objIndex);
+                ValueNode newObject = allocations[objIndex];
                 if (virtual instanceof VirtualInstanceNode) {
                     VirtualInstanceNode instance = (VirtualInstanceNode) virtual;
                     for (int i = 0; i < entryCount; i++) {
                         ValueNode value = commit.getValues().get(valuePos++);
                         if (value instanceof VirtualObjectNode) {
-                            value = allocations.get(commit.getVirtualObjects().indexOf(value));
+                            value = allocations[commit.getVirtualObjects().indexOf(value)];
                         }
                         graph.addBeforeFixed(commit, graph.add(new WriteNode(newObject, value, createFieldLocation(graph, (HotSpotResolvedJavaField) instance.field(i)), WriteBarrierType.NONE)));
                     }
@@ -816,18 +815,27 @@
                         if (value instanceof VirtualObjectNode) {
                             int indexOf = commit.getVirtualObjects().indexOf(value);
                             assert indexOf != -1 : commit + " " + value;
-                            value = allocations.get(indexOf);
+                            value = allocations[indexOf];
                         }
                         graph.addBeforeFixed(commit, graph.add(new WriteNode(newObject, value, createArrayLocation(graph, element.getKind(), ConstantNode.forInt(i, graph)), WriteBarrierType.NONE)));
                     }
                 }
             }
+            for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
+                FixedValueAnchorNode anchor = graph.add(new FixedValueAnchorNode(allocations[objIndex]));
+                allocations[objIndex] = anchor;
+                graph.addBeforeFixed(commit, anchor);
+            }
+            for (int objIndex = 0; objIndex < commit.getVirtualObjects().size(); objIndex++) {
+                for (int lockDepth : commit.getLocks().get(objIndex)) {
+                    MonitorEnterNode enter = graph.add(new MonitorEnterNode(allocations[objIndex], lockDepth));
+                    graph.addBeforeFixed(commit, enter);
+                }
+            }
             for (Node usage : commit.usages().snapshot()) {
                 AllocatedObjectNode addObject = (AllocatedObjectNode) usage;
                 int index = commit.getVirtualObjects().indexOf(addObject.getVirtualObject());
-                FixedValueAnchorNode anchor = graph.add(new FixedValueAnchorNode(allocations.get(index)));
-                graph.addBeforeFixed(commit, anchor);
-                graph.replaceFloating(addObject, anchor);
+                graph.replaceFloating(addObject, allocations[index]);
             }
             graph.removeFixed(commit);
         } else if (n instanceof CheckCastNode) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/BeginLockScopeNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -38,22 +38,12 @@
  * is locked (ensuring the GC sees and updates the object) so it must come after any null pointer
  * check on the object.
  */
-public final class BeginLockScopeNode extends AbstractStateSplit implements LIRGenLowerable, MonitorEnter, MonitorReference {
-
-    private final boolean eliminated;
+public final class BeginLockScopeNode extends AbstractStateSplit implements LIRGenLowerable, MonitorEnter {
 
-    private int lockDepth = -1;
+    private int lockDepth;
 
-    public BeginLockScopeNode(boolean eliminated) {
+    public BeginLockScopeNode(int lockDepth) {
         super(StampFactory.forWord());
-        this.eliminated = eliminated;
-    }
-
-    public int getLockDepth() {
-        return lockDepth;
-    }
-
-    public void setLockDepth(int lockDepth) {
         this.lockDepth = lockDepth;
     }
 
@@ -72,12 +62,10 @@
         assert lockDepth != -1;
         HotSpotLIRGenerator hsGen = (HotSpotLIRGenerator) gen;
         StackSlot slot = hsGen.getLockSlot(lockDepth);
-        if (!eliminated) {
-            Value result = gen.emitAddress(slot);
-            gen.setResult(this, result);
-        }
+        Value result = gen.emitAddress(slot);
+        gen.setResult(this, result);
     }
 
     @NodeIntrinsic
-    public static native Word beginLockScope(@ConstantNodeParameter boolean eliminated);
+    public static native Word beginLockScope(@ConstantNodeParameter int lockDepth);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/CurrentLockNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -28,26 +28,18 @@
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.word.*;
 
 /**
  * Intrinsic for getting the lock in the current {@linkplain BeginLockScopeNode lock scope}.
  */
-public final class CurrentLockNode extends FixedWithNextNode implements LIRGenLowerable, MonitorReference {
+public final class CurrentLockNode extends FixedWithNextNode implements LIRGenLowerable {
 
-    private int lockDepth = -1;
+    private int lockDepth;
 
-    public CurrentLockNode() {
+    public CurrentLockNode(int lockDepth) {
         super(StampFactory.forWord());
-    }
-
-    public int getLockDepth() {
-        return lockDepth;
-    }
-
-    public void setLockDepth(int lockDepth) {
         this.lockDepth = lockDepth;
     }
 
@@ -62,5 +54,5 @@
     }
 
     @NodeIntrinsic
-    public static native Word currentLock();
+    public static native Word currentLock(@ConstantNodeParameter int lockDepth);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeArrayNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeArrayNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -39,16 +39,14 @@
     @Input private final ValueNode allocationSize;
     private final ResolvedJavaType type;
     private final boolean fillContents;
-    private final boolean locked;
 
-    public InitializeArrayNode(ValueNode memory, ValueNode length, ValueNode allocationSize, ResolvedJavaType type, boolean fillContents, boolean locked) {
+    public InitializeArrayNode(ValueNode memory, ValueNode length, ValueNode allocationSize, ResolvedJavaType type, boolean fillContents) {
         super(StampFactory.exactNonNull(type));
         this.memory = memory;
         this.type = type;
         this.length = length;
         this.allocationSize = allocationSize;
         this.fillContents = fillContents;
-        this.locked = locked;
     }
 
     public ValueNode memory() {
@@ -77,16 +75,11 @@
         return fillContents;
     }
 
-    public boolean locked() {
-        return locked;
-    }
-
     @Override
     public void lower(LoweringTool tool, LoweringType loweringType) {
         tool.getRuntime().lower(this, tool);
     }
 
     @NodeIntrinsic
-    public static native Object initialize(Object memory, int length, int allocationSize, @ConstantNodeParameter ResolvedJavaType type, @ConstantNodeParameter boolean fillContents,
-                    @ConstantNodeParameter boolean locked);
+    public static native Object initialize(Object memory, int length, int allocationSize, @ConstantNodeParameter ResolvedJavaType type, @ConstantNodeParameter boolean fillContents);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeObjectNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/InitializeObjectNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -37,14 +37,12 @@
     @Input private final ValueNode memory;
     private final ResolvedJavaType type;
     private final boolean fillContents;
-    private final boolean locked;
 
-    public InitializeObjectNode(ValueNode memory, ResolvedJavaType type, boolean fillContents, boolean locked) {
+    public InitializeObjectNode(ValueNode memory, ResolvedJavaType type, boolean fillContents) {
         super(StampFactory.exactNonNull(type));
         this.memory = memory;
         this.type = type;
         this.fillContents = fillContents;
-        this.locked = locked;
     }
 
     public ValueNode memory() {
@@ -59,10 +57,6 @@
         return fillContents;
     }
 
-    public boolean locked() {
-        return locked;
-    }
-
     @Override
     public void lower(LoweringTool tool, LoweringType loweringType) {
         tool.getRuntime().lower(this, tool);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorExitStubCall.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/MonitorExitStubCall.java	Tue Apr 30 17:29:33 2013 +0200
@@ -28,29 +28,21 @@
 import com.oracle.graal.compiler.target.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.word.*;
 
 /**
  * Node implementing a call to HotSpot's {@code graal_monitorexit} stub.
  */
-public class MonitorExitStubCall extends DeoptimizingStubCall implements LIRGenLowerable, MonitorReference {
+public class MonitorExitStubCall extends DeoptimizingStubCall implements LIRGenLowerable {
 
     @Input private final ValueNode object;
     private int lockDepth;
     public static final Descriptor MONITOREXIT = new Descriptor("monitorexit", true, void.class, Object.class, Word.class);
 
-    public MonitorExitStubCall(ValueNode object) {
+    public MonitorExitStubCall(ValueNode object, int lockDepth) {
         super(StampFactory.forVoid());
         this.object = object;
-    }
-
-    public int getLockDepth() {
-        return lockDepth;
-    }
-
-    public void setLockDepth(int lockDepth) {
         this.lockDepth = lockDepth;
     }
 
@@ -64,5 +56,5 @@
     }
 
     @NodeIntrinsic
-    public static native void call(Object hub);
+    public static native void call(Object hub, @ConstantNodeParameter int lockDepth);
 }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/MonitorSnippets.java	Tue Apr 30 17:29:33 2013 +0200
@@ -76,7 +76,7 @@
     public static final boolean CHECK_BALANCED_MONITORS = Boolean.getBoolean("graal.monitors.checkBalanced");
 
     @Snippet
-    public static void monitorenter(Object object, @ConstantParameter boolean checkNull, @ConstantParameter boolean trace) {
+    public static void monitorenter(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean checkNull, @ConstantParameter boolean trace) {
         verifyOop(object);
 
         if (checkNull && object == null) {
@@ -86,7 +86,7 @@
         // Load the mark word - this includes a null-check on object
         final Word mark = loadWordFromObject(object, markOffset());
 
-        final Word lock = beginLockScope(false);
+        final Word lock = beginLockScope(lockDepth);
 
         trace(trace, "           object: 0x%016lx\n", Word.fromObject(object));
         trace(trace, "             lock: 0x%016lx\n", lock);
@@ -248,17 +248,11 @@
         }
     }
 
-    @Snippet
-    public static void monitorenterEliminated() {
-        incCounter();
-        beginLockScope(true);
-    }
-
     /**
      * Calls straight out to the monitorenter stub.
      */
     @Snippet
-    public static void monitorenterStub(Object object, @ConstantParameter boolean checkNull, @ConstantParameter boolean trace) {
+    public static void monitorenterStub(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean checkNull, @ConstantParameter boolean trace) {
         verifyOop(object);
         incCounter();
         if (checkNull && object == null) {
@@ -266,13 +260,13 @@
         }
         // BeginLockScope nodes do not read from object so a use of object
         // cannot float about the null check above
-        final Word lock = beginLockScope(false);
+        final Word lock = beginLockScope(lockDepth);
         traceObject(trace, "+lock{stub}", object);
         MonitorEnterStubCall.call(object, lock);
     }
 
     @Snippet
-    public static void monitorexit(Object object, @ConstantParameter boolean trace) {
+    public static void monitorexit(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean trace) {
         trace(trace, "           object: 0x%016lx\n", Word.fromObject(object));
         if (useBiasedLocking()) {
             // Check for biased locking unlock case, which is a no-op
@@ -291,7 +285,7 @@
             }
         }
 
-        final Word lock = CurrentLockNode.currentLock();
+        final Word lock = CurrentLockNode.currentLock(lockDepth);
 
         // Load displaced mark
         final Word displacedMark = lock.readWord(lockDisplacedMarkOffset(), DISPLACED_MARK_WORD_LOCATION);
@@ -309,7 +303,7 @@
                 // The object's mark word was not pointing to the displaced header,
                 // we do unlocking via runtime call.
                 traceObject(trace, "-lock{stub}", object);
-                MonitorExitStubCall.call(object);
+                MonitorExitStubCall.call(object, lockDepth);
             } else {
                 traceObject(trace, "-lock{cas}", object);
             }
@@ -322,16 +316,10 @@
      * Calls straight out to the monitorexit stub.
      */
     @Snippet
-    public static void monitorexitStub(Object object, @ConstantParameter boolean trace) {
+    public static void monitorexitStub(Object object, @ConstantParameter int lockDepth, @ConstantParameter boolean trace) {
         verifyOop(object);
         traceObject(trace, "-lock{stub}", object);
-        MonitorExitStubCall.call(object);
-        endLockScope();
-        decCounter();
-    }
-
-    @Snippet
-    public static void monitorexitEliminated() {
+        MonitorExitStubCall.call(object, lockDepth);
         endLockScope();
         decCounter();
     }
@@ -398,8 +386,6 @@
         private final SnippetInfo monitorexit = snippet(MonitorSnippets.class, "monitorexit");
         private final SnippetInfo monitorenterStub = snippet(MonitorSnippets.class, "monitorenterStub");
         private final SnippetInfo monitorexitStub = snippet(MonitorSnippets.class, "monitorexitStub");
-        private final SnippetInfo monitorenterEliminated = snippet(MonitorSnippets.class, "monitorenterEliminated");
-        private final SnippetInfo monitorexitEliminated = snippet(MonitorSnippets.class, "monitorexitEliminated");
         private final SnippetInfo initCounter = snippet(MonitorSnippets.class, "initCounter");
         private final SnippetInfo checkCounter = snippet(MonitorSnippets.class, "checkCounter");
 
@@ -416,18 +402,16 @@
             FrameState stateAfter = monitorenterNode.stateAfter();
 
             Arguments args;
-            if (monitorenterNode.eliminated()) {
-                args = new Arguments(monitorenterEliminated);
+            if (useFastLocking) {
+                args = new Arguments(monitorenter);
             } else {
-                if (useFastLocking) {
-                    args = new Arguments(monitorenter);
-                } else {
-                    args = new Arguments(monitorenterStub);
-                }
-                args.add("object", monitorenterNode.object());
-                args.addConst("checkNull", !monitorenterNode.object().stamp().nonNull());
-                args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
+                args = new Arguments(monitorenterStub);
             }
+            args.add("object", monitorenterNode.object());
+            args.addConst("lockDepth", monitorenterNode.getLockDepth());
+            args.addConst("checkNull", !monitorenterNode.object().stamp().nonNull());
+            boolean tracingEnabledForMethod = stateAfter != null && (isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
+            args.addConst("trace", isTracingEnabledForType(monitorenterNode.object()) || tracingEnabledForMethod);
 
             Map<Node, Node> nodes = template(args).instantiate(runtime, monitorenterNode, DEFAULT_REPLACER, args);
 
@@ -436,9 +420,6 @@
                     BeginLockScopeNode begin = (BeginLockScopeNode) n;
                     begin.setStateAfter(stateAfter);
                 }
-                if (n instanceof MonitorReference) {
-                    ((MonitorReference) n).setLockDepth(monitorenterNode.getLockDepth());
-                }
             }
         }
 
@@ -447,17 +428,14 @@
             FrameState stateAfter = monitorexitNode.stateAfter();
 
             Arguments args;
-            if (monitorexitNode.eliminated()) {
-                args = new Arguments(monitorexitEliminated);
+            if (useFastLocking) {
+                args = new Arguments(monitorexit);
             } else {
-                if (useFastLocking) {
-                    args = new Arguments(monitorexit);
-                } else {
-                    args = new Arguments(monitorexitStub);
-                }
-                args.add("object", monitorexitNode.object());
-                args.addConst("trace", isTracingEnabledForType(monitorexitNode.object()) || isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
+                args = new Arguments(monitorexitStub);
             }
+            args.add("object", monitorexitNode.object());
+            args.addConst("lockDepth", monitorexitNode.getLockDepth());
+            args.addConst("trace", isTracingEnabledForType(monitorexitNode.object()) || isTracingEnabledForMethod(stateAfter.method()) || isTracingEnabledForMethod(graph.method()));
 
             Map<Node, Node> nodes = template(args).instantiate(runtime, monitorexitNode, DEFAULT_REPLACER, args);
 
@@ -466,9 +444,6 @@
                     EndLockScopeNode end = (EndLockScopeNode) n;
                     end.setStateAfter(stateAfter);
                 }
-                if (n instanceof MonitorReference) {
-                    ((MonitorReference) n).setLockDepth(monitorexitNode.getLockDepth());
-                }
             }
         }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Tue Apr 30 17:29:33 2013 +0200
@@ -72,18 +72,14 @@
     }
 
     @Snippet
-    public static Object initializeObject(Word memory, Word hub, Word prototypeMarkWord, @ConstantParameter int size, @ConstantParameter boolean fillContents, @ConstantParameter boolean locked) {
+    public static Object initializeObject(Word memory, Word hub, Word prototypeMarkWord, @ConstantParameter int size, @ConstantParameter boolean fillContents) {
 
         Object result;
         if (probability(SLOW_PATH_PROBABILITY, memory.equal(0))) {
             new_stub.inc();
             result = NewInstanceStubCall.call(hub);
         } else {
-            if (locked) {
-                formatObject(hub, size, memory, thread().or(biasedLockPattern()), fillContents);
-            } else {
-                formatObject(hub, size, memory, prototypeMarkWord, fillContents);
-            }
+            formatObject(hub, size, memory, prototypeMarkWord, fillContents);
             result = memory.toObject();
         }
         /*
@@ -95,16 +91,7 @@
     }
 
     @Snippet
-    public static Object initializeArray(Word memory, Word hub, int length, int allocationSize, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter boolean fillContents,
-                    @ConstantParameter boolean locked) {
-        if (locked) {
-            return initializeArray(memory, hub, length, allocationSize, thread().or(biasedLockPattern()), headerSize, fillContents);
-        } else {
-            return initializeArray(memory, hub, length, allocationSize, prototypeMarkWord, headerSize, fillContents);
-        }
-    }
-
-    private static Object initializeArray(Word memory, Word hub, int length, int allocationSize, Word prototypeMarkWord, int headerSize, boolean fillContents) {
+    public static Object initializeArray(Word memory, Word hub, int length, int allocationSize, Word prototypeMarkWord, @ConstantParameter int headerSize, @ConstantParameter boolean fillContents) {
         Object result;
         if (probability(SLOW_PATH_PROBABILITY, memory.equal(0))) {
             newarray_stub.inc();
@@ -132,7 +119,7 @@
         }
         int allocationSize = computeArrayAllocationSize(length, alignment, headerSize, log2ElementSize);
         Word memory = TLABAllocateNode.allocateVariableSize(allocationSize);
-        return InitializeArrayNode.initialize(memory, length, allocationSize, type, fillContents, false);
+        return InitializeArrayNode.initialize(memory, length, allocationSize, type, fillContents);
     }
 
     /**
@@ -243,7 +230,7 @@
                 graph.addBeforeFixed(newInstanceNode, tlabAllocateNode);
                 memory = tlabAllocateNode;
             }
-            InitializeObjectNode initializeNode = graph.add(new InitializeObjectNode(memory, type, newInstanceNode.fillContents(), newInstanceNode.locked()));
+            InitializeObjectNode initializeNode = graph.add(new InitializeObjectNode(memory, type, newInstanceNode.fillContents()));
             graph.replaceFixedWithFixed(newInstanceNode, initializeNode);
         }
 
@@ -269,7 +256,7 @@
                  * anyway for both allocation and initialization - it just needs to be non-null
                  */
                 ConstantNode size = ConstantNode.forInt(-1, graph);
-                InitializeArrayNode initializeNode = graph.add(new InitializeArrayNode(zero, lengthNode, size, arrayType, newArrayNode.fillContents(), newArrayNode.locked()));
+                InitializeArrayNode initializeNode = graph.add(new InitializeArrayNode(zero, lengthNode, size, arrayType, newArrayNode.fillContents()));
                 graph.replaceFixedWithFixed(newArrayNode, initializeNode);
             } else if (length != null && belowThan(length, MAX_ARRAY_FAST_PATH_ALLOCATION_LENGTH)) {
                 // Calculate aligned size
@@ -277,7 +264,7 @@
                 ConstantNode sizeNode = ConstantNode.forInt(size, graph);
                 tlabAllocateNode = graph.add(new TLABAllocateNode(sizeNode));
                 graph.addBeforeFixed(newArrayNode, tlabAllocateNode);
-                InitializeArrayNode initializeNode = graph.add(new InitializeArrayNode(tlabAllocateNode, lengthNode, sizeNode, arrayType, newArrayNode.fillContents(), newArrayNode.locked()));
+                InitializeArrayNode initializeNode = graph.add(new InitializeArrayNode(tlabAllocateNode, lengthNode, sizeNode, arrayType, newArrayNode.fillContents()));
                 graph.replaceFixedWithFixed(newArrayNode, initializeNode);
             } else {
                 Arguments args = new Arguments(allocateArrayAndInitialize);
@@ -319,7 +306,6 @@
             args.add("hub", hub);
             args.add("prototypeMarkWord", type.prototypeMarkWord());
             args.addConst("size", size).addConst("fillContents", initializeNode.fillContents());
-            args.addConst("locked", initializeNode.locked());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering initializeObject in %s: node=%s, template=%s, arguments=%s", graph, initializeNode, template, args);
@@ -345,7 +331,6 @@
             args.add("prototypeMarkWord", type.prototypeMarkWord());
             args.addConst("headerSize", headerSize);
             args.addConst("fillContents", initializeNode.fillContents());
-            args.addConst("locked", initializeNode.locked());
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering initializeArray in %s: node=%s, template=%s, arguments=%s", graph, initializeNode, template, args);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -103,7 +103,7 @@
                     newEntryState[i] = originalState.getEntry(i);
                 }
                 VirtualObjectNode newVirtual = originalVirtual.duplicate();
-                tool.createVirtualObject(newVirtual, newEntryState, 0);
+                tool.createVirtualObject(newVirtual, newEntryState, null);
                 tool.replaceWithVirtual(newVirtual);
             }
         } else {
@@ -134,7 +134,7 @@
                             }
                         }
                     });
-                    tool.createVirtualObject(newVirtual, state, 0);
+                    tool.createVirtualObject(newVirtual, state, null);
                     tool.replaceWithVirtual(newVirtual);
                 }
             }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/ObjectCloneSnippets.java	Tue Apr 30 17:29:33 2013 +0200
@@ -54,7 +54,7 @@
         int instanceSize = layoutHelper;
         Pointer memory = NewObjectSnippets.allocate(instanceSize);
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.initializeObject((Word) memory, hub, prototypeMarkWord, instanceSize, false, false);
+        Object result = NewObjectSnippets.initializeObject((Word) memory, hub, prototypeMarkWord, instanceSize, false);
 
         memory = Word.fromObject(result);
         for (int offset = 2 * wordSize(); offset < instanceSize; offset += wordSize()) {
@@ -72,7 +72,7 @@
 
         Pointer memory = NewObjectSnippets.allocate(sizeInBytes);
         Word prototypeMarkWord = hub.readWord(prototypeMarkWordOffset(), PROTOTYPE_MARK_WORD_LOCATION);
-        Object result = NewObjectSnippets.initializeArray((Word) memory, hub, arrayLength, sizeInBytes, prototypeMarkWord, headerSize, false, false);
+        Object result = NewObjectSnippets.initializeArray((Word) memory, hub, arrayLength, sizeInBytes, prototypeMarkWord, headerSize, false);
 
         memory = Word.fromObject(result);
         for (int offset = headerSize; offset < sizeInBytes; offset += wordSize()) {
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Apr 30 17:29:33 2013 +0200
@@ -828,7 +828,7 @@
     void genNewInstance(int cpi) {
         JavaType type = lookupType(cpi, NEW);
         if (type instanceof ResolvedJavaType && ((ResolvedJavaType) type).isInitialized()) {
-            NewInstanceNode n = currentGraph.add(new NewInstanceNode((ResolvedJavaType) type, true, false));
+            NewInstanceNode n = currentGraph.add(new NewInstanceNode((ResolvedJavaType) type, true));
             frameState.apush(append(n));
         } else {
             handleUnresolvedNewInstance(type);
@@ -870,7 +870,7 @@
     private void genNewPrimitiveArray(int typeCode) {
         Class<?> clazz = arrayTypeCodeToClass(typeCode);
         ResolvedJavaType elementType = runtime.lookupJavaType(clazz);
-        NewArrayNode nta = currentGraph.add(new NewArrayNode(elementType, frameState.ipop(), true, false));
+        NewArrayNode nta = currentGraph.add(new NewArrayNode(elementType, frameState.ipop(), true));
         frameState.apush(append(nta));
     }
 
@@ -878,7 +878,7 @@
         JavaType type = lookupType(cpi, ANEWARRAY);
         ValueNode length = frameState.ipop();
         if (type instanceof ResolvedJavaType) {
-            NewArrayNode n = currentGraph.add(new NewArrayNode((ResolvedJavaType) type, length, true, false));
+            NewArrayNode n = currentGraph.add(new NewArrayNode((ResolvedJavaType) type, length, true));
             frameState.apush(append(n));
         } else {
             handleUnresolvedNewObjectArray(type, length);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/BoxNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -76,7 +76,7 @@
         VirtualBoxingNode newVirtual = new VirtualBoxingNode(type, boxingKind);
         assert newVirtual.getFields().length == 1;
 
-        tool.createVirtualObject(newVirtual, new ValueNode[]{v}, 0);
+        tool.createVirtualObject(newVirtual, new ValueNode[]{v}, null);
         tool.replaceWithVirtual(newVirtual);
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AccessMonitorNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -23,43 +23,24 @@
 package com.oracle.graal.nodes.java;
 
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.debug.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
-import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.nodes.virtual.*;
 
 /**
  * The {@code AccessMonitorNode} is the base class of both monitor acquisition and release.
  * <p>
- * The VM needs information about monitors in the debug information. This information is built from
- * the nesting level of {@link MonitorEnterNode} when the LIR is constructed. Therefore, monitor
- * nodes must not be removed from the graph unless it is guaranteed that the nesting level does not
- * change. For example, you must not remove a {@link MonitorEnterNode} for a thread-local object or
- * for a recursive locking. Instead, mark the node as {@link #eliminated}. This makes sure that the
- * meta data still contains the complete locking hierarchy.
- * <p>
  * The Java bytecode specification allows non-balanced locking. Graal does not handle such cases and
  * throws a {@link BailoutException} instead during graph building.
  */
-public abstract class AccessMonitorNode extends AbstractStateSplit implements StateSplit, MemoryCheckpoint, Virtualizable {
+public abstract class AccessMonitorNode extends AbstractStateSplit implements StateSplit, MemoryCheckpoint {
 
     @Input private ValueNode object;
-    private boolean eliminated;
 
     public ValueNode object() {
         return object;
     }
 
-    public boolean eliminated() {
-        return eliminated;
-    }
-
-    public void eliminate() {
-        eliminated = true;
-    }
-
     /**
      * Creates a new AccessMonitor instruction.
      * 
@@ -69,22 +50,4 @@
         super(StampFactory.forVoid());
         this.object = object;
     }
-
-    @Override
-    public void virtualize(VirtualizerTool tool) {
-        State state = tool.getObjectState(object);
-        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().getClass() == VirtualInstanceNode.class) {
-            Debug.log("monitor operation %s on %s\n", this, state);
-            int newLockCount = state.getLockCount() + (this instanceof MonitorEnterNode ? 1 : -1);
-            state.setLockCount(newLockCount);
-            tool.replaceFirstInput(object(), state.getVirtualObject());
-            tool.customAction(new Runnable() {
-
-                @Override
-                public void run() {
-                    eliminate();
-                }
-            });
-        }
-    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorEnterNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -29,7 +29,7 @@
 /**
  * The {@code MonitorEnterNode} represents the acquisition of a monitor.
  */
-public final class MonitorEnterNode extends AccessMonitorNode implements Lowerable, MonitorEnter, MonitorReference {
+public final class MonitorEnterNode extends AccessMonitorNode implements Virtualizable, Lowerable, MonitorEnter, MonitorReference {
 
     private int lockDepth;
 
@@ -48,6 +48,7 @@
         return new Object[]{LocationNode.ANY_LOCATION};
     }
 
+    @Override
     public void lower(LoweringTool tool, LoweringType loweringType) {
         tool.getRuntime().lower(this, tool);
     }
@@ -59,4 +60,13 @@
     public void setLockDepth(int lockDepth) {
         this.lockDepth = lockDepth;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
+            state.addLock(getLockDepth());
+            tool.delete();
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/MonitorExitNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -22,15 +22,15 @@
  */
 package com.oracle.graal.nodes.java;
 
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.graph.*;
 
 /**
  * The {@code MonitorEnterNode} represents a monitor release.
  */
-public final class MonitorExitNode extends AccessMonitorNode implements Lowerable, Node.IterableNodeType, MonitorExit, MonitorReference {
+public final class MonitorExitNode extends AccessMonitorNode implements Virtualizable, Lowerable, Node.IterableNodeType, MonitorExit, MonitorReference {
 
     private int lockDepth;
 
@@ -49,6 +49,7 @@
         return new Object[]{LocationNode.ANY_LOCATION};
     }
 
+    @Override
     public void lower(LoweringTool tool, LoweringType loweringType) {
         tool.getRuntime().lower(this, tool);
     }
@@ -60,4 +61,14 @@
     public void setLockDepth(int lockDepth) {
         this.lockDepth = lockDepth;
     }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        State state = tool.getObjectState(object());
+        if (state != null && state.getState() == EscapeState.Virtual && state.getVirtualObject().hasIdentity()) {
+            int removedLock = state.removeLock();
+            assert removedLock == getLockDepth();
+            tool.delete();
+        }
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewArrayNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -38,8 +38,6 @@
     private final ResolvedJavaType elementType;
     private final boolean fillContents;
 
-    private final boolean locked;
-
     @Override
     public ValueNode length() {
         return length;
@@ -52,14 +50,12 @@
      *            the array itself).
      * @param length the node that produces the length for this allocation.
      * @param fillContents determines whether the array elements should be initialized to zero/null.
-     * @param locked determines whether the array should be locked immediately.
      */
-    public NewArrayNode(ResolvedJavaType elementType, ValueNode length, boolean fillContents, boolean locked) {
+    public NewArrayNode(ResolvedJavaType elementType, ValueNode length, boolean fillContents) {
         super(StampFactory.exactNonNull(elementType.getArrayClass()));
         this.length = length;
         this.elementType = elementType;
         this.fillContents = fillContents;
-        this.locked = locked;
     }
 
     /**
@@ -70,13 +66,6 @@
     }
 
     /**
-     * @return <code>true</code> if the array will be locked immediately.
-     */
-    public boolean locked() {
-        return locked;
-    }
-
-    /**
      * The list of node which produce input for this instruction.
      */
     public ValueNode dimension(int index) {
@@ -127,7 +116,7 @@
                     state[i] = defaultForKind;
                 }
                 VirtualObjectNode virtualObject = new VirtualArrayNode(elementType, constantLength);
-                tool.createVirtualObject(virtualObject, state, 0);
+                tool.createVirtualObject(virtualObject, state, null);
                 tool.replaceWithVirtual(virtualObject);
             }
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -37,7 +37,6 @@
 
     private final ResolvedJavaType instanceClass;
     private final boolean fillContents;
-    private final boolean locked;
 
     /**
      * Constructs a NewInstanceNode.
@@ -45,13 +44,11 @@
      * @param type the class being allocated
      * @param fillContents determines whether the new object's fields should be initialized to
      *            zero/null.
-     * @param locked determines whether the new object should be locked immediately.
      */
-    public NewInstanceNode(ResolvedJavaType type, boolean fillContents, boolean locked) {
+    public NewInstanceNode(ResolvedJavaType type, boolean fillContents) {
         super(StampFactory.exactNonNull(type));
         this.instanceClass = type;
         this.fillContents = fillContents;
-        this.locked = locked;
     }
 
     /**
@@ -70,13 +67,6 @@
         return fillContents;
     }
 
-    /**
-     * @return <code>true</code> if the new object will be locked immediately.
-     */
-    public boolean locked() {
-        return locked;
-    }
-
     @Override
     public ValueNode canonical(CanonicalizerTool tool) {
         if (usages().isEmpty()) {
@@ -103,7 +93,7 @@
             for (int i = 0; i < state.length; i++) {
                 state[i] = ConstantNode.defaultForKind(fields[i].getType().getKind(), graph());
             }
-            tool.createVirtualObject(virtualObject, state, 0);
+            tool.createVirtualObject(virtualObject, state, null);
             tool.replaceWithVirtual(virtualObject);
         }
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/Virtualizable.java	Tue Apr 30 17:29:33 2013 +0200
@@ -47,9 +47,9 @@
 
         public abstract ValueNode getEntry(int index);
 
-        public abstract int getLockCount();
+        public abstract void addLock(int depth);
 
-        public abstract void setLockCount(int lockCount);
+        public abstract int removeLock();
 
         public abstract ValueNode getMaterializedValue();
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/VirtualizerTool.java	Tue Apr 30 17:29:33 2013 +0200
@@ -64,9 +64,9 @@
      * 
      * @param virtualObject the new virtual object.
      * @param entryState the initial state of the virtual object's fields.
-     * @param lockCount the initial locking depth.
+     * @param locks the initial locking depths.
      */
-    void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount);
+    void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int[] locks);
 
     /**
      * Queries the current state of the given value: if it is virtualized (thread-local and the
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/CommitAllocationNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/CommitAllocationNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -35,7 +35,7 @@
 
     @Input private final NodeInputList<VirtualObjectNode> virtualObjects = new NodeInputList<>(this);
     @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
-    private final List<Integer> lockCounts = new ArrayList<>();
+    private final List<int[]> locks = new ArrayList<>();
 
     public CommitAllocationNode() {
         super(StampFactory.forVoid());
@@ -49,13 +49,13 @@
         return values;
     }
 
-    public List<Integer> getLockCounts() {
-        return lockCounts;
+    public List<int[]> getLocks() {
+        return locks;
     }
 
     @Override
     public boolean verify() {
-        assertTrue(virtualObjects.size() == lockCounts.size(), "lockCounts size doesn't match");
+        assertTrue(virtualObjects.size() == locks.size(), "lockCounts size doesn't match");
         int valueCount = 0;
         for (VirtualObjectNode virtual : virtualObjects) {
             valueCount += virtual.entryCount();
@@ -77,7 +77,7 @@
         for (int i = 0; i < virtualObjects.size(); i++) {
             VirtualObjectNode virtualObject = virtualObjects.get(i);
             int entryCount = virtualObject.entryCount();
-            tool.createVirtualObject(virtualObject, values.subList(pos, pos + entryCount).toArray(new ValueNode[entryCount]), lockCounts.get(i));
+            tool.createVirtualObject(virtualObject, values.subList(pos, pos + entryCount).toArray(new ValueNode[entryCount]), locks.get(i));
             pos += entryCount;
         }
         tool.delete();
@@ -96,8 +96,8 @@
                 s.append(i == 0 ? "" : ",").append(value == null ? "_" : value.toString(Verbosity.Id));
             }
             s.append("]");
-            if (lockCounts.get(objIndex) > 0) {
-                s.append(" locked(").append(lockCounts.get(objIndex)).append(")");
+            if (locks.get(objIndex).length > 0) {
+                s.append(" locked(").append(Arrays.toString(locks.get(objIndex))).append(")");
             }
             properties.put("object(" + virtual.toString(Verbosity.Id) + ")", s.toString());
         }
@@ -138,22 +138,22 @@
 
         if (usedCount < virtualObjects.size()) {
             List<VirtualObjectNode> newVirtualObjects = new ArrayList<>(usedCount);
-            List<Integer> newLockCounts = new ArrayList<>(usedCount);
+            List<int[]> newLocks = new ArrayList<>(usedCount);
             List<ValueNode> newValues = new ArrayList<>();
             int valuePos = 0;
             for (int objIndex = 0; objIndex < virtualObjects.size(); objIndex++) {
                 VirtualObjectNode virtualObject = virtualObjects.get(objIndex);
                 if (used[objIndex]) {
                     newVirtualObjects.add(virtualObject);
-                    newLockCounts.add(lockCounts.get(objIndex));
+                    newLocks.add(locks.get(objIndex));
                     newValues.addAll(values.subList(valuePos, valuePos + virtualObject.entryCount()));
                 }
                 valuePos += virtualObject.entryCount();
             }
             virtualObjects.clear();
             virtualObjects.addAll(newVirtualObjects);
-            lockCounts.clear();
-            lockCounts.addAll(newLockCounts);
+            locks.clear();
+            locks.addAll(newLocks);
             values.clear();
             values.addAll(newValues);
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualArrayNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -140,8 +140,7 @@
     }
 
     @Override
-    public AllocatedObjectNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
-        assert lockCount == 0;
+    public AllocatedObjectNode getMaterializedRepresentation(ValueNode[] entries, int[] locks) {
         return new AllocatedObjectNode(this);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualBoxingNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -46,9 +46,9 @@
     }
 
     @Override
-    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
+    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int[] locks) {
         assert entries.length == 1;
-        assert lockCount == 0;
+        assert locks.length == 0;
         return new BoxNode(entries[0], type(), boxingKind);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualInstanceNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -96,7 +96,7 @@
     }
 
     @Override
-    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount) {
+    public ValueNode getMaterializedRepresentation(ValueNode[] entries, int[] locks) {
         return new AllocatedObjectNode(this);
     }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/virtual/VirtualObjectNode.java	Tue Apr 30 17:29:33 2013 +0200
@@ -80,7 +80,7 @@
      * {@link AllocatedObjectNode} then this node will be attached to a {@link CommitAllocationNode}
      * , otherwise the node will just be added to the graph.
      */
-    public abstract ValueNode getMaterializedRepresentation(ValueNode[] entries, int lockCount);
+    public abstract ValueNode getMaterializedRepresentation(ValueNode[] entries, int[] locks);
 
     @Override
     public void generate(LIRGeneratorTool gen) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/BlockState.java	Tue Apr 30 17:29:33 2013 +0200
@@ -26,7 +26,6 @@
 
 import java.util.*;
 
-import com.oracle.graal.api.code.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
@@ -151,26 +150,23 @@
         PartialEscapeClosure.METRIC_MATERIALIZATIONS.increment();
         List<AllocatedObjectNode> objects = new ArrayList<>(2);
         List<ValueNode> values = new ArrayList<>(8);
-        List<Integer> lockCounts = new ArrayList<>(2);
+        List<int[]> locks = new ArrayList<>(2);
         List<ValueNode> otherAllocations = new ArrayList<>(2);
-        materializeWithCommit(virtual, objects, lockCounts, values, otherAllocations, state);
+        materializeWithCommit(virtual, objects, locks, values, otherAllocations, state);
 
-        materializeEffects.addMaterializationBefore(fixed, objects, lockCounts, values, otherAllocations);
+        materializeEffects.addMaterializationBefore(fixed, objects, locks, values, otherAllocations);
     }
 
-    private void materializeWithCommit(VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<Integer> lockCounts, List<ValueNode> values, List<ValueNode> otherAllocations, EscapeState state) {
+    private void materializeWithCommit(VirtualObjectNode virtual, List<AllocatedObjectNode> objects, List<int[]> locks, List<ValueNode> values, List<ValueNode> otherAllocations, EscapeState state) {
         trace("materializing %s", virtual);
         ObjectState obj = getObjectState(virtual);
-        if (obj.getLockCount() > 0 && obj.virtual.type().isArray()) {
-            throw new BailoutException("array materialized with lock");
-        }
 
         ValueNode[] entries = obj.getEntries();
-        ValueNode representation = virtual.getMaterializedRepresentation(entries, obj.getLockCount());
+        ValueNode representation = virtual.getMaterializedRepresentation(entries, obj.getLocks());
         obj.escape(representation, state);
         if (representation instanceof AllocatedObjectNode) {
             objects.add((AllocatedObjectNode) representation);
-            lockCounts.add(obj.getLockCount());
+            locks.add(obj.getLocks());
             int pos = values.size();
             while (values.size() < pos + entries.length) {
                 values.add(null);
@@ -179,7 +175,7 @@
                 ObjectState entryObj = getObjectState(entries[i]);
                 if (entryObj != null) {
                     if (entryObj.isVirtual()) {
-                        materializeWithCommit(entryObj.getVirtualObject(), objects, lockCounts, values, otherAllocations, state);
+                        materializeWithCommit(entryObj.getVirtualObject(), objects, locks, values, otherAllocations, state);
                     }
                     values.set(pos + i, entryObj.getMaterializedValue());
                 } else {
@@ -194,7 +190,7 @@
             }
         } else {
             otherAllocations.add(representation);
-            assert obj.getLockCount() == 0;
+            assert obj.getLocks().length == 0;
         }
     }
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Tue Apr 30 17:29:33 2013 +0200
@@ -310,12 +310,12 @@
      * 
      * @param position The fixed node before which the materialization node should be added.
      * @param objects The allocated objects.
-     * @param lockCounts The lock count for each object.
+     * @param locks The lock depths for each object.
      * @param values The values (field, elements) of all objects.
      * @param otherAllocations A list of allocations that need to be added before the rest (used for
      *            boxing allocations).
      */
-    public void addMaterializationBefore(final FixedNode position, final List<AllocatedObjectNode> objects, final List<Integer> lockCounts, final List<ValueNode> values,
+    public void addMaterializationBefore(final FixedNode position, final List<AllocatedObjectNode> objects, final List<int[]> locks, final List<ValueNode> values,
                     final List<ValueNode> otherAllocations) {
         add(new Effect() {
 
@@ -348,7 +348,7 @@
                         obj.setCommit(commit);
                     }
                     commit.getValues().addAll(values);
-                    commit.getLockCounts().addAll(lockCounts);
+                    commit.getLocks().addAll(locks);
 
                     assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.usages().count();
                     HashSet<AllocatedObjectNode> materializedValues = new HashSet<>(commit.usages().filter(AllocatedObjectNode.class).snapshot());
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/ObjectState.java	Tue Apr 30 17:29:33 2013 +0200
@@ -36,32 +36,69 @@
  */
 class ObjectState extends Virtualizable.State {
 
+    private static final int[] EMPTY_INT_ARRAY = new int[0];
+
+    public static final class LockState {
+
+        public final int depth;
+        public final LockState next;
+
+        private LockState(int depth, LockState next) {
+            this.depth = depth;
+            this.next = next;
+        }
+
+        @Override
+        public String toString() {
+            return next == null ? String.valueOf(depth) : depth + "," + next;
+        }
+
+        public static boolean equals(LockState a, LockState b) {
+            if ((a == null) != (b == null)) {
+                return false;
+            }
+            if (a != null) {
+                if (a.depth != b.depth) {
+                    return false;
+                }
+                return equals(a.next, b.next);
+            }
+            return true;
+        }
+    }
+
     final VirtualObjectNode virtual;
 
     private EscapeState state;
     private ValueNode[] entries;
     private ValueNode materializedValue;
-    private int lockCount;
+    private LockState locks;
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, EscapeState state, int lockCount) {
+    public ObjectState(VirtualObjectNode virtual, ValueNode[] entries, EscapeState state, int[] locks) {
         this.virtual = virtual;
         this.entries = entries;
         this.state = state;
-        this.lockCount = lockCount;
+        if (locks == null) {
+            this.locks = null;
+        } else {
+            for (int i = locks.length - 1; i >= 0; i--) {
+                this.locks = new LockState(locks[i], this.locks);
+            }
+        }
     }
 
-    public ObjectState(VirtualObjectNode virtual, ValueNode materializedValue, EscapeState state, int lockCount) {
+    public ObjectState(VirtualObjectNode virtual, ValueNode materializedValue, EscapeState state, LockState locks) {
         this.virtual = virtual;
         this.materializedValue = materializedValue;
         this.state = state;
-        this.lockCount = lockCount;
+        this.locks = locks;
     }
 
     private ObjectState(ObjectState other) {
         virtual = other.virtual;
         entries = other.entries == null ? null : other.entries.clone();
         materializedValue = other.materializedValue;
-        lockCount = other.lockCount;
+        locks = other.locks;
         state = other.state;
     }
 
@@ -120,20 +157,52 @@
     }
 
     @Override
-    public int getLockCount() {
-        return lockCount;
+    public void addLock(int depth) {
+        locks = new LockState(depth, locks);
     }
 
     @Override
-    public void setLockCount(int lockCount) {
-        this.lockCount = lockCount;
+    public int removeLock() {
+        try {
+            return locks.depth;
+        } finally {
+            locks = locks.next;
+        }
+    }
+
+    public int[] getLocks() {
+        if (locks == null) {
+            return EMPTY_INT_ARRAY;
+        }
+        int cnt = 0;
+        LockState current = locks;
+        while (current != null) {
+            cnt++;
+            current = current.next;
+        }
+        int[] result = new int[cnt];
+        current = locks;
+        cnt = 0;
+        while (current != null) {
+            result[cnt++] = current.depth;
+            current = current.next;
+        }
+        return result;
+    }
+
+    public boolean hasLocks() {
+        return locks != null;
+    }
+
+    public boolean locksEqual(ObjectState other) {
+        return LockState.equals(locks, other.locks);
     }
 
     @Override
     public String toString() {
         StringBuilder str = new StringBuilder().append('{');
-        if (lockCount > 0) {
-            str.append('l').append(lockCount).append(' ');
+        if (locks != null) {
+            str.append('l').append(locks).append(' ');
         }
         if (entries != null) {
             for (int i = 0; i < entries.length; i++) {
@@ -152,7 +221,7 @@
         final int prime = 31;
         int result = 1;
         result = prime * result + Arrays.hashCode(entries);
-        result = prime * result + lockCount;
+        result = prime * result + (locks != null ? locks.depth : 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());
@@ -171,7 +240,7 @@
         if (!Arrays.equals(entries, other.entries)) {
             return false;
         }
-        if (lockCount != other.lockCount) {
+        if (!LockState.equals(locks, other.locks)) {
             return false;
         }
         if (materializedValue == null) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Apr 30 17:29:33 2013 +0200
@@ -238,7 +238,7 @@
                     }
                 });
                 for (ObjectState obj : state.getStates()) {
-                    if (obj.isVirtual() && obj.getLockCount() > 0) {
+                    if (obj.isVirtual() && obj.hasLocks()) {
                         virtual.add(obj);
                     }
                 }
@@ -502,7 +502,6 @@
                     }
                     int virtual = 0;
                     ObjectState startObj = objStates[0];
-                    int lockCount = startObj.getLockCount();
                     boolean locksMatch = true;
                     ValueNode singleValue = startObj.isVirtual() ? null : startObj.getMaterializedValue();
                     for (ObjectState obj : objStates) {
@@ -514,7 +513,7 @@
                                 singleValue = null;
                             }
                         }
-                        locksMatch &= obj.getLockCount() == lockCount;
+                        locksMatch &= obj.locksEqual(startObj);
                     }
 
                     assert virtual < states.size() || locksMatch : "mismatching lock counts at " + merge;
@@ -531,9 +530,9 @@
                                 ensureMaterialized(state, obj, predecessor.getEndNode(), blockEffects.get(predecessor), METRIC_MATERIALIZATIONS_MERGE);
                                 afterMergeEffects.addPhiInput(materializedValuePhi, obj.getMaterializedValue());
                             }
-                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Global, lockCount));
+                            newState.addObject(object, new ObjectState(object, materializedValuePhi, EscapeState.Global, null));
                         } else {
-                            newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Global, lockCount));
+                            newState.addObject(object, new ObjectState(object, singleValue, EscapeState.Global, null));
                         }
                     } else {
                         assert virtual == states.size();
@@ -567,7 +566,7 @@
                                 values[index] = phis[index];
                             }
                         }
-                        newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, lockCount));
+                        newState.addObject(object, new ObjectState(object, values, EscapeState.Virtual, startObj.getLocks()));
                     }
                 }
 
@@ -638,7 +637,7 @@
                             }
                         }
                         mergeEffects.addFloatingNode(virtual, "valueObjectNode");
-                        newState.addObject(virtual, new ObjectState(virtual, Arrays.copyOf(phis, phis.length, ValueNode[].class), EscapeState.Virtual, 0));
+                        newState.addObject(virtual, new ObjectState(virtual, Arrays.copyOf(phis, phis.length, ValueNode[].class), EscapeState.Virtual, null));
                         newState.addAndMarkAlias(virtual, virtual, usages);
                         newState.addAndMarkAlias(virtual, phi, usages);
                     } else {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Tue Apr 30 12:14:59 2013 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualizerToolImpl.java	Tue Apr 30 17:29:33 2013 +0200
@@ -148,7 +148,7 @@
     }
 
     @Override
-    public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int lockCount) {
+    public void createVirtualObject(VirtualObjectNode virtualObject, ValueNode[] entryState, int[] locks) {
         trace("{{%s}} ", current);
         if (virtualObject.isAlive()) {
             state.addAndMarkAlias(virtualObject, virtualObject, usages);
@@ -158,7 +158,7 @@
         for (int i = 0; i < entryState.length; i++) {
             entryState[i] = state.getScalarAlias(entryState[i]);
         }
-        state.addObject(virtualObject, new ObjectState(virtualObject, entryState, EscapeState.Virtual, lockCount));
+        state.addObject(virtualObject, new ObjectState(virtualObject, entryState, EscapeState.Virtual, locks));
         state.addAndMarkAlias(virtualObject, virtualObject, usages);
         PartialEscapeClosure.METRIC_ALLOCATION_REMOVED.increment();
     }