changeset 12741:a5b5e1ebab81

Merge
author Christos Kotselidis <christos.kotselidis@oracle.com>
date Sat, 09 Nov 2013 21:34:07 +0100
parents ad2434911b69 (current diff) bb85b81258a0 (diff)
children 40924dbc623b
files graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java
diffstat 93 files changed, 2059 insertions(+), 871 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Constant.java	Sat Nov 09 21:34:07 2013 +0100
@@ -194,7 +194,7 @@
      * @return the constant value
      */
     public long asLong() {
-        assert getKind() == Kind.Long || getKind().getStackKind() == Kind.Int;
+        assert getKind().isNumericInteger();
         return primitive;
     }
 
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/Kind.java	Sat Nov 09 21:34:07 2013 +0100
@@ -117,6 +117,24 @@
     }
 
     /**
+     * Checks whether this type is a Java primitive type representing an integer number.
+     * 
+     * @return {@code true} if the stack kind is {@link #Int} or {@link #Long}.
+     */
+    public boolean isNumericInteger() {
+        return isStackInt || this == Kind.Long;
+    }
+
+    /**
+     * Checks whether this type is a Java primitive type representing a floating point number.
+     * 
+     * @return {@code true} if this is {@link #Float} or {@link #Double}.
+     */
+    public boolean isNumericFloat() {
+        return this == Kind.Float || this == Kind.Double;
+    }
+
+    /**
      * Returns the kind corresponding to the Java type string.
      * 
      * @param typeString the Java type string
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/MetaAccessProvider.java	Sat Nov 09 21:34:07 2013 +0100
@@ -70,9 +70,11 @@
     /**
      * Encodes a deoptimization action and a deoptimization reason in an integer value.
      * 
+     * @param speculationId a speculation ID returned by SpeculationLog.addSpeculation
+     * 
      * @return the encoded value as an integer
      */
-    Constant encodeDeoptActionAndReason(DeoptimizationAction action, DeoptimizationReason reason);
+    Constant encodeDeoptActionAndReason(DeoptimizationAction action, DeoptimizationReason reason, int speculationId);
 
     DeoptimizationReason decodeDeoptReason(Constant constant);
 
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64AsmOptions.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64AsmOptions.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,11 +23,9 @@
 package com.oracle.graal.asm.amd64;
 
 public class AMD64AsmOptions {
-
-    public static int Atomics = 0;
-    public static boolean UseNormalNop = false;
-    public static boolean UseAddressNop = true;
-    public static boolean UseIncDec = true;
-    public static boolean UseXmmLoadAndClearUpper = true;
-    public static boolean UseXmmRegToRegMoveAll = true;
+    public static final boolean UseNormalNop = false;
+    public static final boolean UseAddressNop = true;
+    public static final boolean UseIncDec = true;
+    public static final boolean UseXmmLoadAndClearUpper = true;
+    public static final boolean UseXmmRegToRegMoveAll = true;
 }
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Sat Nov 09 21:34:07 2013 +0100
@@ -524,26 +524,10 @@
     // and stores reg into adr if so; otherwise, the value at adr is loaded into X86.rax,.
     // The ZF is set if the compared values were equal, and cleared otherwise.
     public final void cmpxchgl(Register reg, AMD64Address adr) { // cmpxchg
-        if ((Atomics & 2) != 0) {
-            // caveat: no instructionmark, so this isn't relocatable.
-            // Emit a synthetic, non-atomic, CAS equivalent.
-            // Beware. The synthetic form sets all ICCs, not just ZF.
-            // cmpxchg r,[m] is equivalent to X86.rax, = CAS (m, X86.rax, r)
-            cmpl(rax, adr);
-            movl(rax, adr);
-            if (reg.equals(rax)) {
-                Label l = new Label();
-                jccb(ConditionFlag.NotEqual, l);
-                movl(adr, reg);
-                bind(l);
-            }
-        } else {
-
-            prefix(adr, reg);
-            emitByte(0x0F);
-            emitByte(0xB1);
-            emitOperandHelper(reg, adr);
-        }
+        prefix(adr, reg);
+        emitByte(0x0F);
+        emitByte(0xB1);
+        emitOperandHelper(reg, adr);
     }
 
     public final void cvtsd2ss(Register dst, AMD64Address src) {
@@ -860,12 +844,7 @@
     }
 
     public final void lock() {
-        if ((Atomics & 1) != 0) {
-            // Emit either nothing, a NOP, or a NOP: prefix
-            emitByte(0x90);
-        } else {
-            emitByte(0xF0);
-        }
+        emitByte(0xF0);
     }
 
     public final void movapd(Register dst, Register src) {
--- a/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.asm.hsail/src/com/oracle/graal/asm/hsail/HSAILAssembler.java	Sat Nov 09 21:34:07 2013 +0100
@@ -355,39 +355,75 @@
     }
 
     /**
-     * Emit code to build a 64-bit pointer from a compressed-oop and the associated base and shift.
-     * We only emit this if base and shift are not both zero.
+     * Emits code to build a 64-bit pointer from a compressed value and the associated base and
+     * shift. The compressed value could represent either a normal oop or a klass ptr. If the
+     * compressed value is 0, the uncompressed must also be 0. We only emit this if base and shift
+     * are not both zero.
+     * 
+     * @param result the register containing the compressed value on input and the uncompressed ptr
+     *            on output
+     * @param base the amount to be added to the compressed value
+     * @param shift the number of bits to shift left the compressed value
+     * @param testForNull true if the compressed value might be null
      */
-    public void emitCompressedOopDecode(Value result, long narrowOopBase, int narrowOopShift) {
-        if (narrowOopBase == 0) {
-            emit("shl", result, result, Constant.forInt(narrowOopShift));
-        } else if (narrowOopShift == 0) {
-            // only use add if result is not starting as null (unsigned compare)
-            emitCompare(result, Constant.forLong(0), "eq", false, true);
-            emit("add", result, result, Constant.forLong(narrowOopBase));
-            emitConditionalMove(result, Constant.forLong(0), result, 64);
+    public void emitCompressedOopDecode(Value result, long base, int shift, boolean testForNull) {
+        assert (base != 0 || shift != 0);
+        assert (!isConstant(result));
+        if (base == 0) {
+            // we don't have to test for null if shl is the only operation
+            emitForceUnsigned("shl", result, result, Constant.forInt(shift));
+        } else if (shift == 0) {
+            // only use add if result is not starting as null (test only if testForNull is true)
+            emitWithOptionalTestForNull(testForNull, "add", result, result, Constant.forLong(base));
         } else {
-            // only use mad if result is not starting as null (unsigned compare)
-            emitCompare(result, Constant.forLong(0), "eq", false, true);
-            emitTextFormattedInstruction("mad_u64 ", result, result, Constant.forInt(1 << narrowOopShift), Constant.forLong(narrowOopBase));
-            emitConditionalMove(result, Constant.forLong(0), result, 64);
+            // only use mad if result is not starting as null (test only if testForNull is true)
+            emitWithOptionalTestForNull(testForNull, "mad", result, result, Constant.forInt(1 << shift), Constant.forLong(base));
         }
     }
 
     /**
-     * Emit code to build a 32-bit compressed pointer from a full 64-bit pointer using the
-     * associated base and shift. We only emit this if base and shift are not both zero.
+     * Emits code to build a compressed value from a full 64-bit pointer using the associated base
+     * and shift. The compressed value could represent either a normal oop or a klass ptr. If the
+     * ptr is 0, the compressed value must also be 0. We only emit this if base and shift are not
+     * both zero.
+     * 
+     * @param result the register containing the 64-bit pointer on input and the compressed value on
+     *            output
+     * @param base the amount to be subtracted from the 64-bit pointer
+     * @param shift the number of bits to shift right the 64-bit pointer
+     * @param testForNull true if the 64-bit pointer might be null
      */
-    public void emitCompressedOopEncode(Value result, long narrowOopBase, int narrowOopShift) {
-        if (narrowOopBase != 0) {
-            // only use sub if result is not starting as null (unsigned compare)
+    public void emitCompressedOopEncode(Value result, long base, int shift, boolean testForNull) {
+        assert (base != 0 || shift != 0);
+        assert (!isConstant(result));
+        if (base != 0) {
+            // only use sub if result is not starting as null (test only if testForNull is true)
+            emitWithOptionalTestForNull(testForNull, "sub", result, result, Constant.forLong(base));
+        }
+        if (shift != 0) {
+            // note that the shr can still be done even if the result is null
+            emitForceUnsigned("shr", result, result, Constant.forInt(shift));
+        }
+    }
+
+    /**
+     * Emits code for the requested mnemonic on the result and sources. In addition, if testForNull
+     * is true, surrounds the instruction with code that will guarantee that if the result starts as
+     * 0, it will remain 0.
+     * 
+     * @param testForNull true if we want to add the code to check for and preserve null
+     * @param mnemonic the instruction to be applied (without size prefix)
+     * @param result the register which is both an input and the final output
+     * @param sources the sources for the mnemonic instruction
+     */
+    private void emitWithOptionalTestForNull(boolean testForNull, String mnemonic, Value result, Value... sources) {
+        if (testForNull) {
             emitCompare(result, Constant.forLong(0), "eq", false, true);
-            emit("sub", result, result, Constant.forLong(narrowOopBase));
+        }
+        emitForceUnsigned(mnemonic, result, sources);
+        if (testForNull) {
             emitConditionalMove(result, Constant.forLong(0), result, 64);
         }
-        if (narrowOopShift != 0) {
-            emit("shr", result, result, Constant.forInt(narrowOopShift));
-        }
     }
 
     /**
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -279,7 +279,7 @@
     }
 
     private void emitIntegerTest(Value a, Value b) {
-        assert a.getKind().getStackKind() == Kind.Int || a.getKind() == Kind.Long;
+        assert a.getKind().isNumericInteger();
         if (LIRValueUtil.isVariable(b)) {
             append(new AMD64TestOp(load(b), loadNonConst(a)));
         } else {
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/GraalKernelTester.java	Sat Nov 09 21:34:07 2013 +0100
@@ -27,12 +27,14 @@
  * This class extends KernelTester and provides a base class
  * for which the HSAIL code comes from the Graal compiler.
  */
-import com.oracle.graal.hotspot.hsail.*;
+import static com.oracle.graal.phases.GraalOptions.*;
 
-import java.lang.reflect.Method;
 import java.io.*;
+import java.lang.reflect.*;
 
-import static com.oracle.graal.phases.GraalOptions.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.hsail.*;
+import com.oracle.graal.options.*;
 
 public abstract class GraalKernelTester extends KernelTester {
 
@@ -75,4 +77,14 @@
         boolean canExecuteCalls = runningOnSimulator();
         return (canGenerateCalls && canExecuteCalls);
     }
+
+    public static OptionValue<?> getOptionFromField(Class declaringClass, String fieldName) {
+        try {
+            Field f = declaringClass.getDeclaredField(fieldName);
+            f.setAccessible(true);
+            return (OptionValue<?>) f.get(null);
+        } catch (Exception e) {
+            throw new GraalInternalError(e);
+        }
+    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/InstanceOfTest.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,97 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.hsail.test;
+
+import com.oracle.graal.compiler.hsail.test.infra.GraalKernelTester;
+import org.junit.Test;
+
+/**
+ * Tests instanceof operator. Requires correct support for decompression of klass ptrs.
+ */
+public class InstanceOfTest extends GraalKernelTester {
+
+    static final int NUM = 20;
+
+    abstract static class Shape {
+
+        public abstract float getArea();
+    }
+
+    static class Circle extends Shape {
+
+        private float radius;
+
+        Circle(float r) {
+            radius = r;
+        }
+
+        @Override
+        public float getArea() {
+            return (float) (Math.PI * radius * radius);
+        }
+    }
+
+    static class Square extends Shape {
+
+        private float len;
+
+        Square(float len) {
+            this.len = len;
+        }
+
+        @Override
+        public float getArea() {
+            return len * len;
+        }
+    }
+
+    @Result public float[] outArray = new float[NUM];
+    public Shape[] inShapeArray = new Shape[NUM];
+
+    void setupArrays() {
+        for (int i = 0; i < NUM; i++) {
+            if (i % 2 == 0) {
+                inShapeArray[i] = new Circle(i + 1);
+            } else {
+                inShapeArray[i] = new Square(i + 1);
+            }
+            outArray[i] = -i;
+        }
+    }
+
+    public void run(int gid) {
+        outArray[gid] = (inShapeArray[gid] instanceof Circle ? 1.0f : 2.0f);
+    }
+
+    @Override
+    public void runTest() {
+        setupArrays();
+
+        dispatchMethodKernel(NUM);
+    }
+
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticNBodyCallTest.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticNBodyCallTest.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,10 +23,15 @@
 
 package com.oracle.graal.compiler.hsail.test;
 
+import static com.oracle.graal.phases.GraalOptions.*;
 import static org.junit.Assume.*;
 
 import org.junit.*;
 
+import com.oracle.graal.options.*;
+import com.oracle.graal.options.OptionValue.OverrideScope;
+import com.oracle.graal.phases.*;
+
 /**
  * Unit test of NBody demo app. This version uses a call to the main routine which would normally be
  * too large to inline.
@@ -37,10 +42,15 @@
         StaticNBodyTest.run(inxyz, outxyz, invxyz, outvxyz, gid);
     }
 
+    public void before() {
+    }
+
     @Test
     @Override
-    public void test() {
-        assumeTrue(aggressiveInliningEnabled() || canHandleHSAILMethodCalls());
-        testGeneratedHsail();
+    public void test() throws Exception {
+        try (OverrideScope s = OptionValue.override(InlineEverything, true, getOptionFromField(GraalOptions.class, "RemoveNeverExecutedCode"), false)) {
+            assumeTrue(aggressiveInliningEnabled() || canHandleHSAILMethodCalls());
+            testGeneratedHsail();
+        }
     }
 }
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticNBodyTest.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StaticNBodyTest.java	Sat Nov 09 21:34:07 2013 +0100
@@ -96,7 +96,7 @@
     }
 
     @Test
-    public void test() {
+    public void test() throws Exception {
         testGeneratedHsail();
     }
 }
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StringContainsAcceptTest.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/StringContainsAcceptTest.java	Sat Nov 09 21:34:07 2013 +0100
@@ -24,6 +24,12 @@
 package com.oracle.graal.compiler.hsail.test;
 
 import org.junit.Test;
+
+import com.oracle.graal.options.*;
+import com.oracle.graal.options.OptionValue.*;
+import com.oracle.graal.phases.*;
+
+import static com.oracle.graal.phases.GraalOptions.*;
 import static org.junit.Assume.*;
 
 /**
@@ -50,8 +56,9 @@
     @Test
     @Override
     public void test() {
-        assumeTrue(aggressiveInliningEnabled() || canHandleHSAILMethodCalls());
-        testGeneratedHsail();
+        try (OverrideScope s = OptionValue.override(InlineEverything, true, getOptionFromField(GraalOptions.class, "RemoveNeverExecutedCode"), false)) {
+            assumeTrue(aggressiveInliningEnabled() || canHandleHSAILMethodCalls());
+            testGeneratedHsail();
+        }
     }
-
 }
--- a/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.ptx/src/com/oracle/graal/compiler/ptx/PTXLIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -414,7 +414,7 @@
 
     private void emitIntegerTest(Value a, Value b) {
 
-        assert a.getKind().getStackKind() == Kind.Int || a.getKind() == Kind.Long;
+        assert a.getKind().isNumericInteger();
 
         if (LIRValueUtil.isVariable(b)) {
             append(new PTXTestOp(load(b), loadNonConst(a), nextPredRegNum));
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -267,7 +267,7 @@
     }
 
     private void emitIntegerTest(Value a, Value b) {
-        assert a.getKind().getStackKind() == Kind.Int || a.getKind() == Kind.Long;
+        assert a.getKind().isNumericInteger();
         if (LIRValueUtil.isVariable(b)) {
             append(new SPARCTestOp(load(b), loadNonConst(a)));
         } else {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Sat Nov 09 21:34:07 2013 +0100
@@ -209,12 +209,15 @@
 
         HighTierContext highTierContext = new HighTierContext(providers, assumptions, cache, plan, optimisticOpts);
         suites.getHighTier().apply(graph, highTierContext);
+        graph.maybeCompress();
 
         MidTierContext midTierContext = new MidTierContext(providers, assumptions, target, optimisticOpts);
         suites.getMidTier().apply(graph, midTierContext);
+        graph.maybeCompress();
 
         LowTierContext lowTierContext = new LowTierContext(providers, assumptions, target);
         suites.getLowTier().apply(graph, lowTierContext);
+        graph.maybeCompress();
 
         // we do not want to store statistics about OSR compilations because it may prevent inlining
         if (!graph.isOSR()) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java	Sat Nov 09 21:34:07 2013 +0100
@@ -54,6 +54,8 @@
     public static final OptionValue<String> DebugValueSummary = new OptionValue<>("Name");
     @Option(help = "Omit reporting 0-value metrics")
     public static final OptionValue<Boolean> SuppressZeroDebugValues = new OptionValue<>(false);
+    @Option(help = "Report and reset metrics after bootstrapping")
+    public static final OptionValue<Boolean> ResetDebugValuesAfterBootstrap = new OptionValue<>(true);
     @Option(help = "Send Graal IR to dump handlers on error")
     public static final OptionValue<Boolean> DumpOnError = new OptionValue<>(false);
     @Option(help = "Enable expensive assertions")
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/IntervalWalker.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/IntervalWalker.java	Sat Nov 09 21:34:07 2013 +0100
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.compiler.alloc;
 
-import static com.oracle.graal.phases.GraalOptions.*;
-
 import com.oracle.graal.compiler.alloc.Interval.RegisterBinding;
 import com.oracle.graal.compiler.alloc.Interval.RegisterBindingLists;
 import com.oracle.graal.compiler.alloc.Interval.State;
@@ -203,13 +201,17 @@
         currentInterval.rewindRange();
     }
 
+    int getTraceLevel() {
+        return allocator.getTraceLevel();
+    }
+
     void walkTo(int toOpId) {
         assert currentPosition <= toOpId : "can not walk backwards";
         while (currentInterval != null) {
             boolean isActive = currentInterval.from() <= toOpId;
             int opId = isActive ? currentInterval.from() : toOpId;
 
-            if (TraceLinearScanLevel.getValue() >= 2 && !TTY.isSuppressed()) {
+            if (getTraceLevel() >= 2 && !TTY.isSuppressed()) {
                 if (currentPosition < opId) {
                     TTY.println();
                     TTY.println("walkTo(%d) *", opId);
@@ -240,7 +242,7 @@
     private void intervalMoved(Interval interval, State from, State to) {
         // intervalMoved() is called whenever an interval moves from one interval list to another.
         // In the implementation of this method it is prohibited to move the interval to any list.
-        if (TraceLinearScanLevel.getValue() >= 4 && !TTY.isSuppressed()) {
+        if (getTraceLevel() >= 4 && !TTY.isSuppressed()) {
             TTY.print(from.toString() + " to " + to.toString());
             TTY.fillTo(23);
             TTY.out().println(interval.logString(allocator));
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Sat Nov 09 21:34:07 2013 +0100
@@ -26,7 +26,6 @@
 import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.compiler.GraalDebugConfig.*;
 import static com.oracle.graal.lir.LIRValueUtil.*;
-import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.util.*;
 
@@ -46,6 +45,7 @@
 import com.oracle.graal.lir.LIRInstruction.ValueProcedure;
 import com.oracle.graal.lir.StandardOp.MoveOp;
 import com.oracle.graal.nodes.cfg.*;
+import com.oracle.graal.options.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -56,6 +56,13 @@
  */
 public final class LinearScan {
 
+    static class Options {
+        // @formatter:off
+        @Option(help = "The trace level for the linear scan register allocator")
+        public static final OptionValue<Integer> TraceLinearScanLevel = new OptionValue<>(0);
+        // @formatter:on
+    }
+
     final TargetDescription target;
     final LIR ir;
     final LIRGenerator gen;
@@ -158,6 +165,8 @@
      */
     private final int firstVariableNumber;
 
+    private final int traceLevel;
+
     public LinearScan(TargetDescription target, LIR ir, LIRGenerator gen, FrameMap frameMap) {
         this.target = target;
         this.ir = ir;
@@ -170,6 +179,11 @@
         this.firstVariableNumber = registers.length;
         this.variables = new ArrayList<>(ir.numVariables() * 3 / 2);
         this.blockData = new BlockMap<>(ir.cfg);
+        traceLevel = Options.TraceLinearScanLevel.getValue();
+    }
+
+    int getTraceLevel() {
+        return traceLevel;
     }
 
     public int getFirstLirInstructionId(Block block) {
@@ -504,7 +518,7 @@
 
     // called once before assignment of register numbers
     void eliminateSpillMoves() {
-        if (TraceLinearScanLevel.getValue() >= 3) {
+        if (getTraceLevel() >= 3) {
             TTY.println(" Eliminating unnecessary spill moves");
         }
 
@@ -538,7 +552,7 @@
                     if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) {
                         // move target is a stack slot that is always correct, so eliminate
                         // instruction
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (getTraceLevel() >= 4) {
                             TTY.println("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
                         }
                         instructions.set(j, null); // null-instructions are deleted by assignRegNum
@@ -564,7 +578,7 @@
 
                         insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation));
 
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (getTraceLevel() >= 4) {
                             StackSlot slot = interval.spillSlot();
                             TTY.println("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, slot, opId);
                         }
@@ -582,7 +596,7 @@
         assert interval == Interval.EndMarker : "missed an interval";
     }
 
-    private static void checkIntervals(Interval interval) {
+    private void checkIntervals(Interval interval) {
         Interval prev = null;
         Interval temp = interval;
         while (temp != Interval.EndMarker) {
@@ -596,7 +610,7 @@
             assert temp.spillDefinitionPos() >= temp.from() : "invalid order";
             assert temp.spillDefinitionPos() <= temp.from() + 2 : "only intervals defined once at their start-pos can be optimized";
 
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (traceLevel >= 4) {
                 TTY.println("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
             }
 
@@ -698,7 +712,7 @@
                             int operandNum = operandNumber(operand);
                             if (!liveKill.get(operandNum)) {
                                 liveGen.set(operandNum);
-                                if (TraceLinearScanLevel.getValue() >= 4) {
+                                if (getTraceLevel() >= 4) {
                                     TTY.println("  Setting liveGen for operand %d at instruction %d", operandNum, op.id());
                                 }
                             }
@@ -720,7 +734,7 @@
                         int operandNum = operandNumber(operand);
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
-                            if (TraceLinearScanLevel.getValue() >= 4) {
+                            if (getTraceLevel() >= 4) {
                                 TTY.println("  Setting liveGen for LIR opId %d, operand %d because of state for %s", op.id(), operandNum, op);
                             }
                         }
@@ -763,7 +777,7 @@
             blockData.get(block).liveIn = new BitSet(liveSize);
             blockData.get(block).liveOut = new BitSet(liveSize);
 
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("liveGen  B%d %s", block.getId(), blockData.get(block).liveGen);
                 TTY.println("liveKill B%d %s", block.getId(), blockData.get(block).liveKill);
             }
@@ -852,7 +866,7 @@
                     liveIn.or(blockData.get(block).liveGen);
                 }
 
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (getTraceLevel() >= 4) {
                     traceLiveness(changeOccurredInBlock, iterationCount, block);
                 }
             }
@@ -974,7 +988,7 @@
         if (!isProcessed(operand)) {
             return;
         }
-        if (TraceLinearScanLevel.getValue() >= 2 && kind == null) {
+        if (getTraceLevel() >= 2 && kind == null) {
             TTY.println(" use %s from %d to %d (%s)", operand, from, to, registerPriority.name());
         }
 
@@ -993,7 +1007,7 @@
         if (!isProcessed(operand)) {
             return;
         }
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" temp %s tempPos %d (%s)", operand, tempPos, RegisterPriority.MustHaveRegister.name());
         }
 
@@ -1014,7 +1028,7 @@
         if (!isProcessed(operand)) {
             return;
         }
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" def %s defPos %d (%s)", operand, defPos, registerPriority.name());
         }
 
@@ -1035,7 +1049,7 @@
             // also add register priority for dead intervals
             interval.addRange(defPos, defPos + 1);
             interval.addUsePos(defPos, registerPriority);
-            if (TraceLinearScanLevel.getValue() >= 2) {
+            if (getTraceLevel() >= 2) {
                 TTY.println("Warning: def of operand %s at %d occurs without use", operand, defPos);
             }
         }
@@ -1096,7 +1110,7 @@
                     assert blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block";
                     assert isVariable(move.getResult()) : "result of move must be a variable";
 
-                    if (TraceLinearScanLevel.getValue() >= 4) {
+                    if (getTraceLevel() >= 4) {
                         TTY.println("found move from stack slot %s to %s", slot, move.getResult());
                     }
                 }
@@ -1126,7 +1140,7 @@
                             from.setLocationHint(to);
                         }
 
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (getTraceLevel() >= 4) {
                             TTY.println("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber);
                         }
                         return registerHint;
@@ -1159,7 +1173,7 @@
             for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) {
                 assert live.get(operandNum) : "should not stop here otherwise";
                 AllocatableValue operand = operandFor(operandNum);
-                if (TraceLinearScanLevel.getValue() >= 2) {
+                if (getTraceLevel() >= 2) {
                     TTY.println("live in %s to %d", operand, blockTo + 2);
                 }
 
@@ -1187,7 +1201,7 @@
                             addTemp(r.asValue(), opId, RegisterPriority.None, Kind.Illegal);
                         }
                     }
-                    if (TraceLinearScanLevel.getValue() >= 4) {
+                    if (getTraceLevel() >= 4) {
                         TTY.println("operation destroys all caller-save registers");
                     }
                 }
@@ -1438,7 +1452,7 @@
         Interval result = interval.getSplitChildAtOpId(opId, mode, this);
 
         if (result != null) {
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("Split child at pos " + opId + " of interval " + interval.toString() + " is " + result.toString());
             }
             return result;
@@ -1492,7 +1506,7 @@
 
     void resolveFindInsertPos(Block fromBlock, Block toBlock, MoveResolver moveResolver) {
         if (fromBlock.getSuccessorCount() <= 1) {
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("inserting moves at end of fromBlock B%d", fromBlock.getId());
             }
 
@@ -1506,7 +1520,7 @@
             }
 
         } else {
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("inserting moves at beginning of toBlock B%d", toBlock.getId());
             }
 
@@ -1551,7 +1565,7 @@
 
                     // prevent optimization of two consecutive blocks
                     if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) {
-                        if (TraceLinearScanLevel.getValue() >= 3) {
+                        if (getTraceLevel() >= 3) {
                             TTY.println(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId());
                         }
                         blockCompleted.set(block.getLinearScanNumber());
@@ -1578,7 +1592,7 @@
                     // check for duplicate edges between the same blocks (can happen with switch
                     // blocks)
                     if (!alreadyResolved.get(toBlock.getLinearScanNumber())) {
-                        if (TraceLinearScanLevel.getValue() >= 3) {
+                        if (getTraceLevel() >= 3) {
                             TTY.println(" processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId());
                         }
                         alreadyResolved.set(toBlock.getLinearScanNumber());
@@ -1657,7 +1671,7 @@
     }
 
     void computeOopMap(IntervalWalker iw, LIRInstruction op, BitSet registerRefMap, BitSet frameRefMap) {
-        if (TraceLinearScanLevel.getValue() >= 3) {
+        if (getTraceLevel() >= 3) {
             TTY.println("creating oop map at opId %d", op.id());
         }
 
@@ -1866,7 +1880,7 @@
     }
 
     void printIntervals(String label) {
-        if (TraceLinearScanLevel.getValue() >= 1) {
+        if (getTraceLevel() >= 1) {
             int i;
             TTY.println();
             TTY.println(label);
@@ -1896,27 +1910,27 @@
 
     boolean verify() {
         // (check that all intervals have a correct register and that no registers are overwritten)
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" verifying intervals *");
         }
         verifyIntervals();
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" verifying that no oops are in fixed intervals *");
         }
         // verifyNoOopsInFixedIntervals();
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" verifying that unpinned constants are not alive across block boundaries");
         }
         verifyConstants();
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" verifying register allocation *");
         }
         verifyRegisters();
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println(" no errors found *");
         }
 
@@ -2068,7 +2082,7 @@
 
             // visit all operands where the liveAtEdge bit is set
             for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) {
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (getTraceLevel() >= 4) {
                     TTY.println("checking interval %d of block B%d", operandNum, block.getId());
                 }
                 Value operand = operandFor(operandNum);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Sat Nov 09 21:34:07 2013 +0100
@@ -25,8 +25,6 @@
 import static com.oracle.graal.api.code.CodeUtil.*;
 import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.lir.LIRValueUtil.*;
-import static com.oracle.graal.phases.GraalOptions.*;
-
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -295,7 +293,7 @@
         int optimalSplitPos = -1;
         if (minSplitPos == maxSplitPos) {
             // trivial case, no optimization of split position possible
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("      min-pos and max-pos are equal, no optimization possible");
             }
             optimalSplitPos = minSplitPos;
@@ -319,7 +317,7 @@
             assert minBlock.getLinearScanNumber() <= maxBlock.getLinearScanNumber() : "invalid order";
             if (minBlock == maxBlock) {
                 // split position cannot be moved to block boundary : so split as late as possible
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (getTraceLevel() >= 4) {
                     TTY.println("      cannot move split pos to block boundary because minPos and maxPos are in same block");
                 }
                 optimalSplitPos = maxSplitPos;
@@ -331,14 +329,14 @@
                     // as mustHaveRegister) with a hole before each definition. When the register is
                     // needed
                     // for the second definition : an earlier reloading is unnecessary.
-                    if (TraceLinearScanLevel.getValue() >= 4) {
+                    if (getTraceLevel() >= 4) {
                         TTY.println("      interval has hole just before maxSplitPos, so splitting at maxSplitPos");
                     }
                     optimalSplitPos = maxSplitPos;
 
                 } else {
                     // seach optimal block boundary between minSplitPos and maxSplitPos
-                    if (TraceLinearScanLevel.getValue() >= 4) {
+                    if (getTraceLevel() >= 4) {
                         TTY.println("      moving split pos to optimal block boundary between block B%d and B%d", minBlock.getId(), maxBlock.getId());
                     }
 
@@ -347,7 +345,7 @@
                         // max-position :
                         // then split before this loop
                         int loopEndPos = interval.nextUsageExact(RegisterPriority.LiveAtLoopEnd, allocator.getLastLirInstructionId(minBlock) + 2);
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (getTraceLevel() >= 4) {
                             TTY.println("      loop optimization: loop end found at pos %d", loopEndPos);
                         }
 
@@ -362,7 +360,7 @@
                             // of the interval (normally, only mustHaveRegister causes a reloading)
                             Block loopBlock = allocator.blockForId(loopEndPos);
 
-                            if (TraceLinearScanLevel.getValue() >= 4) {
+                            if (getTraceLevel() >= 4) {
                                 TTY.println("      interval is used in loop that ends in block B%d, so trying to move maxBlock back from B%d to B%d", loopBlock.getId(), maxBlock.getId(),
                                                 loopBlock.getId());
                             }
@@ -371,11 +369,11 @@
                             optimalSplitPos = findOptimalSplitPos(minBlock, loopBlock, allocator.getLastLirInstructionId(loopBlock) + 2);
                             if (optimalSplitPos == allocator.getLastLirInstructionId(loopBlock) + 2) {
                                 optimalSplitPos = -1;
-                                if (TraceLinearScanLevel.getValue() >= 4) {
+                                if (getTraceLevel() >= 4) {
                                     TTY.println("      loop optimization not necessary");
                                 }
                             } else {
-                                if (TraceLinearScanLevel.getValue() >= 4) {
+                                if (getTraceLevel() >= 4) {
                                     TTY.println("      loop optimization successful");
                                 }
                             }
@@ -389,7 +387,7 @@
                 }
             }
         }
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("      optimal split position: %d", optimalSplitPos);
         }
 
@@ -401,13 +399,13 @@
     // 1) the left part has already a location assigned
     // 2) the right part is sorted into to the unhandled-list
     void splitBeforeUsage(Interval interval, int minSplitPos, int maxSplitPos) {
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("----- splitting interval: ");
         }
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println(interval.logString(allocator));
         }
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("      between %d and %d", minSplitPos, maxSplitPos);
         }
 
@@ -425,7 +423,7 @@
         if (optimalSplitPos == interval.to() && interval.nextUsage(RegisterPriority.MustHaveRegister, minSplitPos) == Integer.MAX_VALUE) {
             // the split position would be just before the end of the interval
             // . no split at all necessary
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("      no split necessary because optimal split position is at end of interval");
             }
             return;
@@ -440,7 +438,7 @@
             optimalSplitPos = (optimalSplitPos - 1) | 1;
         }
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("      splitting at position %d", optimalSplitPos);
         }
         assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary";
@@ -453,10 +451,10 @@
         assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position";
         unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart);
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("      split interval in two parts (insertMoveWhenActivated: %b)", moveNecessary);
         }
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.print("      ");
             TTY.println(interval.logString(allocator));
             TTY.print("      ");
@@ -474,7 +472,7 @@
         int maxSplitPos = currentPosition;
         int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos) + 1, interval.from());
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.print("----- splitting and spilling interval: ");
             TTY.println(interval.logString(allocator));
             TTY.println("      between %d and %d", minSplitPos, maxSplitPos);
@@ -488,7 +486,7 @@
 
         if (minSplitPos == interval.from()) {
             // the whole interval is never used, so spill it entirely to memory
-            if (TraceLinearScanLevel.getValue() >= 2) {
+            if (getTraceLevel() >= 2) {
                 TTY.println("      spilling entire interval because split pos is at beginning of interval");
                 TTY.println("      use positions: " + interval.usePosList().size());
             }
@@ -507,7 +505,7 @@
                 if (isRegister(parent.location())) {
                     if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) {
                         // parent is never used, so kick it out of its assigned register
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (getTraceLevel() >= 4) {
                             TTY.println("      kicking out interval %d out of its register because it is never used", parent.operandNumber);
                         }
                         allocator.assignSpillSlot(parent);
@@ -532,7 +530,7 @@
                 optimalSplitPos = (optimalSplitPos - 1) | 1;
             }
 
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("      splitting at position %d", optimalSplitPos);
             }
             assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary";
@@ -543,7 +541,7 @@
             allocator.changeSpillState(spilledPart, optimalSplitPos);
 
             if (!allocator.isBlockBegin(optimalSplitPos)) {
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (getTraceLevel() >= 4) {
                     TTY.println("      inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber);
                 }
                 insertMove(optimalSplitPos, interval, spilledPart);
@@ -553,7 +551,7 @@
             assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild";
             spilledPart.makeCurrentSplitChild();
 
-            if (TraceLinearScanLevel.getValue() >= 2) {
+            if (getTraceLevel() >= 2) {
                 TTY.println("      split interval in two parts");
                 TTY.print("      ");
                 TTY.println(interval.logString(allocator));
@@ -602,7 +600,7 @@
     }
 
     boolean allocFreeRegister(Interval interval) {
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("trying to find free register for " + interval.logString(allocator));
         }
 
@@ -618,7 +616,7 @@
         // (either as a fixed register or a normal allocated register in the past)
         // only intervals overlapping with cur are processed, non-overlapping invervals can be
         // ignored safely
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("      state of registers:");
             for (Register register : availableRegs) {
                 int i = register.number;
@@ -630,7 +628,7 @@
         Interval locationHint = interval.locationHint(true);
         if (locationHint != null && locationHint.location() != null && isRegister(locationHint.location())) {
             hint = asRegister(locationHint.location());
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("      hint register %d from interval %s", hint.number, locationHint.logString(allocator));
             }
         }
@@ -674,7 +672,7 @@
 
         splitPos = usePos[reg.number];
         interval.assignLocation(reg.asValue(interval.kind()));
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("selected register %d", reg.number);
         }
 
@@ -700,7 +698,7 @@
 
     // Split an Interval and spill it to memory so that cur can be placed in a register
     void allocLockedRegister(Interval interval) {
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("need to split and spill to get register for " + interval.logString(allocator));
         }
 
@@ -713,7 +711,7 @@
         spillCollectActiveAny();
         spillCollectInactiveAny(interval);
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("      state of registers:");
             for (Register reg : availableRegs) {
                 int i = reg.number;
@@ -746,7 +744,7 @@
 
         if (reg == null || usePos[reg.number] <= firstUsage) {
             // the first use of cur is later than the spilling position -> spill cur
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("able to spill current interval. firstUsage(register): %d, usePos: %d", firstUsage, reg == null ? 0 : usePos[reg.number]);
             }
 
@@ -765,7 +763,7 @@
 
         int splitPos = blockPos[reg.number];
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("decided to use register %d", reg.number);
         }
         assert splitPos > 0 : "invalid splitPos";
@@ -793,7 +791,7 @@
             if (isOdd(pos)) {
                 // the current instruction is a call that blocks all registers
                 if (pos < allocator.maxOpId() && allocator.hasCall(pos + 1) && interval.to() > pos + 1) {
-                    if (TraceLinearScanLevel.getValue() >= 4) {
+                    if (getTraceLevel() >= 4) {
                         TTY.println("      free register cannot be available because all registers blocked by following call");
                     }
 
@@ -885,11 +883,11 @@
         Interval interval = currentInterval;
         boolean result = true;
 
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (getTraceLevel() >= 2) {
             TTY.println("+++++ activating interval " + interval.logString(allocator));
         }
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (getTraceLevel() >= 4) {
             TTY.println("      splitParent: %s, insertMoveWhenActivated: %b", interval.splitParent().operandNumber, interval.insertMoveWhenActivated());
         }
 
@@ -898,7 +896,7 @@
             // activating an interval that has a stack slot assigned . split it at first use
             // position
             // used for method parameters
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("      interval has spill slot assigned (method parameter) . split it before first use");
             }
             splitStackInterval(interval);
@@ -908,7 +906,7 @@
             if (interval.location() == null) {
                 // interval has not assigned register . normal allocation
                 // (this is the normal case for most intervals)
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (getTraceLevel() >= 4) {
                     TTY.println("      normal allocation of register");
                 }
 
@@ -934,7 +932,7 @@
             assert interval.isSplitChild();
             assert interval.currentSplitChild() != null;
             assert !interval.currentSplitChild().operand.equals(operand) : "cannot insert move between same interval";
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (getTraceLevel() >= 4) {
                 TTY.println("Inserting move from interval %d to %d because insertMoveWhenActivated is set", interval.currentSplitChild().operandNumber, interval.operandNumber);
             }
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,8 +23,6 @@
 package com.oracle.graal.compiler.alloc;
 
 import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.phases.GraalOptions.*;
-
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -201,7 +199,7 @@
 
         insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr));
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (allocator.getTraceLevel() >= 4) {
             TTY.println("MoveResolver: inserted move from %d (%s) to %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location());
         }
     }
@@ -213,7 +211,7 @@
         AllocatableValue toOpr = toInterval.operand;
         insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr));
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (allocator.getTraceLevel() >= 4) {
             TTY.print("MoveResolver: inserted move from constant %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location());
         }
     }
@@ -285,7 +283,7 @@
                 }
                 spillInterval.assignLocation(spillSlot);
 
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (allocator.getTraceLevel() >= 4) {
                     TTY.println("created new Interval %s for spilling", spillInterval.operand);
                 }
 
@@ -327,7 +325,7 @@
     }
 
     void addMapping(Interval fromInterval, Interval toInterval) {
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (allocator.getTraceLevel() >= 4) {
             TTY.println("MoveResolver: adding mapping from interval %d (%s) to interval %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location());
         }
 
@@ -339,7 +337,7 @@
     }
 
     void addMapping(Value fromOpr, Interval toInterval) {
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (allocator.getTraceLevel() >= 4) {
             TTY.println("MoveResolver: adding mapping from %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location());
         }
         assert isConstant(fromOpr) : "only for constants";
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/RegisterVerifier.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/RegisterVerifier.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,8 +23,6 @@
 package com.oracle.graal.compiler.alloc;
 
 import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.phases.GraalOptions.*;
-
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -92,7 +90,7 @@
     }
 
     private void processBlock(Block block) {
-        if (TraceLinearScanLevel.getValue() >= 2) {
+        if (allocator.getTraceLevel() >= 2) {
             TTY.println();
             TTY.println("processBlock B%d", block.getId());
         }
@@ -100,7 +98,7 @@
         // must copy state because it is modified
         Interval[] inputState = copy(stateForBlock(block));
 
-        if (TraceLinearScanLevel.getValue() >= 4) {
+        if (allocator.getTraceLevel() >= 4) {
             TTY.println("Input-State of intervals:");
             TTY.print("    ");
             for (int i = 0; i < stateSize(); i++) {
@@ -142,7 +140,7 @@
                         savedStateCorrect = false;
                         savedState[i] = null;
 
-                        if (TraceLinearScanLevel.getValue() >= 4) {
+                        if (allocator.getTraceLevel() >= 4) {
                             TTY.println("processSuccessor B%d: invalidating slot %d", block.getId(), i);
                         }
                     }
@@ -151,12 +149,12 @@
 
             if (savedStateCorrect) {
                 // already processed block with correct inputState
-                if (TraceLinearScanLevel.getValue() >= 2) {
+                if (allocator.getTraceLevel() >= 2) {
                     TTY.println("processSuccessor B%d: previous visit already correct", block.getId());
                 }
             } else {
                 // must re-visit this block
-                if (TraceLinearScanLevel.getValue() >= 2) {
+                if (allocator.getTraceLevel() >= 2) {
                     TTY.println("processSuccessor B%d: must re-visit because input state changed", block.getId());
                 }
                 addToWorkList(block);
@@ -164,7 +162,7 @@
 
         } else {
             // block was not processed before, so set initial inputState
-            if (TraceLinearScanLevel.getValue() >= 2) {
+            if (allocator.getTraceLevel() >= 2) {
                 TTY.println("processSuccessor B%d: initial visit", block.getId());
             }
 
@@ -177,16 +175,16 @@
         return inputState.clone();
     }
 
-    static void statePut(Interval[] inputState, Value location, Interval interval) {
+    static void statePut(Interval[] inputState, Value location, Interval interval, int traceLevel) {
         if (location != null && isRegister(location)) {
             Register reg = asRegister(location);
             int regNum = reg.number;
             if (interval != null) {
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (traceLevel >= 4) {
                     TTY.println("        %s = %s", reg, interval.operand);
                 }
             } else if (inputState[regNum] != null) {
-                if (TraceLinearScanLevel.getValue() >= 4) {
+                if (traceLevel >= 4) {
                     TTY.println("        %s = null", reg);
                 }
             }
@@ -209,7 +207,7 @@
         for (int i = 0; i < ops.size(); i++) {
             final LIRInstruction op = ops.get(i);
 
-            if (TraceLinearScanLevel.getValue() >= 4) {
+            if (allocator.getTraceLevel() >= 4) {
                 TTY.println(op.toStringWithIdPrefix());
             }
 
@@ -239,7 +237,7 @@
                             interval = interval.getSplitChildAtOpId(op.id(), mode, allocator);
                         }
 
-                        statePut(inputState, interval.location(), interval.splitParent());
+                        statePut(inputState, interval.location(), interval.splitParent(), allocator.getTraceLevel());
                     }
                     return operand;
                 }
@@ -250,7 +248,7 @@
             // invalidate all caller save registers at calls
             if (op.destroysCallerSavedRegisters()) {
                 for (Register r : allocator.frameMap.registerConfig.getCallerSaveRegisters()) {
-                    statePut(inputState, r.asValue(), null);
+                    statePut(inputState, r.asValue(), null, allocator.getTraceLevel());
                 }
             }
             op.forEachAlive(useProc);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -39,6 +39,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.FallThroughOp;
 import com.oracle.graal.lir.StandardOp.JumpOp;
 import com.oracle.graal.lir.StandardOp.LabelOp;
 import com.oracle.graal.nodes.*;
@@ -49,6 +50,7 @@
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.virtual.*;
+import com.oracle.graal.options.*;
 import com.oracle.graal.phases.util.*;
 
 /**
@@ -56,6 +58,15 @@
  */
 public abstract class LIRGenerator implements LIRGeneratorTool {
 
+    public static class Options {
+        // @formatter:off
+        @Option(help = "Print HIR along side LIR as the latter is generated")
+        public static final OptionValue<Boolean> PrintIRWithLIR = new OptionValue<>(false);
+        @Option(help = "The trace level for the LIR generator")
+        public static final OptionValue<Integer> TraceLIRGeneratorLevel = new OptionValue<>(0);
+        // @formatter:on
+    }
+
     public final FrameMap frameMap;
     public final NodeMap<Value> nodeOperands;
     public final LIR lir;
@@ -67,6 +78,8 @@
     protected final DebugInfoBuilder debugInfoBuilder;
 
     protected Block currentBlock;
+    private final int traceLevel;
+    private final boolean printIRWithLIR;
 
     /**
      * Maps constants the variables within the scope of a single block to avoid loading a constant
@@ -106,6 +119,8 @@
         this.nodeOperands = graph.createNodeMap();
         this.lir = lir;
         this.debugInfoBuilder = createDebugInfoBuilder(nodeOperands);
+        this.traceLevel = Options.TraceLIRGeneratorLevel.getValue();
+        this.printIRWithLIR = Options.PrintIRWithLIR.getValue();
     }
 
     @SuppressWarnings("hiding")
@@ -316,7 +331,7 @@
     }
 
     public void append(LIRInstruction op) {
-        if (PrintIRWithLIR.getValue() && !TTY.isSuppressed()) {
+        if (printIRWithLIR && !TTY.isSuppressed()) {
             if (currentInstruction != null && lastInstructionPrinted != currentInstruction) {
                 lastInstructionPrinted = currentInstruction;
                 InstructionPrinter ip = new InstructionPrinter(TTY.out());
@@ -330,7 +345,7 @@
     }
 
     public void doBlock(Block block) {
-        if (PrintIRWithLIR.getValue()) {
+        if (printIRWithLIR) {
             TTY.print(block.toString());
         }
 
@@ -343,7 +358,7 @@
 
         append(new LabelOp(new Label(block.getId()), block.isAligned()));
 
-        if (TraceLIRGeneratorLevel.getValue() >= 1) {
+        if (traceLevel >= 1) {
             TTY.println("BEGIN Generating LIR for block B" + block.getId());
         }
 
@@ -357,7 +372,7 @@
         List<ScheduledNode> nodes = lir.nodesFor(block);
         for (int i = 0; i < nodes.size(); i++) {
             Node instr = nodes.get(i);
-            if (TraceLIRGeneratorLevel.getValue() >= 3) {
+            if (traceLevel >= 3) {
                 TTY.println("LIRGen for " + instr);
             }
             if (!ConstantNodeRecordsUsages && instr instanceof ConstantNode) {
@@ -387,13 +402,13 @@
             emitJump(getLIRBlock((FixedNode) successors.first()));
         }
 
-        if (TraceLIRGeneratorLevel.getValue() >= 1) {
+        if (traceLevel >= 1) {
             TTY.println("END Generating LIR for block B" + block.getId());
         }
 
         currentBlock = null;
 
-        if (PrintIRWithLIR.getValue()) {
+        if (printIRWithLIR) {
             TTY.println();
         }
     }
@@ -412,11 +427,16 @@
             return false;
         }
         LIRInstruction lirInstruction = instructions.get(instructions.size() - 1);
-        return lirInstruction instanceof StandardOp.JumpOp;
+        if (lirInstruction instanceof StandardOp.JumpOp) {
+            return true;
+        } else if (lirInstruction instanceof FallThroughOp) {
+            return ((FallThroughOp) lirInstruction).fallThroughTarget() != null;
+        }
+        return false;
     }
 
     private void doRoot(ValueNode instr) {
-        if (TraceLIRGeneratorLevel.getValue() >= 2) {
+        if (traceLevel >= 2) {
             TTY.println("Emitting LIR for instruction " + instr);
         }
         currentInstruction = instr;
@@ -497,7 +517,7 @@
     }
 
     private void moveToPhi(MergeNode merge, AbstractEndNode pred) {
-        if (TraceLIRGeneratorLevel.getValue() >= 1) {
+        if (traceLevel >= 1) {
             TTY.println("MOVE TO PHI from " + pred + " to " + merge);
         }
         PhiResolver resolver = new PhiResolver(this);
@@ -753,7 +773,7 @@
         }
     }
 
-    private void emitSequentialSwitch(final SwitchNode x, Variable key, LabelRef defaultTarget) {
+    protected void emitSequentialSwitch(final SwitchNode x, Variable key, LabelRef defaultTarget) {
         int keyCount = x.keyCount();
         Integer[] indexes = Util.createSortedPermutation(keyCount, new Comparator<Integer>() {
 
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/internal/DebugValueMap.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/internal/DebugValueMap.java	Sat Nov 09 21:34:07 2013 +0100
@@ -55,6 +55,17 @@
         }
     }
 
+    public void reset() {
+        if (values != null) {
+            Arrays.fill(values, 0L);
+        }
+        if (children != null) {
+            for (DebugValueMap child : children) {
+                child.reset();
+            }
+        }
+    }
+
     private void ensureSize(int index) {
         if (values == null) {
             values = new long[index + 1];
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Sat Nov 09 21:34:07 2013 +0100
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, 2011, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2011, 2013, 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
@@ -24,6 +24,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.GraphEvent.NodeEvent;
 import com.oracle.graal.graph.Node.ValueNumberable;
 import com.oracle.graal.graph.iterators.*;
@@ -37,7 +38,15 @@
 
     private static final boolean TIME_TRAVEL = false;
 
-    private final ArrayList<Node> nodes;
+    /**
+     * The set of nodes in the graph, ordered by {@linkplain #register(Node) registration} time.
+     */
+    private Node[] nodes;
+
+    /**
+     * The number of valid entries in {@link #nodes}.
+     */
+    private int nodesSize;
 
     /**
      * Records the modification count for nodes. This is only used in assertions.
@@ -53,9 +62,15 @@
     // they contain the first and last pointer to a linked list of all nodes with this type.
     private final ArrayList<Node> nodeCacheFirst;
     private final ArrayList<Node> nodeCacheLast;
-    private int deletedNodeCount;
+    private int nodesDeletedSinceLastCompression;
+    private int nodesDeletedBeforeLastCompression;
     private GraphEventLog eventLog;
 
+    /**
+     * The number of times this graph has been compressed.
+     */
+    int compressions;
+
     NodeChangedListener inputChangedListener;
     NodeChangedListener usagesDroppedToZeroListener;
     private final HashMap<CacheEntry, Node> cachedNodes = new HashMap<>();
@@ -110,19 +125,21 @@
         return enabled;
     }
 
+    private static final int INITIAL_NODES_SIZE = 32;
+
     /**
      * Creates an empty Graph with a given name.
      * 
      * @param name the name of the graph, used for debugging purposes
      */
     public Graph(String name) {
-        nodes = new ArrayList<>(32);
+        nodes = new Node[INITIAL_NODES_SIZE];
         nodeCacheFirst = new ArrayList<>(NodeClass.cacheSize());
         nodeCacheLast = new ArrayList<>(NodeClass.cacheSize());
         this.name = name;
         if (MODIFICATION_COUNTS_ENABLED) {
-            nodeModCounts = new int[nodes.size()];
-            nodeUsageModCounts = new int[nodes.size()];
+            nodeModCounts = new int[INITIAL_NODES_SIZE];
+            nodeUsageModCounts = new int[INITIAL_NODES_SIZE];
         }
     }
 
@@ -204,16 +221,31 @@
      * @return the number of live nodes in this graph
      */
     public int getNodeCount() {
-        return nodes.size() - getDeletedNodeCount();
+        return nodesSize - getNodesDeletedSinceLastCompression();
+    }
+
+    /**
+     * Gets the number of times this graph has been {@linkplain #maybeCompress() compressed}. Node
+     * identifiers are only stable between compressions. To ensure this constraint is observed, any
+     * entity relying upon stable node identifiers should use {@link NodeIdAccessor}.
+     */
+    public int getCompressions() {
+        return compressions;
     }
 
     /**
-     * Gets the number of node which have been deleted from this graph.
-     * 
-     * @return the number of node which have been deleted from this graph
+     * Gets the number of nodes which have been deleted from this graph since it was last
+     * {@linkplain #maybeCompress() compressed}.
      */
-    public int getDeletedNodeCount() {
-        return deletedNodeCount;
+    public int getNodesDeletedSinceLastCompression() {
+        return nodesDeletedSinceLastCompression;
+    }
+
+    /**
+     * Gets the total number of nodes which have been deleted from this graph.
+     */
+    public int getTotalNodesDeleted() {
+        return nodesDeletedSinceLastCompression + nodesDeletedBeforeLastCompression;
     }
 
     /**
@@ -394,15 +426,56 @@
         }
     }
 
-    public boolean isNew(int mark, Node node) {
-        return node.id >= mark;
+    public boolean isNew(Mark mark, Node node) {
+        return node.id >= mark.getValue();
     }
 
     /**
-     * Gets a mark that can be used with {@link #getNewNodes(int)}.
+     * A snapshot of the {@linkplain Graph#getNodeCount() live node count} in a graph.
      */
-    public int getMark() {
-        return nodeIdCount();
+    public static class Mark extends NodeIdAccessor {
+        private final int value;
+
+        Mark(Graph graph) {
+            super(graph);
+            this.value = graph.nodeIdCount();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Mark) {
+                Mark other = (Mark) obj;
+                return other.getValue() == getValue() && other.getGraph() == getGraph();
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return value ^ (epoch + 11);
+        }
+
+        /**
+         * Determines if this mark is positioned at the first live node in the graph.
+         */
+        public boolean isStart() {
+            return value == 0;
+        }
+
+        /**
+         * Gets the {@linkplain Graph#getNodeCount() live node count} of the associated graph when
+         * this object was created.
+         */
+        int getValue() {
+            return value;
+        }
+    }
+
+    /**
+     * Gets a mark that can be used with {@link #getNewNodes}.
+     */
+    public Mark getMark() {
+        return new Mark(this);
     }
 
     private class NodeIterator implements Iterator<Node> {
@@ -419,22 +492,22 @@
         }
 
         private void forward() {
-            if (index < nodes.size()) {
+            if (index < nodesSize) {
                 do {
                     index++;
-                } while (index < nodes.size() && nodes.get(index) == null);
+                } while (index < nodesSize && nodes[index] == null);
             }
         }
 
         @Override
         public boolean hasNext() {
             checkForDeletedNode();
-            return index < nodes.size();
+            return index < nodesSize;
         }
 
         private void checkForDeletedNode() {
-            if (index < nodes.size()) {
-                while (index < nodes.size() && nodes.get(index) == null) {
+            if (index < nodesSize) {
+                while (index < nodesSize && nodes[index] == null) {
                     index++;
                 }
             }
@@ -443,7 +516,7 @@
         @Override
         public Node next() {
             try {
-                return nodes.get(index);
+                return nodes[index];
             } finally {
                 forward();
             }
@@ -459,8 +532,8 @@
      * Returns an {@link Iterable} providing all nodes added since the last {@link Graph#getMark()
      * mark}.
      */
-    public NodeIterable<Node> getNewNodes(int mark) {
-        final int index = mark;
+    public NodeIterable<Node> getNewNodes(Mark mark) {
+        final int index = mark.getValue();
         return new AbstractNodeIterable<Node>() {
 
             @Override
@@ -493,6 +566,55 @@
     private static final Node PLACE_HOLDER = new Node() {
     };
 
+    /**
+     * When the percent of live nodes in {@link #nodes} fall below this number, a call to
+     * {@link #maybeCompress()} will actually do compression.
+     */
+    public static final int COMPRESSION_THRESHOLD = Integer.getInteger("graal.graphCompressionThreshold", 70);
+
+    private static final DebugMetric GraphCompressions = Debug.metric("GraphCompressions");
+
+    /**
+     * If the {@linkplain #COMPRESSION_THRESHOLD compression threshold} is met, the list of nodes is
+     * compressed such that all non-null entries precede all null entries while preserving the
+     * ordering between the nodes within the list.
+     */
+    public boolean maybeCompress() {
+        if (Debug.isEnabled()) {
+            return false;
+        }
+        int liveNodeCount = getNodeCount();
+        int liveNodePercent = liveNodeCount * 100 / nodesSize;
+        if (COMPRESSION_THRESHOLD == 0 || liveNodePercent >= COMPRESSION_THRESHOLD) {
+            return false;
+        }
+        GraphCompressions.increment();
+        int nextId = 0;
+        for (int i = 0; nextId < liveNodeCount; i++) {
+            Node n = nodes[i];
+            if (n != null) {
+                assert n.id == i;
+                if (i != nextId) {
+                    assert n.id > nextId;
+                    n.id = nextId;
+                    nodes[nextId] = n;
+                    nodes[i] = null;
+                }
+                nextId++;
+            }
+        }
+        if (MODIFICATION_COUNTS_ENABLED) {
+            // This will cause any current iteration to fail with an assertion
+            Arrays.fill(nodeModCounts, 0);
+            Arrays.fill(nodeUsageModCounts, 0);
+        }
+        nodesSize = nextId;
+        compressions++;
+        nodesDeletedBeforeLastCompression += nodesDeletedSinceLastCompression;
+        nodesDeletedSinceLastCompression = 0;
+        return true;
+    }
+
     private class TypedNodeIterator<T extends IterableNodeType> implements Iterator<T> {
 
         private final int[] ids;
@@ -656,8 +778,12 @@
     void register(Node node) {
         assert !node.isExternal();
         assert node.id() == Node.INITIAL_ID;
-        int id = nodes.size();
-        nodes.add(id, node);
+        if (nodes.length == nodesSize) {
+            nodes = Arrays.copyOf(nodes, (nodesSize * 2) + 1);
+        }
+        int id = nodesSize;
+        nodes[id] = node;
+        nodesSize++;
 
         int nodeClassId = node.getNodeClass().iterableId();
         if (nodeClassId != NodeClass.NOT_ITERABLE) {
@@ -704,8 +830,8 @@
     void unregister(Node node) {
         assert !node.isDeleted() : "cannot delete a node twice! node=" + node;
         logNodeDeleted(node);
-        nodes.set(node.id(), null);
-        deletedNodeCount++;
+        nodes[node.id] = null;
+        nodesDeletedSinceLastCompression++;
 
         // nodes aren't removed from the type cache here - they will be removed during iteration
     }
@@ -728,7 +854,7 @@
     }
 
     Node getNode(int i) {
-        return nodes.get(i);
+        return nodes[i];
     }
 
     /**
@@ -737,7 +863,7 @@
      * @return the number of node ids generated so far
      */
     int nodeIdCount() {
-        return nodes.size();
+        return nodesSize;
     }
 
     /**
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Sat Nov 09 21:34:07 2013 +0100
@@ -152,7 +152,7 @@
         this.id = INITIAL_ID;
     }
 
-    protected int id() {
+    int id() {
         return id;
     }
 
@@ -745,12 +745,12 @@
         }
         if (recordsUsages()) {
             for (Node usage : usages()) {
-                assertFalse(usage.isDeleted(), "usage must never be deleted");
+                assertFalse(usage.isDeleted(), "usage %s must never be deleted", usage);
                 assertTrue(usage.inputs().contains(this), "missing input in usage %s", usage);
             }
         }
         if (predecessor != null) {
-            assertFalse(predecessor.isDeleted(), "predecessor must never be deleted");
+            assertFalse(predecessor.isDeleted(), "predecessor %s must never be deleted", predecessor);
             assertTrue(predecessor.successors().contains(this), "missing successor in predecessor %s", predecessor);
         }
         return true;
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Sat Nov 09 21:34:07 2013 +0100
@@ -30,8 +30,8 @@
 
     private final boolean autoGrow;
     private final BitSet bitMap;
-    private final Graph graph;
     private int nodeCount;
+    private final NodeIdAccessor nodeIdAccessor;
 
     public NodeBitMap(Graph graph) {
         this(graph, false);
@@ -42,14 +42,14 @@
     }
 
     private NodeBitMap(Graph graph, boolean autoGrow, int nodeCount, BitSet bits) {
-        this.graph = graph;
+        this.nodeIdAccessor = new NodeIdAccessor(graph);
         this.autoGrow = autoGrow;
         this.nodeCount = nodeCount;
         bitMap = bits;
     }
 
     public Graph graph() {
-        return graph;
+        return nodeIdAccessor.getGraph();
     }
 
     public void setUnion(NodeBitMap other) {
@@ -70,11 +70,11 @@
     }
 
     public boolean isMarked(Node node) {
-        return bitMap.get(node.id());
+        return bitMap.get(nodeIdAccessor.getNodeId(node));
     }
 
     public boolean isNew(Node node) {
-        return node.id() >= nodeCount;
+        return nodeIdAccessor.getNodeId(node) >= nodeCount;
     }
 
     public void mark(Node node) {
@@ -82,7 +82,7 @@
             grow();
         }
         assert check(node);
-        bitMap.set(node.id());
+        bitMap.set(nodeIdAccessor.getNodeId(node));
     }
 
     public void clear(Node node) {
@@ -90,7 +90,7 @@
             return;
         }
         assert check(node);
-        bitMap.clear(node.id());
+        bitMap.clear(nodeIdAccessor.getNodeId(node));
     }
 
     public void clearAll() {
@@ -98,20 +98,20 @@
     }
 
     public void intersect(NodeBitMap other) {
-        assert graph == other.graph;
+        assert graph() == other.graph();
         bitMap.and(other.bitMap);
     }
 
     public void grow(Node node) {
-        nodeCount = Math.max(nodeCount, node.id() + 1);
+        nodeCount = Math.max(nodeCount, nodeIdAccessor.getNodeId(node) + 1);
     }
 
     public void grow() {
-        nodeCount = Math.max(nodeCount, graph.nodeIdCount());
+        nodeCount = Math.max(nodeCount, graph().nodeIdCount());
     }
 
     private boolean check(Node node) {
-        assert node.graph() == graph : "this node is not part of the graph";
+        assert node.graph() == graph() : "this node is not part of the graph";
         assert !isNew(node) : "node was added to the graph after creating the node bitmap: " + node;
         assert node.isAlive() : "node is deleted!";
         return true;
@@ -180,7 +180,7 @@
     }
 
     public NodeBitMap copy() {
-        return new NodeBitMap(graph, autoGrow, nodeCount, (BitSet) bitMap.clone());
+        return new NodeBitMap(graph(), autoGrow, nodeCount, (BitSet) bitMap.clone());
     }
 
     @Override
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Sat Nov 09 21:34:07 2013 +0100
@@ -677,6 +677,32 @@
         }
     }
 
+    private static int deepHashCode0(Object o) {
+        if (o instanceof Object[]) {
+            return Arrays.deepHashCode((Object[]) o);
+        } else if (o instanceof byte[]) {
+            return Arrays.hashCode((byte[]) o);
+        } else if (o instanceof short[]) {
+            return Arrays.hashCode((short[]) o);
+        } else if (o instanceof int[]) {
+            return Arrays.hashCode((int[]) o);
+        } else if (o instanceof long[]) {
+            return Arrays.hashCode((long[]) o);
+        } else if (o instanceof char[]) {
+            return Arrays.hashCode((char[]) o);
+        } else if (o instanceof float[]) {
+            return Arrays.hashCode((float[]) o);
+        } else if (o instanceof double[]) {
+            return Arrays.hashCode((double[]) o);
+        } else if (o instanceof boolean[]) {
+            return Arrays.hashCode((boolean[]) o);
+        } else if (o != null) {
+            return o.hashCode();
+        } else {
+            return 0;
+        }
+    }
+
     public int valueNumber(Node n) {
         int number = 0;
         if (canGVN) {
@@ -695,16 +721,28 @@
                         if (booleanValue) {
                             number += 7;
                         }
+                    } else if (type == Float.TYPE) {
+                        float floatValue = unsafe.getFloat(n, dataOffsets[i]);
+                        number += Float.floatToRawIntBits(floatValue);
+                    } else if (type == Double.TYPE) {
+                        double doubleValue = unsafe.getDouble(n, dataOffsets[i]);
+                        long longValue = Double.doubleToRawLongBits(doubleValue);
+                        number += longValue ^ (longValue >>> 32);
+                    } else if (type == Short.TYPE) {
+                        short shortValue = unsafe.getShort(n, dataOffsets[i]);
+                        number += shortValue;
+                    } else if (type == Character.TYPE) {
+                        char charValue = unsafe.getChar(n, dataOffsets[i]);
+                        number += charValue;
+                    } else if (type == Byte.TYPE) {
+                        byte byteValue = unsafe.getByte(n, dataOffsets[i]);
+                        number += byteValue;
                     } else {
-                        assert false;
+                        assert false : "unhandled property type: " + type;
                     }
                 } else {
                     Object o = unsafe.getObject(n, dataOffsets[i]);
-                    if (o instanceof Object[]) {
-                        number += Arrays.deepHashCode((Object[]) o);
-                    } else if (o != null) {
-                        number += o.hashCode();
-                    }
+                    number += deepHashCode0(o);
                 }
                 number *= 13;
             }
@@ -733,6 +771,12 @@
                     value = unsafe.getFloat(node, dataOffsets[i]);
                 } else if (type == Double.TYPE) {
                     value = unsafe.getDouble(node, dataOffsets[i]);
+                } else if (type == Short.TYPE) {
+                    value = unsafe.getShort(node, dataOffsets[i]);
+                } else if (type == Character.TYPE) {
+                    value = unsafe.getChar(node, dataOffsets[i]);
+                } else if (type == Byte.TYPE) {
+                    value = unsafe.getByte(node, dataOffsets[i]);
                 } else {
                     assert false : "unhandled property type: " + type;
                 }
@@ -743,6 +787,33 @@
         }
     }
 
+    private static boolean deepEquals0(Object e1, Object e2) {
+        assert e1 != null;
+        boolean eq;
+        if (e1 instanceof Object[] && e2 instanceof Object[]) {
+            eq = Arrays.deepEquals((Object[]) e1, (Object[]) e2);
+        } else if (e1 instanceof byte[] && e2 instanceof byte[]) {
+            eq = Arrays.equals((byte[]) e1, (byte[]) e2);
+        } else if (e1 instanceof short[] && e2 instanceof short[]) {
+            eq = Arrays.equals((short[]) e1, (short[]) e2);
+        } else if (e1 instanceof int[] && e2 instanceof int[]) {
+            eq = Arrays.equals((int[]) e1, (int[]) e2);
+        } else if (e1 instanceof long[] && e2 instanceof long[]) {
+            eq = Arrays.equals((long[]) e1, (long[]) e2);
+        } else if (e1 instanceof char[] && e2 instanceof char[]) {
+            eq = Arrays.equals((char[]) e1, (char[]) e2);
+        } else if (e1 instanceof float[] && e2 instanceof float[]) {
+            eq = Arrays.equals((float[]) e1, (float[]) e2);
+        } else if (e1 instanceof double[] && e2 instanceof double[]) {
+            eq = Arrays.equals((double[]) e1, (double[]) e2);
+        } else if (e1 instanceof boolean[] && e2 instanceof boolean[]) {
+            eq = Arrays.equals((boolean[]) e1, (boolean[]) e2);
+        } else {
+            eq = e1.equals(e2);
+        }
+        return eq;
+    }
+
     public boolean valueEqual(Node a, Node b) {
         if (!canGVN || a.getClass() != b.getClass()) {
             return a == b;
@@ -768,12 +839,36 @@
                     if (aLong != bLong) {
                         return false;
                     }
+                } else if (type == Float.TYPE) {
+                    float aFloat = unsafe.getFloat(a, dataOffsets[i]);
+                    float bFloat = unsafe.getFloat(b, dataOffsets[i]);
+                    if (aFloat != bFloat) {
+                        return false;
+                    }
                 } else if (type == Double.TYPE) {
                     double aDouble = unsafe.getDouble(a, dataOffsets[i]);
                     double bDouble = unsafe.getDouble(b, dataOffsets[i]);
                     if (aDouble != bDouble) {
                         return false;
                     }
+                } else if (type == Short.TYPE) {
+                    short aShort = unsafe.getShort(a, dataOffsets[i]);
+                    short bShort = unsafe.getShort(b, dataOffsets[i]);
+                    if (aShort != bShort) {
+                        return false;
+                    }
+                } else if (type == Character.TYPE) {
+                    char aChar = unsafe.getChar(a, dataOffsets[i]);
+                    char bChar = unsafe.getChar(b, dataOffsets[i]);
+                    if (aChar != bChar) {
+                        return false;
+                    }
+                } else if (type == Byte.TYPE) {
+                    byte aByte = unsafe.getByte(a, dataOffsets[i]);
+                    byte bByte = unsafe.getByte(b, dataOffsets[i]);
+                    if (aByte != bByte) {
+                        return false;
+                    }
                 } else {
                     assert false : "unhandled type: " + type;
                 }
@@ -782,14 +877,8 @@
                 Object objectB = unsafe.getObject(b, dataOffsets[i]);
                 if (objectA != objectB) {
                     if (objectA != null && objectB != null) {
-                        if (objectA instanceof Object[] && objectB instanceof Object[]) {
-                            if (!Arrays.deepEquals((Object[]) objectA, (Object[]) objectB)) {
-                                return false;
-                            }
-                        } else {
-                            if (!(objectA.equals(objectB))) {
-                                return false;
-                            }
+                        if (!deepEquals0(objectA, objectB)) {
+                            return false;
                         }
                     } else {
                         return false;
@@ -1271,7 +1360,14 @@
     }
 
     static Map<Node, Node> addGraphDuplicate(final Graph graph, final Graph oldGraph, int estimatedNodeCount, Iterable<Node> nodes, final DuplicationReplacement replacements) {
-        final Map<Node, Node> newNodes = (estimatedNodeCount > (oldGraph.getNodeCount() + oldGraph.getDeletedNodeCount() >> 4)) ? new NodeNodeMap(oldGraph) : new IdentityHashMap<Node, Node>();
+        final Map<Node, Node> newNodes;
+        if (estimatedNodeCount > (oldGraph.getNodeCount() + oldGraph.getNodesDeletedSinceLastCompression() >> 4)) {
+            // Use dense map
+            newNodes = new NodeNodeMap(oldGraph);
+        } else {
+            // Use sparse map
+            newNodes = new IdentityHashMap<>();
+        }
         createNodeDuplicates(graph, nodes, replacements, newNodes);
 
         InplaceUpdateClosure replacementClosure = new InplaceUpdateClosure() {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeIdAccessor.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,63 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.graph;
+
+/**
+ * An entity that depends upon {@linkplain Graph#maybeCompress() stable} node identifiers.
+ */
+class NodeIdAccessor {
+    final Graph graph;
+    final int epoch;
+
+    public NodeIdAccessor(Graph graph) {
+        this.graph = graph;
+        this.epoch = graph.compressions;
+    }
+
+    Graph getGraph() {
+        return graph;
+    }
+
+    /**
+     * Verifies that node identifiers have not changed since this object was created.
+     * 
+     * @return true if the check succeeds
+     * @throws VerificationError if the check fails
+     */
+    public boolean verifyIdsAreStable() {
+        int compressions = graph.compressions - epoch;
+        if (compressions != 0) {
+            throw new VerificationError("accessing node id in %s across %d graph compression%s", graph, compressions, compressions == 1 ? "" : "s");
+        }
+        return true;
+    }
+
+    /**
+     * Gets the identifier for a node. If assertions are enabled, this method asserts that the
+     * identifier is stable.
+     */
+    public int getNodeId(Node node) {
+        assert verifyIdsAreStable();
+        return node.id();
+    }
+}
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeMap.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeMap.java	Sat Nov 09 21:34:07 2013 +0100
@@ -26,9 +26,8 @@
 import java.util.*;
 import java.util.Map.Entry;
 
-public class NodeMap<T> {
+public class NodeMap<T> extends NodeIdAccessor {
 
-    private final Graph graph;
     private final boolean autogrow;
     protected Object[] values;
 
@@ -37,13 +36,13 @@
     }
 
     public NodeMap(Graph graph, boolean autogrow) {
-        this.graph = graph;
+        super(graph);
         this.values = new Object[graph.nodeIdCount()];
         this.autogrow = autogrow;
     }
 
     public NodeMap(NodeMap<T> copyFrom) {
-        this.graph = copyFrom.graph;
+        super(copyFrom.graph);
         this.values = Arrays.copyOf(copyFrom.values, copyFrom.values.length);
         this.autogrow = copyFrom.autogrow;
     }
@@ -51,7 +50,7 @@
     @SuppressWarnings("unchecked")
     public T get(Node node) {
         check(node);
-        return (T) values[node.id()];
+        return (T) values[getNodeId(node)];
     }
 
     public boolean isEmpty() {
@@ -83,7 +82,7 @@
 
     public void set(Node node, T value) {
         check(node);
-        values[node.id()] = value;
+        values[getNodeId(node)] = value;
     }
 
     public int size() {
@@ -91,7 +90,7 @@
     }
 
     public boolean isNew(Node node) {
-        return node.id() >= size();
+        return getNodeId(node) >= size();
     }
 
     public void grow() {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -406,7 +406,7 @@
 
     @Override
     public void emitDeoptimizeCaller(DeoptimizationAction action, DeoptimizationReason reason) {
-        moveDeoptimizationActionAndReasonToThread(getMetaAccess().encodeDeoptActionAndReason(action, reason));
+        moveDeoptimizationActionAndReasonToThread(getMetaAccess().encodeDeoptActionAndReason(action, reason, (short) 0));
         append(new AMD64HotSpotDeoptimizeCallerOp());
     }
 
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotLIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -46,6 +46,30 @@
         this.config = config;
     }
 
+    private int getLogMinObjectAlignment() {
+        return config.logMinObjAlignment();
+    }
+
+    private int getNarrowOopShift() {
+        return config.narrowOopShift;
+    }
+
+    private long getNarrowOopBase() {
+        return config.narrowOopBase;
+    }
+
+    private int getLogKlassAlignment() {
+        return config.logKlassAlignment;
+    }
+
+    private int getNarrowKlassShift() {
+        return config.narrowKlassShift;
+    }
+
+    private long getNarrowKlassBase() {
+        return config.narrowKlassBase;
+    }
+
     private static boolean isCompressCandidate(DeoptimizingNode access) {
         return access != null && ((HeapAccess) access).isCompressible();
     }
@@ -56,9 +80,12 @@
         Variable result = newVariable(kind);
         LIRFrameState state = access != null ? state(access) : null;
         assert access == null || access instanceof HeapAccess;
-        if (config.useCompressedOops && isCompressCandidate(access)) {
+        if (isCompressCandidate(access) && config.useCompressedOops && kind == Kind.Object) {
             Variable scratch = newVariable(Kind.Long);
-            append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, config.narrowOopBase, config.narrowOopShift, config.logMinObjAlignment()));
+            append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, getNarrowOopBase(), getNarrowOopShift(), getLogMinObjectAlignment()));
+        } else if (isCompressCandidate(access) && config.useCompressedClassPointers && kind == Kind.Long) {
+            Variable scratch = newVariable(Kind.Long);
+            append(new LoadCompressedPointer(kind, result, scratch, loadAddress, state, getNarrowKlassBase(), getNarrowKlassShift(), getLogKlassAlignment()));
         } else {
             append(new LoadOp(kind, result, loadAddress, state));
         }
@@ -70,9 +97,12 @@
         HSAILAddressValue storeAddress = asAddressValue(address);
         LIRFrameState state = access != null ? state(access) : null;
         Variable input = load(inputVal);
-        if (config.useCompressedOops && isCompressCandidate(access)) {
+        if (isCompressCandidate(access) && config.useCompressedOops && kind == Kind.Object) {
             Variable scratch = newVariable(Kind.Long);
-            append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, config.narrowOopBase, config.narrowOopShift, config.logMinObjAlignment()));
+            append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, getNarrowOopBase(), getNarrowOopShift(), getLogMinObjectAlignment()));
+        } else if (isCompressCandidate(access) && config.useCompressedClassPointers && kind == Kind.Long) {
+            Variable scratch = newVariable(Kind.Long);
+            append(new StoreCompressedPointer(kind, storeAddress, input, scratch, state, getNarrowKlassBase(), getNarrowKlassShift(), getLogKlassAlignment()));
         } else {
             append(new StoreOp(kind, storeAddress, input, state));
         }
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -207,7 +207,7 @@
 
     @Override
     public void emitDeoptimizeCaller(DeoptimizationAction action, DeoptimizationReason reason) {
-        moveDeoptimizationActionAndReasonToThread(getMetaAccess().encodeDeoptActionAndReason(action, reason));
+        moveDeoptimizationActionAndReasonToThread(getMetaAccess().encodeDeoptActionAndReason(action, reason, (short) 0));
         append(new SPARCHotSpotDeoptimizeCallerOp());
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Sat Nov 09 21:34:07 2013 +0100
@@ -323,6 +323,9 @@
             }
         } while ((System.currentTimeMillis() - startTime) <= TimedBootstrap.getValue());
 
+        if (ResetDebugValuesAfterBootstrap.getValue()) {
+            printDebugValues("bootstrap", true);
+        }
         phaseTransition("bootstrap");
 
         bootstrapRunning = false;
@@ -368,53 +371,7 @@
             CompilationTask.withinEnqueue.set(Boolean.FALSE);
         }
 
-        if (Debug.isEnabled() && areMetricsOrTimersEnabled()) {
-            List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps();
-            List<DebugValue> debugValues = KeyRegistry.getDebugValues();
-            if (debugValues.size() > 0) {
-                ArrayList<DebugValue> sortedValues = new ArrayList<>(debugValues);
-                Collections.sort(sortedValues);
-
-                String summary = DebugValueSummary.getValue();
-                if (summary == null) {
-                    summary = "Complete";
-                }
-                switch (summary) {
-                    case "Name":
-                        printSummary(topLevelMaps, sortedValues);
-                        break;
-                    case "Partial": {
-                        DebugValueMap globalMap = new DebugValueMap("Global");
-                        for (DebugValueMap map : topLevelMaps) {
-                            flattenChildren(map, globalMap);
-                        }
-                        globalMap.normalize();
-                        printMap(new DebugValueScope(null, globalMap), sortedValues);
-                        break;
-                    }
-                    case "Complete": {
-                        DebugValueMap globalMap = new DebugValueMap("Global");
-                        for (DebugValueMap map : topLevelMaps) {
-                            globalMap.addChild(map);
-                        }
-                        globalMap.group();
-                        globalMap.normalize();
-                        printMap(new DebugValueScope(null, globalMap), sortedValues);
-                        break;
-                    }
-                    case "Thread":
-                        for (DebugValueMap map : topLevelMaps) {
-                            TTY.println("Showing the results for thread: " + map.getName());
-                            map.group();
-                            map.normalize();
-                            printMap(new DebugValueScope(null, map), sortedValues);
-                        }
-                        break;
-                    default:
-                        throw new GraalInternalError("Unknown summary type: %s", summary);
-                }
-            }
-        }
+        printDebugValues(ResetDebugValuesAfterBootstrap.getValue() ? "application" : null, false);
         phaseTransition("final");
 
         if (runtime.getConfig().ciTime) {
@@ -426,6 +383,79 @@
         BenchmarkCounters.shutdown(runtime.getCompilerToVM(), compilerStartTime);
     }
 
+    private void printDebugValues(String phase, boolean reset) throws GraalInternalError {
+        if (Debug.isEnabled() && areMetricsOrTimersEnabled()) {
+            TTY.println();
+            if (phase != null) {
+                TTY.println("<DebugValues:" + phase + ">");
+            } else {
+                TTY.println("<DebugValues>");
+            }
+            List<DebugValueMap> topLevelMaps = DebugValueMap.getTopLevelMaps();
+            List<DebugValue> debugValues = KeyRegistry.getDebugValues();
+            if (debugValues.size() > 0) {
+                try {
+                    ArrayList<DebugValue> sortedValues = new ArrayList<>(debugValues);
+                    Collections.sort(sortedValues);
+
+                    String summary = DebugValueSummary.getValue();
+                    if (summary == null) {
+                        summary = "Complete";
+                    }
+                    switch (summary) {
+                        case "Name":
+                            printSummary(topLevelMaps, sortedValues);
+                            break;
+                        case "Partial": {
+                            DebugValueMap globalMap = new DebugValueMap("Global");
+                            for (DebugValueMap map : topLevelMaps) {
+                                flattenChildren(map, globalMap);
+                            }
+                            globalMap.normalize();
+                            printMap(new DebugValueScope(null, globalMap), sortedValues);
+                            break;
+                        }
+                        case "Complete": {
+                            DebugValueMap globalMap = new DebugValueMap("Global");
+                            for (DebugValueMap map : topLevelMaps) {
+                                globalMap.addChild(map);
+                            }
+                            globalMap.group();
+                            globalMap.normalize();
+                            printMap(new DebugValueScope(null, globalMap), sortedValues);
+                            break;
+                        }
+                        case "Thread":
+                            for (DebugValueMap map : topLevelMaps) {
+                                TTY.println("Showing the results for thread: " + map.getName());
+                                map.group();
+                                map.normalize();
+                                printMap(new DebugValueScope(null, map), sortedValues);
+                            }
+                            break;
+                        default:
+                            throw new GraalInternalError("Unknown summary type: %s", summary);
+                    }
+                    if (reset) {
+                        for (DebugValueMap topLevelMap : topLevelMaps) {
+                            topLevelMap.reset();
+                        }
+                    }
+                } catch (Throwable e) {
+                    // Don't want this to change the exit status of the VM
+                    PrintStream err = System.err;
+                    err.println("Error while printing debug values:");
+                    e.printStackTrace();
+                }
+            }
+            if (phase != null) {
+                TTY.println("</DebugValues:" + phase + ">");
+            } else {
+                TTY.println("</DebugValues>");
+            }
+        }
+    }
+
     private void flattenChildren(DebugValueMap map, DebugValueMap globalMap) {
         globalMap.addChild(map);
         for (DebugValueMap child : map.getChildren()) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMetaAccessProvider.java	Sat Nov 09 21:34:07 2013 +0100
@@ -83,12 +83,16 @@
     private static final int ACTION_MASK = 0x07;
     private static final int REASON_SHIFT = 3;
     private static final int REASON_MASK = 0x1f;
+    private static final int DEBUG_SHIFT = 8;
+    private static final int DEBUG_MASK = 0x7fffff;
 
     @Override
-    public Constant encodeDeoptActionAndReason(DeoptimizationAction action, DeoptimizationReason reason) {
+    public Constant encodeDeoptActionAndReason(DeoptimizationAction action, DeoptimizationReason reason, int speculationId) {
         int actionValue = convertDeoptAction(action);
         int reasonValue = convertDeoptReason(reason);
-        Constant c = Constant.forInt(~(((reasonValue) << REASON_SHIFT) + ((actionValue) << ACTION_SHIFT)));
+        int speculationValue = speculationId & DEBUG_MASK;
+        Constant c = Constant.forInt(~((speculationValue << DEBUG_SHIFT) | (reasonValue << REASON_SHIFT) | (actionValue << ACTION_SHIFT)));
+        assert c.asInt() < 0;
         return c;
     }
 
@@ -104,6 +108,10 @@
         return action;
     }
 
+    public short decodeSpeculationId(Constant constant) {
+        return (short) (((~constant.asInt()) >> DEBUG_SHIFT) & DEBUG_MASK);
+    }
+
     public int convertDeoptAction(DeoptimizationAction action) {
         switch (action) {
             case None:
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/NewObjectSnippets.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,7 +23,9 @@
 package com.oracle.graal.hotspot.replacements;
 
 import static com.oracle.graal.api.code.UnsignedMath.*;
+import static com.oracle.graal.api.meta.MetaUtil.*;
 import static com.oracle.graal.hotspot.replacements.HotSpotReplacementsUtil.*;
+import static com.oracle.graal.hotspot.replacements.NewObjectSnippets.Options.*;
 import static com.oracle.graal.nodes.PiArrayNode.*;
 import static com.oracle.graal.nodes.PiNode.*;
 import static com.oracle.graal.nodes.extended.BranchProbabilityNode.*;
@@ -64,11 +66,11 @@
 
     public static final LocationIdentity INIT_LOCATION = new NamedLocationIdentity("Initialization");
 
-    public static class Options {
+    static class Options {
 
         //@formatter:off
         @Option(help = "")
-        private static final OptionValue<Boolean> ProfileAllocations = new OptionValue<>(false);
+        static final OptionValue<Boolean> ProfileAllocations = new OptionValue<>(false);
         //@formatter:on
     }
 
@@ -114,7 +116,7 @@
 
     @Fold
     private static boolean doProfile() {
-        return Options.ProfileAllocations.getValue();
+        return ProfileAllocations.getValue();
     }
 
     private static void profileAllocation(String path, long size, String typeContext) {
@@ -315,7 +317,7 @@
             args.add("hub", hub);
             args.add("prototypeMarkWord", type.prototypeMarkWord());
             args.addConst("fillContents", newInstanceNode.fillContents());
-            args.addConst("typeContext", MetaUtil.toJavaName(type, false));
+            args.addConst("typeContext", ProfileAllocations.getValue() ? toJavaName(type, false) : "");
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateInstance in %s: node=%s, template=%s, arguments=%s", graph, newInstanceNode, template, args);
@@ -342,7 +344,7 @@
             args.addConst("headerSize", headerSize);
             args.addConst("log2ElementSize", log2ElementSize);
             args.addConst("fillContents", newArrayNode.fillContents());
-            args.addConst("typeContext", MetaUtil.toJavaName(arrayType, false));
+            args.addConst("typeContext", ProfileAllocations.getValue() ? toJavaName(arrayType, false) : "");
 
             SnippetTemplate template = template(args);
             Debug.log("Lowering allocateArray in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, args);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -49,6 +49,7 @@
 import com.oracle.graal.nodes.java.MethodCallTargetNode.InvokeKind;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 
 /**
@@ -56,6 +57,13 @@
  */
 public class GraphBuilderPhase extends Phase {
 
+    static class Options {
+        // @formatter:off
+        @Option(help = "The trace level for the bytecode parser used when building a graph from bytecode")
+        public static final OptionValue<Integer> TraceBytecodeParserLevel = new OptionValue<>(0);
+        // @formatter:on
+    }
+
     public static final class RuntimeCalls {
 
         public static final ForeignCallDescriptor CREATE_NULL_POINTER_EXCEPTION = new ForeignCallDescriptor("createNullPointerException", Object.class);
@@ -63,14 +71,14 @@
     }
 
     /**
-     * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace
-     * the bytecode instructions as they are parsed.
+     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
+     * bytecode instructions as they are parsed.
      */
     public static final int TRACELEVEL_INSTRUCTIONS = 1;
 
     /**
-     * The minimum value to which {@link GraalOptions#TraceBytecodeParserLevel} must be set to trace
-     * the frame state before each bytecode instruction as it is parsed.
+     * The minimum value to which {@link Options#TraceBytecodeParserLevel} must be set to trace the
+     * frame state before each bytecode instruction as it is parsed.
      */
     public static final int TRACELEVEL_STATE = 2;
 
@@ -739,7 +747,7 @@
         ValueNode b = mirror ? x : y;
 
         CompareNode condition;
-        assert a.kind() != Kind.Double && a.kind() != Kind.Float;
+        assert !a.kind().isNumericFloat();
         if (cond == Condition.EQ || cond == Condition.NE) {
             if (a.kind() == Kind.Object) {
                 condition = new ObjectEqualsNode(a, b);
@@ -1787,8 +1795,10 @@
         }
     }
 
+    private final int traceLevel = Options.TraceBytecodeParserLevel.getValue();
+
     private void traceState() {
-        if (TraceBytecodeParserLevel.getValue() >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
+        if (traceLevel >= TRACELEVEL_STATE && Debug.isLogEnabled()) {
             Debug.log(String.format("|   state [nr locals = %d, stack depth = %d, method = %s]", frameState.localsSize(), frameState.stackSize(), method));
             for (int i = 0; i < frameState.localsSize(); ++i) {
                 ValueNode value = frameState.localAt(i);
@@ -2018,7 +2028,7 @@
     }
 
     private void traceInstruction(int bci, int opcode, boolean blockStart) {
-        if (TraceBytecodeParserLevel.getValue() >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) {
+        if (traceLevel >= TRACELEVEL_INSTRUCTIONS && Debug.isLogEnabled()) {
             StringBuilder sb = new StringBuilder(40);
             sb.append(blockStart ? '+' : '|');
             if (bci < 10) {
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILMove.java	Sat Nov 09 21:34:07 2013 +0100
@@ -188,57 +188,57 @@
 
     public static class LoadCompressedPointer extends LoadOp {
 
-        private long narrowOopBase;
-        private int narrowOopShift;
-        private int logMinObjAlignment;
+        private final long base;
+        private final int shift;
+        private final int alignment;
         @Temp({REG}) private AllocatableValue scratch;
 
-        public LoadCompressedPointer(Kind kind, AllocatableValue result, AllocatableValue scratch, HSAILAddressValue address, LIRFrameState state, long narrowOopBase, int narrowOopShift,
-                        int logMinObjAlignment) {
+        public LoadCompressedPointer(Kind kind, AllocatableValue result, AllocatableValue scratch, HSAILAddressValue address, LIRFrameState state, long base, int shift, int alignment) {
             super(kind, result, address, state);
-            this.narrowOopBase = narrowOopBase;
-            this.narrowOopShift = narrowOopShift;
-            this.logMinObjAlignment = logMinObjAlignment;
+            this.base = base;
+            this.shift = shift;
+            this.alignment = alignment;
             this.scratch = scratch;
-            assert kind == Kind.Object;
+            assert kind == Kind.Object || kind == Kind.Long;
         }
 
         @Override
         public void emitMemAccess(HSAILAssembler masm) {
             // we will do a 32 bit load, zero extending into a 64 bit register
             masm.emitLoad(result, address.toAddress(), "u32");
-            decodePointer(masm, result, narrowOopBase, narrowOopShift, logMinObjAlignment);
+            boolean testForNull = (kind == Kind.Object);
+            decodePointer(masm, result, base, shift, alignment, testForNull);
         }
     }
 
     public static class StoreCompressedPointer extends HSAILLIRInstruction {
 
         protected final Kind kind;
-        private long narrowOopBase;
-        private int narrowOopShift;
-        private int logMinObjAlignment;
+        private final long base;
+        private final int shift;
+        private final int alignment;
         @Temp({REG}) private AllocatableValue scratch;
         @Alive({REG}) protected AllocatableValue input;
         @Alive({COMPOSITE}) protected HSAILAddressValue address;
         @State protected LIRFrameState state;
 
-        public StoreCompressedPointer(Kind kind, HSAILAddressValue address, AllocatableValue input, AllocatableValue scratch, LIRFrameState state, long narrowOopBase, int narrowOopShift,
-                        int logMinObjAlignment) {
-            this.narrowOopBase = narrowOopBase;
-            this.narrowOopShift = narrowOopShift;
-            this.logMinObjAlignment = logMinObjAlignment;
+        public StoreCompressedPointer(Kind kind, HSAILAddressValue address, AllocatableValue input, AllocatableValue scratch, LIRFrameState state, long base, int shift, int alignment) {
+            this.base = base;
+            this.shift = shift;
+            this.alignment = alignment;
             this.scratch = scratch;
             this.kind = kind;
             this.address = address;
             this.state = state;
             this.input = input;
-            assert kind == Kind.Object;
+            assert kind == Kind.Object || kind == Kind.Long;
         }
 
         @Override
         public void emitCode(TargetMethodAssembler tasm, HSAILAssembler masm) {
             masm.emitMov(scratch, input);
-            encodePointer(masm, scratch, narrowOopBase, narrowOopShift, logMinObjAlignment);
+            boolean testForNull = (kind == Kind.Object);
+            encodePointer(masm, scratch, base, shift, alignment, testForNull);
             if (state != null) {
                 throw new InternalError("NYI");
                 // tasm.recordImplicitException(masm.codeBuffer.position(), state);
@@ -247,24 +247,24 @@
         }
     }
 
-    private static void encodePointer(HSAILAssembler masm, Value scratch, long narrowOopBase, int narrowOopShift, int logMinObjAlignment) {
-        if (narrowOopBase == 0 && narrowOopShift == 0) {
+    private static void encodePointer(HSAILAssembler masm, Value scratch, long base, int shift, int alignment, boolean testForNull) {
+        if (base == 0 && shift == 0) {
             return;
         }
-        if (narrowOopShift != 0) {
-            assert logMinObjAlignment == narrowOopShift : "Encode algorithm is wrong";
+        if (shift != 0) {
+            assert alignment == shift : "Encode algorithm is wrong";
         }
-        masm.emitCompressedOopEncode(scratch, narrowOopBase, narrowOopShift);
+        masm.emitCompressedOopEncode(scratch, base, shift, testForNull);
     }
 
-    private static void decodePointer(HSAILAssembler masm, Value result, long narrowOopBase, int narrowOopShift, int logMinObjAlignment) {
-        if (narrowOopBase == 0 && narrowOopShift == 0) {
+    private static void decodePointer(HSAILAssembler masm, Value result, long base, int shift, int alignment, boolean testForNull) {
+        if (base == 0 && shift == 0) {
             return;
         }
-        if (narrowOopShift != 0) {
-            assert logMinObjAlignment == narrowOopShift : "Decode algorithm is wrong";
+        if (shift != 0) {
+            assert alignment == shift : "Decode algorithm is wrong";
         }
-        masm.emitCompressedOopDecode(result, narrowOopBase, narrowOopShift);
+        masm.emitCompressedOopDecode(result, base, shift, testForNull);
     }
 
     public static class LeaOp extends HSAILLIRInstruction {
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/EdgeMoveOptimizer.java	Sat Nov 09 21:34:07 2013 +0100
@@ -24,7 +24,7 @@
 
 import java.util.*;
 
-import com.oracle.graal.lir.StandardOp.*;
+import com.oracle.graal.lir.StandardOp.MoveOp;
 import com.oracle.graal.nodes.cfg.*;
 
 /**
@@ -182,9 +182,16 @@
 
         assert numSux == 2 : "method should not be called otherwise";
 
-        assert instructions.get(instructions.size() - 1) instanceof StandardOp.JumpOp : "block must end with unconditional jump";
+        LIRInstruction lastInstruction = instructions.get(instructions.size() - 1);
+        if (lastInstruction instanceof StandardOp.FallThroughOp) {
+            assert ((StandardOp.FallThroughOp) lastInstruction).fallThroughTarget() != null : "block must end with unconditional jump";
+            // fall through ops like switches cannot be optimized anyway - they have input operands
+            return;
+        } else {
+            assert lastInstruction instanceof StandardOp.JumpOp : "block must end with unconditional jump";
+        }
 
-        if (instructions.get(instructions.size() - 1).hasState()) {
+        if (lastInstruction.hasState()) {
             // cannot optimize instructions when debug info is needed
             return;
         }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/TargetMethodAssembler.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/TargetMethodAssembler.java	Sat Nov 09 21:34:07 2013 +0100
@@ -186,7 +186,7 @@
      * including long constants that fit into the 32-bit range.
      */
     public int asIntConst(Value value) {
-        assert (value.getKind().getStackKind() == Kind.Int || value.getKind() == Kind.Long) && isConstant(value);
+        assert (value.getKind().isNumericInteger()) && isConstant(value);
         Constant constant = (Constant) value;
         assert !codeCache.needsDataPatch(constant) : constant + " should be in a DataPatch";
         long c = constant.asLong();
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/BasicInductionVariable.java	Sat Nov 09 21:34:07 2013 +0100
@@ -123,19 +123,25 @@
         Kind fromKind = phi.kind();
         StructuredGraph graph = graph();
         ValueNode stride = strideNode();
-        ValueNode maxTripCount = loop.counted().maxTripCountNode(assumePositiveTripCount);
         ValueNode initNode = this.initNode();
         if (fromKind != kind) {
             stride = graph.unique(new ConvertNode(fromKind, kind, stride));
-            maxTripCount = graph.unique(new ConvertNode(fromKind, kind, maxTripCount));
             initNode = graph.unique(new ConvertNode(fromKind, kind, initNode));
         }
+        ValueNode maxTripCount = loop.counted().maxTripCountNode(assumePositiveTripCount);
+        if (maxTripCount.kind() != kind) {
+            maxTripCount = graph.unique(new ConvertNode(maxTripCount.kind(), kind, maxTripCount));
+        }
         return IntegerArithmeticNode.add(graph, IntegerArithmeticNode.mul(graph, stride, IntegerArithmeticNode.sub(graph, maxTripCount, ConstantNode.forIntegerKind(kind, 1, graph))), initNode);
     }
 
     @Override
     public ValueNode exitValueNode() {
+        Kind kind = phi.kind();
         ValueNode maxTripCount = loop.counted().maxTripCountNode(false);
+        if (maxTripCount.kind() != kind) {
+            maxTripCount = graph().unique(new ConvertNode(maxTripCount.kind(), kind, maxTripCount));
+        }
         return IntegerArithmeticNode.add(graph(), IntegerArithmeticNode.mul(graph(), strideNode(), maxTripCount), initNode());
     }
 
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopTransformations.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopTransformations.java	Sat Nov 09 21:34:07 2013 +0100
@@ -26,6 +26,7 @@
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.NodeClass.NodeClassIterator;
 import com.oracle.graal.graph.NodeClass.Position;
 import com.oracle.graal.nodes.*;
@@ -59,7 +60,7 @@
         LoopBeginNode loopBegin = loop.loopBegin();
         StructuredGraph graph = loopBegin.graph();
         while (!loopBegin.isDeleted()) {
-            int mark = graph.getMark();
+            Mark mark = graph.getMark();
             peel(loop);
             canonicalizer.applyIncremental(graph, context, mark);
             if (iterations++ > UNROLL_LIMIT || graph.getNodeCount() > MaximumDesiredSize.getValue() * 3) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/DeoptimizeNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/DeoptimizeNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -31,12 +31,18 @@
 
     private final DeoptimizationAction action;
     private final DeoptimizationReason reason;
+    private final int speculationId;
 
     public DeoptimizeNode(DeoptimizationAction action, DeoptimizationReason reason) {
+        this(action, reason, (short) 0);
+    }
+
+    public DeoptimizeNode(DeoptimizationAction action, DeoptimizationReason reason, int speculationId) {
         assert action != null;
         assert reason != null;
         this.action = action;
         this.reason = reason;
+        this.speculationId = speculationId;
     }
 
     public DeoptimizationAction action() {
@@ -49,12 +55,12 @@
 
     @Override
     public void generate(LIRGeneratorTool gen) {
-        gen.emitDeoptimize(gen.getMetaAccess().encodeDeoptActionAndReason(action, reason), this);
+        gen.emitDeoptimize(gen.getMetaAccess().encodeDeoptActionAndReason(action, reason, speculationId), this);
     }
 
     @Override
     public ValueNode getActionAndReason(MetaAccessProvider metaAccess) {
-        return ConstantNode.forConstant(metaAccess.encodeDeoptActionAndReason(action, reason), metaAccess, graph());
+        return ConstantNode.forConstant(metaAccess.encodeDeoptActionAndReason(action, reason, speculationId), metaAccess, graph());
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/PhiNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -51,7 +51,7 @@
     @Input(notDataflow = true) private MergeNode merge;
     @Input private final NodeInputList<ValueNode> values = new NodeInputList<>(this);
     private final PhiType type;
-    private final Object identity;
+    private final LocationIdentity identity;
 
     /**
      * Create a value phi ({@link PhiType#Value}) with the specified kind.
@@ -77,7 +77,7 @@
      * @param type the type of the new phi
      * @param merge the merge that the new phi belongs to
      */
-    public PhiNode(PhiType type, MergeNode merge, Object identity) {
+    public PhiNode(PhiType type, MergeNode merge, LocationIdentity identity) {
         super(type.stamp);
         assert type.stamp != null : merge + " " + type;
         this.type = type;
@@ -93,7 +93,7 @@
         return merge;
     }
 
-    public Object getIdentity() {
+    public LocationIdentity getIdentity() {
         assert type != PhiType.Value;
         return identity;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ProxyNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ProxyNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.nodes;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.ValueNumberable;
 import com.oracle.graal.graph.spi.*;
@@ -41,9 +42,9 @@
     @Input(notDataflow = true) private AbstractBeginNode proxyPoint;
     @Input private ValueNode value;
     private final PhiType type;
-    private final Object identity;
+    private final LocationIdentity identity;
 
-    public ProxyNode(ValueNode value, AbstractBeginNode exit, PhiType type, Object identity) {
+    public ProxyNode(ValueNode value, AbstractBeginNode exit, PhiType type, LocationIdentity identity) {
         super(type == PhiType.Value ? value.stamp() : type.stamp);
         this.type = type;
         this.identity = identity;
@@ -69,7 +70,7 @@
         return type;
     }
 
-    public Object getIdentity() {
+    public LocationIdentity getIdentity() {
         assert type != PhiType.Value;
         return identity;
     }
@@ -113,7 +114,7 @@
         return graph.unique(new ProxyNode(value, exit, PhiType.Value, null));
     }
 
-    public static ProxyNode forMemory(ValueNode value, AbstractBeginNode exit, Object location, StructuredGraph graph) {
+    public static ProxyNode forMemory(ValueNode value, AbstractBeginNode exit, LocationIdentity location, StructuredGraph graph) {
         return graph.unique(new ProxyNode(value, exit, PhiType.Memory, location));
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/CompareNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -140,29 +140,54 @@
                 setX(convertX.value());
                 setY(convertY.value());
             }
+        } else if (x() instanceof ConvertNode && y().isConstant()) {
+            ConvertNode convertX = (ConvertNode) x();
+            ConstantNode newY = canonicalConvertConstant(convertX, y().asConstant());
+            if (newY != null) {
+                setX(convertX.value());
+                setY(newY);
+            }
+        } else if (y() instanceof ConvertNode && x().isConstant()) {
+            ConvertNode convertY = (ConvertNode) y();
+            ConstantNode newX = canonicalConvertConstant(convertY, x().asConstant());
+            if (newX != null) {
+                setX(newX);
+                setY(convertY.value());
+            }
         }
         return this;
     }
 
+    private static ConstantNode canonicalConvertConstant(ConvertNode convert, Constant constant) {
+        if (convert.isLossless()) {
+            assert constant.getKind() == convert.getToKind();
+            Constant reverseConverted = ConvertNode.convert(convert.getToKind(), convert.getFromKind(), constant);
+            if (convert.evalConst(reverseConverted).equals(constant)) {
+                return ConstantNode.forPrimitive(reverseConverted, convert.graph());
+            }
+        }
+        return null;
+    }
+
     public static CompareNode createCompareNode(StructuredGraph graph, Condition condition, ValueNode x, ValueNode y) {
         assert x.kind() == y.kind();
         assert condition.isCanonical() : "condition is not canonical: " + condition;
-        assert x.kind() != Kind.Double && x.kind() != Kind.Float;
+        assert !x.kind().isNumericFloat();
 
         CompareNode comparison;
         if (condition == Condition.EQ) {
             if (x.kind() == Kind.Object) {
                 comparison = new ObjectEqualsNode(x, y);
             } else {
-                assert x.kind().getStackKind() == Kind.Int || x.kind() == Kind.Long;
+                assert x.kind().isNumericInteger();
                 comparison = new IntegerEqualsNode(x, y);
             }
         } else if (condition == Condition.LT) {
-            assert x.kind().getStackKind() == Kind.Int || x.kind() == Kind.Long;
+            assert x.kind().isNumericInteger();
             comparison = new IntegerLessThanNode(x, y);
         } else {
             assert condition == Condition.BT;
-            assert x.kind().getStackKind() == Kind.Int || x.kind() == Kind.Long;
+            assert x.kind().isNumericInteger();
             comparison = new IntegerBelowThanNode(x, y);
         }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/Condition.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/Condition.java	Sat Nov 09 21:34:07 2013 +0100
@@ -322,7 +322,7 @@
      *         the comparison is known to be false
      */
     public boolean foldCondition(Constant lt, Constant rt, ConstantReflectionProvider constantReflection) {
-        assert lt.getKind() != Kind.Double && lt.getKind() != Kind.Float && rt.getKind() != Kind.Double && rt.getKind() != Kind.Float;
+        assert !lt.getKind().isNumericFloat() && !rt.getKind().isNumericFloat();
         return foldCondition(lt, rt, constantReflection, false);
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/ConvertNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -87,9 +87,7 @@
         throw GraalInternalError.shouldNotReachHere();
     }
 
-    public Constant evalConst(Constant... inputs) {
-        assert inputs.length == 1;
-        Constant c = inputs[0];
+    public static Constant convert(Kind from, Kind to, Constant c) {
         switch (from) {
             case Byte:
                 byte byteVal = (byte) c.asInt();
@@ -228,10 +226,20 @@
         throw GraalInternalError.shouldNotReachHere();
     }
 
+    public Constant evalConst(Constant... inputs) {
+        assert inputs.length == 1;
+        return convert(from, to, inputs[0]);
+    }
+
     @Override
     public Node canonical(CanonicalizerTool tool) {
         if (value.isConstant()) {
             return ConstantNode.forPrimitive(evalConst(value.asConstant()), graph());
+        } else if (value instanceof ConvertNode) {
+            ConvertNode other = (ConvertNode) value;
+            if (other.isLossless() && other.to != Kind.Char) {
+                return graph().unique(new ConvertNode(other.from, this.to, other.value()));
+            }
         }
         return this;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatArithmeticNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/FloatArithmeticNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -32,6 +32,7 @@
 
     public FloatArithmeticNode(Kind kind, ValueNode x, ValueNode y, boolean isStrictFP) {
         super(kind, x, y);
+        assert kind.isNumericFloat();
         this.isStrictFP = isStrictFP;
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerAddNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerAddNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -97,7 +97,7 @@
 
     public static boolean isIntegerAddition(ValueNode result, ValueNode a, ValueNode b) {
         Kind kind = result.kind();
-        if (kind != a.kind() || kind != b.kind() || !(kind.getStackKind() == Kind.Int || kind == Kind.Long)) {
+        if (kind != a.kind() || kind != b.kind() || !kind.isNumericInteger()) {
             return false;
         }
         if (result.isConstant() && a.isConstant() && b.isConstant()) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerArithmeticNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerArithmeticNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -30,7 +30,7 @@
 
     public IntegerArithmeticNode(Kind kind, ValueNode x, ValueNode y) {
         super(kind, x, y);
-        assert kind == Kind.Int || kind == Kind.Long;
+        assert kind.isNumericInteger();
     }
 
     public static IntegerAddNode add(StructuredGraph graph, ValueNode v1, ValueNode v2) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowThanNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerBelowThanNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -39,8 +39,8 @@
      */
     public IntegerBelowThanNode(ValueNode x, ValueNode y) {
         super(x, y);
-        assert x.kind() != Kind.Double && x.kind() != Kind.Float && x.kind() != Kind.Object;
-        assert y.kind() != Kind.Double && y.kind() != Kind.Float && y.kind() != Kind.Object;
+        assert !x.kind().isNumericFloat() && x.kind() != Kind.Object;
+        assert !y.kind().isNumericFloat() && y.kind() != Kind.Object;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerEqualsNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerEqualsNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -38,8 +38,8 @@
      */
     public IntegerEqualsNode(ValueNode x, ValueNode y) {
         super(x, y);
-        assert x.kind() != Kind.Double && x.kind() != Kind.Float && x.kind() != Kind.Object;
-        assert y.kind() != Kind.Double && y.kind() != Kind.Float && y.kind() != Kind.Object;
+        assert !x.kind().isNumericFloat() && x.kind() != Kind.Object;
+        assert !y.kind().isNumericFloat() && y.kind() != Kind.Object;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/IntegerLessThanNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -39,8 +39,8 @@
      */
     public IntegerLessThanNode(ValueNode x, ValueNode y) {
         super(x, y);
-        assert x.kind() != Kind.Double && x.kind() != Kind.Float && x.kind() != Kind.Object;
-        assert y.kind() != Kind.Double && y.kind() != Kind.Float && y.kind() != Kind.Object;
+        assert !x.kind().isNumericFloat() && x.kind() != Kind.Object;
+        assert !y.kind().isNumericFloat() && y.kind() != Kind.Object;
     }
 
     @Override
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingAccessNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingAccessNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -22,10 +22,11 @@
  */
 package com.oracle.graal.nodes.extended;
 
+import com.oracle.graal.api.meta.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.type.*;
 
-public abstract class FloatingAccessNode extends FloatingGuardedNode implements Access {
+public abstract class FloatingAccessNode extends FloatingGuardedNode implements Access, MemoryAccess {
 
     @Input private ValueNode object;
     @Input private LocationNode location;
@@ -46,6 +47,10 @@
         return location;
     }
 
+    public LocationIdentity getLocationIdentity() {
+        return location.getLocationIdentity();
+    }
+
     public boolean getNullCheck() {
         return nullCheck;
     }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/FloatingReadNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -51,7 +51,7 @@
         this.lastLocationAccess = lastLocationAccess;
     }
 
-    public Node lastLocationAccess() {
+    public Node getLastLocationAccess() {
         return lastLocationAccess;
     }
 
@@ -90,7 +90,7 @@
 
     @Override
     public boolean verify() {
-        Node lla = lastLocationAccess();
+        Node lla = getLastLocationAccess();
         assert lla == null || isMemoryCheckPoint(lla) || isMemoryPhi(lla) || isMemoryProxy(lla) : "lastLocationAccess of " + this + " should be a MemoryCheckpoint, but is " + lla;
         return super.verify();
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/MemoryAccess.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.extended;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+
+/**
+ * This interface marks nodes that access some memory location, and that have an edge to the last
+ * node that kills this location.
+ */
+public interface MemoryAccess {
+
+    LocationIdentity getLocationIdentity();
+
+    Node getLastLocationAccess();
+
+    void setLastLocationAccess(Node lla);
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ReadNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ReadNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -77,7 +77,7 @@
                             return ConstantNode.forConstant(constant, metaAccess, read.graph());
                         }
                     }
-                } else if (object.kind() == Kind.Long || object.kind().getStackKind() == Kind.Int) {
+                } else if (object.kind().isNumericInteger()) {
                     long base = object.asConstant().asLong();
                     if (base != 0L) {
                         Constant constant = tool.getConstantReflection().readUnsafeConstant(kind, null, base + displacement, compressible);
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeAccessNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/UnsafeAccessNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -73,6 +73,7 @@
                 // the null check and if a field is found, the offset is so small that this is
                 // never a valid access of an arbitrary address.
                 if (field != null && field.getKind() == this.accessKind()) {
+                    assert !graph().isAfterFloatingReadPhase() : "cannot add more precise memory location after floating read phase";
                     return cloneAsFieldAccess(field);
                 }
             }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/WriteNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,6 +23,7 @@
 package com.oracle.graal.nodes.extended;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.LocationNode.Location;
 import com.oracle.graal.nodes.spi.*;
@@ -32,12 +33,14 @@
 /**
  * Writes a given {@linkplain #value() value} a {@linkplain AccessNode memory location}.
  */
-public final class WriteNode extends AccessNode implements StateSplit, LIRLowerable, MemoryCheckpoint.Single, Virtualizable {
+public final class WriteNode extends AccessNode implements StateSplit, LIRLowerable, MemoryCheckpoint.Single, MemoryAccess, Virtualizable {
 
     @Input private ValueNode value;
     @Input(notDataflow = true) private FrameState stateAfter;
     private final boolean initialization;
 
+    @Input private Node lastLocationAccess;
+
     public FrameState stateAfter() {
         return stateAfter;
     }
@@ -89,6 +92,15 @@
         return location().getLocationIdentity();
     }
 
+    public Node getLastLocationAccess() {
+        return lastLocationAccess;
+    }
+
+    public void setLastLocationAccess(Node lla) {
+        updateUsages(lastLocationAccess, lla);
+        lastLocationAccess = lla;
+    }
+
     @Override
     public void virtualize(VirtualizerTool tool) {
         if (location() instanceof ConstantLocationNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Sat Nov 09 21:34:07 2013 +0100
@@ -57,4 +57,6 @@
      * Gets the closest fixed node preceding the node currently being lowered.
      */
     FixedWithNextNode lastFixedNode();
+
+    GuardingNode getCurrentGuardAnchor();
 }
--- a/graal/com.oracle.graal.options.test/src/com/oracle/graal/options/test/TestOptionValue.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.options.test/src/com/oracle/graal/options/test/TestOptionValue.java	Sat Nov 09 21:34:07 2013 +0100
@@ -25,6 +25,8 @@
 import static com.oracle.graal.options.test.TestOptionValue.Options.*;
 import static org.junit.Assert.*;
 
+import java.util.*;
+
 import org.junit.*;
 
 import com.oracle.graal.options.*;
@@ -99,4 +101,27 @@
             // expected
         }
     }
+
+    @Test
+    public void toStringTest() {
+        assertEquals("com.oracle.graal.options.test.TestOptionValue$Options.Mutable=original", Mutable.toString());
+        try (OverrideScope s1 = OptionValue.override(Mutable, "override1")) {
+            assertEquals("com.oracle.graal.options.test.TestOptionValue$Options.Mutable=override1", Mutable.toString());
+            try (OverrideScope s2 = OptionValue.override(Mutable, "override2")) {
+                assertEquals("com.oracle.graal.options.test.TestOptionValue$Options.Mutable=override2", Mutable.toString());
+            }
+        }
+    }
+
+    @Test
+    public void getValuesTest() {
+        assertEquals(Arrays.asList("original"), Mutable.getValues(null));
+        assertEquals(Arrays.asList(true), Stable.getValues(null));
+        try (OverrideScope s1 = OptionValue.override(Mutable, "override1")) {
+            assertEquals(Arrays.asList("override1", "original"), Mutable.getValues(null));
+            try (OverrideScope s2 = OptionValue.override(Mutable, "override2")) {
+                assertEquals(Arrays.asList("override2", "override1", "original"), Mutable.getValues(null));
+            }
+        }
+    }
 }
--- a/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionValue.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.options/src/com/oracle/graal/options/OptionValue.java	Sat Nov 09 21:34:07 2013 +0100
@@ -22,6 +22,7 @@
  */
 package com.oracle.graal.options;
 
+import java.io.*;
 import java.util.*;
 import java.util.Map.Entry;
 
@@ -136,8 +137,24 @@
 
     private OptionDescriptor descriptor;
 
+    private long reads;
+    private OptionValue<?> next;
+    private static OptionValue head;
+
+    private static final boolean ShowReadsHistogram = Boolean.getBoolean("graal.showOptionValueReadsHistogram");
+
+    private static void addToHistogram(OptionValue<?> option) {
+        if (ShowReadsHistogram) {
+            synchronized (OptionValue.class) {
+                option.next = head;
+                head = option;
+            }
+        }
+    }
+
     public OptionValue(T value) {
         this.value = value;
+        addToHistogram(this);
     }
 
     private static final Object UNINITIALIZED = "UNINITIALIZED";
@@ -149,6 +166,7 @@
     @SuppressWarnings("unchecked")
     protected OptionValue() {
         this.value = (T) UNINITIALIZED;
+        addToHistogram(this);
     }
 
     /**
@@ -167,21 +185,25 @@
 
     /**
      * Gets the name of this option. The name for an option value with a null
-     * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is {@code "<anonymous>"}.
+     * {@linkplain #setDescriptor(OptionDescriptor) descriptor} is the value of
+     * {@link Object#toString()}.
      */
     public String getName() {
-        return descriptor == null ? "<anonymous>" : (descriptor.getDeclaringClass().getName() + "." + descriptor.getName());
+        return descriptor == null ? super.toString() : (descriptor.getDeclaringClass().getName() + "." + descriptor.getName());
     }
 
     @Override
     public String toString() {
-        return getName() + "=" + value;
+        return getName() + "=" + getValue();
     }
 
     /**
      * Gets the value of this option.
      */
     public T getValue() {
+        if (ShowReadsHistogram) {
+            reads++;
+        }
         if (!(this instanceof StableOptionValue)) {
             OverrideScope overrideScope = overrideScopes.get();
             if (overrideScope != null) {
@@ -198,6 +220,26 @@
     }
 
     /**
+     * Gets the values of this option including overridden values.
+     * 
+     * @param c the collection to which the values are added. If null, one is allocated.
+     * @return the collection to which the values were added in order from most overridden to
+     *         current value
+     */
+    @SuppressWarnings("unchecked")
+    public Collection<T> getValues(Collection<T> c) {
+        Collection<T> values = c == null ? new ArrayList<T>() : c;
+        if (!(this instanceof StableOptionValue)) {
+            OverrideScope overrideScope = overrideScopes.get();
+            if (overrideScope != null) {
+                overrideScope.getOverrides(this, (Collection<Object>) values);
+            }
+        }
+        values.add(value);
+        return values;
+    }
+
+    /**
      * Sets the value of this option.
      */
     @SuppressWarnings("unchecked")
@@ -214,6 +256,8 @@
 
         abstract <T> T getOverride(OptionValue<T> option);
 
+        abstract void getOverrides(OptionValue<?> option, Collection<Object> c);
+
         public abstract void close();
     }
 
@@ -246,6 +290,13 @@
         }
 
         @Override
+        void getOverrides(OptionValue<?> key, Collection<Object> c) {
+            if (key == this.option) {
+                c.add(value);
+            }
+        }
+
+        @Override
         public void close() {
             overrideScopes.set(null);
         }
@@ -311,10 +362,52 @@
         }
 
         @Override
+        void getOverrides(OptionValue<?> option, Collection<Object> c) {
+            Object v = overrides.get(option);
+            if (v != null) {
+                c.add(v);
+            }
+            if (parent != null) {
+                parent.getOverrides(option, c);
+            }
+        }
+
+        @Override
         public void close() {
             if (!overrides.isEmpty()) {
                 overrideScopes.set(parent);
             }
         }
     }
+
+    static {
+        if (ShowReadsHistogram) {
+            Runtime.getRuntime().addShutdownHook(new Thread() {
+                @Override
+                public void run() {
+                    ArrayList<OptionValue<?>> options = new ArrayList<>();
+                    for (OptionValue<?> option = head; option != null; option = option.next) {
+                        options.add(option);
+                    }
+                    Collections.sort(options, new Comparator<OptionValue<?>>() {
+
+                        public int compare(OptionValue<?> o1, OptionValue<?> o2) {
+                            if (o1.reads < o2.reads) {
+                                return -1;
+                            } else if (o1.reads > o2.reads) {
+                                return 1;
+                            } else {
+                                return o1.getName().compareTo(o2.getName());
+                            }
+                        }
+                    });
+                    PrintStream out = System.out;
+                    out.println("=== OptionValue reads histogram ===");
+                    for (OptionValue<?> option : options) {
+                        out.println(option.reads + "\t" + option);
+                    }
+                }
+            });
+        }
+    }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/CanonicalizerPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -28,6 +28,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Graph.NodeChangedListener;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
@@ -71,14 +72,14 @@
     }
 
     /**
-     * @param newNodesMark only the {@linkplain Graph#getNewNodes(int) new nodes} specified by this
+     * @param newNodesMark only the {@linkplain Graph#getNewNodes(Mark) new nodes} specified by this
      *            mark are processed
      */
-    public void applyIncremental(StructuredGraph graph, PhaseContext context, int newNodesMark) {
+    public void applyIncremental(StructuredGraph graph, PhaseContext context, Mark newNodesMark) {
         applyIncremental(graph, context, newNodesMark, true);
     }
 
-    public void applyIncremental(StructuredGraph graph, PhaseContext context, int newNodesMark, boolean dumpGraph) {
+    public void applyIncremental(StructuredGraph graph, PhaseContext context, Mark newNodesMark, boolean dumpGraph) {
         new Instance(context, canonicalizeReads, newNodesMark, customCanonicalizer).apply(graph, dumpGraph);
     }
 
@@ -94,11 +95,11 @@
         new Instance(context, canonicalizeReads, workingSet, customCanonicalizer).apply(graph, dumpGraph);
     }
 
-    public void applyIncremental(StructuredGraph graph, PhaseContext context, Iterable<Node> workingSet, int newNodesMark) {
+    public void applyIncremental(StructuredGraph graph, PhaseContext context, Iterable<Node> workingSet, Mark newNodesMark) {
         applyIncremental(graph, context, workingSet, newNodesMark, true);
     }
 
-    public void applyIncremental(StructuredGraph graph, PhaseContext context, Iterable<Node> workingSet, int newNodesMark, boolean dumpGraph) {
+    public void applyIncremental(StructuredGraph graph, PhaseContext context, Iterable<Node> workingSet, Mark newNodesMark, boolean dumpGraph) {
         new Instance(context, canonicalizeReads, workingSet, newNodesMark, customCanonicalizer).apply(graph, dumpGraph);
     }
 
@@ -109,7 +110,7 @@
 
     private static final class Instance extends Phase {
 
-        private final int newNodesMark;
+        private final Mark newNodesMark;
         private final PhaseContext context;
         private final CustomCanonicalizer customCanonicalizer;
         private final Iterable<Node> initWorkingSet;
@@ -119,18 +120,18 @@
         private Tool tool;
 
         private Instance(PhaseContext context, boolean canonicalizeReads, CustomCanonicalizer customCanonicalizer) {
-            this(context, canonicalizeReads, null, 0, customCanonicalizer);
+            this(context, canonicalizeReads, null, null, customCanonicalizer);
         }
 
         private Instance(PhaseContext context, boolean canonicalizeReads, Iterable<Node> workingSet, CustomCanonicalizer customCanonicalizer) {
-            this(context, canonicalizeReads, workingSet, 0, customCanonicalizer);
+            this(context, canonicalizeReads, workingSet, null, customCanonicalizer);
         }
 
-        private Instance(PhaseContext context, boolean canonicalizeReads, int newNodesMark, CustomCanonicalizer customCanonicalizer) {
+        private Instance(PhaseContext context, boolean canonicalizeReads, Mark newNodesMark, CustomCanonicalizer customCanonicalizer) {
             this(context, canonicalizeReads, null, newNodesMark, customCanonicalizer);
         }
 
-        private Instance(PhaseContext context, boolean canonicalizeReads, Iterable<Node> workingSet, int newNodesMark, CustomCanonicalizer customCanonicalizer) {
+        private Instance(PhaseContext context, boolean canonicalizeReads, Iterable<Node> workingSet, Mark newNodesMark, CustomCanonicalizer customCanonicalizer) {
             super("Canonicalizer");
             this.newNodesMark = newNodesMark;
             this.context = context;
@@ -141,13 +142,14 @@
 
         @Override
         protected void run(StructuredGraph graph) {
+            boolean wholeGraph = newNodesMark == null || newNodesMark.isStart();
             if (initWorkingSet == null) {
-                workList = graph.createNodeWorkList(newNodesMark == 0, MAX_ITERATION_PER_NODE);
+                workList = graph.createNodeWorkList(wholeGraph, MAX_ITERATION_PER_NODE);
             } else {
                 workList = graph.createNodeWorkList(false, MAX_ITERATION_PER_NODE);
                 workList.addAll(initWorkingSet);
             }
-            if (newNodesMark > 0) {
+            if (!wholeGraph) {
                 workList.addAll(graph.getNewNodes(newNodesMark));
             }
             tool = new Tool();
@@ -182,7 +184,7 @@
                     return;
                 }
                 StructuredGraph graph = (StructuredGraph) node.graph();
-                int mark = graph.getMark();
+                Mark mark = graph.getMark();
                 if (!tryKillUnused(node)) {
                     if (!tryCanonicalize(node, nodeClass)) {
                         if (node instanceof ValueNode) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/FloatingReadPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -176,6 +176,10 @@
 
         @Override
         protected MemoryMapImpl processNode(FixedNode node, MemoryMapImpl state) {
+            if (node instanceof MemoryAccess) {
+                processAccess((MemoryAccess) node, state);
+            }
+
             if (node instanceof FloatableAccessNode && execmode == ExecutionMode.CREATE_FLOATING_READS) {
                 processFloatable((FloatableAccessNode) node, state);
             } else if (node instanceof MemoryCheckpoint.Single) {
@@ -191,6 +195,14 @@
             return state;
         }
 
+        private static void processAccess(MemoryAccess access, MemoryMapImpl state) {
+            LocationIdentity locationIdentity = access.getLocationIdentity();
+            if (locationIdentity != LocationIdentity.ANY_LOCATION) {
+                ValueNode lastLocationAccess = state.getLastLocationAccess(locationIdentity);
+                access.setLastLocationAccess(lastLocationAccess);
+            }
+        }
+
         private static void processCheckpoint(MemoryCheckpoint.Single checkpoint, MemoryMapImpl state) {
             processIdentity(checkpoint.getLocationIdentity(), checkpoint, state);
         }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -34,6 +34,7 @@
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.options.*;
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.schedule.*;
@@ -53,6 +54,12 @@
  * does the actual control-flow expansion of the remaining {@link GuardNode GuardNodes}.
  */
 public class GuardLoweringPhase extends BasePhase<MidTierContext> {
+    static class Options {
+        //@formatter:off
+        @Option(help = "")
+        public static final OptionValue<Boolean> UseGuardIdAsSpeculationId = new OptionValue<>(false);
+        //@formatter:on
+    }
 
     private static class UseImplicitNullChecks extends ScheduledNodeIterator {
 
@@ -124,9 +131,11 @@
     private static class LowerGuards extends ScheduledNodeIterator {
 
         private final Block block;
+        private boolean useGuardIdAsSpeculationId;
 
         public LowerGuards(Block block) {
             this.block = block;
+            this.useGuardIdAsSpeculationId = Options.UseGuardIdAsSpeculationId.getValue();
         }
 
         @Override
@@ -144,7 +153,8 @@
         private void lowerToIf(GuardNode guard) {
             StructuredGraph graph = guard.graph();
             AbstractBeginNode fastPath = graph.add(new BeginNode());
-            DeoptimizeNode deopt = graph.add(new DeoptimizeNode(guard.action(), guard.reason()));
+            @SuppressWarnings("deprecation")
+            DeoptimizeNode deopt = graph.add(new DeoptimizeNode(guard.action(), guard.reason(), useGuardIdAsSpeculationId ? guard.getId() : 0));
             AbstractBeginNode deoptBranch = AbstractBeginNode.begin(deopt);
             AbstractBeginNode trueSuccessor;
             AbstractBeginNode falseSuccessor;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -24,6 +24,7 @@
 
 import java.util.*;
 
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Graph.NodeChangedListener;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
@@ -44,7 +45,7 @@
 
     @Override
     protected void run(StructuredGraph graph, C context) {
-        int newNodesMark = graph.getMark();
+        Mark newNodesMark = graph.getMark();
         final Set<Node> changedNodes = new HashSet<>();
 
         NodeChangedListener listener = new NodeChangedListener() {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -32,6 +32,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.spi.*;
@@ -180,7 +181,7 @@
 
     private void doInline(GraphInfo callerGraphInfo, MethodInvocation calleeInfo, Assumptions callerAssumptions, HighTierContext context) {
         StructuredGraph callerGraph = callerGraphInfo.graph();
-        int markBeforeInlining = callerGraph.getMark();
+        Mark markBeforeInlining = callerGraph.getMark();
         InlineInfo callee = calleeInfo.callee();
         try {
             List<Node> invokeUsages = callee.invoke().asNode().usages().snapshot();
@@ -190,7 +191,7 @@
             Debug.dump(callerGraph, "after %s", callee);
 
             if (OptCanonicalizer.getValue()) {
-                int markBeforeCanonicalization = callerGraph.getMark();
+                Mark markBeforeCanonicalization = callerGraph.getMark();
                 canonicalizer.applyIncremental(callerGraph, context, invokeUsages, markBeforeInlining);
 
                 // process invokes that are possibly created during canonicalization
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -29,6 +29,7 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
@@ -49,8 +50,8 @@
     final class LoweringToolImpl implements LoweringTool {
 
         private final PhaseContext context;
-        private final GuardingNode guardAnchor;
         private final NodeBitMap activeGuards;
+        private GuardingNode guardAnchor;
         private FixedWithNextNode lastFixedNode;
         private ControlFlowGraph cfg;
 
@@ -93,6 +94,11 @@
         }
 
         @Override
+        public GuardingNode getCurrentGuardAnchor() {
+            return guardAnchor;
+        }
+
+        @Override
         public GuardingNode createNullCheckGuard(GuardedNode guardedNode, ValueNode object) {
             if (ObjectStamp.isObjectNonNull(object)) {
                 // Short cut creation of null check guard if the object is known to be non-null.
@@ -169,10 +175,10 @@
      * @throws AssertionError if the check fails
      */
     private boolean checkPostLowering(StructuredGraph graph, PhaseContext context) {
-        int expectedMark = graph.getMark();
+        Mark expectedMark = graph.getMark();
         lower(graph, context, 1);
-        int mark = graph.getMark();
-        assert mark == expectedMark : graph + ": a second round in the current lowering phase introduced these new nodes: " + graph.getNewNodes(mark).snapshot();
+        Mark mark = graph.getMark();
+        assert mark.equals(expectedMark) : graph + ": a second round in the current lowering phase introduced these new nodes: " + graph.getNewNodes(mark).snapshot();
         return true;
     }
 
@@ -198,15 +204,15 @@
      * @param preLoweringMark the graph mark before {@code node} was lowered
      * @throws AssertionError if the check fails
      */
-    private static boolean checkPostNodeLowering(Node node, LoweringToolImpl loweringTool, int preLoweringMark) {
+    private static boolean checkPostNodeLowering(Node node, LoweringToolImpl loweringTool, Mark preLoweringMark) {
         StructuredGraph graph = (StructuredGraph) node.graph();
-        int postLoweringMark = graph.getMark();
+        Mark postLoweringMark = graph.getMark();
         NodeIterable<Node> newNodesAfterLowering = graph.getNewNodes(preLoweringMark);
         for (Node n : newNodesAfterLowering) {
             if (n instanceof Lowerable) {
                 ((Lowerable) n).lower(loweringTool);
-                int mark = graph.getMark();
-                assert postLoweringMark == mark : graph + ": lowering of " + node + " produced lowerable " + n + " that should have been recursively lowered as it introduces these new nodes: " +
+                Mark mark = graph.getMark();
+                assert postLoweringMark.equals(mark) : graph + ": lowering of " + node + " produced lowerable " + n + " that should have been recursively lowered as it introduces these new nodes: " +
                                 graph.getNewNodes(postLoweringMark).snapshot();
             }
         }
@@ -236,7 +242,7 @@
             if (anchor == null) {
                 anchor = block.getBeginNode();
             }
-            process(block, activeGuards, anchor);
+            anchor = process(block, activeGuards, anchor);
 
             // Process always reached block first.
             Block alwaysReachedBlock = block.getPostdominator();
@@ -261,9 +267,9 @@
             }
         }
 
-        private void process(final Block b, final NodeBitMap activeGuards, final GuardingNode anchor) {
+        private GuardingNode process(final Block b, final NodeBitMap activeGuards, final GuardingNode startAnchor) {
 
-            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, anchor, activeGuards, b.getBeginNode(), schedule.getCFG());
+            final LoweringToolImpl loweringTool = new LoweringToolImpl(context, startAnchor, activeGuards, b.getBeginNode(), schedule.getCFG());
 
             // Lower the instructions of this block.
             List<ScheduledNode> nodes = schedule.nodesFor(b);
@@ -285,8 +291,11 @@
 
                 if (node instanceof Lowerable) {
                     assert checkUsagesAreScheduled(node);
-                    int preLoweringMark = node.graph().getMark();
+                    Mark preLoweringMark = node.graph().getMark();
                     ((Lowerable) node).lower(loweringTool);
+                    if (node == startAnchor && node.isDeleted()) {
+                        loweringTool.guardAnchor = BeginNode.prevBegin(nextNode);
+                    }
                     assert checkPostNodeLowering(node, loweringTool, preLoweringMark);
                 }
 
@@ -310,6 +319,7 @@
                     loweringTool.setLastFixedNode((FixedWithNextNode) nextLastFixed);
                 }
             }
+            return loweringTool.getCurrentGuardAnchor();
         }
 
         /**
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/ReadEliminationPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -35,7 +35,7 @@
         for (FloatingReadNode n : graph.getNodes(FloatingReadNode.class)) {
             if (isReadEliminable(n)) {
                 NodeMap<ValueNode> nodeMap = n.graph().createNodeMap();
-                ValueNode value = getValue(n, n.lastLocationAccess(), nodeMap);
+                ValueNode value = getValue(n, n.getLastLocationAccess(), nodeMap);
                 Debug.log("Eliminated memory read %1.1s and replaced with node %s", n, value);
                 graph.replaceFloating(n, value);
             }
@@ -43,7 +43,7 @@
     }
 
     private static boolean isReadEliminable(FloatingReadNode n) {
-        return isWrites(n, n.lastLocationAccess(), n.graph().createNodeBitMap());
+        return isWrites(n, n.getLastLocationAccess(), n.graph().createNodeBitMap());
     }
 
     private static boolean isWrites(FloatingReadNode n, Node lastLocationAccess, NodeBitMap visited) {
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -29,6 +29,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.Graph.DuplicationReplacement;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.VirtualState.NodeClosure;
 import com.oracle.graal.nodes.extended.*;
@@ -232,7 +233,7 @@
         private void duplicate(PhaseContext phaseContext) {
             Debug.log("tail duplication at merge %s in %s", merge, graph.method());
 
-            int startMark = graph.getMark();
+            Mark startMark = graph.getMark();
 
             ValueAnchorNode anchor = addValueAnchor();
 
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Sat Nov 09 21:34:07 2013 +0100
@@ -169,20 +169,12 @@
     @Option(help = "")
     public static final OptionValue<Boolean> PrintProfilingInformation = new OptionValue<>(false);
     @Option(help = "")
-    public static final OptionValue<Boolean> PrintIRWithLIR = new OptionValue<>(false);
-    @Option(help = "")
     public static final OptionValue<Boolean> PrintCodeBytes = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Boolean> PrintBailout = new OptionValue<>(false);
     @Option(help = "")
-    public static final OptionValue<Integer> TraceLinearScanLevel = new OptionValue<>(0);
-    @Option(help = "")
-    public static final OptionValue<Integer> TraceLIRGeneratorLevel = new OptionValue<>(0);
-    @Option(help = "")
     public static final OptionValue<Boolean> TraceEscapeAnalysis = new OptionValue<>(false);
     @Option(help = "")
-    public static final OptionValue<Integer> TraceBytecodeParserLevel = new OptionValue<>(0);
-    @Option(help = "")
     public static final OptionValue<Boolean> ExitVMOnBailout = new OptionValue<>(false);
     @Option(help = "")
     public static final OptionValue<Boolean> ExitVMOnException = new OptionValue<>(true);
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Sat Nov 09 21:34:07 2013 +0100
@@ -189,7 +189,7 @@
                 MergeNode mergeNode = (MergeNode) block.getBeginNode();
                 for (PhiNode phi : mergeNode.usages().filter(PhiNode.class)) {
                     if (phi.type() == PhiType.Memory) {
-                        LocationIdentity identity = (LocationIdentity) phi.getIdentity();
+                        LocationIdentity identity = phi.getIdentity();
                         locationKilledBy(identity, phi, currentState);
                     }
                 }
@@ -266,7 +266,7 @@
                 for (Node usage : begin.usages()) {
                     if (usage instanceof ProxyNode && ((ProxyNode) usage).type() == PhiType.Memory) {
                         ProxyNode proxy = (ProxyNode) usage;
-                        LocationIdentity identity = (LocationIdentity) proxy.getIdentity();
+                        LocationIdentity identity = proxy.getIdentity();
                         locationKilledBy(identity, proxy, exitState);
                     }
                 }
@@ -347,7 +347,7 @@
                 if (n.location().getLocationIdentity() == FINAL_LOCATION) {
                     continue;
                 }
-                Node first = n.lastLocationAccess();
+                Node first = n.getLastLocationAccess();
                 assert first != null;
 
                 Map<LocationIdentity, Node> killMap = blockToKillMapInit.get(forKillLocation(first));
@@ -424,7 +424,7 @@
         } else if (n instanceof FloatingReadNode) {
             FloatingReadNode frn = (FloatingReadNode) n;
             Debug.printf(" // from %s", frn.location().getLocationIdentity());
-            Debug.printf(", lastAccess: %s", frn.lastLocationAccess());
+            Debug.printf(", lastAccess: %s", frn.getLastLocationAccess());
             Debug.printf(", object: %s", frn.object());
         } else if (n instanceof GuardNode) {
             Debug.printf(", guard: %s", ((GuardNode) n).getGuard());
@@ -544,7 +544,7 @@
         LocationIdentity locid = n.location().getLocationIdentity();
         assert locid != FINAL_LOCATION;
 
-        Node upperBound = n.lastLocationAccess();
+        Node upperBound = n.getLastLocationAccess();
         Block upperBoundBlock = forKillLocation(upperBound);
         Block earliestBlock = earliestBlock(n);
         assert upperBoundBlock.dominates(earliestBlock) : "upper bound (" + upperBoundBlock + ") should dominate earliest (" + earliestBlock + ")";
@@ -925,7 +925,7 @@
                     FloatingReadNode frn = (FloatingReadNode) i;
                     if (frn.location().getLocationIdentity() != FINAL_LOCATION) {
                         reads.add(frn);
-                        if (nodesFor(b).contains(frn.lastLocationAccess())) {
+                        if (nodesFor(b).contains(frn.getLastLocationAccess())) {
                             assert !beforeLastLocation.isMarked(frn);
                             beforeLastLocation.mark(frn);
                         }
@@ -970,7 +970,7 @@
         for (FloatingReadNode frn : new ArrayList<>(reads)) { // TODO: change to iterator?
             LocationIdentity readLocation = frn.location().getLocationIdentity();
             assert readLocation != FINAL_LOCATION;
-            if (frn.lastLocationAccess() == node) {
+            if (frn.getLastLocationAccess() == node) {
                 assert identity == ANY_LOCATION || readLocation == identity : "location doesn't match: " + readLocation + ", " + identity;
                 beforeLastLocation.clear(frn);
             } else if (!beforeLastLocation.isMarked(frn) && (readLocation == identity || (!(node instanceof StartNode) && ANY_LOCATION == identity))) {
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Sat Nov 09 21:34:07 2013 +0100
@@ -408,7 +408,7 @@
             }
         }
         Map<Object, Object> props = new HashMap<>();
-        int firstExternalNodeId = graph.getNodeCount() + graph.getDeletedNodeCount();
+        int firstExternalNodeId = graph.getNodeCount() + graph.getTotalNodesDeleted();
         for (Node node : graph.getNodes()) {
             for (Node input : node.inputs()) {
                 if (input.isExternal()) {
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Sat Nov 09 21:34:07 2013 +0100
@@ -36,6 +36,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.internal.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
@@ -79,14 +80,23 @@
         this.snippetTemplateCache = new HashMap<>();
     }
 
+    private static final boolean UseSnippetGraphCache = Boolean.parseBoolean(System.getProperty("graal.useSnippetGraphCache", "true"));
+    private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime");
+
     public StructuredGraph getSnippet(ResolvedJavaMethod method) {
         assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName();
         assert !Modifier.isAbstract(method.getModifiers()) && !Modifier.isNative(method.getModifiers()) : "Snippet must not be abstract or native";
 
-        StructuredGraph graph = graphs.get(method);
+        StructuredGraph graph = UseSnippetGraphCache ? graphs.get(method) : null;
         if (graph == null) {
-            graphs.putIfAbsent(method, makeGraph(method, null, inliningPolicy(method), method.getAnnotation(Snippet.class).removeAllFrameStates()));
-            graph = graphs.get(method);
+            try (TimerCloseable a = SnippetPreparationTime.start()) {
+                StructuredGraph newGraph = makeGraph(method, null, inliningPolicy(method), method.getAnnotation(Snippet.class).removeAllFrameStates());
+                if (UseSnippetGraphCache) {
+                    return newGraph;
+                }
+                graphs.putIfAbsent(method, newGraph);
+                graph = graphs.get(method);
+            }
         }
         return graph;
     }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/SnippetTemplate.java	Sat Nov 09 21:34:07 2013 +0100
@@ -23,7 +23,9 @@
 package com.oracle.graal.replacements;
 
 import static com.oracle.graal.api.meta.LocationIdentity.*;
+import static com.oracle.graal.api.meta.MetaUtil.*;
 
+import java.io.*;
 import java.lang.reflect.*;
 import java.util.*;
 import java.util.concurrent.*;
@@ -33,6 +35,7 @@
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.internal.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.iterators.*;
 import com.oracle.graal.loop.*;
@@ -71,8 +74,20 @@
         protected final ResolvedJavaMethod method;
         protected final boolean[] constantParameters;
         protected final boolean[] varargsParameters;
+
+        /**
+         * Times instantiations of all templates derived form this snippet.
+         * 
+         * @see SnippetTemplate#instantiationTimer
+         */
+        private final DebugTimer instantiationTimer;
+
+        /**
+         * Counts instantiations of all templates derived from this snippet.
+         * 
+         * @see SnippetTemplate#instantiationCounter
+         */
         private final DebugMetric instantiationCounter;
-        private final DebugTimer instantiationTimer;
 
         /**
          * The parameter names, taken from the local variables table. Only used for assertion
@@ -101,6 +116,17 @@
             assert initNames();
         }
 
+        private int templateCount;
+
+        void notifyNewTemplate() {
+            templateCount++;
+            if (UseSnippetTemplateCache && templateCount > MaxTemplatesPerSnippet) {
+                PrintStream err = System.err;
+                err.printf("WARNING: Exceeded %d templates for snippet %s%n" + "         Adjust maximum with %s system property%n", MaxTemplatesPerSnippet, format("%h.%n(%p)", method),
+                                MAX_TEMPLATES_PER_SNIPPET_PROPERTY_NAME);
+            }
+        }
+
         private boolean initNames() {
             int slotIdx = 0;
             for (int i = 0; i < names.length; i++) {
@@ -332,8 +358,14 @@
         }
     }
 
-    private static final DebugTimer SnippetCreationAndSpecialization = Debug.timer("SnippetCreationAndSpecialization");
-    private static final DebugMetric SnippetSpecializations = Debug.metric("SnippetSpecializations");
+    private static final DebugTimer SnippetTemplateCreationTime = Debug.timer("SnippetTemplateCreationTime");
+    private static final DebugMetric SnippetTemplates = Debug.metric("SnippetTemplateCount");
+    private static final DebugMetric SnippetTemplatesNodeCount = Debug.metric("SnippetTemplatesNodeCount");
+    private static final DebugMetric SnippetGraphsNodeCount = Debug.metric("SnippetGraphsNodeCount");
+
+    private static final String MAX_TEMPLATES_PER_SNIPPET_PROPERTY_NAME = "graal.maxTemplatesPerSnippet";
+    private static final boolean UseSnippetTemplateCache = Boolean.parseBoolean(System.getProperty("graal.useSnippetTemplateCache", "true"));
+    private static final int MaxTemplatesPerSnippet = Integer.getInteger(MAX_TEMPLATES_PER_SNIPPET_PROPERTY_NAME, 50);
 
     /**
      * Base class for snippet classes. It provides a cache for {@link SnippetTemplate}s.
@@ -347,7 +379,11 @@
         protected AbstractTemplates(Providers providers, TargetDescription target) {
             this.providers = providers;
             this.target = target;
-            this.templates = new ConcurrentHashMap<>();
+            if (UseSnippetTemplateCache) {
+                this.templates = new ConcurrentHashMap<>();
+            } else {
+                this.templates = null;
+            }
         }
 
         /**
@@ -371,10 +407,10 @@
          * Gets a template for a given key, creating it first if necessary.
          */
         protected SnippetTemplate template(final Arguments args) {
-            SnippetTemplate template = templates.get(args.cacheKey);
+            SnippetTemplate template = UseSnippetTemplateCache ? templates.get(args.cacheKey) : null;
             if (template == null) {
-                SnippetSpecializations.increment();
-                try (TimerCloseable a = SnippetCreationAndSpecialization.start()) {
+                SnippetTemplates.increment();
+                try (TimerCloseable a = SnippetTemplateCreationTime.start()) {
                     template = Debug.scope("SnippetSpecialization", args.info.method, new Callable<SnippetTemplate>() {
 
                         @Override
@@ -382,7 +418,9 @@
                             return new SnippetTemplate(providers, args);
                         }
                     });
-                    templates.put(args.cacheKey, template);
+                    if (UseSnippetTemplateCache) {
+                        templates.put(args.cacheKey, template);
+                    }
                 }
             }
             return template;
@@ -404,11 +442,39 @@
         return false;
     }
 
+    private static String debugValueName(String category, Arguments args) {
+        if (Debug.isEnabled()) {
+            StringBuilder result = new StringBuilder(category).append('[');
+            SnippetInfo info = args.info;
+            result.append(info.method.getName()).append('(');
+            String sep = "";
+            for (int i = 0; i < info.getParameterCount(); i++) {
+                if (info.isConstantParameter(i)) {
+                    result.append(sep);
+                    if (info.names[i] != null) {
+                        result.append(info.names[i]);
+                    } else {
+                        result.append(i);
+                    }
+                    result.append('=').append(args.values[i]);
+                    sep = ", ";
+                }
+            }
+            result.append(")]");
+            return result.toString();
+
+        }
+        return null;
+    }
+
     /**
      * Creates a snippet template.
      */
     protected SnippetTemplate(final Providers providers, Arguments args) {
         StructuredGraph snippetGraph = providers.getReplacements().getSnippet(args.info.method);
+        SnippetGraphsNodeCount.add(snippetGraph.getNodeCount());
+        instantiationTimer = Debug.timer(debugValueName("SnippetTemplateInstantiationTime", args));
+        instantiationCounter = Debug.metric(debugValueName("SnippetTemplateInstantiationCount", args));
 
         ResolvedJavaMethod method = snippetGraph.method();
         Signature signature = method.getSignature();
@@ -507,7 +573,7 @@
                 LoopBeginNode loopBegin = explodeLoop.findLoopBegin();
                 if (loopBegin != null) {
                     LoopEx loop = new LoopsData(snippetCopy).loop(loopBegin);
-                    int mark = snippetCopy.getMark();
+                    Mark mark = snippetCopy.getMark();
                     LoopTransformations.fullUnroll(loop, phaseContext, new CanonicalizerPhase(true));
                     new CanonicalizerPhase(true).applyIncremental(snippetCopy, phaseContext, mark);
                 }
@@ -596,6 +662,9 @@
         this.deoptNodes = curDeoptNodes;
         this.stampNodes = curStampNodes;
         this.returnNode = retNode;
+
+        SnippetTemplatesNodeCount.add(nodes.size());
+        args.info.notifyNewTemplate();
     }
 
     private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, VarargsPlaceholderNode[] placeholders) {
@@ -676,6 +745,20 @@
     private MemoryMapNode memoryMap;
 
     /**
+     * Times instantiations of this template.
+     * 
+     * @see SnippetInfo#instantiationTimer
+     */
+    private final DebugTimer instantiationTimer;
+
+    /**
+     * Counts instantiations of this template.
+     * 
+     * @see SnippetInfo#instantiationCounter
+     */
+    private final DebugMetric instantiationCounter;
+
+    /**
      * Gets the instantiation-time bindings to this template's parameters.
      * 
      * @return the map that will be used to bind arguments to parameters when inlining this template
@@ -783,7 +866,7 @@
                 return;
             }
             for (Node usage : newNode.usages().snapshot()) {
-                if (usage instanceof FloatingReadNode && ((FloatingReadNode) usage).lastLocationAccess() == newNode) {
+                if (usage instanceof FloatingReadNode && ((FloatingReadNode) usage).getLastLocationAccess() == newNode) {
                     assert newNode.graph().isAfterFloatingReadPhase();
 
                     // lastLocationAccess points into the snippet graph. find a proper
@@ -878,14 +961,15 @@
      */
     public Map<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args) {
         assert checkSnippetKills(replacee);
-        try (TimerCloseable a = args.info.instantiationTimer.start()) {
+        try (TimerCloseable a = args.info.instantiationTimer.start(); TimerCloseable b = instantiationTimer.start()) {
             args.info.instantiationCounter.increment();
+            instantiationCounter.increment();
             // Inline the snippet nodes, replacing parameters with the given args in the process
             StartNode entryPointNode = snippet.start();
             FixedNode firstCFGNode = entryPointNode.next();
             StructuredGraph replaceeGraph = replacee.graph();
             IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
-            replacements.put(entryPointNode, replaceeGraph.start());
+            replacements.put(entryPointNode, BeginNode.prevBegin(replacee));
             Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
             Debug.dump(replaceeGraph, "After inlining snippet %s", snippet.method());
 
@@ -973,6 +1057,7 @@
         assert checkSnippetKills(replacee);
         try (TimerCloseable a = args.info.instantiationTimer.start()) {
             args.info.instantiationCounter.increment();
+            instantiationCounter.increment();
 
             // Inline the snippet nodes, replacing parameters with the given args in the process
             String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}";
@@ -981,7 +1066,7 @@
             FixedNode firstCFGNode = entryPointNode.next();
             StructuredGraph replaceeGraph = replacee.graph();
             IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
-            replacements.put(entryPointNode, replaceeGraph.start());
+            replacements.put(entryPointNode, tool.getCurrentGuardAnchor().asNode());
             Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
             Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method());
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ReverseBytesNode.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/ReverseBytesNode.java	Sat Nov 09 21:34:07 2013 +0100
@@ -38,7 +38,7 @@
 
     public ReverseBytesNode(ValueNode value) {
         super(StampFactory.forKind(value.kind()));
-        assert kind().getStackKind() == Kind.Int || kind() == Kind.Long;
+        assert kind().isNumericInteger();
         this.value = value;
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationPolicy.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationPolicy.java	Sat Nov 09 21:34:07 2013 +0100
@@ -22,96 +22,8 @@
  */
 package com.oracle.graal.truffle;
 
-import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
-
-public class CompilationPolicy {
-
-    private int invokeCounter;
-    private int originalInvokeCounter;
-    private int loopAndInvokeCounter;
-    private long prevTimestamp;
-
-    private final int compilationThreshold;
-    private final String name;
-
-    public CompilationPolicy(final int compilationThreshold, final int initialInvokeCounter, final String name) {
-        this.invokeCounter = initialInvokeCounter;
-        this.loopAndInvokeCounter = compilationThreshold;
-        this.originalInvokeCounter = compilationThreshold;
-        this.prevTimestamp = System.nanoTime();
-
-        this.compilationThreshold = compilationThreshold;
-        this.name = name;
-    }
-
-    public String getName() {
-        return this.name;
-    }
-
-    public int getInvokeCounter() {
-        return invokeCounter;
-    }
-
-    public int getOriginalInvokeCounter() {
-        return originalInvokeCounter;
-    }
-
-    public int getLoopAndInvokeCounter() {
-        return loopAndInvokeCounter;
-    }
-
-    public void compilationInvalidated() {
-        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
-        invokeCounter = invalidationReprofileCount;
-        if (TruffleFunctionInlining.getValue()) {
-            originalInvokeCounter += invalidationReprofileCount;
-        }
-    }
+public interface CompilationPolicy {
 
-    public void countInterpreterCall() {
-        invokeCounter--;
-        loopAndInvokeCounter--;
-    }
-
-    public void inlined(int minInvokesAfterInlining) {
-        invokeCounter = minInvokesAfterInlining;
-        int inliningReprofileCount = TruffleInliningReprofileCount.getValue();
-        loopAndInvokeCounter = inliningReprofileCount;
-        originalInvokeCounter = inliningReprofileCount;
-    }
-
-    public void reportLoopCount(int count) {
-        loopAndInvokeCounter = Math.max(0, loopAndInvokeCounter - count);
-    }
-
-    public void nodeReplaced() {
-        // delay compilation until tree is deemed stable enough
-        int replaceBackoff = TruffleReplaceReprofileCount.getValue();
-        if (loopAndInvokeCounter < replaceBackoff) {
-            loopAndInvokeCounter = replaceBackoff;
-        }
-    }
+    boolean shouldCompile(CompilationProfile profile);
 
-    public boolean compileOrInline() {
-        if (invokeCounter <= 0 && loopAndInvokeCounter <= 0) {
-            if (TruffleUseTimeForCompilationDecision.getValue()) {
-                long timestamp = System.nanoTime();
-                long timespan = (timestamp - prevTimestamp);
-                if (timespan < (TruffleCompilationDecisionTime.getValue())) {
-                    return true;
-                }
-                this.loopAndInvokeCounter = compilationThreshold;
-                this.originalInvokeCounter = compilationThreshold;
-                this.prevTimestamp = timestamp;
-                if (TruffleCompilationDecisionTimePrintFail.getValue()) {
-                    // Checkstyle: stop
-                    System.out.println(name + ": timespan  " + (timespan / 1000000) + " ms  larger than threshold");
-                    // Checkstyle: resume
-                }
-            } else {
-                return true;
-            }
-        }
-        return false;
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/CompilationProfile.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,125 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.truffle;
+
+import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
+
+public class CompilationProfile {
+
+    private static final int MIN_INVOKES_AFTER_INLINING = 2;
+
+    private int invokeCounter;
+    private int originalInvokeCounter;
+    private int loopAndInvokeCounter;
+    /**
+     * Number of times an installed code for this tree was invalidated.
+     */
+    private int invalidationCount;
+
+    /**
+     * Number of times a node was replaced in this tree.
+     */
+    private int nodeReplaceCount;
+
+    private long previousTimestamp;
+
+    private final int compilationThreshold;
+    private final String name;
+
+    public CompilationProfile(final int compilationThreshold, final int initialInvokeCounter, final String name) {
+        this.invokeCounter = initialInvokeCounter;
+        this.loopAndInvokeCounter = compilationThreshold;
+        this.originalInvokeCounter = compilationThreshold;
+        this.previousTimestamp = System.nanoTime();
+
+        this.compilationThreshold = compilationThreshold;
+        this.name = name;
+    }
+
+    public long getPreviousTimestamp() {
+        return previousTimestamp;
+    }
+
+    public String getName() {
+        return this.name;
+    }
+
+    public int getInvalidationCount() {
+        return invalidationCount;
+    }
+
+    public int getNodeReplaceCount() {
+        return nodeReplaceCount;
+    }
+
+    public int getInvokeCounter() {
+        return invokeCounter;
+    }
+
+    public int getOriginalInvokeCounter() {
+        return originalInvokeCounter;
+    }
+
+    public int getLoopAndInvokeCounter() {
+        return loopAndInvokeCounter;
+    }
+
+    void reportTiminingFailed(long timestamp) {
+        this.loopAndInvokeCounter = compilationThreshold;
+        this.originalInvokeCounter = compilationThreshold;
+        this.previousTimestamp = timestamp;
+    }
+
+    void reportInvalidated() {
+        invalidationCount++;
+        int invalidationReprofileCount = TruffleInvalidationReprofileCount.getValue();
+        invokeCounter = invalidationReprofileCount;
+        originalInvokeCounter += invalidationReprofileCount;
+    }
+
+    void reportInterpreterCall() {
+        invokeCounter--;
+        loopAndInvokeCounter--;
+    }
+
+    void reportInliningPerformed() {
+        invokeCounter = MIN_INVOKES_AFTER_INLINING;
+        int inliningReprofileCount = TruffleInliningReprofileCount.getValue();
+        loopAndInvokeCounter = inliningReprofileCount;
+        originalInvokeCounter = inliningReprofileCount;
+    }
+
+    void reportLoopCount(int count) {
+        loopAndInvokeCounter = Math.max(0, loopAndInvokeCounter - count);
+    }
+
+    void reportNodeReplaced() {
+        nodeReplaceCount++;
+        // delay compilation until tree is deemed stable enough
+        int replaceBackoff = TruffleReplaceReprofileCount.getValue();
+        if (loopAndInvokeCounter < replaceBackoff) {
+            loopAndInvokeCounter = replaceBackoff;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/DefaultCompilationPolicy.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,31 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.truffle;
+
+public class DefaultCompilationPolicy implements CompilationPolicy {
+
+    public boolean shouldCompile(CompilationProfile profile) {
+        return profile.getInvokeCounter() <= 0 && profile.getLoopAndInvokeCounter() <= 0;
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/OptimizedCallTarget.java	Sat Nov 09 21:34:07 2013 +0100
@@ -41,36 +41,35 @@
 public final class OptimizedCallTarget extends DefaultCallTarget implements FrameFactory, LoopCountReceiver, ReplaceObserver {
 
     private static final PrintStream OUT = TTY.out().out();
-    private static final int MIN_INVOKES_AFTER_INLINING = 2;
+
+    private InstalledCode installedCode;
+    private Future<InstalledCode> installedCodeTask;
+    private final TruffleCompiler compiler;
+    private final CompilationProfile compilationProfile;
+    private final CompilationPolicy compilationPolicy;
+    private final TruffleInlining inlining;
+    private boolean compilationEnabled;
+    private int callCount;
 
     protected OptimizedCallTarget(RootNode rootNode, FrameDescriptor descriptor, TruffleCompiler compiler, int invokeCounter, int compilationThreshold) {
         super(rootNode, descriptor);
         this.compiler = compiler;
-        this.compilationPolicy = new CompilationPolicy(compilationThreshold, invokeCounter, rootNode.toString());
+        this.compilationProfile = new CompilationProfile(compilationThreshold, invokeCounter, rootNode.toString());
+        this.inlining = new TruffleInliningImpl();
         this.rootNode.setCallTarget(this);
 
+        if (TruffleUseTimeForCompilationDecision.getValue()) {
+            compilationPolicy = new TimedCompilationPolicy();
+        } else {
+            compilationPolicy = new DefaultCompilationPolicy();
+        }
+        this.compilationEnabled = true;
+
         if (TruffleCallTargetProfiling.getValue()) {
             registerCallTarget(this);
         }
     }
 
-    private InstalledCode installedCode;
-    private Future<InstalledCode> installedCodeTask;
-    private final TruffleCompiler compiler;
-    private final CompilationPolicy compilationPolicy;
-    private boolean disableCompilation;
-    private int callCount;
-
-    /**
-     * Number of times an installed code for this tree was invalidated.
-     */
-    private int invalidationCount;
-
-    /**
-     * Number of times a node was replaced in this tree.
-     */
-    private int nodeReplaceCount;
-
     @Override
     public Object call(PackedFrame caller, Arguments args) {
         return callHelper(caller, args);
@@ -99,6 +98,10 @@
         }
     }
 
+    public CompilationProfile getCompilationProfile() {
+        return compilationProfile;
+    }
+
     private Object compiledCodeInvalidated(PackedFrame caller, Arguments args) {
         invalidate();
         return call(caller, args);
@@ -109,10 +112,10 @@
         if (m != null) {
             CompilerAsserts.neverPartOfCompilation();
             installedCode = null;
-            invalidationCount++;
-            compilationPolicy.compilationInvalidated();
+            compilationProfile.reportInvalidated();
             if (TraceTruffleCompilation.getValue()) {
-                OUT.printf("[truffle] invalidated %-48s |Inv# %d                                     |Replace# %d\n", rootNode, invalidationCount, nodeReplaceCount);
+                OUT.printf("[truffle] invalidated %-48s |Inv# %d                                     |Replace# %d\n", rootNode, compilationProfile.getInvalidationCount(),
+                                compilationProfile.getNodeReplaceCount());
             }
         }
 
@@ -120,50 +123,63 @@
         if (task != null) {
             task.cancel(true);
             this.installedCodeTask = null;
-            compilationPolicy.compilationInvalidated();
+            compilationProfile.reportInvalidated();
         }
     }
 
     private Object interpreterCall(PackedFrame caller, Arguments args) {
         CompilerAsserts.neverPartOfCompilation();
-        compilationPolicy.countInterpreterCall();
-        if (disableCompilation || !compilationPolicy.compileOrInline()) {
-            return executeHelper(caller, args);
-        } else {
-            return compileOrInline(caller, args);
+        compilationProfile.reportInterpreterCall();
+        if (compilationEnabled && shouldCompile()) {
+            if (isCompiling()) {
+                return waitForCompilation(caller, args);
+            }
+            boolean inlined = shouldInline() && inline();
+            if (!inlined) {
+                compile();
+            }
+        }
+        return executeHelper(caller, args);
+    }
+
+    private boolean shouldCompile() {
+        return compilationPolicy.shouldCompile(compilationProfile);
+    }
+
+    private static boolean shouldInline() {
+        return TruffleFunctionInlining.getValue();
+    }
+
+    private boolean isCompiling() {
+        if (installedCodeTask != null) {
+            if (installedCodeTask.isCancelled()) {
+                installedCodeTask = null;
+                return false;
+            }
+            return true;
+        }
+        return false;
+    }
+
+    public void compile() {
+        this.installedCodeTask = compiler.compile(this);
+        if (!TruffleBackgroundCompilation.getValue()) {
+            installedCode = receiveInstalledCode();
         }
     }
 
-    private Object compileOrInline(PackedFrame caller, Arguments args) {
-        if (installedCodeTask != null) {
-            // There is already a compilation running.
-            if (installedCodeTask.isCancelled()) {
-                installedCodeTask = null;
-            } else {
-                if (installedCodeTask.isDone()) {
-                    receiveInstalledCode();
-                }
-                return executeHelper(caller, args);
-            }
+    private Object waitForCompilation(PackedFrame caller, Arguments args) {
+        if (installedCodeTask.isDone()) {
+            installedCode = receiveInstalledCode();
         }
-
-        if (TruffleFunctionInlining.getValue() && inline()) {
-            compilationPolicy.inlined(MIN_INVOKES_AFTER_INLINING);
-            return call(caller, args);
-        } else {
-            compile();
-            return executeHelper(caller, args);
-        }
+        return executeHelper(caller, args);
     }
 
-    private void receiveInstalledCode() {
+    private InstalledCode receiveInstalledCode() {
         try {
-            this.installedCode = installedCodeTask.get();
-            if (TruffleCallTargetProfiling.getValue()) {
-                resetProfiling();
-            }
+            return installedCodeTask.get();
         } catch (InterruptedException | ExecutionException e) {
-            disableCompilation = true;
+            compilationEnabled = false;
             OUT.printf("[truffle] opt failed %-48s  %s\n", rootNode, e.getMessage());
             if (e.getCause() instanceof BailoutException) {
                 // Bailout => move on.
@@ -175,21 +191,21 @@
                     System.exit(-1);
                 }
             }
+            return null;
         }
-        installedCodeTask = null;
     }
 
+    /**
+     * Forces inlining whether or not function inlining is enabled.
+     * 
+     * @return true if an inlining was performed
+     */
     public boolean inline() {
-        CompilerAsserts.neverPartOfCompilation();
-        return new InliningHelper(this).inline();
-    }
-
-    public void compile() {
-        CompilerAsserts.neverPartOfCompilation();
-        this.installedCodeTask = compiler.compile(this);
-        if (!TruffleBackgroundCompilation.getValue()) {
-            receiveInstalledCode();
+        boolean result = inlining.performInlining(this);
+        if (result) {
+            compilationProfile.reportInliningPerformed();
         }
+        return result;
     }
 
     public Object executeHelper(PackedFrame caller, Arguments args) {
@@ -208,198 +224,13 @@
 
     @Override
     public void reportLoopCount(int count) {
-        compilationPolicy.reportLoopCount(count);
+        compilationProfile.reportLoopCount(count);
     }
 
     @Override
     public void nodeReplaced() {
-        nodeReplaceCount++;
+        compilationProfile.reportNodeReplaced();
         invalidate();
-        compilationPolicy.nodeReplaced();
-    }
-
-    private static class InliningHelper {
-
-        private final OptimizedCallTarget target;
-
-        public InliningHelper(OptimizedCallTarget target) {
-            this.target = target;
-        }
-
-        public boolean inline() {
-            final InliningPolicy policy = new InliningPolicy(target);
-            if (!policy.continueInlining()) {
-                if (TraceTruffleInliningDetails.getValue()) {
-                    List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
-                    if (!inlinableCallSites.isEmpty()) {
-                        OUT.printf("[truffle] inlining hit caller size limit (%3d >= %3d).%3d remaining call sites in %s:\n", policy.callerNodeCount, TruffleInliningMaxCallerSize.getValue(),
-                                        inlinableCallSites.size(), target.getRootNode());
-                        policy.sortByRelevance(inlinableCallSites);
-                        printCallSiteInfo(policy, inlinableCallSites, "");
-                    }
-                }
-                return false;
-            }
-
-            List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
-            if (inlinableCallSites.isEmpty()) {
-                return false;
-            }
-
-            policy.sortByRelevance(inlinableCallSites);
-
-            boolean inlined = false;
-            for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
-                if (!policy.isWorthInlining(inlinableCallSite)) {
-                    break;
-                }
-                if (inlinableCallSite.getCallSite().inline(target)) {
-                    if (TraceTruffleInlining.getValue()) {
-                        printCallSiteInfo(policy, inlinableCallSite, "inlined");
-                    }
-                    inlined = true;
-                    break;
-                }
-            }
-
-            if (inlined) {
-                for (InlinableCallSiteInfo callSite : inlinableCallSites) {
-                    callSite.getCallSite().resetCallCount();
-                }
-            } else {
-                if (TraceTruffleInliningDetails.getValue()) {
-                    OUT.printf("[truffle] inlining stopped.%3d remaining call sites in %s:\n", inlinableCallSites.size(), target.getRootNode());
-                    printCallSiteInfo(policy, inlinableCallSites, "");
-                }
-            }
-
-            return inlined;
-        }
-
-        private static void printCallSiteInfo(InliningPolicy policy, List<InlinableCallSiteInfo> inlinableCallSites, String msg) {
-            for (InlinableCallSiteInfo candidate : inlinableCallSites) {
-                printCallSiteInfo(policy, candidate, msg);
-            }
-        }
-
-        private static void printCallSiteInfo(InliningPolicy policy, InlinableCallSiteInfo callSite, String msg) {
-            String calls = String.format("%4s/%4s", callSite.getCallCount(), policy.callerInvocationCount);
-            String nodes = String.format("%3s/%3s", callSite.getInlineNodeCount(), policy.callerNodeCount);
-            OUT.printf("[truffle] %-9s %-50s |Nodes %6s |Calls %6s %7.3f |%s\n", msg, callSite.getCallSite(), nodes, calls, policy.metric(callSite), callSite.getCallSite().getCallTarget());
-        }
-
-        private static final class InliningPolicy {
-
-            private final int callerNodeCount;
-            private final int callerInvocationCount;
-
-            public InliningPolicy(OptimizedCallTarget caller) {
-                this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
-                this.callerInvocationCount = caller.compilationPolicy.getOriginalInvokeCounter();
-            }
-
-            public boolean continueInlining() {
-                return callerNodeCount < TruffleInliningMaxCallerSize.getValue();
-            }
-
-            public boolean isWorthInlining(InlinableCallSiteInfo callSite) {
-                return callSite.getInlineNodeCount() <= TruffleInliningMaxCalleeSize.getValue() && callSite.getInlineNodeCount() + callerNodeCount <= TruffleInliningMaxCallerSize.getValue() &&
-                                callSite.getCallCount() > 0 && callSite.getRecursiveDepth() < TruffleInliningMaxRecursiveDepth.getValue() &&
-                                (frequency(callSite) >= TruffleInliningMinFrequency.getValue() || callSite.getInlineNodeCount() <= TruffleInliningTrivialSize.getValue());
-            }
-
-            public double metric(InlinableCallSiteInfo callSite) {
-                double cost = callSite.getInlineNodeCount();
-                double metric = frequency(callSite) / cost;
-                return metric;
-            }
-
-            private double frequency(InlinableCallSiteInfo callSite) {
-                return (double) callSite.getCallCount() / (double) callerInvocationCount;
-            }
-
-            public void sortByRelevance(List<InlinableCallSiteInfo> inlinableCallSites) {
-                Collections.sort(inlinableCallSites, new Comparator<InlinableCallSiteInfo>() {
-
-                    @Override
-                    public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
-                        int result = (isWorthInlining(cs2) ? 1 : 0) - (isWorthInlining(cs1) ? 1 : 0);
-                        if (result == 0) {
-                            return Double.compare(metric(cs2), metric(cs1));
-                        }
-                        return result;
-                    }
-                });
-            }
-        }
-
-        private static final class InlinableCallSiteInfo {
-
-            private final InlinableCallSite callSite;
-            private final int callCount;
-            private final int nodeCount;
-            private final int recursiveDepth;
-
-            public InlinableCallSiteInfo(InlinableCallSite callSite) {
-                this.callSite = callSite;
-                this.callCount = callSite.getCallCount();
-                this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
-                this.recursiveDepth = calculateRecursiveDepth();
-            }
-
-            public int getRecursiveDepth() {
-                return recursiveDepth;
-            }
-
-            private int calculateRecursiveDepth() {
-                int depth = 0;
-                Node parent = ((Node) callSite).getParent();
-                while (!(parent instanceof RootNode)) {
-                    assert parent != null;
-                    if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
-                        depth++;
-                    }
-                    parent = parent.getParent();
-                }
-                if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
-                    depth++;
-                }
-                return depth;
-            }
-
-            public InlinableCallSite getCallSite() {
-                return callSite;
-            }
-
-            public int getCallCount() {
-                return callCount;
-            }
-
-            public int getInlineNodeCount() {
-                return nodeCount;
-            }
-        }
-
-        private static List<InlinableCallSiteInfo> getInlinableCallSites(final DefaultCallTarget target) {
-            final ArrayList<InlinableCallSiteInfo> inlinableCallSites = new ArrayList<>();
-            target.getRootNode().accept(new NodeVisitor() {
-
-                @Override
-                public boolean visit(Node node) {
-                    if (node instanceof InlinableCallSite) {
-                        inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
-                    }
-                    return true;
-                }
-            });
-            return inlinableCallSites;
-        }
-    }
-
-    private static void resetProfiling() {
-        for (OptimizedCallTarget callTarget : OptimizedCallTarget.callTargets.keySet()) {
-            callTarget.callCount = 0;
-        }
     }
 
     private static void printProfiling() {
@@ -425,19 +256,19 @@
                 continue;
             }
 
-            int notInlinedCallSiteCount = InliningHelper.getInlinableCallSites(callTarget).size();
+            int notInlinedCallSiteCount = TruffleInliningImpl.getInlinableCallSites(callTarget).size();
             int nodeCount = NodeUtil.countNodes(callTarget.rootNode);
             int inlinedCallSiteCount = NodeUtil.countNodes(callTarget.rootNode, InlinedCallSite.class);
             String comment = callTarget.installedCode == null ? " int" : "";
-            comment += callTarget.disableCompilation ? " fail" : "";
+            comment += callTarget.compilationEnabled ? "" : " fail";
             OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d%s\n", callTarget.getRootNode(), callTarget.callCount, inlinedCallSiteCount, notInlinedCallSiteCount, nodeCount,
-                            callTarget.invalidationCount, comment);
+                            callTarget.getCompilationProfile().getInvalidationCount(), comment);
 
             totalCallCount += callTarget.callCount;
             totalInlinedCallSiteCount += inlinedCallSiteCount;
             totalNotInlinedCallSiteCount += notInlinedCallSiteCount;
             totalNodeCount += nodeCount;
-            totalInvalidationCount += callTarget.invalidationCount;
+            totalInvalidationCount += callTarget.getCompilationProfile().getInvalidationCount();
         }
         OUT.printf("%-50s | %10d | %15d | %15d | %10d | %3d\n", "Total", totalCallCount, totalInlinedCallSiteCount, totalNotInlinedCallSiteCount, totalNodeCount, totalInvalidationCount);
     }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Sat Nov 09 21:34:07 2013 +0100
@@ -34,6 +34,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.debug.internal.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.java.*;
@@ -211,7 +212,7 @@
 
                     if (inlineGraph != null) {
                         int nodeCountBefore = graph.getNodeCount();
-                        int mark = graph.getMark();
+                        Mark mark = graph.getMark();
                         InliningUtil.inline(methodCallTargetNode.invoke(), inlineGraph, false);
                         if (Debug.isDumpEnabled()) {
                             int nodeCountAfter = graph.getNodeCount();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TimedCompilationPolicy.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.truffle;
+
+import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
+
+public class TimedCompilationPolicy extends DefaultCompilationPolicy {
+
+    @Override
+    public boolean shouldCompile(CompilationProfile profile) {
+        if (super.shouldCompile(profile)) {
+            long timestamp = System.nanoTime();
+            long prevTimestamp = profile.getPreviousTimestamp();
+            long timespan = (timestamp - prevTimestamp);
+            if (timespan < (TruffleCompilationDecisionTime.getValue())) {
+                return true;
+            }
+            // TODO shouldCompile should not modify the compilation profile
+            // maybe introduce another method?
+            profile.reportTiminingFailed(timestamp);
+            if (TruffleCompilationDecisionTimePrintFail.getValue()) {
+                // Checkstyle: stop
+                System.out.println(profile.getName() + ": timespan  " + (timespan / 1000000) + " ms  larger than threshold");
+                // Checkstyle: resume
+            }
+        }
+        return false;
+    }
+
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCache.java	Sat Nov 09 21:34:07 2013 +0100
@@ -32,6 +32,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.Graph.Mark;
 import com.oracle.graal.graph.Node;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.java.*;
@@ -114,7 +115,7 @@
                 CanonicalizerPhase canonicalizerPhase = new CanonicalizerPhase(!AOTCompilation.getValue());
                 PartialEscapePhase partialEscapePhase = new PartialEscapePhase(false, canonicalizerPhase);
 
-                int mark = 0;
+                Mark mark = null;
                 while (true) {
 
                     partialEscapePhase.apply(graph, phaseContext);
@@ -128,7 +129,7 @@
 
                     boolean inliningProgress = false;
                     for (MethodCallTargetNode methodCallTarget : graph.getNodes(MethodCallTargetNode.class)) {
-                        if (graph.getMark() != mark) {
+                        if (graph.getMark().equals(mark)) {
                             // Make sure macro substitutions such as
                             // CompilerDirectives.transferToInterpreter get processed first.
                             for (Node newNode : graph.getNewNodes(mark)) {
@@ -153,7 +154,7 @@
                                 }
                             }
                             List<ValueNode> argumentSnapshot = methodCallTarget.arguments().snapshot();
-                            int beforeInvokeMark = graph.getMark();
+                            Mark beforeInvokeMark = graph.getMark();
                             expandInvoke(methodCallTarget);
                             for (Node arg : argumentSnapshot) {
                                 if (arg != null && arg.recordsUsages()) {
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerImpl.java	Sat Nov 09 21:34:07 2013 +0100
@@ -160,12 +160,16 @@
         if (compiledMethod == null) {
             throw new BailoutException("Could not install method, code cache is full!");
         }
+        if (!compiledMethod.isValid()) {
+            return null;
+        }
 
         if (TraceTruffleCompilation.getValue()) {
             int nodeCountTruffle = NodeUtil.countNodes(compilable.getRootNode());
-            OUT.printf("[truffle] optimized %-50s %d |Nodes %7d |Time %5.0f(%4.0f+%-4.0f)ms |Nodes %5d/%5d |CodeSize %d\n", compilable.getRootNode(), compilable.hashCode(), nodeCountTruffle,
+            byte[] code = compiledMethod.getCode();
+            OUT.printf("[truffle] optimized %-50s %x |Nodes %7d |Time %5.0f(%4.0f+%-4.0f)ms |Nodes %5d/%5d |CodeSize %d\n", compilable.getRootNode(), compilable.hashCode(), nodeCountTruffle,
                             (timeCompilationFinished - timeCompilationStarted) / 1e6, (timePartialEvaluationFinished - timeCompilationStarted) / 1e6,
-                            (timeCompilationFinished - timePartialEvaluationFinished) / 1e6, nodeCountPartialEval, nodeCountLowered, compiledMethod.getCode().length);
+                            (timeCompilationFinished - timePartialEvaluationFinished) / 1e6, nodeCountPartialEval, nodeCountLowered, code != null ? code.length : 0);
         }
         return compiledMethod;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInlining.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,28 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.truffle;
+
+public interface TruffleInlining {
+
+    boolean performInlining(OptimizedCallTarget callTarget);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleInliningImpl.java	Sat Nov 09 21:34:07 2013 +0100
@@ -0,0 +1,207 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.truffle;
+
+import static com.oracle.graal.truffle.TruffleCompilerOptions.*;
+
+import java.io.*;
+import java.util.*;
+
+import com.oracle.graal.debug.*;
+import com.oracle.truffle.api.impl.*;
+import com.oracle.truffle.api.nodes.*;
+
+class TruffleInliningImpl implements TruffleInlining {
+
+    private static final PrintStream OUT = TTY.out().out();
+
+    @Override
+    public boolean performInlining(OptimizedCallTarget target) {
+        final InliningPolicy policy = new InliningPolicy(target);
+        if (!policy.continueInlining()) {
+            if (TraceTruffleInliningDetails.getValue()) {
+                List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
+                if (!inlinableCallSites.isEmpty()) {
+                    OUT.printf("[truffle] inlining hit caller size limit (%3d >= %3d).%3d remaining call sites in %s:\n", policy.callerNodeCount, TruffleInliningMaxCallerSize.getValue(),
+                                    inlinableCallSites.size(), target.getRootNode());
+                    policy.sortByRelevance(inlinableCallSites);
+                    printCallSiteInfo(policy, inlinableCallSites, "");
+                }
+            }
+            return false;
+        }
+
+        List<InlinableCallSiteInfo> inlinableCallSites = getInlinableCallSites(target);
+        if (inlinableCallSites.isEmpty()) {
+            return false;
+        }
+
+        policy.sortByRelevance(inlinableCallSites);
+
+        boolean inlined = false;
+        for (InlinableCallSiteInfo inlinableCallSite : inlinableCallSites) {
+            if (!policy.isWorthInlining(inlinableCallSite)) {
+                break;
+            }
+            if (inlinableCallSite.getCallSite().inline(target)) {
+                if (TraceTruffleInlining.getValue()) {
+                    printCallSiteInfo(policy, inlinableCallSite, "inlined");
+                }
+                inlined = true;
+                break;
+            }
+        }
+
+        if (inlined) {
+            for (InlinableCallSiteInfo callSite : inlinableCallSites) {
+                callSite.getCallSite().resetCallCount();
+            }
+        } else {
+            if (TraceTruffleInliningDetails.getValue()) {
+                OUT.printf("[truffle] inlining stopped.%3d remaining call sites in %s:\n", inlinableCallSites.size(), target.getRootNode());
+                printCallSiteInfo(policy, inlinableCallSites, "");
+            }
+        }
+
+        return inlined;
+    }
+
+    private static void printCallSiteInfo(InliningPolicy policy, List<InlinableCallSiteInfo> inlinableCallSites, String msg) {
+        for (InlinableCallSiteInfo candidate : inlinableCallSites) {
+            printCallSiteInfo(policy, candidate, msg);
+        }
+    }
+
+    private static void printCallSiteInfo(InliningPolicy policy, InlinableCallSiteInfo callSite, String msg) {
+        String calls = String.format("%4s/%4s", callSite.getCallCount(), policy.callerInvocationCount);
+        String nodes = String.format("%3s/%3s", callSite.getInlineNodeCount(), policy.callerNodeCount);
+        OUT.printf("[truffle] %-9s %-50s |Nodes %6s |Calls %6s %7.3f |%s\n", msg, callSite.getCallSite(), nodes, calls, policy.metric(callSite), callSite.getCallSite().getCallTarget());
+    }
+
+    private static final class InliningPolicy {
+
+        private final int callerNodeCount;
+        private final int callerInvocationCount;
+
+        public InliningPolicy(OptimizedCallTarget caller) {
+            this.callerNodeCount = NodeUtil.countNodes(caller.getRootNode());
+            this.callerInvocationCount = caller.getCompilationProfile().getOriginalInvokeCounter();
+        }
+
+        public boolean continueInlining() {
+            return callerNodeCount < TruffleInliningMaxCallerSize.getValue();
+        }
+
+        public boolean isWorthInlining(InlinableCallSiteInfo callSite) {
+            return callSite.getInlineNodeCount() <= TruffleInliningMaxCalleeSize.getValue() && callSite.getInlineNodeCount() + callerNodeCount <= TruffleInliningMaxCallerSize.getValue() &&
+                            callSite.getCallCount() > 0 && callSite.getRecursiveDepth() < TruffleInliningMaxRecursiveDepth.getValue() &&
+                            (frequency(callSite) >= TruffleInliningMinFrequency.getValue() || callSite.getInlineNodeCount() <= TruffleInliningTrivialSize.getValue());
+        }
+
+        public double metric(InlinableCallSiteInfo callSite) {
+            double cost = callSite.getInlineNodeCount();
+            double metric = frequency(callSite) / cost;
+            return metric;
+        }
+
+        private double frequency(InlinableCallSiteInfo callSite) {
+            return (double) callSite.getCallCount() / (double) callerInvocationCount;
+        }
+
+        public void sortByRelevance(List<InlinableCallSiteInfo> inlinableCallSites) {
+            Collections.sort(inlinableCallSites, new Comparator<InlinableCallSiteInfo>() {
+
+                @Override
+                public int compare(InlinableCallSiteInfo cs1, InlinableCallSiteInfo cs2) {
+                    int result = (isWorthInlining(cs2) ? 1 : 0) - (isWorthInlining(cs1) ? 1 : 0);
+                    if (result == 0) {
+                        return Double.compare(metric(cs2), metric(cs1));
+                    }
+                    return result;
+                }
+            });
+        }
+    }
+
+    private static final class InlinableCallSiteInfo {
+
+        private final InlinableCallSite callSite;
+        private final int callCount;
+        private final int nodeCount;
+        private final int recursiveDepth;
+
+        public InlinableCallSiteInfo(InlinableCallSite callSite) {
+            this.callSite = callSite;
+            this.callCount = callSite.getCallCount();
+            this.nodeCount = NodeUtil.countNodes(callSite.getInlineTree());
+            this.recursiveDepth = calculateRecursiveDepth();
+        }
+
+        public int getRecursiveDepth() {
+            return recursiveDepth;
+        }
+
+        private int calculateRecursiveDepth() {
+            int depth = 0;
+            Node parent = ((Node) callSite).getParent();
+            while (!(parent instanceof RootNode)) {
+                assert parent != null;
+                if (parent instanceof InlinedCallSite && ((InlinedCallSite) parent).getCallTarget() == callSite.getCallTarget()) {
+                    depth++;
+                }
+                parent = parent.getParent();
+            }
+            if (((RootNode) parent).getCallTarget() == callSite.getCallTarget()) {
+                depth++;
+            }
+            return depth;
+        }
+
+        public InlinableCallSite getCallSite() {
+            return callSite;
+        }
+
+        public int getCallCount() {
+            return callCount;
+        }
+
+        public int getInlineNodeCount() {
+            return nodeCount;
+        }
+    }
+
+    static List<InlinableCallSiteInfo> getInlinableCallSites(final DefaultCallTarget target) {
+        final ArrayList<InlinableCallSiteInfo> inlinableCallSites = new ArrayList<>();
+        target.getRootNode().accept(new NodeVisitor() {
+
+            @Override
+            public boolean visit(Node node) {
+                if (node instanceof InlinableCallSite) {
+                    inlinableCallSites.add(new InlinableCallSiteInfo((InlinableCallSite) node));
+                }
+                return true;
+            }
+        });
+        return inlinableCallSites;
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/AssumptionValidAssumption.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/AssumptionValidAssumption.java	Sat Nov 09 21:34:07 2013 +0100
@@ -25,17 +25,37 @@
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.truffle.*;
 
-public class AssumptionValidAssumption extends Assumptions.Assumption {
+public final class AssumptionValidAssumption extends Assumptions.Assumption {
 
     private static final long serialVersionUID = 2010244979610891262L;
 
-    private OptimizedAssumption assumption;
+    private final OptimizedAssumption assumption;
 
     public AssumptionValidAssumption(OptimizedAssumption assumption) {
         this.assumption = assumption;
+        assert assumption != null;
     }
 
     public OptimizedAssumption getAssumption() {
         return assumption;
     }
+
+    @Override
+    public int hashCode() {
+        return 31 + assumption.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (obj instanceof AssumptionValidAssumption) {
+            AssumptionValidAssumption other = (AssumptionValidAssumption) obj;
+            return other.assumption == this.assumption;
+        }
+        return false;
+    }
+
+    @Override
+    public String toString() {
+        return assumption.toString();
+    }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Sat Nov 09 21:33:31 2013 +0100
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/nodes/Node.java	Sat Nov 09 21:34:07 2013 +0100
@@ -24,6 +24,7 @@
  */
 package com.oracle.truffle.api.nodes;
 
+import java.io.*;
 import java.lang.annotation.*;
 import java.util.*;
 
@@ -184,6 +185,7 @@
         if (!NodeUtil.replaceChild(this.parent, this, newNode)) {
             fixupTree();
         }
+        reportReplace();
         return newNode;
     }
 
@@ -193,7 +195,7 @@
      * This is a rather expensive operation but rare to occur.
      */
     private void fixupTree() {
-        Node rootNode = NodeUtil.findParent(this, RootNode.class);
+        Node rootNode = getRootNode();
         if (rootNode == null) {
             throw new UnsupportedOperationException("Tree does not have a root node.");
         }
@@ -243,6 +245,15 @@
         return false;
     }
 
+    private void reportReplace() {
+        RootNode rootNode = getRootNode();
+        if (rootNode != null) {
+            if (rootNode.getCallTarget() instanceof ReplaceObserver) {
+                ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced();
+            }
+        }
+    }
+
     /**
      * Intended to be implemented by subclasses of {@link Node} to receive a notification when the
      * node is rewritten. This method is invoked before the actual replace has happened.
@@ -251,37 +262,34 @@
      * @param reason the reason the replace supplied
      */
     protected void onReplace(Node newNode, String reason) {
-        RootNode rootNode = NodeUtil.findParent(this, RootNode.class);
-        if (rootNode != null) {
-            if (rootNode.getCallTarget() instanceof ReplaceObserver) {
-                ((ReplaceObserver) rootNode.getCallTarget()).nodeReplaced();
+        if (TruffleOptions.TraceRewrites) {
+            traceRewrite(newNode, reason);
+        }
+    }
+
+    private void traceRewrite(Node newNode, String reason) {
+        Class<? extends Node> from = getClass();
+        Class<? extends Node> to = newNode.getClass();
+
+        if (TruffleOptions.TraceRewritesFilterFromKind != null) {
+            if (filterByKind(from, TruffleOptions.TraceRewritesFilterFromKind)) {
+                return;
             }
         }
-        if (TruffleOptions.TraceRewrites) {
-            Class<? extends Node> from = getClass();
-            Class<? extends Node> to = newNode.getClass();
 
-            if (TruffleOptions.TraceRewritesFilterFromKind != null) {
-                if (filterByKind(from, TruffleOptions.TraceRewritesFilterFromKind)) {
-                    return;
-                }
-            }
-
-            if (TruffleOptions.TraceRewritesFilterToKind != null) {
-                if (filterByKind(to, TruffleOptions.TraceRewritesFilterToKind)) {
-                    return;
-                }
-            }
-
-            String filter = TruffleOptions.TraceRewritesFilterClass;
-            if (filter != null && (filterByContainsClassName(from, filter) || filterByContainsClassName(to, filter))) {
+        if (TruffleOptions.TraceRewritesFilterToKind != null) {
+            if (filterByKind(to, TruffleOptions.TraceRewritesFilterToKind)) {
                 return;
             }
+        }
 
-            // CheckStyle: stop system..print check
-            System.out.printf("[truffle]   rewrite %-50s |From %-40s |To %-40s |Reason %s.%n", this.toString(), formatNodeInfo(from), formatNodeInfo(to), reason);
-            // CheckStyle: resume system..print check
+        String filter = TruffleOptions.TraceRewritesFilterClass;
+        if (filter != null && (filterByContainsClassName(from, filter) || filterByContainsClassName(to, filter))) {
+            return;
         }
+
+        PrintStream out = System.out;
+        out.printf("[truffle]   rewrite %-50s |From %-40s |To %-40s |Reason %s.%n", this.toString(), formatNodeInfo(from), formatNodeInfo(to), reason);
     }
 
     private static String formatNodeInfo(Class<? extends Node> clazz) {
@@ -383,6 +391,23 @@
     }
 
     /**
+     * Get the root node of the tree a node belongs to.
+     * 
+     * @return the {@link RootNode} or {@code null} if there is none.
+     */
+    protected final RootNode getRootNode() {
+        Node rootNode = this;
+        while (rootNode.getParent() != null) {
+            assert !(rootNode instanceof RootNode) : "root node must not have a parent";
+            rootNode = rootNode.getParent();
+        }
+        if (rootNode instanceof RootNode) {
+            return (RootNode) rootNode;
+        }
+        return null;
+    }
+
+    /**
      * Converts this node to a textual representation useful for debugging.
      */
     @Override
--- a/mx/commands.py	Sat Nov 09 21:33:31 2013 +0100
+++ b/mx/commands.py	Sat Nov 09 21:34:07 2013 +0100
@@ -1331,11 +1331,8 @@
     mx.run_java(['-jar', jacocoreport.get_path(True), '-in', 'jacoco.exec', '-g', join(_graal_home, 'graal'), out])
 
 def sl(args):
-    """run an SL program
-
-    VM args should have a @ prefix."""
-    vmArgs = [a[1:] for a in args if a[0] == '@']
-    slArgs = [a for a in args if a[0] != '@']
+    """run an SL program"""
+    vmArgs, slArgs = _extract_VM_args(args)
     vm(vmArgs + ['-cp', mx.classpath("com.oracle.truffle.sl"), "com.oracle.truffle.sl.SimpleLanguage"] + slArgs)
 
 def isGraalEnabled(vm):
--- a/mxtool/mx.py	Sat Nov 09 21:33:31 2013 +0100
+++ b/mxtool/mx.py	Sat Nov 09 21:34:07 2013 +0100
@@ -1425,34 +1425,28 @@
         else:
             preexec_fn = os.setsid
 
-        if not callable(out) and not callable(err) and timeout is None:
-            # The preexec_fn=os.setsid
-            p = subprocess.Popen(args, cwd=cwd, preexec_fn=preexec_fn, creationflags=creationflags, env=env)
-            _currentSubprocess = (p, args)
+        def redirect(stream, f):
+            for line in iter(stream.readline, ''):
+                f(line)
+            stream.close()
+        stdout = out if not callable(out) else subprocess.PIPE
+        stderr = err if not callable(err) else subprocess.PIPE
+        p = subprocess.Popen(args, cwd=cwd, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, creationflags=creationflags, env=env)
+        _currentSubprocess = (p, args)
+        if callable(out):
+            t = Thread(target=redirect, args=(p.stdout, out))
+            t.daemon = True  # thread dies with the program
+            t.start()
+        if callable(err):
+            t = Thread(target=redirect, args=(p.stderr, err))
+            t.daemon = True  # thread dies with the program
+            t.start()
+        if timeout is None or timeout == 0:
             retcode = waitOn(p)
         else:
-            def redirect(stream, f):
-                for line in iter(stream.readline, ''):
-                    f(line)
-                stream.close()
-            stdout = out if not callable(out) else subprocess.PIPE
-            stderr = err if not callable(err) else subprocess.PIPE
-            p = subprocess.Popen(args, cwd=cwd, stdout=stdout, stderr=stderr, preexec_fn=preexec_fn, creationflags=creationflags, env=env)
-            _currentSubprocess = (p, args)
-            if callable(out):
-                t = Thread(target=redirect, args=(p.stdout, out))
-                t.daemon = True  # thread dies with the program
-                t.start()
-            if callable(err):
-                t = Thread(target=redirect, args=(p.stderr, err))
-                t.daemon = True  # thread dies with the program
-                t.start()
-            if timeout is None or timeout == 0:
-                retcode = waitOn(p)
-            else:
-                if get_os() == 'windows':
-                    abort('Use of timeout not (yet) supported on Windows')
-                retcode = _waitWithTimeout(p, args, timeout)
+            if get_os() == 'windows':
+                abort('Use of timeout not (yet) supported on Windows')
+            retcode = _waitWithTimeout(p, args, timeout)
     except OSError as e:
         log('Error executing \'' + ' '.join(args) + '\': ' + str(e))
         if _opts.verbose:
@@ -2983,10 +2977,10 @@
         # The path should always be p.name/dir. independent of where the workspace actually is.
         # So we use the parent folder of the project, whatever that is, to generate such a relative path.
         logicalWorkspaceRoot = os.path.dirname(p.dir)
-        binFolder = os.path.relpath(p.output_dir(), logicalWorkspaceRoot) 
-        
+        binFolder = os.path.relpath(p.output_dir(), logicalWorkspaceRoot)
+
         if _isAnnotationProcessorDependency(p):
-            refreshFile = os.path.relpath(join(p.dir, p.name + '.jar'),  logicalWorkspaceRoot)
+            refreshFile = os.path.relpath(join(p.dir, p.name + '.jar'), logicalWorkspaceRoot)
             _genEclipseBuilder(out, p, 'Jar', 'archive ' + p.name, refresh=True, refreshFile=refreshFile, relevantResources=[binFolder], async=True, xmlIndent='', xmlStandalone='no')
 
         if projToDist.has_key(p.name):
@@ -3068,18 +3062,18 @@
             refreshScope = '${project}'
         else:
             refreshScope = '${working_set:<?xml version="1.0" encoding="UTF-8"?><resources><item path="' + refreshFile + '" type="1"/></resources>}'
-        
-        launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.core.ATTR_REFRESH_RECURSIVE', 'value':  'false'})  
+
+        launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.core.ATTR_REFRESH_RECURSIVE', 'value':  'false'})
         launchOut.element('stringAttribute', {'key' : 'org.eclipse.debug.core.ATTR_REFRESH_SCOPE', 'value':  refreshScope})
 
     if relevantResources is not None:
         resources = '${working_set:<?xml version="1.0" encoding="UTF-8"?><resources>'
         for relevantResource in relevantResources:
-            resources += '<item path="' + relevantResource +'" type="2" />'
+            resources += '<item path="' + relevantResource + '" type="2" />'
         resources += '</resources>}'
         launchOut.element('stringAttribute', {'key' : 'org.eclipse.ui.externaltools.ATTR_BUILD_SCOPE', 'value': resources})
-        
-    
+
+
     launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_CONSOLE_OUTPUT_ON', 'value': consoleOn})
     launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.ui.ATTR_LAUNCH_IN_BACKGROUND', 'value': 'true' if async else 'false'})
     if logToFile:
--- a/src/share/vm/code/nmethod.cpp	Sat Nov 09 21:33:31 2013 +0100
+++ b/src/share/vm/code/nmethod.cpp	Sat Nov 09 21:34:07 2013 +0100
@@ -229,6 +229,7 @@
 #ifdef GRAAL
 static java_nmethod_stats_struct graal_java_nmethod_stats;
 #endif
+static java_nmethod_stats_struct unknwon_java_nmethod_stats;
 
 static native_nmethod_stats_struct native_nmethod_stats;
 static pc_nmethod_stats_struct pc_nmethod_stats;
@@ -237,18 +238,21 @@
 #ifdef COMPILER1
   if (nm->is_compiled_by_c1()) {
     c1_java_nmethod_stats.note_nmethod(nm);
-  }
+  } else
 #endif
 #ifdef COMPILER2
   if (nm->is_compiled_by_c2()) {
     c2_java_nmethod_stats.note_nmethod(nm);
-  }
+  } else
 #endif
 #ifdef GRAAL
   if (nm->is_compiled_by_graal()) {
     graal_java_nmethod_stats.note_nmethod(nm);
+  } else
+#endif
+  {
+    unknwon_java_nmethod_stats.note_nmethod(nm);
   }
-#endif
 }
 
 
@@ -3087,6 +3091,7 @@
 #ifdef GRAAL
   graal_java_nmethod_stats.print_nmethod_stats("Graal");
 #endif
+  unknwon_java_nmethod_stats.print_nmethod_stats("Unknown");
   DebugInformationRecorder::print_statistics();
   pc_nmethod_stats.print_pc_stats();
   Dependencies::print_statistics();
--- a/src/share/vm/runtime/deoptimization.cpp	Sat Nov 09 21:33:31 2013 +0100
+++ b/src/share/vm/runtime/deoptimization.cpp	Sat Nov 09 21:34:07 2013 +0100
@@ -1339,6 +1339,9 @@
 
     DeoptReason reason = trap_request_reason(trap_request);
     DeoptAction action = trap_request_action(trap_request);
+#ifdef GRAAL
+    short speculation_id = trap_request_speculation_id(trap_request);
+#endif
     jint unloaded_class_index = trap_request_index(trap_request); // CP idx or -1
 
     vframe*  vf  = vframe::new_vframe(&fr, &reg_map, thread);
@@ -1349,7 +1352,11 @@
     ScopeDesc*      trap_scope  = cvf->scope();
     
     if (TraceDeoptimization) {
-      tty->print_cr("  bci=%d pc=%d, relative_pc=%d, method=%s", trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name()->as_C_string());
+      tty->print_cr("  bci=%d pc=%d, relative_pc=%d, method=%s" GRAAL_ONLY(", speculation=%d"), trap_scope->bci(), fr.pc(), fr.pc() - nm->code_begin(), trap_scope->method()->name()->as_C_string()
+#ifdef GRAAL
+          , speculation_id
+#endif
+          );
     }
 
     methodHandle    trap_method = trap_scope->method();
@@ -1460,12 +1467,16 @@
           tty->print(" (Graal: no installed code) ");
         }
 #endif //GRAAL
-        tty->print(" (@" INTPTR_FORMAT ") thread=" UINTX_FORMAT " reason=%s action=%s unloaded_class_index=%d",
+        tty->print(" (@" INTPTR_FORMAT ") thread=" UINTX_FORMAT " reason=%s action=%s unloaded_class_index=%d" GRAAL_ONLY(" speculation=%d"),
                    fr.pc(),
                    os::current_thread_id(),
                    trap_reason_name(reason),
                    trap_action_name(action),
-                   unloaded_class_index);
+                   unloaded_class_index
+#ifdef GRAAL
+                   , speculation_id
+#endif
+                   );
         if (class_name != NULL) {
           tty->print(unresolved ? " unresolved class: " : " symbol: ");
           class_name->print_symbol_on(tty);
@@ -1970,13 +1981,24 @@
   jint unloaded_class_index = trap_request_index(trap_request);
   const char* reason = trap_reason_name(trap_request_reason(trap_request));
   const char* action = trap_action_name(trap_request_action(trap_request));
+#ifdef GRAAL
+  short speculation_id = trap_request_speculation_id(trap_request);
+#endif
   size_t len;
   if (unloaded_class_index < 0) {
-    len = jio_snprintf(buf, buflen, "reason='%s' action='%s'",
-                       reason, action);
+    len = jio_snprintf(buf, buflen, "reason='%s' action='%s'" GRAAL_ONLY(" speculation='%d'"),
+                       reason, action
+#ifdef GRAAL
+                       ,speculation_id
+#endif
+                       );
   } else {
-    len = jio_snprintf(buf, buflen, "reason='%s' action='%s' index='%d'",
-                       reason, action, unloaded_class_index);
+    len = jio_snprintf(buf, buflen, "reason='%s' action='%s' index='%d'" GRAAL_ONLY(" speculation='%d'"),
+                       reason, action, unloaded_class_index
+#ifdef GRAAL
+                       ,speculation_id
+#endif
+                       );
   }
   if (len >= buflen)
     buf[buflen-1] = '\0';
--- a/src/share/vm/runtime/deoptimization.hpp	Sat Nov 09 21:33:31 2013 +0100
+++ b/src/share/vm/runtime/deoptimization.hpp	Sat Nov 09 21:34:07 2013 +0100
@@ -102,8 +102,10 @@
   enum {
     _action_bits = 3,
     _reason_bits = 5,
+    _speculation_id_bits = 16,
     _action_shift = 0,
     _reason_shift = _action_shift+_action_bits,
+    _speculation_id_shift = _reason_shift+_reason_bits,
     BC_CASE_LIMIT = PRODUCT_ONLY(1) NOT_PRODUCT(4) // for _deoptimization_hist
   };
 
@@ -286,6 +288,14 @@
       // standard action for unloaded CP entry
       return _unloaded_action;
   }
+  static short trap_request_speculation_id(int trap_request) {
+      if (trap_request < 0)
+        return (DeoptAction)
+          ((~(trap_request) >> _speculation_id_shift) & right_n_bits(_speculation_id_bits));
+      else
+        // standard action for unloaded CP entry
+        return 0;
+    }
   static int trap_request_index(int trap_request) {
     if (trap_request < 0)
       return -1;