changeset 13456:9c88b1138240

Merge with 430c9f08728d9efa37f4311aa712e969f9e5e254
author Michael Van De Vanter <michael.van.de.vanter@oracle.com>
date Tue, 17 Dec 2013 21:26:42 -0800
parents 69d2e4baa215 (current diff) 430c9f08728d (diff)
children d3f662f9b7d6
files graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatSqrtTest.java
diffstat 124 files changed, 3985 insertions(+), 1390 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.amd64/src/com/oracle/graal/amd64/AMD64.java	Tue Dec 17 21:26:42 2013 -0800
@@ -26,6 +26,7 @@
 import static com.oracle.graal.api.code.Register.*;
 
 import java.nio.*;
+import java.util.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.code.Register.RegisterCategory;
@@ -111,22 +112,23 @@
 
     // @formatter:on
 
-    private final int supportedSSEVersion;
-    private final int supportedAVXVersion;
-
-    public AMD64(int supportedSSEVersion, int supportedAVXVersion) {
-        super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8);
-        assert supportedSSEVersion >= 2;
-        this.supportedSSEVersion = supportedSSEVersion;
-        this.supportedAVXVersion = supportedAVXVersion;
+    /**
+     * Basic set of CPU features mirroring what is returned from the cpuid instruction.
+     */
+    public static enum CPUFeature {
+        SSE, SSE2, SSE3, SSE4, SSE4a, SSE4_1, SSE4_2, SSSE3, POPCNT, LZCNT, AVX, AVX2, ERMS, AMD_3DNOW_PREFETCH, AES,
     }
 
-    public int getSupportedSSEVersion() {
-        return supportedSSEVersion;
+    private final EnumSet<CPUFeature> features;
+
+    public AMD64(EnumSet<CPUFeature> features) {
+        super("AMD64", 8, ByteOrder.LITTLE_ENDIAN, true, allRegisters, LOAD_STORE | STORE_STORE, 1, r15.encoding + 1, 8);
+        this.features = features;
+        assert features.contains(CPUFeature.SSE2) : "minimum config for x64";
     }
 
-    public int getSupportedAVXVersion() {
-        return supportedAVXVersion;
+    public EnumSet<CPUFeature> getFeatures() {
+        return features;
     }
 
     @Override
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ExceptionHandler.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ExceptionHandler.java	Tue Dec 17 21:26:42 2013 -0800
@@ -111,4 +111,9 @@
     public String toString() {
         return "ExceptionHandler<startBCI=" + startBCI + ", endBCI=" + endBCI + ", handlerBCI=" + handlerBCI + ", catchTypeCPI=" + catchTypeCPI + ", catchType=" + catchType + ">";
     }
+
+    @Override
+    public int hashCode() {
+        return catchTypeCPI ^ endBCI ^ handlerBCI;
+    }
 }
--- a/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.asm.amd64/src/com/oracle/graal/asm/amd64/AMD64Assembler.java	Tue Dec 17 21:26:42 2013 -0800
@@ -174,6 +174,10 @@
         this.frameRegister = registerConfig == null ? null : registerConfig.getFrameRegister();
     }
 
+    private boolean supports(CPUFeature feature) {
+        return ((AMD64) target.arch).getFeatures().contains(feature);
+    }
+
     private static int encode(Register r) {
         assert r.encoding < 16 && r.encoding >= 0 : "encoding out of range: " + r.encoding;
         return r.encoding & 0x7;
@@ -450,6 +454,7 @@
     }
 
     public final void bsrq(Register dst, Register src) {
+        assert !supports(CPUFeature.LZCNT);
         int encode = prefixqAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -457,6 +462,7 @@
     }
 
     public final void bsrq(Register dst, AMD64Address src) {
+        assert !supports(CPUFeature.LZCNT);
         prefixq(src, dst);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -464,6 +470,7 @@
     }
 
     public final void bsrl(Register dst, Register src) {
+        assert !supports(CPUFeature.LZCNT);
         int encode = prefixAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -471,6 +478,7 @@
     }
 
     public final void bsrl(Register dst, AMD64Address src) {
+        assert !supports(CPUFeature.LZCNT);
         prefix(src, dst);
         emitByte(0x0F);
         emitByte(0xBD);
@@ -909,7 +917,7 @@
 
     public final void movb(AMD64Address dst, Register src) {
         assert src.getRegisterCategory() == AMD64.CPU : "must have byte register";
-        prefix(dst, src); // , true)
+        prefix(dst, src, true);
         emitByte(0x88);
         emitOperandHelper(src, dst);
     }
@@ -1405,6 +1413,7 @@
     }
 
     public final void popcntl(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.POPCNT);
         emitByte(0xF3);
         prefix(src, dst);
         emitByte(0x0F);
@@ -1413,6 +1422,7 @@
     }
 
     public final void popcntl(Register dst, Register src) {
+        assert supports(CPUFeature.POPCNT);
         emitByte(0xF3);
         int encode = prefixAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
@@ -1421,6 +1431,7 @@
     }
 
     public final void popcntq(Register dst, AMD64Address src) {
+        assert supports(CPUFeature.POPCNT);
         emitByte(0xF3);
         prefixq(src, dst);
         emitByte(0x0F);
@@ -1429,6 +1440,7 @@
     }
 
     public final void popcntq(Register dst, Register src) {
+        assert supports(CPUFeature.POPCNT);
         emitByte(0xF3);
         int encode = prefixqAndEncode(dst.encoding, src.encoding);
         emitByte(0x0F);
@@ -1446,6 +1458,14 @@
         emitByte(0x50 | encode);
     }
 
+    public void pushfq() {
+        emitByte(0x9c);
+    }
+
+    public void popfq() {
+        emitByte(0x9D);
+    }
+
     public final void ret(int imm16) {
         if (imm16 == 0) {
             emitByte(0xC3);
@@ -1875,6 +1895,10 @@
     }
 
     private void prefix(AMD64Address adr, Register reg) {
+        prefix(adr, reg, false);
+    }
+
+    private void prefix(AMD64Address adr, Register reg, boolean byteinst) {
         if (reg.encoding < 8) {
             if (needsRex(adr.getBase())) {
                 if (needsRex(adr.getIndex())) {
@@ -1885,7 +1909,7 @@
             } else {
                 if (needsRex(adr.getIndex())) {
                     emitByte(Prefix.REXX);
-                } else if (reg.encoding >= 4) {
+                } else if (byteinst && reg.encoding >= 4) {
                     emitByte(Prefix.REX);
                 }
             }
@@ -2279,7 +2303,11 @@
         subq(dst, imm32, false);
     }
 
-    public final void subq(Register dst, int imm32, boolean force32Imm) {
+    public final void subqWide(Register dst, int imm32) {
+        subq(dst, imm32, true);
+    }
+
+    private void subq(Register dst, int imm32, boolean force32Imm) {
         prefixqAndEncode(dst.encoding);
         emitArith(0x81, 0xE8, dst, imm32, force32Imm);
     }
@@ -2537,28 +2565,28 @@
     }
 
     void prefetchr(AMD64Address src) {
-        // assert(VM_Version::supports_3dnow_prefetch(), "must support");
+        assert supports(CPUFeature.AMD_3DNOW_PREFETCH);
         prefetchPrefix(src);
         emitByte(0x0D);
         emitOperandHelper(0, src);
     }
 
     public void prefetcht0(AMD64Address src) {
-        // NOT_LP64(assert(VM_Version::supports_sse(), "must support"));
+        assert supports(CPUFeature.SSE);
         prefetchPrefix(src);
         emitByte(0x18);
         emitOperandHelper(1, src);
     }
 
     public void prefetcht1(AMD64Address src) {
-        // NOT_LP64(assert(VM_Version::supports_sse(), "must support"));
+        assert supports(CPUFeature.SSE);
         prefetchPrefix(src);
         emitByte(0x18);
         emitOperandHelper(2, src);
     }
 
     public void prefetcht2(AMD64Address src) {
-        // NOT_LP64(assert(VM_Version::supports_sse(), "must support"));
+        assert supports(CPUFeature.SSE);
         prefix(src);
         emitByte(0x0f);
         emitByte(0x18);
@@ -2566,7 +2594,7 @@
     }
 
     public void prefetchw(AMD64Address src) {
-        // assert(VM_Version::supports_3dnow_prefetch(), "must support");
+        assert supports(CPUFeature.AMD_3DNOW_PREFETCH);
         prefix(src);
         emitByte(0x0f);
         emitByte(0x0D);
--- a/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler.hsail.test.infra/src/com/oracle/graal/compiler/hsail/test/infra/KernelTester.java	Tue Dec 17 21:26:42 2013 -0800
@@ -280,10 +280,13 @@
     }
 
     /**
-     * This isEqualsFP method allows subclass to override what FP equality means for this particular
-     * unit test.
+     * Tests two floating point values for equality.
      */
     public boolean isEqualsFP(double first, double second) {
+        // Special case for checking whether expected and actual values are both NaNs.
+        if (Double.isNaN(first) && Double.isNaN(second)) {
+            return true;
+        }
         return first == second;
     }
 
--- /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/DoubleAbsTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for calls to Math.abs(double). Generates a abs_f64 instruction.
+ */
+public class DoubleAbsTest extends GraalKernelTester {
+
+    static final int num = 40;
+    // Output array storing the results of calling Math.abs().
+    @Result protected double[] outArray = new double[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.abs() on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(double[] out, double[] ina, int gid) {
+        out[gid] = Math.abs(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(double[] in) {
+        for (int i = 0; i < num; i++) {
+            // Include positive and negative values as well as corner cases.
+            if (i == 1) {
+                in[i] = Double.NaN;
+            } else if (i == 2) {
+                in[i] = Double.NEGATIVE_INFINITY;
+            } else if (i == 3) {
+                in[i] = Double.POSITIVE_INFINITY;
+            } else if (i == 4) {
+                in[i] = -0.0;
+            } else {
+                in[i] = i < num / 2 ? i : -i;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        double[] inArray = new double[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- /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/DoubleCeilTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for calls to Math.ceil(). Generates a ceil_f64 instruction.
+ */
+public class DoubleCeilTest extends GraalKernelTester {
+
+    static final int num = 40;
+    // Output array storing the results.
+    @Result protected double[] outArray = new double[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.ceil() on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(double[] out, double[] ina, int gid) {
+        out[gid] = Math.ceil(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(double[] in) {
+        // Initialize arrays with a mix of positive and negativ values and any corner cases.
+        for (int i = 0; i < num; i++) {
+            if (i == 0) {
+                in[i] = 0.0;
+            } else if (i == 1) {
+                in[i] = -0.0;
+            } else if (i == 2) {
+                in[i] = Double.NaN;
+            } else if (i == 3) {
+                in[i] = Double.NEGATIVE_INFINITY;
+            } else if (i == 4) {
+                in[i] = Double.POSITIVE_INFINITY;
+            } else {
+                in[i] = i < num / 2 ? i + 0.5 : -i - 0.5;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        double[] inArray = new double[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- /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/DoubleFloorTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for calls to Math.floor(). Generates a floor_f64 instruction.
+ */
+public class DoubleFloorTest extends GraalKernelTester {
+
+    static final int num = 40;
+    // Output array storing the results.
+    @Result protected double[] outArray = new double[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.floor() on an element
+     * of an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(double[] out, double[] ina, int gid) {
+        out[gid] = Math.floor(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(double[] in) {
+        // Initialize input array with a mix of positive and negative values and any corner cases.
+        for (int i = 0; i < num; i++) {
+            if (i == 0) {
+                in[i] = 0.0;
+            } else if (i == 1) {
+                in[i] = -0.0;
+            } else if (i == 2) {
+                in[i] = Double.NaN;
+            } else if (i == 3) {
+                in[i] = Double.NEGATIVE_INFINITY;
+            } else if (i == 4) {
+                in[i] = Double.POSITIVE_INFINITY;
+            } else {
+                in[i] = i < num / 2 ? i + 0.5 : -i - 0.5;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        double[] inArray = new double[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- /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/DoubleRintTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,95 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.*;
+
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for Math.rint(). Generates an rint_f64 instruction.
+ */
+public class DoubleRintTest extends GraalKernelTester {
+
+    static final int size = 64;
+    @Result double[] out = new double[size];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.rint() on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param in the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(double[] out, double[] in, int gid) {
+        out[gid] = Math.rint(in[gid]);
+    }
+
+    /**
+     * Initialize input arrays.
+     * 
+     * @param in the input array
+     */
+    void setupArrays(double[] in) {
+        // Initialize input array with a mix of positive and negative values and corner cases.
+        for (int i = 0; i < size; i++) {
+            if (i == 1) {
+                in[i] = Double.NaN;
+            } else if (i == 2) {
+                in[i] = Double.NEGATIVE_INFINITY;
+            } else if (i == 3) {
+                in[i] = Double.POSITIVE_INFINITY;
+
+            } else if (i == 4) {
+                in[i] = 0.0;
+            } else if (i == 5) {
+                in[i] = -0.0;
+            } else {
+                in[i] = i < size / 2 ? i + 0.5 : -i - 0.6;
+            }
+            out[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        double[] inArray = new double[size];
+        setupArrays(inArray);
+        dispatchMethodKernel(size, out, inArray);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /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/DoubleSqrtTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,94 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.*;
+
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for call to Math.sqrt(double). Generates a sqrt_f64 instruction.
+ */
+public class DoubleSqrtTest extends GraalKernelTester {
+
+    static final int size = 64;
+    @Result double[] out = new double[size];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.sqrt() on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param in the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(double[] in, double[] out, int gid) {
+        out[gid] = Math.sqrt(in[gid]);
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(double[] in) {
+        for (int i = 0; i < size; i++) {
+            // Include positive and negative values as well as corner cases.
+            if (i == 1) {
+                in[i] = Double.NaN;
+            } else if (i == 2) {
+                in[i] = Double.NEGATIVE_INFINITY;
+            } else if (i == 3) {
+                in[i] = Double.POSITIVE_INFINITY;
+            } else if (i == 4) {
+                in[i] = -0.0;
+            } else if (i > 5 && i < 10) {
+                in[i] = i + 0.5;
+            } else {
+                in[i] = i < size / 2 ? i : -i;
+            }
+            out[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        double[] inArray = new double[size];
+        setupArrays(inArray);
+        dispatchMethodKernel(size, inArray, out);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        testGeneratedHsail();
+    }
+}
--- /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/FloatAbsTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for calls to Math.abs(float). Generates an abs_f32 instruction.
+ */
+public class FloatAbsTest extends GraalKernelTester {
+
+    static final int num = 40;
+    // Output array storing the results of calling Math.abs().
+    @Result protected float[] outArray = new float[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.abs() on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(float[] out, float[] ina, int gid) {
+        out[gid] = Math.abs(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(float[] in) {
+        for (int i = 0; i < num; i++) {
+            // Initialize array with positive and negative values as well as corner cases.
+            if (i == 1) {
+                in[i] = Float.NaN;
+            } else if (i == 2) {
+                in[i] = Float.NEGATIVE_INFINITY;
+            } else if (i == 3) {
+                in[i] = Float.POSITIVE_INFINITY;
+            } else if (i == 4) {
+                in[i] = -0;
+            } else {
+                in[i] = i < num / 2 ? i : -i;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        float[] inArray = new float[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatDivPrecisionTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -47,10 +47,6 @@
 
     }
 
-    /**
-     * Allows subclass to override what FP equality means for this particular unit test.
-     * 
-     */
     @Override
     public boolean isEqualsFP(double first, double second) {
         return Math.abs(first - second) == 0;
--- a/graal/com.oracle.graal.compiler.hsail.test/src/com/oracle/graal/compiler/hsail/test/FloatSqrtTest.java	Tue Dec 17 20:22:45 2013 -0800
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,59 +0,0 @@
-/*
- * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * 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 org.junit.*;
-
-import com.oracle.graal.compiler.hsail.test.infra.*;
-
-/**
- * Tests floating point square root.
- */
-public class FloatSqrtTest extends GraalKernelTester {
-
-    static final int size = 64;
-    float[] input = new float[size];
-    @Result float[] output = new float[size];
-    {
-        for (int i = 0; i < size; i++) {
-            input[i] = i;
-            output[i] = -1.0f;
-        }
-
-    }
-
-    public static void run(float[] input1, float[] output1, int gid) {
-        output1[gid] = (float) Math.sqrt(input1[gid]);
-    }
-
-    @Override
-    public void runTest() {
-        dispatchMethodKernel(size, input, output);
-    }
-
-    @Test
-    public void test() {
-        testGeneratedHsail();
-    }
-}
--- /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/IntAbsTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for call to Math.abs(int). Generates an abs_s32 instruction.
+ */
+public class IntAbsTest extends GraalKernelTester {
+
+    static final int num = 20;
+    // Output array storing the results of negation operations.
+    @Result protected int[] outArray = new int[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.abs( ) on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(int[] out, int[] ina, int gid) {
+        out[gid] = Math.abs(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(int[] in) {
+        // initialize input array with a mix of positive and negative values and any corner cases.
+        for (int i = 0; i < num; i++) {
+            if (i == 1) {
+                in[i] = Integer.MIN_VALUE;
+            } else {
+                in[i] = i < num / 2 ? i : -i;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        int[] inArray = new int[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- /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/LongAbsTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2009, 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 org.junit.Test;
+import com.oracle.graal.compiler.hsail.test.infra.*;
+
+/**
+ * Tests intrinsic for call to Math.abs(long). Generates an abs_s64 instruction.
+ */
+public class LongAbsTest extends GraalKernelTester {
+
+    static final int num = 20;
+    // Output array storing the results of the operations.
+    @Result protected long[] outArray = new long[num];
+
+    /**
+     * The static "kernel" method we will be testing. This method calls Math.abs( ) on an element of
+     * an input array and writes the result to the corresponding index of an output array. By
+     * convention the gid is the last parameter.
+     * 
+     * @param out the output array.
+     * @param ina the input array.
+     * @param gid the parameter used to index into the input and output arrays.
+     */
+    public static void run(long[] out, long[] ina, int gid) {
+        out[gid] = Math.abs(ina[gid]);
+    }
+
+    /**
+     * Tests the HSAIL code generated for this unit test by comparing the result of executing this
+     * code with the result of executing a sequential Java version of this unit test.
+     */
+    @Test
+    public void test() {
+        super.testGeneratedHsail();
+    }
+
+    /**
+     * Initializes the input and output arrays passed to the run routine.
+     * 
+     * @param in the input array.
+     */
+    void setupArrays(long[] in) {
+        // Initialize input array with a mix of positive and negative values and any corner cases.
+        for (int i = 0; i < num; i++) {
+            if (i == 1) {
+                in[i] = Long.MIN_VALUE;
+            } else {
+                in[i] = i < num / 2 ? i : -i;
+            }
+            outArray[i] = 0;
+        }
+    }
+
+    /**
+     * Dispatches the HSAIL kernel for this test case.
+     */
+    @Override
+    public void runTest() {
+        long[] inArray = new long[num];
+        setupArrays(inArray);
+        dispatchMethodKernel(num, outArray, inArray);
+    }
+}
--- a/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler.hsail/src/com/oracle/graal/compiler/hsail/HSAILLIRGenerator.java	Tue Dec 17 21:26:42 2013 -0800
@@ -646,11 +646,61 @@
         throw GraalInternalError.unimplemented();
     }
 
+    /**
+     * Emits the LIR code for the {@link HSAILArithmetic#ABS} operation.
+     * 
+     * @param input the source operand
+     * @return Value representing the result of the operation
+     */
     @Override
     public Value emitMathAbs(Value input) {
-        throw GraalInternalError.unimplemented();
+        Variable result = newVariable(input.getPlatformKind());
+        append(new Op1Stack(ABS, result, input));
+        return result;
+    }
+
+    /**
+     * Emits the LIR code for the {@link HSAILArithmetic#CEIL} operation.
+     * 
+     * @param input the source operand
+     * @return Value representing the result of the operation
+     */
+    public Value emitMathCeil(Value input) {
+        Variable result = newVariable(input.getPlatformKind());
+        append(new Op1Stack(CEIL, result, input));
+        return result;
     }
 
+    /**
+     * Emits the LIR code for the {@link HSAILArithmetic#FLOOR} operation.
+     * 
+     * @param input the source operand
+     * @return Value representing the result of the operation
+     */
+    public Value emitMathFloor(Value input) {
+        Variable result = newVariable(input.getPlatformKind());
+        append(new Op1Stack(FLOOR, result, input));
+        return result;
+    }
+
+    /**
+     * Emits the LIR code for the {@link HSAILArithmetic#RINT} operation.
+     * 
+     * @param input the source operand
+     * @return Value representing the result of the operation
+     */
+    public Value emitMathRint(Value input) {
+        Variable result = newVariable(input.getPlatformKind());
+        append(new Op1Stack(RINT, result, input));
+        return result;
+    }
+
+    /**
+     * Emits the LIR code for the {@link HSAILArithmetic#SQRT} operation.
+     * 
+     * @param input the source operand
+     * @return value representing the result of the operation
+     */
     @Override
     public Value emitMathSqrt(Value input) {
         Variable result = newVariable(input.getPlatformKind());
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CommonedConstantsTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2012, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.test;
+
+import java.lang.reflect.*;
+
+import org.junit.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.phases.common.*;
+
+/**
+ * Tests any optimization that commons loads of non-inlineable constants.
+ */
+public class CommonedConstantsTest extends GraalCompilerTest {
+
+    public static final String[] array = {"1", "2", null};
+
+    // A method where a constant is used on the normal and exception edge of a non-inlined call.
+    // The dominating block of both usages is the block containing the call.
+    public static Object test0Snippet(String[] arr, int i) {
+        Object result = null;
+        try {
+            result = Array.get(arr, i);
+        } catch (ArrayIndexOutOfBoundsException e) {
+            result = array[0];
+        }
+        if (result == null) {
+            result = array[2];
+        }
+        return result;
+    }
+
+    @Test
+    public void test0() {
+        // Ensure the exception path is profiled
+        ResolvedJavaMethod javaMethod = getMetaAccess().lookupJavaMethod(getMethod("test0Snippet"));
+        javaMethod.reprofile();
+        test0Snippet(array, array.length);
+
+        test("test0Snippet", array, 0);
+        test("test0Snippet", array, 2);
+        test("test0Snippet", array, 3);
+        test("test0Snippet", array, 1);
+    }
+
+    public static final char[] alphabet = "abcdefghijklmnopqrstuvwxyz".toCharArray();
+
+    static int noninlineLength(char[] s) {
+        return s.length;
+    }
+
+    /**
+     * A constant with usages before and after a non-inlined call.
+     */
+    public static int test1Snippet(String s) {
+        if (s == null) {
+            return noninlineLength(alphabet) + 1;
+        }
+        char[] sChars = s.toCharArray();
+        int count = 0;
+        for (int i = 0; i < alphabet.length && i < sChars.length; i++) {
+            if (alphabet[i] == sChars[i]) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    @Test
+    public void test1() {
+        getSuites().getHighTier().findPhase(AbstractInliningPhase.class).remove();
+        test1Snippet(new String(alphabet));
+
+        test("test1Snippet", (Object) null);
+        test("test1Snippet", "test1Snippet");
+        test("test1Snippet", "");
+    }
+
+    /**
+     * A constant with only usage in a loop.
+     */
+    public static int test2Snippet(String s) {
+        char[] sChars = s.toCharArray();
+        int count = 0;
+        for (int i = 0; i < alphabet.length && i < sChars.length; i++) {
+            if (alphabet[i] == sChars[i]) {
+                count++;
+            }
+        }
+        return count;
+    }
+
+    @Test
+    public void test2() {
+        assert getSuites().getHighTier().findPhase(AbstractInliningPhase.class).hasNext();
+        test2Snippet(new String(alphabet));
+
+        test("test2Snippet", (Object) null);
+        test("test2Snippet", "test1Snippet");
+        test("test2Snippet", "");
+    }
+}
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/MemoryScheduleTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -87,7 +87,7 @@
     @Test
     public void testSimple() {
         for (TestMode mode : TestMode.values()) {
-            SchedulePhase schedule = getFinalSchedule("testSimpleSnippet", mode, MemoryScheduling.OPTIMAL);
+            SchedulePhase schedule = getFinalSchedule("testSimpleSnippet", mode);
             StructuredGraph graph = schedule.getCFG().graph;
             assertReadAndWriteInSameBlock(schedule, true);
             assertOrderedAfterSchedule(schedule, graph.getNodes().filter(FloatingReadNode.class).first(), graph.getNodes().filter(WriteNode.class).first());
@@ -112,7 +112,7 @@
     @Test
     public void testSplit1() {
         for (TestMode mode : TestMode.values()) {
-            SchedulePhase schedule = getFinalSchedule("testSplit1Snippet", mode, MemoryScheduling.OPTIMAL);
+            SchedulePhase schedule = getFinalSchedule("testSplit1Snippet", mode);
             assertReadWithinStartBlock(schedule, true);
             assertReadWithinReturnBlock(schedule, false);
         }
@@ -135,7 +135,7 @@
 
     @Test
     public void testSplit2() {
-        SchedulePhase schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testSplit2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, true);
     }
@@ -159,7 +159,7 @@
 
     @Test
     public void testLoop1() {
-        SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testLoop1Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinReturnBlock(schedule, false);
@@ -184,7 +184,7 @@
 
     @Test
     public void testLoop2() {
-        SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testLoop2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(6, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, true);
@@ -206,7 +206,7 @@
 
     @Test
     public void testLoop3() {
-        SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testLoop3Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(7, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinReturnBlock(schedule, false);
@@ -218,7 +218,7 @@
 
     @Test
     public void testStringReplace() {
-        getFinalSchedule("testStringReplaceSnippet", TestMode.INLINED_WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        getFinalSchedule("testStringReplaceSnippet", TestMode.INLINED_WITHOUT_FRAMESTATES);
         test("testStringReplaceSnippet", "acbaaa");
     }
 
@@ -242,7 +242,7 @@
 
     @Test
     public void testLoop5() {
-        SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testLoop5Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(7, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, false);
@@ -258,7 +258,7 @@
 
     @Test
     public void testArrayCopy() {
-        SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testArrayCopySnippet", TestMode.INLINED_WITHOUT_FRAMESTATES);
         StructuredGraph graph = schedule.getCFG().getStartBlock().getBeginNode().graph();
         ReturnNode ret = graph.getNodes().filter(ReturnNode.class).first();
         assertTrue(ret.result() instanceof FloatingReadNode);
@@ -279,7 +279,7 @@
 
     @Test
     public void testIfRead1() {
-        SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testIfRead1Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, true);
         assertReadAndWriteInSameBlock(schedule, false);
@@ -300,7 +300,7 @@
 
     @Test
     public void testIfRead2() {
-        SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testIfRead2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertEquals(1, schedule.getCFG().graph.getNodes().filter(FloatingReadNode.class).count());
         assertReadWithinStartBlock(schedule, false);
@@ -322,7 +322,7 @@
 
     @Test
     public void testIfRead3() {
-        SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testIfRead3Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, true);
@@ -343,7 +343,7 @@
 
     @Test
     public void testIfRead4() {
-        SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testIfRead4Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, false);
@@ -362,7 +362,7 @@
 
     @Test
     public void testIfRead5() {
-        SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testIfRead5Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertEquals(4, schedule.getCFG().getBlocks().length);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, true);
@@ -388,7 +388,7 @@
 
     @Test
     public void testBlockSchedule() {
-        SchedulePhase schedule = getFinalSchedule("testBlockScheduleSnippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testBlockScheduleSnippet", TestMode.WITHOUT_FRAMESTATES);
         StructuredGraph graph = schedule.getCFG().graph;
         NodeIterable<WriteNode> writeNodes = graph.getNodes().filter(WriteNode.class);
 
@@ -447,7 +447,7 @@
 
     @Test
     public void testProxy1() {
-        SchedulePhase schedule = getFinalSchedule("testProxy1Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testProxy1Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, true); // read of container.a should be in start block
         /*
          * read of container.b for increment operation should be in return block. TODO: not sure
@@ -473,7 +473,7 @@
 
     @Test
     public void testProxy2() {
-        SchedulePhase schedule = getFinalSchedule("testProxy2Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testProxy2Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, false);
     }
@@ -496,7 +496,7 @@
 
     @Test
     public void testStringHashCode() {
-        SchedulePhase schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testStringHashCodeSnippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, true);
         assertReadWithinReturnBlock(schedule, false);
 
@@ -528,7 +528,7 @@
 
     @Test
     public void testLoop4() {
-        SchedulePhase schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES, MemoryScheduling.OPTIMAL);
+        SchedulePhase schedule = getFinalSchedule("testLoop4Snippet", TestMode.WITHOUT_FRAMESTATES);
         assertReadWithinStartBlock(schedule, false);
         assertReadWithinReturnBlock(schedule, false);
     }
@@ -573,6 +573,10 @@
         assertTrue(!(inSame ^ schedule.getCFG().blockFor(read) == schedule.getCFG().blockFor(write)));
     }
 
+    private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode) {
+        return getFinalSchedule(snippet, mode, MemoryScheduling.OPTIMAL);
+    }
+
     private SchedulePhase getFinalSchedule(final String snippet, final TestMode mode, final MemoryScheduling memsched) {
         return getFinalSchedule(snippet, mode, memsched, SchedulingStrategy.LATEST_OUT_OF_LOOPS);
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue Dec 17 21:26:42 2013 -0800
@@ -262,14 +262,13 @@
             for (Block b : lir.linearScanOrder()) {
                 emitBlock(lirGen, b);
             }
+            lirGen.beforeRegisterAllocation();
 
             Debug.dump(lir, "After LIR generation");
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
 
-        lirGen.beforeRegisterAllocation();
-
         try (Scope s = Debug.scope("Allocator")) {
             if (backend.shouldAllocateRegisters()) {
                 new LinearScan(target, lir, lirGen, frameMap).allocate();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalDebugConfig.java	Tue Dec 17 21:26:42 2013 -0800
@@ -110,6 +110,10 @@
         return isEnabled(logFilter);
     }
 
+    public boolean isLogEnabledForMethod() {
+        return isEnabledForMethod(logFilter);
+    }
+
     public boolean isMeterEnabled() {
         return isEnabled(meterFilter);
     }
@@ -118,6 +122,10 @@
         return isEnabled(dumpFilter);
     }
 
+    public boolean isDumpEnabledForMethod() {
+        return isEnabledForMethod(dumpFilter);
+    }
+
     public boolean isTimeEnabled() {
         return isEnabled(timerFilter);
     }
@@ -130,6 +138,10 @@
         return checkDebugFilter(Debug.currentScope(), filter) && checkMethodFilter();
     }
 
+    private boolean isEnabledForMethod(DebugFilter filter) {
+        return filter != null && checkMethodFilter();
+    }
+
     private static boolean checkDebugFilter(String currentScope, DebugFilter filter) {
         return filter != null && filter.matches(currentScope);
     }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/Interval.java	Tue Dec 17 21:26:42 2013 -0800
@@ -418,7 +418,8 @@
 
     /**
      * The {@linkplain RegisterValue register} or {@linkplain StackSlot spill slot} assigned to this
-     * interval.
+     * interval. In case of a spilled interval which is re-materialized this is
+     * {@link Value#ILLEGAL}.
      */
     private AllocatableValue location;
 
@@ -498,6 +499,17 @@
      */
     private Interval locationHint;
 
+    /**
+     * The value with which a spilled child interval can be re-materialized. Currently this must be
+     * a Constant.
+     */
+    private Constant materializedValue;
+
+    /**
+     * The number of times {@link #addMaterializationValue(Constant)} is called.
+     */
+    private int numMaterializationValuesAdded;
+
     void assignLocation(AllocatableValue newLocation) {
         if (isRegister(newLocation)) {
             assert this.location == null : "cannot re-assign location for " + this;
@@ -505,6 +517,8 @@
                 this.location = asRegister(newLocation).asValue(kind);
                 return;
             }
+        } else if (isIllegal(newLocation)) {
+            assert canMaterialize();
         } else {
             assert this.location == null || isRegister(this.location) : "cannot re-assign location for " + this;
             assert isStackSlot(newLocation);
@@ -621,7 +635,7 @@
 
     // returns true if this interval has a shadow copy on the stack that is always correct
     boolean alwaysInMemory() {
-        return splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory;
+        return (splitParent().spillState == SpillState.StoreAtDefinition || splitParent().spillState == SpillState.StartInMemory) && !canMaterialize();
     }
 
     void removeFirstUsePos() {
@@ -693,6 +707,34 @@
         currentSplitChild = this;
     }
 
+    /**
+     * Sets the value which is used for re-materialization.
+     */
+    void addMaterializationValue(Constant value) {
+        if (numMaterializationValuesAdded == 0) {
+            materializedValue = value;
+        } else {
+            // Interval is defined on multiple places -> no materialization is possible.
+            materializedValue = null;
+        }
+        numMaterializationValuesAdded++;
+    }
+
+    /**
+     * Returns true if this interval can be re-materialized when spilled. This means that no
+     * spill-moves are needed. Instead of restore-moves the materializeValue is restored.
+     */
+    public boolean canMaterialize() {
+        return getMaterializedValue() != null;
+    }
+
+    /**
+     * Returns the value which is moved to a register instead of a restore-move from stack.
+     */
+    public Constant getMaterializedValue() {
+        return splitParent().materializedValue;
+    }
+
     int calcTo() {
         assert first != Range.EndMarker : "interval has no range";
 
@@ -864,12 +906,24 @@
         }
     }
 
+    private RegisterPriority adaptPriority(RegisterPriority priority) {
+        /*
+         * In case of re-materialized values we require that use-operands are registers, because we
+         * don't have the value at a stack location. (Note that ShouldHaveRegister means that the
+         * operand can also be a StackSlot).
+         */
+        if (priority == RegisterPriority.ShouldHaveRegister && canMaterialize()) {
+            return RegisterPriority.MustHaveRegister;
+        }
+        return priority;
+    }
+
     // Note: use positions are sorted descending . first use has highest index
     int firstUsage(RegisterPriority minRegisterPriority) {
         assert isVariable(operand) : "cannot access use positions for fixed intervals";
 
         for (int i = usePosList.size() - 1; i >= 0; --i) {
-            RegisterPriority registerPriority = usePosList.registerPriority(i);
+            RegisterPriority registerPriority = adaptPriority(usePosList.registerPriority(i));
             if (registerPriority.greaterEqual(minRegisterPriority)) {
                 return usePosList.usePos(i);
             }
@@ -882,7 +936,7 @@
 
         for (int i = usePosList.size() - 1; i >= 0; --i) {
             int usePos = usePosList.usePos(i);
-            if (usePos >= from && usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) {
+            if (usePos >= from && adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) {
                 return usePos;
             }
         }
@@ -894,7 +948,7 @@
 
         for (int i = usePosList.size() - 1; i >= 0; --i) {
             int usePos = usePosList.usePos(i);
-            if (usePos >= from && usePosList.registerPriority(i) == exactRegisterPriority) {
+            if (usePos >= from && adaptPriority(usePosList.registerPriority(i)) == exactRegisterPriority) {
                 return usePos;
             }
         }
@@ -910,7 +964,7 @@
             if (usePos > from) {
                 return prev;
             }
-            if (usePosList.registerPriority(i).greaterEqual(minRegisterPriority)) {
+            if (adaptPriority(usePosList.registerPriority(i)).greaterEqual(minRegisterPriority)) {
                 prev = usePos;
             }
         }
@@ -1181,6 +1235,10 @@
             buf.append(usePosList.usePos(i)).append(':').append(usePosList.registerPriority(i));
             prev = usePosList.usePos(i);
         }
-        return buf.append("} spill-state{").append(spillState()).append("}").toString();
+        buf.append("} spill-state{").append(spillState()).append("}");
+        if (canMaterialize()) {
+            buf.append(" (remat:").append(getMaterializedValue().toString()).append(")");
+        }
+        return buf.toString();
     }
 }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScan.java	Tue Dec 17 21:26:42 2013 -0800
@@ -263,11 +263,11 @@
         }
     };
 
-    static final IntervalPredicate IS_OOP_INTERVAL = new IntervalPredicate() {
+    static final IntervalPredicate IS_STACK_INTERVAL = new IntervalPredicate() {
 
         @Override
         public boolean apply(Interval i) {
-            return !isRegister(i.operand) && i.kind() == Kind.Object;
+            return !isRegister(i.operand);
         }
     };
 
@@ -282,7 +282,9 @@
     void assignSpillSlot(Interval interval) {
         // assign the canonical spill slot of the parent (if a part of the interval
         // is already spilled) or allocate a new spill slot
-        if (interval.spillSlot() != null) {
+        if (interval.canMaterialize()) {
+            interval.assignLocation(Value.ILLEGAL);
+        } else if (interval.spillSlot() != null) {
             interval.assignLocation(interval.spillSlot());
         } else {
             StackSlot slot = frameMap.allocateSpillSlot(interval.kind());
@@ -519,9 +521,7 @@
 
     // called once before assignment of register numbers
     void eliminateSpillMoves() {
-        if (getTraceLevel() >= 3) {
-            TTY.println(" Eliminating unnecessary spill moves");
-        }
+        Indent indent = Debug.logAndIndent("Eliminating unnecessary spill moves");
 
         // collect all intervals that must be stored after their definition.
         // the list is sorted by Interval.spillDefinitionPos
@@ -553,9 +553,7 @@
                     if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) {
                         // move target is a stack slot that is always correct, so eliminate
                         // instruction
-                        if (getTraceLevel() >= 4) {
-                            TTY.println("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
-                        }
+                        indent.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
                         instructions.set(j, null); // null-instructions are deleted by assignRegNum
                     }
 
@@ -565,25 +563,23 @@
                     assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval";
 
                     while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) {
-                        if (!insertionBuffer.initialized()) {
-                            // prepare insertion buffer (appended when all instructions of the block
-                            // are processed)
-                            insertionBuffer.init(instructions);
-                        }
-
-                        AllocatableValue fromLocation = interval.location();
-                        AllocatableValue toLocation = canonicalSpillOpr(interval);
+                        if (!interval.canMaterialize()) {
+                            if (!insertionBuffer.initialized()) {
+                                // prepare insertion buffer (appended when all instructions of the
+                                // block are processed)
+                                insertionBuffer.init(instructions);
+                            }
 
-                        assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState();
-                        assert isStackSlot(toLocation) : "to operand must be a stack slot";
-
-                        insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation));
+                            AllocatableValue fromLocation = interval.location();
+                            AllocatableValue toLocation = canonicalSpillOpr(interval);
 
-                        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);
+                            assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" + interval.spillState();
+                            assert isStackSlot(toLocation) : "to operand must be a stack slot";
+
+                            insertionBuffer.append(j + 1, ir.spillMoveFactory.createMove(toLocation, fromLocation));
+
+                            indent.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
                         }
-
                         interval = interval.next;
                     }
                 }
@@ -595,9 +591,10 @@
         } // end of block iteration
 
         assert interval == Interval.EndMarker : "missed an interval";
+        indent.outdent();
     }
 
-    private void checkIntervals(Interval interval) {
+    private static void checkIntervals(Interval interval) {
         Interval prev = null;
         Interval temp = interval;
         while (temp != Interval.EndMarker) {
@@ -607,13 +604,11 @@
                 assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from :  then they must also be sorted by spillDefinitionPos";
             }
 
-            assert temp.spillSlot() != null : "interval has no spill slot assigned";
+            assert temp.spillSlot() != null || temp.canMaterialize() : "interval has no spill slot assigned";
             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 (traceLevel >= 4) {
-                TTY.println("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
-            }
+            Debug.log("interval %d (from %d to %d) must be stored at %d", temp.operandNumber, temp.from(), temp.to(), temp.spillDefinitionPos());
 
             prev = temp;
             temp = temp.next;
@@ -695,6 +690,8 @@
 
         // iterate all blocks
         for (final Block block : sortedBlocks) {
+            Indent indent = Debug.logAndIndent("compute local live sets for block %d", block.getId());
+
             final BitSet liveGen = new BitSet(liveSize);
             final BitSet liveKill = new BitSet(liveSize);
 
@@ -713,9 +710,7 @@
                             int operandNum = operandNumber(operand);
                             if (!liveKill.get(operandNum)) {
                                 liveGen.set(operandNum);
-                                if (getTraceLevel() >= 4) {
-                                    TTY.println("  Setting liveGen for operand %d at instruction %d", operandNum, op.id());
-                                }
+                                Debug.log("liveGen for operand %d", operandNum);
                             }
                             if (block.getLoop() != null) {
                                 intervalInLoop.setBit(operandNum, block.getLoop().index);
@@ -735,9 +730,7 @@
                         int operandNum = operandNumber(operand);
                         if (!liveKill.get(operandNum)) {
                             liveGen.set(operandNum);
-                            if (getTraceLevel() >= 4) {
-                                TTY.println("  Setting liveGen for LIR opId %d, operand %d because of state for %s", op.id(), operandNum, op);
-                            }
+                            Debug.log("liveGen in state for operand %d", operandNum);
                         }
                         return operand;
                     }
@@ -749,6 +742,7 @@
                         if (isVariable(operand)) {
                             int varNum = operandNumber(operand);
                             liveKill.set(varNum);
+                            Debug.log("liveKill for operand %d", varNum);
                             if (block.getLoop() != null) {
                                 intervalInLoop.setBit(varNum, block.getLoop().index);
                             }
@@ -764,6 +758,7 @@
                     }
                 };
 
+                Indent indent2 = indent.logAndIndent("handle op %d", op.id());
                 op.forEachInput(useProc);
                 op.forEachAlive(useProc);
                 // Add uses of live locals from interpreter's point of view for proper debug
@@ -771,17 +766,19 @@
                 op.forEachState(stateProc);
                 op.forEachTemp(defProc);
                 op.forEachOutput(defProc);
+                indent2.outdent();
             } // end of instruction iteration
 
-            blockData.get(block).liveGen = liveGen;
-            blockData.get(block).liveKill = liveKill;
-            blockData.get(block).liveIn = new BitSet(liveSize);
-            blockData.get(block).liveOut = new BitSet(liveSize);
+            BlockData blockSets = blockData.get(block);
+            blockSets.liveGen = liveGen;
+            blockSets.liveKill = liveKill;
+            blockSets.liveIn = new BitSet(liveSize);
+            blockSets.liveOut = new BitSet(liveSize);
 
-            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);
-            }
+            indent.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
+            indent.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
+
+            indent.outdent();
         } // end of block iteration
     }
 
@@ -814,6 +811,7 @@
      * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block.
      */
     void computeGlobalLiveSets() {
+        Indent indent = Debug.logAndIndent("compute global live sets");
         int numBlocks = blockCount();
         boolean changeOccurred;
         boolean changeOccurredInBlock;
@@ -825,9 +823,12 @@
         do {
             changeOccurred = false;
 
+            Indent indent2 = indent.logAndIndent("new iteration %d", iterationCount);
+
             // iterate all blocks in reverse order
             for (int i = numBlocks - 1; i >= 0; i--) {
                 Block block = blockAt(i);
+                BlockData blockSets = blockData.get(block);
 
                 changeOccurredInBlock = false;
 
@@ -844,10 +845,10 @@
                         liveOut.clear();
                     }
 
-                    if (!blockData.get(block).liveOut.equals(liveOut)) {
+                    if (!blockSets.liveOut.equals(liveOut)) {
                         // A change occurred. Swap the old and new live out sets to avoid copying.
-                        BitSet temp = blockData.get(block).liveOut;
-                        blockData.get(block).liveOut = liveOut;
+                        BitSet temp = blockSets.liveOut;
+                        blockSets.liveOut = liveOut;
                         liveOut = temp;
 
                         changeOccurred = true;
@@ -860,15 +861,13 @@
                     // !liveKill(block))
                     // note: liveIn has to be computed only in first iteration or if liveOut has
                     // changed!
-                    BitSet liveIn = blockData.get(block).liveIn;
+                    BitSet liveIn = blockSets.liveIn;
                     liveIn.clear();
-                    liveIn.or(blockData.get(block).liveOut);
-                    liveIn.andNot(blockData.get(block).liveKill);
-                    liveIn.or(blockData.get(block).liveGen);
-                }
+                    liveIn.or(blockSets.liveOut);
+                    liveIn.andNot(blockSets.liveKill);
+                    liveIn.or(blockSets.liveGen);
 
-                if (getTraceLevel() >= 4) {
-                    traceLiveness(changeOccurredInBlock, iterationCount, block);
+                    indent2.log("block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
                 }
             }
             iterationCount++;
@@ -876,6 +875,7 @@
             if (changeOccurred && iterationCount > 50) {
                 throw new BailoutException("too many iterations in computeGlobalLiveSets");
             }
+            indent2.outdent();
         } while (changeOccurred);
 
         if (DetailedAsserts.getValue()) {
@@ -888,55 +888,53 @@
             if (DetailedAsserts.getValue()) {
                 reportFailure(numBlocks);
             }
-
-            TTY.println("preds=" + startBlock.getPredecessorCount() + ", succs=" + startBlock.getSuccessorCount());
-            TTY.println("startBlock-ID: " + startBlock.getId());
-
             // bailout if this occurs in product mode.
             throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn);
         }
+        indent.outdent();
     }
 
     private void reportFailure(int numBlocks) {
-        TTY.println(gen.getGraph().toString());
-        TTY.println("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):");
+        Indent indent = Debug.logAndIndent("report failure");
+        indent.log("graph: %s", gen.getGraph());
+        indent.log("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):");
         BitSet startBlockLiveIn = blockData.get(ir.cfg.getStartBlock()).liveIn;
         for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
             Value operand = operandFor(operandNum);
-            TTY.println("  var %d; operand=%s; node=%s", operandNum, operand.toString(), gen.valueForOperand(operand));
+            indent.log("  var %d; operand=%s; node=%s", operandNum, operand, gen.valueForOperand(operand));
         }
 
         // print some additional information to simplify debugging
         for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
             Value operand = operandFor(operandNum);
-            TTY.println("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand.toString(), gen.valueForOperand(operand));
+            final Indent indent2 = indent.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, gen.valueForOperand(operand));
 
             Deque<Block> definedIn = new ArrayDeque<>();
             HashSet<Block> usedIn = new HashSet<>();
             for (Block block : sortedBlocks) {
                 if (blockData.get(block).liveGen.get(operandNum)) {
                     usedIn.add(block);
-                    TTY.println("used in block B%d {", block.getId());
+                    indent2.log("used in block B%d {", block.getId());
                     for (LIRInstruction ins : ir.lir(block)) {
-                        TTY.println("  " + ins.id() + ": " + ins.toString());
+                        indent2.log("  %d: %s", ins.id(), ins);
                         ins.forEachState(new ValueProcedure() {
 
                             @Override
                             public Value doValue(Value liveStateOperand) {
-                                TTY.println("    operand=" + liveStateOperand);
+                                indent2.log("    operand=" + liveStateOperand);
                                 return liveStateOperand;
                             }
                         });
                     }
-                    TTY.println("}");
+                    indent2.log("}");
                 }
                 if (blockData.get(block).liveKill.get(operandNum)) {
                     definedIn.add(block);
-                    TTY.println("defined in block B%d {", block.getId());
+                    indent2.log("defined in block B%d {", block.getId());
                     for (LIRInstruction ins : ir.lir(block)) {
-                        TTY.println("  " + ins.id() + ": " + ins.toString());
+                        indent2.log("  %d: %s", ins.id(), ins);
                     }
-                    TTY.println("}");
+                    indent2.log("}");
                 }
             }
 
@@ -957,12 +955,13 @@
                     }
                 }
             }
-            TTY.print("**** offending usages are in: ");
+            indent.log("**** offending usages are in: ");
             for (Block block : usedIn) {
-                TTY.print("B%d ", block.getId());
+                indent2.log("B%d ", block.getId());
             }
-            TTY.println();
+            indent2.outdent();
         }
+        indent.outdent();
     }
 
     private void verifyLiveness() {
@@ -977,21 +976,10 @@
         }
     }
 
-    private void traceLiveness(boolean changeOccurredInBlock, int iterationCount, Block block) {
-        char c = iterationCount == 0 || changeOccurredInBlock ? '*' : ' ';
-        TTY.print("(%d) liveIn%c  B%d ", iterationCount, c, block.getId());
-        TTY.println(blockData.get(block).liveIn.toString());
-        TTY.print("(%d) liveOut%c B%d ", iterationCount, c, block.getId());
-        TTY.println(blockData.get(block).liveOut.toString());
-    }
-
     void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, PlatformKind kind) {
         if (!isProcessed(operand)) {
             return;
         }
-        if (getTraceLevel() >= 2 && kind == null) {
-            TTY.println(" use %s from %d to %d (%s)", operand, from, to, registerPriority.name());
-        }
 
         Interval interval = getOrCreateInterval(operand);
         if (kind != Kind.Illegal) {
@@ -1002,15 +990,14 @@
 
         // Register use position at even instruction id.
         interval.addUsePos(to & ~1, registerPriority);
+
+        Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name());
     }
 
     void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, PlatformKind kind) {
         if (!isProcessed(operand)) {
             return;
         }
-        if (getTraceLevel() >= 2) {
-            TTY.println(" temp %s tempPos %d (%s)", operand, tempPos, RegisterPriority.MustHaveRegister.name());
-        }
 
         Interval interval = getOrCreateInterval(operand);
         if (kind != Kind.Illegal) {
@@ -1019,19 +1006,20 @@
 
         interval.addRange(tempPos, tempPos + 1);
         interval.addUsePos(tempPos, registerPriority);
+        interval.addMaterializationValue(null);
+
+        Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
     }
 
     boolean isProcessed(Value operand) {
         return !isRegister(operand) || attributes(asRegister(operand)).isAllocatable();
     }
 
-    void addDef(AllocatableValue operand, int defPos, RegisterPriority registerPriority, PlatformKind kind) {
+    void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, PlatformKind kind) {
         if (!isProcessed(operand)) {
             return;
         }
-        if (getTraceLevel() >= 2) {
-            TTY.println(" def %s defPos %d (%s)", operand, defPos, registerPriority.name());
-        }
+        int defPos = op.id();
 
         Interval interval = getOrCreateInterval(operand);
         if (kind != Kind.Illegal) {
@@ -1050,9 +1038,7 @@
             // also add register priority for dead intervals
             interval.addRange(defPos, defPos + 1);
             interval.addUsePos(defPos, registerPriority);
-            if (getTraceLevel() >= 2) {
-                TTY.println("Warning: def of operand %s at %d occurs without use", operand, defPos);
-            }
+            Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
         }
 
         changeSpillDefinitionPos(interval, defPos);
@@ -1060,6 +1046,9 @@
             // detection of method-parameters and roundfp-results
             interval.setSpillState(SpillState.StartInMemory);
         }
+        interval.addMaterializationValue(gen.getMaterializedValue(op, operand));
+
+        Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
     }
 
     /**
@@ -1153,6 +1142,9 @@
     }
 
     void buildIntervals() {
+
+        Indent indent = Debug.logAndIndent("build intervals");
+
         intervalsSize = operandSize();
         intervals = new Interval[intervalsSize + INITIAL_SPLIT_INTERVALS_CAPACITY];
 
@@ -1161,7 +1153,10 @@
 
         // iterate all blocks in reverse order
         for (int i = blockCount() - 1; i >= 0; i--) {
+
             Block block = blockAt(i);
+            Indent indent2 = indent.logAndIndent("handle block %d", block.getId());
+
             List<LIRInstruction> instructions = ir.lir(block);
             final int blockFrom = getFirstLirInstructionId(block);
             int blockTo = getLastLirInstructionId(block);
@@ -1174,9 +1169,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 (getTraceLevel() >= 2) {
-                    TTY.println("live in %s to %d", operand, blockTo + 2);
-                }
+                indent.log("live in %d: %s", operandNum, operand);
 
                 addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, Kind.Illegal);
 
@@ -1195,6 +1188,8 @@
                 final LIRInstruction op = instructions.get(j);
                 final int opId = op.id();
 
+                Indent indent3 = indent2.logAndIndent("handle inst %d: %s", opId, op);
+
                 // add a temp range for each register if operation destroys caller-save registers
                 if (op.destroysCallerSavedRegisters()) {
                     for (Register r : callerSaveRegs) {
@@ -1202,9 +1197,7 @@
                             addTemp(r.asValue(), opId, RegisterPriority.None, Kind.Illegal);
                         }
                     }
-                    if (getTraceLevel() >= 4) {
-                        TTY.println("operation destroys all caller-save registers");
-                    }
+                    indent.log("operation destroys all caller-save registers");
                 }
 
                 op.forEachOutput(new ValueProcedure() {
@@ -1212,7 +1205,7 @@
                     @Override
                     public Value doValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
                         if (isVariableOrRegister(operand)) {
-                            addDef((AllocatableValue) operand, opId, registerPriorityOfOutputOperand(op), operand.getPlatformKind());
+                            addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getPlatformKind());
                             addRegisterHint(op, operand, mode, flags, true);
                         }
                         return operand;
@@ -1270,7 +1263,10 @@
                 // special steps for some instructions (especially moves)
                 handleMethodArguments(op);
 
+                indent3.outdent();
+
             } // end of instruction iteration
+            indent2.outdent();
         } // end of block iteration
 
         // add the range [0, 1] to all fixed intervals.
@@ -1280,6 +1276,7 @@
                 interval.addRange(0, 1);
             }
         }
+        indent.outdent();
     }
 
     // * Phase 5: actual register allocation
@@ -1431,6 +1428,7 @@
     };
 
     public void allocateRegisters() {
+        Indent indent = Debug.logAndIndent("allocate registers");
         Interval precoloredIntervals;
         Interval notPrecoloredIntervals;
 
@@ -1442,6 +1440,7 @@
         LinearScanWalker lsw = new LinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals);
         lsw.walk();
         lsw.finishAllocation();
+        indent.outdent();
     }
 
     // * Phase 6: resolve data flow
@@ -1546,6 +1545,8 @@
      * have been split.
      */
     void resolveDataFlow() {
+        Indent indent = Debug.logAndIndent("resolve data flow");
+
         int numBlocks = blockCount();
         MoveResolver moveResolver = new MoveResolver(this);
         BitSet blockCompleted = new BitSet(numBlocks);
@@ -1608,6 +1609,7 @@
                 }
             }
         }
+        indent.outdent();
     }
 
     // * Phase 7: assign register numbers back to LIR
@@ -1652,15 +1654,18 @@
             interval = splitChildAtOpId(interval, opId, mode);
         }
 
+        if (isIllegal(interval.location()) && interval.canMaterialize()) {
+            return interval.getMaterializedValue();
+        }
         return interval.location();
     }
 
-    IntervalWalker initComputeOopMaps() {
+    protected IntervalWalker initIntervalWalker(IntervalPredicate predicate) {
         // setup lists of potential oops for walking
         Interval oopIntervals;
         Interval nonOopIntervals;
 
-        oopIntervals = createUnhandledLists(IS_OOP_INTERVAL, null).first;
+        oopIntervals = createUnhandledLists(predicate, null).first;
 
         // intervals that have no oops inside need not to be processed.
         // to ensure a walking until the last instruction id, add a dummy interval
@@ -1671,7 +1676,11 @@
         return new IntervalWalker(this, oopIntervals, nonOopIntervals);
     }
 
-    void computeOopMap(IntervalWalker iw, LIRInstruction op, BitSet registerRefMap, BitSet frameRefMap) {
+    /**
+     * Visits all intervals for a frame state. The frame state use this information to build the OOP
+     * maps.
+     */
+    void markFrameLocations(IntervalWalker iw, LIRInstruction op, LIRFrameState info) {
         if (getTraceLevel() >= 3) {
             TTY.println("creating oop map at opId %d", op.id());
         }
@@ -1694,11 +1703,11 @@
             // moves, any intervals which end at this instruction are included
             // in the oop map since we may safepoint while doing the patch
             // before we've consumed the inputs.
-            if (op.id() < interval.currentTo()) {
+            if (op.id() < interval.currentTo() && !isIllegal(interval.location())) {
                 // caller-save registers must not be included into oop-maps at calls
                 assert !op.destroysCallerSavedRegisters() || !isRegister(operand) || !isCallerSave(operand) : "interval is in a caller-save register at a call . register will be overwritten";
 
-                frameMap.setReference(interval.location(), registerRefMap, frameRefMap);
+                info.markLocation(interval.location(), frameMap);
 
                 // Spill optimization: when the stack value is guaranteed to be always correct,
                 // then it must be added to the oop map even if the interval is currently in a
@@ -1707,7 +1716,7 @@
                     assert interval.spillDefinitionPos() > 0 : "position not set correctly";
                     assert interval.spillSlot() != null : "no spill slot assigned";
                     assert !isRegister(interval.operand) : "interval is on stack :  so stack slot is registered twice";
-                    frameMap.setReference(interval.spillSlot(), registerRefMap, frameRefMap);
+                    info.markLocation(interval.spillSlot(), frameMap);
                 }
             }
         }
@@ -1718,9 +1727,8 @@
     }
 
     private void computeDebugInfo(IntervalWalker iw, final LIRInstruction op, LIRFrameState info) {
-        BitSet registerRefMap = op.destroysCallerSavedRegisters() && callKillsRegisters ? null : frameMap.initRegisterRefMap();
-        BitSet frameRefMap = frameMap.initFrameRefMap();
-        computeOopMap(iw, op, registerRefMap, frameRefMap);
+        info.initDebugInfo(frameMap, !op.destroysCallerSavedRegisters() || !callKillsRegisters);
+        markFrameLocations(iw, op, info);
 
         info.forEachState(new ValueProcedure() {
 
@@ -1750,12 +1758,11 @@
                 // the intervals
                 // if the interval is not live, colorLirOperand will cause an assert on failure
                 Value result = colorLirOperand((Variable) operand, tempOpId, mode);
-                assert !hasCall(tempOpId) || isStackSlot(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls";
+                assert !hasCall(tempOpId) || isStackSlot(result) || isConstant(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls";
                 return result;
             }
         });
-
-        info.finish(registerRefMap, frameRefMap);
+        info.finish(op, frameMap);
     }
 
     private void assignLocations(List<LIRInstruction> instructions, final IntervalWalker iw) {
@@ -1811,7 +1818,7 @@
     }
 
     private void assignLocations() {
-        IntervalWalker iw = initComputeOopMaps();
+        IntervalWalker iw = initIntervalWalker(IS_STACK_INTERVAL);
         for (Block block : sortedBlocks) {
             assignLocations(ir.lir(block), iw);
         }
@@ -1819,6 +1826,8 @@
 
     public void allocate() {
 
+        Indent indent = Debug.logAndIndent(false, "allocate %s", gen.getGraph().method());
+
         try (Scope s = Debug.scope("LifetimeAnalysis")) {
             numberInstructions();
             printLir("Before register allocation", true);
@@ -1869,11 +1878,21 @@
             printLir("After register number assignment", true);
             EdgeMoveOptimizer.optimize(ir);
             ControlFlowOptimizer.optimize(ir);
+
+            /*
+             * Temporarily disabled because of problem in specjvm2008. TODO: fix the problem and
+             * re-enable it.
+             * 
+             * RedundantMoveElimination.optimize(ir, frameMap, gen.getGraph().method());
+             */
+
             NullCheckOptimizer.optimize(ir, target.implicitNullCheckLimit);
             printLir("After control flow optimization", false);
         } catch (Throwable e) {
             throw Debug.handle(e);
         }
+
+        indent.outdent();
     }
 
     void printIntervals(String label) {
@@ -1998,7 +2017,7 @@
                 }
                 Value l1 = i1.location();
                 Value l2 = i2.location();
-                if (i1.intersects(i2) && (l1.equals(l2))) {
+                if (i1.intersects(i2) && !isIllegal(l1) && (l1.equals(l2))) {
                     if (DetailedAsserts.getValue()) {
                         TTY.println("Intervals %d and %d overlap and have the same register assigned", i1.operandNumber, i2.operandNumber);
                         TTY.println(i1.logString(this));
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/LinearScanWalker.java	Tue Dec 17 21:26:42 2013 -0800
@@ -399,66 +399,52 @@
     // 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 (getTraceLevel() >= 2) {
-            TTY.println("----- splitting interval: ");
-        }
-        if (getTraceLevel() >= 4) {
-            TTY.println(interval.logString(allocator));
-        }
-        if (getTraceLevel() >= 2) {
-            TTY.println("      between %d and %d", minSplitPos, maxSplitPos);
-        }
+
+        try (Indent indent = Debug.logAndIndent("splitting interval %s between %d and %d", interval, minSplitPos, maxSplitPos)) {
 
-        assert interval.from() < minSplitPos : "cannot split at start of interval";
-        assert currentPosition < minSplitPos : "cannot split before current position";
-        assert minSplitPos <= maxSplitPos : "invalid order";
-        assert maxSplitPos <= interval.to() : "cannot split after end of interval";
+            assert interval.from() < minSplitPos : "cannot split at start of interval";
+            assert currentPosition < minSplitPos : "cannot split before current position";
+            assert minSplitPos <= maxSplitPos : "invalid order";
+            assert maxSplitPos <= interval.to() : "cannot split after end of interval";
+
+            int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true);
 
-        int optimalSplitPos = findOptimalSplitPos(interval, minSplitPos, maxSplitPos, true);
-
-        assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range";
-        assert optimalSplitPos <= interval.to() : "cannot split after end of interval";
-        assert optimalSplitPos > interval.from() : "cannot split at start of interval";
+            assert minSplitPos <= optimalSplitPos && optimalSplitPos <= maxSplitPos : "out of range";
+            assert optimalSplitPos <= interval.to() : "cannot split after end of interval";
+            assert optimalSplitPos > interval.from() : "cannot split at start of interval";
 
-        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 (getTraceLevel() >= 4) {
-                TTY.println("      no split necessary because optimal split position is at end of interval");
+            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
+                indent.log("no split necessary because optimal split position is at end of interval");
+                return;
             }
-            return;
-        }
 
-        // must calculate this before the actual split is performed and before split position is
-        // moved to odd opId
-        boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos);
+            // must calculate this before the actual split is performed and before split position is
+            // moved to odd opId
+            boolean moveNecessary = !allocator.isBlockBegin(optimalSplitPos) && !interval.hasHoleBetween(optimalSplitPos - 1, optimalSplitPos);
 
-        if (!allocator.isBlockBegin(optimalSplitPos)) {
-            // move position before actual instruction (odd opId)
-            optimalSplitPos = (optimalSplitPos - 1) | 1;
-        }
+            if (!allocator.isBlockBegin(optimalSplitPos)) {
+                // move position before actual instruction (odd opId)
+                optimalSplitPos = (optimalSplitPos - 1) | 1;
+            }
 
-        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";
-        assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary";
+            indent.log("splitting at position %d", optimalSplitPos);
 
-        Interval splitPart = interval.split(optimalSplitPos, allocator);
+            assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary";
+            assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary";
 
-        splitPart.setInsertMoveWhenActivated(moveNecessary);
+            Interval splitPart = interval.split(optimalSplitPos, allocator);
 
-        assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position";
-        unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart);
+            splitPart.setInsertMoveWhenActivated(moveNecessary);
 
-        if (getTraceLevel() >= 2) {
-            TTY.println("      split interval in two parts (insertMoveWhenActivated: %b)", moveNecessary);
-        }
-        if (getTraceLevel() >= 2) {
-            TTY.print("      ");
-            TTY.println(interval.logString(allocator));
-            TTY.print("      ");
-            TTY.println(splitPart.logString(allocator));
+            assert splitPart.from() >= currentInterval.currentFrom() : "cannot append new interval before current walk position";
+            unhandledLists.addToListSortedByStartAndUsePositions(RegisterBinding.Any, splitPart);
+
+            if (Debug.isLogEnabled()) {
+                indent.log("left interval  %s: %s", moveNecessary ? "      " : "", interval.logString(allocator));
+                indent.log("right interval %s: %s", moveNecessary ? "(move)" : "", splitPart.logString(allocator));
+            }
         }
     }
 
@@ -472,11 +458,7 @@
         int maxSplitPos = currentPosition;
         int minSplitPos = Math.max(interval.previousUsage(RegisterPriority.ShouldHaveRegister, maxSplitPos) + 1, interval.from());
 
-        if (getTraceLevel() >= 2) {
-            TTY.print("----- splitting and spilling interval: ");
-            TTY.println(interval.logString(allocator));
-            TTY.println("      between %d and %d", minSplitPos, maxSplitPos);
-        }
+        Indent indent = Debug.logAndIndent("splitting and spilling interval %s between %d and %d", interval, minSplitPos, maxSplitPos);
 
         assert interval.state == State.Active : "why spill interval that is not active?";
         assert interval.from() <= minSplitPos : "cannot split before start of interval";
@@ -486,33 +468,31 @@
 
         if (minSplitPos == interval.from()) {
             // the whole interval is never used, so spill it entirely to memory
-            if (getTraceLevel() >= 2) {
-                TTY.println("      spilling entire interval because split pos is at beginning of interval");
-                TTY.println("      use positions: " + interval.usePosList().size());
-            }
-            assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition";
+
+            try (Indent indent2 = indent.logAndIndent("spilling entire interval because split pos is at beginning of interval (use positions: %d)", interval.usePosList().size())) {
 
-            allocator.assignSpillSlot(interval);
-            allocator.changeSpillState(interval, minSplitPos);
+                assert interval.firstUsage(RegisterPriority.ShouldHaveRegister) > currentPosition : "interval must not have use position before currentPosition";
+
+                allocator.assignSpillSlot(interval);
+                allocator.changeSpillState(interval, minSplitPos);
 
-            // Also kick parent intervals out of register to memory when they have no use
-            // position. This avoids short interval in register surrounded by intervals in
-            // memory . avoid useless moves from memory to register and back
-            Interval parent = interval;
-            while (parent != null && parent.isSplitChild()) {
-                parent = parent.getSplitChildBeforeOpId(parent.from());
+                // Also kick parent intervals out of register to memory when they have no use
+                // position. This avoids short interval in register surrounded by intervals in
+                // memory . avoid useless moves from memory to register and back
+                Interval parent = interval;
+                while (parent != null && parent.isSplitChild()) {
+                    parent = parent.getSplitChildBeforeOpId(parent.from());
 
-                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 (getTraceLevel() >= 4) {
-                            TTY.println("      kicking out interval %d out of its register because it is never used", parent.operandNumber);
+                    if (isRegister(parent.location())) {
+                        if (parent.firstUsage(RegisterPriority.ShouldHaveRegister) == Integer.MAX_VALUE) {
+                            // parent is never used, so kick it out of its assigned register
+                            indent2.log("kicking out interval %d out of its register because it is never used", parent.operandNumber);
+                            allocator.assignSpillSlot(parent);
+                        } else {
+                            // do not go further back because the register is actually used by
+                            // the interval
+                            parent = null;
                         }
-                        allocator.assignSpillSlot(parent);
-                    } else {
-                        // do not go further back because the register is actually used by the
-                        // interval
-                        parent = null;
                     }
                 }
             }
@@ -530,35 +510,30 @@
                 optimalSplitPos = (optimalSplitPos - 1) | 1;
             }
 
-            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";
-            assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary";
+            try (Indent indent2 = indent.logAndIndent("splitting at position %d", optimalSplitPos)) {
+                assert allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 1) : "split pos must be odd when not on block boundary";
+                assert !allocator.isBlockBegin(optimalSplitPos) || (optimalSplitPos % 2 == 0) : "split pos must be even on block boundary";
 
-            Interval spilledPart = interval.split(optimalSplitPos, allocator);
-            allocator.assignSpillSlot(spilledPart);
-            allocator.changeSpillState(spilledPart, optimalSplitPos);
+                Interval spilledPart = interval.split(optimalSplitPos, allocator);
+                allocator.assignSpillSlot(spilledPart);
+                allocator.changeSpillState(spilledPart, optimalSplitPos);
 
-            if (!allocator.isBlockBegin(optimalSplitPos)) {
-                if (getTraceLevel() >= 4) {
-                    TTY.println("      inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber);
+                if (!allocator.isBlockBegin(optimalSplitPos)) {
+                    indent2.log("inserting move from interval %d to %d", interval.operandNumber, spilledPart.operandNumber);
+                    insertMove(optimalSplitPos, interval, spilledPart);
                 }
-                insertMove(optimalSplitPos, interval, spilledPart);
-            }
 
-            // the currentSplitChild is needed later when moves are inserted for reloading
-            assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild";
-            spilledPart.makeCurrentSplitChild();
+                // the currentSplitChild is needed later when moves are inserted for reloading
+                assert spilledPart.currentSplitChild() == interval : "overwriting wrong currentSplitChild";
+                spilledPart.makeCurrentSplitChild();
 
-            if (getTraceLevel() >= 2) {
-                TTY.println("      split interval in two parts");
-                TTY.print("      ");
-                TTY.println(interval.logString(allocator));
-                TTY.print("      ");
-                TTY.println(spilledPart.logString(allocator));
+                if (Debug.isLogEnabled()) {
+                    indent2.log("left interval: %s", interval.logString(allocator));
+                    indent2.log("spilled interval   : %s", spilledPart.logString(allocator));
+                }
             }
         }
+        indent.outdent();
     }
 
     void splitStackInterval(Interval interval) {
@@ -883,13 +858,8 @@
         Interval interval = currentInterval;
         boolean result = true;
 
-        if (getTraceLevel() >= 2) {
-            TTY.println("+++++ activating interval " + interval.logString(allocator));
-        }
-
-        if (getTraceLevel() >= 4) {
-            TTY.println("      splitParent: %s, insertMoveWhenActivated: %b", interval.splitParent().operandNumber, interval.insertMoveWhenActivated());
-        }
+        Indent indent = Debug.logAndIndent("activating interval %s,  splitParent: %s, insertMoveWhenActivated: %b", interval.logString(allocator), interval.splitParent().operandNumber,
+                        interval.insertMoveWhenActivated());
 
         final Value operand = interval.operand;
         if (interval.location() != null && isStackSlot(interval.location())) {
@@ -940,6 +910,8 @@
         }
         interval.makeCurrentSplitChild();
 
+        indent.outdent();
+
         return result; // true = interval is moved to active list
     }
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/alloc/MoveResolver.java	Tue Dec 17 21:26:42 2013 -0800
@@ -199,9 +199,7 @@
 
         insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr));
 
-        if (allocator.getTraceLevel() >= 4) {
-            TTY.println("MoveResolver: inserted move from %d (%s) to %d (%s)", fromInterval.operandNumber, fromInterval.location(), toInterval.operandNumber, toInterval.location());
-        }
+        Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
     }
 
     private void insertMove(Value fromOpr, Interval toInterval) {
@@ -211,9 +209,7 @@
         AllocatableValue toOpr = toInterval.operand;
         insertionBuffer.append(insertIdx, allocator.ir.spillMoveFactory.createMove(toOpr, fromOpr));
 
-        if (allocator.getTraceLevel() >= 4) {
-            TTY.print("MoveResolver: inserted move from constant %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location());
-        }
+        Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
     }
 
     private void resolveMappings() {
@@ -283,9 +279,7 @@
                 }
                 spillInterval.assignLocation(spillSlot);
 
-                if (allocator.getTraceLevel() >= 4) {
-                    TTY.println("created new Interval %s for spilling", spillInterval.operand);
-                }
+                Debug.log("created new Interval for spilling: %s", spillInterval);
 
                 // insert a move from register to stack and update the mapping
                 insertMove(fromInterval, spillInterval);
@@ -325,9 +319,18 @@
     }
 
     void addMapping(Interval fromInterval, Interval toInterval) {
-        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());
+
+        if (isIllegal(toInterval.location()) && toInterval.canMaterialize()) {
+            Debug.log("no store to rematerializable interval %s needed", toInterval);
+            return;
         }
+        if (isIllegal(fromInterval.location()) && fromInterval.canMaterialize()) {
+            // Instead of a reload, re-materialize the value
+            Value rematValue = fromInterval.getMaterializedValue();
+            addMapping(rematValue, toInterval);
+            return;
+        }
+        Debug.log("add move mapping from %s to %s", fromInterval, toInterval);
 
         assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
         assert fromInterval.kind() == toInterval.kind();
@@ -337,9 +340,8 @@
     }
 
     void addMapping(Value fromOpr, Interval toInterval) {
-        if (allocator.getTraceLevel() >= 4) {
-            TTY.println("MoveResolver: adding mapping from %s to %d (%s)", fromOpr, toInterval.operandNumber, toInterval.location());
-        }
+        Debug.log("add move mapping from %s to %s", fromOpr, toInterval);
+
         assert isConstant(fromOpr) : "only for constants";
 
         mappingFrom.add(null);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/LIRGenerator.java	Tue Dec 17 21:26:42 2013 -0800
@@ -28,6 +28,7 @@
 import static com.oracle.graal.lir.LIR.*;
 import static com.oracle.graal.lir.LIRValueUtil.*;
 import static com.oracle.graal.nodes.ConstantNode.*;
+import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.util.*;
 import java.util.Map.Entry;
@@ -39,9 +40,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.StandardOp.BlockEndOp;
-import com.oracle.graal.lir.StandardOp.JumpOp;
-import com.oracle.graal.lir.StandardOp.LabelOp;
+import com.oracle.graal.lir.StandardOp.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
@@ -82,10 +81,75 @@
     private final boolean printIRWithLIR;
 
     /**
-     * Maps constants the variables within the scope of a single block to avoid loading a constant
-     * more than once per block.
+     * Handle for an operation that loads a constant into a variable. The operation starts in the
+     * first block where the constant is used but will eventually be
+     * {@linkplain LIRGenerator#insertConstantLoads() moved} to a block dominating all usages of the
+     * constant.
      */
-    private Map<Constant, Variable> constantsLoadedInCurrentBlock;
+    public static class LoadConstant implements Comparable<LoadConstant> {
+        /**
+         * The index of {@link #op} within {@link #block}'s instruction list or -1 if {@code op} is
+         * to be moved to a dominator block.
+         */
+        int index;
+
+        /**
+         * The operation that loads the constant.
+         */
+        private final LIRInstruction op;
+
+        /**
+         * The block that does or will contain {@link #op}. This is initially the block where the
+         * first usage of the constant is seen during LIR generation.
+         */
+        private Block block;
+
+        /**
+         * The variable into which the constant is loaded.
+         */
+        private final Variable variable;
+
+        public LoadConstant(Variable variable, Block block, int index, LIRInstruction op) {
+            this.variable = variable;
+            this.block = block;
+            this.index = index;
+            this.op = op;
+        }
+
+        /**
+         * Sorts {@link LoadConstant} objects according to their enclosing blocks. This is used to
+         * group loads per block in {@link LIRGenerator#insertConstantLoads()}.
+         */
+        public int compareTo(LoadConstant o) {
+            if (block.getId() < o.block.getId()) {
+                return -1;
+            }
+            if (block.getId() > o.block.getId()) {
+                return 1;
+            }
+            return 0;
+        }
+
+        @Override
+        public String toString() {
+            return block + "#" + op;
+        }
+
+        /**
+         * Removes the {@link #op} from its original location if it is still at that location.
+         */
+        public void unpin(LIR lir) {
+            if (index >= 0) {
+                // Replace the move with a filler op so that the operation
+                // list does not need to be adjusted.
+                List<LIRInstruction> instructions = lir.lir(block);
+                instructions.set(index, new NoOp(null, -1));
+                index = -1;
+            }
+        }
+    }
+
+    private Map<Constant, LoadConstant> constantLoads;
 
     private ValueNode currentInstruction;
     private ValueNode lastInstructionPrinted; // Debugging only
@@ -123,6 +187,19 @@
         this.printIRWithLIR = Options.PrintIRWithLIR.getValue();
     }
 
+    /**
+     * Returns a value for a interval definition, which can be used for re-materialization.
+     * 
+     * @param op An instruction which defines a value
+     * @param operand The destination operand of the instruction
+     * @return Returns the value which is moved to the instruction and which can be reused at all
+     *         reload-locations in case the interval of this instruction is spilled. Currently this
+     *         can only be a {@link Constant}.
+     */
+    public Constant getMaterializedValue(LIRInstruction op, Value operand) {
+        return null;
+    }
+
     @SuppressWarnings("hiding")
     protected DebugInfoBuilder createDebugInfoBuilder(NodeMap<Value> nodeOperands) {
         return new DebugInfoBuilder(nodeOperands);
@@ -174,7 +251,7 @@
         if (nodeOperands == null) {
             return null;
         }
-        Value operand = !node.isExternal() ? nodeOperands.get(node) : null;
+        Value operand = nodeOperands.get(node);
         if (operand == null) {
             return getConstantOperand(node);
         }
@@ -186,18 +263,27 @@
             Constant value = node.asConstant();
             if (value != null) {
                 if (canInlineConstant(value)) {
-                    return !node.isExternal() ? setResult(node, value) : value;
+                    return setResult(node, value);
                 } else {
                     Variable loadedValue;
-                    if (constantsLoadedInCurrentBlock == null) {
-                        constantsLoadedInCurrentBlock = new HashMap<>();
-                        loadedValue = null;
+                    if (constantLoads == null) {
+                        constantLoads = new HashMap<>();
+                    }
+                    LoadConstant load = constantLoads.get(value);
+                    if (load == null) {
+                        int index = lir.lir(currentBlock).size();
+                        loadedValue = emitMove(value);
+                        LIRInstruction op = lir.lir(currentBlock).get(index);
+                        constantLoads.put(value, new LoadConstant(loadedValue, currentBlock, index, op));
                     } else {
-                        loadedValue = constantsLoadedInCurrentBlock.get(value);
-                    }
-                    if (loadedValue == null) {
-                        loadedValue = emitMove(value);
-                        constantsLoadedInCurrentBlock.put(value, loadedValue);
+                        Block dominator = ControlFlowGraph.commonDominator(load.block, currentBlock);
+                        loadedValue = load.variable;
+                        if (dominator != load.block) {
+                            load.unpin(lir);
+                        } else {
+                            assert load.block != currentBlock || load.index < lir.lir(currentBlock).size();
+                        }
+                        load.block = dominator;
                     }
                     return loadedValue;
                 }
@@ -350,7 +436,6 @@
         }
 
         currentBlock = block;
-        resetLoadedConstants();
 
         // set up the list of LIR instructions
         assert lir.lir(block) == null : "LIR list already computed for this block";
@@ -414,12 +499,6 @@
         }
     }
 
-    private void resetLoadedConstants() {
-        if (constantsLoadedInCurrentBlock != null && !constantsLoadedInCurrentBlock.isEmpty()) {
-            constantsLoadedInCurrentBlock.clear();
-        }
-    }
-
     protected abstract boolean peephole(ValueNode valueNode);
 
     private boolean hasBlockEnd(Block block) {
@@ -795,10 +874,87 @@
 
     @Override
     public void beforeRegisterAllocation() {
+        insertConstantLoads();
     }
 
     /**
-     * Gets an garbage vale for a given kind.
+     * Moves deferred {@linkplain LoadConstant loads} of constants into blocks dominating all usages
+     * of the constant. Any operations inserted into a block are guaranteed to be immediately prior
+     * to the first control flow instruction near the end of the block.
+     */
+    private void insertConstantLoads() {
+        if (constantLoads != null) {
+            // Remove loads where all usages are in the same block.
+            for (Iterator<Map.Entry<Constant, LoadConstant>> iter = constantLoads.entrySet().iterator(); iter.hasNext();) {
+                LoadConstant lc = iter.next().getValue();
+
+                // Move loads of constant outside of loops
+                if (OptScheduleOutOfLoops.getValue()) {
+                    Block outOfLoopDominator = lc.block;
+                    while (outOfLoopDominator.getLoop() != null) {
+                        outOfLoopDominator = outOfLoopDominator.getDominator();
+                    }
+                    if (outOfLoopDominator != lc.block) {
+                        lc.unpin(lir);
+                        lc.block = outOfLoopDominator;
+                    }
+                }
+
+                if (lc.index != -1) {
+                    assert lir.lir(lc.block).get(lc.index) == lc.op;
+                    iter.remove();
+                }
+            }
+            if (constantLoads.isEmpty()) {
+                return;
+            }
+
+            // Sorting groups the loads per block.
+            LoadConstant[] groupedByBlock = constantLoads.values().toArray(new LoadConstant[constantLoads.size()]);
+            Arrays.sort(groupedByBlock);
+
+            int groupBegin = 0;
+            while (true) {
+                int groupEnd = groupBegin + 1;
+                Block block = groupedByBlock[groupBegin].block;
+                while (groupEnd < groupedByBlock.length && groupedByBlock[groupEnd].block == block) {
+                    groupEnd++;
+                }
+                int groupSize = groupEnd - groupBegin;
+
+                List<LIRInstruction> ops = lir.lir(block);
+                int lastIndex = ops.size() - 1;
+                assert ops.get(lastIndex) instanceof BlockEndOp;
+                int insertionIndex = lastIndex;
+                for (int i = Math.max(0, lastIndex - MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END); i < lastIndex; i++) {
+                    if (getExceptionEdge(ops.get(i)) != null) {
+                        insertionIndex = i;
+                        break;
+                    }
+                }
+
+                if (groupSize == 1) {
+                    ops.add(insertionIndex, groupedByBlock[groupBegin].op);
+                } else {
+                    assert groupSize > 1;
+                    List<LIRInstruction> moves = new ArrayList<>(groupSize);
+                    for (int i = groupBegin; i < groupEnd; i++) {
+                        moves.add(groupedByBlock[i].op);
+                    }
+                    ops.addAll(insertionIndex, moves);
+                }
+
+                if (groupEnd == groupedByBlock.length) {
+                    break;
+                }
+                groupBegin = groupEnd;
+            }
+            constantLoads = null;
+        }
+    }
+
+    /**
+     * Gets a garbage value for a given kind.
      */
     protected Constant zapValueForKind(PlatformKind kind) {
         long dead = 0xDEADDEADDEADDEADL;
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/Debug.java	Tue Dec 17 21:26:42 2013 -0800
@@ -69,6 +69,17 @@
         return ENABLED;
     }
 
+    public static boolean isDumpEnabledForMethod() {
+        if (!ENABLED) {
+            return false;
+        }
+        DebugConfig config = DebugScope.getConfig();
+        if (config == null) {
+            return false;
+        }
+        return config.isDumpEnabledForMethod();
+    }
+
     public static boolean isDumpEnabled() {
         return ENABLED && DebugScope.getInstance().isDumpEnabled();
     }
@@ -81,6 +92,17 @@
         return ENABLED && DebugScope.getInstance().isTimeEnabled();
     }
 
+    public static boolean isLogEnabledForMethod() {
+        if (!ENABLED) {
+            return false;
+        }
+        DebugConfig config = DebugScope.getConfig();
+        if (config == null) {
+            return false;
+        }
+        return config.isLogEnabledForMethod();
+    }
+
     public static boolean isLogEnabled() {
         return ENABLED && DebugScope.getInstance().isLogEnabled();
     }
@@ -431,6 +453,10 @@
                 return isLogEnabled;
             }
 
+            public boolean isLogEnabledForMethod() {
+                return isLogEnabled;
+            }
+
             @Override
             public boolean isMeterEnabled() {
                 return isMeterEnabled;
@@ -441,6 +467,10 @@
                 return isDumpEnabled;
             }
 
+            public boolean isDumpEnabledForMethod() {
+                return isDumpEnabled;
+            }
+
             @Override
             public boolean isTimeEnabled() {
                 return isTimerEnabled;
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DebugConfig.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DebugConfig.java	Tue Dec 17 21:26:42 2013 -0800
@@ -36,6 +36,12 @@
     boolean isLogEnabled();
 
     /**
+     * Determines if logging can be enabled in the current method, regardless of the
+     * {@linkplain Debug#currentScope() current debug scope}.
+     */
+    boolean isLogEnabledForMethod();
+
+    /**
      * Determines if metering is enabled in the {@linkplain Debug#currentScope() current debug
      * scope}.
      * 
@@ -52,6 +58,12 @@
     boolean isDumpEnabled();
 
     /**
+     * Determines if dumping can be enabled in the current method, regardless of the
+     * {@linkplain Debug#currentScope() current debug scope}.
+     */
+    boolean isDumpEnabledForMethod();
+
+    /**
      * Adds an object the context used by this configuration to do filtering.
      */
     void addToContext(Object o);
--- a/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DelegatingDebugConfig.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.debug/src/com/oracle/graal/debug/DelegatingDebugConfig.java	Tue Dec 17 21:26:42 2013 -0800
@@ -38,6 +38,10 @@
         return delegate.isLogEnabled();
     }
 
+    public boolean isLogEnabledForMethod() {
+        return delegate.isLogEnabledForMethod();
+    }
+
     @Override
     public boolean isMeterEnabled() {
         return delegate.isMeterEnabled();
@@ -48,6 +52,10 @@
         return delegate.isDumpEnabled();
     }
 
+    public boolean isDumpEnabledForMethod() {
+        return delegate.isDumpEnabledForMethod();
+    }
+
     @Override
     public boolean isTimeEnabled() {
         return delegate.isTimeEnabled();
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Tue Dec 17 21:26:42 2013 -0800
@@ -75,11 +75,6 @@
     NodeChangedListener usagesDroppedToZeroListener;
     private final HashMap<CacheEntry, Node> cachedNodes = new HashMap<>();
 
-    /**
-     * Determines if external nodes will use {@link Graph}'s canonicalization cache.
-     */
-    public static final boolean CacheExternalNodesInGraph = Boolean.parseBoolean(System.getProperty("graal.cacheExternalNodesInGraph", "false"));
-
     private static final class CacheEntry {
 
         private final Node node;
@@ -347,24 +342,6 @@
         return uniqueHelper(node, true);
     }
 
-    /**
-     * Looks for a node <i>similar</i> to {@code node}. If not found, {@code node} is added to a
-     * cache in this graph used to canonicalize nodes.
-     * <p>
-     * Note that node must implement {@link ValueNumberable} and must be an
-     * {@linkplain Node#isExternal() external} node.
-     * 
-     * @return a node similar to {@code node} if one exists, otherwise {@code node}
-     */
-    public <T extends Node> T uniqueExternal(T node) {
-        assert node.isExternal() : node;
-        assert node instanceof ValueNumberable : node;
-        if (!CacheExternalNodesInGraph) {
-            return node;
-        }
-        return uniqueHelper(node, false);
-    }
-
     @SuppressWarnings("unchecked")
     <T extends Node> T uniqueHelper(T node, boolean addIfMissing) {
         assert node.getNodeClass().valueNumberable();
@@ -381,14 +358,13 @@
     }
 
     void putNodeIntoCache(Node node) {
-        assert node.isExternal() || node.graph() == this || node.graph() == null;
+        assert node.graph() == this || node.graph() == null;
         assert node.getNodeClass().valueNumberable();
         assert node.getNodeClass().isLeafNode() : node.getClass();
         cachedNodes.put(new CacheEntry(node), node);
     }
 
     Node findNodeInCache(Node node) {
-        assert !node.isExternal() || CacheExternalNodesInGraph;
         CacheEntry key = new CacheEntry(node);
         Node result = cachedNodes.get(key);
         if (result != null && result.isDeleted()) {
@@ -589,7 +565,7 @@
      * ordering between the nodes within the list.
      */
     public boolean maybeCompress() {
-        if (Debug.isEnabled()) {
+        if (Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()) {
             return false;
         }
         int liveNodeCount = getNodeCount();
@@ -785,7 +761,6 @@
     }
 
     void register(Node node) {
-        assert !node.isExternal();
         assert node.id() == Node.INITIAL_ID;
         if (nodes.length == nodesSize) {
             nodes = Arrays.copyOf(nodes, (nodesSize * 2) + 1);
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Node.java	Tue Dec 17 21:26:42 2013 -0800
@@ -157,11 +157,9 @@
     }
 
     /**
-     * Gets the graph context of this node. This must not be called for {@linkplain #isExternal()
-     * external} nodes.
+     * Gets the graph context of this node.
      */
     public Graph graph() {
-        assert !isExternal() : "external node has no graph: " + this;
         return graph;
     }
 
@@ -288,15 +286,6 @@
     }
 
     /**
-     * Determines if this node has a {@linkplain #graph() graph} context or is external to any
-     * graph. The {@link #graph()} method must only be called on nodes for which this method returns
-     * true.
-     */
-    public boolean isExternal() {
-        return false;
-    }
-
-    /**
      * Finds the index of the last non-null entry in a node array. The search assumes that all
      * non-null entries precede the first null entry in the array.
      * 
@@ -531,7 +520,6 @@
         assert assertFalse(other == this, "cannot replace a node with itself");
         assert assertFalse(isDeleted(), "cannot replace deleted node");
         assert assertTrue(other == null || !other.isDeleted(), "cannot replace with deleted node %s", other);
-        assert assertTrue(other == null || other.isExternal() || other.graph() == graph, "cannot replace with node in different graph: %s", other == null || other.isExternal() ? null : other.graph());
         return true;
     }
 
@@ -588,7 +576,7 @@
         assert assertFalse(isDeleted(), "cannot clear inputs of deleted node");
 
         for (Node input : inputs()) {
-            if (input.recordsUsages() && !input.isExternal()) {
+            if (input.recordsUsages()) {
                 removeThisFromUsages(input);
                 if (input.usages().isEmpty()) {
                     NodeChangedListener listener = graph.usagesDroppedToZeroListener;
@@ -637,7 +625,6 @@
     }
 
     public final Node copyWithInputs() {
-        assert !isExternal();
         Node newNode = clone(graph);
         NodeClass clazz = getNodeClass();
         clazz.copyInputs(this, newNode);
@@ -676,7 +663,6 @@
     }
 
     final Node clone(Graph into, boolean clearInputsAndSuccessors) {
-        assert !isExternal();
         NodeClass nodeClass = getNodeClass();
         if (nodeClass.valueNumberable() && nodeClass.isLeafNode()) {
             Node otherNode = into.findNodeInCache(this);
@@ -711,23 +697,6 @@
         return newNode;
     }
 
-    /**
-     * Determines if a given node is {@linkplain Graph#uniqueExternal(Node) unique} within a given
-     * graph if the node is non-null and {@linkplain #isExternal() external}.
-     * 
-     * @param node node to check
-     * @param graph graph context to use
-     * @return true if node is null, not external or unique within {@code graph} otherwise raises a
-     *         {@link VerificationError}
-     */
-    public static boolean verifyUniqueIfExternal(Node node, Graph graph) {
-        if (node != null && node.isExternal() && Graph.CacheExternalNodesInGraph) {
-            Node cached = graph.findNodeInCache(node);
-            node.assertTrue(cached == node, "external node does not match canonical node %s", cached);
-        }
-        return true;
-    }
-
     protected void afterClone(@SuppressWarnings("unused") Node other) {
     }
 
@@ -736,8 +705,6 @@
         assertTrue(graph() != null, "null graph");
         for (Node input : inputs()) {
             assertTrue(!input.recordsUsages() || input.usages().contains(this), "missing usage in input %s", input);
-            assert verifyUniqueIfExternal(input, graph());
-            assertTrue(input.isExternal() || input.graph() == graph(), "mismatching graph in input %s", input);
         }
         for (Node successor : successors()) {
             assertTrue(successor.predecessor() == this, "missing predecessor in %s (actual: %s)", successor, successor.predecessor());
@@ -791,9 +758,7 @@
     }
 
     /**
-     * Nodes always use an {@linkplain System#identityHashCode(Object) identity} hash code. For this
-     * reason, {@linkplain #isExternal() external} nodes should still be {@link Graph#unique unique}
-     * within the context of a graph.
+     * Nodes always use an {@linkplain System#identityHashCode(Object) identity} hash code.
      */
     @Override
     public final int hashCode() {
@@ -801,8 +766,7 @@
     }
 
     /**
-     * Equality tests must rely solely on identity. For this reason, {@linkplain #isExternal()
-     * external} nodes should still be {@link Graph#unique unique} within the context of a graph.
+     * Equality tests must rely solely on identity.
      */
     @Override
     public final boolean equals(Object obj) {
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Tue Dec 17 21:26:42 2013 -0800
@@ -937,7 +937,6 @@
             if (input != null) {
                 Node newInput = duplicationReplacement.replacement(input, true);
                 node.updateUsages(null, newInput);
-                assert Node.verifyUniqueIfExternal(newInput, node.graph());
                 assert newInput == null || fieldTypes.get(inputOffsets[index]).isAssignableFrom(newInput.getClass()) : "Can not assign " + newInput.getClass() + " to " +
                                 fieldTypes.get(inputOffsets[index]) + " in " + node;
                 putNode(node, inputOffsets[index], newInput);
@@ -994,7 +993,6 @@
             Node oldNode = list.get(i);
             if (oldNode != null) {
                 Node newNode = duplicationReplacement.replacement(oldNode, true);
-                assert Node.verifyUniqueIfExternal(newNode, node.graph());
                 result.set(i, newNode);
             }
         }
@@ -1079,7 +1077,6 @@
     }
 
     public boolean replaceFirstInput(Node node, Node old, Node other) {
-        assert Node.verifyUniqueIfExternal(other, node.graph());
         int index = 0;
         while (index < directInputCount) {
             Node input = getNode(node, inputOffsets[index]);
@@ -1384,9 +1381,6 @@
         InplaceUpdateClosure replacementClosure = new InplaceUpdateClosure() {
 
             public Node replacement(Node node, boolean isInput) {
-                if (node.isExternal() && node instanceof ValueNumberable) {
-                    return graph.uniqueExternal(node);
-                }
                 Node target = newNodes.get(node);
                 if (target == null) {
                     Node replacement = node;
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackend.java	Tue Dec 17 21:26:42 2013 -0800
@@ -83,8 +83,8 @@
      * @param isVerifiedEntryPoint specifies if the code buffer is currently at the verified entry
      *            point
      */
-    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit, boolean isVerifiedEntryPoint) {
-        if (stackShadowPages > 0) {
+    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit, boolean isVerifiedEntryPoint) {
+        if (pagesToBang > 0) {
 
             AMD64MacroAssembler asm = (AMD64MacroAssembler) crb.asm;
             int frameSize = crb.frameMap.frameSize();
@@ -92,7 +92,7 @@
                 int lastFramePage = frameSize / unsafe.pageSize();
                 // emit multiple stack bangs for methods with frames larger than a page
                 for (int i = 0; i <= lastFramePage; i++) {
-                    int disp = (i + stackShadowPages) * unsafe.pageSize();
+                    int disp = (i + pagesToBang) * unsafe.pageSize();
                     if (afterFrameInit) {
                         disp -= frameSize;
                     }
@@ -141,12 +141,12 @@
                 }
             } else {
                 int verifiedEntryPointOffset = asm.codeBuffer.position();
-                if (!isStub && stackShadowPages > 0) {
-                    emitStackOverflowCheck(crb, stackShadowPages, false, true);
+                if (!isStub && pagesToBang > 0) {
+                    emitStackOverflowCheck(crb, pagesToBang, false, true);
                     assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE;
                 }
                 if (!isStub && asm.codeBuffer.position() == verifiedEntryPointOffset) {
-                    asm.subq(rsp, frameSize, true);
+                    asm.subqWide(rsp, frameSize);
                     assert asm.codeBuffer.position() - verifiedEntryPointOffset >= PATCHED_VERIFIED_ENTRY_POINT_INSTRUCTION_SIZE;
                 } else {
                     asm.decrementq(rsp, frameSize);
@@ -278,7 +278,7 @@
      * @param installedCodeOwner see {@link Backend#emitCode}
      */
     public void emitCodeBody(ResolvedJavaMethod installedCodeOwner, CompilationResultBuilder crb, LIRGenerator lirGen) {
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
     }
 
     /**
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotBackendFactory.java	Tue Dec 17 21:26:42 2013 -0800
@@ -37,7 +37,40 @@
 public class AMD64HotSpotBackendFactory implements HotSpotBackendFactory {
 
     protected Architecture createArchitecture(HotSpotVMConfig config) {
-        return new AMD64(config.useSSE, config.useAVX);
+        return new AMD64(computeFeatures(config));
+    }
+
+    protected EnumSet<AMD64.CPUFeature> computeFeatures(HotSpotVMConfig config) {
+        // Configure the feature set using the HotSpot flag settings.
+        EnumSet<AMD64.CPUFeature> features = EnumSet.noneOf(AMD64.CPUFeature.class);
+        assert config.useSSE >= 2 : "minimum config for x64";
+        features.add(AMD64.CPUFeature.SSE);
+        features.add(AMD64.CPUFeature.SSE2);
+        if (config.useSSE > 2) {
+            features.add(AMD64.CPUFeature.SSE3);
+        }
+        if (config.useSSE > 3) {
+            features.add(AMD64.CPUFeature.SSE4);
+        }
+        if (config.useAVX > 0) {
+            features.add(AMD64.CPUFeature.AVX);
+        }
+        if (config.useAVX > 1) {
+            features.add(AMD64.CPUFeature.AVX2);
+        }
+        if (config.useCountLeadingZerosInstruction) {
+            features.add(AMD64.CPUFeature.LZCNT);
+        }
+        if (config.usePopCountInstruction) {
+            features.add(AMD64.CPUFeature.POPCNT);
+        }
+        if (config.useAESIntrinsics) {
+            features.add(AMD64.CPUFeature.AES);
+        }
+        if (config.allocatePrefetchInstr == 3) {
+            features.add(AMD64.CPUFeature.AMD_3DNOW_PREFETCH);
+        }
+        return features;
     }
 
     protected TargetDescription createTarget(HotSpotVMConfig config) {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotLIRGenerator.java	Tue Dec 17 21:26:42 2013 -0800
@@ -48,7 +48,7 @@
 import com.oracle.graal.hotspot.nodes.*;
 import com.oracle.graal.hotspot.stubs.*;
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.StandardOp.PlaceholderOp;
+import com.oracle.graal.lir.StandardOp.NoOp;
 import com.oracle.graal.lir.StandardOp.SaveRegistersOp;
 import com.oracle.graal.lir.amd64.*;
 import com.oracle.graal.lir.amd64.AMD64ControlFlow.CondMoveOp;
@@ -93,14 +93,14 @@
      */
     class SaveRbp {
 
-        final PlaceholderOp placeholder;
+        final NoOp placeholder;
 
         /**
          * The slot reserved for saving RBP.
          */
         final StackSlot reservedSlot;
 
-        public SaveRbp(PlaceholderOp placeholder) {
+        public SaveRbp(NoOp placeholder) {
             this.placeholder = placeholder;
             this.reservedSlot = frameMap.allocateSpillSlot(Kind.Long);
             assert reservedSlot.getRawOffset() == -16 : reservedSlot.getRawOffset();
@@ -172,7 +172,7 @@
 
         emitIncomingValues(params);
 
-        saveRbp = new SaveRbp(new PlaceholderOp(currentBlock, lir.lir(currentBlock).size()));
+        saveRbp = new SaveRbp(new NoOp(currentBlock, lir.lir(currentBlock).size()));
         append(saveRbp.placeholder);
 
         for (LocalNode local : graph.getNodes(LocalNode.class)) {
@@ -433,6 +433,7 @@
 
     @Override
     public void beforeRegisterAllocation() {
+        super.beforeRegisterAllocation();
         boolean hasDebugInfo = lir.hasDebugInfo();
         AllocatableValue savedRbp = saveRbp.finalize(hasDebugInfo);
         if (hasDebugInfo) {
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotRegisterConfig.java	Tue Dec 17 21:26:42 2013 -0800
@@ -40,6 +40,13 @@
 
     private final Register[] allocatable;
 
+    /**
+     * The same as {@link #allocatable}, except if parameter registers are removed with the
+     * {@link #RegisterPressure} option. The caller saved registers always include all parameter
+     * registers.
+     */
+    private final Register[] callerSaved;
+
     private final HashMap<PlatformKind, Register[]> categorized = new HashMap<>();
 
     private final RegisterAttributes[] attributesMap;
@@ -129,12 +136,20 @@
 
         csl = null;
         allocatable = initAllocatable(config.useCompressedOops);
+        Set<Register> callerSaveSet = new HashSet<>();
+        Collections.addAll(callerSaveSet, allocatable);
+        Collections.addAll(callerSaveSet, xmmParameterRegisters);
+        Collections.addAll(callerSaveSet, javaGeneralParameterRegisters);
+        Collections.addAll(callerSaveSet, nativeGeneralParameterRegisters);
+        callerSaved = callerSaveSet.toArray(new Register[callerSaveSet.size()]);
+        assert callerSaved.length == allocatable.length || RegisterPressure.getValue() != null;
+
         attributesMap = RegisterAttributes.createMap(this, AMD64.allRegisters);
     }
 
     @Override
     public Register[] getCallerSaveRegisters() {
-        return getAllocatableRegisters();
+        return callerSaved;
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.amd64/src/com/oracle/graal/hotspot/amd64/AMD64HotSpotSafepointOp.java	Tue Dec 17 21:26:42 2013 -0800
@@ -42,20 +42,24 @@
 public class AMD64HotSpotSafepointOp extends AMD64LIRInstruction {
 
     @State protected LIRFrameState state;
-    @Temp({OperandFlag.REG}) private AllocatableValue temp;
+    @Temp({OperandFlag.REG, OperandFlag.ILLEGAL}) private AllocatableValue temp;
 
     private final HotSpotVMConfig config;
 
     public AMD64HotSpotSafepointOp(LIRFrameState state, HotSpotVMConfig config, LIRGeneratorTool tool) {
         this.state = state;
         this.config = config;
-        temp = tool.newVariable(tool.target().wordKind);
+        if (isPollingPageFar(config)) {
+            temp = tool.newVariable(tool.target().wordKind);
+        } else {
+            // Don't waste a register if it's unneeded
+            temp = Value.ILLEGAL;
+        }
     }
 
     @Override
     public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler asm) {
-        RegisterValue scratch = (RegisterValue) temp;
-        emitCode(crb, asm, config, false, state, scratch.getRegister());
+        emitCode(crb, asm, config, false, state, temp instanceof RegisterValue ? ((RegisterValue) temp).getRegister() : null);
     }
 
     /**
@@ -76,7 +80,7 @@
             if (state != null) {
                 crb.recordInfopoint(pos, state, InfopointReason.SAFEPOINT);
             }
-            asm.movq(scratch, new AMD64Address(scratch));
+            asm.testl(rax, new AMD64Address(scratch));
         } else {
             crb.recordMark(atReturn ? MARK_POLL_RETURN_NEAR : MARK_POLL_NEAR);
             if (state != null) {
@@ -84,7 +88,7 @@
             }
             // The C++ code transforms the polling page offset into an RIP displacement
             // to the real address at that offset in the polling page.
-            asm.movq(scratch, new AMD64Address(rip, 0));
+            asm.testl(rax, new AMD64Address(rip, 0));
         }
     }
 }
--- a/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.hsail/src/com/oracle/graal/hotspot/hsail/HSAILHotSpotBackend.java	Tue Dec 17 21:26:42 2013 -0800
@@ -41,6 +41,8 @@
 import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.lir.hsail.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.Replacements;
+import com.oracle.graal.replacements.hsail.*;
 
 /**
  * HSAIL specific backend.
@@ -63,12 +65,23 @@
         return true;
     }
 
+    /**
+     * Completes the initialization of the HSAIL backend. This includes initializing the providers
+     * and registering any method substitutions specified by the HSAIL backend.
+     */
     @Override
     public void completeInitialization() {
         final HotSpotProviders providers = getProviders();
         HotSpotVMConfig config = getRuntime().getConfig();
+        // Initialize the lowering provider.
         final HotSpotLoweringProvider lowerer = (HotSpotLoweringProvider) providers.getLowerer();
         lowerer.initialize(providers, config);
+
+        // Register the replacements used by the HSAIL backend.
+        Replacements replacements = providers.getReplacements();
+
+        // Register the substitutions for java.lang.Math routines.
+        replacements.registerSubstitutions(HSAILMathSubstitutions.class);
     }
 
     /**
@@ -284,7 +297,7 @@
             }
         }
         // Prologue done, Emit code for the LIR.
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
         // Now that code is emitted go back and figure out what the upper Bound stack size was.
         long maxStackSize = ((HSAILAssembler) crb.asm).upperBoundStackSize();
         String spillsegStringFinal;
--- a/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.ptx/src/com/oracle/graal/hotspot/ptx/PTXHotSpotBackend.java	Tue Dec 17 21:26:42 2013 -0800
@@ -284,7 +284,7 @@
         }
         // Emit code for the LIR
         try {
-            lirGen.lir.emitCode(crb);
+            crb.emit(lirGen.lir);
         } catch (GraalInternalError e) {
             e.printStackTrace();
             // TODO : Better error handling needs to be done once
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotBackend.java	Tue Dec 17 21:26:42 2013 -0800
@@ -81,15 +81,15 @@
      * @param afterFrameInit specifies if the stack pointer has already been adjusted to allocate
      *            the current frame
      */
-    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int stackShadowPages, boolean afterFrameInit) {
-        if (stackShadowPages > 0) {
+    protected static void emitStackOverflowCheck(CompilationResultBuilder crb, int pagesToBang, boolean afterFrameInit) {
+        if (pagesToBang > 0) {
             SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
             final int frameSize = crb.frameMap.totalFrameSize();
             if (frameSize > 0) {
                 int lastFramePage = frameSize / unsafe.pageSize();
                 // emit multiple stack bangs for methods with frames larger than a page
                 for (int i = 0; i <= lastFramePage; i++) {
-                    int disp = (i + stackShadowPages) * unsafe.pageSize();
+                    int disp = (i + pagesToBang) * unsafe.pageSize();
                     if (afterFrameInit) {
                         disp -= frameSize;
                     }
@@ -124,8 +124,8 @@
             final int frameSize = crb.frameMap.totalFrameSize();
 
             SPARCMacroAssembler masm = (SPARCMacroAssembler) crb.asm;
-            if (!isStub && stackShadowPages > 0) {
-                emitStackOverflowCheck(crb, stackShadowPages, false);
+            if (!isStub && pagesToBang > 0) {
+                emitStackOverflowCheck(crb, pagesToBang, false);
             }
             new Save(sp, -frameSize, sp).emit(masm);
 
@@ -207,7 +207,7 @@
         crb.recordMark(Marks.MARK_VERIFIED_ENTRY);
 
         // Emit code for the LIR
-        lirGen.lir.emitCode(crb);
+        crb.emit(lirGen.lir);
 
         HotSpotFrameContext frameContext = (HotSpotFrameContext) crb.frameContext;
         HotSpotForeignCallsProvider foreignCalls = getProviders().getForeignCalls();
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/CompileTheWorldTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -28,6 +28,7 @@
 
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.CompileTheWorld.Config;
 
 /**
  * Tests {@link CompileTheWorld} functionality.
@@ -39,7 +40,7 @@
         boolean originalSetting = ExitVMOnException.getValue();
         // Compile a couple classes in rt.jar
         String file = System.getProperty("java.home") + "/lib/rt.jar";
-        new CompileTheWorld(file, 1, 5, false).compile();
+        new CompileTheWorld(file, new Config(null), 1, 5, false).compile();
         ExitVMOnException.setValue(originalSetting);
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompilationTask.java	Tue Dec 17 21:26:42 2013 -0800
@@ -47,7 +47,7 @@
 import com.oracle.graal.phases.*;
 import com.oracle.graal.phases.tiers.*;
 
-public final class CompilationTask implements Runnable {
+public class CompilationTask implements Runnable {
 
     public static final ThreadLocal<Boolean> withinEnqueue = new ThreadLocal<Boolean>() {
 
@@ -77,7 +77,7 @@
         return new CompilationTask(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id);
     }
 
-    private CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) {
+    protected CompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) {
         assert id >= 0;
         this.backend = backend;
         this.plan = plan;
@@ -104,7 +104,7 @@
     public void run() {
         withinEnqueue.set(Boolean.FALSE);
         try {
-            runCompilation();
+            runCompilation(true);
         } finally {
             if (method.currentTask() == this) {
                 method.setCurrentTask(null);
@@ -120,7 +120,11 @@
 
     public static final DebugTimer CodeInstallationTime = Debug.timer("CodeInstallation");
 
-    public void runCompilation() {
+    protected Suites getSuites(HotSpotProviders providers) {
+        return providers.getSuites().getDefaultSuites();
+    }
+
+    public void runCompilation(boolean clearFromCompilationQueue) {
         /*
          * no code must be outside this try/finally because it could happen otherwise that
          * clearQueuedForCompilation() is not executed
@@ -164,7 +168,7 @@
                 }
                 InlinedBytecodes.add(method.getCodeSize());
                 CallingConvention cc = getCallingConvention(providers.getCodeCache(), Type.JavaCallee, graph.method(), false);
-                Suites suites = providers.getSuites().getDefaultSuites();
+                Suites suites = getSuites(providers);
                 result = compileGraph(graph, cc, method, providers, backend, backend.getTarget(), graphCache, plan, optimisticOpts, profilingInfo, method.getSpeculationLog(), suites, true,
                                 new CompilationResult(), CompilationResultBuilderFactory.Default);
 
@@ -211,8 +215,10 @@
                 c2vm.notifyCompilationStatistics(id, method, entryBCI != INVOCATION_ENTRY_BCI, (int) processedBytes, time, timeUnitsPerSecond, installedCode);
             }
 
-            assert method.isQueuedForCompilation();
-            method.clearQueuedForCompilation();
+            if (clearFromCompilationQueue) {
+                assert method.isQueuedForCompilation();
+                method.clearQueuedForCompilation();
+            }
         }
     }
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,7 +22,9 @@
  */
 package com.oracle.graal.hotspot;
 
+import static com.oracle.graal.hotspot.CompileTheWorld.Options.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
+import static com.oracle.graal.nodes.StructuredGraph.*;
 import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.io.*;
@@ -32,12 +34,17 @@
 import java.util.jar.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.meta.ProfilingInfo.TriState;
 import com.oracle.graal.bytecode.*;
 import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.HotSpotOptions.OptionConsumer;
 import com.oracle.graal.hotspot.bridge.*;
 import com.oracle.graal.hotspot.meta.*;
-import com.oracle.graal.nodes.*;
+import com.oracle.graal.options.*;
+import com.oracle.graal.options.OptionValue.OverrideScope;
 import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.replacements.*;
 
 /**
@@ -46,21 +53,109 @@
 public final class CompileTheWorld {
 
     /**
-     * This is our magic token to trigger reading files from the boot class path.
+     * Magic token to trigger reading files from the boot class path.
      */
     public static final String SUN_BOOT_CLASS_PATH = "sun.boot.class.path";
 
+    public static class Options {
+        // @formatter:off
+        @Option(help = "Compile all methods in all classes on given class path")
+        public static final OptionValue<String> CompileTheWorldClasspath = new OptionValue<>(SUN_BOOT_CLASS_PATH);
+        @Option(help = "Verbose CompileTheWorld operation")
+        public static final OptionValue<Boolean> CompileTheWorldVerbose = new OptionValue<>(true);
+        @Option(help = "The number of CompileTheWorld iterations to perform")
+        public static final OptionValue<Integer> CompileTheWorldIterations = new OptionValue<>(1);
+        @Option(help = "First class to consider when using -XX:+CompileTheWorld")
+        public static final OptionValue<Integer> CompileTheWorldStartAt = new OptionValue<>(1);
+        @Option(help = "Last class to consider when using -XX:+CompileTheWorld")
+        public static final OptionValue<Integer> CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE);
+        @Option(help = "Option value overrides to use during compile the world. For example, " +
+                       "to disable inlining and partial escape analysis specify '-PartialEscapeAnalysis -Inline'. " +
+                       "The format for each option is the same as on the command line just without the '-G:' prefix.")
+        public static final OptionValue<String> CompileTheWorldConfig = new OptionValue<>(null);
+        // @formatter:on
+
+        /**
+         * Overrides {@link #CompileTheWorldStartAt} and {@link #CompileTheWorldStopAt} from
+         * {@code -XX} HotSpot options of the same name if the latter have non-default values.
+         */
+        static void overrideWithNativeOptions(HotSpotVMConfig c) {
+            if (c.compileTheWorldStartAt != 1) {
+                CompileTheWorldStartAt.setValue(c.compileTheWorldStartAt);
+            }
+            if (c.compileTheWorldStopAt != Integer.MAX_VALUE) {
+                CompileTheWorldStopAt.setValue(c.compileTheWorldStopAt);
+            }
+        }
+    }
+
+    /**
+     * A mechanism for overriding Graal options that affect compilation. A {@link Config} object
+     * should be used in a try-with-resources statement to ensure overriding of options is scoped
+     * properly. For example:
+     * 
+     * <pre>
+     *     Config config = ...;
+     *     try (AutoCloseable s = config == null ? null : config.apply()) {
+     *         // perform a Graal compilation
+     *     }
+     * </pre>
+     */
+    @SuppressWarnings("serial")
+    public static class Config extends HashMap<OptionValue<?>, Object> implements AutoCloseable, OptionConsumer {
+        OverrideScope scope;
+
+        /**
+         * Creates a {@link Config} object by parsing a set of space separated override options.
+         * 
+         * @param options a space separated set of option value settings with each option setting in
+         *            a format compatible with
+         *            {@link HotSpotOptions#parseOption(String, OptionConsumer)}. Ignored if null.
+         */
+        public Config(String options) {
+            if (options != null) {
+                for (String option : options.split("\\s+")) {
+                    if (!HotSpotOptions.parseOption(option, this)) {
+                        throw new GraalInternalError("Invalid option specified: %s", option);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Applies the overrides represented by this object. The overrides are in effect until
+         * {@link #close()} is called on this object.
+         */
+        Config apply() {
+            assert scope == null;
+            scope = OptionValue.override(this);
+            return this;
+        }
+
+        public void close() {
+            assert scope != null;
+            scope.close();
+
+            scope = null;
+
+        }
+
+        public void set(OptionDescriptor desc, Object value) {
+            put(desc.getOptionValue(), value);
+        }
+    }
+
     // Some runtime instances we need.
     private final HotSpotGraalRuntime runtime = runtime();
     private final VMToCompilerImpl vmToCompiler = (VMToCompilerImpl) runtime.getVMToCompiler();
 
-    /** List of Zip/Jar files to compile (see {@link GraalOptions#CompileTheWorld}. */
+    /** List of Zip/Jar files to compile (see {@link #CompileTheWorldClasspath}. */
     private final String files;
 
-    /** Class index to start compilation at (see {@link GraalOptions#CompileTheWorldStartAt}. */
+    /** Class index to start compilation at (see {@link #CompileTheWorldStartAt}. */
     private final int startAt;
 
-    /** Class index to stop compilation at (see {@link GraalOptions#CompileTheWorldStopAt}. */
+    /** Class index to stop compilation at (see {@link #CompileTheWorldStopAt}. */
     private final int stopAt;
 
     // Counters
@@ -69,44 +164,35 @@
     private long compileTime = 0;
 
     private boolean verbose;
+    private final Config config;
 
     /**
-     * Create a compile-the-world instance with default values from
-     * {@link GraalOptions#CompileTheWorld}, {@link GraalOptions#CompileTheWorldStartAt} and
-     * {@link GraalOptions#CompileTheWorldStopAt}.
-     */
-    public CompileTheWorld() {
-        this(CompileTheWorld.getValue(), CompileTheWorldStartAt.getValue(), CompileTheWorldStopAt.getValue(), true);
-    }
-
-    /**
-     * Create a compile-the-world instance.
+     * Creates a compile-the-world instance.
      * 
      * @param files {@link File#pathSeparator} separated list of Zip/Jar files to compile
      * @param startAt index of the class file to start compilation at
      * @param stopAt index of the class file to stop compilation at
      */
-    public CompileTheWorld(String files, int startAt, int stopAt, boolean verbose) {
+    public CompileTheWorld(String files, Config config, int startAt, int stopAt, boolean verbose) {
         this.files = files;
         this.startAt = startAt;
         this.stopAt = stopAt;
         this.verbose = verbose;
+        this.config = config;
 
         // We don't want the VM to exit when a method fails to compile...
-        ExitVMOnException.setValue(false);
+        config.put(ExitVMOnException, false);
 
         // ...but we want to see exceptions.
-        PrintBailout.setValue(true);
-        PrintStackTraceOnException.setValue(true);
+        config.put(PrintBailout, true);
+        config.put(PrintStackTraceOnException, true);
     }
 
     /**
-     * Compile all methods in all classes in the Zip/Jar files in
-     * {@link GraalOptions#CompileTheWorld}. If the GraalOptions.CompileTheWorld contains the magic
-     * token {@link CompileTheWorld#SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files
-     * from the boot class path.
-     * 
-     * @throws Throwable
+     * Compiles all methods in all classes in the Zip/Jar archive files in
+     * {@link #CompileTheWorldClasspath}. If {@link #CompileTheWorldClasspath} contains the magic
+     * token {@link #SUN_BOOT_CLASS_PATH} passed up from HotSpot we take the files from the boot
+     * class path.
      */
     public void compile() throws Throwable {
         if (SUN_BOOT_CLASS_PATH.equals(files)) {
@@ -145,7 +231,7 @@
     }
 
     /**
-     * Compile all methods in all classes in the Zip/Jar files passed.
+     * Compiles all methods in all classes in the Zip/Jar files passed.
      * 
      * @param fileList {@link File#pathSeparator} separated list of Zip/Jar files to compile
      * @throws Throwable
@@ -186,7 +272,7 @@
                 String className = je.getName().substring(0, je.getName().length() - ".class".length());
                 classFileCounter++;
 
-                try {
+                try (AutoCloseable s = config.apply()) {
                     // Load and initialize class
                     Class<?> javaClass = Class.forName(className.replace('/', '.'), true, loader);
 
@@ -229,16 +315,42 @@
         }
 
         println();
-        println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime);
+        TTY.println("CompileTheWorld : Done (%d classes, %d methods, %d ms)", classFileCounter, compiledMethodsCounter, compileTime);
     }
 
     /**
-     * Helper method to schedule a method for compilation and gather some statistics.
+     * A compilation task that creates a fresh compilation suite for its compilation. This is
+     * required so that a CTW compilation can be {@linkplain Config configured} differently from a
+     * VM triggered compilation.
+     */
+    static class CTWCompilationTask extends CompilationTask {
+
+        CTWCompilationTask(HotSpotBackend backend, PhasePlan plan, OptimisticOptimizations optimisticOpts, ProfilingInfo profilingInfo, HotSpotResolvedJavaMethod method, int entryBCI, int id) {
+            super(backend, plan, optimisticOpts, profilingInfo, method, entryBCI, id);
+        }
+
+        @Override
+        protected Suites getSuites(HotSpotProviders providers) {
+            return providers.getSuites().createSuites();
+        }
+    }
+
+    /**
+     * Compiles a method and gathers some statistics.
      */
     private void compileMethod(HotSpotResolvedJavaMethod method) {
         try {
             long start = System.currentTimeMillis();
-            vmToCompiler.compileMethod(method, StructuredGraph.INVOCATION_ENTRY_BCI, true);
+
+            // Be optimistic and return false for exceptionSeen.
+            final ProfilingInfo profilingInfo = DefaultProfilingInfo.get(TriState.FALSE);
+            final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo);
+            int id = vmToCompiler.allocateCompileTaskId();
+            HotSpotBackend backend = runtime.getHostBackend();
+            PhasePlan phasePlan = vmToCompiler.createPhasePlan(backend.getProviders(), optimisticOpts, false);
+            CompilationTask task = new CTWCompilationTask(backend, phasePlan, optimisticOpts, profilingInfo, method, INVOCATION_ENTRY_BCI, id);
+            task.runCompilation(false);
+
             compileTime += (System.currentTimeMillis() - start);
             compiledMethodsCounter++;
             method.reprofile();  // makes the method also not-entrant
@@ -250,18 +362,16 @@
     }
 
     /**
-     * Helper method for CompileTheWorld to determine if a method should be compiled (Cf.
-     * CompilationPolicy::can_be_compiled).
+     * Determines if a method should be compiled (Cf. CompilationPolicy::can_be_compiled).
      * 
      * @return true if it can be compiled, false otherwise
      */
-    private static boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
+    private boolean canBeCompiled(HotSpotResolvedJavaMethod javaMethod, int modifiers) {
         if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
             return false;
         }
-        // This number is from HotSpot:
-        final int hugeMethodLimit = 8000;
-        if (javaMethod.getCodeSize() > hugeMethodLimit) {
+        HotSpotVMConfig c = runtime.getConfig();
+        if (c.dontCompileHugeMethods && javaMethod.getCodeSize() > c.hugeMethodLimit) {
             return false;
         }
         // Skip @Snippets for now
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotGraalRuntime.java	Tue Dec 17 21:26:42 2013 -0800
@@ -24,6 +24,7 @@
 
 import static com.oracle.graal.graph.UnsafeAccess.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.Options.*;
+import static com.oracle.graal.phases.GraalOptions.*;
 
 import java.lang.reflect.*;
 import java.util.*;
@@ -231,24 +232,15 @@
         initMirror(typeDouble);
         initMirror(typeVoid);
 
-        // Set some global options:
-        if (config.compileTheWorld) {
-            GraalOptions.CompileTheWorld.setValue(CompileTheWorld.SUN_BOOT_CLASS_PATH);
-        }
-        if (config.compileTheWorldStartAt != 1) {
-            GraalOptions.CompileTheWorldStartAt.setValue(config.compileTheWorldStartAt);
-        }
-        if (config.compileTheWorldStopAt != Integer.MAX_VALUE) {
-            GraalOptions.CompileTheWorldStopAt.setValue(config.compileTheWorldStopAt);
-        }
+        CompileTheWorld.Options.overrideWithNativeOptions(config);
 
         // Only set HotSpotPrintCompilation and HotSpotPrintInlining if they still have their
         // default value (false).
-        if (GraalOptions.HotSpotPrintCompilation.getValue() == false) {
-            GraalOptions.HotSpotPrintCompilation.setValue(config.printCompilation);
+        if (HotSpotPrintCompilation.getValue() == false) {
+            HotSpotPrintCompilation.setValue(config.printCompilation);
         }
-        if (GraalOptions.HotSpotPrintInlining.getValue() == false) {
-            GraalOptions.HotSpotPrintInlining.setValue(config.printInlining);
+        if (HotSpotPrintInlining.getValue() == false) {
+            HotSpotPrintInlining.setValue(config.printInlining);
         }
 
         if (Boolean.valueOf(System.getProperty("graal.printconfig"))) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotHostBackend.java	Tue Dec 17 21:26:42 2013 -0800
@@ -40,11 +40,11 @@
     /**
      * This will be 0 if stack banging is disabled.
      */
-    protected final int stackShadowPages;
+    protected final int pagesToBang;
 
     public HotSpotHostBackend(HotSpotGraalRuntime runtime, HotSpotProviders providers) {
         super(runtime, providers);
-        this.stackShadowPages = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0;
+        this.pagesToBang = runtime.getConfig().useStackBanging ? runtime.getConfig().stackShadowPages : 0;
     }
 
     @Override
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotOptions.java	Tue Dec 17 21:26:42 2013 -0800
@@ -103,6 +103,22 @@
 
     // Called from VM code
     public static boolean setOption(String option) {
+        return parseOption(option, null);
+    }
+
+    interface OptionConsumer {
+        void set(OptionDescriptor desc, Object value);
+    }
+
+    /**
+     * Parses a given option value specification.
+     * 
+     * @param option the specification of an option and its value
+     * @param setter the object to notify of the parsed option and value. If null, the
+     *            {@link OptionValue#setValue(Object)} method of the specified option is called
+     *            instead.
+     */
+    public static boolean parseOption(String option, OptionConsumer setter) {
         if (option.length() == 0) {
             return false;
         }
@@ -175,9 +191,13 @@
         }
 
         if (value != null) {
-            OptionValue<?> optionValue = desc.getOptionValue();
-            optionValue.setValue(value);
-            // Logger.info("Set option " + desc.getName() + " to " + value);
+            if (setter != null) {
+                setter.set(desc, value);
+            } else {
+                OptionValue<?> optionValue = desc.getOptionValue();
+                optionValue.setValue(value);
+                // Logger.info("Set option " + desc.getName() + " to " + value);
+            }
         } else {
             Logger.info("Wrong value \"" + valueString + "\" for option " + optionName);
             return false;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/HotSpotVMConfig.java	Tue Dec 17 21:26:42 2013 -0800
@@ -662,6 +662,8 @@
     @HotSpotVMFlag(name = "CompileTheWorld") @Stable public boolean compileTheWorld;
     @HotSpotVMFlag(name = "CompileTheWorldStartAt") @Stable public int compileTheWorldStartAt;
     @HotSpotVMFlag(name = "CompileTheWorldStopAt") @Stable public int compileTheWorldStopAt;
+    @HotSpotVMFlag(name = "DontCompileHugeMethods") @Stable public boolean dontCompileHugeMethods;
+    @HotSpotVMFlag(name = "HugeMethodLimit") @Stable public int hugeMethodLimit;
     @HotSpotVMFlag(name = "PrintCompilation") @Stable public boolean printCompilation;
     @HotSpotVMFlag(name = "PrintInlining") @Stable public boolean printInlining;
     @HotSpotVMFlag(name = "GraalUseFastLocking") @Stable public boolean useFastLocking;
@@ -669,6 +671,7 @@
     @HotSpotVMFlag(name = "UseTLAB") @Stable public boolean useTLAB;
     @HotSpotVMFlag(name = "UseBiasedLocking") @Stable public boolean useBiasedLocking;
     @HotSpotVMFlag(name = "UsePopCountInstruction") @Stable public boolean usePopCountInstruction;
+    @HotSpotVMFlag(name = "UseCountLeadingZerosInstruction") @Stable public boolean useCountLeadingZerosInstruction;
     @HotSpotVMFlag(name = "UseAESIntrinsics") @Stable public boolean useAESIntrinsics;
     @HotSpotVMFlag(name = "UseCRC32Intrinsics") @Stable public boolean useCRC32Intrinsics;
     @HotSpotVMFlag(name = "UseG1GC") @Stable public boolean useG1GC;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVM.java	Tue Dec 17 21:26:42 2013 -0800
@@ -73,11 +73,10 @@
      * Used to implement {@link ResolvedJavaType#findUniqueConcreteMethod(ResolvedJavaMethod)}.
      * 
      * @param metaspaceMethod the metaspace Method on which to based the search
-     * @param resultHolder the holder of the result is put in element 0 of this array
      * @return the metaspace Method result or 0 is there is no unique concrete method for
      *         {@code metaspaceMethod}
      */
-    long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder);
+    long findUniqueConcreteMethod(long metaspaceMethod);
 
     /**
      * Used to determine if an interface has exactly one implementor.
@@ -189,6 +188,8 @@
      */
     void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethod method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond, HotSpotInstalledCode installedCode);
 
+    void printCompilationStatistics(boolean perCompiler, boolean aggregate);
+
     void resetCompilationStatistics();
 
     void initializeConfiguration(HotSpotVMConfig config);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/CompilerToVMImpl.java	Tue Dec 17 21:26:42 2013 -0800
@@ -56,7 +56,7 @@
     public native boolean isMethodCompilable(long metaspaceMethod);
 
     @Override
-    public native long getUniqueConcreteMethod(long metaspaceMethod, HotSpotResolvedObjectType[] resultHolder);
+    public native long findUniqueConcreteMethod(long metaspaceMethod);
 
     @Override
     public native ResolvedJavaType getUniqueImplementor(HotSpotResolvedObjectType interfaceType);
@@ -154,6 +154,8 @@
     public synchronized native void notifyCompilationStatistics(int id, HotSpotResolvedJavaMethod method, boolean osr, int processedBytecodes, long time, long timeUnitsPerSecond,
                     HotSpotInstalledCode installedCode);
 
+    public synchronized native void printCompilationStatistics(boolean perCompiler, boolean aggregate);
+
     public native void resetCompilationStatistics();
 
     /**
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Tue Dec 17 21:26:42 2013 -0800
@@ -25,6 +25,7 @@
 
 import static com.oracle.graal.compiler.GraalDebugConfig.*;
 import static com.oracle.graal.graph.UnsafeAccess.*;
+import static com.oracle.graal.hotspot.CompileTheWorld.Options.*;
 import static com.oracle.graal.hotspot.HotSpotGraalRuntime.*;
 
 import java.io.*;
@@ -42,6 +43,7 @@
 import com.oracle.graal.debug.internal.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.CompileTheWorld.Config;
 import com.oracle.graal.hotspot.debug.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.phases.*;
@@ -65,6 +67,10 @@
     @Option(help = "Print compilation queue activity periodically")
     private static final OptionValue<Boolean> PrintQueue = new OptionValue<>(false);
 
+    @Option(help = "Interval in milliseconds at which to print compilation rate periodically. " +
+                   "The compilation statistics are reset after each print out.")
+    private static final OptionValue<Integer> PrintCompRate = new OptionValue<>(0);
+
     @Option(help = "Print bootstrap progress and summary")
     private static final OptionValue<Boolean> PrintBootstrap = new OptionValue<>(true);
 
@@ -97,6 +103,10 @@
         this.runtime = runtime;
     }
 
+    public int allocateCompileTaskId() {
+        return compileTaskIds.incrementAndGet();
+    }
+
     public void startCompiler(boolean bootstrapEnabled) throws Throwable {
 
         FastNodeClassRegistry.initialize();
@@ -112,8 +122,6 @@
             }
         }
 
-        runtime.getCompilerToVM();
-
         TTY.initialize(log);
 
         if (Log.getValue() == null && Meter.getValue() == null && Time.getValue() == null && Dump.getValue() == null) {
@@ -178,6 +186,29 @@
             t.start();
         }
 
+        if (PrintCompRate.getValue() != 0) {
+            if (!runtime.getConfig().ciTime && !runtime.getConfig().ciTimeEach) {
+                TTY.println("PrintCompRate requires CITime or CITimeEach");
+            } else {
+                Thread t = new Thread() {
+
+                    @Override
+                    public void run() {
+                        while (true) {
+                            runtime.getCompilerToVM().printCompilationStatistics(true, false);
+                            runtime.getCompilerToVM().resetCompilationStatistics();
+                            try {
+                                Thread.sleep(PrintCompRate.getValue());
+                            } catch (InterruptedException e) {
+                            }
+                        }
+                    }
+                };
+                t.setDaemon(true);
+                t.start();
+            }
+        }
+
         BenchmarkCounters.initialize(runtime.getCompilerToVM());
 
         compilerStartTime = System.nanoTime();
@@ -303,7 +334,14 @@
     }
 
     public void compileTheWorld() throws Throwable {
-        new CompileTheWorld().compile();
+        int iterations = CompileTheWorld.Options.CompileTheWorldIterations.getValue();
+        for (int i = 0; i < iterations; i++) {
+            runtime.getCompilerToVM().resetCompilationStatistics();
+            TTY.println("CompileTheWorld : iteration " + i);
+            CompileTheWorld ctw = new CompileTheWorld(CompileTheWorldClasspath.getValue(), new Config(CompileTheWorldConfig.getValue()), CompileTheWorldStartAt.getValue(),
+                            CompileTheWorldStopAt.getValue(), CompileTheWorldVerbose.getValue());
+            ctw.compile();
+        }
         System.exit(0);
     }
 
@@ -556,12 +594,12 @@
 
                 final ProfilingInfo profilingInfo = method.getCompilationProfilingInfo(osrCompilation);
                 final OptimisticOptimizations optimisticOpts = new OptimisticOptimizations(profilingInfo);
-                int id = compileTaskIds.incrementAndGet();
+                int id = allocateCompileTaskId();
                 HotSpotBackend backend = runtime.getHostBackend();
                 CompilationTask task = CompilationTask.create(backend, createPhasePlan(backend.getProviders(), optimisticOpts, osrCompilation), optimisticOpts, profilingInfo, method, entryBCI, id);
 
                 if (blocking) {
-                    task.runCompilation();
+                    task.runCompilation(true);
                 } else {
                     try {
                         method.setCurrentTask(task);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotMethodData.java	Tue Dec 17 21:26:42 2013 -0800
@@ -397,7 +397,7 @@
 
         @Override
         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
-            return sb.append(format("count(%d)", getCounterValue(data, pos)));
+            return sb.append(format("count(%d) null_seen(%s) exception_seen(%s)", getCounterValue(data, pos), getNullSeen(data, pos), getExceptionSeen(data, pos)));
         }
     }
 
@@ -524,7 +524,9 @@
         public StringBuilder appendTo(StringBuilder sb, HotSpotMethodData data, int pos) {
             RawItemProfile<ResolvedJavaType> profile = getRawTypeProfile(data, pos);
             TriState nullSeen = getNullSeen(data, pos);
-            sb.append(format("count(%d) null_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, getTypesNotRecordedExecutionCount(data, pos), profile.entries));
+            TriState exceptionSeen = getExceptionSeen(data, pos);
+            sb.append(format("count(%d) null_seen(%s) exception_seen(%s) nonprofiled_count(%d) entries(%d)", getCounterValue(data, pos), nullSeen, exceptionSeen,
+                            getTypesNotRecordedExecutionCount(data, pos), profile.entries));
             for (int i = 0; i < profile.entries; i++) {
                 long count = profile.counts[i];
                 sb.append(format("%n  %s (%d, %4.2f)", MetaUtil.toJavaName(profile.items[i]), count, (double) count / profile.totalCount));
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaField.java	Tue Dec 17 21:26:42 2013 -0800
@@ -41,7 +41,7 @@
 /**
  * Represents a field in a HotSpot type.
  */
-public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField, LocationIdentity {
+public class HotSpotResolvedJavaField extends CompilerObject implements ResolvedJavaField {
 
     // Must not conflict with any fields flags used by the VM - the assertion in the constructor
     // checks this assumption
@@ -117,7 +117,7 @@
         return false;
     }
 
-    private static final Set<ResolvedJavaField> notEmbeddable = new HashSet<>();
+    private static final List<ResolvedJavaField> notEmbeddable = new ArrayList<>();
 
     private static void addResolvedToSet(Field field) {
         MetaAccessProvider metaAccess = runtime().getHostProviders().getMetaAccess();
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotResolvedJavaMethod.java	Tue Dec 17 21:26:42 2013 -0800
@@ -320,13 +320,22 @@
     }
 
     public ResolvedJavaMethod uniqueConcreteMethod() {
-        HotSpotResolvedObjectType[] resultHolder = {null};
-        long ucm = runtime().getCompilerToVM().getUniqueConcreteMethod(metaspaceMethod, resultHolder);
-        if (ucm != 0L) {
-            assert resultHolder[0] != null;
-            return resultHolder[0].createMethod(ucm);
+        if (holder.isInterface()) {
+            // Cannot trust interfaces. Because of:
+            // interface I { void foo(); }
+            // class A { public void foo() {} }
+            // class B extends A implements I { }
+            // class C extends B { public void foo() { } }
+            // class D extends B { }
+            // Would lead to identify C.foo() as the unique concrete method for I.foo() without
+            // seeing A.foo().
+            return null;
         }
-        return null;
+        final long uniqueConcreteMethod = runtime().getCompilerToVM().findUniqueConcreteMethod(metaspaceMethod);
+        if (uniqueConcreteMethod == 0) {
+            return null;
+        }
+        return fromMetaspace(uniqueConcreteMethod);
     }
 
     @Override
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/BciBlockMapping.java	Tue Dec 17 21:26:42 2013 -0800
@@ -434,7 +434,7 @@
         predecessor.successors.add(sux);
     }
 
-    private final HashSet<Block> jsrVisited = new HashSet<>();
+    private final ArrayList<Block> jsrVisited = new ArrayList<>();
 
     private void createJsrAlternatives(Block block) {
         jsrVisited.add(block);
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -217,7 +217,7 @@
             TTY.println(MetaUtil.indent(MetaUtil.profileToString(profilingInfo, method, CodeUtil.NEW_LINE), "  "));
         }
 
-        Indent indent = Debug.logAndIndent(false, "build graph for %s", method.toString());
+        Indent indent = Debug.logAndIndent(false, "build graph for %s", method);
 
         // compute the block map, setup exception handlers and get the entrypoint(s)
         BciBlockMapping blockMap = createBlockMap();
--- a/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir.amd64/src/com/oracle/graal/lir/amd64/AMD64ControlFlow.java	Tue Dec 17 21:26:42 2013 -0800
@@ -73,12 +73,11 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, AMD64MacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 jcc(masm, true, falseDestination);
             } else {
                 jcc(masm, false, trueDestination);
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILArithmetic.java	Tue Dec 17 21:26:42 2013 -0800
@@ -29,14 +29,17 @@
 import com.oracle.graal.asm.hsail.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.asm.*;
+import com.oracle.graal.lir.asm.CompilationResultBuilder;
 
 /**
  * Defines arithmetic instruction nodes.
  */
 public enum HSAILArithmetic {
+    ABS,
     CALL,
+    CEIL,
     FDIV,
+    FLOOR,
     FREM,
     DADD,
     DDIV,
@@ -97,6 +100,7 @@
     LUSUB,
     LXOR,
     OADD,
+    RINT,
     SQRT,
     UNDEF;
 
@@ -289,10 +293,33 @@
         }
     }
 
+    /**
+     * Emits the HSAIL code for an arithmetic operation taking one input parameter.
+     * 
+     * @param crb the CompilationResultBuilder
+     * @param masm the HSAIL assembler
+     * @param opcode the opcode of the arithmetic operation
+     * @param dst the destination
+     * @param src the source parameter
+     * @param info structure that stores the LIRFrameState. Used for exception handling.
+     */
+
     public static void emit(CompilationResultBuilder crb, HSAILAssembler masm, HSAILArithmetic opcode, Value dst, Value src, LIRFrameState info) {
         int exceptionOffset = -1;
         if (isRegister(src)) {
             switch (opcode) {
+                case ABS:
+                    masm.emit("abs", dst, src);
+                    break;
+                case CEIL:
+                    masm.emit("ceil", dst, src);
+                    break;
+                case FLOOR:
+                    masm.emit("floor", dst, src);
+                    break;
+                case RINT:
+                    masm.emit("rint", dst, src);
+                    break;
                 case SQRT:
                     masm.emit("sqrt", dst, src);
                     break;
@@ -304,14 +331,12 @@
                     break;
                 case INOT:
                 case LNOT:
-                    // Emit the HSAIL instruction for a bitwise not.
                     masm.emitForceBitwise("not", dst, src);
                     break;
                 case INEG:
                 case LNEG:
                 case FNEG:
                 case DNEG:
-                    // Emit the HSAIL instruction for a negate operation.
                     masm.emit("neg", dst, src);
                     break;
                 default:
--- a/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir.hsail/src/com/oracle/graal/lir/hsail/HSAILControlFlow.java	Tue Dec 17 21:26:42 2013 -0800
@@ -188,14 +188,13 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, HSAILAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 HSAILCompare.emit(crb, masm, condition.negate(), x, y, z, !unordered);
                 masm.cbr(masm.nameOf(falseDestination.label()));
             } else {
                 HSAILCompare.emit(crb, masm, condition, x, y, z, unordered);
                 masm.cbr(masm.nameOf(trueDestination.label()));
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir.ptx/src/com/oracle/graal/lir/ptx/PTXControlFlow.java	Tue Dec 17 21:26:42 2013 -0800
@@ -85,12 +85,11 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, PTXMacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 masm.bra(masm.nameOf(falseDestination.label()), predRegNum);
             } else {
                 masm.bra(masm.nameOf(trueDestination.label()));
-                if (!falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+                if (!crb.isSuccessorEdge(falseDestination)) {
                     masm.jmp(falseDestination.label());
                 }
             }
--- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCControlFlow.java	Tue Dec 17 21:26:42 2013 -0800
@@ -73,18 +73,17 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
-            int sourceIndex = crb.getCurrentBlockIndex();
             Label actualTarget;
             Condition actualCondition;
             boolean needJump;
-            if (trueDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex)) {
+            if (crb.isSuccessorEdge(trueDestination)) {
                 actualCondition = condition.negate();
                 actualTarget = falseDestination.label();
                 needJump = false;
             } else {
                 actualCondition = condition;
                 actualTarget = trueDestination.label();
-                needJump = !falseDestination.isCodeEmittingOrderSuccessorEdge(sourceIndex);
+                needJump = !crb.isSuccessorEdge(falseDestination);
             }
             assert kind == Kind.Int || kind == Kind.Long || kind == Kind.Object;
             CC cc = kind == Kind.Int ? CC.Icc : CC.Xcc;
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIR.java	Tue Dec 17 21:26:42 2013 -0800
@@ -25,10 +25,8 @@
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
+import com.oracle.graal.lir.LIRInstruction.StateProcedure;
 import com.oracle.graal.lir.StandardOp.BlockEndOp;
-import com.oracle.graal.lir.asm.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 
@@ -137,46 +135,6 @@
         firstVariableNumber = num;
     }
 
-    public void emitCode(CompilationResultBuilder crb) {
-        crb.frameContext.enter(crb);
-
-        // notifyBlocksOfSuccessors();
-
-        int index = 0;
-        for (Block b : codeEmittingOrder) {
-            crb.setCurrentBlockIndex(index++);
-            emitBlock(crb, b);
-        }
-    }
-
-    private void emitBlock(CompilationResultBuilder crb, Block block) {
-        if (Debug.isDumpEnabled()) {
-            crb.blockComment(String.format("block B%d %s", block.getId(), block.getLoop()));
-        }
-
-        for (LIRInstruction op : lir(block)) {
-            if (Debug.isDumpEnabled()) {
-                crb.blockComment(String.format("%d %s", op.id(), op));
-            }
-
-            try {
-                emitOp(crb, op);
-            } catch (GraalInternalError e) {
-                throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + codeEmittingOrder);
-            }
-        }
-    }
-
-    private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) {
-        try {
-            op.emitCode(crb);
-        } catch (AssertionError t) {
-            throw new GraalInternalError(t);
-        } catch (RuntimeException t) {
-            throw new GraalInternalError(t);
-        }
-    }
-
     public void setHasArgInCallerFrame() {
         hasArgInCallerFrame = true;
     }
@@ -189,15 +147,55 @@
         return hasArgInCallerFrame;
     }
 
+    /**
+     * Gets the exception edge (if any) originating at a given operation.
+     */
+    public static LabelRef getExceptionEdge(LIRInstruction op) {
+        final LabelRef[] exceptionEdge = {null};
+        op.forEachState(new StateProcedure() {
+            @Override
+            protected void doState(LIRFrameState state) {
+                if (state.exceptionEdge != null) {
+                    assert exceptionEdge[0] == null;
+                    exceptionEdge[0] = state.exceptionEdge;
+                }
+            }
+        });
+        return exceptionEdge[0];
+    }
+
+    /**
+     * The maximum distance an operation with an {@linkplain #getExceptionEdge(LIRInstruction)
+     * exception edge} can be from the last instruction of a LIR block. The value of 3 is based on a
+     * non-void call operation that has an exception edge. Such a call may move the result to
+     * another register and then spill it.
+     * <p>
+     * The rationale for such a constant is to limit the search for an insertion point when adding
+     * move operations at the end of a block. Such moves must be inserted before all control flow
+     * instructions.
+     */
+    public static final int MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END = 3;
+
     public static boolean verifyBlock(LIR lir, Block block) {
         List<LIRInstruction> ops = lir.lir(block);
         if (ops.size() == 0) {
-            return true;
+            return false;
         }
-        for (LIRInstruction op : ops.subList(0, ops.size() - 1)) {
+        LIRInstruction opWithExceptionEdge = null;
+        int index = 0;
+        int lastIndex = ops.size() - 1;
+        for (LIRInstruction op : ops.subList(0, lastIndex)) {
             assert !(op instanceof BlockEndOp) : op.getClass();
+            LabelRef exceptionEdge = getExceptionEdge(op);
+            if (exceptionEdge != null) {
+                assert opWithExceptionEdge == null : "multiple ops with an exception edge not allowed";
+                opWithExceptionEdge = op;
+                int distanceFromEnd = lastIndex - index;
+                assert distanceFromEnd <= MAX_EXCEPTION_EDGE_OP_DISTANCE_FROM_END;
+            }
+            index++;
         }
-        LIRInstruction end = ops.get(ops.size() - 1);
+        LIRInstruction end = ops.get(lastIndex);
         assert end instanceof BlockEndOp : end.getClass();
         return true;
     }
@@ -210,7 +208,9 @@
             for (Block pred : block.getPredecessors()) {
                 assert blocks.contains(pred) : "missing predecessor from: " + block + "to: " + pred;
             }
-            verifyBlock(lir, block);
+            if (!verifyBlock(lir, block)) {
+                return false;
+            }
         }
         return true;
     }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRFrameState.java	Tue Dec 17 21:26:42 2013 -0800
@@ -41,7 +41,7 @@
     public final BytecodeFrame topFrame;
     private final VirtualObject[] virtualObjects;
     public final LabelRef exceptionEdge;
-    private DebugInfo debugInfo;
+    protected DebugInfo debugInfo;
 
     public LIRFrameState(BytecodeFrame topFrame, VirtualObject[] virtualObjects, LabelRef exceptionEdge) {
         this.topFrame = topFrame;
@@ -109,8 +109,45 @@
         }
     }
 
-    public void finish(BitSet registerRefMap, BitSet frameRefMap) {
-        debugInfo = new DebugInfo(topFrame, registerRefMap, frameRefMap);
+    /**
+     * Called by the register allocator before {@link #markLocation} to initialize the frame state.
+     * 
+     * @param frameMap The frame map.
+     * @param canHaveRegisters True if there can be any register map entries.
+     */
+    public void initDebugInfo(FrameMap frameMap, boolean canHaveRegisters) {
+        BitSet registerRefMap = (canHaveRegisters ? frameMap.initRegisterRefMap() : null);
+        debugInfo = new DebugInfo(topFrame, registerRefMap, frameMap.initFrameRefMap());
+    }
+
+    /**
+     * Called by the register allocator to mark the specified location as a reference in the
+     * reference map of the debug information. The tracked location can be a {@link RegisterValue}
+     * or a {@link StackSlot}. Note that a {@link Constant} is automatically tracked.
+     * 
+     * @param location The location to be added to the reference map.
+     * @param frameMap The frame map.
+     */
+    public void markLocation(Value location, FrameMap frameMap) {
+        if (location.getKind() == Kind.Object) {
+            if (isRegister(location)) {
+                debugInfo.getRegisterRefMap().set(asRegister(location).number);
+            } else if (isStackSlot(location)) {
+                int index = frameMap.indexForStackSlot(asStackSlot(location));
+                debugInfo.getFrameRefMap().set(index);
+            } else {
+                assert isConstant(location);
+            }
+        }
+    }
+
+    /**
+     * Called by the register allocator after all locations are marked.
+     * 
+     * @param op The instruction to which this frame state belongs.
+     * @param frameMap The frame map.
+     */
+    public void finish(LIRInstruction op, FrameMap frameMap) {
     }
 
     @Override
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LabelRef.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.lir;
 
-import java.util.*;
-
 import com.oracle.graal.asm.*;
 import com.oracle.graal.lir.StandardOp.BranchOp;
 import com.oracle.graal.lir.StandardOp.JumpOp;
@@ -78,21 +76,8 @@
         return ((StandardOp.LabelOp) lir.lir(getTargetBlock()).get(0)).getLabel();
     }
 
-    /**
-     * Determines if the edge represented by this object is from a block to its lexical successor in
-     * the code emitting order of blocks.
-     * 
-     * @param sourceIndex the index of this edge's {@linkplain #getSourceBlock() source} in the code
-     *            emitting order
-     */
-    public boolean isCodeEmittingOrderSuccessorEdge(int sourceIndex) {
-        List<Block> order = lir.codeEmittingOrder();
-        assert order.get(sourceIndex) == block;
-        return sourceIndex < order.size() - 1 && order.get(sourceIndex + 1) == getTargetBlock();
-    }
-
     @Override
     public String toString() {
-        return suxIndex < block.getSuccessorCount() ? getTargetBlock().toString() : "?" + block + ":" + suxIndex + "?";
+        return getSourceBlock() + " -> " + (suxIndex < block.getSuccessors().size() ? getTargetBlock() : "?");
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/RedundantMoveElimination.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,462 @@
+/*
+ * 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.lir;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.StandardOp.MoveOp;
+import com.oracle.graal.lir.LIRInstruction.*;
+import com.oracle.graal.nodes.cfg.*;
+
+/**
+ * Removes move instructions, where the destination value is already in place.
+ */
+public final class RedundantMoveElimination {
+
+    public static void optimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) {
+        RedundantMoveElimination redundantMoveElimination = new RedundantMoveElimination();
+        redundantMoveElimination.doOptimize(lir, frameMap, method);
+    }
+
+    /**
+     * Holds the entry and exit states for each block for dataflow analysis. The state is an array
+     * with an element for each relevant location (register or stack slot). Each element holds the
+     * global number of the location's definition. A location definition is simply an output of an
+     * instruction. Note that because instructions can have multiple outputs it is not possible to
+     * use the instruction id for value numbering. In addition, the result of merging at block
+     * entries (= phi values) get unique value numbers.
+     * 
+     * The value numbers also contain information if it is an object kind value or not: if the
+     * number is negative it is an object kind value.
+     */
+    private static class BlockData {
+
+        BlockData(int stateSize) {
+            entryState = new int[stateSize];
+            exitState = new int[stateSize];
+        }
+
+        /*
+         * The state at block entry for global dataflow analysis. It contains a global value number
+         * for each location to optimize.
+         */
+        int[] entryState;
+
+        /*
+         * The state at block exit for global dataflow analysis. It contains a global value number
+         * for each location to optimize.
+         */
+        int[] exitState;
+
+        /*
+         * The starting number for global value numbering in this block.
+         */
+        int entryValueNum;
+    }
+
+    Map<Block, BlockData> blockData = new HashMap<>();
+
+    Register[] callerSaveRegs;
+
+    Map<StackSlot, Integer> stackIndices = new HashMap<>();
+
+    int numRegs;
+
+    /*
+     * Pseudo value for a not yet assigned location.
+     */
+    static final int INIT_VALUE = 0;
+
+    /**
+     * The main method doing the elimination of redundant moves.
+     */
+    private void doOptimize(LIR lir, FrameMap frameMap, ResolvedJavaMethod method) {
+
+        try (Indent indent = Debug.logAndIndent(false, "eliminate redundant moves in %s", method)) {
+
+            callerSaveRegs = frameMap.registerConfig.getCallerSaveRegisters();
+
+            initBlockData(lir);
+
+            solveDataFlow(lir);
+
+            eliminateMoves(lir);
+        }
+    }
+
+    private void initBlockData(LIR lir) {
+
+        List<Block> blocks = lir.linearScanOrder();
+        numRegs = 0;
+
+        /*
+         * Search for relevant locations which can be optimized. These are register or stack slots
+         * which occur as destinations of move instructions.
+         */
+        for (Block block : blocks) {
+            List<LIRInstruction> instructions = lir.lir(block);
+            for (LIRInstruction op : instructions) {
+                if (isEligibleMove(op)) {
+                    Value dest = ((MoveOp) op).getResult();
+                    if (isRegister(dest)) {
+                        int regNum = ((RegisterValue) dest).getRegister().number;
+                        if (regNum >= numRegs) {
+                            numRegs = regNum + 1;
+                        }
+                    } else if (isStackSlot(dest)) {
+                        StackSlot stackSlot = (StackSlot) dest;
+                        if (!stackIndices.containsKey(stackSlot)) {
+                            stackIndices.put(stackSlot, stackIndices.size());
+                        }
+                    }
+                }
+            }
+        }
+
+        /*
+         * Now we know the number of locations to optimize, so we can allocate the block states.
+         */
+        int numLocations = numRegs + stackIndices.size();
+        Debug.log("num locations = %d (regs = %d, stack = %d)", numLocations, numRegs, stackIndices.size());
+        for (Block block : blocks) {
+            BlockData data = new BlockData(numLocations);
+            blockData.put(block, data);
+        }
+    }
+
+    /**
+     * Calculates the entry and exit states for all basic blocks.
+     */
+    private void solveDataFlow(LIR lir) {
+
+        Indent indent = Debug.logAndIndent("solve data flow");
+
+        List<Block> blocks = lir.linearScanOrder();
+
+        /*
+         * Iterate until there are no more changes.
+         */
+        int currentValueNum = 1;
+        boolean firstRound = true;
+        boolean changed;
+        do {
+            changed = false;
+            Indent indent2 = indent.logAndIndent("new iteration");
+
+            for (Block block : blocks) {
+
+                BlockData data = blockData.get(block);
+                /*
+                 * Initialize the number for global value numbering for this block. It is essential
+                 * that the starting number for a block is consistent at all iterations and also in
+                 * eliminateMoves().
+                 */
+                if (firstRound) {
+                    data.entryValueNum = currentValueNum;
+                }
+                int valueNum = data.entryValueNum;
+                assert valueNum > 0;
+                boolean newState = false;
+
+                if (block == blocks.get(0) || block.isExceptionEntry()) {
+                    /*
+                     * The entry block has undefined values. And also exception handler blocks: the
+                     * LinearScan can insert moves at the end of an exception handler predecessor
+                     * block (after the invoke, which throws the exception), and in reality such
+                     * moves are not in the control flow in case of an exception. So we assume a
+                     * save default for exception handler blocks.
+                     */
+                    indent2.log("kill all values at entry of block %d", block.getId());
+                    clearValues(data.entryState, valueNum);
+                } else {
+                    /*
+                     * Merge the states of predecessor blocks
+                     */
+                    for (Block predecessor : block.getPredecessors()) {
+                        BlockData predData = blockData.get(predecessor);
+                        newState |= mergeState(data.entryState, predData.exitState, valueNum);
+                    }
+                }
+                // Advance by the value numbers which are "consumed" by clearValues and mergeState
+                valueNum += data.entryState.length;
+
+                if (newState || firstRound) {
+
+                    Indent indent3 = indent2.logAndIndent("update block %d", block.getId());
+
+                    /*
+                     * Derive the exit state from the entry state by iterating through all
+                     * instructions of the block.
+                     */
+                    int[] iterState = data.exitState;
+                    copyState(iterState, data.entryState);
+                    List<LIRInstruction> instructions = lir.lir(block);
+
+                    for (LIRInstruction op : instructions) {
+                        valueNum = updateState(iterState, op, valueNum);
+                    }
+                    changed = true;
+                    indent3.outdent();
+                }
+                if (firstRound) {
+                    currentValueNum = valueNum;
+                }
+            }
+            firstRound = false;
+            indent2.outdent();
+
+        } while (changed);
+
+        indent.outdent();
+    }
+
+    /**
+     * Deletes all move instructions where the target location already contains the source value.
+     */
+    private void eliminateMoves(LIR lir) {
+
+        Indent indent = Debug.logAndIndent("eliminate moves");
+
+        List<Block> blocks = lir.linearScanOrder();
+
+        for (Block block : blocks) {
+
+            Indent indent2 = indent.logAndIndent("eliminate moves in block %d", block.getId());
+
+            List<LIRInstruction> instructions = lir.lir(block);
+            BlockData data = blockData.get(block);
+            boolean hasDead = false;
+
+            // Reuse the entry state for iteration, we don't need it later.
+            int[] iterState = data.entryState;
+
+            // Add the values which are "consumed" by clearValues and mergeState in solveDataFlow
+            int valueNum = data.entryValueNum + data.entryState.length;
+
+            int numInsts = instructions.size();
+            for (int idx = 0; idx < numInsts; idx++) {
+                LIRInstruction op = instructions.get(idx);
+                if (isEligibleMove(op)) {
+                    MoveOp moveOp = (MoveOp) op;
+                    int sourceIdx = getStateIdx(moveOp.getInput());
+                    int destIdx = getStateIdx(moveOp.getResult());
+                    if (sourceIdx >= 0 && destIdx >= 0 && iterState[sourceIdx] == iterState[destIdx]) {
+                        assert iterState[sourceIdx] != INIT_VALUE;
+                        indent2.log("delete move %s", op);
+                        instructions.set(idx, null);
+                        hasDead = true;
+                    }
+                }
+                // It doesn't harm if updateState is also called for a deleted move
+                valueNum = updateState(iterState, op, valueNum);
+            }
+            if (hasDead) {
+                instructions.removeAll(Collections.singleton(null));
+            }
+            indent2.outdent();
+        }
+        indent.outdent();
+    }
+
+    /**
+     * Updates the state for one instruction.
+     */
+    private int updateState(final int[] state, LIRInstruction op, int initValueNum) {
+
+        try (final Indent indent = Debug.logAndIndent("update state for op %s, initial value num = %d", op, initValueNum)) {
+            if (isEligibleMove(op)) {
+                /*
+                 * Handle the special case of a move instruction
+                 */
+                MoveOp moveOp = (MoveOp) op;
+                int sourceIdx = getStateIdx(moveOp.getInput());
+                int destIdx = getStateIdx(moveOp.getResult());
+                if (sourceIdx >= 0 && destIdx >= 0) {
+                    state[destIdx] = state[sourceIdx];
+                    indent.log("move value %d from %d to %d", state[sourceIdx], sourceIdx, destIdx);
+                    return initValueNum;
+                }
+            }
+
+            int valueNum = initValueNum;
+
+            if (op.destroysCallerSavedRegisters()) {
+                indent.log("kill all caller save regs");
+
+                for (Register reg : callerSaveRegs) {
+                    if (reg.number < numRegs) {
+                        // Kind.Object is the save default
+                        state[reg.number] = encodeValueNum(valueNum++, true);
+                    }
+                }
+            }
+
+            /*
+             * Value procedure for the instruction's output and temp values
+             */
+            class OutputValueProc extends ValueProcedure {
+
+                int opValueNum;
+
+                OutputValueProc(int opValueNum) {
+                    this.opValueNum = opValueNum;
+                }
+
+                @Override
+                public Value doValue(Value operand, OperandMode mode, EnumSet<OperandFlag> flags) {
+                    int stateIdx = getStateIdx(operand);
+                    if (stateIdx >= 0) {
+                        /*
+                         * Assign a unique number to the output or temp location.
+                         */
+                        state[stateIdx] = encodeValueNum(opValueNum++, operand.getKind() == Kind.Object);
+                        indent.log("set def %d for register %s(%d): %d", opValueNum, operand, stateIdx, state[stateIdx]);
+                    }
+                    return operand;
+                }
+            }
+
+            OutputValueProc outputValueProc = new OutputValueProc(valueNum);
+            op.forEachOutput(outputValueProc);
+            op.forEachTemp(outputValueProc);
+            valueNum = outputValueProc.opValueNum;
+
+            if (op.hasState()) {
+                /*
+                 * All instructions with framestates (mostly method calls), may do garbage
+                 * collection. GC will rewrite all object references which are live at this point.
+                 * So we can't rely on their values.
+                 */
+                indent.log("kill all object values");
+                clearValuesOfKindObject(state, valueNum);
+                valueNum += state.length;
+            }
+
+            return valueNum;
+        }
+    }
+
+    /**
+     * The state merge function for dataflow joins.
+     */
+    private static boolean mergeState(int[] dest, int[] source, int defNum) {
+        assert dest.length == source.length;
+        boolean changed = false;
+        for (int idx = 0; idx < source.length; idx++) {
+            int phiNum = defNum + idx;
+            if (dest[idx] != source[idx] && source[idx] != INIT_VALUE && dest[idx] != encodeValueNum(phiNum, isObjectValue(dest[idx]))) {
+                if (dest[idx] != INIT_VALUE) {
+                    dest[idx] = encodeValueNum(phiNum, isObjectValue(dest[idx]) || isObjectValue(source[idx]));
+                } else {
+                    dest[idx] = source[idx];
+                }
+                changed = true;
+            }
+        }
+        return changed;
+    }
+
+    private static void copyState(int[] dest, int[] source) {
+        assert dest.length == source.length;
+        for (int idx = 0; idx < source.length; idx++) {
+            dest[idx] = source[idx];
+        }
+    }
+
+    private static void clearValues(int[] state, int defNum) {
+        for (int idx = 0; idx < state.length; idx++) {
+            int phiNum = defNum + idx;
+            // Let the killed values assume to be object references: it's the save default.
+            state[idx] = encodeValueNum(phiNum, true);
+        }
+    }
+
+    private static void clearValuesOfKindObject(int[] state, int defNum) {
+        for (int idx = 0; idx < state.length; idx++) {
+            int phiNum = defNum + idx;
+            if (isObjectValue(state[idx])) {
+                state[idx] = encodeValueNum(phiNum, true);
+            }
+        }
+    }
+
+    /**
+     * Returns the index to the state arrays in BlockData for a specific location.
+     */
+    private int getStateIdx(Value location) {
+        if (isRegister(location)) {
+            int regNum = ((RegisterValue) location).getRegister().number;
+            if (regNum < numRegs) {
+                return regNum;
+            }
+        }
+        if (isStackSlot(location)) {
+            StackSlot slot = (StackSlot) location;
+            Integer index = stackIndices.get(slot);
+            if (index != null) {
+                return index.intValue() + numRegs;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Encodes a value number + the is-object information to a number to be stored in a state.
+     */
+    private static int encodeValueNum(int valueNum, boolean isObjectKind) {
+        assert valueNum > 0;
+        if (isObjectKind) {
+            return -valueNum;
+        }
+        return valueNum;
+    }
+
+    /**
+     * Returns true if an encoded value number (which is stored in a state) refers to an object
+     * reference.
+     */
+    private static boolean isObjectValue(int encodedValueNum) {
+        return encodedValueNum < 0;
+    }
+
+    /**
+     * Returns true for a move instruction which is a candidate for elimination.
+     */
+    private static boolean isEligibleMove(LIRInstruction op) {
+        if (op instanceof MoveOp) {
+            MoveOp moveOp = (MoveOp) op;
+            Value source = moveOp.getInput();
+            Value dest = moveOp.getResult();
+            /*
+             * Moves with mismatching kinds are not moves, but memory loads/stores!
+             */
+            return source.getKind() == dest.getKind() && source.getPlatformKind() == dest.getPlatformKind() && source.getKind() != Kind.Illegal;
+        }
+        return false;
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Tue Dec 17 21:26:42 2013 -0800
@@ -113,7 +113,7 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb) {
-            if (!destination.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            if (!crb.isSuccessorEdge(destination)) {
                 crb.asm.jmp(destination.label());
             }
         }
@@ -174,9 +174,10 @@
     }
 
     /**
-     * Placeholder for a LIR instruction that will be subsequently replaced.
+     * A LIR operation that does nothing. If the operation records its position, it can be
+     * subsequently {@linkplain #replace(LIR, LIRInstruction) replaced}.
      */
-    public static class PlaceholderOp extends LIRInstruction {
+    public static class NoOp extends LIRInstruction {
 
         /**
          * The block in which this instruction is located.
@@ -188,7 +189,7 @@
          */
         final int index;
 
-        public PlaceholderOp(Block block, int index) {
+        public NoOp(Block block, int index) {
             this.block = block;
             this.index = index;
         }
@@ -199,7 +200,9 @@
 
         @Override
         public void emitCode(CompilationResultBuilder crb) {
-            throw new GraalInternalError(this + " should have been replaced");
+            if (block != null) {
+                throw new GraalInternalError(this + " should have been replaced");
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/SwitchStrategy.java	Tue Dec 17 21:26:42 2013 -0800
@@ -116,9 +116,9 @@
         }
 
         public void conditionalJumpOrDefault(int index, Condition condition, boolean canFallThrough) {
-            if (canFallThrough && defaultTarget.isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            if (canFallThrough && crb.isSuccessorEdge(defaultTarget)) {
                 conditionalJump(index, condition, keyTargets[index].label());
-            } else if (canFallThrough && keyTargets[index].isCodeEmittingOrderSuccessorEdge(crb.getCurrentBlockIndex())) {
+            } else if (canFallThrough && crb.isSuccessorEdge(keyTargets[index])) {
                 conditionalJump(index, condition.negate(), defaultTarget.label());
             } else {
                 conditionalJump(index, condition, keyTargets[index].label());
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/asm/CompilationResultBuilder.java	Tue Dec 17 21:26:42 2013 -0800
@@ -32,6 +32,7 @@
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
+import com.oracle.graal.nodes.cfg.*;
 
 /**
  * Fills in a {@link CompilationResult} as its code is being assembled.
@@ -59,7 +60,12 @@
     public final FrameMap frameMap;
 
     /**
-     * The index of the block currently being processed in the code emitting block order.
+     * The LIR for which code is being generated.
+     */
+    private LIR lir;
+
+    /**
+     * The index of the block currently being emitted.
      */
     private int currentBlockIndex;
 
@@ -285,16 +291,58 @@
     }
 
     /**
-     * Gets the index of the block currently being processed in the code emitting block order.
+     * Determines if a given edge from the block currently being emitted goes to its lexical
+     * successor.
      */
-    public int getCurrentBlockIndex() {
-        return currentBlockIndex;
+    public boolean isSuccessorEdge(LabelRef edge) {
+        assert lir != null;
+        List<Block> order = lir.codeEmittingOrder();
+        assert order.get(currentBlockIndex) == edge.getSourceBlock();
+        return currentBlockIndex < order.size() - 1 && order.get(currentBlockIndex + 1) == edge.getTargetBlock();
     }
 
     /**
-     * Sets the index of the block currently being processed in the code emitting block order.
+     * Emits code for {@code lir} in its {@linkplain LIR#codeEmittingOrder() code emitting order}.
      */
-    public void setCurrentBlockIndex(int index) {
-        this.currentBlockIndex = index;
+    public void emit(@SuppressWarnings("hiding") LIR lir) {
+        assert this.lir == null;
+        assert currentBlockIndex == 0;
+        this.lir = lir;
+        this.currentBlockIndex = 0;
+        frameContext.enter(this);
+        for (Block b : lir.codeEmittingOrder()) {
+            emitBlock(b);
+            currentBlockIndex++;
+        }
+        this.lir = null;
+        this.currentBlockIndex = 0;
+    }
+
+    private void emitBlock(Block block) {
+        if (Debug.isDumpEnabled()) {
+            blockComment(String.format("block B%d %s", block.getId(), block.getLoop()));
+        }
+
+        for (LIRInstruction op : lir.lir(block)) {
+            if (Debug.isDumpEnabled()) {
+                blockComment(String.format("%d %s", op.id(), op));
+            }
+
+            try {
+                emitOp(this, op);
+            } catch (GraalInternalError e) {
+                throw e.addContext("lir instruction", block + "@" + op.id() + " " + op + "\n" + lir.codeEmittingOrder());
+            }
+        }
+    }
+
+    private static void emitOp(CompilationResultBuilder crb, LIRInstruction op) {
+        try {
+            op.emitCode(crb);
+        } catch (AssertionError t) {
+            throw new GraalInternalError(t);
+        } catch (RuntimeException t) {
+            throw new GraalInternalError(t);
+        }
     }
 }
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue Dec 17 21:26:42 2013 -0800
@@ -84,7 +84,7 @@
     }
 
     public boolean isOutsideLoop(Node n) {
-        return n.isExternal() || !whole().contains(n);
+        return !whole().contains(n);
     }
 
     public LoopBeginNode loopBegin() {
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopFragment.java	Tue Dec 17 21:26:42 2013 -0800
@@ -71,10 +71,7 @@
     @SuppressWarnings("unchecked")
     public <New extends Node, Old extends New> New getDuplicatedNode(Old n) {
         assert isDuplicate();
-        if (!n.isExternal()) {
-            return (New) duplicationMap.get(n);
-        }
-        return n;
+        return (New) duplicationMap.get(n);
     }
 
     protected <New extends Node, Old extends New> void putDuplicatedNode(Old oldNode, New newNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/ConstantNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,8 +22,6 @@
  */
 package com.oracle.graal.nodes;
 
-import static com.oracle.graal.graph.Graph.*;
-
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
@@ -68,40 +66,17 @@
         return value;
     }
 
-    private static boolean ConstantNodesAreExternal = Boolean.parseBoolean(System.getProperty("graal.constantNodesAreExternal", "true"));
-
     /**
      * Used to measure the impact of ConstantNodes not recording their usages. This and all code
      * predicated on this value being true will be removed at some point.
      */
-    public static final boolean ConstantNodeRecordsUsages = !ConstantNodesAreExternal && Boolean.getBoolean("graal.constantNodeRecordsUsages");
+    public static final boolean ConstantNodeRecordsUsages = Boolean.getBoolean("graal.constantNodeRecordsUsages");
 
     @Override
     public boolean recordsUsages() {
         return ConstantNodeRecordsUsages;
     }
 
-    @Override
-    public boolean isDeleted() {
-        if (!ConstantNodesAreExternal) {
-            return super.isDeleted();
-        }
-        return false;
-    }
-
-    @Override
-    public boolean isAlive() {
-        if (!ConstantNodesAreExternal) {
-            return super.isAlive();
-        }
-        return true;
-    }
-
-    @Override
-    public boolean isExternal() {
-        return ConstantNodesAreExternal;
-    }
-
     /**
      * Computes the usages of this node by iterating over all the nodes in the graph, searching for
      * those that have this node as an input.
@@ -121,23 +96,10 @@
 
     /**
      * Gathers all the {@link ConstantNode}s that are inputs to the
-     * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph. This is an expensive
-     * operation that should only be used in test/verification/AOT code.
+     * {@linkplain StructuredGraph#getNodes() live nodes} in a given graph.
      */
     public static NodeIterable<ConstantNode> getConstantNodes(StructuredGraph graph) {
-        if (!ConstantNodesAreExternal) {
-            return graph.getNodes().filter(ConstantNode.class);
-        }
-
-        Map<ConstantNode, ConstantNode> result = new HashMap<>();
-        for (Node node : graph.getNodes()) {
-            for (Node input : node.inputs()) {
-                if (input instanceof ConstantNode) {
-                    result.put((ConstantNode) input, (ConstantNode) input);
-                }
-            }
-        }
-        return new ConstantNodeList(result.keySet());
+        return graph.getNodes().filter(ConstantNode.class);
     }
 
     /**
@@ -150,9 +112,7 @@
             for (Node usage : usages) {
                 usage.replaceFirstInput(this, replacement);
             }
-            if (!isExternal()) {
-                graph.removeFloating(this);
-            }
+            graph.removeFloating(this);
         } else {
             assert graph == graph();
             graph().replaceFloating(this, replacement);
@@ -184,10 +144,6 @@
         if (constant.getKind().getStackKind() == Kind.Int && constant.getKind() != Kind.Int) {
             return forInt(constant.asInt(), graph);
         }
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            Stamp stamp = constant.getKind() == Kind.Object ? StampFactory.forConstant(constant, metaAccess) : StampFactory.forConstant(constant);
-            return graph.asConstantNode(constant, stamp);
-        }
         if (constant.getKind() == Kind.Object) {
             return unique(graph, new ConstantNode(constant, StampFactory.forConstant(constant, metaAccess)));
         } else {
@@ -210,9 +166,6 @@
      * @return a node for a double constant
      */
     public static ConstantNode forDouble(double d, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forDouble(d), null);
-        }
         return unique(graph, createPrimitive(Constant.forDouble(d)));
     }
 
@@ -223,9 +176,6 @@
      * @return a node for a float constant
      */
     public static ConstantNode forFloat(float f, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forFloat(f), null);
-        }
         return unique(graph, createPrimitive(Constant.forFloat(f)));
     }
 
@@ -236,9 +186,6 @@
      * @return a node for an long constant
      */
     public static ConstantNode forLong(long i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forLong(i), null);
-        }
         return unique(graph, createPrimitive(Constant.forLong(i)));
     }
 
@@ -249,9 +196,6 @@
      * @return a node for an integer constant
      */
     public static ConstantNode forInt(int i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forInt(i), null);
-        }
         return unique(graph, createPrimitive(Constant.forInt(i)));
     }
 
@@ -262,9 +206,6 @@
      * @return a node representing the boolean
      */
     public static ConstantNode forBoolean(boolean i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(i ? Constant.INT_1 : Constant.INT_0, null);
-        }
         return unique(graph, createPrimitive(Constant.forInt(i ? 1 : 0)));
     }
 
@@ -275,9 +216,6 @@
      * @return a node representing the byte
      */
     public static ConstantNode forByte(byte i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forInt(i), null);
-        }
         return unique(graph, createPrimitive(Constant.forInt(i)));
     }
 
@@ -288,9 +226,6 @@
      * @return a node representing the char
      */
     public static ConstantNode forChar(char i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forInt(i), null);
-        }
         return unique(graph, createPrimitive(Constant.forInt(i)));
     }
 
@@ -301,9 +236,6 @@
      * @return a node representing the short
      */
     public static ConstantNode forShort(short i, StructuredGraph graph) {
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(Constant.forInt(i), null);
-        }
         return unique(graph, createPrimitive(Constant.forInt(i)));
     }
 
@@ -316,18 +248,11 @@
     public static ConstantNode forObject(Object o, MetaAccessProvider metaAccess, StructuredGraph graph) {
         assert !(o instanceof Constant) : "wrapping a Constant into a Constant";
         Constant constant = Constant.forObject(o);
-        if (ConstantNodesAreExternal && !CacheExternalNodesInGraph) {
-            return graph.asConstantNode(constant, StampFactory.forConstant(constant, metaAccess));
-        }
         return unique(graph, new ConstantNode(constant, StampFactory.forConstant(constant, metaAccess)));
     }
 
     private static ConstantNode unique(StructuredGraph graph, ConstantNode node) {
-        if (!ConstantNodesAreExternal) {
-            return graph.unique(node);
-        }
-        assert CacheExternalNodesInGraph;
-        return graph.uniqueExternal(node);
+        return graph.unique(node);
     }
 
     public static ConstantNode forIntegerKind(Kind kind, long value, StructuredGraph graph) {
@@ -390,15 +315,4 @@
             return super.toString(verbosity);
         }
     }
-
-    static class ConstantNodeList extends NodeList<ConstantNode> {
-
-        public ConstantNodeList(Collection<ConstantNode> nodes) {
-            super(nodes.toArray(new ConstantNode[nodes.size()]));
-        }
-
-        @Override
-        protected void update(ConstantNode oldNode, ConstantNode newNode) {
-        }
-    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StateSplit.java	Tue Dec 17 21:26:42 2013 -0800
@@ -42,9 +42,9 @@
     void setStateAfter(FrameState x);
 
     /**
-     * Determines if this node has a side-effect. Such nodes can not be safely re-executed because
-     * they modified state which is visible to other thread or modified state beyond what is
-     * captured in {@link FrameState} nodes.
+     * Determines if this node has a side-effect. Such nodes cannot be safely re-executed because
+     * they modify state which is visible to other threads or modify state beyond what is captured
+     * in {@link FrameState} nodes.
      */
     boolean hasSideEffect();
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Tue Dec 17 21:26:42 2013 -0800
@@ -29,7 +29,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.java.*;
-import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
 
 /**
@@ -82,38 +81,6 @@
     private boolean isAfterFloatingReadPhase = false;
 
     /**
-     * Used to create canonical {@link ConstantNode}s for {@link Constant}s in this graph.
-     */
-    private Map<Constant, ConstantNode> constants;
-
-    /**
-     * Gets a node for a given constant that is unique/canonical within this graph.
-     * 
-     * @param stamp the stamp for an {@link Kind#Object} constant (ignored otherwise)
-     */
-    public ConstantNode asConstantNode(Constant constant, Stamp stamp) {
-        ConstantNode node;
-        if (constants == null) {
-            constants = new HashMap<>();
-            node = null;
-        } else {
-            node = constants.get(constant);
-        }
-        if (node == null) {
-            node = new ConstantNode(constant, stamp == null ? StampFactory.forConstant(constant) : stamp);
-            constants.put(constant, node);
-        }
-        return node;
-    }
-
-    @SuppressWarnings("unchecked")
-    @Override
-    public <T extends Node> T uniqueExternal(T node) {
-        ConstantNode cn = (ConstantNode) node;
-        return (T) asConstantNode(cn.asConstant(), cn.stamp());
-    }
-
-    /**
      * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start()
      * start} node.
      */
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewArrayNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewArrayNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,7 +22,6 @@
  */
 package com.oracle.graal.nodes.java;
 
-import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
@@ -31,10 +30,9 @@
 /**
  * The {@code AbstractNewArrayNode} is used for all 1-dimensional array allocations.
  */
-public class AbstractNewArrayNode extends DeoptimizingFixedWithNextNode implements Canonicalizable, Lowerable, ArrayLengthProvider {
+public class AbstractNewArrayNode extends AbstractNewObjectNode implements ArrayLengthProvider {
 
     @Input private ValueNode length;
-    private final boolean fillContents;
 
     @Override
     public ValueNode length() {
@@ -49,16 +47,8 @@
      * @param fillContents determines whether the array elements should be initialized to zero/null.
      */
     protected AbstractNewArrayNode(Stamp stamp, ValueNode length, boolean fillContents) {
-        super(stamp);
+        super(stamp, fillContents);
         this.length = length;
-        this.fillContents = fillContents;
-    }
-
-    /**
-     * @return <code>true</code> if the elements of the array will be initialized.
-     */
-    public boolean fillContents() {
-        return fillContents;
     }
 
     /**
@@ -77,23 +67,11 @@
     }
 
     @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (usages().isEmpty()) {
-            Stamp stamp = length.stamp();
-            if (stamp instanceof IntegerStamp && ((IntegerStamp) stamp).isPositive()) {
-                return null;
-            }
+    public void simplify(SimplifierTool tool) {
+        Stamp stamp = length.stamp();
+        if (stamp instanceof IntegerStamp && ((IntegerStamp) stamp).isPositive()) {
+            // otherwise, removing the allocation might swallow a NegativeArraySizeException
+            super.simplify(tool);
         }
-        return this;
-    }
-
-    @Override
-    public void lower(LoweringTool tool) {
-        tool.getLowerer().lower(this, tool);
-    }
-
-    @Override
-    public boolean canDeoptimize() {
-        return true;
     }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/AbstractNewObjectNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,99 @@
+/*
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.nodes.java;
+
+import java.util.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * The {@code AbstractNewObjectNode} is the base class for the new instance and new array nodes.
+ */
+public class AbstractNewObjectNode extends DeoptimizingFixedWithNextNode implements Simplifiable, Lowerable {
+
+    private final boolean fillContents;
+
+    /**
+     * Constructs a new AbstractNewObjectNode.
+     * 
+     * @param stamp the stamp of the newly created object
+     * @param fillContents determines if the object's contents should be initialized to zero/null.
+     */
+    protected AbstractNewObjectNode(Stamp stamp, boolean fillContents) {
+        super(stamp);
+        this.fillContents = fillContents;
+    }
+
+    /**
+     * @return <code>true</code> if the object's contents should be initialized to zero/null.
+     */
+    public boolean fillContents() {
+        return fillContents;
+    }
+
+    @Override
+    public void simplify(SimplifierTool tool) {
+        // poor man's escape analysis: check if the object can be trivially removed
+        for (Node usage : usages()) {
+            if (usage instanceof FixedValueAnchorNode) {
+                if (((FixedValueAnchorNode) usage).usages().isNotEmpty()) {
+                    return;
+                }
+            } else if (usage instanceof WriteNode) {
+                if (((WriteNode) usage).object() != this || usage.usages().isNotEmpty()) {
+                    // we would need to fix up the memory graph if the write has usages
+                    return;
+                }
+            } else {
+                return;
+            }
+        }
+        for (Node usage : usages().snapshot()) {
+            List<Node> snapshot = usage.inputs().snapshot();
+            graph().removeFixed((FixedWithNextNode) usage);
+            for (Node input : snapshot) {
+                tool.removeIfUnused(input);
+            }
+        }
+        List<Node> snapshot = inputs().snapshot();
+        graph().removeFixed(this);
+        for (Node input : snapshot) {
+            tool.removeIfUnused(input);
+        }
+    }
+
+    @Override
+    public void lower(LoweringTool tool) {
+        tool.getLowerer().lower(this, tool);
+    }
+
+    @Override
+    public boolean canDeoptimize() {
+        return true;
+    }
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/CompareAndSwapNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -25,6 +25,8 @@
 import static com.oracle.graal.graph.UnsafeAccess.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
@@ -34,13 +36,14 @@
  * Represents an atomic compare-and-swap operation The result is a boolean that contains whether the
  * value matched the expected value.
  */
-public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single {
+public class CompareAndSwapNode extends AbstractMemoryCheckpoint implements Lowerable, MemoryCheckpoint.Single, Canonicalizable {
 
     @Input private ValueNode object;
     @Input private ValueNode offset;
     @Input private ValueNode expected;
     @Input private ValueNode newValue;
     private final int displacement;
+    private final LocationIdentity locationIdentity;
 
     public ValueNode object() {
         return object;
@@ -63,6 +66,10 @@
     }
 
     public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue) {
+        this(object, displacement, offset, expected, newValue, LocationIdentity.ANY_LOCATION);
+    }
+
+    public CompareAndSwapNode(ValueNode object, int displacement, ValueNode offset, ValueNode expected, ValueNode newValue, LocationIdentity locationIdentity) {
         super(StampFactory.forKind(Kind.Boolean.getStackKind()));
         assert expected.kind() == newValue.kind();
         this.object = object;
@@ -70,11 +77,12 @@
         this.expected = expected;
         this.newValue = newValue;
         this.displacement = displacement;
+        this.locationIdentity = locationIdentity;
     }
 
     @Override
     public LocationIdentity getLocationIdentity() {
-        return LocationIdentity.ANY_LOCATION;
+        return locationIdentity;
     }
 
     @Override
@@ -82,6 +90,24 @@
         tool.getLowerer().lower(this, tool);
     }
 
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getLocationIdentity() == LocationIdentity.ANY_LOCATION) {
+            Constant offsetConstant = offset().asConstant();
+            if (offsetConstant != null) {
+                ResolvedJavaType receiverType = ObjectStamp.typeOrNull(object());
+                if (receiverType != null) {
+                    long constantOffset = offsetConstant.asLong();
+                    ResolvedJavaField field = receiverType.findInstanceFieldWithOffset(constantOffset);
+                    if (field != null && expected().kind() == field.getKind() && newValue().kind() == field.getKind()) {
+                        return graph().add(new CompareAndSwapNode(object, displacement, offset, expected, newValue, field));
+                    }
+                }
+            }
+        }
+        return this;
+    }
+
     // specialized on value type until boxing/unboxing is sorted out in intrinsification
     @NodeIntrinsic
     public static boolean compareAndSwap(Object object, @ConstantNodeParameter int displacement, long offset, Object expected, Object newValue) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewArrayNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/DynamicNewArrayNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -23,6 +23,7 @@
 package com.oracle.graal.nodes.java;
 
 import java.lang.reflect.*;
+import java.util.*;
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
@@ -34,7 +35,7 @@
  * The {@code DynamicNewArrayNode} is used for allocation of arrays when the type is not a
  * compile-time constant.
  */
-public class DynamicNewArrayNode extends AbstractNewArrayNode implements Canonicalizable {
+public class DynamicNewArrayNode extends AbstractNewArrayNode {
 
     @Input private ValueNode elementType;
 
@@ -52,15 +53,20 @@
     }
 
     @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (elementType.isConstant()) {
+    public void simplify(SimplifierTool tool) {
+        if (isAlive() && elementType.isConstant()) {
             Class<?> elementClass = (Class<?>) elementType.asConstant().asObject();
             if (elementClass != null && !(elementClass.equals(void.class))) {
                 ResolvedJavaType javaType = tool.getMetaAccess().lookupJavaType(elementClass);
-                return graph().add(new NewArrayNode(javaType, length(), fillContents()));
+                NewArrayNode newArray = graph().add(new NewArrayNode(javaType, length(), fillContents()));
+                List<Node> snapshot = inputs().snapshot();
+                graph().replaceFixedWithFixed(this, newArray);
+                for (Node input : snapshot) {
+                    tool.removeIfUnused(input);
+                }
+                tool.addToWorkList(newArray);
             }
         }
-        return this;
     }
 
     @NodeIntrinsic
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/NewInstanceNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -26,7 +26,6 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.spi.*;
 import com.oracle.graal.nodes.type.*;
@@ -36,10 +35,9 @@
  * The {@code NewInstanceNode} represents the allocation of an instance class object.
  */
 @NodeInfo(nameTemplate = "New {p#instanceClass/s}")
-public class NewInstanceNode extends DeoptimizingFixedWithNextNode implements Canonicalizable, Lowerable, VirtualizableAllocation {
+public class NewInstanceNode extends AbstractNewObjectNode implements VirtualizableAllocation {
 
     private final ResolvedJavaType instanceClass;
-    private final boolean fillContents;
 
     /**
      * Constructs a NewInstanceNode.
@@ -49,10 +47,9 @@
      *            zero/null.
      */
     public NewInstanceNode(ResolvedJavaType type, boolean fillContents) {
-        super(StampFactory.exactNonNull(type));
+        super(StampFactory.exactNonNull(type), fillContents);
         assert !type.isArray();
         this.instanceClass = type;
-        this.fillContents = fillContents;
     }
 
     /**
@@ -64,27 +61,6 @@
         return instanceClass;
     }
 
-    /**
-     * @return <code>true</code> if the fields of the new object will be initialized.
-     */
-    public boolean fillContents() {
-        return fillContents;
-    }
-
-    @Override
-    public Node canonical(CanonicalizerTool tool) {
-        if (usages().isEmpty()) {
-            return null;
-        } else {
-            return this;
-        }
-    }
-
-    @Override
-    public void lower(LoweringTool tool) {
-        tool.getLowerer().lower(this, tool);
-    }
-
     @Override
     public void virtualize(VirtualizerTool tool) {
         /*
@@ -107,9 +83,4 @@
     protected ConstantNode defaultFieldValue(ResolvedJavaField field) {
         return ConstantNode.defaultForKind(field.getType().getKind(), graph());
     }
-
-    @Override
-    public boolean canDeoptimize() {
-        return true;
-    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LIRGeneratorTool.java	Tue Dec 17 21:26:42 2013 -0800
@@ -102,6 +102,7 @@
 
     /**
      * Called just before register allocation is performed on the LIR owned by this generator.
+     * Overriding implementations of this method must call the overridden method.
      */
     void beforeRegisterAllocation();
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/NodeWithState.java	Tue Dec 17 21:26:42 2013 -0800
@@ -29,13 +29,11 @@
 /**
  * Interface for nodes which have {@link FrameState} nodes as input.
  * <p>
- * Some node can declare more than one interface which requires a {@link FrameState} input (e.g.
+ * Some node can implement more than one interface which requires a {@link FrameState} input (e.g.
  * {@link DeoptimizingNode} and {@link StateSplit}). Since this interface can only report one
- * {@link FrameState}, such nodes must ensure they only maintain a link to at most one
- * {@link FrameState} at all times. Usually this is not a problem because {@link FrameState} are
- * associated only with {@link StateSplit} nodes before the {@link #AFTER_FSA} stage and only with
- * {@link DeoptimizingNode} after.
- * 
+ * FrameState, such nodes must ensure they only maintain a link to at most one FrameState at all
+ * times. Usually this is not a problem because FrameStates are associated only with StateSplit
+ * nodes before the {@link #AFTER_FSA} stage and only with DeoptimizingNodes after.
  * 
  */
 public interface NodeWithState {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/util/GraphUtil.java	Tue Dec 17 21:26:42 2013 -0800
@@ -123,7 +123,7 @@
     }
 
     public static void killWithUnusedFloatingInputs(Node node) {
-        if (node.recordsUsages() && !node.isExternal()) {
+        if (node.recordsUsages()) {
             List<Node> floatingInputs = node.inputs().filter(isFloatingNode()).snapshot();
             node.safeDelete();
 
@@ -354,14 +354,14 @@
          * Process a node as part of this search.
          * 
          * @param node the next node encountered in the search
-         * @param worklist if non-null and {@code node} is not external, {@code node} will be added
-         *            to this list. Otherwise, {@code node} is treated as a candidate result.
+         * @param worklist if non-null, {@code node} will be added to this list. Otherwise,
+         *            {@code node} is treated as a candidate result.
          * @return true if the search should continue, false if a definitive {@link #result} has
          *         been found
          */
         private boolean process(ValueNode node, NodeWorkList worklist) {
             if (node.isAlive()) {
-                if (node.isExternal() || worklist == null) {
+                if (worklist == null) {
                     if (result == null) {
                         // Initial candidate result: continue search
                         result = node;
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/DeadCodeEliminationPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -112,17 +112,13 @@
         for (Node node : graph.getNodes()) {
             if (flood.isMarked(node)) {
                 for (Node input : node.inputs()) {
-                    if (!input.isExternal()) {
-                        flood.add(input);
-                    }
+                    flood.add(input);
                 }
             }
         }
         for (Node current : flood) {
             for (Node input : current.inputs()) {
-                if (!input.isExternal()) {
-                    flood.add(input);
-                }
+                flood.add(input);
             }
         }
     }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/GuardLoweringPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -27,6 +27,7 @@
 import java.util.*;
 import java.util.Map.Entry;
 
+import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.GuardsStage;
@@ -34,7 +35,6 @@
 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.*;
@@ -54,13 +54,6 @@
  * 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 {
 
         private final IdentityHashMap<ValueNode, GuardNode> nullGuarded = new IdentityHashMap<>();
@@ -134,9 +127,9 @@
         private final Block block;
         private boolean useGuardIdAsSpeculationId;
 
-        public LowerGuards(Block block) {
+        public LowerGuards(Block block, boolean useGuardIdAsSpeculationId) {
             this.block = block;
-            this.useGuardIdAsSpeculationId = Options.UseGuardIdAsSpeculationId.getValue();
+            this.useGuardIdAsSpeculationId = useGuardIdAsSpeculationId;
         }
 
         @Override
@@ -203,6 +196,6 @@
         if (OptImplicitNullChecks.getValue() && implicitNullCheckLimit > 0) {
             new UseImplicitNullChecks(implicitNullCheckLimit).processNodes(block, schedule);
         }
-        new LowerGuards(block).processNodes(block, schedule);
+        new LowerGuards(block, Debug.isDumpEnabledForMethod() || Debug.isLogEnabledForMethod()).processNodes(block, schedule);
     }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/IncrementalCanonicalizerPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,13 +22,10 @@
  */
 package com.oracle.graal.phases.common;
 
-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.*;
 import com.oracle.graal.phases.*;
+import com.oracle.graal.phases.common.util.*;
 import com.oracle.graal.phases.tiers.*;
 
 /**
@@ -46,15 +43,8 @@
     @Override
     protected void run(StructuredGraph graph, C context) {
         Mark newNodesMark = graph.getMark();
-        final Set<Node> changedNodes = new HashSet<>();
 
-        NodeChangedListener listener = new NodeChangedListener() {
-
-            @Override
-            public void nodeChanged(Node node) {
-                changedNodes.add(node);
-            }
-        };
+        HashSetNodeChangeListener listener = new HashSetNodeChangeListener();
         graph.trackInputChange(listener);
         graph.trackUsagesDroppedZero(listener);
 
@@ -63,8 +53,8 @@
         graph.stopTrackingInputChange();
         graph.stopTrackingUsagesDroppedZero();
 
-        if (graph.getMark() != newNodesMark || !changedNodes.isEmpty()) {
-            canonicalizer.applyIncremental(graph, context, changedNodes, newNodesMark, false);
+        if (graph.getMark() != newNodesMark || !listener.getChangedNodes().isEmpty()) {
+            canonicalizer.applyIncremental(graph, context, listener.getChangedNodes(), newNodesMark, false);
         }
     }
 }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/InliningUtil.java	Tue Dec 17 21:26:42 2013 -0800
@@ -40,7 +40,6 @@
 import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Graph.DuplicationReplacement;
-import com.oracle.graal.graph.Node.ValueNumberable;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.calc.*;
@@ -1433,12 +1432,7 @@
             if (returnNode.result() instanceof LocalNode) {
                 returnValue = localReplacement.replacement(returnNode.result());
             } else if (returnNode.result() != null) {
-                returnValue = returnNode.result();
-                if (!returnValue.isExternal()) {
-                    returnValue = duplicates.get(returnValue);
-                } else if (returnValue instanceof ValueNumberable) {
-                    returnValue = graph.uniqueExternal(returnValue);
-                }
+                returnValue = duplicates.get(returnNode.result());
             }
             invoke.asNode().replaceAtUsages(returnValue);
             Node returnDuplicate = duplicates.get(returnNode);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/LoweringPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -324,7 +324,7 @@
                     assert (unscheduledUsages = getUnscheduledUsages(node)) != null;
                     Mark preLoweringMark = node.graph().getMark();
                     ((Lowerable) node).lower(loweringTool);
-                    if (node == startAnchor && node.isDeleted()) {
+                    if (loweringTool.guardAnchor.asNode().isDeleted()) {
                         loweringTool.guardAnchor = BeginNode.prevBegin(nextNode);
                     }
                     assert checkPostNodeLowering(node, loweringTool, preLoweringMark, unscheduledUsages);
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/TailDuplicationPhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -87,17 +87,22 @@
             if (fixedNodeCount < TailDuplicationTrivialSize.getValue()) {
                 return true;
             }
-            HashSet<PhiNode> improvements = new HashSet<>();
+            ArrayList<PhiNode> improvements = null;
             for (PhiNode phi : merge.phis()) {
                 Stamp phiStamp = phi.stamp();
                 for (ValueNode input : phi.values()) {
                     if (!input.stamp().equals(phiStamp)) {
-                        improvements.add(phi);
+                        if (improvements == null) {
+                            improvements = new ArrayList<>();
+                        }
+                        if (!improvements.contains(phi)) {
+                            improvements.add(phi);
+                        }
                         break;
                     }
                 }
             }
-            if (improvements.isEmpty()) {
+            if (improvements == null) {
                 return false;
             }
             FixedNode current = merge;
@@ -365,7 +370,7 @@
                         // stop iterating: fixed nodes within the given set are traversal roots
                         // anyway, and all other
                         // fixed nodes are known to be outside.
-                    } else if (!node.isExternal() && !aboveBound.isMarked(node)) {
+                    } else if (!aboveBound.isMarked(node)) {
                         worklist.add(node);
                         aboveBound.mark(node);
                     }
@@ -378,9 +383,7 @@
             while (!worklist.isEmpty()) {
                 Node current = worklist.remove();
                 for (Node input : current.inputs()) {
-                    if (!input.isExternal()) {
-                        aboveClosure.apply(current, input);
-                    }
+                    aboveClosure.apply(current, input);
                 }
             }
 
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/GraalOptions.java	Tue Dec 17 21:26:42 2013 -0800
@@ -87,14 +87,6 @@
     @Option(help = "")
     public static final OptionValue<Integer> DeoptsToDisableOptimisticOptimization = new OptionValue<>(40);
 
-    // compilation queue
-    @Option(help = "Compile all methods in all classes on given class path")
-    public static final OptionValue<String> CompileTheWorld = new OptionValue<>(null);
-    @Option(help = "First class to consider when using CompileTheWorld")
-    public static final OptionValue<Integer> CompileTheWorldStartAt = new OptionValue<>(1);
-    @Option(help = "Last class to consider when using CompileTheWorld")
-    public static final OptionValue<Integer> CompileTheWorldStopAt = new OptionValue<>(Integer.MAX_VALUE);
-
     // graph caching
     @Option(help = "")
     public static final OptionValue<Boolean> CacheGraphs = new OptionValue<>(true);
@@ -226,9 +218,7 @@
     public static final OptionValue<Boolean> CanOmitFrame = new OptionValue<>(true);
 
     @Option(help = "")
-    public static final OptionValue<Boolean> MemoryAwareScheduling = new OptionValue<>(false);
-    @Option(help = "")
-    public static final OptionValue<Boolean> NewMemoryAwareScheduling = new OptionValue<>(true);
+    public static final OptionValue<Boolean> MemoryAwareScheduling = new OptionValue<>(true);
 
     // Translating tableswitch instructions
     @Option(help = "")
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/graph/ComputeProbabilityClosure.java	Tue Dec 17 21:26:42 2013 -0800
@@ -28,6 +28,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.phases.util.*;
 
 /**
  * Computes probabilities for nodes in a graph.
@@ -58,7 +59,7 @@
     public ComputeProbabilityClosure(StructuredGraph graph) {
         this.graph = graph;
         this.nodeProbabilities = new NodesToDoubles(graph.getNodeCount());
-        this.loopInfos = new HashSet<>();
+        this.loopInfos = new ArraySet<>();
         this.mergeLoops = new IdentityHashMap<>();
     }
 
@@ -75,7 +76,7 @@
      * Assume that paths with a DeoptimizeNode at their end are taken infrequently.
      */
     private void adjustControlSplitProbabilities() {
-        HashSet<ControlSplitNode> result = new HashSet<>();
+        Set<ControlSplitNode> result = new ArraySet<>();
         NodeBitMap visitedNodes = new NodeBitMap(graph);
         for (AbstractDeoptimizeNode n : graph.getNodes(AbstractDeoptimizeNode.class)) {
             if (!(n instanceof DeoptimizeNode) || ((DeoptimizeNode) n).action().doesInvalidateCompilation()) {
@@ -90,7 +91,7 @@
         }
     }
 
-    private static void findParentControlSplitNodes(HashSet<ControlSplitNode> result, AbstractDeoptimizeNode n, NodeBitMap visitedNodes) {
+    private static void findParentControlSplitNodes(Set<ControlSplitNode> result, AbstractDeoptimizeNode n, NodeBitMap visitedNodes) {
         ArrayDeque<FixedNode> nodes = new ArrayDeque<>();
         nodes.push(n);
 
@@ -221,13 +222,13 @@
     private class Probability extends MergeableState<Probability> {
 
         public double probability;
-        public HashSet<LoopInfo> loops;
+        public Set<LoopInfo> loops;
         public LoopInfo loopInfo;
 
-        public Probability(double probability, HashSet<LoopInfo> loops) {
+        public Probability(double probability, Set<LoopInfo> loops) {
             assert probability >= 0.0;
             this.probability = probability;
-            this.loops = new HashSet<>(4);
+            this.loops = new ArraySet<>(4);
             if (loops != null) {
                 this.loops.addAll(loops);
             }
@@ -241,7 +242,7 @@
         @Override
         public boolean merge(MergeNode merge, List<Probability> withStates) {
             if (merge.forwardEndCount() > 1) {
-                HashSet<LoopInfo> intersection = new HashSet<>(loops);
+                Set<LoopInfo> intersection = new ArraySet<>(loops);
                 for (Probability other : withStates) {
                     intersection.retainAll(other.loops);
                 }
@@ -271,7 +272,7 @@
                     assert probability >= 0;
                 }
                 loops = intersection;
-                mergeLoops.put(merge, new HashSet<>(intersection));
+                mergeLoops.put(merge, new ArraySet<>(intersection));
                 probability = Math.max(0.0, probability);
             }
             return true;
--- a/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/schedule/SchedulePhase.java	Tue Dec 17 21:26:42 2013 -0800
@@ -33,7 +33,6 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.spi.*;
@@ -42,6 +41,7 @@
 import com.oracle.graal.phases.graph.*;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.BlockIteratorClosure;
 import com.oracle.graal.phases.graph.ReentrantBlockIterator.LoopInfo;
+import com.oracle.graal.phases.util.*;
 
 public final class SchedulePhase extends Phase {
 
@@ -74,111 +74,14 @@
     }
 
     public static enum MemoryScheduling {
-        NONE, CONSERVATIVE, OPTIMAL
-    }
-
-    /**
-     * This closure iterates over all nodes of a scheduled graph (it expects a
-     * {@link SchedulingStrategy#EARLIEST} schedule) and keeps a list of "active" reads. Whenever it
-     * encounters a read, it adds it to the active reads. Whenever it encounters a memory
-     * checkpoint, it adds all reads that need to be committed before this checkpoint to the
-     * "phantom" usages and inputs, so that the read is scheduled before the checkpoint afterwards.
-     * 
-     * At merges, the intersection of all sets of active reads is calculated. A read that was
-     * committed within one predecessor branch cannot be scheduled after the merge anyway.
-     * 
-     * Similarly for loops, all reads that are killed somewhere within the loop are removed from the
-     * exits' active reads, since they cannot be scheduled after the exit anyway.
-     */
-    private class MemoryScheduleClosure extends BlockIteratorClosure<HashSet<FloatingReadNode>> {
-
-        @Override
-        protected HashSet<FloatingReadNode> getInitialState() {
-            return new HashSet<>();
-        }
-
-        @Override
-        protected HashSet<FloatingReadNode> processBlock(Block block, HashSet<FloatingReadNode> currentState) {
-            for (Node node : blockToNodesMap.get(block)) {
-                if (node instanceof FloatingReadNode) {
-                    currentState.add((FloatingReadNode) node);
-                } else if (node instanceof MemoryCheckpoint.Single) {
-                    LocationIdentity identity = ((MemoryCheckpoint.Single) node).getLocationIdentity();
-                    processIdentity(currentState, (FixedNode) node, identity);
-                } else if (node instanceof MemoryCheckpoint.Multi) {
-                    for (LocationIdentity identity : ((MemoryCheckpoint.Multi) node).getLocationIdentities()) {
-                        processIdentity(currentState, (FixedNode) node, identity);
-                    }
-                }
-                assert MemoryCheckpoint.TypeAssertion.correctType(node);
-            }
-            return currentState;
-        }
-
-        private void processIdentity(HashSet<FloatingReadNode> currentState, FixedNode fixed, LocationIdentity identity) {
-            for (Iterator<FloatingReadNode> iter = currentState.iterator(); iter.hasNext();) {
-                FloatingReadNode read = iter.next();
-                if (identity == ANY_LOCATION || read.location().getLocationIdentity() == identity) {
-                    addPhantomReference(read, fixed);
-                    iter.remove();
-                }
-            }
-        }
-
-        public void addPhantomReference(FloatingReadNode read, FixedNode fixed) {
-            List<FixedNode> usageList = phantomUsages.get(read);
-            if (usageList == null) {
-                phantomUsages.put(read, usageList = new ArrayList<>());
-            }
-            usageList.add(fixed);
-            List<FloatingNode> inputList = phantomInputs.get(fixed);
-            if (inputList == null) {
-                phantomInputs.put(fixed, inputList = new ArrayList<>());
-            }
-            inputList.add(read);
-        }
-
-        @Override
-        protected HashSet<FloatingReadNode> merge(Block merge, List<HashSet<FloatingReadNode>> states) {
-            HashSet<FloatingReadNode> state = new HashSet<>(states.get(0));
-            for (int i = 1; i < states.size(); i++) {
-                state.retainAll(states.get(i));
-            }
-            return state;
-        }
-
-        @Override
-        protected HashSet<FloatingReadNode> cloneState(HashSet<FloatingReadNode> oldState) {
-            return new HashSet<>(oldState);
-        }
-
-        @Override
-        protected List<HashSet<FloatingReadNode>> processLoop(Loop loop, HashSet<FloatingReadNode> state) {
-            LoopInfo<HashSet<FloatingReadNode>> info = ReentrantBlockIterator.processLoop(this, loop, new HashSet<>(state));
-
-            List<HashSet<FloatingReadNode>> loopEndStates = info.endStates;
-
-            // collect all reads that were killed in some branch within the loop
-            Set<FloatingReadNode> killedReads = new HashSet<>(state);
-            Set<FloatingReadNode> survivingReads = new HashSet<>(loopEndStates.get(0));
-            for (int i = 1; i < loopEndStates.size(); i++) {
-                survivingReads.retainAll(loopEndStates.get(i));
-            }
-            killedReads.removeAll(survivingReads);
-
-            // reads that were killed within the loop cannot be scheduled after the loop anyway
-            for (HashSet<FloatingReadNode> exitState : info.exitStates) {
-                exitState.removeAll(killedReads);
-            }
-            return info.exitStates;
-        }
+        NONE, OPTIMAL
     }
 
     private class KillSet implements Iterable<LocationIdentity> {
         private final Set<LocationIdentity> set;
 
         public KillSet() {
-            this.set = new HashSet<>();
+            this.set = new ArraySet<>();
         }
 
         public KillSet(KillSet other) {
@@ -340,8 +243,6 @@
      */
     private BlockMap<List<ScheduledNode>> blockToNodesMap;
     private BlockMap<KillSet> blockToKillSet;
-    private final Map<FloatingNode, List<FixedNode>> phantomUsages = new IdentityHashMap<>();
-    private final Map<FixedNode, List<FloatingNode>> phantomInputs = new IdentityHashMap<>();
     private final SchedulingStrategy selectedStrategy;
     private final MemoryScheduling memsched;
 
@@ -350,16 +251,7 @@
     }
 
     public SchedulePhase(SchedulingStrategy strategy) {
-        if (MemoryAwareScheduling.getValue() && NewMemoryAwareScheduling.getValue()) {
-            throw new SchedulingError("cannot enable both: MemoryAware- and NewMemoryAwareScheduling");
-        }
-        if (MemoryAwareScheduling.getValue()) {
-            this.memsched = MemoryScheduling.CONSERVATIVE;
-        } else if (NewMemoryAwareScheduling.getValue()) {
-            this.memsched = MemoryScheduling.OPTIMAL;
-        } else {
-            this.memsched = MemoryScheduling.NONE;
-        }
+        this.memsched = MemoryAwareScheduling.getValue() ? MemoryScheduling.OPTIMAL : MemoryScheduling.NONE;
         this.selectedStrategy = strategy;
     }
 
@@ -374,19 +266,7 @@
         earliestCache = graph.createNodeMap();
         blockToNodesMap = new BlockMap<>(cfg);
 
-        if (memsched == MemoryScheduling.CONSERVATIVE && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) {
-            assignBlockToNodes(graph, SchedulingStrategy.EARLIEST);
-            sortNodesWithinBlocks(graph, SchedulingStrategy.EARLIEST);
-
-            ReentrantBlockIterator.apply(new MemoryScheduleClosure(), getCFG().getStartBlock());
-
-            cfg.clearNodeToBlock();
-            blockToNodesMap = new BlockMap<>(cfg);
-
-            assignBlockToNodes(graph, selectedStrategy);
-            sortNodesWithinBlocks(graph, selectedStrategy);
-            printSchedule("after sorting nodes within blocks");
-        } else if (memsched == MemoryScheduling.OPTIMAL && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) {
+        if (memsched == MemoryScheduling.OPTIMAL && selectedStrategy != SchedulingStrategy.EARLIEST && graph.getNodes(FloatingReadNode.class).isNotEmpty()) {
             blockToKillSet = new BlockMap<>(cfg);
 
             assignBlockToNodes(graph, selectedStrategy);
@@ -654,8 +534,7 @@
                 }
             }
         }
-        assert false : "should have found a block for " + n;
-        return null;
+        throw new SchedulingError("should have found a block for " + n);
     }
 
     /**
@@ -719,15 +598,6 @@
                 blocksForUsage(node, usage, cdbc, strategy);
             }
         }
-        List<FixedNode> usages = phantomUsages.get(node);
-        if (usages != null) {
-            for (FixedNode usage : usages) {
-                if (cfg.getNodeToBlock().get(usage) == null) {
-                    throw new SchedulingError();
-                }
-                cdbc.apply(cfg.getNodeToBlock().get(usage));
-            }
-        }
 
         if (assertionEnabled()) {
             if (cdbc.block != null && !earliestBlock(node).dominates(cdbc.block)) {
@@ -759,10 +629,6 @@
      * Determines the earliest block in which the given node can be scheduled.
      */
     private Block earliestBlock(Node node) {
-        if (node.isExternal()) {
-            return cfg.getStartBlock();
-        }
-
         Block earliest = cfg.getNodeToBlock().get(node);
         if (earliest != null) {
             return earliest;
@@ -1013,10 +879,6 @@
         List<FloatingReadNode> reads = new ArrayList<>();
 
         if (memsched == MemoryScheduling.OPTIMAL) {
-            /*
-             * TODO: add assert for invariant
-             * "floatingreads occur always after memory checkpoints in unsorted list"
-             */
             for (ScheduledNode i : instructions) {
                 if (i instanceof FloatingReadNode) {
                     FloatingReadNode frn = (FloatingReadNode) i;
@@ -1070,8 +932,7 @@
             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))) {
-                // TODO: replace instanceof check with object identity check
+            } else if (!beforeLastLocation.isMarked(frn) && (readLocation == identity || (node != getCFG().graph.start() && ANY_LOCATION == identity))) {
                 reads.remove(frn);
                 addToLatestSorting(b, frn, sortedInstructions, visited, reads, beforeLastLocation);
             }
@@ -1088,7 +949,7 @@
             for (Node input : state.inputs()) {
                 if (input instanceof VirtualState) {
                     addUnscheduledToLatestSorting(b, (VirtualState) input, sortedInstructions, visited, reads, beforeLastLocation);
-                } else if (!input.isExternal()) {
+                } else {
                     addToLatestSorting(b, (ScheduledNode) input, sortedInstructions, visited, reads, beforeLastLocation);
                 }
             }
@@ -1105,17 +966,11 @@
             if (input instanceof FrameState) {
                 assert state == null;
                 state = (FrameState) input;
-            } else if (!input.isExternal()) {
+            } else {
                 addToLatestSorting(b, (ScheduledNode) input, sortedInstructions, visited, reads, beforeLastLocation);
 
             }
         }
-        List<FloatingNode> inputs = phantomInputs.get(i);
-        if (inputs != null) {
-            for (FloatingNode input : inputs) {
-                addToLatestSorting(b, input, sortedInstructions, visited, reads, beforeLastLocation);
-            }
-        }
 
         if (memsched == MemoryScheduling.OPTIMAL && reads.size() != 0) {
             if (i instanceof MemoryCheckpoint.Single) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.phases/src/com/oracle/graal/phases/util/ArraySet.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,54 @@
+/*
+ * 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.phases.util;
+
+import java.util.*;
+
+/**
+ * Mimic a set implementation with an ArrayList. Beneficial for small sets (compared to
+ * {@link HashSet}).
+ */
+public class ArraySet<E> extends ArrayList<E> implements Set<E> {
+    private static final long serialVersionUID = 4476957522387436654L;
+
+    public ArraySet() {
+        super();
+    }
+
+    public ArraySet(int i) {
+        super(i);
+    }
+
+    public ArraySet(Collection<? extends E> c) {
+        super(c);
+    }
+
+    @Override
+    public boolean add(E e) {
+        // avoid duplicated entries
+        if (contains(e)) {
+            return true;
+        }
+        return super.add(e);
+    }
+}
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/BinaryGraphPrinter.java	Tue Dec 17 21:26:42 2013 -0800
@@ -105,7 +105,6 @@
     }
 
     private final ConstantPool constantPool;
-    private final Map<Node, Integer> externalNodeIds;
     private final ByteBuffer buffer;
     private final WritableByteChannel channel;
 
@@ -113,7 +112,6 @@
         constantPool = new ConstantPool();
         buffer = ByteBuffer.allocateDirect(256 * 1024);
         this.channel = channel;
-        this.externalNodeIds = new HashMap<>();
     }
 
     public void print(Graph graph, String title, SchedulePhase predefinedSchedule) throws IOException {
@@ -389,25 +387,9 @@
         }
     }
 
-    /**
-     * Should be higher than any internal {@link Node#getId() node id}.
-     */
-    @SuppressWarnings("javadoc") private static final int FIRST_EXTERNAL_NODE_ID = Integer.getInteger("graal.binaryGraphPrinter.firstExternalNodeId", 10000000);
-
     @SuppressWarnings("deprecation")
-    private int getNodeId(Node node) {
-        if (!node.isExternal()) {
-            assert node.getId() < FIRST_EXTERNAL_NODE_ID : "internal node ids exceeded lowest external node id (" + FIRST_EXTERNAL_NODE_ID +
-                            ") - use graal.binaryGraphPrinter.firstExternalNodeId system property to increase the latter";
-            return node.getId();
-        } else {
-            Integer id = externalNodeIds.get(node);
-            if (id == null) {
-                id = FIRST_EXTERNAL_NODE_ID + externalNodeIds.size();
-                externalNodeIds.put(node, id);
-            }
-            return id;
-        }
+    private static int getNodeId(Node node) {
+        return node.getId();
     }
 
     private void writeNodes(Graph graph) throws IOException {
@@ -419,16 +401,8 @@
             }
         }
         Map<Object, Object> props = new HashMap<>();
-        Map<Node, Integer> externalNodes = new HashMap<>();
-        for (Node node : graph.getNodes()) {
-            for (Node input : node.inputs()) {
-                if (input.isExternal()) {
-                    externalNodes.put(input, getNodeId(input));
-                }
-            }
-        }
 
-        writeInt(graph.getNodeCount() + externalNodes.size());
+        writeInt(graph.getNodeCount());
 
         for (Node node : graph.getNodes()) {
             NodeClass nodeClass = node.getNodeClass();
@@ -453,22 +427,6 @@
 
             props.clear();
         }
-        for (Map.Entry<Node, Integer> e : externalNodes.entrySet()) {
-            Node node = e.getKey();
-            NodeClass nodeClass = node.getNodeClass();
-            node.getDebugProperties(props);
-            writeInt(e.getValue());
-            writePoolObject(nodeClass);
-            writeByte(0);
-            // properties
-            writeShort((char) props.size());
-            for (Entry<Object, Object> entry : props.entrySet()) {
-                String key = entry.getKey().toString();
-                writePoolObject(key);
-                writePropertyObject(entry.getValue());
-            }
-            props.clear();
-        }
     }
 
     private void writeEdges(Node node, Collection<Position> positions) throws IOException {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathIntrinsicsNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,178 @@
+/*
+ * 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.replacements.hsail;
+
+import com.oracle.graal.api.meta.Constant;
+import com.oracle.graal.api.meta.Value;
+import com.oracle.graal.compiler.hsail.HSAILLIRGenerator;
+import com.oracle.graal.graph.GraalInternalError;
+import com.oracle.graal.graph.Node;
+import com.oracle.graal.graph.spi.Canonicalizable;
+import com.oracle.graal.graph.spi.CanonicalizerTool;
+import com.oracle.graal.lir.hsail.HSAILArithmetic;
+import com.oracle.graal.nodes.calc.FloatingNode;
+import com.oracle.graal.nodes.ConstantNode;
+import com.oracle.graal.nodes.ValueNode;
+import com.oracle.graal.nodes.spi.ArithmeticLIRGenerator;
+import com.oracle.graal.nodes.spi.ArithmeticLIRLowerable;
+import com.oracle.graal.nodes.type.StampFactory;
+
+/**
+ * This node implements HSAIL intrinsics for specific {@link Math} routines.
+ */
+public class HSAILMathIntrinsicsNode extends FloatingNode implements Canonicalizable, ArithmeticLIRLowerable {
+
+    /**
+     * The parameter passed to the math operation that this node represents.
+     */
+    @Input private ValueNode param;
+
+    /**
+     * The math operation that this Node represents.
+     */
+    private final HSAILArithmetic operation;
+
+    /**
+     * Gets the parameter passed to the math operation that this node represents.
+     * 
+     * @return the parameter
+     */
+    public ValueNode getParameter() {
+        return param;
+    }
+
+    /**
+     * Returns the math operation represented by this node.
+     * 
+     * @return the operation
+     */
+    public HSAILArithmetic operation() {
+        return operation;
+    }
+
+    /**
+     * Creates a new HSAILMathIntrinsicNode.
+     * 
+     * @param x the argument to the math operation
+     * @param op the math operation
+     */
+    public HSAILMathIntrinsicsNode(ValueNode x, HSAILArithmetic op) {
+        super(StampFactory.forKind(x.kind()));
+        this.param = x;
+        this.operation = op;
+    }
+
+    /**
+     * Generates the LIR instructions for the math operation represented by this node.
+     */
+    @Override
+    public void generate(ArithmeticLIRGenerator gen) {
+        Value input = gen.operand(getParameter());
+        Value result;
+        switch (operation()) {
+            case ABS:
+                result = gen.emitMathAbs(input);
+                break;
+            case CEIL:
+                result = ((HSAILLIRGenerator) (gen)).emitMathCeil(input);
+                break;
+            case FLOOR:
+                result = ((HSAILLIRGenerator) (gen)).emitMathFloor(input);
+                break;
+            case RINT:
+                result = ((HSAILLIRGenerator) (gen)).emitMathRint(input);
+                break;
+            case SQRT:
+                result = gen.emitMathSqrt(input);
+                break;
+
+            default:
+                throw GraalInternalError.shouldNotReachHere();
+        }
+        gen.setResult(this, result);
+    }
+
+    /**
+     * Converts a constant to a boxed double.
+     */
+    public Constant evalConst(Constant... inputs) {
+        assert inputs.length == 1;
+        return Constant.forDouble(compute(inputs[0].asDouble(), operation()));
+    }
+
+    /**
+     * Converts the result of the math operation to a boxed Double constant node.
+     */
+    @Override
+    public Node canonical(CanonicalizerTool tool) {
+        if (getParameter().isConstant()) {
+            return ConstantNode.forPrimitive(evalConst(getParameter().asConstant()), graph());
+        }
+        return this;
+    }
+
+    // The routines below are node intrinsics. A call to any of these routines is replaced by a
+    // HSAILMathIntrinsicNode to handle the Math library operation represented by the
+    // HSAILArithmetic argument.
+
+    /**
+     * Node intrinsic for {@link Math} routines taking a single int parameter.
+     * 
+     * @param value
+     * @param op the math operation
+     * @return the result of the operation
+     */
+    @NodeIntrinsic
+    public static native int compute(int value, @ConstantNodeParameter HSAILArithmetic op);
+
+    /**
+     * Node intrinsic for {@link Math} routines taking a single double parameter.
+     * 
+     * @param value the input parameter
+     * @param op the math operation
+     * @return the result of the operation
+     */
+    @NodeIntrinsic
+    public static native long compute(long value, @ConstantNodeParameter HSAILArithmetic op);
+
+    /**
+     * Node intrinsic for {@link Math} routines taking a single float parameter.
+     * 
+     * @param value the input parameter
+     * @param op the math operation
+     * @return the result of the operation
+     */
+    @NodeIntrinsic
+    public static native float compute(float value, @ConstantNodeParameter HSAILArithmetic op);
+
+    /**
+     * Node intrinsic for {@link Math} routines taking a single double parameter.
+     * 
+     * @param value the input parameter
+     * @param op the math operation
+     * 
+     * @return the result of the operation
+     */
+    @NodeIntrinsic
+    public static native double compute(double value, @ConstantNodeParameter HSAILArithmetic op);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements.hsail/src/com/oracle/graal/replacements/hsail/HSAILMathSubstitutions.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,123 @@
+/*
+ * 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.replacements.hsail;
+
+import com.oracle.graal.api.replacements.ClassSubstitution;
+import com.oracle.graal.api.replacements.MethodSubstitution;
+import com.oracle.graal.lir.hsail.HSAILArithmetic;
+
+/**
+ * Substitutions for {@link Math} methods. For any calls to the routines listed below and annotated
+ * with {@link MethodSubstitution}, Graal replaces the call with a {@link HSAILMathIntrinsicsNode}.
+ */
+@ClassSubstitution(java.lang.Math.class)
+public class HSAILMathSubstitutions {
+
+    /**
+     * Substitution for {@link Math#abs(int)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static int abs(int x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS);
+    }
+
+    /**
+     * Substitution for {@link Math#abs(long)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static long abs(long x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS);
+    }
+
+    /**
+     * Substitution for {@link Math#abs(float)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static float abs(float x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS);
+    }
+
+    /**
+     * Substitution for {@link Math#abs(double)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static double abs(double x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.ABS);
+    }
+
+    /**
+     * Substitution for Math.ceil(double).
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static double ceil(double x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.CEIL);
+    }
+
+    /**
+     * Substitution for {@link Math#floor(double)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static double floor(double x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.FLOOR);
+    }
+
+    /**
+     * Substitution for {@link Math#rint(double)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static double rint(double x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.RINT);
+    }
+
+    /**
+     * Substitution for {@link Math#sqrt(double)}.
+     * 
+     * @param x the input
+     * @return the result of the computation
+     */
+    @MethodSubstitution
+    public static double sqrt(double x) {
+        return HSAILMathIntrinsicsNode.compute(x, HSAILArithmetic.SQRT);
+    }
+}
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue Dec 17 21:26:42 2013 -0800
@@ -60,7 +60,7 @@
     @Option(help = "")
     public static final OptionValue<Integer> TruffleInliningMaxRecursiveDepth = new OptionValue<>(2);
     @Option(help = "")
-    public static final OptionValue<Integer> TruffleInliningMaxCallerSize = new OptionValue<>(2500);
+    public static final OptionValue<Integer> TruffleInliningMaxCallerSize = new OptionValue<>(2250);
     @Option(help = "")
     public static final OptionValue<Integer> TruffleInliningMaxCalleeSize = new OptionValue<>(250);
     @Option(help = "")
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue Dec 17 21:26:42 2013 -0800
@@ -231,7 +231,7 @@
 
     public ValueNode getScalarAlias(ValueNode node) {
         assert !(node instanceof VirtualObjectNode);
-        if (node == null || !node.isAlive() || node.isExternal() || aliases.isNew(node)) {
+        if (node == null || !node.isAlive() || aliases.isNew(node)) {
             return node;
         }
         ValueNode result = aliases.get(node);
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/GraphEffectList.java	Tue Dec 17 21:26:42 2013 -0800
@@ -344,7 +344,7 @@
                     commit.getLocks().addAll(locks);
 
                     assert commit.usages().filter(AllocatedObjectNode.class).count() == commit.usages().count();
-                    HashSet<AllocatedObjectNode> materializedValues = new HashSet<>(commit.usages().filter(AllocatedObjectNode.class).snapshot());
+                    List<AllocatedObjectNode> materializedValues = commit.usages().filter(AllocatedObjectNode.class).snapshot();
                     for (int i = 0; i < commit.getValues().size(); i++) {
                         if (materializedValues.contains(commit.getValues().get(i))) {
                             commit.getValues().set(i, ((AllocatedObjectNode) commit.getValues().get(i)).getVirtualObject());
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue Dec 17 21:26:42 2013 -0800
@@ -37,6 +37,7 @@
 import com.oracle.graal.nodes.spi.Virtualizable.EscapeState;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.schedule.*;
+import com.oracle.graal.phases.util.*;
 import com.oracle.graal.virtual.nodes.*;
 
 public abstract class PartialEscapeClosure<BlockT extends PartialEscapeBlockState<BlockT>> extends EffectsClosure<BlockT> {
@@ -136,7 +137,7 @@
                 nodeWithState.asNode().replaceFirstInput(frameState, frameState.copyWithInputs());
                 frameState = nodeWithState.getState();
             }
-            final HashSet<ObjectState> virtual = new HashSet<>();
+            final Set<ObjectState> virtual = new ArraySet<>();
             frameState.applyToNonVirtual(new NodeClosure<ValueNode>() {
 
                 @Override
@@ -611,7 +612,7 @@
     }
 
     public ObjectState getObjectState(PartialEscapeBlockState<?> state, ValueNode value) {
-        if (value == null || value.isExternal()) {
+        if (value == null) {
             return null;
         }
         if (value.isAlive() && !aliases.isNew(value)) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/VirtualUtil.java	Tue Dec 17 21:26:42 2013 -0800
@@ -69,22 +69,18 @@
         for (Node node : graph.getNodes()) {
             if (flood.isMarked(node)) {
                 for (Node input : node.inputs()) {
-                    if (!input.isExternal()) {
-                        flood.add(input);
-                        if (!path.containsKey(input)) {
-                            path.put(input, node);
-                        }
+                    flood.add(input);
+                    if (!path.containsKey(input)) {
+                        path.put(input, node);
                     }
                 }
             }
         }
         for (Node current : flood) {
             for (Node input : current.inputs()) {
-                if (!input.isExternal()) {
-                    flood.add(input);
-                    if (!path.containsKey(input)) {
-                        path.put(input, current);
-                    }
+                flood.add(input);
+                if (!path.containsKey(input)) {
+                    path.put(input, current);
                 }
             }
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AlwaysValidAssumptionTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,51 @@
+/*
+ * 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.truffle.api.test.utilities;
+
+import static org.junit.Assert.*;
+import org.junit.*;
+
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+
+public class AlwaysValidAssumptionTest {
+
+    @Test
+    public void testCheck() throws InvalidAssumptionException {
+        final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE;
+        assumption.check();
+    }
+
+    @Test
+    public void testIsValid() {
+        final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE;
+        assertTrue(assumption.isValid());
+    }
+
+    @Test(expected = UnsupportedOperationException.class)
+    public void testCannotInvalidate() {
+        final AlwaysValidAssumption assumption = AlwaysValidAssumption.INSTANCE;
+        assumption.invalidate();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/AssumedValueTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,46 @@
+/*
+ * 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.truffle.api.test.utilities;
+
+import static org.junit.Assert.*;
+import org.junit.*;
+
+import com.oracle.truffle.api.utilities.*;
+
+public class AssumedValueTest {
+
+    @Test
+    public void testGet() {
+        final AssumedValue<String> assumedValue = new AssumedValue<>("assumed-value", "1");
+        assertEquals("1", assumedValue.get());
+    }
+
+    @Test
+    public void testSet() {
+        final AssumedValue<String> assumedValue = new AssumedValue<>("assumed-value", "1");
+        assertEquals("1", assumedValue.get());
+        assumedValue.set("2");
+        assertEquals("2", assumedValue.get());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/BranchProfileTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,38 @@
+/*
+ * 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.truffle.api.test.utilities;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.utilities.*;
+
+public class BranchProfileTest {
+
+    @Test
+    public void testEnter() {
+        BranchProfile profile = new BranchProfile();
+        profile.enter();
+        profile.enter();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/CyclicAssumptionTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,62 @@
+/*
+ * 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.truffle.api.test.utilities;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.utilities.*;
+
+public class CyclicAssumptionTest {
+
+    @Test
+    public void testIsValid() {
+        final CyclicAssumption assumption = new CyclicAssumption("cyclic-assumption");
+        assertTrue(assumption.getAssumption().isValid());
+    }
+
+    @Test
+    public void testInvalidate() {
+        final CyclicAssumption cyclicAssumption = new CyclicAssumption("cyclic-assumption");
+
+        final Assumption firstAssumption = cyclicAssumption.getAssumption();
+        assertEquals("cyclic-assumption", firstAssumption.getName());
+        assertTrue(firstAssumption.isValid());
+
+        cyclicAssumption.invalidate();
+
+        assertFalse(firstAssumption.isValid());
+
+        final Assumption secondAssumption = cyclicAssumption.getAssumption();
+        assertEquals("cyclic-assumption", secondAssumption.getName());
+        assertTrue(secondAssumption.isValid());
+
+        cyclicAssumption.invalidate();
+
+        assertFalse(firstAssumption.isValid());
+        assertFalse(secondAssumption.isValid());
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api.test/src/com/oracle/truffle/api/test/utilities/UnionAssumptionTest.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,119 @@
+/*
+ * 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.truffle.api.test.utilities;
+
+import static org.junit.Assert.*;
+
+import org.junit.*;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+import com.oracle.truffle.api.utilities.*;
+
+public class UnionAssumptionTest {
+
+    @Test
+    public void testIsValid() {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+        assertTrue(union.isValid());
+    }
+
+    @Test
+    public void testCheck() throws InvalidAssumptionException {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+        union.check();
+    }
+
+    @Test
+    public void testFirstInvalidateIsValid() {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        first.invalidate();
+
+        assertFalse(union.isValid());
+    }
+
+    @Test(expected = InvalidAssumptionException.class)
+    public void testFirstInvalidateCheck() throws InvalidAssumptionException {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        first.invalidate();
+
+        union.check();
+    }
+
+    @Test
+    public void testSecondInvalidateIsValid() {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        second.invalidate();
+
+        assertFalse(union.isValid());
+    }
+
+    @Test(expected = InvalidAssumptionException.class)
+    public void testSecondInvalidateCheck() throws InvalidAssumptionException {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        second.invalidate();
+
+        union.check();
+    }
+
+    @Test
+    public void testBothInvalidateIsValid() {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        first.invalidate();
+        second.invalidate();
+
+        assertFalse(union.isValid());
+    }
+
+    @Test(expected = InvalidAssumptionException.class)
+    public void testBothInvalidateCheck() throws InvalidAssumptionException {
+        final Assumption first = Truffle.getRuntime().createAssumption("first");
+        final Assumption second = Truffle.getRuntime().createAssumption("second");
+        final UnionAssumption union = new UnionAssumption(first, second);
+
+        first.invalidate();
+        second.invalidate();
+
+        union.check();
+    }
+
+}
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Truffle.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/Truffle.java	Tue Dec 17 21:26:42 2013 -0800
@@ -31,19 +31,21 @@
  */
 public class Truffle {
 
-    private static TruffleRuntime runtime;
+    private static final TruffleRuntime RUNTIME;
 
     private static native TruffleRuntime initializeRuntime();
 
     public static TruffleRuntime getRuntime() {
-        return runtime;
+        return RUNTIME;
     }
 
     static {
+        TruffleRuntime runtime;
         try {
             runtime = initializeRuntime();
         } catch (UnsatisfiedLinkError e) {
             runtime = new DefaultTruffleRuntime();
         }
+        RUNTIME = runtime;
     }
 }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/impl/DefaultCallTarget.java	Tue Dec 17 21:26:42 2013 -0800
@@ -50,6 +50,10 @@
         return rootNode.execute(frame);
     }
 
+    public FrameDescriptor getFrameDescriptor() {
+        return frameDescriptor;
+    }
+
     public RootNode getRootNode() {
         return rootNode;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AlwaysValidAssumption.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,60 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.utilities;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * An assumption that is always valid. Used as a placeholder where an assumption is needed but never
+ * invalidated.
+ */
+public final class AlwaysValidAssumption implements Assumption {
+
+    public static final AlwaysValidAssumption INSTANCE = new AlwaysValidAssumption();
+
+    private AlwaysValidAssumption() {
+    }
+
+    @Override
+    public void check() throws InvalidAssumptionException {
+    }
+
+    @Override
+    public void invalidate() {
+        throw new UnsupportedOperationException("Cannot invalidate this assumption - it is always valid");
+    }
+
+    @Override
+    public String getName() {
+        return getClass().getName();
+    }
+
+    @Override
+    public boolean isValid() {
+        return true;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/AssumedValue.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,68 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.utilities;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * A {@link CompilationFinal} value combined with an {@link Assumption} to notify when it changes.
+ * Note that you should be careful that modifications to this value do not cause deoptimization
+ * loops. This could be by using a value that is monotonic.
+ */
+public class AssumedValue<T> {
+
+    @CompilationFinal private T value;
+    private final CyclicAssumption assumption;
+
+    public AssumedValue(String name, T initialValue) {
+        assumption = new CyclicAssumption(name);
+        value = initialValue;
+    }
+
+    /**
+     * Get the current value, updating it if it has been {@link #set}. The compiler may be able to
+     * make this method return a constant value, but still accommodate mutation.
+     */
+    public T get() {
+        try {
+            assumption.getAssumption().check();
+        } catch (InvalidAssumptionException e) {
+            // No need to rewrite anything - just pick up the new value
+        }
+
+        return value;
+    }
+
+    /**
+     * Set a new value, which will be picked up the next time {@link #get} is called.
+     */
+    public void set(T newValue) {
+        value = newValue;
+        assumption.invalidate();
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/BranchProfile.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,47 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.utilities;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.CompilerDirectives.*;
+
+/**
+ * Utility class to speculate on branches to be never visited. If the {@link #enter()} method is
+ * invoked first the optimized code is invalidated and the branch where {@link #enter()} is invoked
+ * is enabled for compilation. Otherwise if the {@link #enter()} method was never invoked the branch
+ * will not get compiled.
+ */
+public final class BranchProfile {
+
+    @CompilationFinal private boolean visited;
+
+    public void enter() {
+        if (!visited) {
+            CompilerDirectives.transferToInterpreterAndInvalidate();
+            visited = true;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/CyclicAssumption.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,58 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.utilities;
+
+import com.oracle.truffle.api.*;
+
+/**
+ * Holds an {@link Assumption}, and knows how to recreate it with the same properties on
+ * invalidation. Used so that mutability is isolated in this class, and all other classes that need
+ * an assumption that may be recreated can have a final reference to an object of this class. Note
+ * that you should be careful that repeated invalidations do not cause a deoptimization loop in that
+ * same way that you would with any other assumption.
+ */
+public class CyclicAssumption {
+
+    private final String name;
+    private Assumption assumption;
+
+    public CyclicAssumption(String name) {
+        this.name = name;
+        invalidate();
+    }
+
+    public void invalidate() {
+        if (assumption != null) {
+            assumption.invalidate();
+        }
+
+        assumption = Truffle.getRuntime().createAssumption(name);
+    }
+
+    public Assumption getAssumption() {
+        return assumption;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/UnionAssumption.java	Tue Dec 17 21:26:42 2013 -0800
@@ -0,0 +1,72 @@
+/*
+ * 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.  Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * 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.truffle.api.utilities;
+
+import com.oracle.truffle.api.*;
+import com.oracle.truffle.api.nodes.*;
+
+/**
+ * An assumption that combines two other assumptions. A check on this assumption checks both of the
+ * child assumptions.
+ */
+public class UnionAssumption implements Assumption {
+
+    private final String name;
+    private final Assumption first;
+    private final Assumption second;
+
+    public UnionAssumption(String name, Assumption first, Assumption second) {
+        this.name = name;
+        this.first = first;
+        this.second = second;
+    }
+
+    public UnionAssumption(Assumption first, Assumption second) {
+        this(null, first, second);
+    }
+
+    @Override
+    public void check() throws InvalidAssumptionException {
+        first.check();
+        second.check();
+    }
+
+    @Override
+    public void invalidate() {
+        first.invalidate();
+        second.invalidate();
+    }
+
+    @Override
+    public String getName() {
+        return name;
+    }
+
+    @Override
+    public boolean isValid() {
+        return first.isValid() && second.isValid();
+    }
+
+}
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/CallNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -34,127 +34,126 @@
     private static final int INLINE_CACHE_SIZE = 2;
 
     @Child protected TypedNode functionNode;
-
-    private CallNode(TypedNode functionNode) {
-        this.functionNode = adoptChild(functionNode);
-    }
+    @Child protected ArgumentsNode argumentsNode;
 
-    private CallTarget executeCallTargetNode(VirtualFrame frame) {
-        try {
-            return functionNode.executeCallTarget(frame);
-        } catch (UnexpectedResultException e) {
-            throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported.");
-        }
+    public CallNode(TypedNode functionNode, ArgumentsNode argumentsNode) {
+        this.functionNode = adoptChild(functionNode);
+        this.argumentsNode = adoptChild(argumentsNode);
     }
 
     @Override
     public final Object executeGeneric(VirtualFrame frame) {
-        return executeGeneric(frame, executeCallTargetNode(frame));
+        CallTarget function;
+        try {
+            function = functionNode.executeCallTarget(frame);
+        } catch (UnexpectedResultException e) {
+            throw new UnsupportedOperationException("Call to " + e.getMessage() + " not supported.");
+        }
+        Object[] arguments = argumentsNode.executeArray(frame);
+        return executeCall(frame, function, arguments);
     }
 
-    public abstract Object executeGeneric(VirtualFrame frame, CallTarget function);
+    public abstract Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments);
 
     public static CallNode create(TypedNode function, TypedNode[] arguments) {
         return new UninitializedCallNode(function, new ArgumentsNode(arguments), 0);
     }
 
-    private static final class CachedCallNode extends CallNode {
-
-        @Child protected CallNode nextNode;
-        @Child protected TypedNode currentNode;
-        private final CallTarget cachedFunction;
-
-        public CachedCallNode(TypedNode function, TypedNode current, CallNode next, CallTarget cachedFunction) {
-            super(function);
-            this.currentNode = adoptChild(current);
-            this.nextNode = adoptChild(next);
-            this.cachedFunction = cachedFunction;
-        }
-
-        @Override
-        public Object executeGeneric(VirtualFrame frame, CallTarget function) {
-            if (this.cachedFunction == function) {
-                return currentNode.executeGeneric(frame);
-            }
-            return nextNode.executeGeneric(frame, function);
-        }
-    }
-
     private static final class UninitializedCallNode extends CallNode {
 
-        @Child protected ArgumentsNode uninitializedArgs;
         protected final int depth;
 
         UninitializedCallNode(TypedNode function, ArgumentsNode args, int depth) {
-            super(function);
-            this.uninitializedArgs = adoptChild(args);
+            super(function, args);
             this.depth = depth;
         }
 
         UninitializedCallNode(UninitializedCallNode copy) {
-            super(null);
-            this.uninitializedArgs = adoptChild(copy.uninitializedArgs);
+            super(null, null);
             this.depth = copy.depth + 1;
         }
 
         @Override
-        public Object executeGeneric(VirtualFrame frame, CallTarget function) {
+        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
             CompilerDirectives.transferToInterpreter();
-            return specialize(function).executeGeneric(frame, function);
+            return specialize(function).executeCall(frame, function, arguments);
         }
 
         private CallNode specialize(CallTarget function) {
             CompilerAsserts.neverPartOfCompilation();
 
             if (depth < INLINE_CACHE_SIZE) {
-                TypedNode current = createCacheNode(function);
+                DefaultCallTarget callTarget = (DefaultCallTarget) function;
+                FunctionRootNode root = (FunctionRootNode) callTarget.getRootNode();
                 CallNode next = new UninitializedCallNode(this);
-                return replace(new CachedCallNode(this.functionNode, current, next, function));
+                InlinableDirectCallNode directCall = new InlinableDirectCallNode(functionNode, argumentsNode, next, callTarget);
+                replace(directCall);
+                if (root.isInlineImmediatly()) {
+                    return directCall.inlineImpl();
+                } else {
+                    return directCall;
+                }
             } else {
-                CallNode topMost = (CallNode) getTopNode();
-                return topMost.replace(new GenericCallNode(topMost.functionNode, uninitializedArgs));
+                CallNode topMost = (CallNode) NodeUtil.getNthParent(this, depth);
+                return topMost.replace(new GenericCallNode(topMost.functionNode, topMost.argumentsNode));
             }
         }
 
-        protected Node getTopNode() {
-            Node parentNode = this;
-            for (int i = 0; i < depth; i++) {
-                parentNode = parentNode.getParent();
-            }
-            return parentNode;
+    }
+
+    private abstract static class DirectCallNode extends CallNode {
+
+        protected final DefaultCallTarget cachedFunction;
+
+        @Child protected CallNode nextNode;
+
+        public DirectCallNode(TypedNode function, ArgumentsNode arguments, DefaultCallTarget cachedFunction, CallNode next) {
+            super(function, arguments);
+            this.cachedFunction = cachedFunction;
+            this.nextNode = adoptChild(next);
         }
 
-        protected TypedNode createCacheNode(CallTarget function) {
-            ArgumentsNode clonedArgs = NodeUtil.cloneNode(uninitializedArgs);
+        @Override
+        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
+            if (this.cachedFunction == function) {
+                return executeCurrent(frame, arguments);
+            }
+            return nextNode.executeCall(frame, function, arguments);
+        }
 
-            if (function instanceof DefaultCallTarget) {
-                DefaultCallTarget defaultFunction = (DefaultCallTarget) function;
-                RootNode rootNode = defaultFunction.getRootNode();
-                if (rootNode instanceof FunctionRootNode) {
-                    FunctionRootNode root = (FunctionRootNode) rootNode;
-                    if (root.isAlwaysInline()) {
-                        TypedNode inlinedCall = root.inline(clonedArgs);
-                        if (inlinedCall != null) {
-                            return inlinedCall;
-                        }
-                    }
-                    return new InlinableCallNode((DefaultCallTarget) function, clonedArgs);
-                }
-                // got a call target that is not inlinable (should not occur for SL)
-                return new DispatchedCallNode(defaultFunction, clonedArgs);
-            } else {
-                throw new AssertionError();
-            }
+        protected abstract Object executeCurrent(VirtualFrame frame, Object[] arguments);
 
-        }
     }
 
-    private static final class InlinableCallNode extends DispatchedCallNode implements InlinableCallSite {
+    private static final class InlinableDirectCallNode extends DirectCallNode implements InlinableCallSite {
 
         @CompilationFinal private int callCount;
 
-        InlinableCallNode(DefaultCallTarget function, ArgumentsNode arguments) {
-            super(function, arguments);
+        InlinableDirectCallNode(TypedNode function, ArgumentsNode arguments, CallNode next, DefaultCallTarget cachedFunction) {
+            super(function, arguments, cachedFunction, next);
+        }
+
+        @Override
+        public Object executeCurrent(VirtualFrame frame, Object[] arguments) {
+            if (CompilerDirectives.inInterpreter()) {
+                callCount++;
+            }
+            return cachedFunction.call(frame.pack(), new SLArguments(arguments));
+        }
+
+        InlinedDirectCallNode inlineImpl() {
+            CompilerAsserts.neverPartOfCompilation();
+            RootNode root = cachedFunction.getRootNode();
+            TypedNode inlinedNode = ((FunctionRootNode) root).inline();
+            assert inlinedNode != null;
+            return replace(new InlinedDirectCallNode(this, inlinedNode), "Inlined " + root);
+        }
+
+        @Override
+        public boolean inline(FrameFactory factory) {
+            inlineImpl();
+            /* SL is always able to inline if required. */
+            return true;
         }
 
         @Override
@@ -169,7 +168,7 @@
 
         @Override
         public Node getInlineTree() {
-            RootNode root = function.getRootNode();
+            RootNode root = cachedFunction.getRootNode();
             if (root instanceof FunctionRootNode) {
                 return ((FunctionRootNode) root).getUninitializedBody();
             }
@@ -177,67 +176,46 @@
         }
 
         @Override
-        public boolean inline(FrameFactory factory) {
-            CompilerAsserts.neverPartOfCompilation();
-            TypedNode functionCall = null;
-
-            RootNode root = function.getRootNode();
-            if (root instanceof FunctionRootNode) {
-                functionCall = ((FunctionRootNode) root).inline(NodeUtil.cloneNode(args));
-            }
-            if (functionCall != null) {
-                this.replace(functionCall);
-                return true;
-            } else {
-                return false;
-            }
-        }
-
-        @Override
-        public Object executeGeneric(VirtualFrame frame) {
-            if (CompilerDirectives.inInterpreter()) {
-                callCount++;
-            }
-            return super.executeGeneric(frame);
-        }
-
-        @Override
         public CallTarget getCallTarget() {
-            return function;
+            return cachedFunction;
         }
 
     }
 
-    private static class DispatchedCallNode extends TypedNode {
+    private static class InlinedDirectCallNode extends DirectCallNode implements InlinedCallSite {
+
+        private final FrameDescriptor descriptor;
+        @Child private TypedNode inlinedBody;
 
-        @Child protected ArgumentsNode args;
-        protected final DefaultCallTarget function;
-
-        DispatchedCallNode(DefaultCallTarget function, ArgumentsNode arguments) {
-            this.args = adoptChild(arguments);
-            this.function = function;
+        InlinedDirectCallNode(InlinableDirectCallNode prev, TypedNode inlinedBody) {
+            super(prev.functionNode, prev.argumentsNode, prev.cachedFunction, prev.nextNode);
+            this.descriptor = cachedFunction.getFrameDescriptor();
+            this.inlinedBody = adoptChild(inlinedBody);
         }
 
         @Override
-        public Object executeGeneric(VirtualFrame frame) {
-            SLArguments argsObject = new SLArguments(args.executeArray(frame));
-            return function.call(frame.pack(), argsObject);
+        public Object executeCurrent(VirtualFrame frame, Object[] arguments) {
+            SLArguments slArguments = new SLArguments(arguments);
+            VirtualFrame newFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), slArguments, descriptor);
+            return inlinedBody.executeGeneric(newFrame);
         }
+
+        @Override
+        public CallTarget getCallTarget() {
+            return cachedFunction;
+        }
+
     }
 
     private static final class GenericCallNode extends CallNode {
 
-        @Child protected ArgumentsNode args;
-
         GenericCallNode(TypedNode functionNode, ArgumentsNode arguments) {
-            super(functionNode);
-            this.args = adoptChild(arguments);
+            super(functionNode, arguments);
         }
 
         @Override
-        public Object executeGeneric(VirtualFrame frame, CallTarget function) {
-            SLArguments argsObject = new SLArguments(args.executeArray(frame));
-            return function.call(frame.pack(), argsObject);
+        public Object executeCall(VirtualFrame frame, CallTarget function, Object[] arguments) {
+            return function.call(frame.pack(), new SLArguments(arguments));
         }
     }
 
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/FunctionRootNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -35,14 +35,14 @@
 
     private final TypedNode uninitializedBody;
     private final String name;
-    private final boolean alwaysInline;
+    private final boolean inlineImmediatly;
 
-    private FunctionRootNode(TypedNode body, String name, boolean alwaysInline) {
+    private FunctionRootNode(TypedNode body, String name, boolean inlineImmediatly) {
         super(null);
         this.uninitializedBody = NodeUtil.cloneNode(body);
         this.body = adoptChild(body);
         this.name = name;
-        this.alwaysInline = alwaysInline;
+        this.inlineImmediatly = inlineImmediatly;
     }
 
     public static CallTarget createBuiltin(SLContext context, NodeFactory<? extends BuiltinNode> factory, String name) {
@@ -67,36 +67,12 @@
         return body.executeGeneric(frame);
     }
 
-    public boolean isAlwaysInline() {
-        return alwaysInline;
-    }
-
-    public TypedNode inline(ArgumentsNode clonedArgs) {
-        TypedNode clonedBody = NodeUtil.cloneNode(uninitializedBody);
-        if (clonedBody instanceof BuiltinNode) {
-            return inlineBuiltin(clonedArgs, (BuiltinNode) clonedBody);
-        } else if (clonedBody instanceof FunctionBodyNode) {
-            return inlineFunction(clonedArgs, (FunctionBodyNode) clonedBody);
-        } else {
-            throw new UnsupportedOperationException();
-        }
+    public boolean isInlineImmediatly() {
+        return inlineImmediatly;
     }
 
-    private InlinedFunctionNode inlineFunction(ArgumentsNode clonedArgs, FunctionBodyNode clonedBody) {
-        return new InlinedFunctionNode(getCallTarget(), clonedBody, clonedArgs);
-    }
-
-    private static TypedNode inlineBuiltin(ArgumentsNode clonedArgs, BuiltinNode builtin) {
-        TypedNode[] callerArgs = clonedArgs.getArguments();
-        TypedNode[] builtinArgs = builtin.getArguments();
-        for (int i = 0; i < builtinArgs.length; i++) {
-            if (i < callerArgs.length) {
-                builtinArgs[i].replace(callerArgs[i]);
-            } else {
-                builtinArgs[i].replace(new NullLiteralNode());
-            }
-        }
-        return builtin;
+    public TypedNode inline() {
+        return NodeUtil.cloneNode(uninitializedBody);
     }
 
     public Node getUninitializedBody() {
@@ -108,34 +84,6 @@
         return "function " + name;
     }
 
-    private static final class InlinedFunctionNode extends TypedNode implements InlinedCallSite {
-
-        @Child private FunctionBodyNode body;
-        @Child private ArgumentsNode arguments;
-
-        private final CallTarget callTarget;
-        private final FrameDescriptor frameDescriptor;
-
-        public InlinedFunctionNode(CallTarget callTarget, FunctionBodyNode body, ArgumentsNode arguments) {
-            this.callTarget = callTarget;
-            this.body = adoptChild(body);
-            this.frameDescriptor = body.getFrameDescriptor();
-            this.arguments = adoptChild(arguments);
-        }
-
-        @Override
-        public Object executeGeneric(VirtualFrame frame) {
-            SLArguments args = new SLArguments(arguments.executeArray(frame));
-            VirtualFrame childFrame = Truffle.getRuntime().createVirtualFrame(frame.pack(), args, frameDescriptor);
-            return body.executeGeneric(childFrame);
-        }
-
-        public CallTarget getCallTarget() {
-            return callTarget;
-        }
-
-    }
-
     public String getName() {
         return name;
     }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/IfNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/IfNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -24,6 +24,7 @@
 
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.utilities.*;
 
 @NodeChild(value = "conditionNode", type = ConditionNode.class)
 public abstract class IfNode extends StatementNode {
@@ -31,6 +32,9 @@
     @Child private StatementNode thenPartNode;
     @Child private StatementNode elsePartNode;
 
+    private final BranchProfile ifBranch = new BranchProfile();
+    private final BranchProfile elseBranch = new BranchProfile();
+
     public IfNode(StatementNode thenPart, StatementNode elsePart) {
         this.thenPartNode = adoptChild(thenPart);
         this.elsePartNode = adoptChild(elsePart);
@@ -43,9 +47,11 @@
     @Specialization
     public void doVoid(VirtualFrame frame, boolean condition) {
         if (condition) {
+            ifBranch.enter();
             thenPartNode.executeVoid(frame);
         } else {
             if (elsePartNode != null) {
+                elseBranch.enter();
                 elsePartNode.executeVoid(frame);
             }
         }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadArgumentNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadArgumentNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -23,12 +23,16 @@
 package com.oracle.truffle.sl.nodes;
 
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.runtime.*;
 
 public class ReadArgumentNode extends TypedNode {
 
     private final int index;
 
+    private final BranchProfile outOfBounds = new BranchProfile();
+    private final BranchProfile inBounds = new BranchProfile();
+
     public ReadArgumentNode(int index) {
         this.index = index;
     }
@@ -37,8 +41,10 @@
     public Object executeGeneric(VirtualFrame frame) {
         Object[] args = SLArguments.get(frame).arguments;
         if (index < args.length) {
+            inBounds.enter();
             return args[index];
         } else {
+            outOfBounds.enter();
             return SLNull.INSTANCE;
         }
     }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadFunctionNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/ReadFunctionNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -23,16 +23,15 @@
 package com.oracle.truffle.sl.nodes;
 
 import com.oracle.truffle.api.*;
-import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.utilities.*;
 import com.oracle.truffle.sl.runtime.*;
 
 public final class ReadFunctionNode extends TypedNode {
 
     private final SLFunctionRegistry registry;
     private final String name;
-
-    @CompilationFinal private boolean seenInvalidFunction;
+    private final BranchProfile invalidFunction = new BranchProfile();
 
     public ReadFunctionNode(SLFunctionRegistry registry, String name) {
         this.registry = registry;
@@ -50,14 +49,8 @@
         if (target != null) {
             return target;
         }
-        if (!seenInvalidFunction) {
-            CompilerDirectives.transferToInterpreter();
-            seenInvalidFunction = true;
-        }
-        if (seenInvalidFunction) {
-            throw new RuntimeException("Function with name '" + name + "' not found.");
-        }
-        return null;
+        invalidFunction.enter();
+        throw new RuntimeException("Function with name '" + name + "' not found.");
     }
 
 }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WhileNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WhileNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -23,6 +23,7 @@
 package com.oracle.truffle.sl.nodes;
 
 import com.oracle.truffle.api.frame.*;
+import com.oracle.truffle.api.utilities.*;
 
 public class WhileNode extends StatementNode {
 
@@ -33,6 +34,11 @@
     private final BreakException breakTarget;
     private final ContinueException continueTarget;
 
+    private final BranchProfile continueMismatch = new BranchProfile();
+    private final BranchProfile continueMatch = new BranchProfile();
+    private final BranchProfile breakMismatch = new BranchProfile();
+    private final BranchProfile breakMatch = new BranchProfile();
+
     public WhileNode(ConditionNode condition, StatementNode body) {
         this.condition = adoptChild(condition);
         this.body = adoptChild(body);
@@ -49,15 +55,19 @@
                     body.executeVoid(frame);
                 } catch (ContinueException ex) {
                     if (ex != continueTarget) {
+                        continueMismatch.enter();
                         throw ex;
                     }
+                    continueMatch.enter();
                     // Fall through to next loop iteration.
                 }
             }
         } catch (BreakException ex) {
             if (ex != breakTarget) {
+                breakMismatch.enter();
                 throw ex;
             }
+            breakMatch.enter();
             // Done executing this loop, exit method to execute statement following the loop.
         }
     }
--- a/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WriteLocalNode.java	Tue Dec 17 20:22:45 2013 -0800
+++ b/graal/com.oracle.truffle.sl/src/com/oracle/truffle/sl/nodes/WriteLocalNode.java	Tue Dec 17 21:26:42 2013 -0800
@@ -22,6 +22,7 @@
  */
 package com.oracle.truffle.sl.nodes;
 
+import com.oracle.truffle.api.*;
 import com.oracle.truffle.api.dsl.*;
 import com.oracle.truffle.api.frame.*;
 
@@ -36,22 +37,51 @@
         this(node.slot);
     }
 
-    @Specialization
+    @Specialization(guards = "isIntKind")
     public int write(VirtualFrame frame, int right) {
         frame.setInt(slot, right);
         return right;
     }
 
-    @Specialization
+    @Specialization(guards = "isBooleanKind")
     public boolean write(VirtualFrame frame, boolean right) {
         frame.setBoolean(slot, right);
         return right;
     }
 
-    @Specialization
+    @Specialization(guards = "isObjectKind")
     public Object writeGeneric(VirtualFrame frame, Object right) {
         frame.setObject(slot, right);
         return right;
     }
 
+    protected final boolean isIntKind() {
+        return isKind(FrameSlotKind.Int);
+    }
+
+    protected final boolean isBooleanKind() {
+        return isKind(FrameSlotKind.Boolean);
+    }
+
+    protected final boolean isObjectKind() {
+        if (slot.getKind() != FrameSlotKind.Object) {
+            CompilerDirectives.transferToInterpreter();
+            slot.setKind(FrameSlotKind.Object);
+        }
+        return true;
+    }
+
+    private boolean isKind(FrameSlotKind kind) {
+        return slot.getKind() == kind || initialSetKind(kind);
+    }
+
+    private boolean initialSetKind(FrameSlotKind kind) {
+        if (slot.getKind() == FrameSlotKind.Illegal) {
+            CompilerDirectives.transferToInterpreter();
+            slot.setKind(kind);
+            return true;
+        }
+        return false;
+    }
+
 }
--- a/mx/mx_graal.py	Tue Dec 17 20:22:45 2013 -0800
+++ b/mx/mx_graal.py	Tue Dec 17 21:26:42 2013 -0800
@@ -808,7 +808,7 @@
         f_testfile.close()
         harness(projectscp, vmArgs)
 
-def _unittest(args, annotations):
+def _unittest(args, annotations, prefixcp=""):
     mxdir = dirname(__file__)
     name = 'JUnitWrapper'
     javaSource = join(mxdir, name + '.java')
@@ -830,9 +830,9 @@
         if len(testclasses) == 1:
             # Execute Junit directly when one test is being run. This simplifies
             # replaying the VM execution in a native debugger (e.g., gdb).
-            vm(prefixArgs + vmArgs + ['-cp', projectscp, 'org.junit.runner.JUnitCore'] + testclasses)
+            vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp, 'org.junit.runner.JUnitCore'] + testclasses)
         else:
-            vm(prefixArgs + vmArgs + ['-cp', projectscp + os.pathsep + mxdir, name] + [testfile])
+            vm(prefixArgs + vmArgs + ['-cp', prefixcp + projectscp + os.pathsep + mxdir, name] + [testfile])
 
     try:
         _run_tests(args, harness, annotations, testfile)
@@ -1348,9 +1348,17 @@
 
 def trufflejar(args=None):
     """make truffle.jar"""
+
+    # Test with the built classes
+    _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters'])
+
     # We use the DSL processor as the starting point for the classpath - this
     # therefore includes the DSL processor, the DSL and the API.
-    packagejar(mx.classpath("com.oracle.truffle.dsl.processor").split(os.pathsep), "truffle.jar", None)
+    packagejar(mx.classpath("com.oracle.truffle.dsl.processor").split(os.pathsep), "truffle.jar", None, "com.oracle.truffle.dsl.processor.TruffleProcessor")
+
+    # Test with the JAR
+    _unittest(["com.oracle.truffle.api.test", "com.oracle.truffle.api.dsl.test"], ['@Test', '@LongTest', '@Parameters'], "truffle.jar:")
+
 
 def isGraalEnabled(vm):
     return vm != 'original' and not vm.endswith('nograal')
@@ -1444,8 +1452,8 @@
 
     mx.distribution('GRAAL').add_update_listener(_installGraalJarInJdks)
 
-def packagejar(classpath, outputFile, mainClass):
-    prefix = '' if mx.get_os() != 'windows' else '\\??\\' # long file name hack
+def packagejar(classpath, outputFile, mainClass=None, annotationProcessor=None, stripDebug=False):
+    prefix = '' if mx.get_os() != 'windows' else '\\??\\'  # long file name hack
     print "creating", outputFile
     filecount, totalsize = 0, 0
     with zipfile.ZipFile(outputFile, 'w', zipfile.ZIP_DEFLATED) as zf:
@@ -1453,6 +1461,8 @@
         if mainClass != None:
             manifest += "Main-Class: %s\n\n" % (mainClass)
         zf.writestr("META-INF/MANIFEST.MF", manifest)
+        if annotationProcessor != None:
+            zf.writestr("META-INF/services/javax.annotation.processing.Processor", annotationProcessor)
         for cp in classpath:
             print "+", cp
             if cp.endswith(".jar"):
@@ -1465,7 +1475,7 @@
                 for root, _, files in os.walk(cp):
                     for f in files:
                         fullname = os.path.join(root, f)
-                        arcname = fullname[len(cp)+1:].replace('\\', '/')
+                        arcname = fullname[len(cp) + 1:].replace('\\', '/')
                         if f.endswith(".class"):
                             zf.write(prefix + fullname, arcname)
 
@@ -1473,5 +1483,5 @@
             filecount += 1
             totalsize += zi.file_size
     print "%d files (total size: %.2f kB, jar size: %.2f kB)" % (filecount, totalsize / 1e3, os.path.getsize(outputFile) / 1e3)
-    mx.run([mx.exe_suffix(join(mx.java().jdk, 'bin', 'pack200')), '-r', '-G', outputFile])
+    mx.run([mx.exe_suffix(join(mx.java().jdk, 'bin', 'pack200')), '-r'] + (['-G'] if stripDebug else []) + [outputFile])
     print "repacked jar size: %.2f kB" % (os.path.getsize(outputFile) / 1e3)
--- a/mx/projects	Tue Dec 17 20:22:45 2013 -0800
+++ b/mx/projects	Tue Dec 17 21:26:42 2013 -0800
@@ -1,5 +1,5 @@
 # The format of this file is described in the documentation for my.py.
-
+mxversion=1.0
 suite=graal
 
 library@JDK_TOOLS@path=${JAVA_HOME}/lib/tools.jar
@@ -162,7 +162,7 @@
 # graal.hotspot.hsail
 project@com.oracle.graal.hotspot.hsail@subDir=graal
 project@com.oracle.graal.hotspot.hsail@sourceDirs=src
-project@com.oracle.graal.hotspot.hsail@dependencies=com.oracle.graal.compiler.hsail,com.oracle.graal.hotspot
+project@com.oracle.graal.hotspot.hsail@dependencies=com.oracle.graal.replacements.hsail,com.oracle.graal.hotspot
 project@com.oracle.graal.hotspot.hsail@checkstyle=com.oracle.graal.graph
 project@com.oracle.graal.hotspot.hsail@annotationProcessors=com.oracle.graal.service.processor
 project@com.oracle.graal.hotspot.hsail@javaCompliance=1.7
@@ -311,6 +311,14 @@
 project@com.oracle.graal.replacements.amd64@javaCompliance=1.7
 project@com.oracle.graal.replacements.amd64@workingSets=Graal,Replacements,AMD64
 
+# graal.replacements.hsail
+project@com.oracle.graal.replacements.hsail@subDir=graal
+project@com.oracle.graal.replacements.hsail@sourceDirs=src
+project@com.oracle.graal.replacements.hsail@dependencies=com.oracle.graal.compiler.hsail
+project@com.oracle.graal.replacements.hsail@checkstyle=com.oracle.graal.graph
+project@com.oracle.graal.replacements.hsail@javaCompliance=1.7
+project@com.oracle.graal.replacements.hsail@workingSets=Graal,Replacements,HSAIL
+
 # graal.replacements.test
 project@com.oracle.graal.replacements.test@subDir=graal
 project@com.oracle.graal.replacements.test@sourceDirs=src
--- a/mxtool/mx.py	Tue Dec 17 20:22:45 2013 -0800
+++ b/mxtool/mx.py	Tue Dec 17 21:26:42 2013 -0800
@@ -675,6 +675,7 @@
         self.imports = []
         self.commands = None
         self.primary = primary
+        self.requiredMxVersion = None
         self.name = _suitename(mxDir)  # validated in _load_projects
         if load:
             # load suites bottom up to make sure command overriding works properly
@@ -714,10 +715,17 @@
                     parts = key.split('@')
 
                     if len(parts) == 1:
-                        if parts[0] != 'suite':
+                        if parts[0] == 'suite':
+                            if self.name != value:
+                                abort('suite name in project file does not match ' + _suitename(self.mxDir))
+                        elif parts[0] == 'mxversion':
+                            try:
+                                self.requiredMxVersion = JavaVersion(value)
+                            except AssertionError as ae:
+                                abort('Exception while parsing "mxversion" in project file: ' + str(ae))
+                        else:
                             abort('Single part property must be "suite": ' + key)
-                        if self.name != value:
-                            abort('suite name in project file does not match ' + _suitename(self.mxDir))
+
                         continue
                     if len(parts) != 3:
                         abort('Property name does not have 3 parts separated by "@": ' + key)
@@ -889,6 +897,10 @@
 
     def _post_init(self, opts):
         self._load_projects()
+        if self.requiredMxVersion is None:
+            warn("This suite does not express any required mx version. Consider adding 'mxversion=<version>' to your projects file.")
+        elif self.requiredMxVersion > version:
+            abort("This suite requires mx version " + str(self.requiredMxVersion) + " while your current mx verion is " + str(version) + ". Please update mx.")
         # set the global data structures, checking for conflicts unless _check_global_structures is False
         for p in self.projects:
             existing = _projects.get(p.name)
@@ -1552,7 +1564,7 @@
     def __init__(self, versionString):
         validChar = r'[\x21-\x25\x27-\x29\x2c\x2f-\x5e\x60-\x7f]'
         separator = r'[.\-_]'
-        m = re.match(validChar + '+(' + separator + validChar + '+)*', versionString)
+        m = re.match("^" + validChar + '+(' + separator + validChar + '+)*$', versionString)
         assert m is not None, 'not a recognized version string: ' + versionString
         self.versionString = versionString
         self.parts = [int(f) if f.isdigit() else f for f in re.split(separator, versionString)]
@@ -4516,6 +4528,8 @@
         # no need to show the stack trace when the user presses CTRL-C
         abort(1)
 
+version = JavaVersion("1.0")
+
 if __name__ == '__main__':
     # rename this module as 'mx' so it is not imported twice by the commands.py modules
     sys.modules['mx'] = sys.modules.pop('__main__')
--- a/src/share/vm/compiler/compileBroker.cpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/compiler/compileBroker.cpp	Tue Dec 17 21:26:42 2013 -0800
@@ -2311,7 +2311,7 @@
   }
 }
 
-void CompileBroker::print_times() {
+void CompileBroker::print_times(bool per_compiler, bool aggregate) {
 #ifdef GRAAL
   elapsedTimer standard_compilation;
   elapsedTimer total_compilation;
@@ -2331,7 +2331,7 @@
   for (unsigned int i = 0; i < sizeof(_compilers) / sizeof(AbstractCompiler*); i++) {
     AbstractCompiler* comp = _compilers[i];
     if (comp != NULL) {
-      if (!printedHeader) {
+      if (per_compiler && aggregate && !printedHeader) {
         printedHeader = true;
         tty->cr();
         tty->print_cr("Individual compiler times (for compiled methods only)");
@@ -2352,11 +2352,13 @@
       nmethods_size += stats->_nmethods_size;
       nmethods_code_size += stats->_nmethods_code_size;
 
+      if (per_compiler) {
       tty->print_cr("  %s {speed: %d bytes/s; standard: %6.3f s, %d bytes, %d methods; osr: %6.3f s, %d bytes, %d methods; nmethods_size: %d bytes; nmethods_code_size: %d bytes}",
           comp->name(), stats->bytes_per_second(),
           stats->_standard._time.seconds(), stats->_standard._bytes, stats->_standard._count,
           stats->_osr._time.seconds(), stats->_osr._bytes, stats->_osr._count,
           stats->_nmethods_size, stats->_nmethods_code_size);
+      }
     }
   }
   total_compile_count = osr_compile_count + standard_compile_count;
@@ -2378,6 +2380,9 @@
   int nmethods_code_size = CompileBroker::_sum_nmethod_size;
 #endif
 
+  if (!aggregate) {
+    return;
+  }
   tty->cr();
   tty->print_cr("Accumulated compiler times (for compiled methods only)");
   tty->print_cr("------------------------------------------------");
--- a/src/share/vm/compiler/compileBroker.hpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/compiler/compileBroker.hpp	Tue Dec 17 21:26:42 2013 -0800
@@ -434,7 +434,7 @@
   static void mark_on_stack();
 
   // Print a detailed accounting of compilation time
-  static void print_times();
+  static void print_times(bool per_compiler = true, bool aggregate = true);
 
   // Debugging output for failure
   static void print_last_compile();
--- a/src/share/vm/graal/graalCompiler.cpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/graal/graalCompiler.cpp	Tue Dec 17 21:26:42 2013 -0800
@@ -115,6 +115,12 @@
         // Avoid -Xcomp and -Xbatch problems by turning on interpreter and background compilation for bootstrapping.
         FlagSetting a(UseInterpreter, true);
         FlagSetting b(BackgroundCompilation, true);
+#ifndef PRODUCT
+        // Turn off CompileTheWorld during bootstrap so that a counter overflow event
+        // triggers further compilation (see NonTieredCompPolicy::event()) hence
+        // allowing a complete bootstrap
+        FlagSetting c(CompileTheWorld, false);
+#endif
         VMToCompiler::bootstrap();
       }
 
@@ -274,18 +280,6 @@
   return VMToCompiler::createJavaField(field_holder, name, field_type, offset, flags, false, CHECK_NH);
 }
 
-Handle GraalCompiler::createHotSpotResolvedObjectType(methodHandle method, TRAPS) {
-  KlassHandle klass = method->method_holder();
-  oop java_class = klass->java_mirror();
-  oop graal_mirror = java_lang_Class::graal_mirror(java_class);
-  if (graal_mirror != NULL) {
-    assert(graal_mirror->is_a(HotSpotResolvedObjectType::klass()), "unexpected class...");
-    return graal_mirror;
-  }
-  Handle name = java_lang_String::create_from_symbol(klass->name(), CHECK_NH);
-  return GraalCompiler::createHotSpotResolvedObjectType(klass, name, CHECK_NH);
-}
-
 Handle GraalCompiler::createHotSpotResolvedObjectType(KlassHandle klass, Handle name, TRAPS) {
   oop java_class = klass->java_mirror();
   oop graal_mirror = java_lang_Class::graal_mirror(java_class);
--- a/src/share/vm/graal/graalCompiler.hpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/graal/graalCompiler.hpp	Tue Dec 17 21:26:42 2013 -0800
@@ -78,7 +78,6 @@
   static Handle get_JavaField(int offset, int flags, Symbol* field_name, Handle field_holder, Handle field_type, TRAPS);
 
   static Handle createHotSpotResolvedObjectType(KlassHandle klass, Handle name, TRAPS);
-  static Handle createHotSpotResolvedObjectType(methodHandle method, TRAPS);
 
   void exit();
 
--- a/src/share/vm/graal/graalCompilerToVM.cpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/graal/graalCompilerToVM.cpp	Tue Dec 17 21:26:42 2013 -0800
@@ -171,33 +171,14 @@
   return (jlong) (address) method();
 }
 
-C2V_VMENTRY(jlong, getUniqueConcreteMethod, (JNIEnv *, jobject, jlong metaspace_method, jobject resultHolder))
+C2V_VMENTRY(jlong, findUniqueConcreteMethod, (JNIEnv *, jobject, jlong metaspace_method))
   methodHandle method = asMethod(metaspace_method);
   KlassHandle holder = method->method_holder();
-  if (holder->is_interface()) {
-    // Cannot trust interfaces. Because of:
-    // interface I { void foo(); }
-    // class A { public void foo() {} }
-    // class B extends A implements I { }
-    // class C extends B { public void foo() { } }
-    // class D extends B { }
-    // Would lead to identify C.foo() as the unique concrete method for I.foo() without seeing A.foo().
-    return 0L;
-  }
-  methodHandle ucm;
-  {
-    ResourceMark rm;
-    MutexLocker locker(Compile_lock);
-    ucm = Dependencies::find_unique_concrete_method(holder(), method());
-  }
-
-  if (ucm.is_null()) {
-    return 0L;
-  }
-
-  Handle type = GraalCompiler::createHotSpotResolvedObjectType(ucm(), CHECK_0);
-  objArrayOop(JNIHandles::resolve(resultHolder))->obj_at_put(0, type());
-  return (jlong) (address) ucm();
+  assert(!holder->is_interface(), "should be handled in Java code");
+  ResourceMark rm;
+  MutexLocker locker(Compile_lock);
+  Method* ucm = Dependencies::find_unique_concrete_method(holder(), method());
+  return (jlong) (address) ucm;
 C2V_END
 
 C2V_VMENTRY(jobject, getUniqueImplementor, (JNIEnv *, jobject, jobject interface_type))
@@ -670,6 +651,10 @@
   }
 C2V_END
 
+C2V_VMENTRY(void, printCompilationStatistics, (JNIEnv *jniEnv, jobject, jboolean per_compiler, jboolean aggregate))
+CompileBroker::print_times(per_compiler, aggregate);
+C2V_END
+
 C2V_VMENTRY(void, resetCompilationStatistics, (JNIEnv *jniEnv, jobject))
   CompilerStatistics* stats = GraalCompiler::instance()->stats();
   stats->_standard._time.reset();
@@ -885,7 +870,7 @@
   {CC"initializeBytecode",            CC"("METASPACE_METHOD"[B)[B",                                     FN_PTR(initializeBytecode)},
   {CC"exceptionTableStart",           CC"("METASPACE_METHOD")J",                                        FN_PTR(exceptionTableStart)},
   {CC"hasBalancedMonitors",           CC"("METASPACE_METHOD")Z",                                        FN_PTR(hasBalancedMonitors)},
-  {CC"getUniqueConcreteMethod",       CC"("METASPACE_METHOD"["HS_RESOLVED_TYPE")"METASPACE_METHOD,      FN_PTR(getUniqueConcreteMethod)},
+  {CC"findUniqueConcreteMethod",      CC"("METASPACE_METHOD")"METASPACE_METHOD,                         FN_PTR(findUniqueConcreteMethod)},
   {CC"getUniqueImplementor",          CC"("HS_RESOLVED_TYPE")"RESOLVED_TYPE,                            FN_PTR(getUniqueImplementor)},
   {CC"getStackTraceElement",          CC"("METASPACE_METHOD"I)"STACK_TRACE_ELEMENT,                     FN_PTR(getStackTraceElement)},
   {CC"initializeMethod",              CC"("METASPACE_METHOD HS_RESOLVED_METHOD")V",                     FN_PTR(initializeMethod)},
@@ -908,6 +893,7 @@
   {CC"initializeConfiguration",       CC"("HS_CONFIG")V",                                               FN_PTR(initializeConfiguration)},
   {CC"installCode0",                  CC"("HS_COMPILED_CODE HS_INSTALLED_CODE"[Z)I",                    FN_PTR(installCode0)},
   {CC"notifyCompilationStatistics",   CC"(I"HS_RESOLVED_METHOD"ZIJJ"HS_INSTALLED_CODE")V",              FN_PTR(notifyCompilationStatistics)},
+  {CC"printCompilationStatistics",    CC"(ZZ)V",                                                        FN_PTR(printCompilationStatistics)},
   {CC"resetCompilationStatistics",    CC"()V",                                                          FN_PTR(resetCompilationStatistics)},
   {CC"disassembleCodeBlob",           CC"(J)"STRING,                                                    FN_PTR(disassembleCodeBlob)},
   {CC"executeCompiledMethodVarargs",  CC"(["OBJECT HS_INSTALLED_CODE")"OBJECT,                          FN_PTR(executeCompiledMethodVarargs)},
--- a/src/share/vm/runtime/vframe.cpp	Tue Dec 17 20:22:45 2013 -0800
+++ b/src/share/vm/runtime/vframe.cpp	Tue Dec 17 21:26:42 2013 -0800
@@ -266,7 +266,7 @@
 
   // Get oopmap describing oops and int for current bci
   InterpreterOopMap oop_mask;
-  if (TraceDeoptimization && Verbose) {
+  if ((TraceDeoptimization && Verbose) GRAAL_ONLY( || PrintDeoptimizationDetails)) {
     methodHandle m_h(thread(), method());
     OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask);
   } else {
@@ -333,7 +333,7 @@
 
   InterpreterOopMap oop_mask;
   // Get oopmap describing oops and int for current bci
-  if (TraceDeoptimization && Verbose) {
+  if ((TraceDeoptimization && Verbose) GRAAL_ONLY( || PrintDeoptimizationDetails)) {
     methodHandle m_h(method());
     OopMapCache::compute_one_oop_map(m_h, bci(), &oop_mask);
   } else {