changeset 5361:dec5a35ddbe2

lowering checkcasts with Java snippets (incomplete)
author Doug Simon <doug.simon@oracle.com>
date Mon, 07 May 2012 11:40:44 +0200
parents 6cc970203f30
children d42425beb1d1
files graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/LIRGenLowerable.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/TypeCheckSlowPath.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/LowerCheckCastPhase.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/AMD64TypeCheckSlowPathOp.java graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java graal/com.oracle.graal.printer/src/com/oracle/graal/printer/IdealGraphPrinterDumpHandler.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippets.java graal/com.oracle.graal.tests/src/com/oracle/graal/compiler/tests/LowerCheckCastTest.java
diffstat 14 files changed, 574 insertions(+), 18 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/LIRGenLowerable.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/target/LIRGenLowerable.java	Mon May 07 11:40:44 2012 +0200
@@ -23,7 +23,13 @@
 package com.oracle.graal.compiler.target;
 
 import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.lir.*;
+import com.oracle.graal.nodes.spi.*;
 
+/**
+ * An alternative to {@link LIRLowerable} for lowering that is tightly coupled
+ * to {@link LIRGenerator} and {@link LIRInstruction}.
+ */
 public interface LIRGenLowerable {
 
     void generate(LIRGenerator generator);
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/util/InliningUtil.java	Mon May 07 11:40:44 2012 +0200
@@ -27,10 +27,12 @@
 import java.util.concurrent.*;
 
 import com.oracle.graal.compiler.*;
+import com.oracle.graal.compiler.loop.*;
 import com.oracle.graal.compiler.phases.*;
 import com.oracle.graal.cri.*;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
+import com.oracle.graal.lir.cfg.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.PhiNode.PhiType;
 import com.oracle.graal.nodes.calc.*;
@@ -766,10 +768,10 @@
 
     /**
      * Performs an actual inlining, thereby replacing the given invoke with the given inlineGraph.
+     *
      * @param invoke the invoke that will be replaced
      * @param inlineGraph the graph that the invoke will be replaced with
      * @param receiverNullCheck true if a null check needs to be generated for non-static inlinings, false if no such check is required
-     * @return The node that represents the return value, or null for void methods and methods that have no non-exceptional exit.
      */
     public static void inline(Invoke invoke, StructuredGraph inlineGraph, boolean receiverNullCheck) {
         NodeInputList<ValueNode> parameters = invoke.callTarget().arguments();
@@ -903,6 +905,108 @@
         }
     }
 
+    /**
+     * Performs replacement of a node with a snippet graph.
+     *
+     * @param replacee the node that will be replaced
+     * @param anchor if non-null, then this fixed node is control flow replacee. This is required iff replacee is not a fixed node.
+     * @param snippetGraph the graph that the replacee will be replaced with
+     * @param explodeLoops specifies if all the loops in the snippet graph are counted loops that must be completely unrolled
+     * @param args
+     */
+    public static void snippetInline(RiRuntime runtime, Node replacee, FixedNode anchor, StructuredGraph snippetGraph, boolean explodeLoops, Object... args) {
+        // Copy snippet graph, replacing parameters with given args in the process
+        StructuredGraph snippetCopy = new StructuredGraph(snippetGraph.name, snippetGraph.method());
+        IdentityHashMap<Node, Node> replacements = new IdentityHashMap<>();
+        replacements.put(snippetGraph.start(), snippetCopy.start());
+        int localCount = 0;
+        for (LocalNode local : snippetGraph.getNodes(LocalNode.class)) {
+            int index = local.index();
+            if (args[index] instanceof CiConstant) {
+                CiConstant arg = (CiConstant) args[index];
+                assert arg.kind.stackKind() == local.kind() : arg.kind + " != " + local.kind();
+                ConstantNode argNode = ConstantNode.forCiConstant(arg, runtime, snippetCopy);
+                replacements.put(local, argNode);
+                args[index] = null;
+            } else {
+                assert args[index] instanceof ValueNode;
+            }
+            localCount++;
+        }
+        assert localCount == args.length : "snippet argument count mismatch";
+        snippetCopy.addDuplicates(snippetGraph.getNodes(), replacements);
+
+        // Explode all loops in the snippet if requested
+        if (explodeLoops && snippetCopy.hasLoops()) {
+            ControlFlowGraph cfg = ControlFlowGraph.compute(snippetCopy, true, true, false, false);
+            for (Loop loop : cfg.getLoops()) {
+                LoopBeginNode loopBegin = loop.loopBegin();
+                SuperBlock wholeLoop = LoopTransformUtil.wholeLoop(loop);
+                while (!loopBegin.isDeleted()) {
+                    snippetCopy.mark();
+                    LoopTransformUtil.peel(loop, wholeLoop);
+                    new CanonicalizerPhase(null, runtime, null, true, null).apply(snippetCopy);
+                }
+            }
+        }
+
+        // Inline snippet
+        ArrayList<Node> nodes = new ArrayList<>();
+        ReturnNode returnNode = null;
+        BeginNode entryPointNode = snippetCopy.start();
+        FixedNode firstCFGNode = entryPointNode.next();
+        replacements.clear();
+        for (Node node : snippetCopy.getNodes()) {
+            if (node == entryPointNode || node == entryPointNode.stateAfter()) {
+                // Do nothing.
+            } else if (node instanceof LocalNode) {
+                LocalNode local = (LocalNode) node;
+                int index = local.index();
+                assert args[index] instanceof ValueNode;
+                ValueNode arg = (ValueNode) args[index];
+                assert arg.kind() == local.kind();
+                replacements.put(node, arg);
+                args[index] = null;
+            } else {
+                nodes.add(node);
+                if (node instanceof ReturnNode) {
+                    returnNode = (ReturnNode) node;
+                }
+            }
+        }
+
+        StructuredGraph graph = (StructuredGraph) replacee.graph();
+        Map<Node, Node> duplicates = graph.addDuplicates(nodes, replacements);
+        FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
+        if (anchor != null) {
+            assert !(replacee instanceof FixedNode) : "anchor not required when replacing fixed node " + replacee;
+            anchor.replaceAtPredecessors(firstCFGNodeDuplicate);
+        } else {
+            assert replacee.successors().first() != null;
+            assert replacee.predecessor() != null;
+            replacee.replaceAtPredecessors(firstCFGNodeDuplicate);
+        }
+
+        Node returnValue = null;
+        if (returnNode != null) {
+            if (returnNode.result() instanceof LocalNode) {
+                returnValue = replacements.get(returnNode.result());
+            } else {
+                returnValue = duplicates.get(returnNode.result());
+            }
+            assert returnValue != null || replacee.usages().isEmpty();
+            replacee.replaceAtUsages(returnValue);
+        }
+
+        replacee.clearInputs();
+        replacee.replaceAtUsages(null);
+        if (replacee instanceof FixedNode) {
+            GraphUtil.killCFG((FixedNode) replacee);
+        } else {
+            replacee.safeDelete();
+        }
+    }
+
     public static void receiverNullCheck(Invoke invoke) {
         MethodCallTargetNode callTarget = invoke.callTarget();
         StructuredGraph graph = (StructuredGraph) invoke.graph();
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/Graph.java	Mon May 07 11:40:44 2012 +0200
@@ -34,7 +34,7 @@
  */
 public class Graph {
 
-    protected final String name;
+    public final String name;
 
     private static final boolean TIME_TRAVEL = false;
 
@@ -220,6 +220,9 @@
         return true;
     }
 
+    /**
+     * @see #getNewNodes()
+     */
     public void mark() {
         this.mark = nodeIdCount();
     }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Mon May 07 11:40:44 2012 +0200
@@ -555,6 +555,8 @@
             if (type.isPrimitive()) {
                 if (type == Integer.TYPE) {
                     value = unsafe.getInt(node, dataOffsets[i]);
+                } else if (type == Long.TYPE) {
+                    value = unsafe.getLong(node, dataOffsets[i]);
                 } else if (type == Boolean.TYPE) {
                     value = unsafe.getBoolean(node, dataOffsets[i]);
                 } else if (type == Long.TYPE) {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/bridge/VMToCompilerImpl.java	Mon May 07 11:40:44 2012 +0200
@@ -53,6 +53,7 @@
 
     private final Compiler compiler;
     private IntrinsifyArrayCopyPhase intrinsifyArrayCopy;
+    private LowerCheckCastPhase lowerCheckCastPhase;
 
     public final HotSpotTypePrimitive typeBoolean;
     public final HotSpotTypePrimitive typeChar;
@@ -115,10 +116,12 @@
                 @Override
                 public void run() {
                     VMToCompilerImpl.this.intrinsifyArrayCopy = new IntrinsifyArrayCopyPhase(runtime);
+                    VMToCompilerImpl.this.lowerCheckCastPhase = new LowerCheckCastPhase(runtime);
                     GraalIntrinsics.installIntrinsics(runtime, runtime.getCompiler().getTarget());
                     Snippets.install(runtime, runtime.getCompiler().getTarget(), new SystemSnippets());
                     Snippets.install(runtime, runtime.getCompiler().getTarget(), new UnsafeSnippets());
                     Snippets.install(runtime, runtime.getCompiler().getTarget(), new ArrayCopySnippets());
+                    Snippets.install(runtime, runtime.getCompiler().getTarget(), new CheckCastSnippets());
                 }
             });
 
@@ -157,6 +160,8 @@
         }
     }
 
+
+
     /**
      * This method is the first method compiled during bootstrapping. Put any code in there that warms up compiler paths
      * that are otherwise not exercised during bootstrapping and lead to later deoptimization when application code is
@@ -483,6 +488,7 @@
         if (GraalOptions.Intrinsify) {
             phasePlan.addPhase(PhasePosition.HIGH_LEVEL, intrinsifyArrayCopy);
         }
+        phasePlan.addPhase(PhasePosition.HIGH_LEVEL, lowerCheckCastPhase);
         return phasePlan;
     }
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/nodes/TypeCheckSlowPath.java	Mon May 07 11:40:44 2012 +0200
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * 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.nodes;
+
+import com.oracle.graal.compiler.gen.*;
+import com.oracle.graal.compiler.target.*;
+import com.oracle.graal.hotspot.target.amd64.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.max.cri.ci.*;
+
+/**
+ * A node for calling the HotSpot stub implementing the slow path of a type check.
+ * This stub does not use any registers.
+ */
+public final class TypeCheckSlowPath extends FloatingNode implements LIRGenLowerable {
+
+    @Input private ValueNode objectHub;
+    @Input private ValueNode hub;
+
+    public ValueNode objectHub() {
+        return objectHub;
+    }
+
+    public ValueNode hub() {
+        return hub;
+    }
+
+    public TypeCheckSlowPath(ValueNode objectHub, ValueNode hub) {
+        super(StampFactory.forKind(CiKind.Object));
+        this.objectHub = objectHub;
+        this.hub = hub;
+    }
+
+    @Override
+    public void generate(LIRGenerator gen) {
+        CiValue objectHubOpr = gen.operand(objectHub);
+        AMD64TypeCheckSlowPathOp op = new AMD64TypeCheckSlowPathOp(objectHubOpr, gen.operand(hub));
+        gen.append(op);
+        gen.setResult(this, objectHubOpr);
+    }
+
+    @SuppressWarnings("unused")
+    @NodeIntrinsic
+    public static Object check(Object objectHub, Object hub) {
+        throw new UnsupportedOperationException("This method may only be compiled with the Graal compiler");
+    }
+
+
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java	Mon May 07 11:40:44 2012 +0200
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.snippets;
+import com.oracle.graal.graph.Node.Fold;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.nodes.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.snippets.*;
+import com.oracle.max.cri.ci.*;
+import com.oracle.max.cri.ri.*;
+
+
+public class CheckCastSnippets implements SnippetsInterface {
+
+    @Snippet
+    public static Object checkcast(Object hub, Object object, Object[] hintHubs, boolean hintsAreExact) {
+        if (object == null) {
+            return object;
+        }
+        Object objectHub = UnsafeLoadNode.load(object, 0, hubOffset(), CiKind.Object);
+        // if we get an exact match: succeed immediately
+        for (int i = 0; i < hintHubs.length; i++) {
+            Object hintHub = hintHubs[i];
+            if (hintHub == objectHub) {
+                return object;
+            }
+        }
+        if (!hintsAreExact && TypeCheckSlowPath.check(objectHub, hub) == null) {
+            DeoptimizeNode.deopt(RiDeoptAction.InvalidateReprofile, RiDeoptReason.ClassCastException);
+        }
+        return object;
+    }
+
+    @Fold
+    private static int hubOffset() {
+        return CompilerImpl.getInstance().getConfig().hubOffset;
+    }
+
+    @Fold
+    private static int klassOopOffset() {
+        return CompilerImpl.getInstance().getConfig().klassOopOffset;
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/LowerCheckCastPhase.java	Mon May 07 11:40:44 2012 +0200
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.snippets;
+
+import java.util.*;
+
+import com.oracle.graal.compiler.phases.*;
+import com.oracle.graal.compiler.phases.CanonicalizerPhase.IsImmutablePredicate;
+import com.oracle.graal.compiler.util.*;
+import com.oracle.graal.cri.*;
+import com.oracle.graal.debug.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.ri.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.max.cri.ci.*;
+import com.oracle.max.cri.ri.*;
+
+public class LowerCheckCastPhase extends Phase {
+    private final GraalRuntime runtime;
+    private final RiResolvedMethod checkcast;
+
+    public LowerCheckCastPhase(GraalRuntime runtime) {
+        this.runtime = runtime;
+        try {
+            checkcast = runtime.getRiMethod(CheckCastSnippets.class.getDeclaredMethod("checkcast", Object.class, Object.class, Object[].class, boolean.class));
+        } catch (NoSuchMethodException e) {
+            throw new GraalInternalError(e);
+        }
+    }
+
+    private static HotSpotKlassOop klassOop(RiResolvedType resolvedType) {
+        return ((HotSpotType) resolvedType).klassOop();
+    }
+
+    @Override
+    protected void run(StructuredGraph graph) {
+        // TODO (dnsimon) remove this once lowering works in general
+        if (graph.method() == null || !graph.method().holder().name().contains("LowerCheckCastTest")) {
+            return;
+        }
+
+        int hits = 0;
+        graph.mark();
+        IsImmutablePredicate immutabilityPredicate = null;
+        for (CheckCastNode ccn : graph.getNodes(CheckCastNode.class)) {
+            ValueNode hub = ccn.targetClassInstruction();
+            ValueNode object = ccn.object();
+            RiResolvedType[] hints = ccn.hints();
+            StructuredGraph snippetGraph = (StructuredGraph) checkcast.compilerStorage().get(Graph.class);
+            hits++;
+            HotSpotKlassOop[] hintHubs = new HotSpotKlassOop[hints.length];
+            for (int i = 0; i < hintHubs.length; i++) {
+                hintHubs[i] = klassOop(hints[i]);
+            }
+            final CiConstant hintHubsConst = CiConstant.forObject(hintHubs);
+            immutabilityPredicate = new IsImmutablePredicate() {
+                public boolean apply(CiConstant constant) {
+                    return constant == hintHubsConst;
+                }
+            };
+            Debug.log("Lowering checkcast in %s: ccn=%s, hintsHubs=%s, exact=%b", graph, ccn, Arrays.toString(hints), ccn.hintsExact());
+            InliningUtil.snippetInline(runtime, ccn, ccn.anchor(), snippetGraph, true, hub, object, hintHubsConst, CiConstant.forBoolean(ccn.hintsExact()));
+        }
+        if (hits != 0) {
+            Debug.log("Lowered %d checkcasts in %s ", hits, graph);
+            new DeadCodeEliminationPhase().apply(graph);
+            new CanonicalizerPhase(null, runtime, null, false, immutabilityPredicate).apply(graph);
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/target/amd64/AMD64TypeCheckSlowPathOp.java	Mon May 07 11:40:44 2012 +0200
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.hotspot.target.amd64;
+
+import static com.oracle.max.cri.ci.CiValueUtil.*;
+
+import java.util.*;
+
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.lir.amd64.*;
+import com.oracle.graal.lir.asm.*;
+import com.oracle.max.asm.target.amd64.*;
+import com.oracle.max.cri.ci.*;
+
+/**
+ * Performs a call to the {@code slow_subtype_check_id} stub.
+ */
+public class AMD64TypeCheckSlowPathOp extends AMD64LIRInstruction {
+
+    public AMD64TypeCheckSlowPathOp(CiValue objectHub, CiValue hub) {
+        super("TYPECHECK_SLOW", new CiValue[] {objectHub}, null, new CiValue[] {objectHub, hub}, NO_OPERANDS, NO_OPERANDS);
+    }
+
+    @Override
+    public void emitCode(TargetMethodAssembler tasm, AMD64MacroAssembler masm) {
+        CiValue result = output(0);
+        CiValue objectHub = input(0);
+        CiValue hub = input(1);
+
+        masm.push(asRegister(objectHub));
+        masm.push(asRegister(hub));
+        AMD64Call.directCall(tasm, masm, CompilerImpl.getInstance().getConfig().instanceofStub, null);
+
+        // Two pops to balance the two pushes above - the value first popped is discarded
+        masm.pop(asRegister(result));
+        masm.pop(asRegister(result));
+    }
+
+    @Override
+    protected EnumSet<OperandFlag> flagsFor(OperandMode mode, int index) {
+        if (mode == OperandMode.Input) {
+            return EnumSet.of(OperandFlag.Register);
+        } else if (mode == OperandMode.Output) {
+            return EnumSet.of(OperandFlag.Register);
+        }
+        throw GraalInternalError.shouldNotReachHere();
+    }
+}
--- a/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.java/src/com/oracle/graal/java/GraphBuilderPhase.java	Mon May 07 11:40:44 2012 +0200
@@ -639,7 +639,8 @@
             append(anchor);
             CheckCastNode checkCast;
             RiResolvedType[] hints = getTypeCheckHints((RiResolvedType) type, GraalOptions.CheckcastMaxHints);
-            checkCast = currentGraph.unique(new CheckCastNode(anchor, typeInstruction, (RiResolvedType) type, object, hints, Util.isFinalClass((RiResolvedType) type)));
+            boolean hintsExact = Util.isFinalClass((RiResolvedType) type);
+            checkCast = currentGraph.unique(new CheckCastNode(anchor, typeInstruction, (RiResolvedType) type, object, hints, hintsExact));
             append(currentGraph.add(new ValueAnchorNode(checkCast)));
             frameState.apush(checkCast);
         } else {
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/IdealGraphPrinterDumpHandler.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/IdealGraphPrinterDumpHandler.java	Mon May 07 11:40:44 2012 +0200
@@ -160,7 +160,7 @@
     }
 
     private void openScope(String name) {
-        printer.beginGroup(name, name, Debug.contextLookup(RiResolvedMethod.class), -1);
+        printer.beginGroup(Thread.currentThread().getName() + ":" + name, name, Debug.contextLookup(RiResolvedMethod.class), -1);
     }
 
     private void closeScope() {
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Mon May 07 11:40:44 2012 +0200
@@ -27,5 +27,5 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface Snippet {
-
+    boolean atomic() default false;
 }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippets.java	Mon May 07 11:34:16 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippets.java	Mon May 07 11:40:44 2012 +0200
@@ -37,6 +37,7 @@
 import com.oracle.graal.nodes.java.*;
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.cri.ri.*;
+import com.oracle.max.criutils.*;
 
 /**
  * Utilities for snippet installation and management.
@@ -53,16 +54,18 @@
         }
     }
 
-    private static void installSnippets(GraalRuntime runtime, CiTarget target, Class< ? extends SnippetsInterface> clazz,
-                    BoxingMethodPool pool) {
-        for (Method snippet : clazz.getDeclaredMethods()) {
-            int modifiers = snippet.getModifiers();
-            if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
-                throw new RuntimeException("Snippet must not be abstract or native");
-            }
-            RiResolvedMethod snippetRiMethod = runtime.getRiMethod(snippet);
-            if (snippetRiMethod.compilerStorage().get(Graph.class) == null) {
-                buildSnippetGraph(snippetRiMethod, runtime, target, pool);
+    private static void installSnippets(GraalRuntime runtime, CiTarget target, Class< ? extends SnippetsInterface> clazz, BoxingMethodPool pool) {
+        for (Method method : clazz.getDeclaredMethods()) {
+            if (method.getAnnotation(Snippet.class) != null) {
+                Method snippet = method;
+                int modifiers = snippet.getModifiers();
+                if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
+                    throw new RuntimeException("Snippet must not be abstract or native");
+                }
+                RiResolvedMethod snippetRiMethod = runtime.getRiMethod(snippet);
+                if (snippetRiMethod.compilerStorage().get(Graph.class) == null) {
+                    buildSnippetGraph(snippetRiMethod, runtime, target, method.getAnnotation(Snippet.class).atomic(), pool);
+                }
             }
         }
     }
@@ -80,7 +83,7 @@
                     throw new RuntimeException("Snippet must not be abstract or native");
                 }
                 RiResolvedMethod snippetRiMethod = runtime.getRiMethod(snippet);
-                StructuredGraph graph = buildSnippetGraph(snippetRiMethod, runtime, target, pool);
+                StructuredGraph graph = buildSnippetGraph(snippetRiMethod, runtime, target, false, pool);
                 runtime.getRiMethod(method).compilerStorage().put(Graph.class, graph);
             } catch (NoSuchMethodException e) {
                 throw new RuntimeException("Could not resolve method to substitute with: " + snippet.getName(), e);
@@ -88,7 +91,7 @@
         }
     }
 
-    private static StructuredGraph buildSnippetGraph(final RiResolvedMethod snippetRiMethod, final GraalRuntime runtime, final CiTarget target, final BoxingMethodPool pool) {
+    private static StructuredGraph buildSnippetGraph(final RiResolvedMethod snippetRiMethod, final GraalRuntime runtime, final CiTarget target, final boolean atomic, final BoxingMethodPool pool) {
         return Debug.scope("BuildSnippetGraph", snippetRiMethod, new Callable<StructuredGraph>() {
 
             @Override
@@ -109,7 +112,7 @@
                     if (holder.isSubtypeOf(runtime.getType(SnippetsInterface.class))) {
                         StructuredGraph targetGraph = (StructuredGraph) targetMethod.compilerStorage().get(Graph.class);
                         if (targetGraph == null) {
-                            targetGraph = buildSnippetGraph(targetMethod, runtime, target, pool);
+                            targetGraph = buildSnippetGraph(targetMethod, runtime, target, atomic, pool);
                         }
                         InliningUtil.inline(invoke, targetGraph, true);
                         if (GraalOptions.OptCanonicalizer) {
@@ -126,6 +129,18 @@
                     new CanonicalizerPhase(target, runtime, null).apply(graph);
                 }
 
+                if (atomic) {
+                    for (Node n : graph.getNodes()) {
+                        if (n instanceof StateSplit) {
+                            StateSplit stateSplit = (StateSplit) n;
+                            if (stateSplit.stateAfter() != null) {
+                                TTY.println(graph +  ": " + stateSplit);
+                                stateSplit.setStateAfter(null);
+                            }
+                        }
+                    }
+                }
+
                 for (LoopEndNode end : graph.getNodes(LoopEndNode.class)) {
                     end.disableSafepoint();
                 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.tests/src/com/oracle/graal/compiler/tests/LowerCheckCastTest.java	Mon May 07 11:40:44 2012 +0200
@@ -0,0 +1,124 @@
+/*
+ * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.compiler.tests;
+
+import java.lang.reflect.*;
+import java.util.*;
+
+import org.junit.*;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.max.cri.ci.*;
+import com.oracle.max.cri.ri.*;
+
+/**
+ * In the following tests, the usages of local variable "a" are replaced with the integer constant 0.
+ * Then canonicalization is applied and it is verified that the resulting graph is equal to the
+ * graph of the method that just has a "return 1" statement in it.
+ */
+public class LowerCheckCastTest extends GraphTest {
+
+    static int warmup() {
+        Object[] numbers = {76L, (short) 34};
+        int result = 0;
+        for (int i = 0; i < 20; i++) {
+            Object num = numbers[i % numbers.length];
+            result += result + asNumber(num).intValue();
+        }
+        return result;
+    }
+
+    private RiCompiledMethod compile(String name, Class[] hintClasses, boolean exact) {
+        System.out.println("compiling: " + name + ", hints=" + Arrays.toString(hintClasses) + ", exact=" + exact);
+        // Ensure that the method is fully resolved
+        asNumber(0);
+
+        Method method = getMethod(name);
+        final StructuredGraph graph = parse(method);
+
+        RiResolvedType[] hints = new RiResolvedType[hintClasses.length];
+        for (int i = 0; i < hintClasses.length; i++) {
+            hints[i] = runtime.getType(hintClasses[i]);
+        }
+
+        CheckCastNode ccn = graph.getNodes(CheckCastNode.class).first();
+        assert ccn != null;
+        CheckCastNode ccnNew = graph.add(new CheckCastNode(ccn.anchor(), ccn.targetClassInstruction(), ccn.targetClass(), ccn.object(), hints, exact));
+        graph.replaceFloating(ccn, ccnNew);
+
+        final RiResolvedMethod riMethod = runtime.getRiMethod(method);
+        CiTargetMethod targetMethod = runtime.compile(riMethod, graph);
+        return addMethod(riMethod, targetMethod);
+    }
+
+    //@Test
+    public void test1() {
+        Class[] hints = {};
+        RiCompiledMethod compiledMethod = compile("asNumber", hints, false);
+        Assert.assertEquals(Integer.valueOf(111), compiledMethod.executeVarargs(111));
+    }
+
+    //@Test
+    public void test2() {
+        Class[] hints = {Integer.class};
+        RiCompiledMethod compiledMethod = compile("asNumber", hints, false);
+        Assert.assertEquals(Integer.valueOf(111), compiledMethod.executeVarargs(111));
+    }
+
+    @Test
+    public void test3() {
+        Class[] hints = {Long.class, Short.class};
+        RiCompiledMethod compiledMethod = compile("asNumber", hints, false);
+        Assert.assertEquals(Integer.valueOf(111), compiledMethod.executeVarargs(111));
+    }
+
+    //@Test
+    public void test4() {
+        Class[] hints = {};
+        RiCompiledMethod compiledMethod = compile("asString", hints, true);
+        Assert.assertEquals("111", compiledMethod.executeVarargs("111"));
+    }
+
+    @Test
+    public void test5() {
+        Class[] hints = {String.class};
+        RiCompiledMethod compiledMethod = compile("asString", hints, true);
+        Assert.assertEquals("111", compiledMethod.executeVarargs("111"));
+    }
+
+    //@Test(expected = ClassCastException.class)
+    public void test100() {
+        Class[] hints = {};
+        RiCompiledMethod compiledMethod = compile("asNumber", hints, false);
+        compiledMethod.executeVarargs("number");
+    }
+
+    public static Number asNumber(Object o) {
+        return (Number) o;
+    }
+
+    public static String asString(Object o) {
+        return (String) o;
+    }
+}