changeset 21262:3aea74e5ada2

Merge
author Tom Rodriguez <tom.rodriguez@oracle.com>
date Wed, 06 May 2015 12:14:30 -0700
parents 8bae587f0efe (current diff) 0927730ed87f (diff)
children 4154f4842bf0 5e09292fb017
files
diffstat 32 files changed, 2008 insertions(+), 203 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGELOG.md	Wed May 06 11:44:29 2015 -0700
+++ b/CHANGELOG.md	Wed May 06 12:14:30 2015 -0700
@@ -4,6 +4,12 @@
 
 
 ## `tip`
+### Graal
+* Add experimental support constructing low-level IR in SSA form.
+* Add experimental support for SSA linear scan register allocation.
+...
+
+### Truffle
 ...
 
 ## Version 0.7
--- a/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler.common/src/com/oracle/graal/compiler/common/GraalOptions.java	Wed May 06 12:14:30 2015 -0700
@@ -315,6 +315,9 @@
     @Option(help = "Mark well-known stable fields as such.", type = OptionType.Debug)
     public static final OptionValue<Boolean> ImplicitStableValues = new OptionValue<>(true);
 
+    @Option(help = "Generate SSA LIR.", type = OptionType.Debug)
+    public static final OptionValue<Boolean> SSA_LIR = new OptionValue<>(false);
+
     /**
      * Counts the various paths taken through snippets.
      */
--- a/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler.sparc.test/src/com/oracle/graal/compiler/sparc/test/SPARCAllocatorTest.java	Wed May 06 12:14:30 2015 -0700
@@ -58,7 +58,7 @@
 
     @Test
     public void test3() {
-        testAllocation("test3snippet", 3, 1, 0);
+        testAllocation("test3snippet", 3, 0, 0);
     }
 
     public static long test3snippet(long x) {
--- a/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler.sparc/src/com/oracle/graal/compiler/sparc/SPARCLIRGenerator.java	Wed May 06 12:14:30 2015 -0700
@@ -59,6 +59,7 @@
 import com.oracle.graal.lir.sparc.SPARCMove.MoveFpGpVIS3;
 import com.oracle.graal.lir.sparc.SPARCMove.MoveFromRegOp;
 import com.oracle.graal.lir.sparc.SPARCMove.MoveToRegOp;
+import com.oracle.graal.lir.sparc.SPARCMove.SPARCStackMove;
 import com.oracle.graal.lir.sparc.SPARCMove.StackLoadAddressOp;
 import com.oracle.graal.phases.util.*;
 import com.oracle.graal.sparc.*;
@@ -78,6 +79,11 @@
         public LIRInstruction createMove(AllocatableValue result, Value input) {
             return SPARCLIRGenerator.this.createMove(result, input);
         }
+
+        @Override
+        public LIRInstruction createStackMove(AllocatableValue result, Value input) {
+            return new SPARCStackMove(result, input);
+        }
     }
 
     public SPARCLIRGenerator(LIRKindTool lirKindTool, Providers providers, CallingConvention cc, LIRGenerationResult lirGenRes) {
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/EATestBase.java	Wed May 06 12:14:30 2015 -0700
@@ -51,6 +51,7 @@
     public static class TestClassInt {
         public int x;
         public int y;
+        public int z;
 
         public TestClassInt() {
             this(0, 0);
@@ -68,7 +69,7 @@
         @Override
         public boolean equals(Object obj) {
             TestClassInt other = (TestClassInt) obj;
-            return x == other.x && y == other.y;
+            return x == other.x && y == other.y && z == other.z;
         }
 
         @Override
--- a/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler.test/src/com/oracle/graal/compiler/test/ea/UnsafeEATest.java	Wed May 06 12:14:30 2015 -0700
@@ -36,15 +36,23 @@
     public static int zero = 0;
 
     private static final Unsafe unsafe;
-    private static final long fieldOffsetX;
-    private static final long fieldOffsetY;
+    private static final long fieldOffset1;
+    private static final long fieldOffset2;
 
     static {
         unsafe = UnsafeAccess.unsafe;
         try {
-            fieldOffsetX = unsafe.objectFieldOffset(TestClassInt.class.getField("x"));
-            fieldOffsetY = unsafe.objectFieldOffset(TestClassInt.class.getField("y"));
-            assert fieldOffsetY == fieldOffsetX + 4;
+            long localFieldOffset1 = unsafe.objectFieldOffset(TestClassInt.class.getField("x"));
+            // Make the fields 8 byte aligned (Required for testing setLong on Architectures which
+            // does not support unaligned memory access
+            if (localFieldOffset1 % 8 == 0) {
+                fieldOffset1 = localFieldOffset1;
+                fieldOffset2 = unsafe.objectFieldOffset(TestClassInt.class.getField("y"));
+            } else {
+                fieldOffset1 = unsafe.objectFieldOffset(TestClassInt.class.getField("y"));
+                fieldOffset2 = unsafe.objectFieldOffset(TestClassInt.class.getField("z"));
+            }
+            assert fieldOffset2 == fieldOffset1 + 4;
         } catch (Exception e) {
             throw new RuntimeException(e);
         }
@@ -57,8 +65,8 @@
 
     public static int testSimpleIntSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putInt(x, fieldOffsetX, 101);
-        return unsafe.getInt(x, fieldOffsetX);
+        unsafe.putInt(x, fieldOffset1, 101);
+        return unsafe.getInt(x, fieldOffset1);
     }
 
     @Test
@@ -68,7 +76,7 @@
 
     public static TestClassInt testMaterializedIntSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putInt(x, fieldOffsetX, 101);
+        unsafe.putInt(x, fieldOffset1, 101);
         return x;
     }
 
@@ -79,8 +87,8 @@
 
     public static double testSimpleDoubleSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putDouble(x, fieldOffsetX, 10.1);
-        return unsafe.getDouble(x, fieldOffsetX);
+        unsafe.putDouble(x, fieldOffset1, 10.1);
+        return unsafe.getDouble(x, fieldOffset1);
     }
 
     @Test
@@ -97,12 +105,12 @@
         TestClassInt x;
         if (a) {
             x = new TestClassInt(0, 0);
-            unsafe.putDouble(x, fieldOffsetX, doubleField);
+            unsafe.putDouble(x, fieldOffset1, doubleField);
         } else {
             x = new TestClassInt();
-            unsafe.putDouble(x, fieldOffsetX, doubleField2);
+            unsafe.putDouble(x, fieldOffset1, doubleField2);
         }
-        return unsafe.getDouble(x, fieldOffsetX);
+        return unsafe.getDouble(x, fieldOffset1);
     }
 
     @Test
@@ -112,7 +120,7 @@
 
     public static TestClassInt testMaterializedDoubleSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putDouble(x, fieldOffsetX, 10.1);
+        unsafe.putDouble(x, fieldOffset1, 10.1);
         return x;
     }
 
@@ -126,10 +134,10 @@
 
     public static TestClassInt testDeoptDoubleVarSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putDouble(x, fieldOffsetX, doubleField);
+        unsafe.putDouble(x, fieldOffset1, doubleField);
         doubleField2 = 123;
         try {
-            doubleField = ((int) unsafe.getDouble(x, fieldOffsetX)) / zero;
+            doubleField = ((int) unsafe.getDouble(x, fieldOffset1)) / zero;
         } catch (RuntimeException e) {
             return x;
         }
@@ -143,10 +151,10 @@
 
     public static TestClassInt testDeoptDoubleConstantSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putDouble(x, fieldOffsetX, 10.123);
+        unsafe.putDouble(x, fieldOffset1, 10.123);
         doubleField2 = 123;
         try {
-            doubleField = ((int) unsafe.getDouble(x, fieldOffsetX)) / zero;
+            doubleField = ((int) unsafe.getDouble(x, fieldOffset1)) / zero;
         } catch (RuntimeException e) {
             return x;
         }
@@ -163,10 +171,10 @@
 
     public static TestClassInt testDeoptLongVarSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putLong(x, fieldOffsetX, longField);
+        unsafe.putLong(x, fieldOffset1, longField);
         longField2 = 123;
         try {
-            longField = unsafe.getLong(x, fieldOffsetX) / zero;
+            longField = unsafe.getLong(x, fieldOffset1) / zero;
         } catch (RuntimeException e) {
             return x;
         }
@@ -180,10 +188,10 @@
 
     public static TestClassInt testDeoptLongConstantSnippet() {
         TestClassInt x = new TestClassInt();
-        unsafe.putLong(x, fieldOffsetX, 0x2222222210123L);
+        unsafe.putLong(x, fieldOffset1, 0x2222222210123L);
         longField2 = 123;
         try {
-            longField = unsafe.getLong(x, fieldOffsetX) / zero;
+            longField = unsafe.getLong(x, fieldOffset1) / zero;
         } catch (RuntimeException e) {
             return x;
         }
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/LIRGenerationPhase.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/LIRGenerationPhase.java	Wed May 06 12:14:30 2015 -0700
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.compiler;
 
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -29,6 +31,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.phases.*;
+import com.oracle.graal.lir.ssa.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.cfg.*;
 import com.oracle.graal.nodes.spi.*;
@@ -60,6 +63,7 @@
             emitBlock(nodeLirBuilder, lirGenRes, (Block) b, graph, schedule.getBlockToNodesMap());
         }
         context.lirGen.beforeRegisterAllocation();
+        assert !SSA_LIR.getValue() || SSAUtils.verifySSAForm(lirGenRes.getLIR());
     }
 
     private static void emitBlock(NodeLIRBuilderTool nodeLirGen, LIRGenerationResult lirGenRes, Block b, StructuredGraph graph, BlockMap<List<Node>> blockMap) {
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/gen/NodeLIRBuilder.java	Wed May 06 12:14:30 2015 -0700
@@ -42,6 +42,7 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.lir.*;
 import com.oracle.graal.lir.StandardOp.JumpOp;
+import com.oracle.graal.lir.StandardOp.LabelOp;
 import com.oracle.graal.lir.debug.*;
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGenerator.Options;
@@ -192,6 +193,56 @@
         gen.append(op);
     }
 
+    protected LIRKind getExactPhiKind(PhiNode phi) {
+        ArrayList<Value> values = new ArrayList<>(phi.valueCount());
+        for (int i = 0; i < phi.valueCount(); i++) {
+            ValueNode node = phi.valueAt(i);
+            Value value = node instanceof ConstantNode ? ((ConstantNode) node).asJavaConstant() : getOperand(node);
+            if (value != null) {
+                values.add(value);
+            } else {
+                assert isPhiInputFromBackedge(phi, i);
+            }
+        }
+        LIRKind derivedKind = LIRKind.merge(values.toArray(new Value[values.size()]));
+        assert verifyPHIKind(derivedKind, gen.getLIRKind(phi.stamp()));
+        return derivedKind;
+    }
+
+    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);
+        return true;
+    }
+
+    private static boolean isPhiInputFromBackedge(PhiNode phi, int index) {
+        AbstractMergeNode merge = phi.merge();
+        AbstractEndNode end = merge.phiPredecessorAt(index);
+        return end instanceof LoopEndNode && ((LoopEndNode) end).loopBegin().equals(merge);
+    }
+
+    private Value[] createPhiIn(AbstractMergeNode merge) {
+        List<Value> values = new ArrayList<>();
+        for (ValuePhiNode phi : merge.valuePhis()) {
+            assert getOperand(phi) == null;
+            Variable value = gen.newVariable(getExactPhiKind(phi));
+            values.add(value);
+            setResult(phi, value);
+        }
+        return values.toArray(new Value[values.size()]);
+    }
+
+    private Value[] createPhiOut(AbstractMergeNode merge, AbstractEndNode pred) {
+        List<Value> values = new ArrayList<>();
+        for (PhiNode phi : merge.valuePhis()) {
+            Value value = operand(phi.valueAt(pred));
+            assert value != null;
+            values.add(value);
+        }
+        return values.toArray(new Value[values.size()]);
+    }
+
     public void doBlock(Block block, StructuredGraph graph, BlockMap<List<Node>> blockMap) {
         try (BlockScope blockScope = gen.getBlockScope(block)) {
 
@@ -200,6 +251,19 @@
                 emitPrologue(graph);
             } else {
                 assert block.getPredecessorCount() > 0;
+                if (SSA_LIR.getValue()) {
+                    // create phi-in value array
+                    AbstractBeginNode begin = block.getBeginNode();
+                    if (begin instanceof AbstractMergeNode) {
+                        AbstractMergeNode merge = (AbstractMergeNode) begin;
+                        LabelOp label = (LabelOp) gen.getResult().getLIR().getLIRforBlock(block).get(0);
+                        label.setIncomingValues(createPhiIn(merge));
+                        if (Options.PrintIRWithLIR.getValue() && !TTY.isSuppressed()) {
+                            TTY.println("Created PhiIn: " + label);
+
+                        }
+                    }
+                }
             }
 
             List<Node> nodes = blockMap.get(block);
@@ -347,8 +411,13 @@
     @Override
     public void visitEndNode(AbstractEndNode end) {
         AbstractMergeNode merge = end.merge();
-        moveToPhi(merge, end);
-        append(newJumpOp(getLIRBlock(merge)));
+        JumpOp jump = newJumpOp(getLIRBlock(merge));
+        if (SSA_LIR.getValue()) {
+            jump.setOutgoingValues(createPhiOut(merge, end));
+        } else {
+            moveToPhi(merge, end);
+        }
+        append(jump);
     }
 
     /**
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed May 06 12:14:30 2015 -0700
@@ -233,6 +233,8 @@
                 String localShortName = getClazz().getSimpleName();
                 if (localShortName.endsWith("Node") && !localShortName.equals("StartNode") && !localShortName.equals("EndNode")) {
                     shortName = localShortName.substring(0, localShortName.length() - 4);
+                } else {
+                    shortName = localShortName;
                 }
             }
         }
--- a/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotLIRGenerator.java	Wed May 06 12:14:30 2015 -0700
@@ -49,6 +49,7 @@
 import com.oracle.graal.lir.sparc.SPARCMove.NullCheckOp;
 import com.oracle.graal.lir.sparc.SPARCMove.StoreConstantOp;
 import com.oracle.graal.lir.sparc.SPARCMove.StoreOp;
+import com.oracle.graal.sparc.*;
 
 public class SPARCHotSpotLIRGenerator extends SPARCLIRGenerator implements HotSpotLIRGenerator {
 
@@ -214,6 +215,17 @@
     }
 
     @Override
+    public boolean canInlineConstant(JavaConstant c) {
+        if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(c)) {
+            return true;
+        } else if (c instanceof HotSpotObjectConstant) {
+            return ((HotSpotObjectConstant) c).isCompressed();
+        } else {
+            return super.canInlineConstant(c);
+        }
+    }
+
+    @Override
     public void emitStore(LIRKind kind, Value address, Value inputVal, LIRFrameState state) {
         SPARCAddressValue storeAddress = asAddressValue(address);
         if (isConstant(inputVal)) {
@@ -243,15 +255,85 @@
     }
 
     @Override
+    protected SPARCLIRInstruction createMove(AllocatableValue dst, Value src) {
+        if (src instanceof JavaConstant) {
+            return new SPARCHotSpotMove.HotSpotLoadConstantOp(dst, (JavaConstant) src);
+        } else {
+            return super.createMove(dst, src);
+        }
+    }
+
+    @Override
+    public void emitCompareBranch(PlatformKind cmpKind, Value x, Value y, Condition cond, boolean unorderedIsTrue, LabelRef trueDestination, LabelRef falseDestination,
+                    double trueDestinationProbability) {
+        Value localX = x;
+        Value localY = y;
+        if (localX instanceof HotSpotObjectConstant) {
+            localX = load(localX);
+        }
+        if (localY instanceof HotSpotObjectConstant) {
+            localY = load(localY);
+        }
+        super.emitCompareBranch(cmpKind, localX, localY, cond, unorderedIsTrue, trueDestination, falseDestination, trueDestinationProbability);
+    }
+
+    @Override
+    protected boolean emitCompare(PlatformKind cmpKind, Value a, Value b) {
+        Value localA = a;
+        Value localB = b;
+        if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(localA)) {
+            localA = SPARC.g0.asValue(LIRKind.value(Kind.Int));
+        } else if (localA instanceof HotSpotObjectConstant) {
+            localA = load(localA);
+        }
+        if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(localB)) {
+            localB = SPARC.g0.asValue(LIRKind.value(Kind.Int));
+        } else if (localB instanceof HotSpotObjectConstant) {
+            localB = load(localB);
+        }
+        return super.emitCompare(cmpKind, localA, localB);
+    }
+
+    @Override
     public Value emitCompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
-        // TODO
-        throw GraalInternalError.unimplemented();
+        LIRKind inputKind = pointer.getLIRKind();
+        assert inputKind.getPlatformKind() == Kind.Long || inputKind.getPlatformKind() == Kind.Object;
+        if (inputKind.isReference(0)) {
+            // oop
+            Variable result = newVariable(LIRKind.reference(Kind.Int));
+            append(new SPARCHotSpotMove.CompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull));
+            return result;
+        } else {
+            // metaspace pointer
+            Variable result = newVariable(LIRKind.value(Kind.Int));
+            AllocatableValue base = Value.ILLEGAL;
+            if (encoding.base != 0) {
+                base = emitMove(JavaConstant.forLong(encoding.base));
+            }
+            append(new SPARCHotSpotMove.CompressPointer(result, asAllocatable(pointer), base, encoding, nonNull));
+            return result;
+        }
     }
 
     @Override
     public Value emitUncompress(Value pointer, CompressEncoding encoding, boolean nonNull) {
-        // TODO
-        throw GraalInternalError.unimplemented();
+        LIRKind inputKind = pointer.getLIRKind();
+        assert inputKind.getPlatformKind() == Kind.Int;
+        if (inputKind.isReference(0)) {
+            // oop
+            Variable result = newVariable(LIRKind.reference(Kind.Object));
+            append(new SPARCHotSpotMove.UncompressPointer(result, asAllocatable(pointer), getProviders().getRegisters().getHeapBaseRegister().asValue(), encoding, nonNull));
+            return result;
+        } else {
+            // metaspace pointer
+            Variable result = newVariable(LIRKind.value(Kind.Long));
+            AllocatableValue base = Value.ILLEGAL;
+            if (encoding.base != 0) {
+                base = emitMove(JavaConstant.forLong(encoding.base));
+            }
+            append(new SPARCHotSpotMove.UncompressPointer(result, asAllocatable(pointer), base, encoding, nonNull));
+            return result;
+        }
     }
 
     /**
@@ -360,8 +442,16 @@
 
     public void emitNullCheck(Value address, LIRFrameState state) {
         PlatformKind kind = address.getLIRKind().getPlatformKind();
-        assert kind == Kind.Object || kind == Kind.Long : address + " - " + kind + " not an object!";
-        append(new NullCheckOp(load(address), state));
+        if (address.getLIRKind().getPlatformKind() == Kind.Int) {
+            CompressEncoding encoding = config.getOopEncoding();
+            Value uncompressed;
+            uncompressed = emitUncompress(address, encoding, false);
+
+            append(new NullCheckOp(load(uncompressed), state));
+        } else {
+            assert kind == Kind.Object || kind == Kind.Long : address + " - " + kind + " not an object!";
+            append(new NullCheckOp(load(address), state));
+        }
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot.sparc/src/com/oracle/graal/hotspot/sparc/SPARCHotSpotMove.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,193 @@
+/*
+ * Copyright (c) 2013, 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.hotspot.sparc;
+
+import static com.oracle.graal.api.code.ValueUtil.*;
+import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.asm.*;
+import com.oracle.graal.asm.sparc.SPARCAssembler.Annul;
+import com.oracle.graal.asm.sparc.SPARCAssembler.BranchPredict;
+import com.oracle.graal.asm.sparc.SPARCAssembler.CC;
+import com.oracle.graal.asm.sparc.SPARCAssembler.ConditionFlag;
+import com.oracle.graal.asm.sparc.SPARCAssembler.RCondition;
+import com.oracle.graal.asm.sparc.*;
+import com.oracle.graal.compiler.common.*;
+import com.oracle.graal.hotspot.HotSpotVMConfig.CompressEncoding;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.StandardOp.MoveOp;
+import com.oracle.graal.lir.asm.*;
+import com.oracle.graal.lir.sparc.*;
+
+public class SPARCHotSpotMove {
+
+    public static final class HotSpotLoadConstantOp extends SPARCLIRInstruction implements MoveOp {
+        public static final LIRInstructionClass<HotSpotLoadConstantOp> TYPE = LIRInstructionClass.create(HotSpotLoadConstantOp.class);
+
+        @Def({REG}) private AllocatableValue result;
+        private final JavaConstant input;
+
+        public HotSpotLoadConstantOp(AllocatableValue result, JavaConstant input) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+            assert isRegister(result);
+            if (HotSpotCompressedNullConstant.COMPRESSED_NULL.equals(input)) {
+                masm.mov(0, asRegister(result));
+            } else if (input instanceof HotSpotObjectConstant) {
+                boolean compressed = ((HotSpotObjectConstant) input).isCompressed();
+                if (crb.target.inlineObjects) {
+                    crb.recordInlineDataInCode(input);
+                    if (compressed) {
+                        masm.sethi(0xDEADDEAD >>> 10, asRegister(result));
+                        masm.add(asRegister(result), 0xAD & 0x3F, asRegister(result));
+                    } else {
+                        new SPARCMacroAssembler.Setx(0xDEADDEADDEADDEADL, asRegister(result), true).emit(masm);
+                    }
+                } else {
+                    GraalInternalError.unimplemented();
+                }
+            } else if (input instanceof HotSpotMetaspaceConstant) {
+                assert input.getKind() == Kind.Int || input.getKind() == Kind.Long;
+                boolean compressed = input.getKind() == Kind.Int;
+                boolean isImmutable = GraalOptions.ImmutableCode.getValue();
+                boolean generatePIC = GraalOptions.GeneratePIC.getValue();
+                crb.recordInlineDataInCode(input);
+                if (compressed) {
+                    if (isImmutable && generatePIC) {
+                        GraalInternalError.unimplemented();
+                    } else {
+                        new SPARCMacroAssembler.Setx(input.asInt(), asRegister(result), true).emit(masm);
+                    }
+                } else {
+                    if (isImmutable && generatePIC) {
+                        GraalInternalError.unimplemented();
+                    } else {
+                        new SPARCMacroAssembler.Setx(input.asLong(), asRegister(result), true).emit(masm);
+                    }
+                }
+            } else {
+                SPARCMove.move(crb, masm, result, input, SPARCDelayedControlTransfer.DUMMY);
+            }
+        }
+
+        public Value getInput() {
+            return input;
+        }
+
+        public AllocatableValue getResult() {
+            return result;
+        }
+    }
+
+    public static final class CompressPointer extends SPARCLIRInstruction {
+        public static final LIRInstructionClass<CompressPointer> TYPE = LIRInstructionClass.create(CompressPointer.class);
+
+        private final CompressEncoding encoding;
+        private final boolean nonNull;
+
+        @Def({REG}) protected AllocatableValue result;
+        @Use({REG}) protected AllocatableValue input;
+        @Alive({REG, ILLEGAL}) protected AllocatableValue baseRegister;
+
+        public CompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+            this.baseRegister = baseRegister;
+            this.encoding = encoding;
+            this.nonNull = nonNull;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+            SPARCMove.move(crb, masm, result, input, SPARCDelayedControlTransfer.DUMMY);
+
+            Register resReg = asRegister(result);
+            if (encoding.base != 0) {
+                Register baseReg = asRegister(baseRegister);
+                if (!nonNull) {
+                    masm.cmp(resReg, baseReg);
+                    masm.movcc(ConditionFlag.Equal, CC.Xcc, baseReg, resReg);
+                }
+                masm.sub(resReg, baseReg, resReg);
+            }
+
+            if (encoding.shift != 0) {
+                masm.srlx(resReg, encoding.shift, resReg);
+            }
+        }
+    }
+
+    public static final class UncompressPointer extends SPARCLIRInstruction {
+        public static final LIRInstructionClass<UncompressPointer> TYPE = LIRInstructionClass.create(UncompressPointer.class);
+
+        private final CompressEncoding encoding;
+        private final boolean nonNull;
+
+        @Def({REG}) protected AllocatableValue result;
+        @Use({REG}) protected AllocatableValue input;
+        @Alive({REG, ILLEGAL}) protected AllocatableValue baseRegister;
+
+        public UncompressPointer(AllocatableValue result, AllocatableValue input, AllocatableValue baseRegister, CompressEncoding encoding, boolean nonNull) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+            this.baseRegister = baseRegister;
+            this.encoding = encoding;
+            this.nonNull = nonNull;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+            SPARCMove.move(crb, masm, result, input, SPARCDelayedControlTransfer.DUMMY);
+
+            Register resReg = asRegister(result);
+            if (encoding.shift != 0) {
+                masm.sllx(resReg, 32, resReg);
+                masm.srlx(resReg, 32 - encoding.shift, resReg);
+            }
+
+            if (encoding.base != 0) {
+                if (nonNull) {
+                    masm.add(resReg, asRegister(baseRegister), resReg);
+                } else {
+                    masm.cmp(resReg, resReg);
+
+                    Label done = new Label();
+                    masm.bpr(RCondition.Rc_nz, Annul.ANNUL, done, BranchPredict.PREDICT_TAKEN, resReg);
+                    masm.add(asRegister(baseRegister), resReg, resReg);
+                    masm.bind(done);
+                }
+            }
+        }
+    }
+
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotConstantPool.java	Wed May 06 12:14:30 2015 -0700
@@ -115,13 +115,23 @@
         }
     }
 
+    private static class LookupTypeCacheElement {
+        int lastCpi = Integer.MIN_VALUE;
+        JavaType javaType;
+
+        public LookupTypeCacheElement(int lastCpi, JavaType javaType) {
+            super();
+            this.lastCpi = lastCpi;
+            this.javaType = javaType;
+        }
+    }
+
     /**
      * Reference to the C++ ConstantPool object.
      */
     private final long metaspaceConstantPool;
     private final Object[] cache;
-    private ResolvedJavaType lastType;
-    private int lastTypeCpi = Integer.MIN_VALUE;
+    private volatile LookupTypeCacheElement lastLookupType;
 
     public HotSpotConstantPool(long metaspaceConstantPool) {
         this.metaspaceConstantPool = metaspaceConstantPool;
@@ -489,22 +499,15 @@
 
     @Override
     public JavaType lookupType(int cpi, int opcode) {
-        if (cpi == this.lastTypeCpi) {
-            synchronized (this) {
-                if (cpi == this.lastTypeCpi) {
-                    return this.lastType;
-                }
-            }
+        final LookupTypeCacheElement elem = this.lastLookupType;
+        if (elem != null && elem.lastCpi == cpi) {
+            return elem.javaType;
+        } else {
+            final long metaspacePointer = runtime().getCompilerToVM().lookupKlassInPool(metaspaceConstantPool, cpi);
+            JavaType result = getJavaType(metaspacePointer);
+            this.lastLookupType = new LookupTypeCacheElement(cpi, result);
+            return result;
         }
-        final long metaspacePointer = runtime().getCompilerToVM().lookupKlassInPool(metaspaceConstantPool, cpi);
-        JavaType result = getJavaType(metaspacePointer);
-        if (result instanceof ResolvedJavaType) {
-            synchronized (this) {
-                this.lastType = (ResolvedJavaType) result;
-                this.lastTypeCpi = cpi;
-            }
-        }
-        return result;
     }
 
     @Override
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.jtt/src/com/oracle/graal/jtt/loop/LoopPhiResolutionTest.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,68 @@
+/*
+ * 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.jtt.loop;
+
+import org.junit.*;
+
+import com.oracle.graal.api.directives.*;
+import com.oracle.graal.jtt.*;
+
+/*
+ */
+public class LoopPhiResolutionTest extends JTTTest {
+
+    public static int test(int count) {
+        int i1 = 1;
+        int i2 = 2;
+        int i3 = 3;
+        int i4 = 4;
+
+        for (int i = 0; i < count; i++) {
+            i1 = wormhole(i1);
+            i2 = wormhole(i2);
+            i3 = wormhole(i2);
+            i4 = wormhole(i4);
+        }
+        return i1 + i2 * 10 + i3 * 100 + i4 * 1000;
+    }
+
+    private static int wormhole(int x) {
+        return (int) GraalDirectives.opaque((long) x);
+    }
+
+    @Test
+    public void run0() throws Throwable {
+        runTest("test", 0);
+    }
+
+    @Test
+    public void run1() throws Throwable {
+        runTest("test", 1);
+    }
+
+    @Test
+    public void run2() throws Throwable {
+        runTest("test", 10);
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/LIRTest.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,272 @@
+/*
+ * 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.jtt;
+
+import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.stream.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.api.replacements.*;
+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.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.nodeinfo.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.spi.*;
+
+/**
+ * Base class for LIR tests.
+ * <p>
+ * It provides facilities to replace methods with {@link LIRTestSpecification arbitrary LIR
+ * instructions}.
+ */
+public abstract class LIRTest extends JTTTest {
+
+    protected abstract static class LIRTestSpecification {
+        private Value result;
+
+        void generate(LIRGeneratorTool gen, Value arg0) {
+            defaultHandler(gen, arg0);
+        }
+
+        void generate(LIRGeneratorTool gen, Value arg0, Value arg1) {
+            defaultHandler(gen, arg0, arg1);
+        }
+
+        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) {
+            defaultHandler(gen, arg0, arg1, arg2, arg3);
+        }
+
+        void generate(LIRGeneratorTool gen, Value arg0, Value arg1, Value arg2, Value arg3, Value arg4) {
+            defaultHandler(gen, arg0, arg1, arg2, arg3, arg4);
+        }
+
+        private static void defaultHandler(@SuppressWarnings("unused") LIRGeneratorTool gen, Value... args) {
+            throw new GraalInternalError("LIRTestSpecification cannot handle generate() with %d arguments", args.length);
+        }
+
+        void generate(LIRGeneratorTool gen, Value[] values) {
+            if (values.length == 1) {
+                generate(gen, values[0]);
+            } else if (values.length == 2) {
+                generate(gen, values[0], values[1]);
+            } else if (values.length == 3) {
+                generate(gen, values[0], values[1], values[2]);
+            } else if (values.length == 4) {
+                generate(gen, values[0], values[1], values[2], values[3]);
+            } else if (values.length == 5) {
+                generate(gen, values[0], values[1], values[2], values[3], values[4]);
+            } else {
+                GraalInternalError.unimplemented();
+            }
+
+        }
+
+        public void setResult(Value value) {
+            result = value;
+        }
+
+        public Value getResult() {
+            assert result != null : "Result not set (using setResult())";
+            return result;
+        }
+    }
+
+    @NodeInfo
+    private static final class FixedLIRTestNode extends FixedWithNextNode implements LIRLowerable {
+
+        public static final NodeClass<FixedLIRTestNode> TYPE = NodeClass.create(FixedLIRTestNode.class);
+        @Input protected ValueNode opsNode;
+        @Input protected NodeInputList<ValueNode> values;
+        public final SnippetReflectionProvider snippetReflection;
+
+        public FixedLIRTestNode(SnippetReflectionProvider snippetReflection, ValueNode opsNode, ValueNode[] values) {
+            super(TYPE, StampFactory.forVoid());
+            this.opsNode = opsNode;
+            this.values = new NodeInputList<>(this, values);
+            this.snippetReflection = snippetReflection;
+        }
+
+        public NodeInputList<ValueNode> values() {
+            return values;
+        }
+
+        public ValueNode getLIROpsNode() {
+            return opsNode;
+        }
+
+        @Override
+        public void generate(NodeLIRBuilderTool gen) {
+            LIRTestSpecification ops = getLIROperations();
+            Stream<Value> v = values().stream().map(node -> gen.operand(node));
+
+            ops.generate(gen.getLIRGeneratorTool(), v.toArray(size -> new Value[size]));
+        }
+
+        private LIRTestSpecification getLIROperations() {
+            assert getLIROpsNode().isConstant();
+            LIRTestSpecification spec = snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant());
+            return spec;
+        }
+    }
+
+    @NodeInfo
+    private static final class FloatingLIRTestNode extends FloatingNode implements LIRLowerable, Simplifiable {
+
+        public static final NodeClass<FloatingLIRTestNode> TYPE = NodeClass.create(FloatingLIRTestNode.class);
+        @Input protected ValueNode opsNode;
+        @Input protected NodeInputList<ValueNode> values;
+        public final SnippetReflectionProvider snippetReflection;
+
+        public FloatingLIRTestNode(SnippetReflectionProvider snippetReflection, Kind kind, ValueNode opsNode, ValueNode[] values) {
+            super(TYPE, StampFactory.forKind(kind));
+            this.opsNode = opsNode;
+            this.values = new NodeInputList<>(this, values);
+            this.snippetReflection = snippetReflection;
+        }
+
+        public NodeInputList<ValueNode> values() {
+            return values;
+        }
+
+        public ValueNode getLIROpsNode() {
+            return opsNode;
+        }
+
+        @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));
+
+            ops.generate(gen.getLIRGeneratorTool(), v.toArray(size -> new Value[size]));
+            gen.setResult(this, ops.getResult());
+        }
+
+        private LIRTestSpecification getLIROperations() {
+            assert getLIROpsNode().isConstant();
+            LIRTestSpecification spec = snippetReflection.asObject(LIRTestSpecification.class, getLIROpsNode().asJavaConstant());
+            return spec;
+        }
+    }
+
+    private InvocationPlugin fixedLIRNodePlugin = new InvocationPlugin() {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec) {
+            b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0) {
+            b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1) {
+            b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2) {
+            b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1, arg2}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
+            b.add(new FixedLIRTestNode(getSnippetReflection(), spec, new ValueNode[]{arg0, arg1, arg2, arg3}));
+            return true;
+        }
+    };
+
+    private InvocationPlugin floatingLIRNodePlugin = new InvocationPlugin() {
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec) {
+            b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0) {
+            b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1) {
+            b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2) {
+            b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1, arg2}));
+            return true;
+        }
+
+        public boolean apply(GraphBuilderContext b, ResolvedJavaMethod targetMethod, Receiver receiver, ValueNode spec, ValueNode arg0, ValueNode arg1, ValueNode arg2, ValueNode arg3) {
+            b.addPush(new FloatingLIRTestNode(getSnippetReflection(), targetMethod.getSignature().getReturnKind(), spec, new ValueNode[]{arg0, arg1, arg2, arg3}));
+            return true;
+        }
+
+    };
+
+    @Override
+    protected GraphBuilderConfiguration editGraphBuilderConfiguration(GraphBuilderConfiguration conf) {
+        InvocationPlugins invocationPlugins = conf.getPlugins().getInvocationPlugins();
+
+        Class<? extends LIRTest> c = getClass();
+        for (Method m : c.getMethods()) {
+            if (m.getAnnotation(LIRIntrinsic.class) != null) {
+                assert Modifier.isStatic(m.getModifiers());
+                Class<?>[] p = m.getParameterTypes();
+                assert p.length > 0;
+                assert p[0].equals(LIRTestSpecification.class);
+
+                if (m.getReturnType().equals(void.class)) {
+                    invocationPlugins.register(fixedLIRNodePlugin, c, m.getName(), p);
+                } else {
+                    invocationPlugins.register(floatingLIRNodePlugin, c, m.getName(), p);
+                }
+            }
+        }
+        return super.editGraphBuilderConfiguration(conf);
+    }
+
+    @java.lang.annotation.Retention(RetentionPolicy.RUNTIME)
+    @java.lang.annotation.Target(ElementType.METHOD)
+    public static @interface LIRIntrinsic {
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir.jtt/src/com/oracle/graal/lir/jtt/StackMoveTest.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,127 @@
+/*
+ * 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.jtt;
+
+import org.junit.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.lir.framemap.*;
+import com.oracle.graal.lir.gen.*;
+
+public class StackMoveTest extends LIRTest {
+    private static final LIRTestSpecification stackCopy = new LIRTestSpecification() {
+        @Override
+        public void generate(LIRGeneratorTool gen, Value a) {
+            FrameMapBuilder frameMapBuilder = gen.getResult().getFrameMapBuilder();
+            // create slots
+            StackSlotValue s1 = frameMapBuilder.allocateSpillSlot(a.getLIRKind());
+            StackSlotValue s2 = frameMapBuilder.allocateSpillSlot(a.getLIRKind());
+            // move stuff around
+            gen.emitMove(s1, a);
+            gen.append(gen.getSpillMoveFactory().createStackMove(s2, s1));
+            setResult(gen.emitMove(s2));
+        }
+    };
+
+    @SuppressWarnings("unused")
+    @LIRIntrinsic
+    public static int copyInt(LIRTestSpecification spec, int a) {
+        return a;
+    }
+
+    public int testInt(int a) {
+        return copyInt(stackCopy, a);
+    }
+
+    @Test
+    public void runInt() throws Throwable {
+        runTest("testInt", Integer.MIN_VALUE);
+        runTest("testInt", -1);
+        runTest("testInt", 0);
+        runTest("testInt", 1);
+        runTest("testInt", Integer.MAX_VALUE);
+    }
+
+    @SuppressWarnings("unused")
+    @LIRIntrinsic
+    public static long copyLong(LIRTestSpecification spec, long a) {
+        return a;
+    }
+
+    public long testLong(long a) {
+        return copyLong(stackCopy, a);
+    }
+
+    @Test
+    public void runLong() throws Throwable {
+        runTest("testLong", Long.MIN_VALUE);
+        runTest("testLong", -1L);
+        runTest("testLong", 0L);
+        runTest("testLong", 1L);
+        runTest("testLong", Long.MAX_VALUE);
+    }
+
+    @SuppressWarnings("unused")
+    @LIRIntrinsic
+    public static float copyFloat(LIRTestSpecification spec, float a) {
+        return a;
+    }
+
+    public float testFloat(float a) {
+        return copyFloat(stackCopy, a);
+    }
+
+    @Test
+    public void runFloat() throws Throwable {
+        runTest("testFloat", Float.MIN_VALUE);
+        runTest("testFloat", -1f);
+        runTest("testFloat", -0.1f);
+        runTest("testFloat", 0f);
+        runTest("testFloat", 0.1f);
+        runTest("testFloat", 1f);
+        runTest("testFloat", Float.MAX_VALUE);
+    }
+
+    @SuppressWarnings("unused")
+    @LIRIntrinsic
+    public static double copyDouble(LIRTestSpecification spec, double a) {
+        return a;
+    }
+
+    public double testDouble(double a) {
+        return copyDouble(stackCopy, a);
+    }
+
+    @Test
+    public void runDouble() throws Throwable {
+        runTest("testDouble", Double.MIN_VALUE);
+        runTest("testDouble", -1.);
+        runTest("testDouble", -0.1);
+        runTest("testDouble", 0.);
+        runTest("testDouble", 0.1);
+        runTest("testDouble", 1.);
+        runTest("testDouble", Double.MAX_VALUE);
+    }
+
+}
--- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCArithmetic.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCArithmetic.java	Wed May 06 12:14:30 2015 -0700
@@ -66,7 +66,7 @@
         public static final LIRInstructionClass<Unary2Op> TYPE = LIRInstructionClass.create(Unary2Op.class);
 
         @Opcode private final SPARCArithmetic opcode;
-        @Def({REG, HINT}) protected AllocatableValue result;
+        @Def({REG}) protected AllocatableValue result;
         @Use({REG}) protected AllocatableValue x;
 
         public Unary2Op(SPARCArithmetic opcode, AllocatableValue result, AllocatableValue x) {
@@ -90,7 +90,7 @@
         public static final LIRInstructionClass<BinaryRegReg> TYPE = LIRInstructionClass.create(BinaryRegReg.class);
 
         @Opcode private final SPARCArithmetic opcode;
-        @Def({REG, HINT}) protected Value result;
+        @Def({REG}) protected Value result;
         @Use({REG}) protected Value x;
         @Alive({REG}) protected Value y;
         @State LIRFrameState state;
@@ -127,7 +127,7 @@
         public static final LIRInstructionClass<BinaryRegConst> TYPE = LIRInstructionClass.create(BinaryRegConst.class);
 
         @Opcode private final SPARCArithmetic opcode;
-        @Def({REG, HINT}) protected AllocatableValue result;
+        @Def({REG}) protected AllocatableValue result;
         @Use({REG}) protected Value x;
         @State protected LIRFrameState state;
         protected JavaConstant y;
--- a/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCMove.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir.sparc/src/com/oracle/graal/lir/sparc/SPARCMove.java	Wed May 06 12:14:30 2015 -0700
@@ -253,6 +253,62 @@
         }
     }
 
+    @Opcode("STACKMOVE")
+    public static final class SPARCStackMove extends SPARCLIRInstruction implements MoveOp, SPARCTailDelayedLIRInstruction {
+        public static final LIRInstructionClass<SPARCStackMove> TYPE = LIRInstructionClass.create(SPARCStackMove.class);
+
+        @Def({STACK}) protected AllocatableValue result;
+        @Use({STACK, HINT}) protected Value input;
+
+        public SPARCStackMove(AllocatableValue result, Value input) {
+            super(TYPE);
+            this.result = result;
+            this.input = input;
+        }
+
+        @Override
+        public Value getInput() {
+            return input;
+        }
+
+        @Override
+        public AllocatableValue getResult() {
+            return result;
+        }
+
+        @Override
+        public void emitCode(CompilationResultBuilder crb, SPARCMacroAssembler masm) {
+            try (ScratchRegister scratchReg = masm.getScratchRegister()) {
+                Register scratch = scratchReg.getRegister();
+                StackSlot intInput = reInterprete(asStackSlot(getInput()));
+                StackSlot intResult = reInterprete(asStackSlot(getResult()));
+                // move stack slot
+                move(crb, masm, scratch.asValue(intInput.getLIRKind()), intInput, SPARCDelayedControlTransfer.DUMMY);
+                move(crb, masm, intResult, scratch.asValue(intResult.getLIRKind()), delayedControlTransfer);
+            }
+
+        }
+
+        private static StackSlot reInterprete(StackSlot slot) {
+            switch ((Kind) slot.getPlatformKind()) {
+                case Boolean:
+                case Byte:
+                case Short:
+                case Char:
+                case Int:
+                case Long:
+                case Object:
+                    return slot;
+                case Float:
+                    return StackSlot.get(LIRKind.value(Kind.Int), slot.getRawOffset(), slot.getRawAddFrameSize());
+                case Double:
+                    return StackSlot.get(LIRKind.value(Kind.Long), slot.getRawOffset(), slot.getRawAddFrameSize());
+                default:
+                    throw GraalInternalError.shouldNotReachHere();
+            }
+        }
+    }
+
     public abstract static class MemOp extends SPARCLIRInstruction implements ImplicitNullCheck {
         public static final LIRInstructionClass<MemOp> TYPE = LIRInstructionClass.create(MemOp.class);
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/LIRVerifier.java	Wed May 06 12:14:30 2015 -0700
@@ -35,6 +35,7 @@
 import com.oracle.graal.lir.LIRInstruction.OperandFlag;
 import com.oracle.graal.lir.LIRInstruction.OperandMode;
 import com.oracle.graal.lir.framemap.*;
+import com.oracle.graal.lir.ssa.*;
 
 public final class LIRVerifier {
 
@@ -128,6 +129,9 @@
                 LIRInstruction last = lir.getLIRforBlock(block).get(lir.getLIRforBlock(block).size() - 1);
                 assert last instanceof StandardOp.JumpOp : "block with successor must end with unconditional jump";
             }
+            if (block.getPredecessorCount() > 1) {
+                SSAUtils.verifyPhi(lir, block);
+            }
 
             for (LIRInstruction op : lir.getLIRforBlock(block)) {
                 curInstruction = op;
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/StandardOp.java	Wed May 06 12:14:30 2015 -0700
@@ -23,6 +23,7 @@
 package com.oracle.graal.lir;
 
 import static com.oracle.graal.lir.LIRInstruction.OperandFlag.*;
+import static com.oracle.graal.lir.LIRValueUtil.*;
 
 import java.util.*;
 
@@ -87,9 +88,22 @@
 
         public void setIncomingValues(Value[] values) {
             assert incomingValues.length == 0;
+            assert values != null;
             incomingValues = values;
         }
 
+        public int getIncomingSize() {
+            return incomingValues.length;
+        }
+
+        public Value getIncomingValue(int idx) {
+            return incomingValues[idx];
+        }
+
+        public void clearIncomingValues() {
+            incomingValues = NO_VALUES;
+        }
+
         @Override
         public void emitCode(CompilationResultBuilder crb) {
             if (align) {
@@ -101,6 +115,13 @@
         public Label getLabel() {
             return label;
         }
+
+        /**
+         * @return true if this label acts as a PhiIn.
+         */
+        public boolean isPhiIn() {
+            return getIncomingSize() > 0 && isVariable(getIncomingValue(0));
+        }
     }
 
     /**
@@ -109,6 +130,10 @@
     public static class JumpOp extends LIRInstruction implements BlockEndOp {
         public static final LIRInstructionClass<JumpOp> TYPE = LIRInstructionClass.create(JumpOp.class);
 
+        private static final Value[] NO_VALUES = new Value[0];
+
+        @Alive({REG, STACK, CONST}) private Value[] outgoingValues;
+
         private final LabelRef destination;
 
         public JumpOp(LabelRef destination) {
@@ -118,6 +143,25 @@
         protected JumpOp(LIRInstructionClass<? extends JumpOp> c, LabelRef destination) {
             super(c);
             this.destination = destination;
+            this.outgoingValues = NO_VALUES;
+        }
+
+        public void setOutgoingValues(Value[] values) {
+            assert outgoingValues.length == 0;
+            assert values != null;
+            outgoingValues = values;
+        }
+
+        public int getOutgoingSize() {
+            return outgoingValues.length;
+        }
+
+        public Value getOutgoingValue(int idx) {
+            return outgoingValues[idx];
+        }
+
+        public void clearOutgoingValues() {
+            outgoingValues = NO_VALUES;
         }
 
         @Override
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScan.java	Wed May 06 12:14:30 2015 -0700
@@ -42,6 +42,7 @@
 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;
@@ -57,7 +58,7 @@
  * >"Optimized Interval Splitting in a Linear Scan Register Allocator"</a> by Christian Wimmer and
  * Hanspeter Moessenboeck.
  */
-final class LinearScan {
+class LinearScan {
 
     final TargetDescription target;
     final LIRGenerationResult res;
@@ -201,7 +202,9 @@
     }
 
     protected MoveResolver createMoveResolver() {
-        return new MoveResolver(this);
+        MoveResolver moveResolver = new MoveResolver(this);
+        assert moveResolver.checkEmpty();
+        return moveResolver;
     }
 
     public static boolean isVariableOrRegister(Value value) {
@@ -522,6 +525,15 @@
         }
     };
 
+    /**
+     * @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")) {
@@ -536,67 +548,78 @@
 
             LIRInsertionBuffer insertionBuffer = new LIRInsertionBuffer();
             for (AbstractBlockBase<?> block : sortedBlocks) {
-                List<LIRInstruction> instructions = ir.getLIRforBlock(block);
-                int numInst = instructions.size();
-
-                // iterate all instructions of the block. skip the first
-                // because it is always a label
-                for (int j = 1; j < numInst; j++) {
-                    LIRInstruction op = instructions.get(j);
-                    int opId = op.id();
+                try (Indent indent1 = Debug.logAndIndent("Handle %s", block)) {
+                    List<LIRInstruction> instructions = ir.getLIRforBlock(block);
+                    int numInst = instructions.size();
 
-                    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.
-                        assert isVariable(move.getResult()) : "LinearScan inserts only moves to variables";
-
-                        Interval curInterval = intervalFor(move.getResult());
+                    // iterate all instructions of the block.
+                    for (int j = firstInstructionOfInterest(); j < numInst; j++) {
+                        LIRInstruction op = instructions.get(j);
+                        int opId = op.id();
 
-                        if (!isRegister(curInterval.location()) && curInterval.alwaysInMemory()) {
-                            // move target is a stack slot that is always correct, so eliminate
-                            // instruction
-                            if (Debug.isLogEnabled()) {
-                                Debug.log("eliminating move from interval %d to %d", operandNumber(move.getInput()), operandNumber(move.getResult()));
-                            }
-                            // 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);
+                        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);
                                 }
 
-                                AllocatableValue fromLocation = interval.location();
-                                AllocatableValue toLocation = canonicalSpillOpr(interval);
+                                // null-instructions are deleted by assignRegNum
+                                instructions.set(j, null);
+                            }
 
-                                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";
+                        } 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";
 
-                                insertionBuffer.append(j + 1, getSpillMoveFactory().createMove(toLocation, fromLocation));
+                            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);
+                                    }
 
-                                if (Debug.isLogEnabled()) {
-                                    Debug.log("inserting move after definition of interval %d to stack slot %s at opId %d", interval.operandNumber, interval.spillSlot(), opId);
+                                    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;
                             }
-                            interval = interval.next;
                         }
-                    }
-                } // end of instruction iteration
+                    } // end of instruction iteration
 
-                if (insertionBuffer.initialized()) {
-                    insertionBuffer.finish();
+                    if (insertionBuffer.initialized()) {
+                        insertionBuffer.finish();
+                    }
                 }
             } // end of block iteration
 
@@ -604,6 +627,22 @@
         }
     }
 
+    /**
+     * @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;
@@ -1058,7 +1097,7 @@
         }
 
         changeSpillDefinitionPos(interval, defPos);
-        if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal()) {
+        if (registerPriority == RegisterPriority.None && interval.spillState().ordinal() <= SpillState.StartInMemory.ordinal() && isStackSlot(operand)) {
             // detection of method-parameters and roundfp-results
             interval.setSpillState(SpillState.StartInMemory);
         }
@@ -1078,6 +1117,11 @@
             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
@@ -1456,8 +1500,11 @@
         throw new BailoutException("LinearScan: interval is null");
     }
 
-    void resolveCollectMappings(AbstractBlockBase<?> fromBlock, AbstractBlockBase<?> toBlock, MoveResolver moveResolver) {
+    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;
@@ -1551,7 +1598,7 @@
                             // directly resolve between pred and sux (without looking
                             // at the empty block
                             // between)
-                            resolveCollectMappings(pred, sux, moveResolver);
+                            resolveCollectMappings(pred, sux, block, moveResolver);
                             if (moveResolver.hasMappings()) {
                                 moveResolver.setInsertPosition(instructions, 1);
                                 moveResolver.resolveAndAppendMoves();
@@ -1579,7 +1626,7 @@
 
                             // collect all intervals that have been split between
                             // fromBlock and toBlock
-                            resolveCollectMappings(fromBlock, toBlock, moveResolver);
+                            resolveCollectMappings(fromBlock, toBlock, null, moveResolver);
                             if (moveResolver.hasMappings()) {
                                 resolveFindInsertPos(fromBlock, toBlock, moveResolver);
                                 moveResolver.resolveAndAppendMoves();
@@ -1588,6 +1635,7 @@
                     }
                 }
             }
+
         }
     }
 
@@ -1837,6 +1885,8 @@
                     verify();
                 }
 
+                beforeSpillMoveElimination();
+
                 try (Scope s1 = Debug.scope("EliminateSpillMove"); DebugCloseable a = eliminateSpillMoveTimer.start()) {
                     eliminateSpillMoves();
                 } catch (Throwable e) {
@@ -1861,6 +1911,9 @@
         }
     }
 
+    protected void beforeSpillMoveElimination() {
+    }
+
     private static final DebugMetric betterSpillPos = Debug.metric("BetterSpillPosition");
     private static final DebugMetric betterSpillPosWithLowerProbability = Debug.metric("BetterSpillPositionWithLowerProbability");
 
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/LinearScanPhase.java	Wed May 06 12:14:30 2015 -0700
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.lir.alloc.lsra;
 
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
 import java.util.*;
 
 import com.oracle.graal.api.code.*;
@@ -30,12 +32,28 @@
 import com.oracle.graal.lir.gen.*;
 import com.oracle.graal.lir.gen.LIRGeneratorTool.SpillMoveFactory;
 import com.oracle.graal.lir.phases.*;
+import com.oracle.graal.lir.ssa.*;
+import com.oracle.graal.options.*;
+import com.oracle.graal.options.DerivedOptionValue.OptionSupplier;
 
 public final class LinearScanPhase extends AllocationPhase {
 
+    public static final DerivedOptionValue<Boolean> SSA_LSRA = new DerivedOptionValue<>(new OptionSupplier<Boolean>() {
+
+        private static final long serialVersionUID = 9115795480259228194L;
+
+        public Boolean get() {
+            return SSA_LIR.getValue() && !SSADestructionPhase.Options.LIREagerSSADestruction.getValue();
+        }
+    });
+
     @Override
     protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, SpillMoveFactory spillMoveFactory) {
-        new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
+        if (LinearScanPhase.SSA_LSRA.getValue()) {
+            new SSALinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
+        } else {
+            new LinearScan(target, lirGenRes, spillMoveFactory, new RegisterAllocationConfig(lirGenRes.getFrameMapBuilder().getRegisterConfig())).allocate();
+        }
     }
 
 }
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/MoveResolver.java	Wed May 06 12:14:30 2015 -0700
@@ -35,7 +35,7 @@
 
 /**
  */
-final class MoveResolver {
+class MoveResolver {
 
     private final LinearScan allocator;
 
@@ -48,14 +48,16 @@
     private boolean multipleReadsAllowed;
     private final int[] registerBlocked;
 
-    private void setValueBlocked(Value location, int direction) {
+    protected void setValueBlocked(Value location, int direction) {
         assert direction == 1 || direction == -1 : "out of bounds";
         if (isRegister(location)) {
             registerBlocked[asRegister(location).number] += direction;
+        } else {
+            throw GraalInternalError.shouldNotReachHere("unhandled value " + location);
         }
     }
 
-    private int valueBlocked(Value location) {
+    protected int valueBlocked(Value location) {
         if (isRegister(location)) {
             return registerBlocked[asRegister(location).number];
         }
@@ -66,10 +68,18 @@
         multipleReadsAllowed = true;
     }
 
+    protected boolean areMultipleReadsAllowed() {
+        return multipleReadsAllowed;
+    }
+
     boolean hasMappings() {
         return mappingFrom.size() > 0;
     }
 
+    protected LinearScan getAllocator() {
+        return allocator;
+    }
+
     MoveResolver(LinearScan allocator) {
 
         this.allocator = allocator;
@@ -80,18 +90,21 @@
         this.insertIdx = -1;
         this.insertionBuffer = new LIRInsertionBuffer();
         this.registerBlocked = new int[allocator.registers.length];
-        assert checkEmpty();
     }
 
     boolean checkEmpty() {
         assert mappingFrom.size() == 0 && mappingFromOpr.size() == 0 && mappingTo.size() == 0 : "list must be empty before and after processing";
-        for (int i = 0; i < allocator.registers.length; i++) {
+        for (int i = 0; i < getAllocator().registers.length; i++) {
             assert registerBlocked[i] == 0 : "register map must be empty before and after processing";
         }
-        assert !multipleReadsAllowed : "must have default value";
+        checkMultipleReads();
         return true;
     }
 
+    protected void checkMultipleReads() {
+        assert !areMultipleReadsAllowed() : "must have default value";
+    }
+
     private boolean verifyBeforeResolve() {
         assert mappingFrom.size() == mappingFromOpr.size() : "length must be equal";
         assert mappingFrom.size() == mappingTo.size() : "length must be equal";
@@ -99,7 +112,7 @@
 
         int i;
         int j;
-        if (!multipleReadsAllowed) {
+        if (!areMultipleReadsAllowed()) {
             for (i = 0; i < mappingFrom.size(); i++) {
                 for (j = i + 1; j < mappingFrom.size(); j++) {
                     assert mappingFrom.get(i) == null || mappingFrom.get(i) != mappingFrom.get(j) : "cannot read from same interval twice";
@@ -114,7 +127,7 @@
         }
 
         HashSet<Value> usedRegs = new HashSet<>();
-        if (!multipleReadsAllowed) {
+        if (!areMultipleReadsAllowed()) {
             for (i = 0; i < mappingFrom.size(); i++) {
                 Interval interval = mappingFrom.get(i);
                 if (interval != null && !isIllegal(interval.location())) {
@@ -136,37 +149,43 @@
             assert unique : "cannot write to same register twice";
         }
 
-        usedRegs.clear();
-        for (i = 0; i < mappingFrom.size(); i++) {
+        verifyStackSlotMapping();
+
+        return true;
+    }
+
+    protected void verifyStackSlotMapping() {
+        HashSet<Value> usedRegs = new HashSet<>();
+        for (int i = 0; i < mappingFrom.size(); i++) {
             Interval interval = mappingFrom.get(i);
             if (interval != null && !isRegister(interval.location())) {
                 usedRegs.add(interval.location());
             }
         }
-        for (i = 0; i < mappingTo.size(); i++) {
+        for (int i = 0; i < mappingTo.size(); i++) {
             Interval interval = mappingTo.get(i);
             assert !usedRegs.contains(interval.location()) || interval.location().equals(mappingFrom.get(i).location()) : "stack slots used in mappingFrom must be disjoint to mappingTo";
         }
-
-        return true;
     }
 
     // mark assignedReg and assignedRegHi of the interval as blocked
     private void blockRegisters(Interval interval) {
         Value location = interval.location();
-        if (isRegister(location)) {
-            assert multipleReadsAllowed || valueBlocked(location) == 0 : "register already marked as used";
+        if (mightBeBlocked(location)) {
+            assert areMultipleReadsAllowed() || valueBlocked(location) == 0 : "location already marked as used: " + location;
             int direction = 1;
             setValueBlocked(location, direction);
+            Debug.log("block %s", location);
         }
     }
 
     // mark assignedReg and assignedRegHi of the interval as unblocked
     private void unblockRegisters(Interval interval) {
         Value location = interval.location();
-        if (isRegister(location)) {
-            assert valueBlocked(location) > 0 : "register already marked as unused";
+        if (mightBeBlocked(location)) {
+            assert valueBlocked(location) > 0 : "location already marked as unused: " + location;
             setValueBlocked(location, -1);
+            Debug.log("unblock %s", location);
         }
     }
 
@@ -177,9 +196,9 @@
     private boolean safeToProcessMove(Interval from, Interval to) {
         Value fromReg = from != null ? from.location() : null;
 
-        Value reg = to.location();
-        if (isRegister(reg)) {
-            if (valueBlocked(reg) > 1 || (valueBlocked(reg) == 1 && !reg.equals(fromReg))) {
+        Value location = to.location();
+        if (mightBeBlocked(location)) {
+            if ((valueBlocked(location) > 1 || (valueBlocked(location) == 1 && !location.equals(fromReg)))) {
                 return false;
             }
         }
@@ -187,6 +206,10 @@
         return true;
     }
 
+    protected boolean mightBeBlocked(Value location) {
+        return isRegister(location);
+    }
+
     private void createInsertionBuffer(List<LIRInstruction> list) {
         assert !insertionBuffer.initialized() : "overwriting existing buffer";
         insertionBuffer.init(list);
@@ -206,22 +229,30 @@
         assert fromInterval.kind().equals(toInterval.kind()) : "move between different types";
         assert insertIdx != -1 : "must setup insert position first";
 
-        AllocatableValue fromOpr = fromInterval.operand;
-        AllocatableValue toOpr = toInterval.operand;
-
-        insertionBuffer.append(insertIdx, allocator.getSpillMoveFactory().createMove(toOpr, fromOpr));
+        insertionBuffer.append(insertIdx, createMove(fromInterval.operand, toInterval.operand, fromInterval.location(), toInterval.location()));
 
         if (Debug.isLogEnabled()) {
             Debug.log("insert move from %s to %s at %d", fromInterval, toInterval, insertIdx);
         }
     }
 
+    /**
+     * @param fromOpr {@link Interval#operand operand} of the {@code from} interval
+     * @param toOpr {@link Interval#operand operand} of the {@code to} interval
+     * @param fromLocation {@link Interval#location() location} of the {@code to} interval
+     * @param toLocation {@link Interval#location() location} of the {@code to} interval
+     */
+    protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) {
+        return getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr);
+    }
+
     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 insertIdx != -1 : "must setup insert position first";
 
         AllocatableValue toOpr = toInterval.operand;
-        insertionBuffer.append(insertIdx, allocator.getSpillMoveFactory().createMove(toOpr, fromOpr));
+        LIRInstruction move = getAllocator().getSpillMoveFactory().createMove(toOpr, fromOpr);
+        insertionBuffer.append(insertIdx, move);
 
         if (Debug.isLogEnabled()) {
             Debug.log("insert move from value %s to %s at %d", fromOpr, toInterval, insertIdx);
@@ -229,80 +260,86 @@
     }
 
     private void resolveMappings() {
-        assert verifyBeforeResolve();
+        try (Indent indent = Debug.logAndIndent("resolveMapping")) {
+            assert verifyBeforeResolve();
+            if (Debug.isLogEnabled()) {
+                printMapping();
+            }
 
-        // Block all registers that are used as input operands of a move.
-        // When a register is blocked, no move to this register is emitted.
-        // This is necessary for detecting cycles in moves.
-        int i;
-        for (i = mappingFrom.size() - 1; i >= 0; i--) {
-            Interval fromInterval = mappingFrom.get(i);
-            if (fromInterval != null) {
-                blockRegisters(fromInterval);
-            }
-        }
-
-        int spillCandidate = -1;
-        while (mappingFrom.size() > 0) {
-            boolean processedInterval = false;
-
+            // Block all registers that are used as input operands of a move.
+            // When a register is blocked, no move to this register is emitted.
+            // This is necessary for detecting cycles in moves.
+            int i;
             for (i = mappingFrom.size() - 1; i >= 0; i--) {
                 Interval fromInterval = mappingFrom.get(i);
-                Interval toInterval = mappingTo.get(i);
-
-                if (safeToProcessMove(fromInterval, toInterval)) {
-                    // this interval can be processed because target is free
-                    if (fromInterval != null) {
-                        insertMove(fromInterval, toInterval);
-                        unblockRegisters(fromInterval);
-                    } else {
-                        insertMove(mappingFromOpr.get(i), toInterval);
-                    }
-                    mappingFrom.remove(i);
-                    mappingFromOpr.remove(i);
-                    mappingTo.remove(i);
-
-                    processedInterval = true;
-                } else if (fromInterval != null && isRegister(fromInterval.location())) {
-                    // this interval cannot be processed now because target is not free
-                    // it starts in a register, so it is a possible candidate for spilling
-                    spillCandidate = i;
+                if (fromInterval != null) {
+                    blockRegisters(fromInterval);
                 }
             }
 
-            if (!processedInterval) {
-                // no move could be processed because there is a cycle in the move list
-                // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory
-                assert spillCandidate != -1 : "no interval in register for spilling found";
+            int spillCandidate = -1;
+            while (mappingFrom.size() > 0) {
+                boolean processedInterval = false;
 
-                // create a new spill interval and assign a stack slot to it
-                Interval fromInterval = mappingFrom.get(spillCandidate);
-                Interval spillInterval = allocator.createDerivedInterval(fromInterval);
-                spillInterval.setKind(fromInterval.kind());
+                for (i = mappingFrom.size() - 1; i >= 0; i--) {
+                    Interval fromInterval = mappingFrom.get(i);
+                    Interval toInterval = mappingTo.get(i);
 
-                // add a dummy range because real position is difficult to calculate
-                // Note: this range is a special case when the integrity of the allocation is
-                // checked
-                spillInterval.addRange(1, 2);
+                    if (safeToProcessMove(fromInterval, toInterval)) {
+                        // this interval can be processed because target is free
+                        if (fromInterval != null) {
+                            insertMove(fromInterval, toInterval);
+                            unblockRegisters(fromInterval);
+                        } else {
+                            insertMove(mappingFromOpr.get(i), toInterval);
+                        }
+                        mappingFrom.remove(i);
+                        mappingFromOpr.remove(i);
+                        mappingTo.remove(i);
 
-                // do not allocate a new spill slot for temporary interval, but
-                // use spill slot assigned to fromInterval. Otherwise moves from
-                // one stack slot to another can happen (not allowed by LIRAssembler
-                StackSlotValue spillSlot = fromInterval.spillSlot();
-                if (spillSlot == null) {
-                    spillSlot = allocator.frameMapBuilder.allocateSpillSlot(spillInterval.kind());
-                    fromInterval.setSpillSlot(spillSlot);
-                }
-                spillInterval.assignLocation(spillSlot);
-
-                if (Debug.isLogEnabled()) {
-                    Debug.log("created new Interval for spilling: %s", spillInterval);
+                        processedInterval = true;
+                    } else if (fromInterval != null && isRegister(fromInterval.location())) {
+                        // this interval cannot be processed now because target is not free
+                        // it starts in a register, so it is a possible candidate for spilling
+                        spillCandidate = i;
+                    }
                 }
 
-                // insert a move from register to stack and update the mapping
-                insertMove(fromInterval, spillInterval);
-                mappingFrom.set(spillCandidate, spillInterval);
-                unblockRegisters(fromInterval);
+                if (!processedInterval) {
+                    // no move could be processed because there is a cycle in the move list
+                    // (e.g. r1 . r2, r2 . r1), so one interval must be spilled to memory
+                    assert spillCandidate != -1 : "no interval in register for spilling found";
+
+                    // create a new spill interval and assign a stack slot to it
+                    Interval fromInterval = mappingFrom.get(spillCandidate);
+                    Interval spillInterval = getAllocator().createDerivedInterval(fromInterval);
+                    spillInterval.setKind(fromInterval.kind());
+
+                    // add a dummy range because real position is difficult to calculate
+                    // Note: this range is a special case when the integrity of the allocation is
+                    // checked
+                    spillInterval.addRange(1, 2);
+
+                    // do not allocate a new spill slot for temporary interval, but
+                    // use spill slot assigned to fromInterval. Otherwise moves from
+                    // one stack slot to another can happen (not allowed by LIRAssembler
+                    StackSlotValue spillSlot = fromInterval.spillSlot();
+                    if (spillSlot == null) {
+                        spillSlot = getAllocator().frameMapBuilder.allocateSpillSlot(spillInterval.kind());
+                        fromInterval.setSpillSlot(spillSlot);
+                    }
+                    spillInterval.assignLocation(spillSlot);
+
+                    if (Debug.isLogEnabled()) {
+                        Debug.log("created new Interval for spilling: %s", spillInterval);
+                    }
+                    blockRegisters(spillInterval);
+
+                    // insert a move from register to stack and update the mapping
+                    insertMove(fromInterval, spillInterval);
+                    mappingFrom.set(spillCandidate, spillInterval);
+                    unblockRegisters(fromInterval);
+                }
             }
         }
 
@@ -313,6 +350,23 @@
         assert checkEmpty();
     }
 
+    private void printMapping() {
+        try (Indent indent = Debug.logAndIndent("Mapping")) {
+            for (int i = mappingFrom.size() - 1; i >= 0; i--) {
+                Interval fromInterval = mappingFrom.get(i);
+                Interval toInterval = mappingTo.get(i);
+                Value from;
+                Value to = toInterval.location();
+                if (fromInterval == null) {
+                    from = mappingFromOpr.get(i);
+                } else {
+                    from = fromInterval.location();
+                }
+                Debug.log("move %s <- %s", from, to);
+            }
+        }
+    }
+
     void setInsertPosition(List<LIRInstruction> insertList, int insertIdx) {
         assert this.insertIdx == -1 : "use moveInsertPosition instead of setInsertPosition when data already set";
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/alloc/lsra/SSALinearScan.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,200 @@
+/*
+ * 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 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);
+    }
+
+    @Override
+    protected MoveResolver createMoveResolver() {
+        SSAMoveResolver moveResolver = new SSAMoveResolver(this);
+        assert moveResolver.checkEmpty();
+        return moveResolver;
+    }
+
+    @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);
+                }
+            }
+        }
+    }
+
+    @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;
+    }
+
+    @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);
+                }
+            });
+        }
+    }
+
+    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/SSAMoveResolver.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,121 @@
+/*
+ * 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.lir.*;
+import com.oracle.graal.lir.framemap.*;
+
+final class SSAMoveResolver extends MoveResolver {
+
+    private int[] stackBlocked;
+
+    SSAMoveResolver(LinearScan allocator) {
+        super(allocator);
+        this.stackBlocked = new int[((FrameMapBuilderTool) allocator.frameMapBuilder).getNumberOfStackSlots()];
+    }
+
+    @Override
+    boolean checkEmpty() {
+        for (int i = 0; i < stackBlocked.length; i++) {
+            assert stackBlocked[i] == 0 : "stack map must be empty before and after processing";
+        }
+        return super.checkEmpty();
+    }
+
+    @Override
+    protected void checkMultipleReads() {
+        // multiple reads are allowed in SSA LSRA
+    }
+
+    @Override
+    protected void verifyStackSlotMapping() {
+        // relax disjoint stack maps invariant
+    }
+
+    @Override
+    protected boolean areMultipleReadsAllowed() {
+        return true;
+    }
+
+    @Override
+    protected boolean mightBeBlocked(Value location) {
+        if (super.mightBeBlocked(location)) {
+            return true;
+        }
+        if (isStackSlotValue(location)) {
+            return true;
+        }
+        return false;
+    }
+
+    @Override
+    protected void setValueBlocked(Value location, int direction) {
+        assert direction == 1 || direction == -1 : "out of bounds";
+        if (isVirtualStackSlot(location)) {
+            assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!";
+            int stack = asVirtualStackSlot(location).getId();
+            if (stack >= stackBlocked.length) {
+                stackBlocked = Arrays.copyOf(stackBlocked, stack + 1);
+            }
+            stackBlocked[stack] += direction;
+        } else if (isStackSlot(location)) {
+            assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!";
+            assert asStackSlot(location).isInCallerFrame() : "Unexpected stack slot: " + location;
+            // incoming stack arguments can be ignored
+        } else {
+            super.setValueBlocked(location, direction);
+        }
+    }
+
+    @Override
+    protected int valueBlocked(Value location) {
+        if (isVirtualStackSlot(location)) {
+            assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!";
+            int stack = asVirtualStackSlot(location).getId();
+            if (stack >= stackBlocked.length) {
+                return 0;
+            }
+            return stackBlocked[stack];
+        }
+        if (isStackSlot(location)) {
+            assert LinearScanPhase.SSA_LSRA.getValue() : "should only happen if SSA LSRA is used!";
+            assert asStackSlot(location).isInCallerFrame() : "Unexpected stack slot: " + location;
+            // incoming stack arguments are always blocked (aka they can not be written)
+            return 1;
+        }
+        return super.valueBlocked(location);
+    }
+
+    @Override
+    protected LIRInstruction createMove(AllocatableValue fromOpr, AllocatableValue toOpr, AllocatableValue fromLocation, AllocatableValue toLocation) {
+        if (isStackSlotValue(toLocation) && isStackSlotValue(fromLocation)) {
+            return getAllocator().getSpillMoveFactory().createStackMove(toOpr, fromOpr);
+        }
+        return super.createMove(fromOpr, toOpr, fromLocation, toLocation);
+    }
+}
--- a/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationStage.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/phases/PreAllocationOptimizationStage.java	Wed May 06 12:14:30 2015 -0700
@@ -22,11 +22,17 @@
  */
 package com.oracle.graal.lir.phases;
 
+import static com.oracle.graal.compiler.common.GraalOptions.*;
+
 import com.oracle.graal.lir.constopt.*;
-import com.oracle.graal.lir.phases.PreAllocationOptimizationPhase.*;
+import com.oracle.graal.lir.phases.PreAllocationOptimizationPhase.PreAllocationOptimizationContext;
+import com.oracle.graal.lir.ssa.*;
 
 public class PreAllocationOptimizationStage extends LIRPhaseSuite<PreAllocationOptimizationContext> {
     public PreAllocationOptimizationStage() {
+        if (SSA_LIR.getValue() && SSADestructionPhase.Options.LIREagerSSADestruction.getValue()) {
+            appendPhase(new SSADestructionPhase());
+        }
         if (ConstantLoadOptimization.Options.LIROptConstantLoadOptimization.getValue()) {
             appendPhase(new ConstantLoadOptimization());
         }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSADestructionPhase.java	Wed May 06 12:14:30 2015 -0700
@@ -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.ssa;
+
+import java.util.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.gen.*;
+import com.oracle.graal.lir.phases.*;
+import com.oracle.graal.options.*;
+
+public final class SSADestructionPhase extends PreAllocationOptimizationPhase {
+
+    public static class Options {
+        // @formatter:off
+        @Option(help = "Destruct SSA LIR eagerly (before other LIR phases).", type = OptionType.Debug)
+        public static final OptionValue<Boolean> LIREagerSSADestruction = new OptionValue<>(false);
+        // @formatter:on
+    }
+
+    @Override
+    protected <B extends AbstractBlockBase<B>> void run(TargetDescription target, LIRGenerationResult lirGenRes, List<B> codeEmittingOrder, List<B> linearScanOrder, LIRGeneratorTool lirGen) {
+        LIR lir = lirGenRes.getLIR();
+        AbstractControlFlowGraph<?> cfg = lir.getControlFlowGraph();
+        for (AbstractBlockBase<?> block : cfg.getBlocks()) {
+            doBlock(block, lir, lirGen);
+        }
+    }
+
+    private static void doBlock(AbstractBlockBase<?> block, LIR lir, LIRGeneratorTool lirGen) {
+        if (block.getPredecessorCount() > 1) {
+            for (AbstractBlockBase<?> pred : block.getPredecessors()) {
+
+                List<LIRInstruction> instructions = lir.getLIRforBlock(pred);
+
+                int insertBefore = SSAUtils.phiOutIndex(lir, pred);
+
+                PhiResolver resolver = PhiResolver.create(lirGen, new LIRInsertionBuffer(), instructions, insertBefore);
+                SSAUtils.forEachPhiValuePair(lir, block, pred, resolver::move);
+                resolver.dispose();
+
+                SSAUtils.removePhiOut(lir, pred);
+            }
+            SSAUtils.removePhiIn(lir, block);
+        }
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAUtils.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,175 @@
+/*
+ * 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.ssa;
+
+import java.util.*;
+
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.common.cfg.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
+import com.oracle.graal.lir.StandardOp.BlockEndOp;
+import com.oracle.graal.lir.StandardOp.JumpOp;
+import com.oracle.graal.lir.StandardOp.LabelOp;
+
+/**
+ * Utilities for working with Static-Single-Assignment LIR form.
+ *
+ * <h2>Representation of <code>PHI</code>s</h2>
+ *
+ * There is no explicit <code>PHI</code> {@linkplain LIRInstruction}. Instead, they are implemented
+ * as parallel copy that span across a control-flow edge.
+ *
+ * The variables introduced by <code>PHI</code>s of a specific {@linkplain AbstractBlockBase merge
+ * block} are {@linkplain LabelOp#setIncomingValues attached} to the {@linkplain LabelOp} of the
+ * block. The outgoing values from the predecessor are {@link JumpOp#setOutgoingValues input} to the
+ * {@linkplain BlockEndOp} of the predecessor. Because there are no critical edges we know that the
+ * {@link BlockEndOp} of the predecessor has to be a {@link JumpOp}.
+ *
+ * <h3>Example:</h3>
+ *
+ * <pre>
+ * B0 -> B1
+ *   ...
+ *   v0|i = ...
+ *   JUMP ~[v0|i, int[0|0x0]] destination: B0 -> B1
+ * ________________________________________________
+ * 
+ * B2 -> B1
+ *   ...
+ *   v1|i = ...
+ *   v2|i = ...
+ *   JUMP ~[v1|i, v2|i] destination: B2 -> B1
+ * ________________________________________________
+ * 
+ * B1 <- B0,B2
+ *   [v3|i, v4|i] = LABEL
+ *   ...
+ * </pre>
+ */
+public final class SSAUtils {
+
+    public interface PhiValueVisitor {
+        /**
+         * @param phiIn the incoming value at the merge block
+         * @param phiOut the outgoing value from the predecessor block
+         */
+        void visit(Value phiIn, Value phiOut);
+    }
+
+    /**
+     * Visits each phi value pair of an edge, i.e. the outgoing value from the predecessor and the
+     * incoming value to the merge block.
+     */
+    public static void forEachPhiValuePair(LIR lir, AbstractBlockBase<?> merge, AbstractBlockBase<?> pred, PhiValueVisitor visitor) {
+        if (merge.getPredecessorCount() < 2) {
+            return;
+        }
+        assert merge.getPredecessors().contains(pred) : String.format("%s not in predecessor list: %s", pred, merge.getPredecessors());
+        assert pred.getSuccessorCount() == 1 : String.format("Merge predecessor block %s has more than one successor? %s", pred, pred.getSuccessors());
+        assert pred.getSuccessors().get(0) == merge : String.format("Predecessor block %s has wrong successor: %s, should be: %s", pred, pred.getSuccessors().get(0), merge);
+
+        JumpOp jump = phiOut(lir, pred);
+        LabelOp label = phiIn(lir, merge);
+
+        assert label.getIncomingSize() == jump.getOutgoingSize() : String.format("Phi In/Out size mismatch: in=%d vs. out=%d", label.getIncomingSize(), jump.getOutgoingSize());
+
+        for (int i = 0; i < label.getIncomingSize(); i++) {
+            visitor.visit(label.getIncomingValue(i), jump.getOutgoingValue(i));
+        }
+    }
+
+    private static JumpOp phiOut(LIR lir, AbstractBlockBase<?> block) {
+        assert block.getSuccessorCount() == 1;
+        List<LIRInstruction> instructions = lir.getLIRforBlock(block);
+        int index = instructions.size() - 1;
+        LIRInstruction op = instructions.get(index);
+        return (JumpOp) op;
+    }
+
+    public static int phiOutIndex(LIR lir, AbstractBlockBase<?> block) {
+        assert block.getSuccessorCount() == 1;
+        List<LIRInstruction> instructions = lir.getLIRforBlock(block);
+        int index = instructions.size() - 1;
+        assert instructions.get(index) instanceof JumpOp;
+        return index;
+    }
+
+    private static LabelOp phiIn(LIR lir, AbstractBlockBase<?> block) {
+        assert block.getPredecessorCount() > 1;
+        LabelOp label = (LabelOp) lir.getLIRforBlock(block).get(0);
+        return label;
+    }
+
+    public static void removePhiOut(LIR lir, AbstractBlockBase<?> block) {
+        JumpOp jump = phiOut(lir, block);
+        jump.clearOutgoingValues();
+    }
+
+    public static void removePhiIn(LIR lir, AbstractBlockBase<?> block) {
+        LabelOp label = phiIn(lir, block);
+        label.clearIncomingValues();
+    }
+
+    public static boolean verifySSAForm(LIR lir) {
+        return new SSAVerifier(lir).verify();
+    }
+
+    public static void verifyPhi(LIR lir, AbstractBlockBase<?> merge) {
+        assert merge.getPredecessorCount() > 1;
+        for (AbstractBlockBase<?> pred : merge.getPredecessors()) {
+            forEachPhiValuePair(lir, merge, pred, (phiIn, phiOut) -> {
+                assert phiIn.getLIRKind().equals(phiOut.getLIRKind()) ||
+                                (phiIn.getPlatformKind().equals(phiOut.getPlatformKind()) && phiIn.getLIRKind().isDerivedReference() && phiOut.getLIRKind().isValue());
+            });
+        }
+    }
+
+    public static void forEachPhiRegisterHint(LIR lir, AbstractBlockBase<?> block, LabelOp label, Value targetValue, OperandMode mode, ValueConsumer valueConsumer) {
+        assert mode == OperandMode.DEF : "Wrong operand mode: " + mode;
+        assert lir.getLIRforBlock(block).get(0).equals(label) : String.format("Block %s and Label %s do not match!", block, label);
+
+        if (!label.isPhiIn()) {
+            return;
+        }
+        int idx = indexOfValue(label, targetValue);
+        assert idx >= 0 : String.format("Value %s not in label %s", targetValue, label);
+
+        for (AbstractBlockBase<?> pred : block.getPredecessors()) {
+            JumpOp jump = phiOut(lir, pred);
+            Value sourceValue = jump.getOutgoingValue(idx);
+            valueConsumer.visitValue(jump, sourceValue, null, null);
+        }
+
+    }
+
+    private static int indexOfValue(LabelOp label, Value value) {
+        for (int i = 0; i < label.getIncomingSize(); i++) {
+            if (label.getIncomingValue(i).equals(value)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.lir/src/com/oracle/graal/lir/ssa/SSAVerifier.java	Wed May 06 12:14:30 2015 -0700
@@ -0,0 +1,131 @@
+/*
+ * 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.ssa;
+
+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.debug.Debug.Scope;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.lir.LIRInstruction.OperandFlag;
+import com.oracle.graal.lir.LIRInstruction.OperandMode;
+
+final class SSAVerifier {
+    private static class Entry {
+        private final LIRInstruction inst;
+        private final AbstractBlockBase<?> block;
+
+        Entry(LIRInstruction inst, AbstractBlockBase<?> block) {
+            this.inst = inst;
+            this.block = block;
+        }
+    }
+
+    private final LIR lir;
+    private final BitSet visited;
+    private final HashMap<Value, Entry> defined;
+    private AbstractBlockBase<?> currentBlock;
+
+    SSAVerifier(LIR lir) {
+        this.lir = lir;
+        this.visited = new BitSet(lir.getControlFlowGraph().getBlocks().size());
+        this.defined = new HashMap<>();
+    }
+
+    public boolean verify() {
+        try (Scope s = Debug.scope("SSAVerifier", lir)) {
+            for (AbstractBlockBase<?> block : lir.getControlFlowGraph().getBlocks()) {
+                doBlock(block);
+            }
+        } catch (Throwable e) {
+            throw Debug.handle(e);
+        }
+        return true;
+    }
+
+    private void doBlock(AbstractBlockBase<?> b) {
+        if (visited.get(b.getId())) {
+            return;
+        }
+        for (AbstractBlockBase<?> pred : b.getPredecessors()) {
+            if (!b.isLoopHeader() || !pred.isLoopEnd()) {
+                doBlock(pred);
+            }
+        }
+        try (Indent indent = Debug.logAndIndent("handle block %s", b)) {
+            assert verifyBlock(b);
+        }
+    }
+
+    private boolean verifyBlock(AbstractBlockBase<?> block) {
+        currentBlock = block;
+        assert !visited.get(block.getId()) : "Block already visited: " + block;
+        visited.set(block.getId());
+        for (LIRInstruction op : lir.getLIRforBlock(block)) {
+            op.visitEachAlive(this::useConsumer);
+            op.visitEachState(this::useConsumer);
+            op.visitEachInput(this::useConsumer);
+
+            op.visitEachTemp(this::defConsumer);
+            op.visitEachOutput(this::defConsumer);
+
+        }
+        currentBlock = null;
+        return true;
+    }
+
+    /**
+     * @see InstructionValueConsumer
+     * @param mode
+     * @param flags
+     */
+    private void useConsumer(LIRInstruction inst, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
+        if (shouldProcess(value)) {
+            assert defined.keySet().contains(value) || flags.contains(OperandFlag.UNINITIALIZED) : String.format("Value %s used at instruction %s in block %s but never defined", value, inst,
+                            currentBlock);
+        }
+    }
+
+    /**
+     * @see InstructionValueConsumer
+     * @param mode
+     * @param flags
+     */
+    private void defConsumer(LIRInstruction inst, Value value, OperandMode mode, EnumSet<OperandFlag> flags) {
+        if (shouldProcess(value)) {
+            assert !defined.keySet().contains(value) : String.format("Value %s redefined at %s but never defined (previous definition %s in block %s)", value, inst, defined.get(value).inst,
+                            defined.get(value).block);
+            defined.put(value, new Entry(inst, currentBlock));
+        }
+    }
+
+    private static boolean shouldProcess(Value value) {
+        return !value.equals(Value.ILLEGAL) && !isConstant(value) && !isRegister(value) && !isStackSlotValue(value);
+    }
+
+}
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractMergeNode.java	Wed May 06 11:44:29 2015 -0700
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/AbstractMergeNode.java	Wed May 06 12:14:30 2015 -0700
@@ -133,6 +133,10 @@
         return this.usages().filter(PhiNode.class).filter(this::isPhiAtMerge);
     }
 
+    public NodeIterable<ValuePhiNode> valuePhis() {
+        return this.usages().filter(ValuePhiNode.class).filter(this::isPhiAtMerge);
+    }
+
     @Override
     public NodeIterable<Node> anchored() {
         return super.anchored().filter(n -> !isPhiAtMerge(n));
--- a/mx/suite.py	Wed May 06 11:44:29 2015 -0700
+++ b/mx/suite.py	Wed May 06 12:14:30 2015 -0700
@@ -548,6 +548,17 @@
       "workingSets" : "Graal,LIR",
     },
 
+    "com.oracle.graal.lir.jtt" : {
+      "subDir" : "graal",
+      "sourceDirs" : ["src"],
+      "dependencies" : [
+        "com.oracle.graal.jtt",
+      ],
+      "checkstyle" : "com.oracle.graal.graph",
+      "javaCompliance" : "1.8",
+      "workingSets" : "Graal,LIR",
+    },
+
     "com.oracle.graal.lir.test" : {
       "subDir" : "graal",
       "sourceDirs" : ["src"],
--- a/src/cpu/sparc/vm/graalCodeInstaller_sparc.cpp	Wed May 06 11:44:29 2015 -0700
+++ b/src/cpu/sparc/vm/graalCodeInstaller_sparc.cpp	Wed May 06 12:14:30 2015 -0700
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2013, 2014, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2013, 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
@@ -45,7 +45,13 @@
   Handle obj = HotSpotObjectConstantImpl::object(constant);
   jobject value = JNIHandles::make_local(obj());
   if (HotSpotObjectConstantImpl::compressed(constant)) {
-    fatal("unimplemented: narrow oop relocation");
+#ifdef _LP64
+    int oop_index = _oop_recorder->find_index(value);
+    RelocationHolder rspec = oop_Relocation::spec(oop_index);
+    _instructions->relocate(pc, rspec, 1);
+#else
+    fatal("compressed oop on 32bit");
+#endif
   } else {
     NativeMovConstReg* move = nativeMovConstReg_at(pc);
     move->set_data((intptr_t) value);
--- a/src/share/vm/runtime/arguments.cpp	Wed May 06 11:44:29 2015 -0700
+++ b/src/share/vm/runtime/arguments.cpp	Wed May 06 12:14:30 2015 -0700
@@ -1502,9 +1502,9 @@
   // to use UseCompressedOops is InitialHeapSize.
   size_t max_heap_size = MAX2(MaxHeapSize, InitialHeapSize);
   // Set default on graal with sparc to not use compressed oops as long they are not implemented
-#if defined(GRAAL) && defined(TARGET_ARCH_sparc)
+/*#if defined(GRAAL) && defined(TARGET_ARCH_sparc)
   FLAG_SET_DEFAULT(UseCompressedOops, false);
-#else // if !(GRAAL && SOLARIS)
+#else // if !(GRAAL && SOLARIS)*/
   if (max_heap_size <= max_heap_for_compressed_oops()) {
 #if !defined(COMPILER1) || defined(TIERED)
     if (FLAG_IS_DEFAULT(UseCompressedOops)) {
@@ -1529,7 +1529,7 @@
   }
 #endif // _LP64
 #endif // ZERO
-#endif // !(GRAAL && SOLARIS)
+//#endif // !(GRAAL && SOLARIS)
 }