changeset 21349:25bd9e2320de

Merge
author Stefan Anzinger <stefan.anzinger@oracle.com>
date Tue, 12 May 2015 20:56:04 +0200
parents d24e4f349cbc (current diff) a818a6a57ef4 (diff)
children f85738837dae
files graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TestCopyOfVirtualization.java graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java
diffstat 95 files changed, 3747 insertions(+), 2803 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodePosition.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/BytecodePosition.java	Tue May 12 20:56:04 2015 +0200
@@ -109,4 +109,15 @@
     public BytecodePosition getCaller() {
         return caller;
     }
+
+    /*
+     * Adds a caller to the current position returning the new position.
+     */
+    public BytecodePosition addCaller(BytecodePosition link) {
+        if (getCaller() == null) {
+            return new BytecodePosition(link, getMethod(), getBCI());
+        } else {
+            return new BytecodePosition(getCaller().addCaller(link), getMethod(), getBCI());
+        }
+    }
 }
--- a/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeUtil.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.api.code/src/com/oracle/graal/api/code/CodeUtil.java	Tue May 12 20:56:04 2015 +0200
@@ -294,6 +294,9 @@
      */
     public static StringBuilder append(StringBuilder sb, BytecodeFrame frame) {
         MetaUtil.appendLocation(sb.append("at "), frame.getMethod(), frame.getBCI());
+        assert sb.charAt(sb.length() - 1) == ']';
+        sb.deleteCharAt(sb.length() - 1);
+        sb.append(", duringCall: ").append(frame.duringCall).append(", rethrow: ").append(frame.rethrowException).append(']');
         if (frame.values != null && frame.values.length > 0) {
             sb.append(NEW_LINE);
             String table = tabulateValues(frame);
--- a/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaMethod.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.api.meta.test/src/com/oracle/graal/api/meta/test/TestResolvedJavaMethod.java	Tue May 12 20:56:04 2015 +0200
@@ -312,6 +312,25 @@
         }
     }
 
+    @Test
+    public void isJavaLangObjectInitTest() throws NoSuchMethodException {
+        ResolvedJavaMethod method = metaAccess.lookupJavaMethod(Object.class.getConstructor());
+        assertTrue(method.isJavaLangObjectInit());
+        for (Map.Entry<Method, ResolvedJavaMethod> e : methods.entrySet()) {
+            ResolvedJavaMethod m = e.getValue();
+            assertFalse(m.isJavaLangObjectInit());
+        }
+        for (Map.Entry<Constructor<?>, ResolvedJavaMethod> e : constructors.entrySet()) {
+            ResolvedJavaMethod m = e.getValue();
+            Constructor<?> key = e.getKey();
+            if (key.getDeclaringClass() == Object.class && key.getParameters().length == 0) {
+                assertTrue(m.isJavaLangObjectInit());
+            } else {
+                assertFalse(m.isJavaLangObjectInit());
+            }
+        }
+    }
+
     private Method findTestMethod(Method apiMethod) {
         String testName = apiMethod.getName() + "Test";
         for (Method m : getClass().getDeclaredMethods()) {
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/LIRKind.java	Tue May 12 20:56:04 2015 +0200
@@ -133,15 +133,49 @@
      */
     public static LIRKind merge(Value... inputs) {
         assert inputs.length > 0;
+        LIRKind mergeKind = null;
+
         for (Value input : inputs) {
             LIRKind kind = input.getLIRKind();
+
+            assert mergeKind == null || verifyMoveKinds(mergeKind, kind) : String.format("Input kinds do not match %s vs. %s", mergeKind, kind);
+
             if (kind.isDerivedReference()) {
+                /**
+                 * Kind is a derived reference therefore the result can only be also a derived
+                 * reference.
+                 */
                 return kind;
             }
+            if (mergeKind == null) {
+                mergeKind = kind;
+                continue;
+            }
+
+            if (kind.isValue()) {
+                /* Kind is a value. */
+                if (mergeKind.referenceMask != 0) {
+                    /*
+                     * Inputs consists of values and references. Make the result a derived
+                     * reference.
+                     */
+                    return mergeKind.makeDerivedReference();
+                }
+                /* Check that other inputs are also values. */
+            } else {
+                /* Kind is a reference. */
+                if (mergeKind.referenceMask != kind.referenceMask) {
+                    /*
+                     * Reference maps do not match so the result can only be a derived reference.
+                     */
+                    return mergeKind.makeDerivedReference();
+                }
+            }
+
         }
 
         // all inputs are values or references, just return one of them
-        return inputs[0].getLIRKind();
+        return mergeKind;
     }
 
     /**
@@ -277,4 +311,26 @@
         LIRKind other = (LIRKind) obj;
         return platformKind == other.platformKind && referenceMask == other.referenceMask;
     }
+
+    public static boolean verifyMoveKinds(LIRKind dst, LIRKind src) {
+        if (src.equals(dst)) {
+            return true;
+        }
+        /*
+         * TODO(je,rs) What we actually want is toStackKind(src.getPlatformKind()).equals(
+         * dst.getPlatformKind()) but due to the handling of sub-integer at the current point
+         * (phi-)moves from e.g. integer to short can happen. Therefore we compare stack kinds.
+         */
+        if (toStackKind(src.getPlatformKind()).equals(toStackKind(dst.getPlatformKind()))) {
+            return !src.isDerivedReference() || dst.isDerivedReference();
+        }
+        return false;
+    }
+
+    private static PlatformKind toStackKind(PlatformKind platformKind) {
+        if (platformKind instanceof Kind) {
+            return ((Kind) platformKind).getStackKind();
+        }
+        return platformKind;
+    }
 }
--- a/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.api.meta/src/com/oracle/graal/api/meta/ResolvedJavaMethod.java	Tue May 12 20:56:04 2015 +0200
@@ -288,4 +288,11 @@
     default boolean hasReceiver() {
         return !isStatic();
     }
+
+    /**
+     * Determines if this method is {@link java.lang.Object#Object()}.
+     */
+    default boolean isJavaLangObjectInit() {
+        return getDeclaringClass().isJavaLangObject() && getName().equals("<init>");
+    }
 }
--- a/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler.amd64/src/com/oracle/graal/compiler/amd64/AMD64LIRGenerator.java	Tue May 12 20:56:04 2015 +0200
@@ -81,6 +81,7 @@
 
     private static final RegisterValue RCX_I = AMD64.rcx.asValue(LIRKind.value(Kind.Int));
     private AMD64SpillMoveFactory moveFactory;
+    private Map<PlatformKind.Key, RegisterBackupPair> categorized;
 
     private static class RegisterBackupPair {
         public final Register register;
@@ -93,7 +94,6 @@
     }
 
     private class AMD64SpillMoveFactory implements LIRGeneratorTool.SpillMoveFactory {
-        private Map<PlatformKind.Key, RegisterBackupPair> categorized;
 
         @Override
         public LIRInstruction createMove(AllocatableValue result, Value input) {
@@ -102,34 +102,9 @@
 
         @Override
         public LIRInstruction createStackMove(AllocatableValue result, Value input) {
-            RegisterBackupPair backup = getScratchRegister(input.getPlatformKind());
-            return new AMD64StackMove(result, input, backup.register, backup.backupSlot);
+            return AMD64LIRGenerator.this.createStackMove(result, input);
         }
 
-        private RegisterBackupPair getScratchRegister(PlatformKind kind) {
-            PlatformKind.Key key = kind.getKey();
-            if (categorized == null) {
-                categorized = new HashMap<>();
-            } else if (categorized.containsKey(key)) {
-                return categorized.get(key);
-            }
-
-            FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder();
-            RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig();
-
-            Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters());
-            assert availableRegister != null && availableRegister.length > 1;
-            Register scratchRegister = availableRegister[0];
-
-            Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch;
-            LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory()));
-            VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind);
-
-            RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot);
-            categorized.put(key, value);
-
-            return value;
-        }
     }
 
     public AMD64LIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) {
@@ -165,6 +140,42 @@
         }
     }
 
+    protected LIRInstruction createStackMove(AllocatableValue result, Value input) {
+        RegisterBackupPair backup = getScratchRegister(input.getPlatformKind());
+        Register scratchRegister = backup.register;
+        StackSlotValue backupSlot = backup.backupSlot;
+        return createStackMove(result, input, scratchRegister, backupSlot);
+    }
+
+    protected LIRInstruction createStackMove(AllocatableValue result, Value input, Register scratchRegister, StackSlotValue backupSlot) {
+        return new AMD64StackMove(result, input, scratchRegister, backupSlot);
+    }
+
+    protected RegisterBackupPair getScratchRegister(PlatformKind kind) {
+        PlatformKind.Key key = kind.getKey();
+        if (categorized == null) {
+            categorized = new HashMap<>();
+        } else if (categorized.containsKey(key)) {
+            return categorized.get(key);
+        }
+
+        FrameMapBuilder frameMapBuilder = getResult().getFrameMapBuilder();
+        RegisterConfig registerConfig = frameMapBuilder.getRegisterConfig();
+
+        Register[] availableRegister = registerConfig.filterAllocatableRegisters(kind, registerConfig.getAllocatableRegisters());
+        assert availableRegister != null && availableRegister.length > 1;
+        Register scratchRegister = availableRegister[0];
+
+        Architecture arch = frameMapBuilder.getCodeCache().getTarget().arch;
+        LIRKind largestKind = LIRKind.value(arch.getLargestStorableKind(scratchRegister.getRegisterCategory()));
+        VirtualStackSlot backupSlot = frameMapBuilder.allocateSpillSlot(largestKind);
+
+        RegisterBackupPair value = new RegisterBackupPair(scratchRegister, backupSlot);
+        categorized.put(key, value);
+
+        return value;
+    }
+
     @Override
     public void emitMove(AllocatableValue dst, Value src) {
         append(createMove(dst, src));
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Tue May 12 20:56:04 2015 +0200
@@ -74,7 +74,7 @@
 
         @Override
         public LIRInstruction createStackMove(AllocatableValue result, Value input) {
-            return new SPARCStackMove(result, input);
+            return SPARCLIRGenerator.this.createStackMove(result, input);
         }
     }
 
@@ -117,6 +117,10 @@
         }
     }
 
+    protected LIRInstruction createStackMove(AllocatableValue result, Value input) {
+        return new SPARCStackMove(result, input);
+    }
+
     @Override
     public void emitMove(AllocatableValue dst, Value src) {
         append(createMove(dst, src));
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/CopyOfVirtualizationTest.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,152 @@
+/*
+ * Copyright (c) 2015, 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.util.*;
+
+import org.junit.*;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+
+public class CopyOfVirtualizationTest extends GraalCompilerTest {
+
+    @Override
+    protected boolean checkMidTierGraph(StructuredGraph graph) {
+        assertTrue(graph.getNodes().filter(node -> node instanceof NewArrayNode).count() == 0, "shouldn't require allocation in %s", graph);
+        return super.checkMidTierGraph(graph);
+    }
+
+    public byte byteCopyOfVirtualization(int index) {
+        byte[] array = new byte[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public short shortCopyOfVirtualization(int index) {
+        short[] array = new short[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public char charCopyOfVirtualization(int index) {
+        char[] array = new char[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public int intCopyOfVirtualization(int index) {
+        int[] array = new int[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public long longCopyOfVirtualization(int index) {
+        long[] array = new long[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public float floatCopyOfVirtualization(int index) {
+        float[] array = new float[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public double doubleCopyOfVirtualization(int index) {
+        double[] array = new double[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    public Object objectCopyOfVirtualization(int index) {
+        Object[] array = new Object[]{1, 2, 3, 4};
+        return Arrays.copyOf(array, array.length)[index];
+    }
+
+    // @Test
+    public void testCopyOfVirtualization() {
+        test("byteCopyOfVirtualization", 3);
+        test("shortCopyOfVirtualization", 3);
+        test("charCopyOfVirtualization", 3);
+        test("intCopyOfVirtualization", 3);
+        test("longCopyOfVirtualization", 3);
+        test("floatCopyOfVirtualization", 3);
+        test("doubleCopyOfVirtualization", 3);
+        test("objectCopyOfVirtualization", 3);
+    }
+
+    static final byte[] byteArray = new byte[]{1, 2, 3, 4};
+
+    public byte byteCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(byteArray, byteArray.length)[3];
+    }
+
+    static final short[] shortArray = new short[]{1, 2, 3, 4};
+
+    public short shortCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(shortArray, shortArray.length)[3];
+    }
+
+    static final char[] charArray = new char[]{1, 2, 3, 4};
+
+    public char charCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(charArray, charArray.length)[3];
+    }
+
+    static final int[] intArray = new int[]{1, 2, 3, 4};
+
+    public int intCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(intArray, intArray.length)[3];
+    }
+
+    static final long[] longArray = new long[]{1, 2, 3, 4};
+
+    public long longCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(longArray, longArray.length)[3];
+    }
+
+    static final float[] floatArray = new float[]{1, 2, 3, 4};
+
+    public float floatCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(floatArray, floatArray.length)[3];
+    }
+
+    static final double[] doubleArray = new double[]{1, 2, 3, 4};
+
+    public double doubleCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(doubleArray, doubleArray.length)[3];
+    }
+
+    static final Object[] objectArray = new Object[]{1, 2, 3, 4};
+
+    public Object objectCopyOfVirtualizableAllocation() {
+        return Arrays.copyOf(objectArray, objectArray.length)[3];
+    }
+
+    @Test
+    public void testCopyOfVirtualizableAllocation() {
+        test("byteCopyOfVirtualizableAllocation");
+        test("shortCopyOfVirtualizableAllocation");
+        test("charCopyOfVirtualizableAllocation");
+        test("intCopyOfVirtualizableAllocation");
+        test("longCopyOfVirtualizableAllocation");
+        test("floatCopyOfVirtualizableAllocation");
+        test("doubleCopyOfVirtualizableAllocation");
+        test("objectCopyOfVirtualizableAllocation");
+    }
+}
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/GraalCompilerTest.java	Tue May 12 20:56:04 2015 +0200
@@ -321,7 +321,7 @@
             result.append("\n");
             for (Node node : schedule.getBlockToNodesMap().get(block)) {
                 if (node instanceof ValueNode && node.isAlive()) {
-                    if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode)) {
+                    if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof InfopointNode)) {
                         if (node instanceof ConstantNode) {
                             String name = checkConstants ? node.toString(Verbosity.Name) : node.getClass().getSimpleName();
                             String str = name + (excludeVirtual ? "\n" : "    (" + node.getUsageCount() + ")\n");
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/TestCopyOfVirtualization.java	Tue May 12 20:55:48 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,152 +0,0 @@
-/*
- * Copyright (c) 2015, 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.util.*;
-
-import org.junit.*;
-
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-
-public class TestCopyOfVirtualization extends GraalCompilerTest {
-
-    @Override
-    protected boolean checkMidTierGraph(StructuredGraph graph) {
-        assertTrue(graph.getNodes().filter(node -> node instanceof NewArrayNode).count() == 0, "shouldn't require allocation in %s", graph);
-        return super.checkMidTierGraph(graph);
-    }
-
-    public byte byteCopyOfVirtualization(int index) {
-        byte[] array = new byte[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public short shortCopyOfVirtualization(int index) {
-        short[] array = new short[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public char charCopyOfVirtualization(int index) {
-        char[] array = new char[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public int intCopyOfVirtualization(int index) {
-        int[] array = new int[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public long longCopyOfVirtualization(int index) {
-        long[] array = new long[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public float floatCopyOfVirtualization(int index) {
-        float[] array = new float[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public double doubleCopyOfVirtualization(int index) {
-        double[] array = new double[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    public Object objectCopyOfVirtualization(int index) {
-        Object[] array = new Object[]{1, 2, 3, 4};
-        return Arrays.copyOf(array, array.length)[index];
-    }
-
-    // @Test
-    public void testCopyOfVirtualization() {
-        test("byteCopyOfVirtualization", 3);
-        test("shortCopyOfVirtualization", 3);
-        test("charCopyOfVirtualization", 3);
-        test("intCopyOfVirtualization", 3);
-        test("longCopyOfVirtualization", 3);
-        test("floatCopyOfVirtualization", 3);
-        test("doubleCopyOfVirtualization", 3);
-        test("objectCopyOfVirtualization", 3);
-    }
-
-    static final byte[] byteArray = new byte[]{1, 2, 3, 4};
-
-    public byte byteCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(byteArray, byteArray.length)[3];
-    }
-
-    static final short[] shortArray = new short[]{1, 2, 3, 4};
-
-    public short shortCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(shortArray, shortArray.length)[3];
-    }
-
-    static final char[] charArray = new char[]{1, 2, 3, 4};
-
-    public char charCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(charArray, charArray.length)[3];
-    }
-
-    static final int[] intArray = new int[]{1, 2, 3, 4};
-
-    public int intCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(intArray, intArray.length)[3];
-    }
-
-    static final long[] longArray = new long[]{1, 2, 3, 4};
-
-    public long longCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(longArray, longArray.length)[3];
-    }
-
-    static final float[] floatArray = new float[]{1, 2, 3, 4};
-
-    public float floatCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(floatArray, floatArray.length)[3];
-    }
-
-    static final double[] doubleArray = new double[]{1, 2, 3, 4};
-
-    public double doubleCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(doubleArray, doubleArray.length)[3];
-    }
-
-    static final Object[] objectArray = new Object[]{1, 2, 3, 4};
-
-    public Object objectCopyOfVirtualizableAllocation() {
-        return Arrays.copyOf(objectArray, objectArray.length)[3];
-    }
-
-    @Test
-    public void testCopyOfVirtualizableAllocation() {
-        test("byteCopyOfVirtualizableAllocation");
-        test("shortCopyOfVirtualizableAllocation");
-        test("charCopyOfVirtualizableAllocation");
-        test("intCopyOfVirtualizableAllocation");
-        test("longCopyOfVirtualizableAllocation");
-        test("floatCopyOfVirtualizableAllocation");
-        test("doubleCopyOfVirtualizableAllocation");
-        test("objectCopyOfVirtualizableAllocation");
-    }
-}
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/PartialEscapeAnalysisTest.java	Tue May 12 20:56:04 2015 +0200
@@ -23,12 +23,14 @@
 package com.oracle.graal.compiler.test.ea;
 
 import java.lang.ref.*;
+
 import org.junit.*;
 
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
+import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.virtual.*;
 import com.oracle.graal.phases.common.*;
@@ -191,6 +193,23 @@
         assertTrue(graph.getNodes().filter(ReturnNode.class).first().result() == graph.getParameter(0));
     }
 
+    public static int testBoxLoopSnippet(int n) {
+        Integer sum = 0;
+        for (Integer i = 0; i < n; i++) {
+            if (sum == null) {
+                sum = null;
+            } else {
+                sum += i;
+            }
+        }
+        return sum;
+    }
+
+    @Test
+    public void testBoxLoop() {
+        testPartialEscapeAnalysis("testBoxLoopSnippet", 0, 0, BoxNode.class, UnboxNode.class);
+    }
+
     @SafeVarargs
     protected final void testPartialEscapeAnalysis(String snippet, double expectedProbability, int expectedCount, Class<? extends Node>... invalidNodeClasses) {
         prepareGraph(snippet, false);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalCompiler.java	Tue May 12 20:56:04 2015 +0200
@@ -223,7 +223,7 @@
 
     public static <T extends CompilationResult> void emitBackEnd(StructuredGraph graph, Object stub, CallingConvention cc, ResolvedJavaMethod installedCodeOwner, Backend backend,
                     TargetDescription target, T compilationResult, CompilationResultBuilderFactory factory, SchedulePhase schedule, RegisterConfig registerConfig, LIRSuites lirSuites) {
-        try (Scope s = Debug.scope("BackEnd"); DebugCloseable a = BackEnd.start()) {
+        try (Scope s = Debug.scope("BackEnd", schedule); DebugCloseable a = BackEnd.start()) {
             // Repeatedly run the LIR code generation pass to improve statistical profiling results.
             for (int i = 0; i < EmitLIRRepeatCount.getValue(); i++) {
                 SchedulePhase dummySchedule = new SchedulePhase();
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Tue May 12 20:56:04 2015 +0200
@@ -212,7 +212,7 @@
     private static boolean verifyPHIKind(LIRKind derivedKind, LIRKind phiKind) {
         assert derivedKind.getPlatformKind() != Kind.Object || !derivedKind.isDerivedReference();
         PlatformKind phiPlatformKind = phiKind.getPlatformKind();
-        assert derivedKind.getPlatformKind().equals(phiPlatformKind instanceof Kind ? ((Kind) phiPlatformKind).getStackKind() : phiPlatformKind);
+        assert derivedKind.equals(phiKind) || derivedKind.getPlatformKind().equals(phiPlatformKind instanceof Kind ? ((Kind) phiPlatformKind).getStackKind() : phiPlatformKind);
         return true;
     }
 
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderConfiguration.java	Tue May 12 20:56:04 2015 +0200
@@ -229,6 +229,10 @@
         return debugInfoMode.ordinal() >= DebugInfoMode.Full.ordinal();
     }
 
+    public boolean insertSimpleDebugInfo() {
+        return debugInfoMode == DebugInfoMode.Simple;
+    }
+
     public boolean doLivenessAnalysis() {
         return doLivenessAnalysis;
     }
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/GraphBuilderContext.java	Tue May 12 20:56:04 2015 +0200
@@ -41,38 +41,6 @@
 public interface GraphBuilderContext {
 
     /**
-     * Information about a snippet or method substitution currently being processed by the graph
-     * builder. When in the scope of a replacement, the graph builder does not check the value kinds
-     * flowing through the JVM state since replacements can employ non-Java kinds to represent
-     * values such as raw machine words and pointers.
-     */
-    public interface Replacement {
-
-        /**
-         * Gets the method being replaced.
-         */
-        ResolvedJavaMethod getOriginalMethod();
-
-        /**
-         * Gets the replacement method.
-         */
-        ResolvedJavaMethod getReplacementMethod();
-
-        /**
-         * Determines if this replacement is being inlined as a compiler intrinsic. A compiler
-         * intrinsic is atomic with respect to deoptimization. Deoptimization within a compiler
-         * intrinsic will restart the interpreter at the intrinsified call.
-         */
-        boolean isIntrinsic();
-
-        /**
-         * Determines if a call within the compilation scope of this replacement represents a call
-         * to the {@linkplain #getOriginalMethod() original} method.
-         */
-        boolean isCallToOriginal(ResolvedJavaMethod method);
-    }
-
-    /**
      * Raw operation for adding a node to the graph when neither {@link #add},
      * {@link #addPush(ValueNode)} nor {@link #addPush(Kind, ValueNode)} can be used.
      *
@@ -115,8 +83,8 @@
         T equivalentValue = append(value);
         if (equivalentValue instanceof StateSplit) {
             StateSplit stateSplit = (StateSplit) equivalentValue;
-            if (stateSplit.stateAfter() == null) {
-                stateSplit.setStateAfter(createStateAfter());
+            if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) {
+                setStateAfter(stateSplit);
             }
         }
         return equivalentValue;
@@ -149,8 +117,8 @@
         push(kind.getStackKind(), equivalentValue);
         if (equivalentValue instanceof StateSplit) {
             StateSplit stateSplit = (StateSplit) equivalentValue;
-            if (stateSplit.stateAfter() == null) {
-                stateSplit.setStateAfter(createStateAfter());
+            if (stateSplit.stateAfter() == null && stateSplit.hasSideEffect()) {
+                setStateAfter(stateSplit);
             }
         }
         return equivalentValue;
@@ -194,9 +162,12 @@
 
     /**
      * Creates a snap shot of the current frame state with the BCI of the instruction after the one
-     * currently being parsed.
+     * currently being parsed and assigns it to a given {@linkplain StateSplit#hasSideEffect() side
+     * effect} node.
+     *
+     * @param sideEffect a side effect node just appended to the graph
      */
-    FrameState createStateAfter();
+    void setStateAfter(StateSplit sideEffect);
 
     /**
      * Gets the parsing context for the method that inlines the method being parsed by this context.
@@ -204,6 +175,18 @@
     GraphBuilderContext getParent();
 
     /**
+     * Gets the first ancestor parsing context that is not parsing a
+     * {@linkplain #parsingIntrinsic() intrinsic}.
+     */
+    default GraphBuilderContext getNonReplacementAncestor() {
+        GraphBuilderContext ancestor = getParent();
+        while (ancestor != null && ancestor.parsingIntrinsic()) {
+            ancestor = ancestor.getParent();
+        }
+        return ancestor;
+    }
+
+    /**
      * Gets the method currently being parsed.
      */
     ResolvedJavaMethod getMethod();
@@ -235,15 +218,15 @@
     /**
      * Determines if the current parsing context is a snippet or method substitution.
      */
-    default boolean parsingReplacement() {
-        return getReplacement() != null;
+    default boolean parsingIntrinsic() {
+        return getIntrinsic() != null;
     }
 
     /**
-     * Gets the replacement of the current parsing context or {@code null} if not
-     * {@link #parsingReplacement() parsing a replacement}.
+     * Gets the intrinsic of the current parsing context or {@code null} if not
+     * {@link #parsingIntrinsic() parsing an intrinsic}.
      */
-    Replacement getReplacement();
+    IntrinsicContext getIntrinsic();
 
     BailoutException bailout(String string);
 
@@ -268,5 +251,4 @@
         }
         return value;
     }
-
 }
--- a/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/InlineInvokePlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -26,8 +26,9 @@
 import com.oracle.graal.nodes.*;
 
 /**
- * Plugin for specifying what is inlined during graph parsing or for post-processing non-inlined
- * invocations that result in {@link Invoke} nodes.
+ * Plugin for specifying what is inlined during graph parsing. This plugin is also
+ * {@linkplain #notifyOfNoninlinedInvoke notified} of non-inlined invocations (i.e., those for which
+ * an {@link Invoke} node is created).
  */
 public interface InlineInvokePlugin extends GraphBuilderPlugin {
 
@@ -39,24 +40,14 @@
         public final ResolvedJavaMethod methodToInline;
 
         /**
-         * Specifies if {@link #methodToInline} is to be considered a
-         * {@linkplain GraphBuilderContext.Replacement replacement} for the {@code method} passed to
-         * {@link InlineInvokePlugin#getInlineInfo}.
-         */
-        public final boolean isReplacement;
-
-        /**
-         * Specifies if {@link #methodToInline} is an intrinsic for the original method. If so, any
-         * {@link StateSplit} node created in the (recursive) inlining scope will be given a frame
-         * state that restarts the interpreter just before the intrinsified invocation.
+         * Specifies if {@link #methodToInline} is an intrinsic for the original method (i.e., the
+         * {@code method} passed to {@link InlineInvokePlugin#getInlineInfo}).
          */
         public final boolean isIntrinsic;
 
-        public InlineInfo(ResolvedJavaMethod methodToInline, boolean isReplacement, boolean isIntrinsic) {
+        public InlineInfo(ResolvedJavaMethod methodToInline, boolean isIntrinsic) {
             this.methodToInline = methodToInline;
             this.isIntrinsic = isIntrinsic;
-            this.isReplacement = isReplacement;
-            assert !isIntrinsic || isReplacement : "cannot be an intrinsic without also being a replacement";
         }
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.graphbuilderconf/src/com/oracle/graal/graphbuilderconf/IntrinsicContext.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2015, 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.graphbuilderconf;
+
+import static com.oracle.graal.api.code.BytecodeFrame.*;
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.nodes.*;
+
+/**
+ * An intrinsic is a substitute implementation of a Java method (or a bytecode in the case of
+ * snippets) that is itself implemented in Java. This interface provides information about the
+ * intrinsic currently being processed by the graph builder.
+ *
+ * When in the scope of an intrinsic, the graph builder does not check the value kinds flowing
+ * through the JVM state since intrinsics can employ non-Java kinds to represent values such as raw
+ * machine words and pointers.
+ */
+public class IntrinsicContext {
+
+    /**
+     * Gets the method being intrinsified.
+     */
+    final ResolvedJavaMethod method;
+
+    /**
+     * Gets the method providing the intrinsic implementation.
+     */
+    final ResolvedJavaMethod intrinsic;
+
+    public ResolvedJavaMethod getOriginalMethod() {
+        return method;
+    }
+
+    public ResolvedJavaMethod getIntrinsicMethod() {
+        return intrinsic;
+    }
+
+    /**
+     * Determines if a call within the compilation scope of this intrinsic represents a call to the
+     * {@linkplain #getOriginalMethod() original} method. This denotes the path where a partial
+     * intrinsification falls back to the original method.
+     */
+    public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) {
+        return method.equals(targetMethod) || intrinsic.equals(targetMethod);
+    }
+
+    final CompilationContext compilationContext;
+
+    public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod intrinsic, CompilationContext compilationContext) {
+        this.method = method;
+        this.intrinsic = intrinsic;
+        this.compilationContext = compilationContext;
+        assert !isCompilationRoot() || method.hasBytecodes() : "Cannot root compile intrinsic for native or abstract method " + method.format("%H.%n(%p)");
+    }
+
+    public boolean isPostParseInlined() {
+        return compilationContext.equals(INLINE_AFTER_PARSING);
+    }
+
+    public boolean isCompilationRoot() {
+        return compilationContext.equals(ROOT_COMPILATION);
+    }
+
+    /**
+     * Denotes the compilation context in which an intrinsic is being parsed.
+     */
+    public enum CompilationContext {
+        /**
+         * An intrinsic is being processed when parsing an invoke bytecode that calls the
+         * intrinsified method.
+         */
+        INLINE_DURING_PARSING,
+
+        /**
+         * An intrinsic is being processed when inlining an {@link Invoke} in an existing graph.
+         */
+        INLINE_AFTER_PARSING,
+
+        /**
+         * An intrinsic is the root of compilation.
+         */
+        ROOT_COMPILATION
+    }
+
+    /**
+     * Models the state of a graph in terms of {@link StateSplit#hasSideEffect() side effects} that
+     * are control flow predecessors of the current point in a graph.
+     */
+    public interface SideEffectsState {
+
+        /**
+         * Determines if the current program point is preceded by one or more side effects.
+         */
+        boolean isAfterSideEffect();
+
+        /**
+         * Gets the side effects preceding the current program point.
+         */
+        Iterable<StateSplit> sideEffects();
+
+        /**
+         * Records a side effect for the current program point.
+         */
+        void addSideEffect(StateSplit sideEffect);
+    }
+
+    public FrameState createFrameState(StructuredGraph graph, SideEffectsState sideEffects, StateSplit forStateSplit) {
+        assert forStateSplit != graph.start();
+        if (forStateSplit.hasSideEffect()) {
+            if (sideEffects.isAfterSideEffect()) {
+                // Only the last side effect on any execution path in a replacement
+                // can inherit the stateAfter of the replaced node
+                FrameState invalid = graph.add(new FrameState(INVALID_FRAMESTATE_BCI));
+                for (StateSplit lastSideEffect : sideEffects.sideEffects()) {
+                    lastSideEffect.setStateAfter(invalid);
+                }
+            }
+            sideEffects.addSideEffect(forStateSplit);
+            return graph.add(new FrameState(AFTER_BCI));
+        } else {
+            if (forStateSplit instanceof AbstractMergeNode) {
+                // Merge nodes always need a frame state
+                if (sideEffects.isAfterSideEffect()) {
+                    // A merge after one or more side effects
+                    return graph.add(new FrameState(AFTER_BCI));
+                } else {
+                    // A merge before any side effects
+                    return graph.add(new FrameState(BEFORE_BCI));
+                }
+            } else {
+                // Other non-side-effects do not need a state
+                return null;
+            }
+        }
+    }
+
+    @Override
+    public String toString() {
+        return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", intrinsic: " + intrinsic.format("%H.%n(%p)") + ", context: " + compilationContext + "}";
+    }
+}
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/ArrayCopyIntrinsificationTest.java	Tue May 12 20:56:04 2015 +0200
@@ -31,6 +31,7 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.test.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.replacements.arraycopy.*;
 import com.oracle.graal.nodes.*;
 
 /**
@@ -52,9 +53,14 @@
                         Assert.assertTrue(invoke.callTarget() instanceof DirectCallTargetNode);
                         LoweredCallTargetNode directCall = (LoweredCallTargetNode) invoke.callTarget();
                         JavaMethod callee = directCall.targetMethod();
-                        Assert.assertTrue(callee.getName().equals("<init>"));
-                        Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) ||
-                                        getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass()));
+                        if (callee.getDeclaringClass().equals(getMetaAccess().lookupJavaType(System.class)) && callee.getName().equals("arraycopy")) {
+                            // A partial snippet (e.g., ArrayCopySnippets.checkcastArraycopy) may
+                            // call the original arraycopy method
+                        } else {
+                            Assert.assertTrue(callee.toString(), callee.getName().equals("<init>"));
+                            Assert.assertTrue(getMetaAccess().lookupJavaType(ArrayIndexOutOfBoundsException.class).equals(callee.getDeclaringClass()) ||
+                                            getMetaAccess().lookupJavaType(NullPointerException.class).equals(callee.getDeclaringClass()));
+                        }
                     }
                 }
             } else {
@@ -145,25 +151,28 @@
 
     @Test
     public void testObject() {
-        mustIntrinsify = false; // a generic call to arraycopy will not be intrinsified
-
         Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
         testHelper("objectArraycopy", src);
+    }
 
-        mustIntrinsify = true;
+    /**
+     * Tests {@link ArrayCopySnippets#checkcastArraycopySnippet}.
+     */
+    @Test
+    public void testArrayStoreException() {
+        Object[] src = {"one", "two", "three", new ArrayList<>(), new HashMap<>()};
+        Object[] dst = new CharSequence[src.length];
+        // Will throw ArrayStoreException for 4th element
+        test("objectArraycopy", src, 0, dst, 0, src.length);
     }
 
     @Test
     public void testDisjointObject() {
-        mustIntrinsify = false; // a generic call to arraycopy will not be intrinsified
-
         Integer[] src1 = {1, 2, 3, 4};
         test("objectArraycopy", src1, 0, src1, 1, src1.length - 1);
 
         Integer[] src2 = {1, 2, 3, 4};
         test("objectArraycopy", src2, 1, src2, 0, src2.length - 1);
-
-        mustIntrinsify = true;
     }
 
     @Test
--- a/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot.test/src/com/oracle/graal/hotspot/test/HotSpotCryptoSubstitutionTest.java	Tue May 12 20:56:04 2015 +0200
@@ -22,7 +22,7 @@
  */
 package com.oracle.graal.hotspot.test;
 
-import static com.oracle.graal.java.IntrinsicContext.*;
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
 
 import java.io.*;
 import java.lang.reflect.*;
@@ -41,7 +41,7 @@
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.java.*;
 import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.StructuredGraph.*;
+import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
 import com.oracle.graal.phases.*;
 
 /**
@@ -132,7 +132,7 @@
                     StructuredGraph graph = new StructuredGraph(substMethod, AllowAssumptions.YES);
                     Plugins plugins = new Plugins(((HotSpotProviders) getProviders()).getGraphBuilderPlugins());
                     GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
-                    IntrinsicContext initialReplacementContext = new IntrinsicContext(installedCodeOwner, substMethod, null, ROOT_COMPILATION_BCI);
+                    IntrinsicContext initialReplacementContext = new IntrinsicContext(installedCodeOwner, substMethod, ROOT_COMPILATION);
                     new GraphBuilderPhase.Instance(getMetaAccess(), getProviders().getStampProvider(), getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(graph);
                     Assert.assertNotNull(getCode(installedCodeOwner, graph, true));
                     atLeastOneCompiled = true;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/CompileTheWorld.java	Tue May 12 20:56:04 2015 +0200
@@ -305,7 +305,7 @@
                 }
                 if (excludeMethodFilters != null && excludeMethodFilters.length > 0) {
                     String exclude = Arrays.asList(excludeMethodFilters).stream().map(MethodFilter::toString).collect(Collectors.joining(", "));
-                    println("CompileTheWorld : Excluding all methods matching one of the follwing filters: " + exclude);
+                    println("CompileTheWorld : Excluding all methods matching one of the following filters: " + exclude);
                 }
                 println();
 
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantReflectionProvider.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantReflectionProvider.java	Tue May 12 20:56:04 2015 +0200
@@ -35,7 +35,6 @@
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.options.*;
 import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing;
 import com.oracle.graal.replacements.SnippetTemplate.Arguments;
 
 /**
@@ -314,7 +313,7 @@
         ResolvedJavaMethod initMethod = null;
         try {
             Class<?> rjm = ResolvedJavaMethod.class;
-            makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, Object[].class, rjm, FrameStateProcessing.class));
+            makeGraphMethod = metaAccess.lookupJavaMethod(ReplacementsImpl.class.getDeclaredMethod("makeGraph", rjm, Object[].class, rjm));
             initMethod = metaAccess.lookupJavaMethod(SnippetTemplate.AbstractTemplates.class.getDeclaredMethod("template", Arguments.class));
         } catch (NoSuchMethodException | SecurityException e) {
             throw new GraalInternalError(e);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadFieldPlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadFieldPlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -41,7 +41,7 @@
     static final ThreadLocal<Boolean> FieldReadEnabledInImmutableCode = new ThreadLocal<>();
 
     public boolean apply(GraphBuilderContext b, ValueNode receiver, ResolvedJavaField field) {
-        if (!ImmutableCode.getValue() || b.parsingReplacement()) {
+        if (!ImmutableCode.getValue() || b.parsingIntrinsic()) {
             if (receiver.isConstant()) {
                 JavaConstant asJavaConstant = receiver.asJavaConstant();
                 return tryReadField(b, field, asJavaConstant);
@@ -64,10 +64,10 @@
     }
 
     public boolean apply(GraphBuilderContext b, ResolvedJavaField staticField) {
-        if (!ImmutableCode.getValue() || b.parsingReplacement()) {
+        if (!ImmutableCode.getValue() || b.parsingIntrinsic()) {
             // Javac does not allow use of "$assertionsDisabled" for a field name but
             // Eclipse does in which case a suffix is added to the generated field.
-            if (b.parsingReplacement() && staticField.isSynthetic() && staticField.getName().startsWith("$assertionsDisabled")) {
+            if (b.parsingIntrinsic() && staticField.isSynthetic() && staticField.getName().startsWith("$assertionsDisabled")) {
                 // For methods called indirectly from intrinsics, we (silently) disable
                 // assertions so that the parser won't see calls to the AssertionError
                 // constructor (all Invokes must be removed from intrinsics - see
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadIndexedPlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotLoadIndexedPlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -40,7 +40,7 @@
     }
 
     public boolean apply(GraphBuilderContext b, ValueNode array, ValueNode index, Kind elementKind) {
-        if (b.parsingReplacement()) {
+        if (b.parsingIntrinsic()) {
             ResolvedJavaType arrayType = StampTool.typeOrNull(array);
             /*
              * There are cases where the array does not have a known type yet, i.e., the type is
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotParameterPlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotParameterPlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -38,7 +38,7 @@
     }
 
     public FloatingNode interceptParameter(GraphBuilderContext b, int index, Stamp stamp) {
-        if (b.parsingReplacement()) {
+        if (b.parsingIntrinsic()) {
             ResolvedJavaType type = StampTool.typeOrNull(stamp);
             if (wordTypes.isWord(type)) {
                 return new ParameterNode(index, wordTypes.getWordStamp(type));
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotSuitesProvider.java	Tue May 12 20:56:04 2015 +0200
@@ -119,7 +119,7 @@
      * for equality.
      */
     private boolean appendGraphEncoderTest(PhaseSuite<HighTierContext> suite) {
-        suite.appendPhase(new BasePhase<HighTierContext>() {
+        suite.appendPhase(new BasePhase<HighTierContext>("VerifyEncodingDecoding") {
             @Override
             protected void run(StructuredGraph graph, HighTierContext context) {
                 EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, runtime.getTarget().arch);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotClassSubstitutions.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotClassSubstitutions.java	Tue May 12 20:56:04 2015 +0200
@@ -48,6 +48,7 @@
     public static boolean isInterface(final Class<?> thisObj) {
         KlassPointer klass = ClassGetHubNode.readClass(thisObj);
         if (klass.isNull()) {
+            // Class for primitive type
             return false;
         } else {
             int accessFlags = klass.readInt(klassAccessFlagsOffset(), KLASS_ACCESS_FLAGS_LOCATION);
@@ -58,6 +59,7 @@
     public static boolean isArray(final Class<?> thisObj) {
         KlassPointer klass = ClassGetHubNode.readClass(thisObj);
         if (klass.isNull()) {
+            // Class for primitive type
             return false;
         } else {
             return klassIsArray(klass);
@@ -85,6 +87,8 @@
                     }
                 }
             }
+        } else {
+            // Class for primitive type
         }
         return null;
     }
@@ -99,6 +103,8 @@
             if (klassIsArray(klass)) {
                 return PiNode.asNonNullClass(klass.readObject(arrayKlassComponentMirrorOffset(), ARRAY_KLASS_COMPONENT_MIRROR));
             }
+        } else {
+            // Class for primitive type
         }
         return null;
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/HotSpotReplacementsUtil.java	Tue May 12 20:56:04 2015 +0200
@@ -825,6 +825,11 @@
 
     public static final LocationIdentity OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION = NamedLocationIdentity.immutable("ObjArrayKlass::_element_klass");
 
+    @Fold
+    public static int arrayClassElementOffset() {
+        return config().arrayClassElementOffset;
+    }
+
     public static final LocationIdentity PRIMARY_SUPERS_LOCATION = NamedLocationIdentity.immutable("PrimarySupers");
 
     public static final LocationIdentity METASPACE_ARRAY_LENGTH_LOCATION = NamedLocationIdentity.immutable("MetaspaceArrayLength");
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/replacements/arraycopy/ArrayCopySnippets.java	Tue May 12 20:56:04 2015 +0200
@@ -194,7 +194,8 @@
             // no storecheck required.
             ArrayCopyCallNode.arraycopy(nonNullSrc, srcPos, nonNullDest, destPos, length, Kind.Object, false, false);
         } else {
-            KlassPointer destElemKlass = loadHub(nonNullDest);
+            KlassPointer destKlass = loadHub(nonNullDest);
+            KlassPointer destElemKlass = destKlass.readKlassPointer(arrayClassElementOffset(), OBJ_ARRAY_KLASS_ELEMENT_KLASS_LOCATION);
             checkcastArraycopyHelper(srcPos, destPos, length, nonNullSrc, nonNullDest, destElemKlass);
         }
     }
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/stubs/SnippetStub.java	Tue May 12 20:56:04 2015 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.hotspot.stubs;
 
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
+
 import java.lang.reflect.*;
 
 import com.oracle.graal.api.meta.*;
@@ -94,8 +96,8 @@
 
         assert SnippetGraphUnderConstruction.get() == null;
         SnippetGraphUnderConstruction.set(graph);
-        ReplacementContext initialReplacementContext = new ReplacementContext(method, method);
-        new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(graph);
+        IntrinsicContext initialIntrinsicContext = new IntrinsicContext(method, method, INLINE_AFTER_PARSING);
+        new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph);
         SnippetGraphUnderConstruction.set(null);
 
         graph.setGuardsStage(GuardsStage.FLOATING_GUARDS);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/word/KlassPointer.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/word/KlassPointer.java	Tue May 12 20:56:04 2015 +0200
@@ -44,8 +44,8 @@
     public static native KlassPointer fromWord(Pointer pointer);
 
     @HotSpotOperation(opcode = READ_KLASS_POINTER)
-    public native KlassPointer readKlassPointer(int secondarySuperCacheOffset, LocationIdentity secondarySuperCacheLocation);
+    public native KlassPointer readKlassPointer(int offset, LocationIdentity locationIdentity);
 
     @Operation(opcode = Opcode.WRITE_POINTER)
-    public native void writeKlassPointer(int secondarySuperCacheOffset, KlassPointer t, LocationIdentity secondarySuperCacheLocation);
+    public native void writeKlassPointer(int offset, KlassPointer t, LocationIdentity locationIdentity);
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractBytecodeParser.java	Tue May 12 20:56:04 2015 +0200
@@ -73,6 +73,9 @@
         @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug)
         public static final OptionValue<Boolean> FailedLoopExplosionIsFatal = new OptionValue<>(false);
 
+        @Option(help = "When creating info points hide the methods of the substitutions.", type = OptionType.Debug)
+        public static final OptionValue<Boolean> HideSubstitutionStates = new OptionValue<>(false);
+
         // @formatter:on
     }
 
@@ -99,7 +102,7 @@
     protected final ConstantPool constantPool;
     protected final MetaAccessProvider metaAccess;
 
-    protected final ReplacementContext replacementContext;
+    protected final IntrinsicContext intrinsicContext;
 
     /**
      * Meters the number of actual bytecodes parsed.
@@ -107,7 +110,7 @@
     public static final DebugMetric BytecodesParsed = Debug.metric("BytecodesParsed");
 
     public AbstractBytecodeParser(MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
-                    ReplacementContext replacementContext) {
+                    IntrinsicContext intrinsicContext) {
         this.graphBuilderConfig = graphBuilderConfig;
         this.optimisticOpts = optimisticOpts;
         this.metaAccess = metaAccess;
@@ -115,7 +118,7 @@
         this.profilingInfo = (graphBuilderConfig.getUseProfiling() ? method.getProfilingInfo() : null);
         this.constantPool = method.getConstantPool();
         this.method = method;
-        this.replacementContext = replacementContext;
+        this.intrinsicContext = intrinsicContext;
         assert metaAccess != null;
     }
 
@@ -140,7 +143,7 @@
         if (kind == Kind.Object) {
             value = frameState.xpop();
             // astore and astore_<n> may be used to store a returnAddress (jsr)
-            assert parsingReplacement() || (value.getKind() == Kind.Object || value.getKind() == Kind.Int) : value + ":" + value.getKind();
+            assert parsingIntrinsic() || (value.getKind() == Kind.Object || value.getKind() == Kind.Int) : value + ":" + value.getKind();
         } else {
             value = frameState.pop(kind);
         }
@@ -584,13 +587,13 @@
     }
 
     private void maybeEagerlyResolve(int cpi, int bytecode) {
-        if (graphBuilderConfig.eagerResolving() || replacementContext instanceof IntrinsicContext) {
+        if (graphBuilderConfig.eagerResolving() || intrinsicContext != null) {
             constantPool.loadReferencedType(cpi, bytecode);
         }
     }
 
     private JavaTypeProfile getProfileForTypeCheck(ResolvedJavaType type) {
-        if (parsingReplacement() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) {
+        if (parsingIntrinsic() || profilingInfo == null || !optimisticOpts.useTypeCheckHints() || !canHaveSubtype(type)) {
             return null;
         } else {
             return profilingInfo.getTypeProfile(bci());
@@ -1251,7 +1254,7 @@
         Debug.log("%s", sb);
     }
 
-    public boolean parsingReplacement() {
-        return replacementContext != null;
+    public boolean parsingIntrinsic() {
+        return intrinsicContext != null;
     }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractFrameStateBuilder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/AbstractFrameStateBuilder.java	Tue May 12 20:56:04 2015 +0200
@@ -176,8 +176,8 @@
     public T loadLocal(int i) {
         T x = locals[i];
         assert x != null : i;
-        assert parser.parsingReplacement() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null);
-        assert parser.parsingReplacement() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1);
+        assert parser.parsingIntrinsic() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null);
+        assert parser.parsingIntrinsic() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1);
         return x;
     }
 
@@ -193,14 +193,14 @@
      * @param x the instruction which produces the value for the local
      */
     public void storeLocal(int i, T x, Kind kind) {
-        assert x == null || parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x;
+        assert x == null || parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x;
         locals[i] = x;
         if (x != null) {
-            if (kind.needsTwoSlots() && !parser.parsingReplacement()) {
+            if (kind.needsTwoSlots() && !parser.parsingIntrinsic()) {
                 // if this is a double word, then kill i+1
                 locals[i + 1] = null;
             }
-            if (i > 0 && !parser.parsingReplacement()) {
+            if (i > 0 && !parser.parsingIntrinsic()) {
                 T p = locals[i - 1];
                 if (p != null && p.getKind().needsTwoSlots()) {
                     // if there was a double word at i - 1, then kill it
@@ -235,7 +235,7 @@
      * @param x the instruction to push onto the stack
      */
     public void xpush(T x) {
-        assert parser.parsingReplacement() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal));
+        assert parser.parsingIntrinsic() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal));
         stack[stackSize++] = x;
     }
 
@@ -379,7 +379,7 @@
                 newStackSize--;
                 assert stack[newStackSize].getKind().needsTwoSlots();
             } else {
-                assert parser.parsingReplacement() || (stack[newStackSize].getKind().getSlotCount() == 1);
+                assert parser.parsingIntrinsic() || (stack[newStackSize].getKind().getSlotCount() == 1);
             }
             result[i] = stack[newStackSize];
         }
@@ -415,7 +415,7 @@
     }
 
     private T assertKind(Kind kind, T x) {
-        assert x != null && (parser.parsingReplacement() || x.getKind() == kind) : "kind=" + kind + ", value=" + x + ((x == null) ? "" : ", value.kind=" + x.getKind());
+        assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind) : "kind=" + kind + ", value=" + x + ((x == null) ? "" : ", value.kind=" + x.getKind());
         return x;
     }
 
@@ -435,7 +435,7 @@
     }
 
     private T assertObject(T x) {
-        assert x != null && (parser.parsingReplacement() || (x.getKind() == Kind.Object));
+        assert x != null && (parser.parsingIntrinsic() || (x.getKind() == Kind.Object));
         return x;
     }
 
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -28,6 +28,7 @@
 import static com.oracle.graal.compiler.common.GraalInternalError.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
 import static com.oracle.graal.compiler.common.type.StampFactory.*;
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
 import static com.oracle.graal.java.AbstractBytecodeParser.Options.*;
 import static com.oracle.graal.nodes.StructuredGraph.*;
 import static com.oracle.graal.nodes.type.StampTool.*;
@@ -52,6 +53,7 @@
 import com.oracle.graal.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.BciBlockMapping.ExceptionDispatchBlock;
+import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
 import com.oracle.graal.nodeinfo.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
@@ -84,6 +86,87 @@
         return graphBuilderConfig;
     }
 
+    static class IntrinsicScope implements AutoCloseable {
+        FrameState stateBefore;
+        final Mark mark;
+        final BytecodeParser parser;
+
+        static IntrinsicScope create(boolean enteringIntrinsic, BytecodeParser parser, HIRFrameStateBuilder currentFrameState, ValueNode[] args) {
+            if (enteringIntrinsic) {
+                return new IntrinsicScope(parser, currentFrameState, args);
+            }
+            return null;
+        }
+
+        public IntrinsicScope(BytecodeParser parser, HIRFrameStateBuilder currentFrameState, ValueNode[] args) {
+            this.parser = parser;
+            if (args == null) {
+                assert parser.parent == null;
+                assert parser.bci() == 0;
+                mark = null;
+            } else {
+                mark = parser.getGraph().getMark();
+                for (ValueNode arg : args) {
+                    currentFrameState.push(arg.getKind(), arg);
+                }
+                stateBefore = currentFrameState.create(parser.bci(), null);
+                for (int i = args.length - 1; i >= 0; i--) {
+                    ValueNode arg = args[i];
+                    currentFrameState.pop(arg.getKind());
+                }
+            }
+        }
+
+        public void close() {
+            IntrinsicContext intrinsic = parser.intrinsicContext;
+            if (intrinsic != null && intrinsic.isPostParseInlined()) {
+                return;
+            }
+
+            FrameState stateAfterReturn = null;
+            StructuredGraph graph = parser.getGraph();
+            for (Node node : graph.getNewNodes(mark)) {
+                if (node instanceof FrameState) {
+                    FrameState fs = (FrameState) node;
+                    if (BytecodeFrame.isPlaceholderBci(fs.bci)) {
+                        if (fs.bci == BytecodeFrame.AFTER_BCI) {
+                            if (fs.stackSize() != 0) {
+                                assert fs.usages().count() == 1;
+                                ValueNode returnVal = fs.stackAt(0);
+                                assert returnVal == fs.usages().first();
+
+                                ValueNode tos = parser.frameState.pop(returnVal.getKind());
+                                parser.frameState.push(returnVal.getKind(), returnVal);
+                                FrameState newFs = parser.frameState.create(parser.stream.nextBCI(), null);
+                                fs.replaceAndDelete(newFs);
+                                parser.frameState.pop(returnVal.getKind());
+                                parser.frameState.push(tos.getKind(), tos);
+                            } else {
+                                if (stateAfterReturn == null) {
+                                    if (intrinsic != null && intrinsic.isCompilationRoot()) {
+                                        stateAfterReturn = graph.add(new FrameState(BytecodeFrame.INVALID_FRAMESTATE_BCI));
+                                    } else {
+                                        stateAfterReturn = parser.frameState.create(parser.stream.nextBCI(), null);
+                                    }
+                                }
+                                fs.replaceAndDelete(stateAfterReturn);
+                            }
+                        } else if (fs.bci == BytecodeFrame.BEFORE_BCI) {
+                            if (stateBefore == null) {
+                                stateBefore = graph.start().stateAfter();
+                            }
+                            if (stateBefore != fs) {
+                                fs.replaceAndDelete(stateBefore);
+                            }
+                        } else {
+                            assert fs.bci == BytecodeFrame.INVALID_FRAMESTATE_BCI;
+                        }
+                    }
+                }
+            }
+        }
+    }
+
     // Fully qualified name is a workaround for JDK-8056066
     public static class Instance extends com.oracle.graal.phases.Phase {
 
@@ -91,7 +174,7 @@
 
         private final MetaAccessProvider metaAccess;
 
-        private final ReplacementContext initialReplacementContext;
+        private final IntrinsicContext initialIntrinsicContext;
 
         private final GraphBuilderConfiguration graphBuilderConfig;
         private final OptimisticOptimizations optimisticOpts;
@@ -99,13 +182,13 @@
         private final ConstantReflectionProvider constantReflection;
 
         public Instance(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig,
-                        OptimisticOptimizations optimisticOpts, ReplacementContext initialReplacementContext) {
+                        OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
             this.graphBuilderConfig = graphBuilderConfig;
             this.optimisticOpts = optimisticOpts;
             this.metaAccess = metaAccess;
             this.stampProvider = stampProvider;
             this.constantReflection = constantReflection;
-            this.initialReplacementContext = initialReplacementContext;
+            this.initialIntrinsicContext = initialIntrinsicContext;
 
             assert metaAccess != null;
         }
@@ -118,12 +201,15 @@
             this.graph = graph;
             TTY.Filter filter = new TTY.Filter(PrintFilter.getValue(), method);
             try {
-                ReplacementContext replacementContext = initialReplacementContext;
-                BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, replacementContext);
+                IntrinsicContext intrinsicContext = initialIntrinsicContext;
+                BytecodeParser parser = new BytecodeParser(null, metaAccess, method, graphBuilderConfig, optimisticOpts, entryBCI, intrinsicContext);
                 HIRFrameStateBuilder frameState = new HIRFrameStateBuilder(parser, method, graph);
-                frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || replacementContext != null, graphBuilderConfig.getPlugins().getParameterPlugin());
-                parser.build(graph.start(), frameState);
+
+                frameState.initializeForMethodStart(graphBuilderConfig.eagerResolving() || intrinsicContext != null, graphBuilderConfig.getPlugins().getParameterPlugin());
 
+                try (IntrinsicScope s = IntrinsicScope.create(intrinsicContext != null, parser, frameState, null)) {
+                    parser.build(graph.start(), frameState);
+                }
                 GraphUtil.normalizeLoops(graph);
 
                 // Remove dead parameters.
@@ -252,12 +338,12 @@
             private HIRFrameStateBuilder[][] entryStateMatrix;
 
             public BytecodeParser(BytecodeParser parent, MetaAccessProvider metaAccess, ResolvedJavaMethod method, GraphBuilderConfiguration graphBuilderConfig,
-                            OptimisticOptimizations optimisticOpts, int entryBCI, ReplacementContext replacementContext) {
-                super(metaAccess, method, graphBuilderConfig, optimisticOpts, replacementContext);
+                            OptimisticOptimizations optimisticOpts, int entryBCI, IntrinsicContext intrinsicContext) {
+                super(metaAccess, method, graphBuilderConfig, optimisticOpts, intrinsicContext);
                 this.entryBCI = entryBCI;
                 this.parent = parent;
 
-                if (graphBuilderConfig.insertNonSafepointDebugInfo()) {
+                if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
                     lnt = method.getLineNumberTable();
                     previousLineNumber = -1;
                 }
@@ -326,11 +412,27 @@
                     if (startInstruction == graph.start()) {
                         StartNode startNode = graph.start();
                         if (method.isSynchronized()) {
-                            startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI));
+                            assert !parsingIntrinsic();
+                            startNode.setStateAfter(createFrameState(BytecodeFrame.BEFORE_BCI, startNode));
                         } else {
-                            frameState.clearNonLiveLocals(startBlock, liveness, true);
-                            assert bci() == 0;
-                            startNode.setStateAfter(createFrameState(bci()));
+                            if (!parsingIntrinsic()) {
+                                if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
+                                    /*
+                                     * Don't clear the receiver when Object.<init> is the
+                                     * compilation root. The receiver is needed as input to
+                                     * RegisterFinalizerNode.
+                                     */
+                                } else {
+                                    frameState.clearNonLiveLocals(startBlock, liveness, true);
+                                }
+                                assert bci() == 0;
+                                startNode.setStateAfter(createFrameState(bci(), startNode));
+                            } else {
+                                if (startNode.stateAfter() == null) {
+                                    FrameState stateAfterStart = createStateAfterStartOfReplacementGraph();
+                                    startNode.setStateAfter(stateAfterStart);
+                                }
+                            }
                         }
                     }
 
@@ -342,7 +444,7 @@
                         genMonitorEnter(methodSynchronizedObject, bci());
                     }
 
-                    if (graphBuilderConfig.insertNonSafepointDebugInfo()) {
+                    if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
                         append(createInfoPointNode(InfopointReason.METHOD_START));
                     }
 
@@ -372,6 +474,42 @@
                 }
             }
 
+            /**
+             * Creates the frame state after the start node of a graph for an
+             * {@link IntrinsicContext intrinsic} that is the parse root (either for root compiling
+             * or for post-parse inlining).
+             */
+            private FrameState createStateAfterStartOfReplacementGraph() {
+                assert parent == null;
+                assert frameState.method.equals(intrinsicContext.getIntrinsicMethod());
+                assert bci() == 0;
+                assert frameState.stackSize == 0;
+                FrameState stateAfterStart;
+                if (intrinsicContext.isPostParseInlined()) {
+                    stateAfterStart = graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
+                } else {
+                    ResolvedJavaMethod original = intrinsicContext.getOriginalMethod();
+                    ValueNode[] locals;
+                    if (original.getMaxLocals() == frameState.localsSize() || original.isNative()) {
+                        locals = frameState.locals;
+                    } else {
+                        locals = new ValueNode[original.getMaxLocals()];
+                        int parameterCount = original.getSignature().getParameterCount(!original.isStatic());
+                        for (int i = 0; i < parameterCount; i++) {
+                            ValueNode param = frameState.locals[i];
+                            locals[i] = param;
+                            assert param == null || param instanceof ParameterNode || param.isConstant();
+                        }
+                    }
+                    ValueNode[] stack = {};
+                    int stackSize = 0;
+                    ValueNode[] locks = {};
+                    List<MonitorIdNode> monitorIds = Collections.emptyList();
+                    stateAfterStart = graph.add(new FrameState(null, original, 0, locals, stack, stackSize, locks, monitorIds, false, false));
+                }
+                return stateAfterStart;
+            }
+
             private void detectLoops(FixedNode startInstruction) {
                 NodeBitMap visited = graph.createNodeBitMap();
                 NodeBitMap active = graph.createNodeBitMap();
@@ -404,7 +542,7 @@
                                 }
                                 LoopBeginNode loopBegin = (LoopBeginNode) ((EndNode) merge.next()).merge();
                                 LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
-                                if (parsingReplacement()) {
+                                if (parsingIntrinsic()) {
                                     loopEnd.disableSafepoint();
                                 }
                                 endNode.replaceAndDelete(loopEnd);
@@ -728,11 +866,11 @@
                     dispatchBegin = graph.add(new ExceptionObjectNode(metaAccess));
                     dispatchState.apush(dispatchBegin);
                     dispatchState.setRethrowException(true);
-                    dispatchBegin.setStateAfter(dispatchState.create(bci));
+                    dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
                 } else {
                     dispatchBegin = graph.add(new DispatchBeginNode());
                     dispatchState.apush(exceptionObject);
-                    dispatchBegin.setStateAfter(dispatchState.create(bci));
+                    dispatchBegin.setStateAfter(dispatchState.create(bci, dispatchBegin));
                     dispatchState.setRethrowException(true);
                 }
                 this.controlFlowSplit = true;
@@ -946,7 +1084,7 @@
                 append(new IfNode(graph.unique(new IsNullNode(receiver)), exception, falseSucc, 0.01));
                 lastInstr = falseSucc;
 
-                exception.setStateAfter(createFrameState(bci()));
+                exception.setStateAfter(createFrameState(bci(), exception));
                 exception.setNext(handleException(exception, bci()));
                 return nonNullReceiver;
             }
@@ -958,7 +1096,7 @@
                 append(new IfNode(graph.unique(IntegerBelowNode.create(index, length, constantReflection)), trueSucc, exception, 0.99));
                 lastInstr = trueSucc;
 
-                exception.setStateAfter(createFrameState(bci()));
+                exception.setStateAfter(createFrameState(bci(), exception));
                 exception.setNext(handleException(exception, bci()));
             }
 
@@ -971,7 +1109,7 @@
             protected void genStoreField(ValueNode receiver, ResolvedJavaField field, ValueNode value) {
                 StoreFieldNode storeFieldNode = new StoreFieldNode(receiver, field, value);
                 append(storeFieldNode);
-                storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI()));
+                storeFieldNode.setStateAfter(this.createFrameState(stream.nextBCI(), storeFieldNode));
             }
 
             /**
@@ -1104,7 +1242,7 @@
                 }
 
                 JavaType returnType = targetMethod.getSignature().getReturnType(method.getDeclaringClass());
-                if (graphBuilderConfig.eagerResolving() || parsingReplacement()) {
+                if (graphBuilderConfig.eagerResolving() || parsingIntrinsic()) {
                     returnType = returnType.resolve(targetMethod.getDeclaringClass());
                 }
                 if (invokeKind.hasReceiver()) {
@@ -1210,8 +1348,8 @@
                         for (Node n : newNodes) {
                             if (n instanceof StateSplit) {
                                 StateSplit stateSplit = (StateSplit) n;
-                                assert stateSplit.stateAfter() != null : error("%s node added by plugin for %s need to have a non-null frame state: %s", StateSplit.class.getSimpleName(),
-                                                targetMethod.format("%H.%n(%p)"), stateSplit);
+                                assert stateSplit.stateAfter() != null || !stateSplit.hasSideEffect() : error("%s node added by plugin for %s need to have a non-null frame state: %s",
+                                                StateSplit.class.getSimpleName(), targetMethod.format("%H.%n(%p)"), stateSplit);
                             }
                         }
                         try {
@@ -1231,9 +1369,8 @@
                 InvocationPlugin plugin = graphBuilderConfig.getPlugins().getInvocationPlugins().lookupInvocation(targetMethod);
                 if (plugin != null) {
 
-                    ReplacementContext context = this.replacementContext;
-                    if (context != null && context.isCallToOriginal(targetMethod)) {
-                        // Self recursive replacement means the original
+                    if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) {
+                        // Self recursive intrinsic means the original
                         // method should be called.
                         assert !targetMethod.hasBytecodes() : "TODO: when does this happen?";
                         return false;
@@ -1256,69 +1393,56 @@
 
             private boolean tryInline(ValueNode[] args, ResolvedJavaMethod targetMethod, JavaType returnType) {
                 InlineInvokePlugin plugin = graphBuilderConfig.getPlugins().getInlineInvokePlugin();
-                boolean canBeInlined = parsingReplacement() || targetMethod.canBeInlined();
+                boolean canBeInlined = parsingIntrinsic() || targetMethod.canBeInlined();
                 if (plugin == null || !canBeInlined) {
                     return false;
                 }
                 InlineInfo inlineInfo = plugin.getInlineInfo(this, targetMethod, args, returnType);
                 if (inlineInfo != null) {
-                    return inline(plugin, targetMethod, inlineInfo.methodToInline, inlineInfo.isReplacement, inlineInfo.isIntrinsic, args);
+                    return inline(plugin, targetMethod, inlineInfo.methodToInline, inlineInfo.isIntrinsic, args);
                 }
                 return false;
             }
 
             public void intrinsify(ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, ValueNode[] args) {
-                boolean res = inline(null, targetMethod, substitute, true, true, args);
+                boolean res = inline(null, targetMethod, substitute, true, args);
                 assert res : "failed to inline " + substitute;
             }
 
-            private boolean inline(InlineInvokePlugin plugin, ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isReplacement, boolean isIntrinsic, ValueNode[] args) {
-                int bci = bci();
+            private boolean inline(InlineInvokePlugin plugin, ResolvedJavaMethod targetMethod, ResolvedJavaMethod inlinedMethod, boolean isIntrinsic, ValueNode[] args) {
                 if (TraceInlineDuringParsing.getValue() || TraceParserPlugins.getValue()) {
                     if (targetMethod.equals(inlinedMethod)) {
                         traceWithContext("inlining call to %s", inlinedMethod.format("%h.%n(%p)"));
                     } else {
-                        traceWithContext("inlining call to %s as replacement for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)"));
+                        traceWithContext("inlining call to %s as intrinsic for %s", inlinedMethod.format("%h.%n(%p)"), targetMethod.format("%h.%n(%p)"));
                     }
                 }
-                ReplacementContext context = this.replacementContext;
-                if (context != null && context.isCallToOriginal(targetMethod)) {
-                    IntrinsicContext intrinsic = context.asIntrinsic();
-                    if (intrinsic != null) {
-                        if (intrinsic.isCompilationRoot()) {
-                            // A root compiled intrinsic needs to deoptimize
-                            // if the slow path is taken
-                            DeoptimizeNode deopt = append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint));
-                            deopt.setStateBefore(intrinsic.getInvokeStateBefore(graph, null));
-                            return true;
-                        } else {
-                            // Otherwise inline the original method. Any frame state created
-                            // during the inlining will exclude frame(s) in the
-                            // intrinsic method (see HIRFrameStateBuilder.create(int bci)).
-                            parseAndInlineCallee(context.method, args, null);
-                            return true;
-                        }
+                IntrinsicContext intrinsic = this.intrinsicContext;
+                if (intrinsic != null && intrinsic.isCallToOriginal(targetMethod)) {
+                    if (intrinsic.isCompilationRoot()) {
+                        // A root compiled intrinsic needs to deoptimize
+                        // if the slow path is taken. During frame state
+                        // assignment, the deopt node will get its stateBefore
+                        // from the start node of the intrinsic
+                        append(new DeoptimizeNode(InvalidateRecompile, RuntimeConstraint));
+                        return true;
                     } else {
-                        // Self recursive replacement means the original
-                        // method should be called.
-                        if (context.method.hasBytecodes()) {
-                            parseAndInlineCallee(context.method, args, null);
-                            return true;
-                        } else {
+                        // Otherwise inline the original method. Any frame state created
+                        // during the inlining will exclude frame(s) in the
+                        // intrinsic method (see HIRFrameStateBuilder.create(int bci)).
+                        if (intrinsic.getOriginalMethod().isNative()) {
                             return false;
                         }
+                        parseAndInlineCallee(intrinsic.getOriginalMethod(), args, null);
+                        return true;
                     }
                 } else {
-                    if (context == null && isReplacement) {
+                    if (intrinsic == null && isIntrinsic) {
                         assert !inlinedMethod.equals(targetMethod);
-                        if (isIntrinsic) {
-                            context = new IntrinsicContext(targetMethod, inlinedMethod, args, bci);
-                        } else {
-                            context = new ReplacementContext(targetMethod, inlinedMethod);
-                        }
+                        intrinsic = new IntrinsicContext(targetMethod, inlinedMethod, INLINE_DURING_PARSING);
                     }
                     if (inlinedMethod.hasBytecodes()) {
-                        parseAndInlineCallee(inlinedMethod, args, context);
+                        parseAndInlineCallee(inlinedMethod, args, intrinsic);
                         if (plugin != null) {
                             plugin.postInline(inlinedMethod);
                         }
@@ -1362,34 +1486,38 @@
                 return res;
             }
 
-            private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, ReplacementContext calleeReplacementContext) {
-                BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeReplacementContext);
-                HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(parser, targetMethod, graph);
-                if (!targetMethod.isStatic()) {
-                    args[0] = nullCheckedValue(args[0]);
-                }
-                startFrameState.initializeFromArgumentsArray(args);
-                parser.build(this.lastInstr, startFrameState);
+            private void parseAndInlineCallee(ResolvedJavaMethod targetMethod, ValueNode[] args, IntrinsicContext calleeIntrinsicContext) {
+                try (IntrinsicScope s = IntrinsicScope.create(calleeIntrinsicContext != null && !parsingIntrinsic(), this, frameState, args)) {
+
+                    BytecodeParser parser = new BytecodeParser(this, metaAccess, targetMethod, graphBuilderConfig, optimisticOpts, INVOCATION_ENTRY_BCI, calleeIntrinsicContext);
+                    HIRFrameStateBuilder startFrameState = new HIRFrameStateBuilder(parser, targetMethod, graph);
+                    if (!targetMethod.isStatic()) {
+                        args[0] = nullCheckedValue(args[0]);
+                    }
+                    startFrameState.initializeFromArgumentsArray(args);
+                    parser.build(this.lastInstr, startFrameState);
 
-                FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
-                this.lastInstr = calleeBeforeReturnNode;
-                if (calleeBeforeReturnNode != null) {
-                    ValueNode calleeReturnValue = parser.getReturnValue();
-                    if (calleeReturnValue != null) {
-                        frameState.push(targetMethod.getSignature().getReturnKind().getStackKind(), calleeReturnValue);
+                    FixedWithNextNode calleeBeforeReturnNode = parser.getBeforeReturnNode();
+                    this.lastInstr = calleeBeforeReturnNode;
+                    Kind calleeReturnKind = targetMethod.getSignature().getReturnKind();
+                    if (calleeBeforeReturnNode != null) {
+                        ValueNode calleeReturnValue = parser.getReturnValue();
+                        if (calleeReturnValue != null) {
+                            frameState.push(calleeReturnKind.getStackKind(), calleeReturnValue);
+                        }
                     }
-                }
 
-                FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
-                if (calleeBeforeUnwindNode != null) {
-                    ValueNode calleeUnwindValue = parser.getUnwindValue();
-                    assert calleeUnwindValue != null;
-                    calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
-                }
+                    FixedWithNextNode calleeBeforeUnwindNode = parser.getBeforeUnwindNode();
+                    if (calleeBeforeUnwindNode != null) {
+                        ValueNode calleeUnwindValue = parser.getUnwindValue();
+                        assert calleeUnwindValue != null;
+                        calleeBeforeUnwindNode.setNext(handleException(calleeUnwindValue, bci()));
+                    }
 
-                // Record inlined method dependency in the graph
-                if (graph.isInlinedMethodRecordingEnabled()) {
-                    graph.getInlinedMethods().add(targetMethod);
+                    // Record inlined method dependency in the graph
+                    if (graph.isInlinedMethodRecordingEnabled()) {
+                        graph.getInlinedMethods().add(targetMethod);
+                    }
                 }
             }
 
@@ -1400,7 +1528,7 @@
             protected InvokeNode createInvoke(CallTargetNode callTarget, Kind resultType) {
                 InvokeNode invoke = append(new InvokeNode(callTarget, bci()));
                 frameState.pushReturn(resultType, invoke);
-                invoke.setStateAfter(createFrameState(stream.nextBCI()));
+                invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
                 return invoke;
             }
 
@@ -1408,13 +1536,35 @@
                 DispatchBeginNode exceptionEdge = handleException(null, bci());
                 InvokeWithExceptionNode invoke = append(new InvokeWithExceptionNode(callTarget, exceptionEdge, bci()));
                 frameState.pushReturn(resultType, invoke);
-                invoke.setStateAfter(createFrameState(stream.nextBCI()));
+                invoke.setStateAfter(createFrameState(stream.nextBCI(), invoke));
                 return invoke;
             }
 
             @Override
             protected void genReturn(ValueNode returnVal, Kind returnKind) {
-
+                if (parsingIntrinsic() && returnVal != null) {
+                    if (returnVal instanceof StateSplit) {
+                        StateSplit stateSplit = (StateSplit) returnVal;
+                        FrameState stateAfter = stateSplit.stateAfter();
+                        if (stateSplit.hasSideEffect()) {
+                            assert stateSplit != null;
+                            if (stateAfter.bci == BytecodeFrame.AFTER_BCI) {
+                                assert stateAfter.usages().count() == 1;
+                                assert stateAfter.usages().first() == stateSplit;
+                                stateAfter.replaceAtUsages(graph.add(new FrameState(BytecodeFrame.AFTER_BCI, returnVal)));
+                                GraphUtil.killWithUnusedFloatingInputs(stateAfter);
+                            } else {
+                                /*
+                                 * This must be the return value from within a partial
+                                 * intrinsification.
+                                 */
+                                assert !BytecodeFrame.isPlaceholderBci(stateAfter.bci);
+                            }
+                        } else {
+                            assert stateAfter == null;
+                        }
+                    }
+                }
                 if (parent == null) {
                     frameState.setRethrowException(false);
                     frameState.clearStack();
@@ -1440,7 +1590,10 @@
             }
 
             private void beforeReturn(ValueNode x, Kind kind) {
-                if (graphBuilderConfig.insertNonSafepointDebugInfo()) {
+                if (graph.method() != null && graph.method().isJavaLangObjectInit()) {
+                    append(new RegisterFinalizerNode(frameState.localAt(0)));
+                }
+                if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
                     append(createInfoPointNode(InfopointReason.METHOD_END));
                 }
 
@@ -1455,7 +1608,7 @@
                 MonitorIdNode monitorId = graph.add(new MonitorIdNode(frameState.lockDepth()));
                 MonitorEnterNode monitorEnter = append(new MonitorEnterNode(x, monitorId));
                 frameState.pushLock(x, monitorId);
-                monitorEnter.setStateAfter(createFrameState(bci));
+                monitorEnter.setStateAfter(createFrameState(bci, monitorEnter));
             }
 
             @Override
@@ -1466,7 +1619,7 @@
                     throw bailout(String.format("unbalanced monitors: mismatch at monitorexit, %s != %s", GraphUtil.originalValue(x), GraphUtil.originalValue(lockedObject)));
                 }
                 MonitorExitNode monitorExit = append(new MonitorExitNode(x, monitorId, escapedReturnValue));
-                monitorExit.setStateAfter(createFrameState(bci));
+                monitorExit.setStateAfter(createFrameState(bci, monitorExit));
             }
 
             @Override
@@ -1617,7 +1770,7 @@
                             lastLoopExit = loopExit;
                             Debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
                             newState.insertLoopProxies(loopExit, getEntryState(loop, this.getCurrentDimension()));
-                            loopExit.setStateAfter(newState.create(bci));
+                            loopExit.setStateAfter(newState.create(bci, loopExit));
                         }
 
                         lastLoopExit.setNext(target);
@@ -1776,7 +1929,7 @@
                      */
                     LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(block, operatingDimension);
                     LoopEndNode loopEnd = graph.add(new LoopEndNode(loopBegin));
-                    if (parsingReplacement()) {
+                    if (parsingIntrinsic()) {
                         loopEnd.disableSafepoint();
                     }
                     Target target = checkLoopExit(loopEnd, block, state);
@@ -1962,7 +2115,7 @@
                     if (block instanceof ExceptionDispatchBlock) {
                         bci = ((ExceptionDispatchBlock) block).deoptBci;
                     }
-                    abstractMergeNode.setStateAfter(createFrameState(bci));
+                    abstractMergeNode.setStateAfter(createFrameState(bci, abstractMergeNode));
                 }
             }
 
@@ -2045,7 +2198,7 @@
 
                     // Create phi functions for all local variables and operand stack slots.
                     frameState.insertLoopPhis(liveness, block.loopId, loopBegin);
-                    loopBegin.setStateAfter(createFrameState(block.startBci));
+                    loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin));
 
                     /*
                      * We have seen all forward branches. All subsequent backward branches will
@@ -2074,9 +2227,14 @@
                 int bci = block.startBci;
                 BytecodesParsed.add(block.endBci - bci);
 
+                /* Reset line number for new block */
+                if (graphBuilderConfig.insertSimpleDebugInfo()) {
+                    previousLineNumber = -1;
+                }
+
                 while (bci < endBCI) {
-                    if (graphBuilderConfig.insertNonSafepointDebugInfo() && lnt != null) {
-                        currentLineNumber = lnt.getLineNumber(bci);
+                    if (graphBuilderConfig.insertNonSafepointDebugInfo() && !parsingIntrinsic()) {
+                        currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : (graphBuilderConfig.insertFullDebugInfo() ? -1 : bci);
                         if (currentLineNumber != previousLineNumber) {
                             append(createInfoPointNode(InfopointReason.LINE_NUMBER));
                             previousLineNumber = currentLineNumber;
@@ -2093,7 +2251,7 @@
                         }
                         EntryMarkerNode x = append(new EntryMarkerNode());
                         frameState.insertProxies(x);
-                        x.setStateAfter(createFrameState(bci));
+                        x.setStateAfter(createFrameState(bci, x));
                     }
 
                     try {
@@ -2110,7 +2268,7 @@
                     bci = stream.currentBCI();
 
                     assert block == currentBlock;
-                    assert !(lastInstr instanceof StateSplit) || lastInstr instanceof BeginNode || ((StateSplit) lastInstr).stateAfter() != null : lastInstr;
+                    assert checkLastInstruction();
                     lastInstr = finishInstruction(lastInstr, frameState);
                     if (bci < endBCI) {
                         if (bci > block.endBci) {
@@ -2124,6 +2282,18 @@
                 }
             }
 
+            protected boolean checkLastInstruction() {
+                if (lastInstr instanceof BeginNode) {
+                    // ignore
+                } else if (lastInstr instanceof StateSplit) {
+                    StateSplit stateSplit = (StateSplit) lastInstr;
+                    if (stateSplit.hasSideEffect()) {
+                        assert stateSplit.stateAfter() != null : "side effect " + lastInstr + " requires a non-null stateAfter";
+                    }
+                }
+                return true;
+            }
+
             private LoopBeginNode appendLoopBegin(FixedWithNextNode fixedWithNext) {
                 EndNode preLoopEnd = graph.add(new EndNode());
                 LoopBeginNode loopBegin = graph.add(new LoopBeginNode());
@@ -2146,9 +2316,18 @@
 
             private InfopointNode createInfoPointNode(InfopointReason reason) {
                 if (graphBuilderConfig.insertFullDebugInfo()) {
-                    return new FullInfopointNode(reason, createFrameState(bci()));
+                    return new FullInfopointNode(reason, createFrameState(bci(), null));
                 } else {
-                    return new SimpleInfopointNode(reason, new BytecodePosition(null, method, bci()));
+                    BytecodePosition position = createBytecodePosition();
+                    // Update the previous infopoint position if no new fixed nodes were inserted
+                    if (lastInstr instanceof SimpleInfopointNode) {
+                        SimpleInfopointNode lastInfopoint = (SimpleInfopointNode) lastInstr;
+                        if (lastInfopoint.getReason() == reason) {
+                            lastInfopoint.setPosition(position);
+                            return lastInfopoint;
+                        }
+                    }
+                    return new SimpleInfopointNode(reason, position);
                 }
             }
 
@@ -2247,7 +2426,7 @@
                     FixedNode falseSuccessor = createTarget(falseBlock, frameState, false, true);
                     ValueNode ifNode = genIfNode(condition, trueSuccessor, falseSuccessor, probability);
                     append(ifNode);
-                    if (parsingReplacement()) {
+                    if (parsingIntrinsic()) {
                         if (x instanceof BranchProbabilityNode) {
                             ((BranchProbabilityNode) x).simplify(null);
                         } else if (y instanceof BranchProbabilityNode) {
@@ -2377,8 +2556,8 @@
                 return parent;
             }
 
-            public Replacement getReplacement() {
-                return replacementContext;
+            public IntrinsicContext getIntrinsic() {
+                return intrinsicContext;
             }
 
             @Override
@@ -2390,7 +2569,7 @@
                     if (bp != this) {
                         fmt.format("%n%s", indent);
                     }
-                    fmt.format("%s [bci: %d, replacement: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingReplacement());
+                    fmt.format("%s [bci: %d, intrinsic: %s]", bp.method.asStackTraceElement(bp.bci()), bp.bci(), bp.parsingIntrinsic());
                     fmt.format("%n%s", new BytecodeDisassembler().disassemble(bp.method, bp.bci(), bp.bci() + 10));
                     bp = bp.parent;
                     indent += " ";
@@ -2399,21 +2578,27 @@
             }
 
             public BailoutException bailout(String string) {
-                FrameState currentFrameState = createFrameState(bci());
+                FrameState currentFrameState = createFrameState(bci(), null);
                 StackTraceElement[] elements = GraphUtil.approxSourceStackTraceElement(currentFrameState);
                 BailoutException bailout = new BailoutException(string);
                 throw GraphUtil.createBailoutException(string, bailout, elements);
             }
 
-            private FrameState createFrameState(int bci) {
+            private FrameState createFrameState(int bci, StateSplit forStateSplit) {
                 if (currentBlock != null && bci > currentBlock.endBci) {
                     frameState.clearNonLiveLocals(currentBlock, liveness, false);
                 }
-                return frameState.create(bci);
+                return frameState.create(bci, forStateSplit);
             }
 
-            public FrameState createStateAfter() {
-                return createFrameState(stream.nextBCI());
+            public void setStateAfter(StateSplit sideEffect) {
+                assert sideEffect.hasSideEffect();
+                FrameState stateAfter = createFrameState(stream.nextBCI(), sideEffect);
+                sideEffect.setStateAfter(stateAfter);
+            }
+
+            private BytecodePosition createBytecodePosition() {
+                return frameState.createBytecodePosition(bci());
             }
         }
     }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/HIRFrameStateBuilder.java	Tue May 12 20:56:04 2015 +0200
@@ -32,6 +32,7 @@
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graphbuilderconf.*;
+import com.oracle.graal.graphbuilderconf.IntrinsicContext.*;
 import com.oracle.graal.java.BciBlockMapping.BciBlock;
 import com.oracle.graal.java.GraphBuilderPhase.Instance.BytecodeParser;
 import com.oracle.graal.nodeinfo.*;
@@ -40,7 +41,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.util.*;
 
-public final class HIRFrameStateBuilder {
+public final class HIRFrameStateBuilder implements SideEffectsState {
 
     static final ValueNode[] EMPTY_ARRAY = new ValueNode[0];
     static final MonitorIdNode[] EMPTY_MONITOR_ARRAY = new MonitorIdNode[0];
@@ -62,6 +63,12 @@
     private FrameState outerFrameState;
 
     /**
+     * The closest {@link StateSplit#hasSideEffect() side-effect} predecessors. There will be more
+     * than one when the current block contains no side-effects but merging predecessor blocks do.
+     */
+    protected List<StateSplit> sideEffects;
+
+    /**
      * Creates a new frame state builder for the given method and the given target graph.
      *
      * @param method the method whose frame is simulated
@@ -144,14 +151,6 @@
             javaIndex += kind.getSlotCount();
             index++;
         }
-
-        if (parser.replacementContext instanceof IntrinsicContext) {
-            IntrinsicContext intrinsic = (IntrinsicContext) parser.replacementContext;
-            if (intrinsic.isCompilationRoot()) {
-                // Records the parameters to an root compiled intrinsic
-                intrinsic.args = locals.clone();
-            }
-        }
     }
 
     private HIRFrameStateBuilder(HIRFrameStateBuilder other) {
@@ -202,25 +201,19 @@
         return sb.toString();
     }
 
-    public FrameState create(int bci) {
-        BytecodeParser parent = parser.getParent();
-        if (parser.parsingReplacement()) {
-            IntrinsicContext intrinsic = parser.replacementContext.asIntrinsic();
-            if (intrinsic != null) {
-                return intrinsic.getInvokeStateBefore(parser.getGraph(), parent);
-            }
+    public FrameState create(int bci, StateSplit forStateSplit) {
+        if (parser.parsingIntrinsic()) {
+            return parser.intrinsicContext.createFrameState(parser.getGraph(), this, forStateSplit);
         }
-        // If this is the recursive call in a partial intrinsification
-        // the frame(s) of the intrinsic method are omitted
-        while (parent != null && parent.parsingReplacement() && parent.replacementContext.asIntrinsic() != null) {
-            parent = parent.getParent();
-        }
+
+        // Skip intrinsic frames
+        BytecodeParser parent = (BytecodeParser) parser.getNonReplacementAncestor();
         return create(bci, parent, false);
     }
 
     public FrameState create(int bci, BytecodeParser parent, boolean duringCall) {
         if (outerFrameState == null && parent != null) {
-            outerFrameState = parent.getFrameState().create(parent.bci());
+            outerFrameState = parent.getFrameState().create(parent.bci(), null);
         }
         if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
             FrameState newFrameState = outerFrameState.duplicateModified(outerFrameState.bci, true, Kind.Void, this.peek(0));
@@ -228,11 +221,37 @@
         }
         if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
             throw GraalInternalError.shouldNotReachHere();
-            // return graph.add(new FrameState(bci));
         }
         return graph.add(new FrameState(outerFrameState, method, bci, locals, stack, stackSize, lockedObjects, Arrays.asList(monitorIds), rethrowException, duringCall));
     }
 
+    public BytecodePosition createBytecodePosition(int bci) {
+        BytecodeParser parent = parser.getParent();
+        if (AbstractBytecodeParser.Options.HideSubstitutionStates.getValue()) {
+            if (parser.parsingIntrinsic()) {
+                // Attribute to the method being replaced
+                return new BytecodePosition(parent.getFrameState().createBytecodePosition(parent.bci()), parser.intrinsicContext.getOriginalMethod(), -1);
+            }
+            // Skip intrinsic frames
+            parent = (BytecodeParser) parser.getNonReplacementAncestor();
+        }
+        return create(null, bci, parent);
+    }
+
+    private BytecodePosition create(BytecodePosition o, int bci, BytecodeParser parent) {
+        BytecodePosition outer = o;
+        if (outer == null && parent != null) {
+            outer = parent.getFrameState().createBytecodePosition(parent.bci());
+        }
+        if (bci == BytecodeFrame.AFTER_EXCEPTION_BCI && parent != null) {
+            return FrameState.toBytecodePosition(outerFrameState);
+        }
+        if (bci == BytecodeFrame.INVALID_FRAMESTATE_BCI) {
+            throw GraalInternalError.shouldNotReachHere();
+        }
+        return new BytecodePosition(outer, method, bci);
+    }
+
     public HIRFrameStateBuilder copy() {
         return new HIRFrameStateBuilder(this);
     }
@@ -283,6 +302,14 @@
             lockedObjects[i] = merge(lockedObjects[i], other.lockedObjects[i], block);
             assert monitorIds[i] == other.monitorIds[i];
         }
+
+        if (sideEffects == null) {
+            sideEffects = other.sideEffects;
+        } else {
+            if (other.sideEffects != null) {
+                sideEffects.addAll(other.sideEffects);
+            }
+        }
     }
 
     private ValueNode merge(ValueNode currentValue, ValueNode otherValue, AbstractMergeNode block) {
@@ -573,8 +600,8 @@
 
     private boolean assertLoadLocal(int i, ValueNode x) {
         assert x != null : i;
-        assert parser.parsingReplacement() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null);
-        assert parser.parsingReplacement() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1);
+        assert parser.parsingIntrinsic() || (x.getKind().getSlotCount() == 1 || locals[i + 1] == null);
+        assert parser.parsingIntrinsic() || (i == 0 || locals[i - 1] == null || locals[i - 1].getKind().getSlotCount() == 1);
         return true;
     }
 
@@ -593,11 +620,11 @@
         assert assertStoreLocal(x);
         locals[i] = x;
         if (x != null) {
-            if (kind.needsTwoSlots() && !parser.parsingReplacement()) {
+            if (kind.needsTwoSlots() && !parser.parsingIntrinsic()) {
                 // if this is a double word, then kill i+1
                 locals[i + 1] = null;
             }
-            if (i > 0 && !parser.parsingReplacement()) {
+            if (i > 0 && !parser.parsingIntrinsic()) {
                 ValueNode p = locals[i - 1];
                 if (p != null && p.getKind().needsTwoSlots()) {
                     // if there was a double word at i - 1, then kill it
@@ -608,7 +635,7 @@
     }
 
     private boolean assertStoreLocal(ValueNode x) {
-        assert x == null || parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x;
+        assert x == null || parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal) : "unexpected value: " + x;
         return true;
     }
 
@@ -637,8 +664,8 @@
     }
 
     private boolean assertPush(Kind kind, ValueNode x) {
-        assert parser.parsingReplacement() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal);
-        assert x != null && (parser.parsingReplacement() || x.getKind() == kind);
+        assert parser.parsingIntrinsic() || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal);
+        assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind);
         return true;
     }
 
@@ -653,7 +680,7 @@
     }
 
     private boolean assertXpush(ValueNode x) {
-        assert parser.parsingReplacement() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal));
+        assert parser.parsingIntrinsic() || (x == null || (x.getKind() != Kind.Void && x.getKind() != Kind.Illegal));
         return true;
     }
 
@@ -732,7 +759,7 @@
     private boolean assertPop(Kind kind) {
         assert kind != Kind.Void;
         ValueNode x = xpeek();
-        assert x != null && (parser.parsingReplacement() || x.getKind() == kind);
+        assert x != null && (parser.parsingIntrinsic() || x.getKind() == kind);
         return true;
     }
 
@@ -819,7 +846,7 @@
                 newStackSize--;
                 assert stack[newStackSize].getKind().needsTwoSlots();
             } else {
-                assert parser.parsingReplacement() || (stack[newStackSize].getKind().getSlotCount() == 1);
+                assert parser.parsingIntrinsic() || (stack[newStackSize].getKind().getSlotCount() == 1);
             }
             result[i] = stack[newStackSize];
         }
@@ -886,7 +913,7 @@
     }
 
     private boolean assertObject(ValueNode x) {
-        assert x != null && (parser.parsingReplacement() || (x.getKind() == Kind.Object));
+        assert x != null && (parser.parsingIntrinsic() || (x.getKind() == Kind.Object));
         return true;
     }
 
@@ -970,4 +997,24 @@
             }
         }
     }
+
+    @Override
+    public boolean isAfterSideEffect() {
+        return sideEffects != null;
+    }
+
+    @Override
+    public Iterable<StateSplit> sideEffects() {
+        return sideEffects;
+    }
+
+    @Override
+    public void addSideEffect(StateSplit sideEffect) {
+        assert sideEffect != null;
+        assert sideEffect.hasSideEffect();
+        if (sideEffects == null) {
+            sideEffects = new ArrayList<>(4);
+        }
+        sideEffects.add(sideEffect);
+    }
 }
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/IntrinsicContext.java	Tue May 12 20:55:48 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,127 +0,0 @@
-/*
- * Copyright (c) 2015, 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.java;
-
-import static com.oracle.graal.java.HIRFrameStateBuilder.*;
-
-import java.util.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.java.GraphBuilderPhase.Instance.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.java.*;
-
-/**
- * Context for a replacement being inlined as a compiler intrinsic. Deoptimization within a compiler
- * intrinsic must replay the intrinsified call. This context object retains the information required
- * to build a frame state denoting the JVM state just before the intrinsified call.
- */
-public class IntrinsicContext extends ReplacementContext {
-
-    /**
-     * BCI denoting an intrinsic is being parsed for inlining after the caller has been parsed.
-     */
-    public static final int POST_PARSE_INLINE_BCI = -1;
-
-    /**
-     * BCI denoting an intrinsic is the compilation root.
-     */
-    public static final int ROOT_COMPILATION_BCI = -2;
-
-    /**
-     * The arguments to the intrinsic.
-     */
-    ValueNode[] args;
-
-    /**
-     * The BCI of the intrinsified invocation, {@link #POST_PARSE_INLINE_BCI} or
-     * {@link #ROOT_COMPILATION_BCI}.
-     */
-    final int bci;
-
-    private FrameState stateBeforeCache;
-
-    public IntrinsicContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute, ValueNode[] args, int bci) {
-        super(method, substitute);
-        assert bci != POST_PARSE_INLINE_BCI || args == null;
-        this.args = args;
-        this.bci = bci;
-        assert !isCompilationRoot() || method.hasBytecodes() : "Cannot intrinsic for native or abstract method " + method.format("%H.%n(%p)");
-    }
-
-    @Override
-    public boolean isIntrinsic() {
-        return true;
-    }
-
-    public boolean isPostParseInlined() {
-        return bci == POST_PARSE_INLINE_BCI;
-    }
-
-    public boolean isCompilationRoot() {
-        return bci == ROOT_COMPILATION_BCI;
-    }
-
-    public FrameState getInvokeStateBefore(StructuredGraph graph, BytecodeParser parent) {
-        if (isCompilationRoot()) {
-            int maxLocals = method.getMaxLocals();
-            // The 'args' were initialized based on the intrinsic method but a
-            // frame state's 'locals' needs to have the same length as the frame
-            // state method's 'max_locals'.
-            ValueNode[] locals = maxLocals == args.length ? args : Arrays.copyOf(args, maxLocals);
-            ValueNode[] stack = EMPTY_ARRAY;
-            int stackSize = 0;
-            ValueNode[] locks = EMPTY_ARRAY;
-            List<MonitorIdNode> monitorIds = Collections.emptyList();
-            return graph.add(new FrameState(null, method, 0, locals, stack, stackSize, locks, monitorIds, false, false));
-        } else if (isPostParseInlined()) {
-            return graph.add(new FrameState(BytecodeFrame.BEFORE_BCI));
-        } else {
-            assert !parent.parsingReplacement() || parent.replacementContext instanceof IntrinsicContext;
-            if (stateBeforeCache == null) {
-
-                // Find the non-intrinsic ancestor calling the intrinsified method
-                BytecodeParser ancestor = parent;
-                while (ancestor.parsingReplacement()) {
-                    assert ancestor.replacementContext instanceof IntrinsicContext;
-                    ancestor = ancestor.getParent();
-                }
-                FrameState stateDuring = ancestor.getFrameState().create(ancestor.bci(), ancestor.getParent(), true);
-                stateBeforeCache = stateDuring.duplicateModifiedBeforeCall(bci, Kind.Void, args);
-            }
-            return stateBeforeCache;
-        }
-    }
-
-    @Override
-    IntrinsicContext asIntrinsic() {
-        return this;
-    }
-
-    @Override
-    public String toString() {
-        return "Intrinsic{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + ", bci: " + bci + (args == null ? "" : ", args: " + Arrays.toString(args)) +
-                        (stateBeforeCache == null ? "" : ", stateBefore: " + stateBeforeCache) + "}";
-    }
-}
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/ReplacementContext.java	Tue May 12 20:55:48 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,78 +0,0 @@
-/*
- * Copyright (c) 2015, 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.java;
-
-import com.oracle.graal.api.meta.*;
-import com.oracle.graal.api.replacements.*;
-import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement;
-
-/**
- * Information about a substitute method being parsed in lieu of an original method. This can happen
- * when a call to a {@link MethodSubstitution} is encountered or the root of compilation is a
- * {@link MethodSubstitution} or a snippet.
- */
-public class ReplacementContext implements Replacement {
-    /**
-     * The method being replaced.
-     */
-    final ResolvedJavaMethod method;
-
-    /**
-     * The replacement method.
-     */
-    final ResolvedJavaMethod replacement;
-
-    public ReplacementContext(ResolvedJavaMethod method, ResolvedJavaMethod substitute) {
-        this.method = method;
-        this.replacement = substitute;
-    }
-
-    public ResolvedJavaMethod getOriginalMethod() {
-        return method;
-    }
-
-    public ResolvedJavaMethod getReplacementMethod() {
-        return replacement;
-    }
-
-    public boolean isIntrinsic() {
-        return false;
-    }
-
-    /**
-     * Determines if a call within the compilation scope of a replacement represents a call to the
-     * original method.
-     */
-    public boolean isCallToOriginal(ResolvedJavaMethod targetMethod) {
-        return method.equals(targetMethod) || replacement.equals(targetMethod);
-    }
-
-    IntrinsicContext asIntrinsic() {
-        return null;
-    }
-
-    @Override
-    public String toString() {
-        return "Replacement{original: " + method.format("%H.%n(%p)") + ", replacement: " + replacement.format("%H.%n(%p)") + "}";
-    }
-}
--- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Math_sin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/lang/Math_sin.java	Tue May 12 20:56:04 2015 +0200
@@ -44,6 +44,21 @@
     }
 
     @Test
+    public void runFirst() throws Throwable {
+        /*
+         * Execute Double.isNaN enough times to create a profile indicating that the path returning
+         * false is never taken. Then compile and execute the test with a NaN value to test that
+         * deoptimization works in the case of an uncommon trap inlined into an intrinsic. Of
+         * course, this relies on Double.isNaN never having yet been called with NaN. if it has,
+         * this test is equivalent to run0.
+         */
+        for (int i = 0; i < 10000; i++) {
+            Double.isNaN(1D);
+        }
+        executeActual(getResolvedJavaMethod("test"), null, java.lang.Double.NaN);
+    }
+
+    @Test
     public void run0() throws Throwable {
         runTest("test", java.lang.Double.NaN);
     }
--- a/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/threads/Thread_isInterrupted04.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/threads/Thread_isInterrupted04.java	Tue May 12 20:56:04 2015 +0200
@@ -67,7 +67,7 @@
 
     }
 
-    @Test(timeout = 20000)
+    @Test
     public void run0() throws Throwable {
         runTest("test");
     }
--- a/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java	Tue May 12 20:56:04 2015 +0200
@@ -31,7 +31,6 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.graph.*;
-import com.oracle.graal.graph.spi.*;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.MethodIdMap.Receiver;
 import com.oracle.graal.jtt.*;
@@ -49,26 +48,26 @@
  */
 public abstract class LIRTest extends JTTTest {
 
-    protected abstract static class LIRTestSpecification {
+    public abstract static class LIRTestSpecification {
         private Value result;
 
-        void generate(LIRGeneratorTool gen, Value arg0) {
+        public void generate(LIRGeneratorTool gen, Value arg0) {
             defaultHandler(gen, arg0);
         }
 
-        void generate(LIRGeneratorTool gen, Value arg0, Value arg1) {
+        public void generate(LIRGeneratorTool gen, Value arg0, Value arg1) {
             defaultHandler(gen, arg0, arg1);
         }
 
-        void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2) {
+        public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2) {
             defaultHandler(gen, arg0, arg1, arg2);
         }
 
-        void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3) {
+        public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3) {
             defaultHandler(gen, arg0, arg1, arg2, arg3);
         }
 
-        void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) {
+        public void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) {
             defaultHandler(gen, arg0, arg1, arg2, arg3, arg4);
         }
 
@@ -142,7 +141,7 @@
     }
 
     @NodeInfo
-    private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable, Simplifiable {
+    private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable {
 
         public static final NodeClass<FloatingLIRTestNode> TYPE = NodeClass.create(FloatingLIRTestNode.class);
         @Input protected ValueNode opsNode;
@@ -165,13 +164,6 @@
         }
 
         @Override
-        public void simplify(SimplifierTool tool) {
-            if (tool.allUsagesAvailable() && getLIROpsNode().isConstant()) {
-                getLIROpsNode().asConstant();
-            }
-        }
-
-        @Override
         public void generate(NodeLIRBuilderTool gen) {
             LIRTestSpecification ops = getLIROperations();
             Stream<Value> v = values().stream().map(node -> gen.operand(node));
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Tue May 12 20:56:04 2015 +0200
@@ -25,9 +25,7 @@
 import static com.oracle.graal.api.code.CodeUtil.*;
 import static com.oracle.graal.api.code.ValueUtil.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
-import static com.oracle.graal.compiler.common.cfg.AbstractControlFlowGraph.*;
 import static com.oracle.graal.lir.LIRValueUtil.*;
-import static com.oracle.graal.lir.debug.LIRGenerationDebugContext.*;
 
 import java.util.*;
 
@@ -36,20 +34,15 @@
 import com.oracle.graal.compiler.common.*;
 import com.oracle.graal.compiler.common.alloc.*;
 import com.oracle.graal.compiler.common.cfg.*;
-import com.oracle.graal.compiler.common.util.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.lir.LIRInstruction.OperandFlag;
 import com.oracle.graal.lir.LIRInstruction.OperandMode;
-import com.oracle.graal.lir.StandardOp.LabelOp;
-import com.oracle.graal.lir.StandardOp.MoveOp;
 import com.oracle.graal.lir.alloc.lsra.Interval.RegisterBinding;
-import com.oracle.graal.lir.alloc.lsra.Interval.RegisterPriority;
-import com.oracle.graal.lir.alloc.lsra.Interval.SpillState;
 import com.oracle.graal.lir.framemap.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
+import com.oracle.graal.lir.phases.AllocationPhase.AllocationContext;
 import com.oracle.graal.options.*;
 
 /**
@@ -60,19 +53,18 @@
  */
 class LinearScan {
 
-    final TargetDescription target;
     final LIRGenerationResult res;
     final LIR ir;
     final FrameMapBuilder frameMapBuilder;
-    final SpillMoveFactory spillMoveFactory;
     final RegisterAttributes[] registerAttributes;
     final Register[] registers;
     final RegisterAllocationConfig regAllocConfig;
+    private final SpillMoveFactory moveFactory;
 
     final boolean callKillsRegisters;
 
     public static final int DOMINATOR_SPILL_MOVE_ID = -2;
-    private static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1;
+    static final int SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT = 1;
 
     public static class Options {
         // @formatter:off
@@ -113,52 +105,45 @@
         public BitSet liveKill;
     }
 
-    final BlockMap<BlockData> blockData;
+    private final BlockMap<BlockData> blockData;
 
     /**
      * List of blocks in linear-scan order. This is only correct as long as the CFG does not change.
      */
     final List<? extends AbstractBlockBase<?>> sortedBlocks;
 
-    /**
-     * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals.
-     */
-    Interval[] intervals;
+    /** @see #intervals() */
+    private Interval[] intervals;
 
     /**
      * The number of valid entries in {@link #intervals}.
      */
-    int intervalsSize;
+    private int intervalsSize;
 
     /**
      * The index of the first entry in {@link #intervals} for a
      * {@linkplain #createDerivedInterval(Interval) derived interval}.
      */
-    int firstDerivedIntervalIndex = -1;
+    private int firstDerivedIntervalIndex = -1;
 
     /**
      * Intervals sorted by {@link Interval#from()}.
      */
-    Interval[] sortedIntervals;
+    private Interval[] sortedIntervals;
 
     /**
      * Map from an instruction {@linkplain LIRInstruction#id id} to the instruction. Entries should
      * be retrieved with {@link #instructionForId(int)} as the id is not simply an index into this
      * array.
      */
-    LIRInstruction[] opIdToInstructionMap;
+    private LIRInstruction[] opIdToInstructionMap;
 
     /**
      * Map from an instruction {@linkplain LIRInstruction#id id} to the
      * {@linkplain AbstractBlockBase block} containing the instruction. Entries should be retrieved
      * with {@link #blockForId(int)} as the id is not simply an index into this array.
      */
-    AbstractBlockBase<?>[] opIdToBlockMap;
-
-    /**
-     * Bit set for each variable that is contained in each loop.
-     */
-    BitMap2D intervalInLoop;
+    private AbstractBlockBase<?>[] opIdToBlockMap;
 
     /**
      * The {@linkplain #operandNumber(Value) number} of the first variable operand allocated.
@@ -166,10 +151,9 @@
     private final int firstVariableNumber;
 
     LinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) {
-        this.target = target;
         this.res = res;
         this.ir = res.getLIR();
-        this.spillMoveFactory = spillMoveFactory;
+        this.moveFactory = spillMoveFactory;
         this.frameMapBuilder = res.getFrameMapBuilder();
         this.sortedBlocks = ir.linearScanOrder();
         this.registerAttributes = regAllocConfig.getRegisterConfig().getAttributesMap();
@@ -179,8 +163,10 @@
         this.firstVariableNumber = registers.length;
         this.blockData = new BlockMap<>(ir.getControlFlowGraph());
 
-        // If all allocatable registers are caller saved, then no registers are live across a call
-        // site. The register allocator can save time not trying to find a register at a call site.
+        /*
+         * If all allocatable registers are caller saved, then no registers are live across a call
+         * site. The register allocator can save time not trying to find a register at a call site.
+         */
         this.callKillsRegisters = regAllocConfig.getRegisterConfig().areAllAllocatableRegistersCallerSaved();
     }
 
@@ -198,7 +184,7 @@
     }
 
     SpillMoveFactory getSpillMoveFactory() {
-        return spillMoveFactory;
+        return moveFactory;
     }
 
     protected MoveResolver createMoveResolver() {
@@ -216,7 +202,7 @@
      * the {@linkplain Variable variables} and {@linkplain RegisterValue registers} being processed
      * by this allocator.
      */
-    private int operandNumber(Value operand) {
+    int operandNumber(Value operand) {
         if (isRegister(operand)) {
             int number = asRegister(operand).number;
             assert number < firstVariableNumber;
@@ -229,7 +215,7 @@
     /**
      * Gets the number of operands. This value will increase by 1 for new variable.
      */
-    private int operandSize() {
+    int operandSize() {
         return firstVariableNumber + ir.numVariables();
     }
 
@@ -240,6 +226,14 @@
         return firstVariableNumber - 1;
     }
 
+    BlockData getBlockData(AbstractBlockBase<?> block) {
+        return blockData.get(block);
+    }
+
+    void initBlockData(AbstractBlockBase<?> block) {
+        blockData.put(block, new BlockData());
+    }
+
     static final IntervalPredicate IS_PRECOLORED_INTERVAL = new IntervalPredicate() {
 
         @Override
@@ -273,8 +267,10 @@
     }
 
     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
+        /*
+         * 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.canMaterialize()) {
             interval.assignLocation(Value.ILLEGAL);
         } else if (interval.spillSlot() != null) {
@@ -287,6 +283,18 @@
     }
 
     /**
+     * Map from {@linkplain #operandNumber(Value) operand numbers} to intervals.
+     */
+    Interval[] intervals() {
+        return intervals;
+    }
+
+    void initIntervals() {
+        intervalsSize = operandSize();
+        intervals = new Interval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)];
+    }
+
+    /**
      * Creates a new interval.
      *
      * @param operand the operand for the interval
@@ -345,10 +353,6 @@
         return ir.getControlFlowGraph().getLoops().size();
     }
 
-    boolean isIntervalInLoop(int interval, int loop) {
-        return intervalInLoop.at(interval, loop);
-    }
-
     Interval intervalFor(int operandNumber) {
         return intervals[operandNumber];
     }
@@ -368,6 +372,16 @@
         }
     }
 
+    void initOpIdMaps(int numInstructions) {
+        opIdToInstructionMap = new LIRInstruction[numInstructions];
+        opIdToBlockMap = new AbstractBlockBase<?>[numInstructions];
+    }
+
+    void putOpIdMaps(int index, LIRInstruction op, AbstractBlockBase<?> block) {
+        opIdToInstructionMap[index] = op;
+        opIdToBlockMap[index] = block;
+    }
+
     /**
      * Gets the highest instruction id allocated by this object.
      */
@@ -378,10 +392,10 @@
 
     /**
      * Converts an {@linkplain LIRInstruction#id instruction id} to an instruction index. All LIR
-     * instructions in a method have an index one greater than their linear-scan order predecesor
+     * instructions in a method have an index one greater than their linear-scan order predecessor
      * with the first instruction having an index of 0.
      */
-    static int opIdToIndex(int opId) {
+    private static int opIdToIndex(int opId) {
         return opId >> 1;
     }
 
@@ -429,909 +443,15 @@
         return instructionForId(opId).destroysCallerSavedRegisters();
     }
 
-    /**
-     * Eliminates moves from register to stack if the stack slot is known to be correct.
-     */
-    void changeSpillDefinitionPos(Interval interval, int defPos) {
-        assert interval.isSplitParent() : "can only be called for split parents";
-
-        switch (interval.spillState()) {
-            case NoDefinitionFound:
-                assert interval.spillDefinitionPos() == -1 : "must no be set before";
-                interval.setSpillDefinitionPos(defPos);
-                interval.setSpillState(SpillState.NoSpillStore);
-                break;
-
-            case NoSpillStore:
-                assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created";
-                if (defPos < interval.spillDefinitionPos() - 2) {
-                    // second definition found, so no spill optimization possible for this interval
-                    interval.setSpillState(SpillState.NoOptimization);
-                } else {
-                    // two consecutive definitions (because of two-operand LIR form)
-                    assert blockForId(defPos) == blockForId(interval.spillDefinitionPos()) : "block must be equal";
-                }
-                break;
-
-            case NoOptimization:
-                // nothing to do
-                break;
-
-            default:
-                throw new BailoutException("other states not allowed at this time");
-        }
-    }
-
-    // called during register allocation
-    void changeSpillState(Interval interval, int spillPos) {
-        switch (interval.spillState()) {
-            case NoSpillStore: {
-                int defLoopDepth = blockForId(interval.spillDefinitionPos()).getLoopDepth();
-                int spillLoopDepth = blockForId(spillPos).getLoopDepth();
-
-                if (defLoopDepth < spillLoopDepth) {
-                    // the loop depth of the spilling position is higher then the loop depth
-                    // at the definition of the interval . move write to memory out of loop.
-                    if (Options.LSRAOptimizeSpillPosition.getValue()) {
-                        // find best spill position in dominator the tree
-                        interval.setSpillState(SpillState.SpillInDominator);
-                    } else {
-                        // store at definition of the interval
-                        interval.setSpillState(SpillState.StoreAtDefinition);
-                    }
-                } else {
-                    // the interval is currently spilled only once, so for now there is no
-                    // reason to store the interval at the definition
-                    interval.setSpillState(SpillState.OneSpillStore);
-                }
-                break;
-            }
-
-            case OneSpillStore: {
-                if (Options.LSRAOptimizeSpillPosition.getValue()) {
-                    // the interval is spilled more then once
-                    interval.setSpillState(SpillState.SpillInDominator);
-                } else {
-                    // it is better to store it to
-                    // memory at the definition
-                    interval.setSpillState(SpillState.StoreAtDefinition);
-                }
-                break;
-            }
-
-            case SpillInDominator:
-            case StoreAtDefinition:
-            case StartInMemory:
-            case NoOptimization:
-            case NoDefinitionFound:
-                // nothing to do
-                break;
-
-            default:
-                throw new BailoutException("other states not allowed at this time");
-        }
-    }
-
     abstract static class IntervalPredicate {
 
         abstract boolean apply(Interval i);
     }
 
-    private static final IntervalPredicate mustStoreAtDefinition = new IntervalPredicate() {
-
-        @Override
-        public boolean apply(Interval i) {
-            return i.isSplitParent() && i.spillState() == SpillState.StoreAtDefinition;
-        }
-    };
-
-    /**
-     * @return the index of the first instruction that is of interest for
-     *         {@link #eliminateSpillMoves()}
-     */
-    protected int firstInstructionOfInterest() {
-        // skip the first because it is always a label
-        return 1;
-    }
-
-    // called once before assignment of register numbers
-    void eliminateSpillMoves() {
-        try (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
-            Interval interval;
-            interval = createUnhandledLists(mustStoreAtDefinition, null).first;
-            if (DetailedAsserts.getValue()) {
-                checkIntervals(interval);
-            }
-
-            LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
-            for (AbstractBlockBase<?> block : sortedBlocks) {
-                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
-                    List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-                    int numInst = instructions.size();
-
-                    // iterate all instructions of the block.
-                    for (int j = firstInstructionOfInterest(); j < numInst; j++) {
-                        LIRInstruction op = instructions.get(j);
-                        int opId = op.id();
-
-                        if (opId == -1) {
-                            MoveOp move = (MoveOp) op;
-                            /*
-                             * Remove move from register to stack if the stack slot is guaranteed to
-                             * be correct. Only moves that have been inserted by LinearScan can be
-                             * removed.
-                             */
-                            if (canEliminateSpillMove(block, move)) {
-                                /*
-                                 * Move target is a stack slot that is always correct, so eliminate
-                                 * instruction.
-                                 */
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", operandNumber(move.getInput()), move.getInput(), operandNumber(move.getResult()),
-                                                    move.getResult(), block);
-                                }
-
-                                // null-instructions are deleted by assignRegNum
-                                instructions.set(j, null);
-                            }
-
-                        } else {
-                            /*
-                             * Insert move from register to stack just after the beginning of the
-                             * interval.
-                             */
-                            assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order";
-                            assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval";
-
-                            while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) {
-                                if (!interval.canMaterialize()) {
-                                    if (!insertionBuffer.initialized()) {
-                                        /*
-                                         * prepare insertion buffer (appended when all instructions
-                                         * in the block are processed)
-                                         */
-                                        insertionBuffer.init(instructions);
-                                    }
-
-                                    AllocatableValue fromLocation = interval.location();
-                                    AllocatableValue toLocation = canonicalSpillOpr(interval);
-                                    if (!fromLocation.equals(toLocation)) {
-
-                                        assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" +
-                                                        interval.spillState();
-                                        assert isStackSlotValue(toLocation) : "to operand must be a stack slot";
-
-                                        LIRInstruction move = getSpillMoveFactory().createMove(toLocation, fromLocation);
-                                        insertionBuffer.append(j + 1, move);
-
-                                        if (Debug.isLogEnabled()) {
-                                            Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
-                                        }
-                                    }
-                                }
-                                interval = interval.next;
-                            }
-                        }
-                    } // end of instruction iteration
-
-                    if (insertionBuffer.initialized()) {
-                        insertionBuffer.finish();
-                    }
-                }
-            } // end of block iteration
-
-            assert interval == Interval.EndMarker : "missed an interval";
-        }
-    }
-
-    /**
-     * @param block The block {@code move} is located in.
-     * @param move Spill move.
-     */
-    protected boolean canEliminateSpillMove(AbstractBlockBase<?> block, MoveOp move) {
-        assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move;
-
-        Interval curInterval = intervalFor(move.getResult());
-
-        if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) {
-            assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location();
-            return true;
-        }
-        return false;
-    }
-
-    private static void checkIntervals(Interval interval) {
-        Interval prev = null;
-        Interval temp = interval;
-        while (temp != Interval.EndMarker) {
-            assert temp.spillDefinitionPos() > 0 : "invalid spill definition pos";
-            if (prev != null) {
-                assert temp.from() >= prev.from() : "intervals not sorted";
-                assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from :  then they must also be sorted by spillDefinitionPos";
-            }
-
-            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 (Debug.isLogEnabled()) {
-                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;
-        }
-    }
-
-    /**
-     * Numbers all instructions in all blocks. The numbering follows the
-     * {@linkplain ComputeBlockOrder linear scan order}.
-     */
-    void numberInstructions() {
-
-        intervalsSize = operandSize();
-        intervals = new Interval[intervalsSize + (intervalsSize >> SPLIT_INTERVALS_CAPACITY_RIGHT_SHIFT)];
-
-        ValueConsumer setVariableConsumer = (value, mode, flags) -> {
-            if (isVariable(value)) {
-                getOrCreateInterval(asVariable(value));
-            }
-        };
-
-        // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node.
-        int numInstructions = 0;
-        for (AbstractBlockBase<?> block : sortedBlocks) {
-            numInstructions += ir.getLIRforBlock(block).size();
-        }
-
-        // initialize with correct length
-        opIdToInstructionMap = new LIRInstruction[numInstructions];
-        opIdToBlockMap = new AbstractBlockBase<?>[numInstructions];
-
-        int opId = 0;
-        int index = 0;
-        for (AbstractBlockBase<?> block : sortedBlocks) {
-            blockData.put(block, new BlockData());
-
-            List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-
-            int numInst = instructions.size();
-            for (int j = 0; j < numInst; j++) {
-                LIRInstruction op = instructions.get(j);
-                op.setId(opId);
-
-                opIdToInstructionMap[index] = op;
-                opIdToBlockMap[index] = block;
-                assert instructionForId(opId) == op : "must match";
-
-                op.visitEachTemp(setVariableConsumer);
-                op.visitEachOutput(setVariableConsumer);
-
-                index++;
-                opId += 2; // numbering of lirOps by two
-            }
-        }
-        assert index == numInstructions : "must match";
-        assert (index << 1) == opId : "must match: " + (index << 1);
-    }
-
-    /**
-     * Computes local live sets (i.e. {@link BlockData#liveGen} and {@link BlockData#liveKill})
-     * separately for each block.
-     */
-    void computeLocalLiveSets() {
-        int liveSize = liveSetSize();
-
-        intervalInLoop = new BitMap2D(operandSize(), numLoops());
-
-        // iterate all blocks
-        for (final AbstractBlockBase<?> block : sortedBlocks) {
-            try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) {
-
-                final BitSet liveGen = new BitSet(liveSize);
-                final BitSet liveKill = new BitSet(liveSize);
-
-                List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-                int numInst = instructions.size();
-
-                ValueConsumer useConsumer = (operand, mode, flags) -> {
-                    if (isVariable(operand)) {
-                        int operandNum = operandNumber(operand);
-                        if (!liveKill.get(operandNum)) {
-                            liveGen.set(operandNum);
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen for operand %d(%s)", operandNum, operand);
-                            }
-                        }
-                        if (block.getLoop() != null) {
-                            intervalInLoop.setBit(operandNum, block.getLoop().getIndex());
-                        }
-                    }
-
-                    if (DetailedAsserts.getValue()) {
-                        verifyInput(block, liveKill, operand);
-                    }
-                };
-                ValueConsumer stateConsumer = (operand, mode, flags) -> {
-                    if (isVariableOrRegister(operand)) {
-                        int operandNum = operandNumber(operand);
-                        if (!liveKill.get(operandNum)) {
-                            liveGen.set(operandNum);
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
-                            }
-                        }
-                    }
-                };
-                ValueConsumer defConsumer = (operand, mode, flags) -> {
-                    if (isVariable(operand)) {
-                        int varNum = operandNumber(operand);
-                        liveKill.set(varNum);
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("liveKill for operand %d(%s)", varNum, operand);
-                        }
-                        if (block.getLoop() != null) {
-                            intervalInLoop.setBit(varNum, block.getLoop().getIndex());
-                        }
-                    }
-
-                    if (DetailedAsserts.getValue()) {
-                        // fixed intervals are never live at block boundaries, so
-                        // they need not be processed in live sets
-                        // process them only in debug mode so that this can be checked
-                        verifyTemp(liveKill, operand);
-                    }
-                };
-
-                // iterate all instructions of the block
-                for (int j = 0; j < numInst; j++) {
-                    final LIRInstruction op = instructions.get(j);
-
-                    try (Indent indent2 = Debug.logAndIndent("handle op %d", op.id())) {
-                        op.visitEachInput(useConsumer);
-                        op.visitEachAlive(useConsumer);
-                        // Add uses of live locals from interpreter's point of view for proper debug
-                        // information generation
-                        op.visitEachState(stateConsumer);
-                        op.visitEachTemp(defConsumer);
-                        op.visitEachOutput(defConsumer);
-                    }
-                } // end of instruction iteration
-
-                BlockData blockSets = blockData.get(block);
-                blockSets.liveGen = liveGen;
-                blockSets.liveKill = liveKill;
-                blockSets.liveIn = new BitSet(liveSize);
-                blockSets.liveOut = new BitSet(liveSize);
-
-                if (Debug.isLogEnabled()) {
-                    Debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
-                    Debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
-                }
-
-            }
-        } // end of block iteration
-    }
-
-    private void verifyTemp(BitSet liveKill, Value operand) {
-        // fixed intervals are never live at block boundaries, so
-        // they need not be processed in live sets
-        // process them only in debug mode so that this can be checked
-        if (isRegister(operand)) {
-            if (isProcessed(operand)) {
-                liveKill.set(operandNumber(operand));
-            }
-        }
-    }
-
-    private void verifyInput(AbstractBlockBase<?> block, BitSet liveKill, Value operand) {
-        // fixed intervals are never live at block boundaries, so
-        // they need not be processed in live sets.
-        // this is checked by these assertions to be sure about it.
-        // the entry block may have incoming
-        // values in registers, which is ok.
-        if (isRegister(operand) && block != ir.getControlFlowGraph().getStartBlock()) {
-            if (isProcessed(operand)) {
-                assert liveKill.get(operandNumber(operand)) : "using fixed register that is not defined in this block";
-            }
-        }
-    }
-
-    /**
-     * Performs a backward dataflow analysis to compute global live sets (i.e.
-     * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block.
-     */
-    void computeGlobalLiveSets() {
-        try (Indent indent = Debug.logAndIndent("compute global live sets")) {
-            int numBlocks = blockCount();
-            boolean changeOccurred;
-            boolean changeOccurredInBlock;
-            int iterationCount = 0;
-            BitSet liveOut = new BitSet(liveSetSize()); // scratch set for calculations
-
-            // Perform a backward dataflow analysis to compute liveOut and liveIn for each block.
-            // The loop is executed until a fixpoint is reached (no changes in an iteration)
-            do {
-                changeOccurred = false;
-
-                try (Indent indent2 = Debug.logAndIndent("new iteration %d", iterationCount)) {
-
-                    // iterate all blocks in reverse order
-                    for (int i = numBlocks - 1; i >= 0; i--) {
-                        AbstractBlockBase<?> block = blockAt(i);
-                        BlockData blockSets = blockData.get(block);
-
-                        changeOccurredInBlock = false;
-
-                        // liveOut(block) is the union of liveIn(sux), for successors sux of block
-                        int n = block.getSuccessorCount();
-                        if (n > 0) {
-                            liveOut.clear();
-                            // block has successors
-                            if (n > 0) {
-                                for (AbstractBlockBase<?> successor : block.getSuccessors()) {
-                                    liveOut.or(blockData.get(successor).liveIn);
-                                }
-                            }
-
-                            if (!blockSets.liveOut.equals(liveOut)) {
-                                // A change occurred. Swap the old and new live out
-                                // sets to avoid copying.
-                                BitSet temp = blockSets.liveOut;
-                                blockSets.liveOut = liveOut;
-                                liveOut = temp;
-
-                                changeOccurred = true;
-                                changeOccurredInBlock = true;
-                            }
-                        }
-
-                        if (iterationCount == 0 || changeOccurredInBlock) {
-                            // liveIn(block) is the union of liveGen(block) with (liveOut(block) &
-                            // !liveKill(block))
-                            // note: liveIn has to be computed only in first iteration
-                            // or if liveOut has changed!
-                            BitSet liveIn = blockSets.liveIn;
-                            liveIn.clear();
-                            liveIn.or(blockSets.liveOut);
-                            liveIn.andNot(blockSets.liveKill);
-                            liveIn.or(blockSets.liveGen);
-
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
-                            }
-                        }
-                    }
-                    iterationCount++;
-
-                    if (changeOccurred && iterationCount > 50) {
-                        throw new BailoutException("too many iterations in computeGlobalLiveSets");
-                    }
-                }
-            } while (changeOccurred);
-
-            if (DetailedAsserts.getValue()) {
-                verifyLiveness();
-            }
-
-            // check that the liveIn set of the first block is empty
-            AbstractBlockBase<?> startBlock = ir.getControlFlowGraph().getStartBlock();
-            if (blockData.get(startBlock).liveIn.cardinality() != 0) {
-                if (DetailedAsserts.getValue()) {
-                    reportFailure(numBlocks);
-                }
-                // bailout if this occurs in product mode.
-                throw new GraalInternalError("liveIn set of first block must be empty: " + blockData.get(startBlock).liveIn);
-            }
-        }
-    }
-
-    private void reportFailure(int numBlocks) {
-        try (Scope s = Debug.forceLog()) {
-            try (Indent indent = Debug.logAndIndent("report failure")) {
-
-                BitSet startBlockLiveIn = blockData.get(ir.getControlFlowGraph().getStartBlock()).liveIn;
-                try (Indent indent2 = Debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) {
-                    for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
-                        Interval interval = intervalFor(operandNum);
-                        if (interval != null) {
-                            Value operand = interval.operand;
-                            Debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(operand));
-                        } else {
-                            Debug.log("var %d; missing operand", operandNum);
-                        }
-                    }
-                }
-
-                // print some additional information to simplify debugging
-                for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
-                    Interval interval = intervalFor(operandNum);
-                    Value operand = null;
-                    Object valueForOperandFromDebugContext = null;
-                    if (interval != null) {
-                        operand = interval.operand;
-                        valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(operand);
-                    }
-                    try (Indent indent2 = Debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) {
-
-                        Deque<AbstractBlockBase<?>> definedIn = new ArrayDeque<>();
-                        HashSet<AbstractBlockBase<?>> usedIn = new HashSet<>();
-                        for (AbstractBlockBase<?> block : sortedBlocks) {
-                            if (blockData.get(block).liveGen.get(operandNum)) {
-                                usedIn.add(block);
-                                try (Indent indent3 = Debug.logAndIndent("used in block B%d", block.getId())) {
-                                    for (LIRInstruction ins : ir.getLIRforBlock(block)) {
-                                        try (Indent indent4 = Debug.logAndIndent("%d: %s", ins.id(), ins)) {
-                                            ins.forEachState((liveStateOperand, mode, flags) -> {
-                                                Debug.log("operand=%s", liveStateOperand);
-                                                return liveStateOperand;
-                                            });
-                                        }
-                                    }
-                                }
-                            }
-                            if (blockData.get(block).liveKill.get(operandNum)) {
-                                definedIn.add(block);
-                                try (Indent indent3 = Debug.logAndIndent("defined in block B%d", block.getId())) {
-                                    for (LIRInstruction ins : ir.getLIRforBlock(block)) {
-                                        Debug.log("%d: %s", ins.id(), ins);
-                                    }
-                                }
-                            }
-                        }
-
-                        int[] hitCount = new int[numBlocks];
-
-                        while (!definedIn.isEmpty()) {
-                            AbstractBlockBase<?> block = definedIn.removeFirst();
-                            usedIn.remove(block);
-                            for (AbstractBlockBase<?> successor : block.getSuccessors()) {
-                                if (successor.isLoopHeader()) {
-                                    if (!block.isLoopEnd()) {
-                                        definedIn.add(successor);
-                                    }
-                                } else {
-                                    if (++hitCount[successor.getId()] == successor.getPredecessorCount()) {
-                                        definedIn.add(successor);
-                                    }
-                                }
-                            }
-                        }
-                        try (Indent indent3 = Debug.logAndIndent("**** offending usages are in: ")) {
-                            for (AbstractBlockBase<?> block : usedIn) {
-                                Debug.log("B%d", block.getId());
-                            }
-                        }
-                    }
-                }
-            }
-        } catch (Throwable e) {
-            throw Debug.handle(e);
-        }
-    }
-
-    private void verifyLiveness() {
-        // check that fixed intervals are not live at block boundaries
-        // (live set must be empty at fixed intervals)
-        for (AbstractBlockBase<?> block : sortedBlocks) {
-            for (int j = 0; j <= maxRegisterNumber(); j++) {
-                assert !blockData.get(block).liveIn.get(j) : "liveIn  set of fixed register must be empty";
-                assert !blockData.get(block).liveOut.get(j) : "liveOut set of fixed register must be empty";
-                assert !blockData.get(block).liveGen.get(j) : "liveGen set of fixed register must be empty";
-            }
-        }
-    }
-
-    void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) {
-        if (!isProcessed(operand)) {
-            return;
-        }
-
-        Interval interval = getOrCreateInterval(operand);
-        if (!kind.equals(LIRKind.Illegal)) {
-            interval.setKind(kind);
-        }
-
-        interval.addRange(from, to);
-
-        // Register use position at even instruction id.
-        interval.addUsePos(to & ~1, registerPriority);
-
-        if (Debug.isLogEnabled()) {
-            Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name());
-        }
-    }
-
-    void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) {
-        if (!isProcessed(operand)) {
-            return;
-        }
-
-        Interval interval = getOrCreateInterval(operand);
-        if (!kind.equals(LIRKind.Illegal)) {
-            interval.setKind(kind);
-        }
-
-        interval.addRange(tempPos, tempPos + 1);
-        interval.addUsePos(tempPos, registerPriority);
-        interval.addMaterializationValue(null);
-
-        if (Debug.isLogEnabled()) {
-            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, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) {
-        if (!isProcessed(operand)) {
-            return;
-        }
-        int defPos = op.id();
-
-        Interval interval = getOrCreateInterval(operand);
-        if (!kind.equals(LIRKind.Illegal)) {
-            interval.setKind(kind);
-        }
-
-        Range r = interval.first();
-        if (r.from <= defPos) {
-            // Update the starting point (when a range is first created for a use, its
-            // start is the beginning of the current block until a def is encountered.)
-            r.from = defPos;
-            interval.addUsePos(defPos, registerPriority);
-
-        } else {
-            // Dead value - make vacuous interval
-            // also add register priority for dead intervals
-            interval.addRange(defPos, defPos + 1);
-            interval.addUsePos(defPos, registerPriority);
-            if (Debug.isLogEnabled()) {
-                Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
-            }
-        }
-
-        changeSpillDefinitionPos(interval, defPos);
-        if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) {
-            // detection of method-parameters and roundfp-results
-            interval.setSpillState(SpillState.StartInMemory);
-        }
-        interval.addMaterializationValue(LinearScan.getMaterializedValue(op, operand, interval));
-
-        if (Debug.isLogEnabled()) {
-            Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
-        }
-    }
-
-    /**
-     * Determines the register priority for an instruction's output/result operand.
-     */
-    static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) {
-        if (op instanceof MoveOp) {
-            MoveOp move = (MoveOp) op;
-            if (optimizeMethodArgument(move.getInput())) {
-                return RegisterPriority.None;
-            }
-        } else if (op instanceof LabelOp) {
-            LabelOp label = (LabelOp) op;
-            if (label.isPhiIn()) {
-                return RegisterPriority.None;
-            }
-        }
-
-        // all other operands require a register
-        return RegisterPriority.MustHaveRegister;
-    }
-
-    /**
-     * Determines the priority which with an instruction's input operand will be allocated a
-     * register.
-     */
-    static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) {
-        if (flags.contains(OperandFlag.STACK)) {
-            return RegisterPriority.ShouldHaveRegister;
-        }
-        // all other operands require a register
-        return RegisterPriority.MustHaveRegister;
-    }
-
-    private static boolean optimizeMethodArgument(Value value) {
-        /*
-         * Object method arguments that are passed on the stack are currently not optimized because
-         * this requires that the runtime visits method arguments during stack walking.
-         */
-        return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getKind() != Kind.Object;
-    }
-
-    /**
-     * Optimizes moves related to incoming stack based arguments. The interval for the destination
-     * of such moves is assigned the stack slot (which is in the caller's frame) as its spill slot.
-     */
-    void handleMethodArguments(LIRInstruction op) {
-        if (op instanceof MoveOp) {
-            MoveOp move = (MoveOp) op;
-            if (optimizeMethodArgument(move.getInput())) {
-                StackSlot slot = asStackSlot(move.getInput());
-                if (DetailedAsserts.getValue()) {
-                    assert op.id() > 0 : "invalid id";
-                    assert blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block";
-                    assert isVariable(move.getResult()) : "result of move must be a variable";
-
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("found move from stack slot %s to %s", slot, move.getResult());
-                    }
-                }
-
-                Interval interval = intervalFor(move.getResult());
-                interval.setSpillSlot(slot);
-                interval.assignLocation(slot);
-            }
-        }
-    }
-
-    void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
-        if (flags.contains(OperandFlag.HINT) && isVariableOrRegister(targetValue)) {
-
-            op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> {
-                if (isVariableOrRegister(registerHint)) {
-                    Interval from = getOrCreateInterval((AllocatableValue) registerHint);
-                    Interval to = getOrCreateInterval((AllocatableValue) targetValue);
-
-                    /* hints always point from def to use */
-                    if (hintAtDef) {
-                        to.setLocationHint(from);
-                    } else {
-                        from.setLocationHint(to);
-                    }
-                    if (Debug.isLogEnabled()) {
-                        Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber);
-                    }
-
-                    return registerHint;
-                }
-                return null;
-            });
-        }
-    }
-
-    void buildIntervals() {
-
-        try (Indent indent = Debug.logAndIndent("build intervals")) {
-            InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> {
-                if (isVariableOrRegister(operand)) {
-                    addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind());
-                    addRegisterHint(op, operand, mode, flags, true);
-                }
-            };
-
-            InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> {
-                if (isVariableOrRegister(operand)) {
-                    addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind());
-                    addRegisterHint(op, operand, mode, flags, false);
-                }
-            };
-
-            InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> {
-                if (isVariableOrRegister(operand)) {
-                    RegisterPriority p = registerPriorityOfInputOperand(flags);
-                    int opId = op.id();
-                    int blockFrom = getFirstLirInstructionId((blockForId(opId)));
-                    addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind());
-                    addRegisterHint(op, operand, mode, flags, false);
-                }
-            };
-
-            InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> {
-                if (isVariableOrRegister(operand)) {
-                    int opId = op.id();
-                    int blockFrom = getFirstLirInstructionId((blockForId(opId)));
-                    RegisterPriority p = registerPriorityOfInputOperand(flags);
-                    addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind());
-                    addRegisterHint(op, operand, mode, flags, false);
-                }
-            };
-
-            InstructionValueConsumer stateProc = (op, operand, mode, flags) -> {
-                if (isVariableOrRegister(operand)) {
-                    int opId = op.id();
-                    int blockFrom = getFirstLirInstructionId((blockForId(opId)));
-                    addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind());
-                }
-            };
-
-            // create a list with all caller-save registers (cpu, fpu, xmm)
-            Register[] callerSaveRegs = regAllocConfig.getRegisterConfig().getCallerSaveRegisters();
-
-            // iterate all blocks in reverse order
-            for (int i = blockCount() - 1; i >= 0; i--) {
-
-                AbstractBlockBase<?> block = blockAt(i);
-                try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) {
-
-                    List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-                    final int blockFrom = getFirstLirInstructionId(block);
-                    int blockTo = getLastLirInstructionId(block);
-
-                    assert blockFrom == instructions.get(0).id();
-                    assert blockTo == instructions.get(instructions.size() - 1).id();
-
-                    // Update intervals for operands live at the end of this block;
-                    BitSet live = blockData.get(block).liveOut;
-                    for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) {
-                        assert live.get(operandNum) : "should not stop here otherwise";
-                        AllocatableValue operand = intervalFor(operandNum).operand;
-                        if (Debug.isLogEnabled()) {
-                            Debug.log("live in %d: %s", operandNum, operand);
-                        }
-
-                        addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, LIRKind.Illegal);
-
-                        // add special use positions for loop-end blocks when the
-                        // interval is used anywhere inside this loop. It's possible
-                        // that the block was part of a non-natural loop, so it might
-                        // have an invalid loop index.
-                        if (block.isLoopEnd() && block.getLoop() != null && isIntervalInLoop(operandNum, block.getLoop().getIndex())) {
-                            intervalFor(operandNum).addUsePos(blockTo + 1, RegisterPriority.LiveAtLoopEnd);
-                        }
-                    }
-
-                    // iterate all instructions of the block in reverse order.
-                    // definitions of intervals are processed before uses
-                    for (int j = instructions.size() - 1; j >= 0; j--) {
-                        final LIRInstruction op = instructions.get(j);
-                        final int opId = op.id();
-
-                        try (Indent indent3 = Debug.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) {
-                                    if (attributes(r).isAllocatable()) {
-                                        addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal);
-                                    }
-                                }
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("operation destroys all caller-save registers");
-                                }
-                            }
-
-                            op.visitEachOutput(outputConsumer);
-                            op.visitEachTemp(tempConsumer);
-                            op.visitEachAlive(aliveConsumer);
-                            op.visitEachInput(inputConsumer);
-
-                            // Add uses of live locals from interpreter's point of view for proper
-                            // debug information generation
-                            // Treat these operands as temp values (if the live range is extended
-                            // to a call site, the value would be in a register at
-                            // the call otherwise)
-                            op.visitEachState(stateProc);
-
-                            // special steps for some instructions (especially moves)
-                            handleMethodArguments(op);
-
-                        }
-
-                    } // end of instruction iteration
-                }
-            } // end of block iteration
-
-            // add the range [0, 1] to all fixed intervals.
-            // the register allocator need not handle unhandled fixed intervals
-            for (Interval interval : intervals) {
-                if (interval != null && isRegister(interval.operand)) {
-                    interval.addRange(0, 1);
-                }
-            }
-        }
-    }
-
     // * Phase 5: actual register allocation
 
     private static boolean isSorted(Interval[] intervals) {
@@ -1461,30 +581,6 @@
         sortedIntervals = combinedList;
     }
 
-    void allocateRegisters() {
-        try (Indent indent = Debug.logAndIndent("allocate registers")) {
-            Interval precoloredIntervals;
-            Interval notPrecoloredIntervals;
-
-            Interval.Pair result = createUnhandledLists(IS_PRECOLORED_INTERVAL, IS_VARIABLE_INTERVAL);
-            precoloredIntervals = result.first;
-            notPrecoloredIntervals = result.second;
-
-            // allocate cpu registers
-            LinearScanWalker lsw;
-            if (OptimizingLinearScanWalker.Options.LSRAOptimization.getValue()) {
-                lsw = new OptimizingLinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals);
-            } else {
-                lsw = new LinearScanWalker(this, precoloredIntervals, notPrecoloredIntervals);
-            }
-            lsw.walk();
-            lsw.finishAllocation();
-        }
-    }
-
-    // * Phase 6: resolve data flow
-    // (insert moves at edges between blocks if intervals have been split)
-
     // wrapper for Interval.splitChildAtOpId that performs a bailout in product mode
     // instead of returning null
     Interval splitChildAtOpId(Interval interval, int opId, LIRInstruction.OperandMode mode) {
@@ -1500,579 +596,86 @@
         throw new BailoutException("LinearScan: interval is null");
     }
 
-    void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> midBlock, MoveResolver moveResolver) {
-        assert moveResolver.checkEmpty();
-        assert midBlock == null ||
-                        (midBlock.getPredecessorCount() == 1 && midBlock.getSuccessorCount() == 1 && midBlock.getPredecessors().get(0).equals(fromBlock) && midBlock.getSuccessors().get(0).equals(
-                                        toBlock));
-
-        int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock);
-        int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1;
-        int numOperands = operandSize();
-        BitSet liveAtEdge = blockData.get(toBlock).liveIn;
-
-        // visit all variables for which the liveAtEdge bit is set
-        for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) {
-            assert operandNum < numOperands : "live information set for not exisiting interval";
-            assert blockData.get(fromBlock).liveOut.get(operandNum) && blockData.get(toBlock).liveIn.get(operandNum) : "interval not live at this edge";
-
-            Interval fromInterval = splitChildAtOpId(intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF);
-            Interval toInterval = splitChildAtOpId(intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF);
-
-            if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) {
-                // need to insert move instruction
-                moveResolver.addMapping(fromInterval, toInterval);
-            }
-        }
-    }
-
-    void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, MoveResolver moveResolver) {
-        if (fromBlock.getSuccessorCount() <= 1) {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
-            }
-
-            List<LIRInstruction> instructions = ir.getLIRforBlock(fromBlock);
-            LIRInstruction instr = instructions.get(instructions.size() - 1);
-            if (instr instanceof StandardOp.JumpOp) {
-                // insert moves before branch
-                moveResolver.setInsertPosition(instructions, instructions.size() - 1);
-            } else {
-                moveResolver.setInsertPosition(instructions, instructions.size());
-            }
-
-        } else {
-            if (Debug.isLogEnabled()) {
-                Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
-            }
-
-            if (DetailedAsserts.getValue()) {
-                assert ir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
-
-                // because the number of predecessor edges matches the number of
-                // successor edges, blocks which are reached by switch statements
-                // may have be more than one predecessor but it will be guaranteed
-                // that all predecessors will be the same.
-                for (AbstractBlockBase<?> predecessor : toBlock.getPredecessors()) {
-                    assert fromBlock == predecessor : "all critical edges must be broken";
-                }
-            }
-
-            moveResolver.setInsertPosition(ir.getLIRforBlock(toBlock), 1);
-        }
-    }
-
-    /**
-     * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals that
-     * have been split.
-     */
-    void resolveDataFlow() {
-        try (Indent indent = Debug.logAndIndent("resolve data flow")) {
-
-            int numBlocks = blockCount();
-            MoveResolver moveResolver = createMoveResolver();
-            BitSet blockCompleted = new BitSet(numBlocks);
-            BitSet alreadyResolved = new BitSet(numBlocks);
-
-            for (AbstractBlockBase<?> block : sortedBlocks) {
-
-                // check if block has only one predecessor and only one successor
-                if (block.getPredecessorCount() == 1 && block.getSuccessorCount() == 1) {
-                    List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-                    assert instructions.get(0) instanceof StandardOp.LabelOp : "block must start with label";
-                    assert instructions.get(instructions.size() - 1) instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump";
-
-                    // check if block is empty (only label and branch)
-                    if (instructions.size() == 2) {
-                        AbstractBlockBase<?> pred = block.getPredecessors().iterator().next();
-                        AbstractBlockBase<?> sux = block.getSuccessors().iterator().next();
-
-                        // prevent optimization of two consecutive blocks
-                        if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) {
-                            if (Debug.isLogEnabled()) {
-                                Debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId());
-                            }
-
-                            blockCompleted.set(block.getLinearScanNumber());
-
-                            // directly resolve between pred and sux (without looking
-                            // at the empty block
-                            // between)
-                            resolveCollectMappings(pred, sux, block, moveResolver);
-                            if (moveResolver.hasMappings()) {
-                                moveResolver.setInsertPosition(instructions, 1);
-                                moveResolver.resolveAndAppendMoves();
-                            }
-                        }
-                    }
-                }
-            }
-
-            for (AbstractBlockBase<?> fromBlock : sortedBlocks) {
-                if (!blockCompleted.get(fromBlock.getLinearScanNumber())) {
-                    alreadyResolved.clear();
-                    alreadyResolved.or(blockCompleted);
-
-                    for (AbstractBlockBase<?> toBlock : fromBlock.getSuccessors()) {
-
-                        // check for duplicate edges between the same blocks (can happen with switch
-                        // blocks)
-                        if (!alreadyResolved.get(toBlock.getLinearScanNumber())) {
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId());
-                            }
-
-                            alreadyResolved.set(toBlock.getLinearScanNumber());
-
-                            // collect all intervals that have been split between
-                            // fromBlock and toBlock
-                            resolveCollectMappings(fromBlock, toBlock, null, moveResolver);
-                            if (moveResolver.hasMappings()) {
-                                resolveFindInsertPos(fromBlock, toBlock, moveResolver);
-                                moveResolver.resolveAndAppendMoves();
-                            }
-                        }
-                    }
-                }
-            }
-
-        }
-    }
-
-    // * Phase 7: assign register numbers back to LIR
-    // (includes computation of debug information and oop maps)
-
     static StackSlotValue canonicalSpillOpr(Interval interval) {
         assert interval.spillSlot() != null : "canonical spill slot not set";
         return interval.spillSlot();
     }
 
-    /**
-     * Assigns the allocated location for an LIR instruction operand back into the instruction.
-     *
-     * @param operand an LIR instruction operand
-     * @param opId the id of the LIR instruction using {@code operand}
-     * @param mode the usage mode for {@code operand} by the instruction
-     * @return the location assigned for the operand
-     */
-    private Value colorLirOperand(Variable operand, int opId, OperandMode mode) {
+    boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) {
         Interval interval = intervalFor(operand);
         assert interval != null : "interval must exist";
 
         if (opId != -1) {
-            if (DetailedAsserts.getValue()) {
-                AbstractBlockBase<?> block = blockForId(opId);
-                if (block.getSuccessorCount() <= 1 && opId == getLastLirInstructionId(block)) {
-                    /*
-                     * Check if spill moves could have been appended at the end of this block, but
-                     * before the branch instruction. So the split child information for this branch
-                     * would be incorrect.
-                     */
-                    LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1);
-                    if (instr instanceof StandardOp.JumpOp) {
-                        if (blockData.get(block).liveOut.get(operandNumber(operand))) {
-                            assert false : String.format(
-                                            "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s",
-                                            block, instr, operand);
-                        }
-                    }
-                }
-            }
-
-            // operands are not changed when an interval is split during allocation,
-            // so search the right interval here
-            interval = splitChildAtOpId(interval, opId, mode);
-        }
-
-        if (isIllegal(interval.location()) && interval.canMaterialize()) {
-            assert mode != OperandMode.DEF;
-            return interval.getMaterializedValue();
-        }
-        return interval.location();
-    }
-
-    private boolean isMaterialized(AllocatableValue operand, int opId, OperandMode mode) {
-        Interval interval = intervalFor(operand);
-        assert interval != null : "interval must exist";
-
-        if (opId != -1) {
-            // operands are not changed when an interval is split during allocation,
-            // so search the right interval here
+            /*
+             * Operands are not changed when an interval is split during allocation, so search the
+             * right interval here.
+             */
             interval = splitChildAtOpId(interval, opId, mode);
         }
 
         return isIllegal(interval.location()) && interval.canMaterialize();
     }
 
-    protected IntervalWalker initIntervalWalker(IntervalPredicate predicate) {
-        // setup lists of potential oops for walking
-        Interval oopIntervals;
-        Interval nonOopIntervals;
-
-        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
-        // with a high operation id
-        nonOopIntervals = new Interval(Value.ILLEGAL, -1);
-        nonOopIntervals.addRange(Integer.MAX_VALUE - 2, Integer.MAX_VALUE - 1);
-
-        return new IntervalWalker(this, oopIntervals, nonOopIntervals);
-    }
-
-    private boolean isCallerSave(Value operand) {
+    boolean isCallerSave(Value operand) {
         return attributes(asRegister(operand)).isCallerSave();
     }
 
-    /**
-     * @param op
-     * @param operand
-     * @param valueMode
-     * @param flags
-     * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet)
-     */
-    private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) {
-        if (isVirtualStackSlot(operand)) {
-            return operand;
-        }
-        int tempOpId = op.id();
-        OperandMode mode = OperandMode.USE;
-        AbstractBlockBase<?> block = blockForId(tempOpId);
-        if (block.getSuccessorCount() == 1 && tempOpId == getLastLirInstructionId(block)) {
-            // generating debug information for the last instruction of a block.
-            // if this instruction is a branch, spill moves are inserted before this branch
-            // and so the wrong operand would be returned (spill moves at block boundaries
-            // are not
-            // considered in the live ranges of intervals)
-            // Solution: use the first opId of the branch target block instead.
-            final LIRInstruction instr = ir.getLIRforBlock(block).get(ir.getLIRforBlock(block).size() - 1);
-            if (instr instanceof StandardOp.JumpOp) {
-                if (blockData.get(block).liveOut.get(operandNumber(operand))) {
-                    tempOpId = getFirstLirInstructionId(block.getSuccessors().iterator().next());
-                    mode = OperandMode.DEF;
-                }
-            }
-        }
-
-        // Get current location of operand
-        // The operand must be live because debug information is considered when building
-        // 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) || isStackSlotValue(result) || isConstant(result) || !isCallerSave(result) : "cannot have caller-save register operands at calls";
-        return result;
-    }
-
-    private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) {
-        info.forEachState(op, this::debugInfoProcedure);
-    }
-
-    private void assignLocations(List<LIRInstruction> instructions) {
-        int numInst = instructions.size();
-        boolean hasDead = false;
-
-        InstructionValueProcedure assignProc = (op, operand, mode, flags) -> isVariable(operand) ? colorLirOperand((Variable) operand, op.id(), mode) : operand;
-        for (int j = 0; j < numInst; j++) {
-            final LIRInstruction op = instructions.get(j);
-            if (op == null) { // this can happen when spill-moves are removed in eliminateSpillMoves
-                hasDead = true;
-                continue;
-            }
-
-            // remove useless moves
-            MoveOp move = null;
-            if (op instanceof MoveOp) {
-                move = (MoveOp) op;
-                AllocatableValue result = move.getResult();
-                if (isVariable(result) && isMaterialized(result, op.id(), OperandMode.DEF)) {
-                    /*
-                     * This happens if a materializable interval is originally not spilled but then
-                     * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an
-                     * interval this move operation was already generated.
-                     */
-                    instructions.set(j, null);
-                    hasDead = true;
-                    continue;
-                }
-            }
-
-            op.forEachInput(assignProc);
-            op.forEachAlive(assignProc);
-            op.forEachTemp(assignProc);
-            op.forEachOutput(assignProc);
-
-            // compute reference map and debug information
-            op.forEachState((inst, state) -> computeDebugInfo(inst, state));
-
-            // remove useless moves
-            if (move != null) {
-                if (move.getInput().equals(move.getResult())) {
-                    instructions.set(j, null);
-                    hasDead = true;
-                }
-            }
-        }
-
-        if (hasDead) {
-            // Remove null values from the list.
-            instructions.removeAll(Collections.singleton(null));
-        }
-    }
-
-    private void assignLocations() {
-        try (Indent indent = Debug.logAndIndent("assign locations")) {
-            for (AbstractBlockBase<?> block : sortedBlocks) {
-                try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) {
-                    assignLocations(ir.getLIRforBlock(block));
-                }
-            }
-        }
-    }
-
-    private static final DebugTimer lifetimeTimer = Debug.timer("LinearScan_LifetimeAnalysis");
-    private static final DebugTimer registerAllocationTimer = Debug.timer("LinearScan_RegisterAllocation");
-    private static final DebugTimer optimizedSpillPositionTimer = Debug.timer("LinearScan_OptimizeSpillPosition");
-    private static final DebugTimer resolveDataFlowTimer = Debug.timer("LinearScan_ResolveDataFlow");
-    private static final DebugTimer eliminateSpillMoveTimer = Debug.timer("LinearScan_EliminateSpillMove");
-    private static final DebugTimer assignLocationsTimer = Debug.timer("LinearScan_AssignLocations");
-
-    void allocate() {
+    <B extends AbstractBlockBase<B>> void allocate(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
 
         /*
          * This is the point to enable debug logging for the whole register allocation.
          */
         try (Indent indent = Debug.logAndIndent("LinearScan allocate")) {
+            AllocationContext context = new AllocationContext(spillMoveFactory);
 
-            try (Scope s = Debug.scope("LifetimeAnalysis"); DebugCloseable a = lifetimeTimer.start()) {
-                numberInstructions();
-                printLir("Before register allocation", true);
-                computeLocalLiveSets();
-                computeGlobalLiveSets();
-                buildIntervals();
-                sortIntervalsBeforeAllocation();
-            } catch (Throwable e) {
-                throw Debug.handle(e);
-            }
+            createLifetimeAnalysisPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false);
 
-            try (Scope s = Debug.scope("RegisterAllocation"); DebugCloseable a = registerAllocationTimer.start()) {
-                printIntervals("Before register allocation");
-                allocateRegisters();
-            } catch (Throwable e) {
-                throw Debug.handle(e);
-            }
+            sortIntervalsBeforeAllocation();
 
-            if (Options.LSRAOptimizeSpillPosition.getValue()) {
-                try (Scope s = Debug.scope("OptimizeSpillPosition"); DebugCloseable a = optimizedSpillPositionTimer.start()) {
-                    optimizeSpillPosition();
-                } catch (Throwable e) {
-                    throw Debug.handle(e);
-                }
-            }
+            createRegisterAllocationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false);
 
-            try (Scope s = Debug.scope("ResolveDataFlow"); DebugCloseable a = resolveDataFlowTimer.start()) {
-                resolveDataFlow();
-            } catch (Throwable e) {
-                throw Debug.handle(e);
+            if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) {
+                createOptimizeSpillPositionPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context, false);
             }
-
-            try (Scope s = Debug.scope("DebugInfo")) {
-                printIntervals("After register allocation");
-                printLir("After register allocation", true);
-
-                sortIntervalsAfterAllocation();
-
-                if (DetailedAsserts.getValue()) {
-                    verify();
-                }
-
-                beforeSpillMoveElimination();
+            createResolveDataFlowPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context);
 
-                try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = eliminateSpillMoveTimer.start()) {
-                    eliminateSpillMoves();
-                } catch (Throwable e) {
-                    throw Debug.handle(e);
-                }
-                printLir("After spill move elimination", true);
+            sortIntervalsAfterAllocation();
 
-                try (Scope s1 = Debug.scope("AssignLocations"); DebugCloseable a = assignLocationsTimer.start()) {
-                    assignLocations();
-                } catch (Throwable e) {
-                    throw Debug.handle(e);
-                }
-
-                if (DetailedAsserts.getValue()) {
-                    verifyIntervals();
-                }
-            } catch (Throwable e) {
-                throw Debug.handle(e);
+            if (DetailedAsserts.getValue()) {
+                verify();
             }
 
-            printLir("After register number assignment", true);
-        }
-    }
-
-    protected void beforeSpillMoveElimination() {
-    }
-
-    private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition");
-    private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability");
-
-    private void optimizeSpillPosition() {
-        LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[ir.linearScanOrder().size()];
-        for (Interval interval : intervals) {
-            if (interval != null && interval.isSplitParent() && interval.spillState() == SpillState.SpillInDominator) {
-                AbstractBlockBase<?> defBlock = blockForId(interval.spillDefinitionPos());
-                AbstractBlockBase<?> spillBlock = null;
-                Interval firstSpillChild = null;
-                try (Indent indent = Debug.logAndIndent("interval %s (%s)", interval, defBlock)) {
-                    for (Interval splitChild : interval.getSplitChildren()) {
-                        if (isStackSlotValue(splitChild.location())) {
-                            if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) {
-                                firstSpillChild = splitChild;
-                            } else {
-                                assert firstSpillChild.from() < splitChild.from();
-                            }
-                            // iterate all blocks where the interval has use positions
-                            for (AbstractBlockBase<?> splitBlock : blocksForInterval(splitChild)) {
-                                if (dominates(defBlock, splitBlock)) {
-                                    if (Debug.isLogEnabled()) {
-                                        Debug.log("Split interval %s, block %s", splitChild, splitBlock);
-                                    }
-                                    if (spillBlock == null) {
-                                        spillBlock = splitBlock;
-                                    } else {
-                                        spillBlock = commonDominator(spillBlock, splitBlock);
-                                        assert spillBlock != null;
-                                    }
-                                }
-                            }
-                        }
-                    }
-                    if (spillBlock == null) {
-                        // no spill interval
-                        interval.setSpillState(SpillState.StoreAtDefinition);
-                    } else {
-                        // move out of loops
-                        if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) {
-                            spillBlock = moveSpillOutOfLoop(defBlock, spillBlock);
-                        }
+            createSpillMoveEliminationPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context);
+            createAssignLocationsPhase().apply(target, lirGenRes, codeEmittingOrder, linearScanOrder, context);
 
-                        /*
-                         * If the spill block is the begin of the first split child (aka the value
-                         * is on the stack) spill in the dominator.
-                         */
-                        assert firstSpillChild != null;
-                        if (!defBlock.equals(spillBlock) && spillBlock.equals(blockForId(firstSpillChild.from()))) {
-                            AbstractBlockBase<?> dom = spillBlock.getDominator();
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom);
-                            }
-                            spillBlock = dom;
-                        }
-
-                        if (!defBlock.equals(spillBlock)) {
-                            assert dominates(defBlock, spillBlock);
-                            betterSpillPos.increment();
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("Better spill position found (Block %s)", spillBlock);
-                            }
-
-                            if (defBlock.probability() <= spillBlock.probability()) {
-                                // better spill block has the same probability -> do nothing
-                                interval.setSpillState(SpillState.StoreAtDefinition);
-                            } else {
-                                LIRInsertionBuffer insertionBuffer = insertionBuffers[spillBlock.getId()];
-                                if (insertionBuffer == null) {
-                                    insertionBuffer = new LIRInsertionBuffer();
-                                    insertionBuffers[spillBlock.getId()] = insertionBuffer;
-                                    insertionBuffer.init(ir.getLIRforBlock(spillBlock));
-                                }
-                                int spillOpId = getFirstLirInstructionId(spillBlock);
-                                // insert spill move
-                                AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, this).location();
-                                AllocatableValue toLocation = canonicalSpillOpr(interval);
-                                LIRInstruction move = getSpillMoveFactory().createMove(toLocation, fromLocation);
-                                move.setId(DOMINATOR_SPILL_MOVE_ID);
-                                /*
-                                 * We can use the insertion buffer directly because we always insert
-                                 * at position 1.
-                                 */
-                                insertionBuffer.append(1, move);
-
-                                betterSpillPosWithLowerProbability.increment();
-                                interval.setSpillDefinitionPos(spillOpId);
-                            }
-                        } else {
-                            // definition is the best choice
-                            interval.setSpillState(SpillState.StoreAtDefinition);
-                        }
-                    }
-                }
-            }
-        }
-        for (LIRInsertionBuffer insertionBuffer : insertionBuffers) {
-            if (insertionBuffer != null) {
-                assert insertionBuffer.initialized() : "Insertion buffer is nonnull but not initialized!";
-                insertionBuffer.finish();
+            if (DetailedAsserts.getValue()) {
+                verifyIntervals();
             }
         }
     }
 
-    /**
-     * Iterate over all {@link AbstractBlockBase blocks} of an interval.
-     */
-    private class IntervalBlockIterator implements Iterator<AbstractBlockBase<?>> {
-
-        Range range;
-        AbstractBlockBase<?> block;
-
-        public IntervalBlockIterator(Interval interval) {
-            range = interval.first();
-            block = blockForId(range.from);
-        }
+    protected LinearScanLifetimeAnalysisPhase createLifetimeAnalysisPhase() {
+        return new LinearScanLifetimeAnalysisPhase(this);
+    }
 
-        public AbstractBlockBase<?> next() {
-            AbstractBlockBase<?> currentBlock = block;
-            int nextBlockIndex = block.getLinearScanNumber() + 1;
-            if (nextBlockIndex < sortedBlocks.size()) {
-                block = sortedBlocks.get(nextBlockIndex);
-                if (range.to <= getFirstLirInstructionId(block)) {
-                    range = range.next;
-                    if (range == Range.EndMarker) {
-                        block = null;
-                    } else {
-                        block = blockForId(range.from);
-                    }
-                }
-            } else {
-                block = null;
-            }
-            return currentBlock;
-        }
-
-        public boolean hasNext() {
-            return block != null;
-        }
+    protected LinearScanRegisterAllocationPhase createRegisterAllocationPhase() {
+        return new LinearScanRegisterAllocationPhase(this);
     }
 
-    private Iterable<AbstractBlockBase<?>> blocksForInterval(Interval interval) {
-        return new Iterable<AbstractBlockBase<?>>() {
-            public Iterator<AbstractBlockBase<?>> iterator() {
-                return new IntervalBlockIterator(interval);
-            }
-        };
+    protected LinearScanOptimizeSpillPositionPhase createOptimizeSpillPositionPhase() {
+        return new LinearScanOptimizeSpillPositionPhase(this);
     }
 
-    private static AbstractBlockBase<?> moveSpillOutOfLoop(AbstractBlockBase<?> defBlock, AbstractBlockBase<?> spillBlock) {
-        int defLoopDepth = defBlock.getLoopDepth();
-        for (AbstractBlockBase<?> block = spillBlock.getDominator(); !defBlock.equals(block); block = block.getDominator()) {
-            assert block != null : "spill block not dominated by definition block?";
-            if (block.getLoopDepth() <= defLoopDepth) {
-                assert block.getLoopDepth() == defLoopDepth : "Cannot spill an interval outside of the loop where it is defined!";
-                return block;
-            }
-        }
-        return defBlock;
+    protected LinearScanResolveDataFlowPhase createResolveDataFlowPhase() {
+        return new LinearScanResolveDataFlowPhase(this);
+    }
+
+    protected LinearScanEliminateSpillMovePhase createSpillMoveEliminationPhase() {
+        return new LinearScanEliminateSpillMovePhase(this);
+    }
+
+    protected LinearScanAssignLocationsPhase createAssignLocationsPhase() {
+        return new LinearScanAssignLocationsPhase(this);
     }
 
     void printIntervals(String label) {
@@ -2229,14 +832,18 @@
                         iw.walkBefore(op.id());
                         boolean checkLive = true;
 
-                        // Make sure none of the fixed registers is live across an
-                        // oopmap since we can't handle that correctly.
+                        /*
+                         * Make sure none of the fixed registers is live across an oopmap since we
+                         * can't handle that correctly.
+                         */
                         if (checkLive) {
                             for (Interval interval = iw.activeLists.get(RegisterBinding.Fixed); interval != Interval.EndMarker; interval = interval.next) {
                                 if (interval.currentTo() > op.id() + 1) {
-                                    // This interval is live out of this op so make sure
-                                    // that this interval represents some value that's
-                                    // referenced by this op either as an input or output.
+                                    /*
+                                     * This interval is live out of this op so make sure that this
+                                     * interval represents some value that's referenced by this op
+                                     * either as an input or output.
+                                     */
                                     checkConsumer.curInterval = interval;
                                     checkConsumer.ok = false;
 
@@ -2255,37 +862,4 @@
         }
     }
 
-    /**
-     * 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
-     * @param interval The interval for this defined value.
-     * @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 JavaConstant}.
-     */
-    public static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, Interval interval) {
-        if (op instanceof MoveOp) {
-            MoveOp move = (MoveOp) op;
-            if (move.getInput() instanceof JavaConstant) {
-                /*
-                 * Check if the interval has any uses which would accept an stack location (priority
-                 * == ShouldHaveRegister). Rematerialization of such intervals can result in a
-                 * degradation, because rematerialization always inserts a constant load, even if
-                 * the value is not needed in a register.
-                 */
-                Interval.UsePosList usePosList = interval.usePosList();
-                int numUsePos = usePosList.size();
-                for (int useIdx = 0; useIdx < numUsePos; useIdx++) {
-                    Interval.RegisterPriority priority = usePosList.registerPriority(useIdx);
-                    if (priority == Interval.RegisterPriority.ShouldHaveRegister) {
-                        return null;
-                    }
-                }
-                return (JavaConstant) move.getInput();
-            }
-        }
-        return null;
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanAssignLocationsPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,214 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.*;
+import com.oracle.graal.lir.StandardOp.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.*;
+import com.oracle.graal.lir.phases.*;
+
+/**
+ * Phase 7: Assign register numbers back to LIR.
+ */
+final class LinearScanAssignLocationsPhase extends AllocationPhase {
+
+    private final LinearScan allocator;
+
+    LinearScanAssignLocationsPhase(LinearScan allocator) {
+        this.allocator = allocator;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        assignLocations();
+    }
+
+    /**
+     * Assigns the allocated location for an LIR instruction operand back into the instruction.
+     *
+     * @param operand an LIR instruction operand
+     * @param opId the id of the LIR instruction using {@code operand}
+     * @param mode the usage mode for {@code operand} by the instruction
+     * @return the location assigned for the operand
+     */
+    private Value colorLirOperand(Variable operand, int opId, OperandMode mode) {
+        Interval interval = allocator.intervalFor(operand);
+        assert interval != null : "interval must exist";
+
+        if (opId != -1) {
+            if (DetailedAsserts.getValue()) {
+                AbstractBlockBase<?> block = allocator.blockForId(opId);
+                if (block.getSuccessorCount() <= 1 && opId == allocator.getLastLirInstructionId(block)) {
+                    /*
+                     * Check if spill moves could have been appended at the end of this block, but
+                     * before the branch instruction. So the split child information for this branch
+                     * would be incorrect.
+                     */
+                    LIRInstruction instr = allocator.ir.getLIRforBlock(block).get(allocator.ir.getLIRforBlock(block).size() - 1);
+                    if (instr instanceof StandardOp.JumpOp) {
+                        if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) {
+                            assert false : String.format(
+                                            "can't get split child for the last branch of a block because the information would be incorrect (moves are inserted before the branch in resolveDataFlow) block=%s, instruction=%s, operand=%s",
+                                            block, instr, operand);
+                        }
+                    }
+                }
+            }
+
+            /*
+             * Operands are not changed when an interval is split during allocation, so search the
+             * right interval here.
+             */
+            interval = allocator.splitChildAtOpId(interval, opId, mode);
+        }
+
+        if (isIllegal(interval.location()) && interval.canMaterialize()) {
+            assert mode != OperandMode.DEF;
+            return interval.getMaterializedValue();
+        }
+        return interval.location();
+    }
+
+    /**
+     * @param op
+     * @param operand
+     * @param valueMode
+     * @param flags
+     * @see InstructionValueProcedure#doValue(LIRInstruction, Value, OperandMode, EnumSet)
+     */
+    private Value debugInfoProcedure(LIRInstruction op, Value operand, OperandMode valueMode, EnumSet<OperandFlag> flags) {
+        if (isVirtualStackSlot(operand)) {
+            return operand;
+        }
+        int tempOpId = op.id();
+        OperandMode mode = OperandMode.USE;
+        AbstractBlockBase<?> block = allocator.blockForId(tempOpId);
+        if (block.getSuccessorCount() == 1 && tempOpId == allocator.getLastLirInstructionId(block)) {
+            /*
+             * Generating debug information for the last instruction of a block. If this instruction
+             * is a branch, spill moves are inserted before this branch and so the wrong operand
+             * would be returned (spill moves at block boundaries are not considered in the live
+             * ranges of intervals).
+             * 
+             * Solution: use the first opId of the branch target block instead.
+             */
+            final LIRInstruction instr = allocator.ir.getLIRforBlock(block).get(allocator.ir.getLIRforBlock(block).size() - 1);
+            if (instr instanceof StandardOp.JumpOp) {
+                if (allocator.getBlockData(block).liveOut.get(allocator.operandNumber(operand))) {
+                    tempOpId = allocator.getFirstLirInstructionId(block.getSuccessors().iterator().next());
+                    mode = OperandMode.DEF;
+                }
+            }
+        }
+
+        /*
+         * Get current location of operand. The operand must be live because debug information is
+         * considered when building the intervals if the interval is not live, colorLirOperand will
+         * cause an assert on failure.
+         */
+        Value result = colorLirOperand((Variable) operand, tempOpId, mode);
+        assert !allocator.hasCall(tempOpId) || isStackSlotValue(result) || isConstant(result) || !allocator.isCallerSave(result) : "cannot have caller-save register operands at calls";
+        return result;
+    }
+
+    private void computeDebugInfo(final LIRInstruction op, LIRFrameState info) {
+        info.forEachState(op, this::debugInfoProcedure);
+    }
+
+    private void assignLocations(List<LIRInstruction> instructions) {
+        int numInst = instructions.size();
+        boolean hasDead = false;
+
+        InstructionValueProcedure assignProc = (op, operand, mode, flags) -> isVariable(operand) ? colorLirOperand((Variable) operand, op.id(), mode) : operand;
+        for (int j = 0; j < numInst; j++) {
+            final LIRInstruction op = instructions.get(j);
+            if (op == null) {
+                /*
+                 * this can happen when spill-moves are removed in eliminateSpillMoves
+                 */
+                hasDead = true;
+                continue;
+            }
+
+            // remove useless moves
+            MoveOp move = null;
+            if (op instanceof MoveOp) {
+                move = (MoveOp) op;
+                AllocatableValue result = move.getResult();
+                if (isVariable(result) && allocator.isMaterialized(result, op.id(), OperandMode.DEF)) {
+                    /*
+                     * This happens if a materializable interval is originally not spilled but then
+                     * kicked out in LinearScanWalker.splitForSpilling(). When kicking out such an
+                     * interval this move operation was already generated.
+                     */
+                    instructions.set(j, null);
+                    hasDead = true;
+                    continue;
+                }
+            }
+
+            op.forEachInput(assignProc);
+            op.forEachAlive(assignProc);
+            op.forEachTemp(assignProc);
+            op.forEachOutput(assignProc);
+
+            // compute reference map and debug information
+            op.forEachState((inst, state) -> computeDebugInfo(inst, state));
+
+            // remove useless moves
+            if (move != null) {
+                if (move.getInput().equals(move.getResult())) {
+                    instructions.set(j, null);
+                    hasDead = true;
+                }
+            }
+        }
+
+        if (hasDead) {
+            // Remove null values from the list.
+            instructions.removeAll(Collections.singleton(null));
+        }
+    }
+
+    private void assignLocations() {
+        try (Indent indent = Debug.logAndIndent("assign locations")) {
+            for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+                try (Indent indent2 = Debug.logAndIndent("assign locations in block B%d", block.getId())) {
+                    assignLocations(allocator.ir.getLIRforBlock(block));
+                }
+            }
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanEliminateSpillMovePhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,211 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.*;
+import com.oracle.graal.lir.alloc.lsra.Interval.*;
+import com.oracle.graal.lir.alloc.lsra.LinearScan.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.*;
+import com.oracle.graal.lir.phases.*;
+
+class LinearScanEliminateSpillMovePhase extends AllocationPhase {
+
+    private static final IntervalPredicate mustStoreAtDefinition = new LinearScan.IntervalPredicate() {
+
+        @Override
+        public boolean apply(Interval i) {
+            return i.isSplitParent() && i.spillState() == SpillState.StoreAtDefinition;
+        }
+    };
+
+    protected final LinearScan allocator;
+
+    LinearScanEliminateSpillMovePhase(LinearScan allocator) {
+        this.allocator = allocator;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        beforeSpillMoveElimination();
+        eliminateSpillMoves();
+    }
+
+    protected void beforeSpillMoveElimination() {
+    }
+
+    /**
+     * @return the index of the first instruction that is of interest for
+     *         {@link #eliminateSpillMoves()}
+     */
+    protected int firstInstructionOfInterest() {
+        // skip the first because it is always a label
+        return 1;
+    }
+
+    // called once before assignment of register numbers
+    void eliminateSpillMoves() {
+        try (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.
+             */
+            Interval interval;
+            interval = allocator.createUnhandledLists(mustStoreAtDefinition, null).first;
+            if (DetailedAsserts.getValue()) {
+                checkIntervals(interval);
+            }
+
+            LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
+            for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
+                    List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(block);
+                    int numInst = instructions.size();
+
+                    // iterate all instructions of the block.
+                    for (int j = firstInstructionOfInterest(); j < numInst; j++) {
+                        LIRInstruction op = instructions.get(j);
+                        int opId = op.id();
+
+                        if (opId == -1) {
+                            MoveOp move = (MoveOp) op;
+                            /*
+                             * Remove move from register to stack if the stack slot is guaranteed to
+                             * be correct. Only moves that have been inserted by LinearScan can be
+                             * removed.
+                             */
+                            if (canEliminateSpillMove(block, move)) {
+                                /*
+                                 * Move target is a stack slot that is always correct, so eliminate
+                                 * instruction.
+                                 */
+                                if (Debug.isLogEnabled()) {
+                                    Debug.log("eliminating move from interval %d (%s) to %d (%s) in block %s", allocator.operandNumber(move.getInput()), move.getInput(),
+                                                    allocator.operandNumber(move.getResult()), move.getResult(), block);
+                                }
+
+                                // null-instructions are deleted by assignRegNum
+                                instructions.set(j, null);
+                            }
+
+                        } else {
+                            /*
+                             * Insert move from register to stack just after the beginning of the
+                             * interval.
+                             */
+                            assert interval == Interval.EndMarker || interval.spillDefinitionPos() >= opId : "invalid order";
+                            assert interval == Interval.EndMarker || (interval.isSplitParent() && interval.spillState() == SpillState.StoreAtDefinition) : "invalid interval";
+
+                            while (interval != Interval.EndMarker && interval.spillDefinitionPos() == opId) {
+                                if (!interval.canMaterialize()) {
+                                    if (!insertionBuffer.initialized()) {
+                                        /*
+                                         * prepare insertion buffer (appended when all instructions
+                                         * in the block are processed)
+                                         */
+                                        insertionBuffer.init(instructions);
+                                    }
+
+                                    AllocatableValue fromLocation = interval.location();
+                                    AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval);
+                                    if (!fromLocation.equals(toLocation)) {
+
+                                        assert isRegister(fromLocation) : "from operand must be a register but is: " + fromLocation + " toLocation=" + toLocation + " spillState=" +
+                                                        interval.spillState();
+                                        assert isStackSlotValue(toLocation) : "to operand must be a stack slot";
+
+                                        LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation);
+                                        insertionBuffer.append(j + 1, move);
+
+                                        if (Debug.isLogEnabled()) {
+                                            Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
+                                        }
+                                    }
+                                }
+                                interval = interval.next;
+                            }
+                        }
+                    } // end of instruction iteration
+
+                    if (insertionBuffer.initialized()) {
+                        insertionBuffer.finish();
+                    }
+                }
+            } // end of block iteration
+
+            assert interval == Interval.EndMarker : "missed an interval";
+        }
+    }
+
+    /**
+     * @param block The block {@code move} is located in.
+     * @param move Spill move.
+     */
+    protected boolean canEliminateSpillMove(AbstractBlockBase<?> block, MoveOp move) {
+        assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables: " + move;
+
+        Interval curInterval = allocator.intervalFor(move.getResult());
+
+        if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) {
+            assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location();
+            return true;
+        }
+        return false;
+    }
+
+    private static void checkIntervals(Interval interval) {
+        Interval prev = null;
+        Interval temp = interval;
+        while (temp != Interval.EndMarker) {
+            assert temp.spillDefinitionPos() > 0 : "invalid spill definition pos";
+            if (prev != null) {
+                assert temp.from() >= prev.from() : "intervals not sorted";
+                assert temp.spillDefinitionPos() >= prev.spillDefinitionPos() : "when intervals are sorted by from :  then they must also be sorted by spillDefinitionPos";
+            }
+
+            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 (Debug.isLogEnabled()) {
+                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;
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanLifetimeAnalysisPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,829 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
+import static com.oracle.graal.lir.debug.LIRGenerationDebugContext.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.alloc.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.compiler.common.util.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.Scope;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.OperandFlag;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
+import com.oracle.graal.lir.StandardOp.LabelOp;
+import com.oracle.graal.lir.StandardOp.MoveOp;
+import com.oracle.graal.lir.alloc.lsra.Interval.RegisterPriority;
+import com.oracle.graal.lir.alloc.lsra.Interval.SpillState;
+import com.oracle.graal.lir.alloc.lsra.LinearScan.BlockData;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
+import com.oracle.graal.lir.phases.*;
+
+class LinearScanLifetimeAnalysisPhase extends AllocationPhase {
+
+    protected final LinearScan allocator;
+
+    /**
+     * @param linearScan
+     */
+    LinearScanLifetimeAnalysisPhase(LinearScan linearScan) {
+        allocator = linearScan;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        numberInstructions();
+        allocator.printLir("Before register allocation", true);
+        computeLocalLiveSets();
+        computeGlobalLiveSets();
+        buildIntervals();
+    }
+
+    /**
+     * Bit set for each variable that is contained in each loop.
+     */
+    BitMap2D intervalInLoop;
+
+    boolean isIntervalInLoop(int interval, int loop) {
+        return intervalInLoop.at(interval, loop);
+    }
+
+    /**
+     * Numbers all instructions in all blocks. The numbering follows the
+     * {@linkplain ComputeBlockOrder linear scan order}.
+     */
+    void numberInstructions() {
+
+        allocator.initIntervals();
+
+        ValueConsumer setVariableConsumer = (value, mode, flags) -> {
+            if (isVariable(value)) {
+                allocator.getOrCreateInterval(asVariable(value));
+            }
+        };
+
+        // Assign IDs to LIR nodes and build a mapping, lirOps, from ID to LIRInstruction node.
+        int numInstructions = 0;
+        for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+            numInstructions += allocator.ir.getLIRforBlock(block).size();
+        }
+
+        // initialize with correct length
+        allocator.initOpIdMaps(numInstructions);
+
+        int opId = 0;
+        int index = 0;
+        for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+            allocator.initBlockData(block);
+
+            List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(block);
+
+            int numInst = instructions.size();
+            for (int j = 0; j < numInst; j++) {
+                LIRInstruction op = instructions.get(j);
+                op.setId(opId);
+
+                allocator.putOpIdMaps(index, op, block);
+                assert allocator.instructionForId(opId) == op : "must match";
+
+                op.visitEachTemp(setVariableConsumer);
+                op.visitEachOutput(setVariableConsumer);
+
+                index++;
+                opId += 2; // numbering of lirOps by two
+            }
+        }
+        assert index == numInstructions : "must match";
+        assert (index << 1) == opId : "must match: " + (index << 1);
+    }
+
+    /**
+     * Computes local live sets (i.e. {@link BlockData#liveGen} and {@link BlockData#liveKill})
+     * separately for each block.
+     */
+    void computeLocalLiveSets() {
+        int liveSize = allocator.liveSetSize();
+
+        intervalInLoop = new BitMap2D(allocator.operandSize(), allocator.numLoops());
+
+        // iterate all blocks
+        for (final AbstractBlockBase<?> block : allocator.sortedBlocks) {
+            try (Indent indent = Debug.logAndIndent("compute local live sets for block %s", block)) {
+
+                final BitSet liveGen = new BitSet(liveSize);
+                final BitSet liveKill = new BitSet(liveSize);
+
+                List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(block);
+                int numInst = instructions.size();
+
+                ValueConsumer useConsumer = (operand, mode, flags) -> {
+                    if (isVariable(operand)) {
+                        int operandNum = allocator.operandNumber(operand);
+                        if (!liveKill.get(operandNum)) {
+                            liveGen.set(operandNum);
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("liveGen for operand %d(%s)", operandNum, operand);
+                            }
+                        }
+                        if (block.getLoop() != null) {
+                            intervalInLoop.setBit(operandNum, block.getLoop().getIndex());
+                        }
+                    }
+
+                    if (DetailedAsserts.getValue()) {
+                        verifyInput(block, liveKill, operand);
+                    }
+                };
+                ValueConsumer stateConsumer = (operand, mode, flags) -> {
+                    if (LinearScan.isVariableOrRegister(operand)) {
+                        int operandNum = allocator.operandNumber(operand);
+                        if (!liveKill.get(operandNum)) {
+                            liveGen.set(operandNum);
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("liveGen in state for operand %d(%s)", operandNum, operand);
+                            }
+                        }
+                    }
+                };
+                ValueConsumer defConsumer = (operand, mode, flags) -> {
+                    if (isVariable(operand)) {
+                        int varNum = allocator.operandNumber(operand);
+                        liveKill.set(varNum);
+                        if (Debug.isLogEnabled()) {
+                            Debug.log("liveKill for operand %d(%s)", varNum, operand);
+                        }
+                        if (block.getLoop() != null) {
+                            intervalInLoop.setBit(varNum, block.getLoop().getIndex());
+                        }
+                    }
+
+                    if (DetailedAsserts.getValue()) {
+                        /*
+                         * Fixed intervals are never live at block boundaries, so they need not be
+                         * processed in live sets. Process them only in debug mode so that this can
+                         * be checked
+                         */
+                        verifyTemp(liveKill, operand);
+                    }
+                };
+
+                // iterate all instructions of the block
+                for (int j = 0; j < numInst; j++) {
+                    final LIRInstruction op = instructions.get(j);
+
+                    try (Indent indent2 = Debug.logAndIndent("handle op %d", op.id())) {
+                        op.visitEachInput(useConsumer);
+                        op.visitEachAlive(useConsumer);
+                        /*
+                         * Add uses of live locals from interpreter's point of view for proper debug
+                         * information generation.
+                         */
+                        op.visitEachState(stateConsumer);
+                        op.visitEachTemp(defConsumer);
+                        op.visitEachOutput(defConsumer);
+                    }
+                } // end of instruction iteration
+
+                BlockData blockSets = allocator.getBlockData(block);
+                blockSets.liveGen = liveGen;
+                blockSets.liveKill = liveKill;
+                blockSets.liveIn = new BitSet(liveSize);
+                blockSets.liveOut = new BitSet(liveSize);
+
+                if (Debug.isLogEnabled()) {
+                    Debug.log("liveGen  B%d %s", block.getId(), blockSets.liveGen);
+                    Debug.log("liveKill B%d %s", block.getId(), blockSets.liveKill);
+                }
+
+            }
+        } // end of block iteration
+    }
+
+    private void verifyTemp(BitSet liveKill, Value operand) {
+        /*
+         * Fixed intervals are never live at block boundaries, so they need not be processed in live
+         * sets. Process them only in debug mode so that this can be checked
+         */
+        if (isRegister(operand)) {
+            if (allocator.isProcessed(operand)) {
+                liveKill.set(allocator.operandNumber(operand));
+            }
+        }
+    }
+
+    private void verifyInput(AbstractBlockBase<?> block, BitSet liveKill, Value operand) {
+        /*
+         * Fixed intervals are never live at block boundaries, so they need not be processed in live
+         * sets. This is checked by these assertions to be sure about it. The entry block may have
+         * incoming values in registers, which is ok.
+         */
+        if (isRegister(operand) && block != allocator.ir.getControlFlowGraph().getStartBlock()) {
+            if (allocator.isProcessed(operand)) {
+                assert liveKill.get(allocator.operandNumber(operand)) : "using fixed register that is not defined in this block";
+            }
+        }
+    }
+
+    /**
+     * Performs a backward dataflow analysis to compute global live sets (i.e.
+     * {@link BlockData#liveIn} and {@link BlockData#liveOut}) for each block.
+     */
+    void computeGlobalLiveSets() {
+        try (Indent indent = Debug.logAndIndent("compute global live sets")) {
+            int numBlocks = allocator.blockCount();
+            boolean changeOccurred;
+            boolean changeOccurredInBlock;
+            int iterationCount = 0;
+            BitSet liveOut = new BitSet(allocator.liveSetSize()); // scratch set for calculations
+
+            /*
+             * Perform a backward dataflow analysis to compute liveOut and liveIn for each block.
+             * The loop is executed until a fixpoint is reached (no changes in an iteration).
+             */
+            do {
+                changeOccurred = false;
+
+                try (Indent indent2 = Debug.logAndIndent("new iteration %d", iterationCount)) {
+
+                    // iterate all blocks in reverse order
+                    for (int i = numBlocks - 1; i >= 0; i--) {
+                        AbstractBlockBase<?> block = allocator.blockAt(i);
+                        BlockData blockSets = allocator.getBlockData(block);
+
+                        changeOccurredInBlock = false;
+
+                        /* liveOut(block) is the union of liveIn(sux), for successors sux of block. */
+                        int n = block.getSuccessorCount();
+                        if (n > 0) {
+                            liveOut.clear();
+                            // block has successors
+                            if (n > 0) {
+                                for (AbstractBlockBase<?> successor : block.getSuccessors()) {
+                                    liveOut.or(allocator.getBlockData(successor).liveIn);
+                                }
+                            }
+
+                            if (!blockSets.liveOut.equals(liveOut)) {
+                                /*
+                                 * A change occurred. Swap the old and new live out sets to avoid
+                                 * copying.
+                                 */
+                                BitSet temp = blockSets.liveOut;
+                                blockSets.liveOut = liveOut;
+                                liveOut = temp;
+
+                                changeOccurred = true;
+                                changeOccurredInBlock = true;
+                            }
+                        }
+
+                        if (iterationCount == 0 || changeOccurredInBlock) {
+                            /*
+                             * liveIn(block) is the union of liveGen(block) with (liveOut(block) &
+                             * !liveKill(block)).
+                             * 
+                             * Note: liveIn has to be computed only in first iteration or if liveOut
+                             * has changed!
+                             */
+                            BitSet liveIn = blockSets.liveIn;
+                            liveIn.clear();
+                            liveIn.or(blockSets.liveOut);
+                            liveIn.andNot(blockSets.liveKill);
+                            liveIn.or(blockSets.liveGen);
+
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("block %d: livein = %s,  liveout = %s", block.getId(), liveIn, blockSets.liveOut);
+                            }
+                        }
+                    }
+                    iterationCount++;
+
+                    if (changeOccurred && iterationCount > 50) {
+                        throw new BailoutException("too many iterations in computeGlobalLiveSets");
+                    }
+                }
+            } while (changeOccurred);
+
+            if (DetailedAsserts.getValue()) {
+                verifyLiveness();
+            }
+
+            // check that the liveIn set of the first block is empty
+            AbstractBlockBase<?> startBlock = allocator.ir.getControlFlowGraph().getStartBlock();
+            if (allocator.getBlockData(startBlock).liveIn.cardinality() != 0) {
+                if (DetailedAsserts.getValue()) {
+                    reportFailure(numBlocks);
+                }
+                // bailout if this occurs in product mode.
+                throw new GraalInternalError("liveIn set of first block must be empty: " + allocator.getBlockData(startBlock).liveIn);
+            }
+        }
+    }
+
+    private void reportFailure(int numBlocks) {
+        try (Scope s = Debug.forceLog()) {
+            try (Indent indent = Debug.logAndIndent("report failure")) {
+
+                BitSet startBlockLiveIn = allocator.getBlockData(allocator.ir.getControlFlowGraph().getStartBlock()).liveIn;
+                try (Indent indent2 = Debug.logAndIndent("Error: liveIn set of first block must be empty (when this fails, variables are used before they are defined):")) {
+                    for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
+                        Interval interval = allocator.intervalFor(operandNum);
+                        if (interval != null) {
+                            Value operand = interval.operand;
+                            Debug.log("var %d; operand=%s; node=%s", operandNum, operand, getSourceForOperandFromDebugContext(operand));
+                        } else {
+                            Debug.log("var %d; missing operand", operandNum);
+                        }
+                    }
+                }
+
+                // print some additional information to simplify debugging
+                for (int operandNum = startBlockLiveIn.nextSetBit(0); operandNum >= 0; operandNum = startBlockLiveIn.nextSetBit(operandNum + 1)) {
+                    Interval interval = allocator.intervalFor(operandNum);
+                    Value operand = null;
+                    Object valueForOperandFromDebugContext = null;
+                    if (interval != null) {
+                        operand = interval.operand;
+                        valueForOperandFromDebugContext = getSourceForOperandFromDebugContext(operand);
+                    }
+                    try (Indent indent2 = Debug.logAndIndent("---- Detailed information for var %d; operand=%s; node=%s ----", operandNum, operand, valueForOperandFromDebugContext)) {
+
+                        Deque<AbstractBlockBase<?>> definedIn = new ArrayDeque<>();
+                        HashSet<AbstractBlockBase<?>> usedIn = new HashSet<>();
+                        for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+                            if (allocator.getBlockData(block).liveGen.get(operandNum)) {
+                                usedIn.add(block);
+                                try (Indent indent3 = Debug.logAndIndent("used in block B%d", block.getId())) {
+                                    for (LIRInstruction ins : allocator.ir.getLIRforBlock(block)) {
+                                        try (Indent indent4 = Debug.logAndIndent("%d: %s", ins.id(), ins)) {
+                                            ins.forEachState((liveStateOperand, mode, flags) -> {
+                                                Debug.log("operand=%s", liveStateOperand);
+                                                return liveStateOperand;
+                                            });
+                                        }
+                                    }
+                                }
+                            }
+                            if (allocator.getBlockData(block).liveKill.get(operandNum)) {
+                                definedIn.add(block);
+                                try (Indent indent3 = Debug.logAndIndent("defined in block B%d", block.getId())) {
+                                    for (LIRInstruction ins : allocator.ir.getLIRforBlock(block)) {
+                                        Debug.log("%d: %s", ins.id(), ins);
+                                    }
+                                }
+                            }
+                        }
+
+                        int[] hitCount = new int[numBlocks];
+
+                        while (!definedIn.isEmpty()) {
+                            AbstractBlockBase<?> block = definedIn.removeFirst();
+                            usedIn.remove(block);
+                            for (AbstractBlockBase<?> successor : block.getSuccessors()) {
+                                if (successor.isLoopHeader()) {
+                                    if (!block.isLoopEnd()) {
+                                        definedIn.add(successor);
+                                    }
+                                } else {
+                                    if (++hitCount[successor.getId()] == successor.getPredecessorCount()) {
+                                        definedIn.add(successor);
+                                    }
+                                }
+                            }
+                        }
+                        try (Indent indent3 = Debug.logAndIndent("**** offending usages are in: ")) {
+                            for (AbstractBlockBase<?> block : usedIn) {
+                                Debug.log("B%d", block.getId());
+                            }
+                        }
+                    }
+                }
+            }
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+    }
+
+    private void verifyLiveness() {
+        /*
+         * Check that fixed intervals are not live at block boundaries (live set must be empty at
+         * fixed intervals).
+         */
+        for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+            for (int j = 0; j <= allocator.maxRegisterNumber(); j++) {
+                assert !allocator.getBlockData(block).liveIn.get(j) : "liveIn  set of fixed register must be empty";
+                assert !allocator.getBlockData(block).liveOut.get(j) : "liveOut set of fixed register must be empty";
+                assert !allocator.getBlockData(block).liveGen.get(j) : "liveGen set of fixed register must be empty";
+            }
+        }
+    }
+
+    void addUse(AllocatableValue operand, int from, int to, RegisterPriority registerPriority, LIRKind kind) {
+        if (!allocator.isProcessed(operand)) {
+            return;
+        }
+
+        Interval interval = allocator.getOrCreateInterval(operand);
+        if (!kind.equals(LIRKind.Illegal)) {
+            interval.setKind(kind);
+        }
+
+        interval.addRange(from, to);
+
+        // Register use position at even instruction id.
+        interval.addUsePos(to & ~1, registerPriority);
+
+        if (Debug.isLogEnabled()) {
+            Debug.log("add use: %s, from %d to %d (%s)", interval, from, to, registerPriority.name());
+        }
+    }
+
+    void addTemp(AllocatableValue operand, int tempPos, RegisterPriority registerPriority, LIRKind kind) {
+        if (!allocator.isProcessed(operand)) {
+            return;
+        }
+
+        Interval interval = allocator.getOrCreateInterval(operand);
+        if (!kind.equals(LIRKind.Illegal)) {
+            interval.setKind(kind);
+        }
+
+        interval.addRange(tempPos, tempPos + 1);
+        interval.addUsePos(tempPos, registerPriority);
+        interval.addMaterializationValue(null);
+
+        if (Debug.isLogEnabled()) {
+            Debug.log("add temp: %s tempPos %d (%s)", interval, tempPos, RegisterPriority.MustHaveRegister.name());
+        }
+    }
+
+    void addDef(AllocatableValue operand, LIRInstruction op, RegisterPriority registerPriority, LIRKind kind) {
+        if (!allocator.isProcessed(operand)) {
+            return;
+        }
+        int defPos = op.id();
+
+        Interval interval = allocator.getOrCreateInterval(operand);
+        if (!kind.equals(LIRKind.Illegal)) {
+            interval.setKind(kind);
+        }
+
+        Range r = interval.first();
+        if (r.from <= defPos) {
+            /*
+             * Update the starting point (when a range is first created for a use, its start is the
+             * beginning of the current block until a def is encountered).
+             */
+            r.from = defPos;
+            interval.addUsePos(defPos, registerPriority);
+
+        } else {
+            /*
+             * Dead value - make vacuous interval also add register priority for dead intervals
+             */
+            interval.addRange(defPos, defPos + 1);
+            interval.addUsePos(defPos, registerPriority);
+            if (Debug.isLogEnabled()) {
+                Debug.log("Warning: def of operand %s at %d occurs without use", operand, defPos);
+            }
+        }
+
+        changeSpillDefinitionPos(interval, defPos);
+        if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) {
+            // detection of method-parameters and roundfp-results
+            interval.setSpillState(SpillState.StartInMemory);
+        }
+        interval.addMaterializationValue(getMaterializedValue(op, operand, interval));
+
+        if (Debug.isLogEnabled()) {
+            Debug.log("add def: %s defPos %d (%s)", interval, defPos, registerPriority.name());
+        }
+    }
+
+    /**
+     * Optimizes moves related to incoming stack based arguments. The interval for the destination
+     * of such moves is assigned the stack slot (which is in the caller's frame) as its spill slot.
+     */
+    void handleMethodArguments(LIRInstruction op) {
+        if (op instanceof MoveOp) {
+            MoveOp move = (MoveOp) op;
+            if (optimizeMethodArgument(move.getInput())) {
+                StackSlot slot = asStackSlot(move.getInput());
+                if (DetailedAsserts.getValue()) {
+                    assert op.id() > 0 : "invalid id";
+                    assert allocator.blockForId(op.id()).getPredecessorCount() == 0 : "move from stack must be in first block";
+                    assert isVariable(move.getResult()) : "result of move must be a variable";
+
+                    if (Debug.isLogEnabled()) {
+                        Debug.log("found move from stack slot %s to %s", slot, move.getResult());
+                    }
+                }
+
+                Interval interval = allocator.intervalFor(move.getResult());
+                interval.setSpillSlot(slot);
+                interval.assignLocation(slot);
+            }
+        }
+    }
+
+    void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
+        if (flags.contains(OperandFlag.HINT) && LinearScan.isVariableOrRegister(targetValue)) {
+
+            op.forEachRegisterHint(targetValue, mode, (registerHint, valueMode, valueFlags) -> {
+                if (LinearScan.isVariableOrRegister(registerHint)) {
+                    Interval from = allocator.getOrCreateInterval((AllocatableValue) registerHint);
+                    Interval to = allocator.getOrCreateInterval((AllocatableValue) targetValue);
+
+                    /* hints always point from def to use */
+                    if (hintAtDef) {
+                        to.setLocationHint(from);
+                    } else {
+                        from.setLocationHint(to);
+                    }
+                    if (Debug.isLogEnabled()) {
+                        Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), from.operandNumber, to.operandNumber);
+                    }
+
+                    return registerHint;
+                }
+                return null;
+            });
+        }
+    }
+
+    /**
+     * Eliminates moves from register to stack if the stack slot is known to be correct.
+     */
+    void changeSpillDefinitionPos(Interval interval, int defPos) {
+        assert interval.isSplitParent() : "can only be called for split parents";
+
+        switch (interval.spillState()) {
+            case NoDefinitionFound:
+                assert interval.spillDefinitionPos() == -1 : "must no be set before";
+                interval.setSpillDefinitionPos(defPos);
+                interval.setSpillState(SpillState.NoSpillStore);
+                break;
+
+            case NoSpillStore:
+                assert defPos <= interval.spillDefinitionPos() : "positions are processed in reverse order when intervals are created";
+                if (defPos < interval.spillDefinitionPos() - 2) {
+                    // second definition found, so no spill optimization possible for this interval
+                    interval.setSpillState(SpillState.NoOptimization);
+                } else {
+                    // two consecutive definitions (because of two-operand LIR form)
+                    assert allocator.blockForId(defPos) == allocator.blockForId(interval.spillDefinitionPos()) : "block must be equal";
+                }
+                break;
+
+            case NoOptimization:
+                // nothing to do
+                break;
+
+            default:
+                throw new BailoutException("other states not allowed at this time");
+        }
+    }
+
+    private static boolean optimizeMethodArgument(Value value) {
+        /*
+         * Object method arguments that are passed on the stack are currently not optimized because
+         * this requires that the runtime visits method arguments during stack walking.
+         */
+        return isStackSlot(value) && asStackSlot(value).isInCallerFrame() && value.getKind() != Kind.Object;
+    }
+
+    /**
+     * Determines the register priority for an instruction's output/result operand.
+     */
+    private static RegisterPriority registerPriorityOfOutputOperand(LIRInstruction op) {
+        if (op instanceof MoveOp) {
+            MoveOp move = (MoveOp) op;
+            if (optimizeMethodArgument(move.getInput())) {
+                return RegisterPriority.None;
+            }
+        } else if (op instanceof LabelOp) {
+            LabelOp label = (LabelOp) op;
+            if (label.isPhiIn()) {
+                return RegisterPriority.None;
+            }
+        }
+
+        // all other operands require a register
+        return RegisterPriority.MustHaveRegister;
+    }
+
+    /**
+     * Determines the priority which with an instruction's input operand will be allocated a
+     * register.
+     */
+    private static RegisterPriority registerPriorityOfInputOperand(EnumSet<OperandFlag> flags) {
+        if (flags.contains(OperandFlag.STACK)) {
+            return RegisterPriority.ShouldHaveRegister;
+        }
+        // all other operands require a register
+        return RegisterPriority.MustHaveRegister;
+    }
+
+    void buildIntervals() {
+
+        try (Indent indent = Debug.logAndIndent("build intervals")) {
+            InstructionValueConsumer outputConsumer = (op, operand, mode, flags) -> {
+                if (LinearScan.isVariableOrRegister(operand)) {
+                    addDef((AllocatableValue) operand, op, registerPriorityOfOutputOperand(op), operand.getLIRKind());
+                    addRegisterHint(op, operand, mode, flags, true);
+                }
+            };
+
+            InstructionValueConsumer tempConsumer = (op, operand, mode, flags) -> {
+                if (LinearScan.isVariableOrRegister(operand)) {
+                    addTemp((AllocatableValue) operand, op.id(), RegisterPriority.MustHaveRegister, operand.getLIRKind());
+                    addRegisterHint(op, operand, mode, flags, false);
+                }
+            };
+
+            InstructionValueConsumer aliveConsumer = (op, operand, mode, flags) -> {
+                if (LinearScan.isVariableOrRegister(operand)) {
+                    RegisterPriority p = registerPriorityOfInputOperand(flags);
+                    int opId = op.id();
+                    int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId)));
+                    addUse((AllocatableValue) operand, blockFrom, opId + 1, p, operand.getLIRKind());
+                    addRegisterHint(op, operand, mode, flags, false);
+                }
+            };
+
+            InstructionValueConsumer inputConsumer = (op, operand, mode, flags) -> {
+                if (LinearScan.isVariableOrRegister(operand)) {
+                    int opId = op.id();
+                    int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId)));
+                    RegisterPriority p = registerPriorityOfInputOperand(flags);
+                    addUse((AllocatableValue) operand, blockFrom, opId, p, operand.getLIRKind());
+                    addRegisterHint(op, operand, mode, flags, false);
+                }
+            };
+
+            InstructionValueConsumer stateProc = (op, operand, mode, flags) -> {
+                if (LinearScan.isVariableOrRegister(operand)) {
+                    int opId = op.id();
+                    int blockFrom = allocator.getFirstLirInstructionId((allocator.blockForId(opId)));
+                    addUse((AllocatableValue) operand, blockFrom, opId + 1, RegisterPriority.None, operand.getLIRKind());
+                }
+            };
+
+            // create a list with all caller-save registers (cpu, fpu, xmm)
+            Register[] callerSaveRegs = allocator.regAllocConfig.getRegisterConfig().getCallerSaveRegisters();
+
+            // iterate all blocks in reverse order
+            for (int i = allocator.blockCount() - 1; i >= 0; i--) {
+
+                AbstractBlockBase<?> block = allocator.blockAt(i);
+                try (Indent indent2 = Debug.logAndIndent("handle block %d", block.getId())) {
+
+                    List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(block);
+                    final int blockFrom = allocator.getFirstLirInstructionId(block);
+                    int blockTo = allocator.getLastLirInstructionId(block);
+
+                    assert blockFrom == instructions.get(0).id();
+                    assert blockTo == instructions.get(instructions.size() - 1).id();
+
+                    // Update intervals for operands live at the end of this block;
+                    BitSet live = allocator.getBlockData(block).liveOut;
+                    for (int operandNum = live.nextSetBit(0); operandNum >= 0; operandNum = live.nextSetBit(operandNum + 1)) {
+                        assert live.get(operandNum) : "should not stop here otherwise";
+                        AllocatableValue operand = allocator.intervalFor(operandNum).operand;
+                        if (Debug.isLogEnabled()) {
+                            Debug.log("live in %d: %s", operandNum, operand);
+                        }
+
+                        addUse(operand, blockFrom, blockTo + 2, RegisterPriority.None, LIRKind.Illegal);
+
+                        /*
+                         * Add special use positions for loop-end blocks when the interval is used
+                         * anywhere inside this loop. It's possible that the block was part of a
+                         * non-natural loop, so it might have an invalid loop index.
+                         */
+                        if (block.isLoopEnd() && block.getLoop() != null && isIntervalInLoop(operandNum, block.getLoop().getIndex())) {
+                            allocator.intervalFor(operandNum).addUsePos(blockTo + 1, RegisterPriority.LiveAtLoopEnd);
+                        }
+                    }
+
+                    /*
+                     * Iterate all instructions of the block in reverse order. definitions of
+                     * intervals are processed before uses.
+                     */
+                    for (int j = instructions.size() - 1; j >= 0; j--) {
+                        final LIRInstruction op = instructions.get(j);
+                        final int opId = op.id();
+
+                        try (Indent indent3 = Debug.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) {
+                                    if (allocator.attributes(r).isAllocatable()) {
+                                        addTemp(r.asValue(), opId, RegisterPriority.None, LIRKind.Illegal);
+                                    }
+                                }
+                                if (Debug.isLogEnabled()) {
+                                    Debug.log("operation destroys all caller-save registers");
+                                }
+                            }
+
+                            op.visitEachOutput(outputConsumer);
+                            op.visitEachTemp(tempConsumer);
+                            op.visitEachAlive(aliveConsumer);
+                            op.visitEachInput(inputConsumer);
+
+                            /*
+                             * Add uses of live locals from interpreter's point of view for proper
+                             * debug information generation. Treat these operands as temp values (if
+                             * the live range is extended to a call site, the value would be in a
+                             * register at the call otherwise).
+                             */
+                            op.visitEachState(stateProc);
+
+                            // special steps for some instructions (especially moves)
+                            handleMethodArguments(op);
+
+                        }
+
+                    } // end of instruction iteration
+                }
+            } // end of block iteration
+
+            /*
+             * Add the range [0, 1] to all fixed intervals. the register allocator need not handle
+             * unhandled fixed intervals.
+             */
+            for (Interval interval : allocator.intervals()) {
+                if (interval != null && isRegister(interval.operand)) {
+                    interval.addRange(0, 1);
+                }
+            }
+        }
+    }
+
+    /**
+     * 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
+     * @param interval The interval for this defined value.
+     * @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 JavaConstant}.
+     */
+    static JavaConstant getMaterializedValue(LIRInstruction op, Value operand, Interval interval) {
+        if (op instanceof MoveOp) {
+            MoveOp move = (MoveOp) op;
+            if (move.getInput() instanceof JavaConstant) {
+                /*
+                 * Check if the interval has any uses which would accept an stack location (priority
+                 * == ShouldHaveRegister). Rematerialization of such intervals can result in a
+                 * degradation, because rematerialization always inserts a constant load, even if
+                 * the value is not needed in a register.
+                 */
+                Interval.UsePosList usePosList = interval.usePosList();
+                int numUsePos = usePosList.size();
+                for (int useIdx = 0; useIdx < numUsePos; useIdx++) {
+                    Interval.RegisterPriority priority = usePosList.registerPriority(useIdx);
+                    if (priority == Interval.RegisterPriority.ShouldHaveRegister) {
+                        return null;
+                    }
+                }
+                return (JavaConstant) move.getInput();
+            }
+        }
+        return null;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanOptimizeSpillPositionPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,215 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.compiler.common.cfg.AbstractControlFlowGraph.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
+import com.oracle.graal.lir.alloc.lsra.Interval.SpillState;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
+import com.oracle.graal.lir.phases.*;
+
+final class LinearScanOptimizeSpillPositionPhase extends AllocationPhase {
+
+    private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition");
+    private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability");
+
+    private final LinearScan allocator;
+
+    LinearScanOptimizeSpillPositionPhase(LinearScan allocator) {
+        this.allocator = allocator;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        optimizeSpillPosition();
+        allocator.printIntervals("After optimize spill position");
+    }
+
+    private void optimizeSpillPosition() {
+        LIRInsertionBuffer[] insertionBuffers = new LIRInsertionBuffer[allocator.ir.linearScanOrder().size()];
+        for (Interval interval : allocator.intervals()) {
+            if (interval != null && interval.isSplitParent() && interval.spillState() == SpillState.SpillInDominator) {
+                AbstractBlockBase<?> defBlock = allocator.blockForId(interval.spillDefinitionPos());
+                AbstractBlockBase<?> spillBlock = null;
+                Interval firstSpillChild = null;
+                try (Indent indent = Debug.logAndIndent("interval %s (%s)", interval, defBlock)) {
+                    for (Interval splitChild : interval.getSplitChildren()) {
+                        if (isStackSlotValue(splitChild.location())) {
+                            if (firstSpillChild == null || splitChild.from() < firstSpillChild.from()) {
+                                firstSpillChild = splitChild;
+                            } else {
+                                assert firstSpillChild.from() < splitChild.from();
+                            }
+                            // iterate all blocks where the interval has use positions
+                            for (AbstractBlockBase<?> splitBlock : blocksForInterval(splitChild)) {
+                                if (dominates(defBlock, splitBlock)) {
+                                    if (Debug.isLogEnabled()) {
+                                        Debug.log("Split interval %s, block %s", splitChild, splitBlock);
+                                    }
+                                    if (spillBlock == null) {
+                                        spillBlock = splitBlock;
+                                    } else {
+                                        spillBlock = commonDominator(spillBlock, splitBlock);
+                                        assert spillBlock != null;
+                                    }
+                                }
+                            }
+                        }
+                    }
+                    if (spillBlock == null) {
+                        // no spill interval
+                        interval.setSpillState(SpillState.StoreAtDefinition);
+                    } else {
+                        // move out of loops
+                        if (defBlock.getLoopDepth() < spillBlock.getLoopDepth()) {
+                            spillBlock = moveSpillOutOfLoop(defBlock, spillBlock);
+                        }
+
+                        /*
+                         * If the spill block is the begin of the first split child (aka the value
+                         * is on the stack) spill in the dominator.
+                         */
+                        assert firstSpillChild != null;
+                        if (!defBlock.equals(spillBlock) && spillBlock.equals(allocator.blockForId(firstSpillChild.from()))) {
+                            AbstractBlockBase<?> dom = spillBlock.getDominator();
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("Spill block (%s) is the beginning of a spill child -> use dominator (%s)", spillBlock, dom);
+                            }
+                            spillBlock = dom;
+                        }
+
+                        if (!defBlock.equals(spillBlock)) {
+                            assert dominates(defBlock, spillBlock);
+                            betterSpillPos.increment();
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("Better spill position found (Block %s)", spillBlock);
+                            }
+
+                            if (defBlock.probability() <= spillBlock.probability()) {
+                                // better spill block has the same probability -> do nothing
+                                interval.setSpillState(SpillState.StoreAtDefinition);
+                            } else {
+                                LIRInsertionBuffer insertionBuffer = insertionBuffers[spillBlock.getId()];
+                                if (insertionBuffer == null) {
+                                    insertionBuffer = new LIRInsertionBuffer();
+                                    insertionBuffers[spillBlock.getId()] = insertionBuffer;
+                                    insertionBuffer.init(allocator.ir.getLIRforBlock(spillBlock));
+                                }
+                                int spillOpId = allocator.getFirstLirInstructionId(spillBlock);
+                                // insert spill move
+                                AllocatableValue fromLocation = interval.getSplitChildAtOpId(spillOpId, OperandMode.DEF, allocator).location();
+                                AllocatableValue toLocation = LinearScan.canonicalSpillOpr(interval);
+                                LIRInstruction move = allocator.getSpillMoveFactory().createMove(toLocation, fromLocation);
+                                move.setId(LinearScan.DOMINATOR_SPILL_MOVE_ID);
+                                /*
+                                 * We can use the insertion buffer directly because we always insert
+                                 * at position 1.
+                                 */
+                                insertionBuffer.append(1, move);
+
+                                betterSpillPosWithLowerProbability.increment();
+                                interval.setSpillDefinitionPos(spillOpId);
+                            }
+                        } else {
+                            // definition is the best choice
+                            interval.setSpillState(SpillState.StoreAtDefinition);
+                        }
+                    }
+                }
+            }
+        }
+        for (LIRInsertionBuffer insertionBuffer : insertionBuffers) {
+            if (insertionBuffer != null) {
+                assert insertionBuffer.initialized() : "Insertion buffer is nonnull but not initialized!";
+                insertionBuffer.finish();
+            }
+        }
+    }
+
+    /**
+     * Iterate over all {@link AbstractBlockBase blocks} of an interval.
+     */
+    private class IntervalBlockIterator implements Iterator<AbstractBlockBase<?>> {
+
+        Range range;
+        AbstractBlockBase<?> block;
+
+        public IntervalBlockIterator(Interval interval) {
+            range = interval.first();
+            block = allocator.blockForId(range.from);
+        }
+
+        public AbstractBlockBase<?> next() {
+            AbstractBlockBase<?> currentBlock = block;
+            int nextBlockIndex = block.getLinearScanNumber() + 1;
+            if (nextBlockIndex < allocator.sortedBlocks.size()) {
+                block = allocator.sortedBlocks.get(nextBlockIndex);
+                if (range.to <= allocator.getFirstLirInstructionId(block)) {
+                    range = range.next;
+                    if (range == Range.EndMarker) {
+                        block = null;
+                    } else {
+                        block = allocator.blockForId(range.from);
+                    }
+                }
+            } else {
+                block = null;
+            }
+            return currentBlock;
+        }
+
+        public boolean hasNext() {
+            return block != null;
+        }
+    }
+
+    private Iterable<AbstractBlockBase<?>> blocksForInterval(Interval interval) {
+        return new Iterable<AbstractBlockBase<?>>() {
+            public Iterator<AbstractBlockBase<?>> iterator() {
+                return new IntervalBlockIterator(interval);
+            }
+        };
+    }
+
+    private static AbstractBlockBase<?> moveSpillOutOfLoop(AbstractBlockBase<?> defBlock, AbstractBlockBase<?> spillBlock) {
+        int defLoopDepth = defBlock.getLoopDepth();
+        for (AbstractBlockBase<?> block = spillBlock.getDominator(); !defBlock.equals(block); block = block.getDominator()) {
+            assert block != null : "spill block not dominated by definition block?";
+            if (block.getLoopDepth() <= defLoopDepth) {
+                assert block.getLoopDepth() == defLoopDepth : "Cannot spill an interval outside of the loop where it is defined!";
+                return block;
+            }
+        }
+        return defBlock;
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -49,11 +49,13 @@
 
     @Override
     protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        final LinearScan allocator;
         if (LinearScanPhase.SSA_LSRA.getValue()) {
-            new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
+            allocator = new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig()));
         } else {
-            new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
+            allocator = new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig()));
         }
+        allocator.allocate(target, lirGenRes, codeEmittingOrder, linearScanOrder, spillMoveFactory);
     }
 
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanRegisterAllocationPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.*;
+import com.oracle.graal.lir.phases.*;
+
+final class LinearScanRegisterAllocationPhase extends AllocationPhase {
+
+    private final LinearScan allocator;
+
+    LinearScanRegisterAllocationPhase(LinearScan allocator) {
+        this.allocator = allocator;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        allocator.printIntervals("Before register allocation");
+        allocateRegisters();
+        allocator.printIntervals("After register allocation");
+    }
+
+    void allocateRegisters() {
+        try (Indent indent = Debug.logAndIndent("allocate registers")) {
+            Interval precoloredIntervals;
+            Interval notPrecoloredIntervals;
+
+            Interval.Pair result = allocator.createUnhandledLists(LinearScan.IS_PRECOLORED_INTERVAL, LinearScan.IS_VARIABLE_INTERVAL);
+            precoloredIntervals = result.first;
+            notPrecoloredIntervals = result.second;
+
+            // allocate cpu registers
+            LinearScanWalker lsw;
+            if (OptimizingLinearScanWalker.Options.LSRAOptimization.getValue()) {
+                lsw = new OptimizingLinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals);
+            } else {
+                lsw = new LinearScanWalker(allocator, precoloredIntervals, notPrecoloredIntervals);
+            }
+            lsw.walk();
+            lsw.finishAllocation();
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanResolveDataFlowPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,197 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
+import com.oracle.graal.lir.phases.*;
+
+/**
+ * Phase 6: resolve data flow
+ *
+ * Insert moves at edges between blocks if intervals have been split.
+ */
+class LinearScanResolveDataFlowPhase extends AllocationPhase {
+
+    protected final LinearScan allocator;
+
+    LinearScanResolveDataFlowPhase(LinearScan allocator) {
+        this.allocator = allocator;
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
+        resolveDataFlow();
+    }
+
+    void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> midBlock, MoveResolver moveResolver) {
+        assert moveResolver.checkEmpty();
+        assert midBlock == null ||
+                        (midBlock.getPredecessorCount() == 1 && midBlock.getSuccessorCount() == 1 && midBlock.getPredecessors().get(0).equals(fromBlock) && midBlock.getSuccessors().get(0).equals(
+                                        toBlock));
+
+        int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock);
+        int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1;
+        int numOperands = allocator.operandSize();
+        BitSet liveAtEdge = allocator.getBlockData(toBlock).liveIn;
+
+        // visit all variables for which the liveAtEdge bit is set
+        for (int operandNum = liveAtEdge.nextSetBit(0); operandNum >= 0; operandNum = liveAtEdge.nextSetBit(operandNum + 1)) {
+            assert operandNum < numOperands : "live information set for not exisiting interval";
+            assert allocator.getBlockData(fromBlock).liveOut.get(operandNum) && allocator.getBlockData(toBlock).liveIn.get(operandNum) : "interval not live at this edge";
+
+            Interval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), fromBlockLastInstructionId, LIRInstruction.OperandMode.DEF);
+            Interval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(operandNum), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF);
+
+            if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) {
+                // need to insert move instruction
+                moveResolver.addMapping(fromInterval, toInterval);
+            }
+        }
+    }
+
+    void resolveFindInsertPos(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, MoveResolver moveResolver) {
+        if (fromBlock.getSuccessorCount() <= 1) {
+            if (Debug.isLogEnabled()) {
+                Debug.log("inserting moves at end of fromBlock B%d", fromBlock.getId());
+            }
+
+            List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(fromBlock);
+            LIRInstruction instr = instructions.get(instructions.size() - 1);
+            if (instr instanceof StandardOp.JumpOp) {
+                // insert moves before branch
+                moveResolver.setInsertPosition(instructions, instructions.size() - 1);
+            } else {
+                moveResolver.setInsertPosition(instructions, instructions.size());
+            }
+
+        } else {
+            if (Debug.isLogEnabled()) {
+                Debug.log("inserting moves at beginning of toBlock B%d", toBlock.getId());
+            }
+
+            if (DetailedAsserts.getValue()) {
+                assert allocator.ir.getLIRforBlock(fromBlock).get(0) instanceof StandardOp.LabelOp : "block does not start with a label";
+
+                /*
+                 * Because the number of predecessor edges matches the number of successor edges,
+                 * blocks which are reached by switch statements may have be more than one
+                 * predecessor but it will be guaranteed that all predecessors will be the same.
+                 */
+                for (AbstractBlockBase<?> predecessor : toBlock.getPredecessors()) {
+                    assert fromBlock == predecessor : "all critical edges must be broken";
+                }
+            }
+
+            moveResolver.setInsertPosition(allocator.ir.getLIRforBlock(toBlock), 1);
+        }
+    }
+
+    /**
+     * Inserts necessary moves (spilling or reloading) at edges between blocks for intervals that
+     * have been split.
+     */
+    void resolveDataFlow() {
+        try (Indent indent = Debug.logAndIndent("resolve data flow")) {
+
+            int numBlocks = allocator.blockCount();
+            MoveResolver moveResolver = allocator.createMoveResolver();
+            BitSet blockCompleted = new BitSet(numBlocks);
+            BitSet alreadyResolved = new BitSet(numBlocks);
+
+            for (AbstractBlockBase<?> block : allocator.sortedBlocks) {
+
+                // check if block has only one predecessor and only one successor
+                if (block.getPredecessorCount() == 1 && block.getSuccessorCount() == 1) {
+                    List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(block);
+                    assert instructions.get(0) instanceof StandardOp.LabelOp : "block must start with label";
+                    assert instructions.get(instructions.size() - 1) instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump";
+
+                    // check if block is empty (only label and branch)
+                    if (instructions.size() == 2) {
+                        AbstractBlockBase<?> pred = block.getPredecessors().iterator().next();
+                        AbstractBlockBase<?> sux = block.getSuccessors().iterator().next();
+
+                        // prevent optimization of two consecutive blocks
+                        if (!blockCompleted.get(pred.getLinearScanNumber()) && !blockCompleted.get(sux.getLinearScanNumber())) {
+                            if (Debug.isLogEnabled()) {
+                                Debug.log(" optimizing empty block B%d (pred: B%d, sux: B%d)", block.getId(), pred.getId(), sux.getId());
+                            }
+
+                            blockCompleted.set(block.getLinearScanNumber());
+
+                            /*
+                             * Directly resolve between pred and sux (without looking at the empty
+                             * block between).
+                             */
+                            resolveCollectMappings(pred, sux, block, moveResolver);
+                            if (moveResolver.hasMappings()) {
+                                moveResolver.setInsertPosition(instructions, 1);
+                                moveResolver.resolveAndAppendMoves();
+                            }
+                        }
+                    }
+                }
+            }
+
+            for (AbstractBlockBase<?> fromBlock : allocator.sortedBlocks) {
+                if (!blockCompleted.get(fromBlock.getLinearScanNumber())) {
+                    alreadyResolved.clear();
+                    alreadyResolved.or(blockCompleted);
+
+                    for (AbstractBlockBase<?> toBlock : fromBlock.getSuccessors()) {
+
+                        /*
+                         * Check for duplicate edges between the same blocks (can happen with switch
+                         * blocks).
+                         */
+                        if (!alreadyResolved.get(toBlock.getLinearScanNumber())) {
+                            if (Debug.isLogEnabled()) {
+                                Debug.log("processing edge between B%d and B%d", fromBlock.getId(), toBlock.getId());
+                            }
+
+                            alreadyResolved.set(toBlock.getLinearScanNumber());
+
+                            // collect all intervals that have been split between
+                            // fromBlock and toBlock
+                            resolveCollectMappings(fromBlock, toBlock, null, moveResolver);
+                            if (moveResolver.hasMappings()) {
+                                resolveFindInsertPos(fromBlock, toBlock, moveResolver);
+                                moveResolver.resolveAndAppendMoves();
+                            }
+                        }
+                    }
+                }
+            }
+
+        }
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanWalker.java	Tue May 12 20:56:04 2015 +0200
@@ -483,7 +483,7 @@
 
                     allocator.assignSpillSlot(interval);
                     handleSpillSlot(interval);
-                    allocator.changeSpillState(interval, minSplitPos);
+                    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
@@ -529,7 +529,7 @@
                     Interval spilledPart = interval.split(optimalSplitPos, allocator);
                     allocator.assignSpillSlot(spilledPart);
                     handleSpillSlot(spilledPart);
-                    allocator.changeSpillState(spilledPart, optimalSplitPos);
+                    changeSpillState(spilledPart, optimalSplitPos);
 
                     if (!allocator.isBlockBegin(optimalSplitPos)) {
                         if (Debug.isLogEnabled()) {
@@ -551,6 +551,59 @@
         }
     }
 
+    // called during register allocation
+    private void changeSpillState(Interval interval, int spillPos) {
+        switch (interval.spillState()) {
+            case NoSpillStore: {
+                int defLoopDepth = allocator.blockForId(interval.spillDefinitionPos()).getLoopDepth();
+                int spillLoopDepth = allocator.blockForId(spillPos).getLoopDepth();
+
+                if (defLoopDepth < spillLoopDepth) {
+                    /*
+                     * The loop depth of the spilling position is higher then the loop depth at the
+                     * definition of the interval. Move write to memory out of loop.
+                     */
+                    if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) {
+                        // find best spill position in dominator the tree
+                        interval.setSpillState(SpillState.SpillInDominator);
+                    } else {
+                        // store at definition of the interval
+                        interval.setSpillState(SpillState.StoreAtDefinition);
+                    }
+                } else {
+                    /*
+                     * The interval is currently spilled only once, so for now there is no reason to
+                     * store the interval at the definition.
+                     */
+                    interval.setSpillState(SpillState.OneSpillStore);
+                }
+                break;
+            }
+
+            case OneSpillStore: {
+                if (LinearScan.Options.LSRAOptimizeSpillPosition.getValue()) {
+                    // the interval is spilled more then once
+                    interval.setSpillState(SpillState.SpillInDominator);
+                } else {
+                    // It is better to store it to memory at the definition.
+                    interval.setSpillState(SpillState.StoreAtDefinition);
+                }
+                break;
+            }
+
+            case SpillInDominator:
+            case StoreAtDefinition:
+            case StartInMemory:
+            case NoOptimization:
+            case NoDefinitionFound:
+                // nothing to do
+                break;
+
+            default:
+                throw new BailoutException("other states not allowed at this time");
+        }
+    }
+
     /**
      * This is called for every interval that is assigned to a stack slot.
      */
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Tue May 12 20:56:04 2015 +0200
@@ -226,7 +226,7 @@
 
     private void insertMove(Interval fromInterval, Interval toInterval) {
         assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
-        assert fromInterval.kind().equals(toInterval.kind()) : "move between different types";
+        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : "move between different types";
         assert insertIdx != -1 : "must setup insert position first";
 
         insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location()));
@@ -247,7 +247,7 @@
     }
 
     private void insertMove(Value fromOpr, Interval toInterval) {
-        assert fromOpr.getLIRKind().equals(toInterval.kind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind());
+        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromOpr.getLIRKind()) : format("move between different types %s %s", fromOpr.getLIRKind(), toInterval.kind());
         assert insertIdx != -1 : "must setup insert position first";
 
         AllocatableValue toOpr = toInterval.operand;
@@ -409,8 +409,8 @@
         }
 
         assert !fromInterval.operand.equals(toInterval.operand) : "from and to interval equal: " + fromInterval;
-        assert fromInterval.kind().equals(toInterval.kind()) || (fromInterval.kind().getPlatformKind().equals(toInterval.kind().getPlatformKind()) && toInterval.kind().isDerivedReference()) : String.format(
-                        "Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval, toInterval);
+        assert LIRKind.verifyMoveKinds(toInterval.kind(), fromInterval.kind()) : String.format("Kind mismatch: %s vs. %s, from=%s, to=%s", fromInterval.kind(), toInterval.kind(), fromInterval,
+                        toInterval);
         mappingFrom.add(fromInterval);
         mappingFromOpr.add(Value.ILLEGAL);
         mappingTo.add(toInterval);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinarScanResolveDataFlowPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,86 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.ssa.*;
+import com.oracle.graal.lir.ssa.SSAUtils.PhiValueVisitor;
+
+class SSALinarScanResolveDataFlowPhase extends LinearScanResolveDataFlowPhase {
+
+    private static final DebugMetric numPhiResolutionMoves = Debug.metric("SSA LSRA[numPhiResolutionMoves]");
+    private static final DebugMetric numStackToStackMoves = Debug.metric("SSA LSRA[numStackToStackMoves]");
+
+    SSALinarScanResolveDataFlowPhase(LinearScan allocator) {
+        super(allocator);
+    }
+
+    @Override
+    void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> midBlock, MoveResolver moveResolver) {
+        super.resolveCollectMappings(fromBlock, toBlock, midBlock, moveResolver);
+
+        if (toBlock.getPredecessorCount() > 1) {
+            int toBlockFirstInstructionId = allocator.getFirstLirInstructionId(toBlock);
+            int fromBlockLastInstructionId = allocator.getLastLirInstructionId(fromBlock) + 1;
+
+            AbstractBlockBase<?> phiOutBlock = midBlock != null ? midBlock : fromBlock;
+            List<LIRInstruction> instructions = allocator.ir.getLIRforBlock(phiOutBlock);
+            int phiOutIdx = SSAUtils.phiOutIndex(allocator.ir, phiOutBlock);
+            int phiOutId = midBlock != null ? fromBlockLastInstructionId : instructions.get(phiOutIdx).id();
+            assert phiOutId >= 0;
+
+            PhiValueVisitor visitor = new PhiValueVisitor() {
+
+                public void visit(Value phiIn, Value phiOut) {
+                    Interval toInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF);
+                    if (isConstant(phiOut)) {
+                        numPhiResolutionMoves.increment();
+                        moveResolver.addMapping(phiOut, toInterval);
+                    } else {
+                        Interval fromInterval = allocator.splitChildAtOpId(allocator.intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF);
+                        if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) {
+                            numPhiResolutionMoves.increment();
+                            if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) {
+                                moveResolver.addMapping(fromInterval, toInterval);
+                            } else {
+                                numStackToStackMoves.increment();
+                                moveResolver.addMapping(fromInterval, toInterval);
+                            }
+                        }
+                    }
+                }
+            };
+
+            SSAUtils.forEachPhiValuePair(allocator.ir, toBlock, phiOutBlock, visitor);
+            SSAUtils.removePhiOut(allocator.ir, phiOutBlock);
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java	Tue May 12 20:56:04 2015 +0200
@@ -22,32 +22,13 @@
  */
 package com.oracle.graal.lir.alloc.lsra;
 
-import static com.oracle.graal.api.code.ValueUtil.*;
-import static com.oracle.graal.lir.LIRValueUtil.*;
-
-import java.util.*;
-
 import com.oracle.graal.api.code.*;
-import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.alloc.*;
-import com.oracle.graal.compiler.common.cfg.*;
-import com.oracle.graal.debug.*;
-import com.oracle.graal.debug.Debug.Scope;
-import com.oracle.graal.lir.*;
-import com.oracle.graal.lir.LIRInstruction.OperandFlag;
-import com.oracle.graal.lir.LIRInstruction.OperandMode;
-import com.oracle.graal.lir.StandardOp.LabelOp;
-import com.oracle.graal.lir.StandardOp.MoveOp;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
-import com.oracle.graal.lir.ssa.*;
-import com.oracle.graal.lir.ssa.SSAUtils.PhiValueVisitor;
 
 final class SSALinearScan extends LinearScan {
 
-    private static final DebugMetric numPhiResolutionMoves = Debug.metric("SSA LSRA[numPhiResolutionMoves]");
-    private static final DebugMetric numStackToStackMoves = Debug.metric("SSA LSRA[numStackToStackMoves]");
-
     SSALinearScan(TargetDescription target, LIRGenerationResult res, SpillMoveFactory spillMoveFactory, RegisterAllocationConfig regAllocConfig) {
         super(target, res, spillMoveFactory, regAllocConfig);
     }
@@ -60,141 +41,18 @@
     }
 
     @Override
-    protected int firstInstructionOfInterest() {
-        // also look at Labels as they define PHI values
-        return 0;
-    }
-
-    @Override
-    void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, AbstractBlockBase<?> midBlock, MoveResolver moveResolver) {
-        super.resolveCollectMappings(fromBlock, toBlock, midBlock, moveResolver);
-
-        if (toBlock.getPredecessorCount() > 1) {
-            int toBlockFirstInstructionId = getFirstLirInstructionId(toBlock);
-            int fromBlockLastInstructionId = getLastLirInstructionId(fromBlock) + 1;
-
-            AbstractBlockBase<?> phiOutBlock = midBlock != null ? midBlock : fromBlock;
-            List<LIRInstruction> instructions = ir.getLIRforBlock(phiOutBlock);
-            int phiOutIdx = SSAUtils.phiOutIndex(ir, phiOutBlock);
-            int phiOutId = midBlock != null ? fromBlockLastInstructionId : instructions.get(phiOutIdx).id();
-            assert phiOutId >= 0;
-
-            PhiValueVisitor visitor = new PhiValueVisitor() {
-
-                public void visit(Value phiIn, Value phiOut) {
-                    Interval toInterval = splitChildAtOpId(intervalFor(phiIn), toBlockFirstInstructionId, LIRInstruction.OperandMode.DEF);
-                    if (isConstant(phiOut)) {
-                        numPhiResolutionMoves.increment();
-                        moveResolver.addMapping(phiOut, toInterval);
-                    } else {
-                        Interval fromInterval = splitChildAtOpId(intervalFor(phiOut), phiOutId, LIRInstruction.OperandMode.DEF);
-                        if (fromInterval != toInterval && !fromInterval.location().equals(toInterval.location())) {
-                            numPhiResolutionMoves.increment();
-                            if (!(isStackSlotValue(toInterval.location()) && isStackSlotValue(fromInterval.location()))) {
-                                moveResolver.addMapping(fromInterval, toInterval);
-                            } else {
-                                numStackToStackMoves.increment();
-                                moveResolver.addMapping(fromInterval, toInterval);
-                            }
-                        }
-                    }
-                }
-            };
-
-            SSAUtils.forEachPhiValuePair(ir, toBlock, phiOutBlock, visitor);
-            SSAUtils.removePhiOut(ir, phiOutBlock);
-        }
-    }
-
-    @Override
-    protected void beforeSpillMoveElimination() {
-        /*
-         * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value
-         * matches (ie. there is no resolution move) are falsely detected as errors.
-         */
-        try (Scope s1 = Debug.scope("Remove Phi In")) {
-            for (AbstractBlockBase<?> toBlock : sortedBlocks) {
-                if (toBlock.getPredecessorCount() > 1) {
-                    SSAUtils.removePhiIn(ir, toBlock);
-                }
-            }
-        }
+    protected LinearScanLifetimeAnalysisPhase createLifetimeAnalysisPhase() {
+        return new SSALinearScanLifetimeAnalysisPhase(this);
     }
 
     @Override
-    protected boolean canEliminateSpillMove(AbstractBlockBase<?> block, MoveOp move) {
-        // SSA Linear Scan might introduce moves to stack slots
-        assert isVariable(move.getResult()) || LinearScanPhase.SSA_LSRA.getValue() : "Move should not be produced in a non-SSA compilation: " + move;
-
-        Interval curInterval = intervalFor(move.getResult());
-
-        if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory() && !isPhiResolutionMove(block, move, curInterval)) {
-            assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location();
-            return true;
-        }
-        return false;
+    protected LinearScanResolveDataFlowPhase createResolveDataFlowPhase() {
+        return new SSALinarScanResolveDataFlowPhase(this);
     }
 
     @Override
-    void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
-        super.addRegisterHint(op, targetValue, mode, flags, hintAtDef);
-
-        if (hintAtDef && op instanceof LabelOp) {
-            LabelOp label = (LabelOp) op;
-
-            Interval to = getOrCreateInterval((AllocatableValue) targetValue);
-
-            SSAUtils.forEachPhiRegisterHint(ir, blockForId(label.id()), label, targetValue, mode, (ValueConsumer) (registerHint, valueMode, valueFlags) -> {
-                if (isVariableOrRegister(registerHint)) {
-                    Interval from = getOrCreateInterval((AllocatableValue) registerHint);
-
-                    setHint(op, to, from);
-                    setHint(op, from, to);
-                }
-            });
-        }
+    protected LinearScanEliminateSpillMovePhase createSpillMoveEliminationPhase() {
+        return new SSALinearScanEliminateSpillMovePhase(this);
     }
 
-    private static void setHint(final LIRInstruction op, Interval target, Interval source) {
-        Interval currentHint = target.locationHint(false);
-        if (currentHint == null || currentHint.from() > target.from()) {
-            /*
-             * Update hint if there was none or if the hint interval starts after the hinted
-             * interval.
-             */
-            target.setLocationHint(source);
-            if (Debug.isLogEnabled()) {
-                Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber);
-            }
-        }
-    }
-
-    private boolean isPhiResolutionMove(AbstractBlockBase<?> block, MoveOp move, Interval toInterval) {
-        if (!LinearScanPhase.SSA_LSRA.getValue()) {
-            return false;
-        }
-        if (!toInterval.isSplitParent()) {
-            return false;
-        }
-        if ((toInterval.from() & 1) == 1) {
-            // phi intervals start at even positions.
-            return false;
-        }
-        if (block.getSuccessorCount() != 1) {
-            return false;
-        }
-        LIRInstruction op = instructionForId(toInterval.from());
-        if (!(op instanceof LabelOp)) {
-            return false;
-        }
-        AbstractBlockBase<?> intStartBlock = blockForId(toInterval.from());
-        assert ir.getLIRforBlock(intStartBlock).get(0).equals(op);
-        if (!block.getSuccessors().get(0).equals(intStartBlock)) {
-            return false;
-        }
-        try (Indent indet = Debug.indent()) {
-            Debug.log("Is a move (%s) to phi interval %s", move, toInterval);
-        }
-        return true;
-    }
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanEliminateSpillMovePhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,104 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
+
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.debug.Debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.*;
+import com.oracle.graal.lir.ssa.*;
+
+public class SSALinearScanEliminateSpillMovePhase extends LinearScanEliminateSpillMovePhase {
+
+    SSALinearScanEliminateSpillMovePhase(LinearScan allocator) {
+        super(allocator);
+    }
+
+    @Override
+    protected int firstInstructionOfInterest() {
+        // also look at Labels as they define PHI values
+        return 0;
+    }
+
+    @Override
+    protected void beforeSpillMoveElimination() {
+        /*
+         * PHI Ins are needed for the RegisterVerifier, otherwise PHIs where the Out and In value
+         * matches (ie. there is no resolution move) are falsely detected as errors.
+         */
+        try (Scope s1 = Debug.scope("Remove Phi In")) {
+            for (AbstractBlockBase<?> toBlock : allocator.sortedBlocks) {
+                if (toBlock.getPredecessorCount() > 1) {
+                    SSAUtils.removePhiIn(allocator.ir, toBlock);
+                }
+            }
+        }
+    }
+
+    @Override
+    protected boolean canEliminateSpillMove(AbstractBlockBase<?> block, MoveOp move) {
+        // SSA Linear Scan might introduce moves to stack slots
+        assert isVariable(move.getResult()) || LinearScanPhase.SSA_LSRA.getValue() : "Move should not be produced in a non-SSA compilation: " + move;
+
+        Interval curInterval = allocator.intervalFor(move.getResult());
+
+        if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory() && !isPhiResolutionMove(block, move, curInterval)) {
+            assert isStackSlotValue(curInterval.location()) : "Not a stack slot: " + curInterval.location();
+            return true;
+        }
+        return false;
+    }
+
+    private boolean isPhiResolutionMove(AbstractBlockBase<?> block, MoveOp move, Interval toInterval) {
+        if (!LinearScanPhase.SSA_LSRA.getValue()) {
+            return false;
+        }
+        if (!toInterval.isSplitParent()) {
+            return false;
+        }
+        if ((toInterval.from() & 1) == 1) {
+            // phi intervals start at even positions.
+            return false;
+        }
+        if (block.getSuccessorCount() != 1) {
+            return false;
+        }
+        LIRInstruction op = allocator.instructionForId(toInterval.from());
+        if (!(op instanceof LabelOp)) {
+            return false;
+        }
+        AbstractBlockBase<?> intStartBlock = allocator.blockForId(toInterval.from());
+        assert allocator.ir.getLIRforBlock(intStartBlock).get(0).equals(op);
+        if (!block.getSuccessors().get(0).equals(intStartBlock)) {
+            return false;
+        }
+        try (Indent indet = Debug.indent()) {
+            Debug.log("Is a move (%s) to phi interval %s", move, toInterval);
+        }
+        return true;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScanLifetimeAnalysisPhase.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2015, 2015, 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.alloc.lsra;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.*;
+import com.oracle.graal.lir.StandardOp.*;
+import com.oracle.graal.lir.ssa.*;
+
+public class SSALinearScanLifetimeAnalysisPhase extends LinearScanLifetimeAnalysisPhase {
+
+    SSALinearScanLifetimeAnalysisPhase(LinearScan linearScan) {
+        super(linearScan);
+    }
+
+    @Override
+    void addRegisterHint(final LIRInstruction op, final Value targetValue, OperandMode mode, EnumSet<OperandFlag> flags, final boolean hintAtDef) {
+        super.addRegisterHint(op, targetValue, mode, flags, hintAtDef);
+
+        if (hintAtDef && op instanceof LabelOp) {
+            LabelOp label = (LabelOp) op;
+
+            Interval to = allocator.getOrCreateInterval((AllocatableValue) targetValue);
+
+            SSAUtils.forEachPhiRegisterHint(allocator.ir, allocator.blockForId(label.id()), label, targetValue, mode, (ValueConsumer) (registerHint, valueMode, valueFlags) -> {
+                if (LinearScan.isVariableOrRegister(registerHint)) {
+                    Interval from = allocator.getOrCreateInterval((AllocatableValue) registerHint);
+
+                    setHint(op, to, from);
+                    setHint(op, from, to);
+                }
+            });
+        }
+    }
+
+    private static void setHint(final LIRInstruction op, Interval target, Interval source) {
+        Interval currentHint = target.locationHint(false);
+        if (currentHint == null || currentHint.from() > target.from()) {
+            /*
+             * Update hint if there was none or if the hint interval starts after the hinted
+             * interval.
+             */
+            target.setLocationHint(source);
+            if (Debug.isLogEnabled()) {
+                Debug.log("operation at opId %d: added hint from interval %d to %d", op.id(), source.operandNumber, target.operandNumber);
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/stackslotalloc/StackInterval.java	Tue May 12 20:56:04 2015 +0200
@@ -99,7 +99,7 @@
 
     @Override
     public String toString() {
-        return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint.getOperand());
+        return String.format("SI[%d-%d] k=%s o=%s l=%s h=%s", from, to, kind, operand, location, hint != null ? hint.getOperand() : "null");
     }
 
     public void setLocationHint(StackInterval locationHint) {
--- a/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.loop/src/com/oracle/graal/loop/LoopEx.java	Tue May 12 20:56:04 2015 +0200
@@ -162,7 +162,7 @@
     public boolean detectCounted() {
         LoopBeginNode loopBegin = loopBegin();
         FixedNode next = loopBegin.next();
-        while (next instanceof FixedGuardNode || next instanceof ValueAnchorNode) {
+        while (next instanceof FixedGuardNode || next instanceof ValueAnchorNode || next instanceof InfopointNode) {
             next = ((FixedWithNextNode) next).next();
         }
         if (next instanceof IfNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/FrameState.java	Tue May 12 20:56:04 2015 +0200
@@ -117,6 +117,19 @@
                         bci == BytecodeFrame.INVALID_FRAMESTATE_BCI;
     }
 
+    /**
+     * Creates a placeholder frame state with a single element on the stack representing a return
+     * value. This allows the parsing of an intrinsic to communicate the returned value in a
+     * {@link StateSplit#stateAfter() stateAfter} to the inlining call site.
+     *
+     * @param bci this must be {@link BytecodeFrame#AFTER_BCI}
+     */
+    public FrameState(int bci, ValueNode returnValue) {
+        this(null, null, bci, 0, returnValue.getKind().getSlotCount(), 0, false, false, null, Collections.<EscapeObjectState> emptyList());
+        assert bci == BytecodeFrame.AFTER_BCI;
+        this.values.initialize(0, returnValue);
+    }
+
     public FrameState(FrameState outerFrameState, ResolvedJavaMethod method, int bci, ValueNode[] locals, ValueNode[] stack, int stackSize, ValueNode[] locks, List<MonitorIdNode> monitorIds,
                     boolean rethrowException, boolean duringCall) {
         this(outerFrameState, method, bci, locals.length, stackSize, locks.length, rethrowException, duringCall, monitorIds, Collections.<EscapeObjectState> emptyList());
@@ -153,6 +166,17 @@
         this.outerFrameState = x;
     }
 
+    public BytecodePosition toBytecodePosition() {
+        return toBytecodePosition(this);
+    }
+
+    public static BytecodePosition toBytecodePosition(FrameState fs) {
+        if (fs == null) {
+            return null;
+        }
+        return new BytecodePosition(toBytecodePosition(fs.outerFrameState()), fs.method(), fs.bci);
+    }
+
     /**
      * @see BytecodeFrame#rethrowException
      */
@@ -410,7 +434,11 @@
         String nl = CodeUtil.NEW_LINE;
         FrameState fs = frameState;
         while (fs != null) {
-            MetaUtil.appendLocation(sb, fs.method, fs.bci).append(nl);
+            MetaUtil.appendLocation(sb, fs.method, fs.bci);
+            if (BytecodeFrame.isPlaceholderBci(fs.bci)) {
+                sb.append("//").append(getPlaceholderBciName(fs.bci));
+            }
+            sb.append(nl);
             sb.append("locals: [");
             for (int i = 0; i < fs.localsSize(); i++) {
                 sb.append(i == 0 ? "" : ", ").append(fs.localAt(i) == null ? "_" : fs.localAt(i).toString(Verbosity.Id));
@@ -434,7 +462,11 @@
         if (verbosity == Verbosity.Debugger) {
             return toString(this);
         } else if (verbosity == Verbosity.Name) {
-            return super.toString(Verbosity.Name) + "@" + bci;
+            String res = super.toString(Verbosity.Name) + "@" + bci;
+            if (BytecodeFrame.isPlaceholderBci(bci)) {
+                res += "[" + getPlaceholderBciName(bci) + "]";
+            }
+            return res;
         } else {
             return super.toString(verbosity);
         }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/GraphDecoder.java	Tue May 12 20:56:04 2015 +0200
@@ -409,7 +409,7 @@
             methodScope.unwindNode = (UnwindNode) node;
 
         } else {
-            simplifyFixedNode(methodScope, loopScope, nodeOrderId, node);
+            handleFixedNode(methodScope, loopScope, nodeOrderId, node);
         }
 
         return resultScope;
@@ -527,7 +527,7 @@
      * @param nodeOrderId The orderId of the node.
      * @param node The node to be simplified.
      */
-    protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
+    protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
     }
 
     protected void handleProxyNodes(MethodScope methodScope, LoopScope loopScope, LoopExitNode loopExit) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimpleInfopointNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimpleInfopointNode.java	Tue May 12 20:56:04 2015 +0200
@@ -48,15 +48,7 @@
     }
 
     public void addCaller(BytecodePosition caller) {
-        this.position = relink(this.position, caller);
-    }
-
-    private static BytecodePosition relink(BytecodePosition position, BytecodePosition link) {
-        if (position.getCaller() == null) {
-            return new BytecodePosition(link, position.getMethod(), position.getBCI());
-        } else {
-            return new BytecodePosition(relink(position.getCaller(), link), position.getMethod(), position.getBCI());
-        }
+        this.position = position.addCaller(caller);
     }
 
     @Override
@@ -65,4 +57,21 @@
             graph().removeFixed(this);
         }
     }
+
+    public void setPosition(BytecodePosition position) {
+        this.position = position;
+    }
+
+    @Override
+    public boolean verify() {
+        BytecodePosition pos = position;
+        if (pos != null) {
+            // Verify that the outermost position belongs to this graph.
+            while (pos.getCaller() != null) {
+                pos = pos.getCaller();
+            }
+            assert pos.getMethod().equals(graph().method());
+        }
+        return super.verify();
+    }
 }
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/SimplifyingGraphDecoder.java	Tue May 12 20:56:04 2015 +0200
@@ -91,7 +91,7 @@
     }
 
     @Override
-    protected void simplifyFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
+    protected void handleFixedNode(MethodScope methodScope, LoopScope loopScope, int nodeOrderId, FixedNode node) {
         if (node instanceof IfNode) {
             IfNode ifNode = (IfNode) node;
             if (ifNode.condition() instanceof LogicNegationNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/OrNode.java	Tue May 12 20:56:04 2015 +0200
@@ -37,7 +37,7 @@
 import com.oracle.graal.nodes.util.*;
 
 @NodeInfo(shortName = "|")
-public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryCommutative<ValueNode> {
+public final class OrNode extends BinaryArithmeticNode<Or> implements BinaryCommutative<ValueNode>, NarrowableArithmeticNode {
 
     public static final NodeClass<OrNode> TYPE = NodeClass.create(OrNode.class);
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SqrtNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/SqrtNode.java	Tue May 12 20:56:04 2015 +0200
@@ -34,7 +34,7 @@
  * Square root.
  */
 @NodeInfo
-public final class SqrtNode extends UnaryArithmeticNode<Sqrt> implements ArithmeticLIRLowerable, NarrowableArithmeticNode {
+public final class SqrtNode extends UnaryArithmeticNode<Sqrt> implements ArithmeticLIRLowerable {
 
     public static final NodeClass<SqrtNode> TYPE = NodeClass.create(SqrtNode.class);
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/calc/XorNode.java	Tue May 12 20:56:04 2015 +0200
@@ -37,7 +37,7 @@
 import com.oracle.graal.nodes.util.*;
 
 @NodeInfo(shortName = "^")
-public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCommutative<ValueNode> {
+public final class XorNode extends BinaryArithmeticNode<Xor> implements BinaryCommutative<ValueNode>, NarrowableArithmeticNode {
 
     public static final NodeClass<XorNode> TYPE = NodeClass.create(XorNode.class);
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/extended/ForeignCallNode.java	Tue May 12 20:56:04 2015 +0200
@@ -107,6 +107,12 @@
     }
 
     @Override
+    public void setStateAfter(FrameState x) {
+        assert hasSideEffect() || x == null;
+        super.setStateAfter(x);
+    }
+
+    @Override
     public FrameState stateDuring() {
         return stateDuring;
     }
--- a/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.phases.common/src/com/oracle/graal/phases/common/inlining/InliningUtil.java	Tue May 12 20:56:04 2015 +0200
@@ -427,11 +427,18 @@
     }
 
     public static BytecodePosition processSimpleInfopoint(Invoke invoke, SimpleInfopointNode infopointNode, BytecodePosition incomingPos) {
-        BytecodePosition pos = incomingPos != null ? incomingPos : new BytecodePosition(toBytecodePosition(invoke.stateAfter().outerFrameState()), invoke.asNode().graph().method(), invoke.bci());
+        BytecodePosition pos = processBytecodePosition(invoke, incomingPos);
         infopointNode.addCaller(pos);
+        assert infopointNode.verify();
         return pos;
     }
 
+    public static BytecodePosition processBytecodePosition(Invoke invoke, BytecodePosition incomingPos) {
+        assert invoke.stateAfter() != null;
+        assert incomingPos == null || incomingPos.equals(InliningUtil.processBytecodePosition(invoke, null)) : incomingPos + " " + InliningUtil.processBytecodePosition(invoke, null);
+        return incomingPos != null ? incomingPos : new BytecodePosition(FrameState.toBytecodePosition(invoke.stateAfter().outerFrameState()), invoke.stateAfter().method(), invoke.bci());
+    }
+
     public static void processMonitorId(FrameState stateAfter, MonitorIdNode monitorIdNode) {
         if (stateAfter != null) {
             int callerLockDepth = stateAfter.nestedLockDepth();
@@ -439,13 +446,6 @@
         }
     }
 
-    private static BytecodePosition toBytecodePosition(FrameState fs) {
-        if (fs == null) {
-            return null;
-        }
-        return new BytecodePosition(toBytecodePosition(fs.outerFrameState()), fs.method(), fs.bci);
-    }
-
     protected static void processFrameStates(Invoke invoke, StructuredGraph inlineGraph, Map<Node, Node> duplicates, FrameState stateAtExceptionEdge, boolean alwaysDuplicateStateAfter) {
         FrameState stateAtReturn = invoke.stateAfter();
         FrameState outerFrameState = null;
@@ -468,14 +468,28 @@
         Kind invokeReturnKind = invoke.asNode().getKind();
 
         if (frameState.bci == BytecodeFrame.AFTER_BCI) {
+            FrameState stateAfterReturn = stateAtReturn;
+            if (frameState.method() == null) {
+                // This is a frame state for a side effect within an intrinsic
+                // that was parsed for post-parse intrinsification
+                for (Node usage : frameState.usages()) {
+                    if (usage instanceof ForeignCallNode) {
+                        // A foreign call inside an intrinsic needs to have
+                        // the BCI of the invoke being intrinsified
+                        ForeignCallNode foreign = (ForeignCallNode) usage;
+                        foreign.setBci(invoke.bci());
+                    }
+                }
+            }
+
             /*
              * pop return kind from invoke's stateAfter and replace with this frameState's return
              * value (top of stack)
              */
-            FrameState stateAfterReturn = stateAtReturn;
             if (invokeReturnKind != Kind.Void && (alwaysDuplicateStateAfter || (frameState.stackSize() > 0 && stateAfterReturn.stackAt(0) != frameState.stackAt(0)))) {
                 stateAfterReturn = stateAtReturn.duplicateModified(invokeReturnKind, frameState.stackAt(0));
             }
+
             frameState.replaceAndDelete(stateAfterReturn);
             return stateAfterReturn;
         } else if (stateAtExceptionEdge != null && isStateAfterException(frameState)) {
@@ -581,25 +595,26 @@
         ValueNode singleReturnValue = null;
         PhiNode returnValuePhi = null;
         for (ReturnNode returnNode : returnNodes) {
-            if (returnNode.result() != null) {
-                if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == returnNode.result())) {
+            ValueNode result = returnNode.result();
+            if (result != null) {
+                if (returnValuePhi == null && (singleReturnValue == null || singleReturnValue == result)) {
                     /* Only one return value, so no need yet for a phi node. */
-                    singleReturnValue = returnNode.result();
+                    singleReturnValue = result;
 
                 } else if (returnValuePhi == null) {
                     /* Found a second return value, so create phi node. */
-                    returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(returnNode.result().stamp().unrestricted(), merge));
+                    returnValuePhi = merge.graph().addWithoutUnique(new ValuePhiNode(result.stamp().unrestricted(), merge));
                     if (canonicalizedNodes != null) {
                         canonicalizedNodes.add(returnValuePhi);
                     }
                     for (int i = 0; i < merge.forwardEndCount(); i++) {
                         returnValuePhi.addInput(singleReturnValue);
                     }
-                    returnValuePhi.addInput(returnNode.result());
+                    returnValuePhi.addInput(result);
 
                 } else {
                     /* Multiple return values, just add to existing phi node. */
-                    returnValuePhi.addInput(returnNode.result());
+                    returnValuePhi.addInput(result);
                 }
             }
 
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DynamicNewArrayTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/DynamicNewArrayTest.java	Tue May 12 20:56:04 2015 +0200
@@ -55,6 +55,7 @@
     @Test
     public void test4() {
         test("dynamic", Boolean.class, -7);
+        test("dynamicSynchronized", Boolean.class, -7);
     }
 
     @Test
@@ -95,4 +96,8 @@
     public static Object dynamic(Class<?> elementType, int length) {
         return Array.newInstance(elementType, length);
     }
+
+    public static synchronized Object dynamicSynchronized(Class<?> elementType, int length) {
+        return Array.newInstance(elementType, length);
+    }
 }
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/ObjectAccessTest.java	Tue May 12 20:56:04 2015 +0200
@@ -32,7 +32,6 @@
 import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing;
 import com.oracle.graal.word.*;
 
 /**
@@ -50,7 +49,7 @@
 
     @Override
     protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) {
-        return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(m, null, null);
     }
 
     @Test
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/PointerTest.java	Tue May 12 20:56:04 2015 +0200
@@ -35,7 +35,6 @@
 import com.oracle.graal.phases.common.*;
 import com.oracle.graal.phases.tiers.*;
 import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing;
 import com.oracle.graal.word.*;
 import com.oracle.graal.word.nodes.*;
 
@@ -56,7 +55,7 @@
 
     @Override
     protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) {
-        return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(m, null, null);
     }
 
     @Test
--- a/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements.test/src/com/oracle/graal/replacements/test/WordTest.java	Tue May 12 20:56:04 2015 +0200
@@ -29,7 +29,6 @@
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
 import com.oracle.graal.replacements.*;
-import com.oracle.graal.replacements.ReplacementsImpl.FrameStateProcessing;
 import com.oracle.graal.word.*;
 
 /**
@@ -45,7 +44,7 @@
 
     @Override
     protected StructuredGraph parseEager(ResolvedJavaMethod m, AllowAssumptions allowAssumptions) {
-        return installer.makeGraph(m, null, null, FrameStateProcessing.CollapseFrameForSingleSideEffect);
+        return installer.makeGraph(m, null, null);
     }
 
     @Test
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/CollapseFrameForSingleSideEffectPhase.java	Tue May 12 20:55:48 2015 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,240 +0,0 @@
-/*
- * Copyright (c) 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.replacements;
-
-import static com.oracle.graal.api.code.BytecodeFrame.*;
-
-import java.util.*;
-
-import com.oracle.graal.api.code.*;
-import com.oracle.graal.graph.*;
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.util.*;
-import com.oracle.graal.phases.*;
-import com.oracle.graal.phases.graph.*;
-import com.oracle.graal.phases.graph.ReentrantNodeIterator.LoopInfo;
-import com.oracle.graal.phases.graph.ReentrantNodeIterator.NodeIteratorClosure;
-
-/**
- * This phase ensures that there's a single {@linkplain BytecodeFrame#AFTER_BCI collapsed frame
- * state} per path.
- *
- * Removes other frame states from {@linkplain StateSplit#hasSideEffect() non-side-effecting} nodes
- * in the graph, and replaces them with {@linkplain BytecodeFrame#INVALID_FRAMESTATE_BCI invalid
- * frame states}.
- *
- * The invalid frame states ensure that no deoptimization to a snippet frame state will happen.
- */
-public class CollapseFrameForSingleSideEffectPhase extends Phase {
-
-    private static class IterationState {
-        public final IterationState previous;
-        public final Node node;
-        public final Collection<IterationState> merge;
-        public final boolean invalid;
-
-        private IterationState(IterationState previous, Node node, Collection<IterationState> merge, boolean invalid) {
-            this.previous = previous;
-            this.node = node;
-            this.merge = merge;
-            this.invalid = invalid;
-        }
-
-        public IterationState() {
-            this(null, null, null, false);
-        }
-
-        public IterationState addSideEffect(StateSplit sideEffect) {
-            return new IterationState(this, sideEffect.asNode(), null, true);
-        }
-
-        public IterationState addBranch(AbstractBeginNode begin) {
-            return new IterationState(this, begin, null, this.invalid);
-        }
-
-        public static IterationState merge(AbstractMergeNode merge, Collection<IterationState> before, boolean invalid) {
-            return new IterationState(null, merge, before, invalid);
-        }
-
-        public void markAll(NodeBitMap set) {
-            IterationState state = this;
-            while (state != null && state.node != null && !set.contains(state.node)) {
-                set.mark(state.node);
-                if (state.merge != null) {
-                    for (IterationState branch : state.merge) {
-                        branch.markAll(set);
-                    }
-                }
-                state = state.previous;
-            }
-        }
-
-        public void markMasked(NodeBitMap unmasked, NodeBitMap masked) {
-            IterationState state = this;
-            while (state != null && state.node != null && !masked.contains(state.node)) {
-                if (state.node instanceof StateSplit) {
-                    unmasked.mark(state.node);
-                    StateSplit split = (StateSplit) state.node;
-                    if (split.hasSideEffect() && state.previous != null) {
-                        state.previous.markAll(masked);
-                        return;
-                    }
-                }
-
-                if (state.merge != null) {
-                    for (IterationState branch : state.merge) {
-                        branch.markMasked(unmasked, masked);
-                    }
-                }
-                state = state.previous;
-            }
-        }
-    }
-
-    @Override
-    protected void run(StructuredGraph graph) {
-        CollapseFrameForSingleSideEffectClosure closure = new CollapseFrameForSingleSideEffectClosure();
-        ReentrantNodeIterator.apply(closure, graph.start(), new IterationState());
-        closure.finishProcessing(graph);
-    }
-
-    private static class CollapseFrameForSingleSideEffectClosure extends NodeIteratorClosure<IterationState> {
-
-        private List<IterationState> returnStates = new ArrayList<>();
-        private List<IterationState> unwindStates = new ArrayList<>();
-
-        @Override
-        protected IterationState processNode(FixedNode node, IterationState currentState) {
-            IterationState state = currentState;
-            if (node instanceof StateSplit) {
-                StateSplit stateSplit = (StateSplit) node;
-                FrameState frameState = stateSplit.stateAfter();
-                if (frameState != null) {
-                    if (stateSplit.hasSideEffect()) {
-                        setStateAfter(node.graph(), stateSplit, INVALID_FRAMESTATE_BCI, false);
-                        state = state.addSideEffect(stateSplit);
-                    } else if (currentState.invalid) {
-                        setStateAfter(node.graph(), stateSplit, INVALID_FRAMESTATE_BCI, false);
-                    } else if (stateSplit instanceof StartNode) {
-                        setStateAfter(node.graph(), stateSplit, BEFORE_BCI, false);
-                    } else {
-                        stateSplit.setStateAfter(null);
-                        if (frameState.hasNoUsages()) {
-                            GraphUtil.killWithUnusedFloatingInputs(frameState);
-                        }
-                    }
-                }
-            }
-            if (node instanceof ReturnNode) {
-                returnStates.add(currentState);
-            } else if (node instanceof UnwindNode) {
-                unwindStates.add(currentState);
-            }
-            return state;
-        }
-
-        @Override
-        protected IterationState merge(AbstractMergeNode merge, List<IterationState> states) {
-            boolean invalid = false;
-            for (IterationState state : states) {
-                if (state.invalid) {
-                    invalid = true;
-                    break;
-                }
-            }
-            return IterationState.merge(merge, states, invalid);
-        }
-
-        public void finishProcessing(StructuredGraph graph) {
-            NodeBitMap maskedSideEffects = new NodeBitMap(graph);
-            NodeBitMap returnSideEffects = new NodeBitMap(graph);
-            NodeBitMap unwindSideEffects = new NodeBitMap(graph);
-
-            for (IterationState returnState : returnStates) {
-                returnState.markMasked(returnSideEffects, maskedSideEffects);
-            }
-            for (IterationState unwindState : unwindStates) {
-                unwindState.markMasked(unwindSideEffects, maskedSideEffects);
-            }
-
-            for (Node returnSideEffect : returnSideEffects) {
-                if (!unwindSideEffects.contains(returnSideEffect) && !maskedSideEffects.contains(returnSideEffect)) {
-                    StateSplit split = (StateSplit) returnSideEffect;
-                    setStateAfter(graph, split, AFTER_BCI, true);
-                }
-            }
-
-            for (Node unwindSideEffect : unwindSideEffects) {
-                if (!returnSideEffects.contains(unwindSideEffect) && !maskedSideEffects.contains(unwindSideEffect)) {
-                    StateSplit split = (StateSplit) unwindSideEffect;
-                    setStateAfter(graph, split, AFTER_EXCEPTION_BCI, true);
-                }
-            }
-        }
-
-        @Override
-        protected IterationState afterSplit(AbstractBeginNode node, IterationState oldState) {
-            return oldState.addBranch(node);
-        }
-
-        @Override
-        protected Map<LoopExitNode, IterationState> processLoop(LoopBeginNode loop, IterationState initialState) {
-            LoopInfo<IterationState> info = ReentrantNodeIterator.processLoop(this, loop, initialState);
-
-            boolean isNowInvalid = initialState.invalid;
-            for (IterationState endState : info.endStates.values()) {
-                isNowInvalid |= endState.invalid;
-            }
-
-            if (isNowInvalid) {
-                setStateAfter(loop.graph(), loop, INVALID_FRAMESTATE_BCI, false);
-            }
-
-            IterationState endState = IterationState.merge(loop, info.endStates.values(), isNowInvalid);
-            return ReentrantNodeIterator.processLoop(this, loop, endState).exitStates;
-        }
-
-        /**
-         * Creates and sets a special frame state for a node. If the existing frame state is
-         * non-null and has no other usages, it is deleted via
-         * {@link GraphUtil#killWithUnusedFloatingInputs(Node)}.
-         *
-         * @param graph the graph context
-         * @param node the node whose frame state is updated
-         * @param bci {@link BytecodeFrame#BEFORE_BCI}, {@link BytecodeFrame#AFTER_EXCEPTION_BCI} or
-         *            {@link BytecodeFrame#INVALID_FRAMESTATE_BCI}
-         * @param replaceOnly only perform the update if the node currently has a non-null frame
-         *            state
-         */
-        private static void setStateAfter(StructuredGraph graph, StateSplit node, int bci, boolean replaceOnly) {
-            assert (bci == BEFORE_BCI && node instanceof StartNode) || bci == AFTER_BCI || bci == AFTER_EXCEPTION_BCI || bci == INVALID_FRAMESTATE_BCI;
-            FrameState currentStateAfter = node.stateAfter();
-            if (currentStateAfter != null || !replaceOnly) {
-                node.setStateAfter(graph.add(new FrameState(bci)));
-                if (currentStateAfter != null && currentStateAfter.hasNoUsages()) {
-                    GraphUtil.killWithUnusedFloatingInputs(currentStateAfter);
-                }
-            }
-        }
-    }
-}
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultGenericInvocationPlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -73,9 +73,9 @@
     }
 
     public boolean apply(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
-        if (b.parsingReplacement() && wordOperationPlugin.apply(b, method, args)) {
+        if (b.parsingIntrinsic() && wordOperationPlugin.apply(b, method, args)) {
             return true;
-        } else if (b.parsingReplacement()) {
+        } else if (b.parsingIntrinsic()) {
             NodeIntrinsic intrinsic = nodeIntrinsification.getIntrinsic(method);
             if (intrinsic != null) {
                 Signature sig = method.getSignature();
@@ -145,12 +145,18 @@
             b.add(new UnsafeStoreNode(copy.destinationObject(), copy.destinationOffset(), value, copy.accessKind(), copy.getLocationIdentity()));
             return true;
         } else if (res instanceof ForeignCallNode) {
-            ForeignCallNode foreign = (ForeignCallNode) res;
-            foreign.setBci(b.bci());
+            /*
+             * Need to update the BCI of a ForeignCallNode so that it gets the stateDuring in the
+             * case that the foreign call can deoptimize. As with all deoptimization, we need a
+             * state in a normal method as opposed to an intrinsic.
+             */
+            GraphBuilderContext ancestor = b.getNonReplacementAncestor();
+            if (ancestor != null) {
+                ForeignCallNode foreign = (ForeignCallNode) res;
+                foreign.setBci(ancestor.bci());
+            }
         }
 
-        res = b.add(res);
-
         boolean nonValueType = false;
         if (returnKind == Kind.Object && stamp instanceof ObjectStamp) {
             ResolvedJavaType type = ((ObjectStamp) stamp).type();
@@ -162,16 +168,10 @@
 
         if (returnKind != Kind.Void) {
             assert nonValueType || res.getKind().getStackKind() != Kind.Void;
-            b.push(returnKind.getStackKind(), res);
+            res = b.addPush(returnKind.getStackKind(), res);
         } else {
             assert res.getKind().getStackKind() == Kind.Void;
-        }
-
-        if (res instanceof StateSplit) {
-            StateSplit stateSplit = (StateSplit) res;
-            if (stateSplit.stateAfter() == null) {
-                stateSplit.setStateAfter(b.createStateAfter());
-            }
+            res = b.add(res);
         }
 
         return true;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/DefaultInlineInvokePlugin.java	Tue May 12 20:56:04 2015 +0200
@@ -40,7 +40,7 @@
         InlineInfo inlineInfo = replacements.getInlineInfo(b, method, args, returnType);
         if (inlineInfo == null) {
             if (InlineDuringParsing.getValue() && method.hasBytecodes() && method.getCode().length <= TrivialInliningSize.getValue() && b.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
-                return new InlineInfo(method, false, false);
+                return new InlineInfo(method, false);
             }
         }
         return inlineInfo;
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/GraphKit.java	Tue May 12 20:56:04 2015 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.replacements;
 
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
+
 import java.lang.reflect.*;
 import java.util.*;
 
@@ -152,7 +154,7 @@
             if (invoke.getKind() != Kind.Void) {
                 frameStateBuilder.push(returnType.getKind(), invoke);
             }
-            invoke.setStateAfter(frameStateBuilder.create(bci));
+            invoke.setStateAfter(frameStateBuilder.create(bci, invoke));
             if (invoke.getKind() != Kind.Void) {
                 frameStateBuilder.pop(returnType.getKind());
             }
@@ -219,7 +221,7 @@
         GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
 
         StructuredGraph calleeGraph = new StructuredGraph(method, AllowAssumptions.NO);
-        IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, null, IntrinsicContext.POST_PARSE_INLINE_BCI);
+        IntrinsicContext initialReplacementContext = new IntrinsicContext(method, method, INLINE_AFTER_PARSING);
         new GraphBuilderPhase.Instance(metaAccess, providers.getStampProvider(), providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialReplacementContext).apply(calleeGraph);
 
         // Remove all frame states from inlinee
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/IntrinsicGraphBuilder.java	Tue May 12 20:56:04 2015 +0200
@@ -152,8 +152,10 @@
         return graph;
     }
 
-    public FrameState createStateAfter() {
-        return getGraph().add(new FrameState(BytecodeFrame.BEFORE_BCI));
+    public void setStateAfter(StateSplit sideEffect) {
+        assert sideEffect.hasSideEffect();
+        FrameState stateAfter = getGraph().add(new FrameState(BytecodeFrame.BEFORE_BCI));
+        sideEffect.setStateAfter(stateAfter);
     }
 
     public GraphBuilderContext getParent() {
@@ -180,11 +182,11 @@
         return 0;
     }
 
-    public boolean parsingReplacement() {
+    public boolean parsingIntrinsic() {
         return true;
     }
 
-    public Replacement getReplacement() {
+    public IntrinsicContext getIntrinsic() {
         throw GraalInternalError.shouldNotReachHere();
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/PEGraphDecoder.java	Tue May 12 20:56:04 2015 +0200
@@ -97,6 +97,15 @@
         public boolean isInlinedMethod() {
             return caller != null;
         }
+
+        public BytecodePosition getBytecodePosition() {
+            if (bytecodePosition == null) {
+                ensureOuterStateDecoded(this);
+                ensureExceptionStateDecoded(this);
+                bytecodePosition = InliningUtil.processBytecodePosition(invokeData.invoke, null);
+            }
+            return bytecodePosition;
+        }
     }
 
     protected class PENonAppendGraphBuilderContext implements GraphBuilderContext {
@@ -139,7 +148,7 @@
         }
 
         @Override
-        public Replacement getReplacement() {
+        public IntrinsicContext getIntrinsic() {
             return null;
         }
 
@@ -169,7 +178,7 @@
         }
 
         @Override
-        public FrameState createStateAfter() {
+        public void setStateAfter(StateSplit stateSplit) {
             throw unimplemented();
         }
 
@@ -217,10 +226,11 @@
         }
 
         @Override
-        public FrameState createStateAfter() {
+        public void setStateAfter(StateSplit stateSplit) {
             Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
             getGraph().add(stateAfter);
-            return (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter);
+            FrameState fs = (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter);
+            stateSplit.setStateAfter(fs);
         }
 
         @Override
@@ -411,7 +421,7 @@
         if (inlineInfo == null) {
             return false;
         }
-        assert !inlineInfo.isIntrinsic && !inlineInfo.isReplacement : "not supported";
+        assert !inlineInfo.isIntrinsic : "not supported";
 
         ResolvedJavaMethod inlineMethod = inlineInfo.methodToInline;
         EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod);
@@ -519,6 +529,15 @@
     protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method);
 
     @Override
+    protected void handleFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) {
+        PEMethodScope methodScope = (PEMethodScope) s;
+        if (node instanceof SimpleInfopointNode && methodScope.isInlinedMethod()) {
+            InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.getBytecodePosition());
+        }
+        super.handleFixedNode(s, loopScope, nodeOrderId, node);
+    }
+
+    @Override
     protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node node) {
         PEMethodScope methodScope = (PEMethodScope) s;
 
@@ -591,11 +610,7 @@
         PEMethodScope methodScope = (PEMethodScope) s;
 
         if (methodScope.isInlinedMethod()) {
-            if (node instanceof SimpleInfopointNode) {
-                methodScope.bytecodePosition = InliningUtil.processSimpleInfopoint(methodScope.invokeData.invoke, (SimpleInfopointNode) node, methodScope.bytecodePosition);
-                return node;
-
-            } else if (node instanceof FrameState) {
+            if (node instanceof FrameState) {
                 FrameState frameState = (FrameState) node;
 
                 ensureOuterStateDecoded(methodScope);
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/ReplacementsImpl.java	Tue May 12 20:56:04 2015 +0200
@@ -24,6 +24,7 @@
 
 import static com.oracle.graal.api.meta.MetaUtil.*;
 import static com.oracle.graal.compiler.common.GraalOptions.*;
+import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
 import static com.oracle.graal.java.AbstractBytecodeParser.Options.*;
 import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*;
 import static java.lang.String.*;
@@ -45,7 +46,6 @@
 import com.oracle.graal.graph.Node.NodeIntrinsic;
 import com.oracle.graal.graphbuilderconf.*;
 import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
-import com.oracle.graal.graphbuilderconf.GraphBuilderContext.Replacement;
 import com.oracle.graal.java.*;
 import com.oracle.graal.java.GraphBuilderPhase.Instance;
 import com.oracle.graal.nodes.*;
@@ -93,13 +93,13 @@
     public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
         ResolvedJavaMethod subst = getSubstitutionMethod(method);
         if (subst != null) {
-            if (b.parsingReplacement() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) {
+            if (b.parsingIntrinsic() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) {
                 // Forced inlining of intrinsics
-                return new InlineInfo(subst, true, true);
+                return new InlineInfo(subst, true);
             }
             return null;
         }
-        if (b.parsingReplacement()) {
+        if (b.parsingIntrinsic()) {
             assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), DefaultGenericInvocationPlugin.class.getName());
 
             assert b.getDepth() < MAX_GRAPH_INLINING_DEPTH : "inlining limit exceeded";
@@ -110,7 +110,7 @@
             }
 
             // Force inlining when parsing replacements
-            return new InlineInfo(method, true, true);
+            return new InlineInfo(method, true);
         } else {
             assert method.getAnnotation(NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", NodeIntrinsic.class.getSimpleName(),
                             method.format("%h.%n"), b);
@@ -119,10 +119,10 @@
     }
 
     public void notifyOfNoninlinedInvoke(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
-        if (b.parsingReplacement()) {
-            Replacement replacement = b.getReplacement();
-            assert replacement.isCallToOriginal(method) : format("All non-recursive calls in the replacement %s must be inlined or intrinsified: found call to %s",
-                            replacement.getReplacementMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)"));
+        if (b.parsingIntrinsic()) {
+            IntrinsicContext intrinsic = b.getIntrinsic();
+            assert intrinsic.isCallToOriginal(method) : format("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s",
+                            intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)"));
         }
     }
 
@@ -299,9 +299,7 @@
         StructuredGraph graph = UseSnippetGraphCache ? graphs.get(method) : null;
         if (graph == null) {
             try (DebugCloseable a = SnippetPreparationTime.start()) {
-                FrameStateProcessing frameStateProcessing = method.getAnnotation(Snippet.class).removeAllFrameStates() ? FrameStateProcessing.Removal
-                                : FrameStateProcessing.CollapseFrameForSingleSideEffect;
-                StructuredGraph newGraph = makeGraph(method, args, recursiveEntry, frameStateProcessing);
+                StructuredGraph newGraph = makeGraph(method, args, recursiveEntry);
                 Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
                 if (!UseSnippetGraphCache || args != null) {
                     return newGraph;
@@ -346,7 +344,7 @@
         }
         StructuredGraph graph = graphs.get(substitute);
         if (graph == null) {
-            graph = makeGraph(substitute, null, original, FrameStateProcessing.None);
+            graph = makeGraph(substitute, null, original);
             graph.freeze();
             graphs.putIfAbsent(substitute, graph);
             graph = graphs.get(substitute);
@@ -445,19 +443,18 @@
      * @param args
      * @param original the original method if {@code method} is a {@linkplain MethodSubstitution
      *            substitution} otherwise null
-     * @param frameStateProcessing controls how {@link FrameState FrameStates} should be handled.
      */
-    public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original, FrameStateProcessing frameStateProcessing) {
+    public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
         try (OverrideScope s = OptionValue.override(DeoptALot, false)) {
-            return createGraphMaker(method, original, frameStateProcessing).makeGraph(args);
+            return createGraphMaker(method, original).makeGraph(args);
         }
     }
 
     /**
      * Can be overridden to return an object that specializes various parts of graph preprocessing.
      */
-    protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original, FrameStateProcessing frameStateProcessing) {
-        return new GraphMaker(this, substitute, original, frameStateProcessing);
+    protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) {
+        return new GraphMaker(this, substitute, original);
     }
 
     /**
@@ -465,18 +462,6 @@
      */
     final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphCache = new ConcurrentHashMap<>();
 
-    public enum FrameStateProcessing {
-        None,
-        /**
-         * @see CollapseFrameForSingleSideEffectPhase
-         */
-        CollapseFrameForSingleSideEffect,
-        /**
-         * Removes frame states from all nodes in the graph.
-         */
-        Removal
-    }
-
     /**
      * Creates and preprocesses a graph for a replacement.
      */
@@ -496,16 +481,10 @@
          */
         protected final ResolvedJavaMethod substitutedMethod;
 
-        /**
-         * Controls how FrameStates are processed.
-         */
-        private FrameStateProcessing frameStateProcessing;
-
-        protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod, FrameStateProcessing frameStateProcessing) {
+        protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
             this.replacements = replacements;
             this.method = substitute;
             this.substitutedMethod = substitutedMethod;
-            this.frameStateProcessing = frameStateProcessing;
         }
 
         public StructuredGraph makeGraph(Object[] args) {
@@ -536,18 +515,6 @@
             new ConvertDeoptimizeToGuardPhase().apply(graph, null);
             assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node";
 
-            switch (frameStateProcessing) {
-                case Removal:
-                    for (Node node : graph.getNodes()) {
-                        if (node instanceof StateSplit) {
-                            ((StateSplit) node).setStateAfter(null);
-                        }
-                    }
-                    break;
-                case CollapseFrameForSingleSideEffect:
-                    new CollapseFrameForSingleSideEffectPhase().apply(graph);
-                    break;
-            }
             new DeadCodeEliminationPhase(Required).apply(graph);
         }
 
@@ -627,16 +594,16 @@
 
         protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig,
                         OptimisticOptimizations optimisticOpts) {
-            ReplacementContext initialReplacementContext = null;
+            IntrinsicContext initialIntrinsicContext = null;
             if (method.getAnnotation(Snippet.class) == null) {
-                // Late inlined intrinsic
-                initialReplacementContext = new IntrinsicContext(substitutedMethod, method, null, -1);
+                // Post-parse inlined intrinsic
+                initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, INLINE_AFTER_PARSING);
             } else {
                 // Snippet
                 ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method;
-                initialReplacementContext = new ReplacementContext(original, method);
+                initialIntrinsicContext = new IntrinsicContext(original, method, INLINE_AFTER_PARSING);
             }
-            return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialReplacementContext);
+            return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
         }
     }
 
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/StandardGraphBuilderPlugins.java	Tue May 12 20:56:04 2015 +0200
@@ -84,6 +84,7 @@
         if (Options.UseBlackholeSubstitution.getValue()) {
             registerJMHBlackholePlugins(plugins);
         }
+        registerJFRThrowablePlugins(plugins);
     }
 
     private static final Field STRING_VALUE_FIELD;
@@ -459,7 +460,7 @@
         }
 
         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode value) {
-            if (b.parsingReplacement()) {
+            if (b.parsingIntrinsic()) {
                 ResolvedJavaMethod rootMethod = b.getGraph().method();
                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
                     // Disable invocation plugins for boxing snippets so that the
@@ -486,7 +487,7 @@
         }
 
         public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver) {
-            if (b.parsingReplacement()) {
+            if (b.parsingIntrinsic()) {
                 ResolvedJavaMethod rootMethod = b.getGraph().method();
                 if (b.getMetaAccess().lookupJavaType(BoxingSnippets.class).isAssignableFrom(rootMethod.getDeclaringClass())) {
                     // Disable invocation plugins for unboxing snippets so that the
@@ -651,4 +652,18 @@
             }
         }
     }
+
+    private static void registerJFRThrowablePlugins(InvocationPlugins plugins) {
+        String name = "oracle.jrockit.jfr.jdkevents.ThrowableTracer";
+        Class<?> tracerClass = MethodSubstitutionPlugin.resolveClass(name, true);
+        if (tracerClass != null) {
+            Registration r = new Registration(plugins, tracerClass);
+            r.register2("traceThrowable", Throwable.class, String.class, new InvocationPlugin() {
+                public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode throwable, ValueNode message) {
+                    b.add(new VirtualizableInvokeMacroNode(b.getInvokeKind(), targetMethod, b.bci(), b.getInvokeReturnType(), throwable, message));
+                    return true;
+                }
+            });
+        }
+    }
 }
--- a/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/MacroNode.java	Tue May 12 20:56:04 2015 +0200
@@ -24,7 +24,6 @@
 
 import static com.oracle.graal.api.code.BytecodeFrame.*;
 
-import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.api.replacements.*;
 import com.oracle.graal.compiler.common.*;
@@ -114,13 +113,6 @@
         StructuredGraph methodSubstitution = tool.getReplacements().getSubstitution(getTargetMethod(), true, bci);
         if (methodSubstitution != null) {
             methodSubstitution = methodSubstitution.copy();
-            if (stateAfter() == null || stateAfter().bci == BytecodeFrame.AFTER_BCI) {
-                /*
-                 * handles the case of a MacroNode inside a snippet used for another MacroNode
-                 * lowering
-                 */
-                new CollapseFrameForSingleSideEffectPhase().apply(methodSubstitution);
-            }
             return lowerReplacement(methodSubstitution, tool);
         }
         return null;
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.replacements/src/com/oracle/graal/replacements/nodes/VirtualizableInvokeMacroNode.java	Tue May 12 20:56:04 2015 +0200
@@ -0,0 +1,54 @@
+/*
+ * Copyright (c) 2015, 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.nodes;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.CallTargetNode.InvokeKind;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * A helper class to allow elimination of byte code instrumentation that could interfere with escape
+ * analysis.
+ */
+@NodeInfo
+public class VirtualizableInvokeMacroNode extends MacroStateSplitNode implements Virtualizable {
+
+    public static final NodeClass<VirtualizableInvokeMacroNode> TYPE = NodeClass.create(VirtualizableInvokeMacroNode.class);
+
+    public VirtualizableInvokeMacroNode(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, int bci, JavaType returnType, ValueNode... arguments) {
+        super(TYPE, invokeKind, targetMethod, bci, returnType, arguments);
+    }
+
+    @Override
+    public void virtualize(VirtualizerTool tool) {
+        for (ValueNode arg : arguments) {
+            State state = tool.getObjectState(arg);
+            if (state != null && state.getState() == EscapeState.Virtual) {
+                tool.delete();
+            }
+        }
+    }
+}
--- a/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle.hotspot/src/com/oracle/graal/truffle/hotspot/HotSpotTruffleRuntime.java	Tue May 12 20:56:04 2015 +0200
@@ -306,7 +306,7 @@
     }
 
     @Override
-    public boolean enableInfopoints() {
+    public boolean platformEnableInfopoints() {
         return HotSpotGraalRuntime.runtime().getCompilerToVM().shouldDebugNonSafepoints();
     }
 
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/BytecodeInterpreterPartialEvaluationTest.java	Tue May 12 20:56:04 2015 +0200
@@ -269,7 +269,7 @@
         assertPartialEvalEqualsAndRunsCorrect(new Program("nestedLoopsProgram", bytecodes, 0, 6));
     }
 
-    @Test(timeout = 1000)
+    @Test(timeout = 2000)
     public void manyIfsProgram() {
         byte[] bytecodes = new byte[]{
         /* 0: */Bytecode.CONST,
--- a/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle.test/src/com/oracle/graal/truffle/test/ConditionAnchoringTest.java	Tue May 12 20:56:04 2015 +0200
@@ -147,7 +147,7 @@
     private static final class InlineEverythingPlugin implements InlineInvokePlugin {
         public InlineInfo getInlineInfo(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
             assert method.hasBytecodes();
-            return new InlineInfo(method, false, false);
+            return new InlineInfo(method, false);
         }
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/GraalTruffleRuntime.java	Tue May 12 20:56:04 2015 +0200
@@ -281,7 +281,12 @@
 
     public abstract void reinstallStubs();
 
-    public abstract boolean enableInfopoints();
+    public final boolean enableInfopoints() {
+        /* Currently infopoints can change code generation so don't enable them automatically */
+        return platformEnableInfopoints() && TruffleEnableInfopoints.getValue();
+    }
+
+    protected abstract boolean platformEnableInfopoints();
 
     private final class DispatchTruffleCompilationListener implements GraalTruffleCompilationListener {
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/PartialEvaluator.java	Tue May 12 20:56:04 2015 +0200
@@ -190,7 +190,7 @@
             if (replacements.hasSubstitution(original, builder.bci())) {
                 return null;
             }
-            assert !builder.parsingReplacement();
+            assert !builder.parsingIntrinsic();
 
             if (TruffleCompilerOptions.TruffleFunctionInlining.getValue()) {
                 if (original.equals(callSiteProxyMethod)) {
@@ -210,12 +210,12 @@
                     if (decision != null && decision.isInline()) {
                         inlining.push(decision);
                         builder.getAssumptions().record(new AssumptionValidAssumption((OptimizedAssumption) decision.getTarget().getNodeRewritingAssumption()));
-                        return new InlineInfo(callInlinedMethod, false, false);
+                        return new InlineInfo(callInlinedMethod, false);
                     }
                 }
             }
 
-            return new InlineInfo(original, false, false);
+            return new InlineInfo(original, false);
         }
 
         @Override
@@ -277,10 +277,10 @@
                  * We want to inline invokes that have a constant MethodHandle parameter to remove
                  * invokedynamic related calls as early as possible.
                  */
-                return new InlineInfo(original, false, false);
+                return new InlineInfo(original, false);
             }
             if (inlineDuringParsing && original.hasBytecodes() && original.getCode().length < TrivialInliningSize.getValue() && builder.getDepth() < InlineDuringParsingMaxDepth.getValue()) {
-                return new InlineInfo(original, false, false);
+                return new InlineInfo(original, false);
             }
             return null;
         }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/TruffleCompilerOptions.java	Tue May 12 20:56:04 2015 +0200
@@ -170,5 +170,8 @@
 
     @Option(help = "Print additional more verbose Truffle compilation statistics at the end of a run.", type = OptionType.Debug)
     public static final OptionValue<Boolean> TruffleCompilationStatisticDetails = new OptionValue<>(false);
+
+    @Option(help = "Enable support for simple infopoints in truffle partial evaluations.", type = OptionType.Expert)
+    public static final OptionValue<Boolean> TruffleEnableInfopoints = new OptionValue<>(false);
     // @formatter:on
 }
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/asserts/NeverPartOfCompilationNode.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/nodes/asserts/NeverPartOfCompilationNode.java	Tue May 12 20:56:04 2015 +0200
@@ -34,8 +34,8 @@
     public static final NodeClass<NeverPartOfCompilationNode> TYPE = NodeClass.create(NeverPartOfCompilationNode.class);
     protected final String message;
 
-    public NeverPartOfCompilationNode(String message, FrameState stateAfter) {
-        super(TYPE, StampFactory.forVoid(), stateAfter);
+    public NeverPartOfCompilationNode(String message) {
+        super(TYPE, StampFactory.forVoid());
         this.message = message;
     }
 
--- a/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.truffle/src/com/oracle/graal/truffle/substitutions/TruffleGraphBuilderPlugins.java	Tue May 12 20:56:04 2015 +0200
@@ -239,7 +239,7 @@
             public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode message) {
                 if (message.isConstant()) {
                     String messageString = message.asConstant().toValueString();
-                    b.add(new NeverPartOfCompilationNode(messageString, b.createStateAfter()));
+                    b.add(new NeverPartOfCompilationNode(messageString));
                     return true;
                 } else if (canDelayIntrinsification) {
                     return false;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/EffectsClosure.java	Tue May 12 20:56:04 2015 +0200
@@ -182,7 +182,7 @@
 
     /**
      * Collects the effects of virtualizing the given node.
-     * 
+     *
      * @return {@code true} if the effects include removing the node, {@code false} otherwise.
      */
     protected abstract boolean processNode(Node node, BlockT state, GraphEffectList effects, FixedWithNextNode lastFixedNode);
@@ -210,6 +210,7 @@
 
         BlockT loopEntryState = initialState;
         BlockT lastMergedState = cloneState(initialState);
+        processInitialLoopState(loop, lastMergedState);
         MergeProcessor mergeProcessor = createMergeProcessor(loop.getHeader());
         for (int iteration = 0; iteration < 10; iteration++) {
             LoopInfo<BlockT> info = ReentrantBlockIterator.processLoop(this, loop, cloneState(lastMergedState));
@@ -245,6 +246,11 @@
         throw new GraalInternalError("too many iterations at %s", loop);
     }
 
+    @SuppressWarnings("unused")
+    protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) {
+        // nothing to do
+    }
+
     private void doMergeWithoutDead(MergeProcessor mergeProcessor, List<BlockT> states) {
         int alive = 0;
         for (BlockT state : states) {
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationBlockState.java	Tue May 12 20:56:04 2015 +0200
@@ -115,7 +115,7 @@
         ValueNode cacheObject;
         ObjectState obj = closure.getObjectState(this, object);
         if (obj != null) {
-            assert !obj.isVirtual();
+            assert !obj.isVirtual() : object;
             cacheObject = obj.getMaterializedValue();
         } else {
             cacheObject = object;
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PEReadEliminationClosure.java	Tue May 12 20:56:04 2015 +0200
@@ -27,6 +27,7 @@
 import java.util.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
@@ -212,6 +213,23 @@
     }
 
     @Override
+    protected void processInitialLoopState(Loop<Block> loop, PEReadEliminationBlockState initialState) {
+        super.processInitialLoopState(loop, initialState);
+
+        for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) {
+            ValueNode firstValue = phi.valueAt(0);
+            if (firstValue != null) {
+                firstValue = GraphUtil.unproxify(firstValue);
+                for (Map.Entry<ReadCacheEntry, ValueNode> entry : new ArrayList<>(initialState.getReadCache().entrySet())) {
+                    if (entry.getKey().object == firstValue) {
+                        initialState.addReadCache(phi, entry.getKey().identity, entry.getKey().index, entry.getValue(), this);
+                    }
+                }
+            }
+        }
+    }
+
+    @Override
     protected void processLoopExit(LoopExitNode exitNode, PEReadEliminationBlockState initialState, PEReadEliminationBlockState exitState, GraphEffectList effects) {
         super.processLoopExit(exitNode, initialState, exitState, effects);
 
--- a/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.graal.virtual/src/com/oracle/graal/virtual/phases/ea/PartialEscapeClosure.java	Tue May 12 20:56:04 2015 +0200
@@ -26,6 +26,7 @@
 
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.compiler.common.cfg.*;
 import com.oracle.graal.compiler.common.type.*;
 import com.oracle.graal.compiler.common.util.*;
 import com.oracle.graal.debug.*;
@@ -359,6 +360,20 @@
     }
 
     @Override
+    protected void processInitialLoopState(Loop<Block> loop, BlockT initialState) {
+        super.processInitialLoopState(loop, initialState);
+
+        for (PhiNode phi : ((LoopBeginNode) loop.getHeader().getBeginNode()).phis()) {
+            if (phi.valueAt(0) != null) {
+                ObjectState state = getObjectState(initialState, phi.valueAt(0));
+                if (state != null && state.isVirtual()) {
+                    addAndMarkAlias(state.virtual, phi);
+                }
+            }
+        }
+    }
+
+    @Override
     protected void processLoopExit(LoopExitNode exitNode, BlockT initialState, BlockT exitState, GraphEffectList effects) {
         if (exitNode.graph().hasValueProxies()) {
             Map<VirtualObjectNode, ProxyNode> proxies = Node.newMap();
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/instrument/Instrument.java	Tue May 12 20:56:04 2015 +0200
@@ -420,7 +420,7 @@
         @NodeInfo(cost = NodeCost.NONE)
         private final class ToolEvalNodeInstrumentNode extends AbstractInstrumentNode {
 
-            @Child ToolEvalNode toolEvalNode;
+            @Child private ToolEvalNode toolEvalNode;
 
             private ToolEvalNodeInstrumentNode(AbstractInstrumentNode nextNode) {
                 super(nextNode);
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/source/Source.java	Tue May 12 20:56:04 2015 +0200
@@ -35,32 +35,46 @@
  * Representation of a guest language source code unit and its contents. Sources originate in
  * several ways:
  * <ul>
- * <li><strong>Literal:</strong> A named text string. These are not indexed and should be considered
- * value objects; equality is defined based on contents. <br>
+ * <li><strong>Literal:</strong> An anonymous text string: not named and not indexed. These should
+ * be considered value objects; equality is defined based on contents.<br>
  * See {@link Source#fromText(CharSequence, String)}</li>
  * <p>
+ * <li><strong>Named Literal:</strong> A text string that can be retrieved by name as if it were a
+ * file, but without any assumption that the name is related to a file path. Creating a new literal
+ * with an already existing name will replace its predecessor in the index.<br>
+ * See {@link Source#fromNamedText(CharSequence, String)}<br>
+ * See {@link Source#find(String)}</li>
+ * <p>
  * <li><strong>File:</strong> Each file is represented as a canonical object, indexed by the
  * absolute, canonical path name of the file. File contents are <em>read lazily</em> and contents
  * optionally <em>cached</em>. <br>
  * See {@link Source#fromFileName(String)}<br>
- * See {@link Source#fromFileName(String, boolean)}</li>
+ * See {@link Source#fromFileName(String, boolean)}<br>
+ * See {@link Source#find(String)}</li>
  * <p>
  * <li><strong>URL:</strong> Each URL source is represented as a canonical object, indexed by the
  * URL. Contents are <em>read eagerly</em> and <em>cached</em>. <br>
- * See {@link Source#fromURL(URL, String)}</li>
+ * See {@link Source#fromURL(URL, String)}<br>
+ * See {@link Source#find(String)}</li>
  * <p>
- * <li><strong>Reader:</strong> Contents are <em>read eagerly</em> and treated as a <em>Literal</em>
- * . <br>
+ * <li><strong>Reader:</strong> Contents are <em>read eagerly</em> and treated as an anonymous
+ * (non-indexed) <em>Literal</em> . <br>
  * See {@link Source#fromReader(Reader, String)}</li>
  * <p>
- * <li><strong>Pseudo File:</strong> A literal text string that can be retrieved by name as if it
- * were a file, unlike literal sources; useful for testing. <br>
- * See {@link Source#asPseudoFile(CharSequence, String)}</li>
+ * <li><strong>Sub-Source:</strong> A representation of the contents of a sub-range of another
+ * {@link Source}.<br>
+ * See @link {@link Source#subSource(Source, int, int)}<br>
+ * See @link {@link Source#subSource(Source, int)}</li>
+ * <p>
+ * <li><strong>AppendableSource:</strong> Literal contents are provided by the client,
+ * incrementally, after the instance is created.<br>
+ * See {@link Source#fromAppendableText(String)}<br>
+ * See {@link Source#fromNamedAppendableText(String)}</li>
  * </ul>
  * <p>
  * <strong>File cache:</strong>
  * <ol>
- * <li>File content caching is optional, <em>off</em> by default.</li>
+ * <li>File content caching is optional, <em>on</em> by default.</li>
  * <li>The first access to source file contents will result in the contents being read, and (if
  * enabled) cached.</li>
  * <li>If file contents have been cached, access to contents via {@link Source#getInputStream()} or
@@ -128,14 +142,24 @@
      */
     private static final List<WeakReference<Source>> allSources = Collections.synchronizedList(new ArrayList<WeakReference<Source>>());
 
-    // Files and pseudo files are indexed.
-    private static final Map<String, WeakReference<Source>> filePathToSource = new HashMap<>();
+    /**
+     * Index of all named sources.
+     */
+    private static final Map<String, WeakReference<Source>> nameToSource = new HashMap<>();
 
     private static boolean fileCacheEnabled = true;
 
     private static final List<SourceListener> sourceListeners = new ArrayList<>();
 
     /**
+     * Locates an existing instance by the name under which it was indexed.
+     */
+    public static Source find(String name) {
+        final WeakReference<Source> nameRef = nameToSource.get(name);
+        return nameRef == null ? null : nameRef.get();
+    }
+
+    /**
      * Gets the canonical representation of a source file, whose contents will be read lazily and
      * then cached.
      *
@@ -146,7 +170,7 @@
      */
     public static Source fromFileName(String fileName, boolean reset) throws IOException {
 
-        final WeakReference<Source> nameRef = filePathToSource.get(fileName);
+        final WeakReference<Source> nameRef = nameToSource.get(fileName);
         Source source = nameRef == null ? null : nameRef.get();
         if (source == null) {
             final File file = new File(fileName);
@@ -154,11 +178,11 @@
                 throw new IOException("Can't read file " + fileName);
             }
             final String path = file.getCanonicalPath();
-            final WeakReference<Source> pathRef = filePathToSource.get(path);
+            final WeakReference<Source> pathRef = nameToSource.get(path);
             source = pathRef == null ? null : pathRef.get();
             if (source == null) {
                 source = new FileSource(file, fileName, path);
-                filePathToSource.put(path, new WeakReference<>(source));
+                nameToSource.put(path, new WeakReference<>(source));
             }
         }
         if (reset) {
@@ -193,17 +217,17 @@
      */
     public static Source fromFileName(CharSequence chars, String fileName) throws IOException {
 
-        final WeakReference<Source> nameRef = filePathToSource.get(fileName);
+        final WeakReference<Source> nameRef = nameToSource.get(fileName);
         Source source = nameRef == null ? null : nameRef.get();
         if (source == null) {
             final File file = new File(fileName);
             // We are going to trust that the fileName is readable.
             final String path = file.getCanonicalPath();
-            final WeakReference<Source> pathRef = filePathToSource.get(path);
+            final WeakReference<Source> pathRef = nameToSource.get(path);
             source = pathRef == null ? null : pathRef.get();
             if (source == null) {
                 source = new FileSource(file, fileName, path, chars);
-                filePathToSource.put(path, new WeakReference<>(source));
+                nameToSource.put(path, new WeakReference<>(source));
             }
         }
         notifyNewSource(source).tagAs(Tags.FROM_FILE);
@@ -211,8 +235,7 @@
     }
 
     /**
-     * Creates a non-canonical source from literal text. If an already created literal source must
-     * be retrievable by name, use {@link #asPseudoFile(CharSequence, String)}.
+     * Creates an anonymous source from literal text: not named and not indexed.
      *
      * @param chars textual source code
      * @param description a note about the origin, for error messages and debugging
@@ -226,6 +249,79 @@
     }
 
     /**
+     * Creates an anonymous source from literal text that is provided incrementally after creation:
+     * not named and not indexed.
+     *
+     * @param description a note about the origin, for error messages and debugging
+     * @return a newly created, non-indexed, initially empty, appendable source representation
+     */
+    public static Source fromAppendableText(String description) {
+        final Source source = new AppendableLiteralSource(description);
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
+        return source;
+    }
+
+    /**
+     * Creates a source from literal text that can be retrieved by name, with no assumptions about
+     * the structure or meaning of the name. If the name is already in the index, the new instance
+     * will replace the previously existing instance in the index.
+     *
+     * @param chars textual source code
+     * @param name string to use for indexing/lookup
+     * @return a newly created, source representation
+     */
+    public static Source fromNamedText(CharSequence chars, String name) {
+        final Source source = new LiteralSource(name, chars.toString());
+        nameToSource.put(name, new WeakReference<>(source));
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
+        return source;
+    }
+
+    /**
+     * Creates a source from literal text that is provided incrementally after creation and which
+     * can be retrieved by name, with no assumptions about the structure or meaning of the name. If
+     * the name is already in the index, the new instance will replace the previously existing
+     * instance in the index.
+     *
+     * @param name string to use for indexing/lookup
+     * @return a newly created, indexed, initially empty, appendable source representation
+     */
+    public static Source fromNamedAppendableText(String name) {
+        final Source source = new AppendableLiteralSource(name);
+        nameToSource.put(name, new WeakReference<>(source));
+        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
+        return source;
+    }
+
+    /**
+     * Creates a {@linkplain Source Source instance} that represents the contents of a sub-range of
+     * an existing {@link Source}.
+     *
+     * @param base an existing Source instance
+     * @param baseCharIndex 0-based index of the first character of the sub-range
+     * @param length the number of characters in the sub-range
+     * @return a new instance representing a sub-range of another Source
+     * @throws IllegalArgumentException if the specified sub-range is not contained in the base
+     */
+    public static Source subSource(Source base, int baseCharIndex, int length) {
+        final SubSource subSource = SubSource.create(base, baseCharIndex, length);
+        return subSource;
+    }
+
+    /**
+     * Creates a {@linkplain Source Source instance} that represents the contents of a sub-range at
+     * the end of an existing {@link Source}.
+     *
+     * @param base an existing Source instance
+     * @param baseCharIndex 0-based index of the first character of the sub-range
+     * @return a new instance representing a sub-range at the end of another Source
+     * @throws IllegalArgumentException if the index is out of range
+     */
+    public static Source subSource(Source base, int baseCharIndex) {
+        return subSource(base, baseCharIndex, base.getLength() - baseCharIndex);
+    }
+
+    /**
      * Creates a source whose contents will be read immediately from a URL and cached.
      *
      * @param url
@@ -286,21 +382,6 @@
         return source;
     }
 
-    /**
-     * Creates a source from literal text, but which acts as a file and can be retrieved by name
-     * (unlike other literal sources); intended for testing.
-     *
-     * @param chars textual source code
-     * @param pseudoFileName string to use for indexing/lookup
-     * @return a newly created, source representation, canonical with respect to its name
-     */
-    public static Source asPseudoFile(CharSequence chars, String pseudoFileName) {
-        final Source source = new LiteralSource(pseudoFileName, chars.toString());
-        filePathToSource.put(pseudoFileName, new WeakReference<>(source));
-        notifyNewSource(source).tagAs(Tags.FROM_LITERAL);
-        return source;
-    }
-
     // TODO (mlvdv) enable per-file choice whether to cache?
     /**
      * Enables/disables caching of file contents, <em>disabled</em> by default. Caching of sources
@@ -372,7 +453,7 @@
 
     private final ArrayList<SourceTag> tags = new ArrayList<>();
 
-    Source() {
+    private Source() {
     }
 
     private TextMap textMap = null;
@@ -448,7 +529,7 @@
      * Gets the number of characters in the source.
      */
     public final int getLength() {
-        return checkTextMap().length();
+        return getTextMap().length();
     }
 
     /**
@@ -467,9 +548,8 @@
      * Gets the text (not including a possible terminating newline) in a (1-based) numbered line.
      */
     public final String getCode(int lineNumber) {
-        checkTextMap();
-        final int offset = textMap.lineStartOffset(lineNumber);
-        final int length = textMap.lineLength(lineNumber);
+        final int offset = getTextMap().lineStartOffset(lineNumber);
+        final int length = getTextMap().lineLength(lineNumber);
         return getCode().substring(offset, offset + length);
     }
 
@@ -478,7 +558,7 @@
      * source without a terminating newline count as a line.
      */
     public final int getLineCount() {
-        return checkTextMap().lineCount();
+        return getTextMap().lineCount();
     }
 
     /**
@@ -488,7 +568,7 @@
      * @throws IllegalArgumentException if the offset is outside the text contents
      */
     public final int getLineNumber(int offset) throws IllegalArgumentException {
-        return checkTextMap().offsetToLine(offset);
+        return getTextMap().offsetToLine(offset);
     }
 
     /**
@@ -497,7 +577,7 @@
      * @throws IllegalArgumentException if the offset is outside the text contents
      */
     public final int getColumnNumber(int offset) throws IllegalArgumentException {
-        return checkTextMap().offsetToCol(offset);
+        return getTextMap().offsetToCol(offset);
     }
 
     /**
@@ -506,7 +586,7 @@
      * @throws IllegalArgumentException if there is no such line in the text
      */
     public final int getLineStartOffset(int lineNumber) throws IllegalArgumentException {
-        return checkTextMap().lineStartOffset(lineNumber);
+        return getTextMap().lineStartOffset(lineNumber);
     }
 
     /**
@@ -516,7 +596,17 @@
      * @throws IllegalArgumentException if there is no such line in the text
      */
     public final int getLineLength(int lineNumber) throws IllegalArgumentException {
-        return checkTextMap().lineLength(lineNumber);
+        return getTextMap().lineLength(lineNumber);
+    }
+
+    /**
+     * Append text to a Source explicitly created as <em>Appendable</em>.
+     *
+     * @param chars the text to append
+     * @throws UnsupportedOperationException by concrete subclasses that do not support appending
+     */
+    public void appendCode(CharSequence chars) {
+        throw new UnsupportedOperationException();
     }
 
     /**
@@ -556,9 +646,8 @@
      * @throws IllegalStateException if the source is one of the "null" instances
      */
     public final SourceSection createSection(String identifier, int startLine, int startColumn, int length) {
-        checkTextMap();
-        final int lineStartOffset = textMap.lineStartOffset(startLine);
-        if (startColumn > textMap.lineLength(startLine)) {
+        final int lineStartOffset = getTextMap().lineStartOffset(startLine);
+        if (startColumn > getTextMap().lineLength(startLine)) {
             throw new IllegalArgumentException("column out of range");
         }
         final int startOffset = lineStartOffset + startColumn - 1;
@@ -586,10 +675,8 @@
      */
     public final SourceSection createSection(String identifier, int charIndex, int length) throws IllegalArgumentException {
         checkRange(charIndex, length);
-        checkTextMap();
         final int startLine = getLineNumber(charIndex);
         final int startColumn = charIndex - getLineStartOffset(startLine) + 1;
-
         return new DefaultSourceSection(this, identifier, startLine, startColumn, charIndex, length);
     }
 
@@ -610,9 +697,8 @@
      * @throws IllegalStateException if the source is one of the "null" instances
      */
     public final SourceSection createSection(String identifier, int lineNumber) {
-        checkTextMap();
-        final int charIndex = textMap.lineStartOffset(lineNumber);
-        final int length = textMap.lineLength(lineNumber);
+        final int charIndex = getTextMap().lineStartOffset(lineNumber);
+        final int length = getTextMap().lineLength(lineNumber);
         return createSection(identifier, charIndex, length);
     }
 
@@ -627,13 +713,25 @@
         return new LineLocationImpl(this, lineNumber);
     }
 
-    private TextMap checkTextMap() {
+    /**
+     * An object suitable for using as a key into a hashtable that defines equivalence between
+     * different source types.
+     */
+    protected Object getHashKey() {
+        return getName();
+    }
+
+    protected final TextMap getTextMap() {
         if (textMap == null) {
             textMap = createTextMap();
         }
         return textMap;
     }
 
+    protected final void clearTextMap() {
+        textMap = null;
+    }
+
     protected TextMap createTextMap() {
         final String code = getCode();
         if (code == null) {
@@ -644,22 +742,22 @@
 
     private static final class LiteralSource extends Source {
 
-        private final String name; // Name used originally to describe the source
+        private final String description;
         private final String code;
 
-        public LiteralSource(String name, String code) {
-            this.name = name;
+        public LiteralSource(String description, String code) {
+            this.description = description;
             this.code = code;
         }
 
         @Override
         public String getName() {
-            return name;
+            return description;
         }
 
         @Override
         public String getShortName() {
-            return name;
+            return description;
         }
 
         @Override
@@ -669,7 +767,7 @@
 
         @Override
         public String getPath() {
-            return name;
+            return description;
         }
 
         @Override
@@ -688,11 +786,7 @@
 
         @Override
         public int hashCode() {
-            final int prime = 31;
-            int result = 1;
-            result = prime * result + name.hashCode();
-            result = prime * result + (code == null ? 0 : code.hashCode());
-            return result;
+            return description.hashCode();
         }
 
         @Override
@@ -703,11 +797,69 @@
             if (obj == null) {
                 return false;
             }
-            if (!(obj instanceof LiteralSource)) {
-                return false;
+            if (obj instanceof LiteralSource) {
+                LiteralSource other = (LiteralSource) obj;
+                return description.equals(other.description);
             }
-            LiteralSource other = (LiteralSource) obj;
-            return name.equals(other.name) && code.equals(other.code);
+            return false;
+        }
+    }
+
+    private static final class AppendableLiteralSource extends Source {
+        private String description;
+        final List<CharSequence> codeList = new ArrayList<>();
+
+        public AppendableLiteralSource(String description) {
+            this.description = description;
+        }
+
+        @Override
+        public String getName() {
+            return description;
+        }
+
+        @Override
+        public String getShortName() {
+            return description;
+        }
+
+        @Override
+        public String getCode() {
+            return getCodeFromIndex(0);
+        }
+
+        @Override
+        public String getPath() {
+            return description;
+        }
+
+        @Override
+        public URL getURL() {
+            return null;
+        }
+
+        @Override
+        public Reader getReader() {
+            return new StringReader(getCode());
+        }
+
+        @Override
+        protected void reset() {
+        }
+
+        private String getCodeFromIndex(int index) {
+            StringBuilder sb = new StringBuilder();
+            for (int i = index; i < codeList.size(); i++) {
+                CharSequence s = codeList.get(i);
+                sb.append(s);
+            }
+            return sb.toString();
+        }
+
+        @Override
+        public void appendCode(CharSequence chars) {
+            codeList.add(chars);
+            clearTextMap();
         }
 
     }
@@ -745,6 +897,11 @@
         }
 
         @Override
+        protected Object getHashKey() {
+            return path;
+        }
+
+        @Override
         public String getCode() {
             if (fileCacheEnabled) {
                 if (code == null || timeStamp != file.lastModified()) {
@@ -807,7 +964,6 @@
         protected void reset() {
             this.code = null;
         }
-
     }
 
     private static final class URLSource extends Source {
@@ -867,7 +1023,61 @@
         @Override
         protected void reset() {
         }
+    }
 
+    private static final class SubSource extends Source {
+        private final Source base;
+        private final int baseIndex;
+        private final int subLength;
+
+        private static SubSource create(Source base, int baseIndex, int length) {
+            if (baseIndex < 0 || length < 0 || baseIndex + length > base.getLength()) {
+                throw new IllegalArgumentException("text positions out of range");
+            }
+            return new SubSource(base, baseIndex, length);
+        }
+
+        private SubSource(Source base, int baseIndex, int length) {
+            this.base = base;
+            this.baseIndex = baseIndex;
+            this.subLength = length;
+        }
+
+        @Override
+        protected void reset() {
+            assert false;
+        }
+
+        @Override
+        public String getName() {
+            return base.getName();
+        }
+
+        @Override
+        public String getShortName() {
+            return base.getShortName();
+        }
+
+        @Override
+        public String getPath() {
+            return base.getPath();
+        }
+
+        @Override
+        public URL getURL() {
+            return null;
+        }
+
+        @Override
+        public Reader getReader() {
+            assert false;
+            return null;
+        }
+
+        @Override
+        public String getCode() {
+            return base.getCode(baseIndex, subLength);
+        }
     }
 
     private static final class BytesSource extends Source {
@@ -1132,7 +1342,7 @@
             final int prime = 31;
             int result = 1;
             result = prime * result + line;
-            result = prime * result + source.hashCode();
+            result = prime * result + source.getHashKey().hashCode();
             return result;
         }
 
@@ -1151,7 +1361,7 @@
             if (line != other.line) {
                 return false;
             }
-            return source.equals(other.source);
+            return source.getHashKey().equals(other.source.getHashKey());
         }
 
     }
--- a/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/ConditionProfile.java	Tue May 12 20:55:48 2015 +0200
+++ b/graal/com.oracle.truffle.api/src/com/oracle/truffle/api/utilities/ConditionProfile.java	Tue May 12 20:56:04 2015 +0200
@@ -52,6 +52,8 @@
  * @see #createBinaryProfile()
  */
 public abstract class ConditionProfile extends NodeCloneable {
+    ConditionProfile() {
+    }
 
     public abstract boolean profile(boolean value);
 
--- a/mx/mx_graal.py	Tue May 12 20:55:48 2015 +0200
+++ b/mx/mx_graal.py	Tue May 12 20:56:04 2015 +0200
@@ -1606,7 +1606,6 @@
     parser = ArgumentParser(prog='mx ctw')
     parser.add_argument('--ctwopts', action='store', help='space separated Graal options used for CTW compilations (default: --ctwopts="' + defaultCtwopts + '")', default=defaultCtwopts, metavar='<options>')
     parser.add_argument('--jar', action='store', help='jar of classes to compiled instead of rt.jar', metavar='<path>')
-    parser.add_argument('vmargs', nargs=REMAINDER, metavar='VM options...')
 
     args, vmargs = parser.parse_known_args(args)
 
--- a/mxtool/mx.py	Tue May 12 20:55:48 2015 +0200
+++ b/mxtool/mx.py	Tue May 12 20:56:04 2015 +0200
@@ -4157,8 +4157,8 @@
     launchOut.open('launchConfiguration', {'type' : 'org.eclipse.ui.externaltools.ProgramBuilderLaunchConfigurationType'})
     launchOut.element('booleanAttribute', {'key' : 'org.eclipse.debug.core.capture_output', 'value': consoleOn})
     launchOut.open('mapAttribute', {'key' : 'org.eclipse.debug.core.environmentVariables'})
-    launchOut.element('mapEntry', {'key' : 'JAVA_HOME', 'value' : _opts.java_home})
-    launchOut.element('mapEntry', {'key' : 'EXTRA_JAVA_HOMES', 'value' : _opts.extra_java_homes})
+    launchOut.element('mapEntry', {'key' : 'JAVA_HOME', 'value' : _default_java_home.jdk})
+    launchOut.element('mapEntry', {'key' : 'EXTRA_JAVA_HOMES', 'value' :  os.pathsep.join([extraJavaHome.jdk for extraJavaHome in _extra_java_homes])})
     launchOut.close('mapAttribute')
 
     if refresh: