changeset 4233:fa53d5e4aa35

Remove lock information from frame states, and compute it instead when LIR is generated.
author Christian Wimmer <Christian.Wimmer@Oracle.com>
date Thu, 05 Jan 2012 17:02:58 -0800
parents 382523fc390c
children 057620486c90
files graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiValueUtil.java graal/com.oracle.max.criutils/src/com/oracle/max/criutils/CompilationPrinter.java graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompilation.java graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/MonitorObject.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/AccessMonitorNode.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorEnterNode.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorExitNode.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/FrameStateAccess.java graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java
diffstat 16 files changed, 219 insertions(+), 291 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiValueUtil.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.cri/src/com/oracle/max/cri/ci/CiValueUtil.java	Thu Jan 05 17:02:58 2012 -0800
@@ -37,6 +37,10 @@
         return value instanceof CiVirtualObject;
     }
 
+    public static CiVirtualObject asVirtualObject(CiValue value) {
+        assert value != null;
+        return (CiVirtualObject) value;
+    }
 
     public static boolean isConstant(CiValue value) {
         assert value != null;
--- a/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/CompilationPrinter.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.criutils/src/com/oracle/max/criutils/CompilationPrinter.java	Thu Jan 05 17:02:58 2012 -0800
@@ -22,7 +22,10 @@
  */
 package com.oracle.max.criutils;
 
+import static com.oracle.max.cri.ci.CiValueUtil.*;
+
 import java.io.*;
+import java.util.*;
 
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.cri.ri.*;
@@ -124,6 +127,7 @@
 
         if (codePos != null) {
             CiCodePos curCodePos = codePos;
+            List<CiVirtualObject> virtualObjects = new ArrayList<>();
             do {
                 sb.append(CiUtil.toLocation(curCodePos.method, curCodePos.bci));
                 sb.append('\n');
@@ -132,35 +136,47 @@
                     if (frame.numStack > 0) {
                         sb.append("stack: ");
                         for (int i = 0; i < frame.numStack; i++) {
-                            sb.append(valueToString(frame.getStackValue(i))).append(' ');
+                            sb.append(valueToString(frame.getStackValue(i), virtualObjects)).append(' ');
                         }
                         sb.append("\n");
                     }
-
+                    sb.append("locals: ");
+                    for (int i = 0; i < frame.numLocals; i++) {
+                        sb.append(valueToString(frame.getLocalValue(i), virtualObjects)).append(' ');
+                    }
+                    sb.append("\n");
                     if (frame.numLocks > 0) {
                         sb.append("locks: ");
                         for (int i = 0; i < frame.numLocks; ++i) {
-                            sb.append(valueToString(frame.getLockValue(i))).append(' ');
+                            sb.append(valueToString(frame.getLockValue(i), virtualObjects)).append(' ');
                         }
                         sb.append("\n");
                     }
 
-                    sb.append("locals: ");
-                    for (int i = 0; i < frame.numLocals; i++) {
-                        sb.append(valueToString(frame.getLocalValue(i))).append(' ');
-                    }
-                    sb.append("\n");
                 }
                 curCodePos = curCodePos.caller;
             } while (curCodePos != null);
+
+            for (int i = 0; i < virtualObjects.size(); i++) {
+                CiVirtualObject obj = virtualObjects.get(i);
+                sb.append(obj).append(" ").append(obj.type().name()).append(" ");
+                for (int j = 0; j < obj.values().length; j++) {
+                    sb.append(valueToString(obj.values()[j], virtualObjects)).append(' ');
+                }
+                sb.append("\n");
+
+            }
         }
         return sb.toString();
     }
 
-    protected String valueToString(CiValue value) {
+    protected String valueToString(CiValue value, List<CiVirtualObject> virtualObjects) {
         if (value == null) {
             return "-";
         }
+        if (isVirtualObject(value) && !virtualObjects.contains(asVirtualObject(value))) {
+            virtualObjects.add(asVirtualObject(value));
+        }
         return value.toString();
     }
 
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompilation.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/GraalCompilation.java	Thu Jan 05 17:02:58 2012 -0800
@@ -84,7 +84,7 @@
         this.method = method;
         this.stats = stats == null ? new CiStatistics() : stats;
         this.registerConfig = method == null ? compiler.compilerStubRegisterConfig : compiler.runtime.getRegisterConfig(method);
-        this.placeholderState = debugInfoLevel == DebugInfoLevel.REF_MAPS ? new FrameState(method, 0, 0, 0, 0, false) : null;
+        this.placeholderState = debugInfoLevel == DebugInfoLevel.REF_MAPS ? new FrameState(method, 0, 0, 0, false) : null;
 
         if (context().isObserved() && method != null) {
             context().observable.fireCompilationStarted(this);
@@ -394,25 +394,6 @@
         return null;
     }
 
-    /**
-     * Gets the maximum number of locks in the graph's frame states.
-     */
-    public int maxLocks() {
-        int maxLocks = 0;
-        for (FrameState node : graph.getNodes(FrameState.class)) {
-            int lockCount = 0;
-            FrameState current = node;
-            while (current != null) {
-                lockCount += current.locksSize();
-                current = current.outerFrameState();
-            }
-            if (lockCount > maxLocks) {
-                maxLocks = lockCount;
-            }
-        }
-        return maxLocks;
-    }
-
     private GraalContext context() {
         return compiler.context;
     }
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/DebugInfoBuilder.java	Thu Jan 05 17:02:58 2012 -0800
@@ -27,6 +27,7 @@
 
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.graal.compiler.*;
+import com.oracle.max.graal.compiler.gen.LIRGenerator.*;
 import com.oracle.max.graal.compiler.lir.*;
 import com.oracle.max.graal.graph.*;
 import com.oracle.max.graal.nodes.*;
@@ -35,32 +36,20 @@
 public class DebugInfoBuilder {
     public final GraalCompilation compilation;
 
-    private final NodeMap<CiStackSlot> lockDataMap;
-
     public DebugInfoBuilder(GraalCompilation compilation) {
         this.compilation = compilation;
-        this.lockDataMap = new NodeMap<>(compilation.graph);
-    }
-
-    public CiStackSlot lockDataFor(MonitorObject object) {
-        CiStackSlot result = lockDataMap.get(object);
-        if (result == null) {
-            result = compilation.frameMap().allocateStackBlock(compilation.compiler.runtime.sizeOfLockData(), false);
-            lockDataMap.set(object, result);
-        }
-        return result;
     }
 
 
     private HashMap<VirtualObjectNode, CiVirtualObject> virtualObjects = new HashMap<>();
 
-    public LIRDebugInfo build(FrameState topState, List<CiStackSlot> pointerSlots, LabelRef exceptionEdge) {
+    public LIRDebugInfo build(FrameState topState, LockScope locks, List<CiStackSlot> pointerSlots, LabelRef exceptionEdge) {
         if (compilation.placeholderState != null) {
             return null;
         }
 
         assert virtualObjects.size() == 0;
-        CiFrame frame = computeFrameForState(topState);
+        CiFrame frame = computeFrameForState(topState, locks);
 
         CiVirtualObject[] virtualObjectsArray = null;
         if (virtualObjects.size() != 0) {
@@ -121,28 +110,37 @@
         return new LIRDebugInfo(frame, virtualObjectsArray, pointerSlots, exceptionEdge);
     }
 
-    private CiFrame computeFrameForState(FrameState state) {
-        CiValue[] values = new CiValue[state.valuesSize() + state.locksSize()];
+    private CiFrame computeFrameForState(FrameState state, LockScope locks) {
+        int numLocks = (locks != null && locks.callerState == state.outerFrameState()) ? locks.stateDepth + 1 : 0;
+
+        CiValue[] values = new CiValue[state.valuesSize() + numLocks];
         int valueIndex = 0;
 
         for (int i = 0; i < state.valuesSize(); i++) {
             values[valueIndex++] = toCiValue(state.valueAt(i));
         }
 
-        for (int i = 0; i < state.locksSize(); i++) {
-            MonitorObject monitorObject = state.lockAt(i);
-            CiValue owner = toCiValue(monitorObject.owner());
-            CiValue lockData = lockDataFor(monitorObject);
-            boolean eliminated = owner instanceof CiVirtualObject;
+        LockScope nextLock = locks;
+        for (int i = numLocks - 1; i >= 0; i--) {
+            assert locks != null && nextLock.callerState == state.outerFrameState() && nextLock.stateDepth == i;
 
-            values[valueIndex++] = new CiMonitorValue(owner, lockData, eliminated);
+            CiValue owner = toCiValue(nextLock.monitor.object());
+            CiValue lockData = nextLock.lockData;
+            boolean eliminated = nextLock.monitor.eliminated();
+            values[state.valuesSize() + nextLock.stateDepth] = new CiMonitorValue(owner, lockData, eliminated);
+
+            nextLock = nextLock.outer;
         }
 
         CiFrame caller = null;
         if (state.outerFrameState() != null) {
-            caller = computeFrameForState(state.outerFrameState());
+            caller = computeFrameForState(state.outerFrameState(), nextLock);
+        } else {
+            if (nextLock != null) {
+                throw new CiBailout("unbalanced monitors: found monitor for unknown frame");
+            }
         }
-        CiFrame frame = new CiFrame(caller, state.method(), state.bci, state.rethrowException(), values, state.localsSize(), state.stackSize(), state.locksSize());
+        CiFrame frame = new CiFrame(caller, state.method(), state.bci, state.rethrowException(), values, state.localsSize(), state.stackSize(), numLocks);
         return frame;
     }
 
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/gen/LIRGenerator.java	Thu Jan 05 17:02:58 2012 -0800
@@ -74,6 +74,62 @@
     private ValueNode lastInstructionPrinted; // Debugging only
     private FrameState lastState;
 
+    /**
+     * Class used to reconstruct the nesting of locks that is required for debug information.
+     */
+    public static class LockScope {
+        /**
+         * Linked list of locks. {@link LIRGenerator#curLocks} is the head of the list.
+         */
+        public final LockScope outer;
+
+        /**
+         * The frame state of the caller of the method performing the lock, or null if the outermost method
+         * performs the lock. This information is used to compute the {@link CiFrame} that this lock belongs to.
+         * We cannot use the actual frame state of the locking method, because it is now unique for a method. The
+         * caller frame states are unique, i.e., all frame states of an inlined methods refer to the same caller frame state.
+         */
+        public final FrameState callerState;
+
+        /**
+         * The number of locks already found for this frame state.
+         */
+        public final int stateDepth;
+
+        /**
+         * The monitor enter node, with information about the object that is locked and the elimination status.
+         */
+        public final MonitorEnterNode monitor;
+
+        /**
+         * Space in the stack frame needed by the VM to perform the locking.
+         */
+        public final CiStackSlot lockData;
+
+        public LockScope(LockScope outer, FrameState callerState, MonitorEnterNode monitor, CiStackSlot lockData) {
+            this.outer = outer;
+            this.callerState = callerState;
+            this.monitor = monitor;
+            this.lockData = lockData;
+            if (outer != null && outer.callerState == callerState) {
+                this.stateDepth = outer.stateDepth + 1;
+            } else {
+                this.stateDepth = 0;
+            }
+        }
+    }
+
+    /**
+     * Mapping from blocks to the lock state at the end of the block, indexed by the id number of the block.
+     */
+    private LockScope[] blockLocks;
+
+    /**
+     * The list of currently locked monitors.
+     */
+    private LockScope curLocks;
+
+
     public LIRGenerator(GraalCompilation compilation, RiXirGenerator xir) {
         this.context = compilation.compiler.context;
         this.compilation = compilation;
@@ -81,6 +137,7 @@
         this.xir = xir;
         this.xirSupport = new XirSupport();
         this.debugInfoBuilder = new DebugInfoBuilder(compilation);
+        this.blockLocks = new LockScope[lir.linearScanOrder().size()];
     }
 
     @Override
@@ -88,6 +145,13 @@
         return compilation.compiler.target;
     }
 
+    private LockScope locksFor(LIRBlock block) {
+        return blockLocks[block.blockID()];
+    }
+
+    private void setLocksFor(LIRBlock block, LockScope locks) {
+        blockLocks[block.blockID()] = locks;
+    }
 
     /**
      * Returns the operand that has been previously initialized by {@link #setResult()}
@@ -176,7 +240,7 @@
     }
 
     public LIRDebugInfo stateFor(FrameState state, List<CiStackSlot> pointerSlots, LabelRef exceptionEdge) {
-        return debugInfoBuilder.build(state, pointerSlots, exceptionEdge);
+        return debugInfoBuilder.build(state, curLocks, pointerSlots, exceptionEdge);
     }
 
     /**
@@ -223,9 +287,15 @@
         }
 
         if (block == lir.startBlock()) {
+            assert block.getPredecessors().size() == 0;
             emitPrologue();
-        } else if (block.getPredecessors().size() > 0) {
+            curLocks = null;
+
+        } else {
+            assert block.getPredecessors().size() > 0;
             FrameState fs = null;
+            curLocks = locksFor(block.predAt(0));
+
             for (Block p : block.getPredecessors()) {
                 LIRBlock pred = (LIRBlock) p;
                 if (fs == null) {
@@ -234,6 +304,9 @@
                     fs = null;
                     break;
                 }
+                if (curLocks != locksFor(pred)) {
+                    throw new CiBailout("unbalanced monitors: predecessor blocks have different monitor states");
+                }
             }
             if (GraalOptions.TraceLIRGeneratorLevel >= 2) {
                 if (fs == null) {
@@ -308,6 +381,7 @@
             TTY.println("END Generating LIR for block B" + block.blockID());
         }
 
+        setLocksFor(currentBlock, curLocks);
         block.setLastState(lastState);
         currentBlock = null;
 
@@ -400,18 +474,46 @@
 
     @Override
     public void visitMonitorEnter(MonitorEnterNode x) {
-        XirArgument obj = toXirArgument(x.object().owner());
-        XirArgument lockAddress = toXirArgument(emitLea(debugInfoBuilder.lockDataFor(x.object())));
+        CiStackSlot lockData = compilation.frameMap().allocateStackBlock(compilation.compiler.runtime.sizeOfLockData(), false);
+        if (x.eliminated()) {
+            // No code is emitted for elimianted locks, but for proper debug information generation we need to
+            // register the monitor and its lock data.
+            curLocks = new LockScope(curLocks, x.stateAfter().outerFrameState(), x, lockData);
+            return;
+        }
+
+        XirArgument obj = toXirArgument(x.object());
+        XirArgument lockAddress = lockData == null ? null : toXirArgument(emitLea(lockData));
+
+        LIRDebugInfo stateBefore = state();
+        // The state before the monitor enter is used for null checks, so it must not contain the newly locked object.
+        curLocks = new LockScope(curLocks, x.stateAfter().outerFrameState(), x, lockData);
+        // The state after the monitor enter is used for deotpimization, after the montior has blocked, so it must contain the newly locked object.
+        LIRDebugInfo stateAfter = stateFor(x.stateAfter());
+
         XirSnippet snippet = xir.genMonitorEnter(site(x), obj, lockAddress);
-        emitXir(snippet, x, state(), stateFor(x.stateAfter()), null, true);
+        emitXir(snippet, x, stateBefore, stateAfter, null, true);
     }
 
     @Override
     public void visitMonitorExit(MonitorExitNode x) {
-        XirArgument obj = toXirArgument(x.object().owner());
-        XirArgument lockAddress = toXirArgument(emitLea(debugInfoBuilder.lockDataFor(x.object())));
+        if (curLocks == null || curLocks.monitor.object() != x.object() || curLocks.monitor.eliminated() != x.eliminated()) {
+            throw new CiBailout("unbalanced monitors: attempting to unlock an object that is not on top of the locking stack");
+        }
+        if (x.eliminated()) {
+            curLocks = curLocks.outer;
+            return;
+        }
+
+        CiStackSlot lockData = curLocks.lockData;
+        XirArgument obj = toXirArgument(x.object());
+        XirArgument lockAddress = lockData == null ? null : toXirArgument(emitLea(lockData));
+
+        LIRDebugInfo stateBefore = state();
+        curLocks = curLocks.outer;
+
         XirSnippet snippet = xir.genMonitorExit(site(x), obj, lockAddress);
-        emitXir(snippet, x, state(), null, true);
+        emitXir(snippet, x, stateBefore, null, true);
     }
 
     @Override
--- a/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.compiler/src/com/oracle/max/graal/compiler/util/InliningUtil.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -294,23 +294,12 @@
             }
             return false;
         }
-        if (invoke.stateAfter().locksSize() > 0) {
-            if (GraalOptions.TraceInlining) {
-                TTY.println("not inlining %s because of locks", methodName(invoke.callTarget().targetMethod(), invoke));
-            }
-            return false;
-        }
         if (invoke.predecessor() == null) {
             if (GraalOptions.TraceInlining) {
                 TTY.println("not inlining %s because the invoke is dead code", methodName(invoke.callTarget().targetMethod(), invoke));
             }
             return false;
         }
-        if (invoke.stateAfter() == null) {
-            if (GraalOptions.TraceInlining) {
-                TTY.println("not inlining %s because of missing frame state", methodName(invoke.callTarget().targetMethod(), invoke));
-            }
-        }
         return true;
     }
 
--- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/FrameStateBuilder.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -41,7 +41,6 @@
 
     private final ValueNode[] locals;
     private final ValueNode[] stack;
-    private final ArrayList<MonitorObject> locks;
 
     private int stackIndex;
     private boolean rethrowException;
@@ -84,7 +83,6 @@
             javaIndex += stackSlots(kind);
             index++;
         }
-        this.locks = new ArrayList<>();
     }
 
     @Override
@@ -103,19 +101,15 @@
         for (int i = 0; i < other.stackSize(); i++) {
             stack[i] = other.stackAt(i);
         }
-        locks.clear();
-        for (int i = 0; i < other.locksSize(); i++) {
-            locks.add(other.lockAt(i));
-        }
         this.rethrowException = other.rethrowException();
     }
 
     public FrameState create(int bci) {
-        return graph.add(new FrameState(method, bci, locals, stack, stackIndex, locks, rethrowException));
+        return graph.add(new FrameState(method, bci, locals, stack, stackIndex, rethrowException));
     }
 
     public FrameState duplicateWithException(int bci, ValueNode exceptionObject) {
-        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, locks, true));
+        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[]{exceptionObject}, 1, true));
         frameState.setOuterFrameState(outerFrameState());
         return frameState;
     }
@@ -371,24 +365,6 @@
     }
 
     /**
-     * Locks a new object within the specified IRScope.
-     * @param scope the IRScope in which this locking operation occurs
-     * @param obj the object being locked
-     */
-    public void lock(MonitorObject obj) {
-        assert obj == null || (obj.kind() != CiKind.Void && obj.kind() != CiKind.Illegal) : "unexpected value: " + obj;
-        locks.add(obj);
-    }
-
-    /**
-     * Unlock the lock on the top of the stack.
-     */
-    public void unlock(MonitorObject obj) {
-        assert locks.get(locks.size() - 1) == obj;
-        locks.remove(locks.size() - 1);
-    }
-
-    /**
      * Get the value on the stack at the specified stack index.
      *
      * @param i the index into the stack, with {@code 0} being the bottom of the stack
@@ -409,15 +385,6 @@
     }
 
     /**
-     * Retrieves the lock at the specified index in the lock stack.
-     * @param i the index into the lock stack
-     * @return the instruction which produced the object at the specified location in the lock stack
-     */
-    public final MonitorObject lockAt(int i) {
-        return locks.get(i);
-    }
-
-    /**
      * Returns the size of the local variables.
      *
      * @return the size of the local variables
@@ -427,13 +394,6 @@
     }
 
     /**
-     * Gets number of locks held by this frame state.
-     */
-    public int locksSize() {
-        return locks.size();
-    }
-
-    /**
      * Gets the current size (height) of the stack.
      */
     public int stackSize() {
@@ -448,10 +408,6 @@
         return new ValueArrayIterator(locals);
     }
 
-    public List<MonitorObject> locks() {
-        return Collections.unmodifiableList(locks);
-    }
-
 
     private static class ValueArrayIterator implements Iterator<ValueNode> {
         private final ValueNode[] array;
@@ -494,10 +450,8 @@
     public ValueNode valueAt(int i) {
         if (i < locals.length) {
             return locals[i];
-        } else if (i < locals.length + stackIndex) {
+        } else {
             return stack[i - locals.length];
-        } else {
-            return locks.get(i - locals.length - stack.length);
         }
     }
 
@@ -507,7 +461,7 @@
     }
 
     public FrameState duplicateWithoutStack(int bci) {
-        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, locks, false));
+        FrameState frameState = graph.add(new FrameState(method, bci, locals, new ValueNode[0], 0, false));
         frameState.setOuterFrameState(outerFrameState());
         return frameState;
     }
--- a/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.java/src/com/oracle/max/graal/java/GraphBuilderPhase.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -1091,26 +1091,14 @@
     }
 
     private MonitorEnterNode genMonitorEnter(ValueNode x) {
-        MonitorObject monitorObject = currentGraph.add(new MonitorObject(x));
-        MonitorEnterNode monitorEnter = currentGraph.add(new MonitorEnterNode(monitorObject));
-        frameState.lock(monitorObject);
+        MonitorEnterNode monitorEnter = currentGraph.add(new MonitorEnterNode(x));
         appendWithBCI(monitorEnter);
         return monitorEnter;
     }
 
     private MonitorExitNode genMonitorExit(ValueNode x) {
-        if (frameState.locksSize() <= 0) {
-            throw new CiBailout("monitor stack underflow");
-        }
-        MonitorObject monitorObject = frameState.lockAt(frameState.locksSize() - 1);
-
-        // We only compile methods with balanced monitors.  However, x might be a phi function
-        // that can be optimized away later on, so we have to disable the check for phi functions.
-        assert x == monitorObject.owner() || x instanceof PhiNode;
-
-        MonitorExitNode monitorExit = currentGraph.add(new MonitorExitNode(monitorObject));
+        MonitorExitNode monitorExit = currentGraph.add(new MonitorExitNode(x));
         appendWithBCI(monitorExit);
-        frameState.unlock(monitorObject);
         return monitorExit;
     }
 
@@ -1475,10 +1463,6 @@
                 ValueNode value = frameState.stackAt(i);
                 log.println(String.format("|   stack[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value));
             }
-            for (int i = 0; i < frameState.locksSize(); ++i) {
-                ValueNode value = frameState.lockAt(i);
-                log.println(String.format("|   lock[%d] = %-8s : %s", i, value == null ? "bogus" : value.kind().javaName, value));
-            }
         }
     }
 
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/FrameState.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -41,8 +41,6 @@
 
     protected final int stackSize;
 
-    protected final int locksSize;
-
     private boolean rethrowException;
 
     /**
@@ -112,38 +110,32 @@
      * @param bci the bytecode index of the frame state
      * @param localsSize number of locals
      * @param stackSize size of the stack
-     * @param lockSize number of locks
      * @param rethrowException if true the VM should re-throw the exception on top of the stack when deopt'ing using this framestate
      */
-    public FrameState(RiResolvedMethod method, int bci, int localsSize, int stackSize, int locksSize, boolean rethrowException) {
+    public FrameState(RiResolvedMethod method, int bci, int localsSize, int stackSize, boolean rethrowException) {
         assert stackSize >= 0;
         this.method = method;
         this.bci = bci;
         this.localsSize = localsSize;
         this.stackSize = stackSize;
-        this.locksSize = locksSize;
-        this.values = new NodeInputList<>(this, localsSize + stackSize + locksSize);
+        this.values = new NodeInputList<>(this, localsSize + stackSize);
         this.virtualObjectMappings = new NodeInputList<>(this);
         this.rethrowException = rethrowException;
         assert !rethrowException || stackSize == 1 : "must have exception on top of the stack";
     }
 
-    public FrameState(RiResolvedMethod method, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, List<MonitorObject> locks, boolean rethrowException) {
+    public FrameState(RiResolvedMethod method, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, boolean rethrowException) {
         this.method = method;
         this.bci = bci;
         this.localsSize = locals.length;
         this.stackSize = stackSize;
-        this.locksSize = locks.size();
-        final ValueNode[] newValues = new ValueNode[locals.length + stackSize + locks.size()];
+        final ValueNode[] newValues = new ValueNode[locals.length + stackSize];
         for (int i = 0; i < locals.length; i++) {
             newValues[i] = locals[i];
         }
         for (int i = 0; i < stackSize; i++) {
             newValues[localsSize + i] = stack[i];
         }
-        for (int i = 0; i < locks.size(); i++) {
-            newValues[locals.length + stackSize + i] = locks.get(i);
-        }
         this.values = new NodeInputList<>(this, newValues);
         this.virtualObjectMappings = new NodeInputList<>(this);
         this.rethrowException = rethrowException;
@@ -183,7 +175,7 @@
     }
 
     public FrameState duplicate(int newBci, boolean duplicateOuter) {
-        FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize, locksSize, rethrowException));
+        FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize, rethrowException));
         other.values.setAll(values);
         other.virtualObjectMappings.setAll(virtualObjectMappings);
         FrameState newOuterFrameState = outerFrameState();
@@ -207,7 +199,7 @@
     public FrameState duplicateModified(int newBci, boolean newRethrowException, CiKind popKind, ValueNode... pushedValues) {
         int popSlots = popKind == CiKind.Void ? 0 : isTwoSlot(popKind) ? 2 : 1;
         int pushSlots = pushedValues.length;
-        FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize - popSlots + pushSlots, locksSize(), newRethrowException));
+        FrameState other = graph().add(new FrameState(method, newBci, localsSize, stackSize - popSlots + pushSlots, newRethrowException));
         for (int i = 0; i < localsSize; i++) {
             other.setValueAt(i, localAt(i));
         }
@@ -218,16 +210,13 @@
         for (int i = 0; i < pushSlots; i++) {
             other.setValueAt(slot++, pushedValues[i]);
         }
-        for (int i = 0; i < locksSize; i++) {
-            other.setValueAt(localsSize + other.stackSize + i, lockAt(i));
-        }
         other.virtualObjectMappings.setAll(virtualObjectMappings);
         other.setOuterFrameState(outerFrameState());
         return other;
     }
 
     public boolean isCompatibleWith(FrameStateAccess other) {
-        if (stackSize() != other.stackSize() || localsSize() != other.localsSize() || locksSize() != other.locksSize()) {
+        if (stackSize() != other.stackSize() || localsSize() != other.localsSize()) {
             return false;
         }
         for (int i = 0; i < stackSize(); i++) {
@@ -237,11 +226,6 @@
                 return false;
             }
         }
-        for (int i = 0; i < locksSize(); i++) {
-            if (lockAt(i) != other.lockAt(i)) {
-                return false;
-            }
-        }
         if (other.outerFrameState() != outerFrameState()) {
             return false;
         }
@@ -249,7 +233,7 @@
     }
 
     public boolean equals(FrameStateAccess other) {
-        if (stackSize() != other.stackSize() || localsSize() != other.localsSize() || locksSize() != other.locksSize()) {
+        if (stackSize() != other.stackSize() || localsSize() != other.localsSize()) {
             return false;
         }
         for (int i = 0; i < stackSize(); i++) {
@@ -259,11 +243,6 @@
                 return false;
             }
         }
-        for (int i = 0; i < locksSize(); i++) {
-            if (lockAt(i) != other.lockAt(i)) {
-                return false;
-            }
-        }
         if (other.outerFrameState() != outerFrameState()) {
             return false;
         }
@@ -285,13 +264,6 @@
     }
 
     /**
-     * Gets number of locks held by this frame state.
-     */
-    public int locksSize() {
-        return locksSize;
-    }
-
-    /**
      * Invalidates the local variable at the specified index. If the specified index refers to a doubleword local, then
      * invalidates the high word as well.
      *
@@ -352,16 +324,6 @@
     }
 
     /**
-     * Retrieves the lock at the specified index in the lock stack.
-     * @param i the index into the lock stack
-     * @return the instruction which produced the object at the specified location in the lock stack
-     */
-    public MonitorObject lockAt(int i) {
-        assert i >= 0;
-        return (MonitorObject) valueAt(localsSize + stackSize + i);
-    }
-
-    /**
      * Inserts a phi statement into the stack at the specified stack index.
      * @param block the block begin for which we are creating the phi
      * @param i the index into the stack for which to create a phi
@@ -411,7 +373,7 @@
      * @return the value at index {@code i} which may be {@code null}
      */
     public ValueNode valueAt(int i) {
-        assert i < (localsSize + stackSize + locksSize);
+        assert i < (localsSize + stackSize);
         return values.isEmpty() ? null : values.get(i);
     }
 
@@ -607,10 +569,6 @@
             ValueNode value = stackAt(i);
             sb.append(String.format("  stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value));
         }
-        for (int i = 0; i < locksSize(); ++i) {
-            ValueNode value = lockAt(i);
-            sb.append(String.format("  lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value));
-        }
         return sb.toString();
     }
 
@@ -633,10 +591,6 @@
                 ValueNode value = fs.stackAt(i);
                 sb.append(String.format("  stack[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value));
             }
-            for (int i = 0; i < fs.locksSize(); ++i) {
-                ValueNode value = fs.lockAt(i);
-                sb.append(String.format("  lock[%d] = %-8s : %s%n", i, value == null ? "bogus" : value.kind().javaName, value));
-            }
             fs = fs.outerFrameState();
         }
         return sb.toString();
@@ -684,11 +638,6 @@
             str.append(i == 0 ? "" : ", ").append(stackAt(i) == null ? "_" : stackAt(i).toString(Verbosity.Id));
         }
         properties.put("stack", str.toString());
-        str = new StringBuilder();
-        for (int i = 0; i < locksSize(); i++) {
-            str.append(i == 0 ? "" : ", ").append(lockAt(i) == null ? "_" : lockAt(i).toString(Verbosity.Id));
-        }
-        properties.put("locks", str.toString());
         properties.put("rethrowException", rethrowException);
         return properties;
     }
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/MonitorObject.java	Thu Jan 05 17:02:13 2012 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,54 +0,0 @@
-/*
- * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.max.graal.nodes;
-
-import com.oracle.max.graal.nodes.java.*;
-import com.oracle.max.graal.nodes.spi.*;
-
-/**
- * Encapsulates the object that is locked and unlocked. This node is referenced by a {@link MonitorEnterNode},
- * all {@link MonitorExitNode} that correspond to this monitor enter, and in all {@link FrameState}s in between
- * the monitor enter and monitor exits.
- */
-public class MonitorObject extends ValueNode implements LIRLowerable {
-    @Input private ValueNode owner;
-
-    public ValueNode owner() {
-        return owner;
-    }
-
-    /**
-     * Creates a new MonitorObjectNode.
-     *
-     * @param object The object that is processed by the monitor operation.
-     */
-    public MonitorObject(ValueNode object) {
-        super(object.stamp());
-        this.owner = object;
-    }
-
-    @Override
-    public void generate(LIRGeneratorTool gen) {
-        // Nothing to do, monitor objects are processed as part of the monitor enter / monitor exit nodes.
-    }
-}
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/AccessMonitorNode.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/AccessMonitorNode.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -22,19 +22,42 @@
  */
 package com.oracle.max.graal.nodes.java;
 
+import com.oracle.max.cri.ci.*;
 import com.oracle.max.graal.nodes.*;
 import com.oracle.max.graal.nodes.extended.*;
 import com.oracle.max.graal.nodes.type.*;
 
 /**
  * The {@code AccessMonitorNode} is the base class of both monitor acquisition and release.
+ * <br>
+ * 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.
+ * <br>
+ * The Java bytecode specification allows non-balanced locking. Graal does not handle such cases and throws a
+ * {@link CiBailout} instead. Detecting non-balanced monitors during bytecode parsing is difficult, since the
+ * node flowing into the {@link MonitorExitNode} can be a phi function hiding the node that was flowing into the
+ * {@link MonitorEnterNode}. Optimization phases are free to throw {@link CiBailout} if they detect such cases.
+ * Otherwise, they are detected during LIR construction.
  */
 public abstract class AccessMonitorNode extends AbstractStateSplit implements MemoryCheckpoint {
 
-    @Input private MonitorObject object;
+    @Input private ValueNode object;
+    @Data private boolean eliminated;
+
+    public ValueNode object() {
+        return object;
+    }
 
-    public MonitorObject object() {
-        return object;
+    public boolean eliminated() {
+        return eliminated;
+    }
+
+    public void makeEliminated() {
+        eliminated = true;
     }
 
     /**
@@ -42,7 +65,7 @@
      *
      * @param object the instruction producing the object
      */
-    public AccessMonitorNode(MonitorObject object) {
+    public AccessMonitorNode(ValueNode object) {
         super(StampFactory.illegal());
         this.object = object;
     }
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorEnterNode.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorEnterNode.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -35,7 +35,7 @@
      *
      * @param object the instruction producing the object
      */
-    public MonitorEnterNode(MonitorObject object) {
+    public MonitorEnterNode(ValueNode object) {
         super(object);
     }
 
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorExitNode.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/java/MonitorExitNode.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -36,7 +36,7 @@
      *
      * @param object the instruction produces the object value
      */
-    public MonitorExitNode(MonitorObject object) {
+    public MonitorExitNode(ValueNode object) {
         super(object);
     }
 
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/EscapeOp.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -46,8 +46,8 @@
         } else if (usage instanceof FrameState) {
             assert ((FrameState) usage).inputs().contains(node);
             return true;
-        } else if (usage instanceof MonitorObject) {
-            assert ((MonitorObject) usage).owner() == node;
+        } else if (usage instanceof AccessMonitorNode) {
+            assert ((AccessMonitorNode) usage).object() == node;
             return false;
         } else if (usage instanceof LoadFieldNode) {
             assert ((LoadFieldNode) usage).object() == node;
@@ -107,14 +107,8 @@
             IsTypeNode x = (IsTypeNode) usage;
             assert x.type() == ((ValueNode) node).exactType();
             x.replaceAndDelete(ConstantNode.forBoolean(true, node.graph()));
-        } else if (usage instanceof MonitorObject) {
-            // delete all MonitorEnterNode and MonitorExitNode
-            for (Node n : usage.usages().snapshot()) {
-                if (n instanceof AccessMonitorNode) {
-                    AccessMonitorNode x = (AccessMonitorNode) n;
-                    x.replaceAndDelete(x.next());
-                }
-            }
+        } else if (usage instanceof AccessMonitorNode) {
+            ((AccessMonitorNode) usage).makeEliminated();
         }
     }
 
--- a/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/FrameStateAccess.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.nodes/src/com/oracle/max/graal/nodes/spi/FrameStateAccess.java	Thu Jan 05 17:02:58 2012 -0800
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
@@ -32,16 +32,12 @@
 
     int stackSize();
 
-    int locksSize();
-
     boolean rethrowException();
 
     ValueNode valueAt(int i);
 
     ValueNode localAt(int i);
 
-    ValueNode lockAt(int i);
-
     ValueNode stackAt(int i);
 
     FrameState outerFrameState();
--- a/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java	Thu Jan 05 17:02:13 2012 -0800
+++ b/graal/com.oracle.max.graal.printer/src/com/oracle/max/graal/printer/CFGPrinter.java	Thu Jan 05 17:02:58 2012 -0800
@@ -316,14 +316,6 @@
                 buf.append("\n");
             }
 
-            if (curState.locksSize() > 0) {
-                buf.append("locks: ");
-                for (int i = 0; i < curState.locksSize(); ++i) {
-                    buf.append(stateValueToString(curState.lockAt(i))).append(' ');
-                }
-                buf.append("\n");
-            }
-
             buf.append("locals: ");
             for (int i = 0; i < curState.localsSize(); i++) {
                 buf.append(stateValueToString(curState.localAt(i))).append(' ');