changeset 6302:5d7d9a6953bd

added InstanceOfSnippets which passes InstanceOfTest but is not yet enabled for general lowering
author Doug Simon <doug.simon@oracle.com>
date Fri, 31 Aug 2012 17:57:30 +0200
parents 4535a87e8bf8
children 7acbc95b4b2b
files graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/LoweringPhase.java graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.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/InstanceOfSnippets.java graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java graal/com.oracle.graal.printer/src/com/oracle/graal/printer/GraphPrinterDumpHandler.java graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/InstanceOfTest.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/ExplodeLoopNode.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/JumpNode.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/LoadSnippetParameterNode.java graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/LoadSnippetVarargParameterNode.java
diffstat 18 files changed, 1088 insertions(+), 225 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/GraalOptions.java	Fri Aug 31 17:57:30 2012 +0200
@@ -271,6 +271,7 @@
      * Only instructions in methods whose fully qualified name contains this option will be HIR lowered.
      */
     public static String HIRLowerCheckcast = "";
+    public static String HIRLowerInstanceOf = "InstanceOfTest";
     public static String HIRLowerNewInstance = "";
     public static String HIRLowerNewArray = "";
 
--- a/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/LoweringPhase.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.compiler/src/com/oracle/graal/compiler/phases/LoweringPhase.java	Fri Aug 31 17:57:30 2012 +0200
@@ -22,6 +22,8 @@
  */
 package com.oracle.graal.compiler.phases;
 
+import java.util.*;
+
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.*;
@@ -39,7 +41,16 @@
  */
 public class LoweringPhase extends Phase {
 
-    private abstract class LoweringToolBase implements LoweringTool {
+    final class LoweringToolImpl implements LoweringTool {
+
+        final FixedNode guardAnchor;
+        final NodeBitMap activeGuards;
+        FixedWithNextNode lastFixedNode;
+
+        public LoweringToolImpl(FixedNode guardAnchor, NodeBitMap activeGuards) {
+            this.guardAnchor = guardAnchor;
+            this.activeGuards = activeGuards;
+        }
 
         @Override
         public GraalCodeCacheProvider getRuntime() {
@@ -60,6 +71,27 @@
         public Assumptions assumptions() {
             return assumptions;
         }
+
+        @Override
+        public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, boolean negated, long leafGraphId) {
+            if (GraalOptions.OptEliminateGuards) {
+                for (Node usage : condition.usages()) {
+                    if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage)) {
+                        return (ValueNode) usage;
+                    }
+                }
+            }
+            GuardNode newGuard = guardAnchor.graph().unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, leafGraphId));
+            if (GraalOptions.OptEliminateGuards) {
+                activeGuards.grow();
+                activeGuards.mark(newGuard);
+            }
+            return newGuard;
+        }
+
+        public FixedWithNextNode lastFixedNode() {
+            return lastFixedNode;
+        }
     }
 
     private final GraalCodeCacheProvider runtime;
@@ -130,42 +162,42 @@
         }
     }
 
-    private void process(final Block b, final NodeBitMap activeGuards, final ValueNode anchor, SchedulePhase schedule, NodeBitMap processed) {
+    private void process(final Block b, final NodeBitMap activeGuards, final FixedNode anchor, SchedulePhase schedule, NodeBitMap processed) {
 
-        final LoweringTool loweringTool = new LoweringToolBase() {
+        final LoweringToolImpl loweringTool = new LoweringToolImpl(anchor, activeGuards);
+
+        // Lower the instructions of this block.
+        List<ScheduledNode> nodes = schedule.nodesFor(b);
 
-            @Override
-            public ValueNode getGuardAnchor() {
-                return anchor;
+        for (Node node : nodes) {
+            FixedNode lastFixedNext = null;
+            if (node instanceof FixedWithNextNode) {
+                FixedWithNextNode fixed = (FixedWithNextNode) node;
+                lastFixedNext = fixed.next();
+                loweringTool.lastFixedNode = fixed;
             }
 
-            @Override
-            public ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, boolean negated, long leafGraphId) {
-                FixedNode guardAnchor = (FixedNode) getGuardAnchor();
-                if (GraalOptions.OptEliminateGuards) {
-                    for (Node usage : condition.usages()) {
-                        if (!activeGuards.isNew(usage) && activeGuards.isMarked(usage)) {
-                            return (ValueNode) usage;
-                        }
-                    }
-                }
-                GuardNode newGuard = guardAnchor.graph().unique(new GuardNode(condition, guardAnchor, deoptReason, action, negated, leafGraphId));
-                if (GraalOptions.OptEliminateGuards) {
-                    activeGuards.grow();
-                    activeGuards.mark(newGuard);
-                }
-                return newGuard;
-            }
-        };
-
-        // Lower the instructions of this block.
-        for (Node node : schedule.nodesFor(b)) {
-            if (!processed.isMarked(node)) {
+            if (node.isAlive() && !processed.isMarked(node)) {
                 processed.mark(node);
                 if (node instanceof Lowerable) {
                     ((Lowerable) node).lower(loweringTool);
                 }
             }
+
+            if (loweringTool.lastFixedNode == node && !node.isAlive()) {
+                if (lastFixedNext == null) {
+                    loweringTool.lastFixedNode = null;
+                } else {
+                    Node prev = lastFixedNext.predecessor();
+                    if (prev != node && prev instanceof FixedWithNextNode) {
+                        loweringTool.lastFixedNode = (FixedWithNextNode) prev;
+                    } else if (lastFixedNext instanceof FixedWithNextNode) {
+                        loweringTool.lastFixedNode = (FixedWithNextNode) lastFixedNext;
+                    } else {
+                        loweringTool.lastFixedNode = null;
+                    }
+                }
+            }
         }
     }
 }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeClass.java	Fri Aug 31 17:57:30 2012 +0200
@@ -606,7 +606,7 @@
         while (index < directInputCount) {
             Node input = getNode(node, inputOffsets[index]);
             if (input == old) {
-                assert other == null || fieldTypes.get(inputOffsets[index]).isAssignableFrom(other.getClass()); // : "Can not assign " + other.getClass() + " to " + inputTypes[index] + " in " + node;
+                assert other == null || fieldTypes.get(inputOffsets[index]).isAssignableFrom(other.getClass()) : "Can not assign " + other.getClass() + " to " + fieldTypes.get(inputOffsets[index]) + " in " + node;
                 putNode(node, inputOffsets[index], other);
                 return true;
             }
@@ -830,7 +830,7 @@
         // create node duplicates
         for (Node node : nodes) {
             if (node != null) {
-                assert !node.isDeleted() : "trying to duplicate deleted node";
+                assert !node.isDeleted() : "trying to duplicate deleted node: " + node;
                 Node replacement = replacements.replacement(node);
                 if (replacement != node) {
                     assert replacement != null;
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/meta/HotSpotRuntime.java	Fri Aug 31 17:57:30 2012 +0200
@@ -61,6 +61,7 @@
     private final HotSpotRegisterConfig globalStubRegConfig;
     private final HotSpotGraalRuntime graalRuntime;
     private CheckCastSnippets.Templates checkcastSnippets;
+    private InstanceOfSnippets.Templates instanceofSnippets;
     private NewObjectSnippets.Templates newObjectSnippets;
 
     public HotSpotRuntime(HotSpotVMConfig config, HotSpotGraalRuntime graalRuntime) {
@@ -77,8 +78,10 @@
         installer.install(UnsafeSnippets.class);
         installer.install(ArrayCopySnippets.class);
         installer.install(CheckCastSnippets.class);
+        installer.install(InstanceOfSnippets.class);
         installer.install(NewObjectSnippets.class);
         checkcastSnippets = new CheckCastSnippets.Templates(this);
+        instanceofSnippets = new InstanceOfSnippets.Templates(this);
         newObjectSnippets = new NewObjectSnippets.Templates(this, graalRuntime.getTarget(), config.useTLAB);
     }
 
@@ -407,6 +410,10 @@
             if (matches(graph, GraalOptions.HIRLowerCheckcast)) {
                 checkcastSnippets.lower((CheckCastNode) n, tool);
             }
+        } else if (n instanceof InstanceOfNode) {
+            if (matches(graph, GraalOptions.HIRLowerInstanceOf)) {
+                instanceofSnippets.lower((InstanceOfNode) n, tool);
+            }
         } else if (n instanceof NewInstanceNode) {
             if (matches(graph, GraalOptions.HIRLowerNewInstance)) {
                 newObjectSnippets.lower((NewInstanceNode) n, tool);
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java	Fri Aug 31 17:57:30 2012 +0200
@@ -22,14 +22,13 @@
  */
 package com.oracle.graal.hotspot.snippets;
 import static com.oracle.graal.hotspot.snippets.ArrayCopySnippets.*;
-import static com.oracle.graal.snippets.Snippet.Multiple.*;
+import static com.oracle.graal.snippets.Snippet.Varargs.*;
 import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*;
 
 import com.oracle.graal.api.code.*;
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.nodes.*;
@@ -40,16 +39,16 @@
 import com.oracle.graal.snippets.Snippet.ConstantParameter;
 import com.oracle.graal.snippets.Snippet.Fold;
 import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.Snippet.VarargsParameter;
+import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates;
 import com.oracle.graal.snippets.SnippetTemplate.Arguments;
-import com.oracle.graal.snippets.SnippetTemplate.Cache;
 import com.oracle.graal.snippets.SnippetTemplate.Key;
 import com.oracle.graal.snippets.nodes.*;
 
 /**
  * Snippets used for implementing the type test of a checkcast instruction.
  *
- * The test first checks against the profiled types (if any) and then implements the
- * checks described in the paper <a href="http://dl.acm.org/citation.cfm?id=583821">
+ * The type tests implemented are described in the paper <a href="http://dl.acm.org/citation.cfm?id=583821">
  * Fast subtype checking in the HotSpot JVM</a> by Cliff Click and John Rose.
  */
 public class CheckCastSnippets implements SnippetsInterface {
@@ -58,7 +57,10 @@
      * Type test used when the type being tested against is a final type.
      */
     @Snippet
-    public static Object checkcastExact(@Parameter("object") Object object, @Parameter("exactHub") Object exactHub, @ConstantParameter("checkNull") boolean checkNull) {
+    public static Object checkcastExact(
+                    @Parameter("object") Object object,
+                    @Parameter("exactHub") Object exactHub,
+                    @ConstantParameter("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             isNull.inc();
             return object;
@@ -80,7 +82,11 @@
      * first.
      */
     @Snippet
-    public static Object checkcastPrimary(@Parameter("hub") Object hub, @Parameter("object") Object object, @ConstantParameter("checkNull") boolean checkNull, @ConstantParameter("superCheckOffset") int superCheckOffset) {
+    public static Object checkcastPrimary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @ConstantParameter("checkNull") boolean checkNull,
+                    @ConstantParameter("superCheckOffset") int superCheckOffset) {
         if (checkNull && object == null) {
             isNull.inc();
             return object;
@@ -98,7 +104,11 @@
      * Type test used when the type being tested against is a restricted secondary type.
      */
     @Snippet
-    public static Object checkcastSecondary(@Parameter("hub") Object hub, @Parameter("object") Object object, @Parameter(value = "hints", multiple = true) Object[] hints, @ConstantParameter("checkNull") boolean checkNull) {
+    public static Object checkcastSecondary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             isNull.inc();
             return object;
@@ -124,7 +134,11 @@
      * in an object array store check).
      */
     @Snippet
-    public static Object checkcastUnknown(@Parameter("hub") Object hub, @Parameter("object") Object object, @Parameter(value = "hints", multiple = true) Object[] hints, @ConstantParameter("checkNull") boolean checkNull) {
+    public static Object checkcastUnknown(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             isNull.inc();
             return object;
@@ -238,26 +252,19 @@
         return HotSpotGraalRuntime.getInstance().getConfig().hubOffset;
     }
 
-    public static class Templates {
+    public static class Templates extends AbstractTemplates<CheckCastSnippets> {
 
-        private final Cache cache;
         private final ResolvedJavaMethod exact;
         private final ResolvedJavaMethod primary;
         private final ResolvedJavaMethod secondary;
         private final ResolvedJavaMethod unknown;
-        private final CodeCacheProvider runtime;
 
         public Templates(CodeCacheProvider runtime) {
-            this.runtime = runtime;
-            this.cache = new Cache(runtime);
-            try {
-                exact = runtime.getResolvedJavaMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastExact", Object.class, Object.class, boolean.class));
-                primary = runtime.getResolvedJavaMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastPrimary", Object.class, Object.class, boolean.class, int.class));
-                secondary = runtime.getResolvedJavaMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastSecondary", Object.class, Object.class, Object[].class, boolean.class));
-                unknown = runtime.getResolvedJavaMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastUnknown", Object.class, Object.class, Object[].class, boolean.class));
-            } catch (NoSuchMethodException e) {
-                throw new GraalInternalError(e);
-            }
+            super(runtime, CheckCastSnippets.class);
+            exact = snippet("checkcastExact", Object.class, Object.class, boolean.class);
+            primary = snippet("checkcastPrimary", Object.class, Object.class, boolean.class, int.class);
+            secondary = snippet("checkcastSecondary", Object.class, Object.class, Object[].class, boolean.class);
+            unknown = snippet("checkcastUnknown", Object.class, Object.class, Object[].class, boolean.class);
         }
 
         /**
@@ -275,7 +282,7 @@
 
             if (target == null) {
                 HotSpotKlassOop[] hints = createHints(hintInfo);
-                key = new Key(unknown).add("hints", multiple(Object.class, hints.length)).add("checkNull", checkNull);
+                key = new Key(unknown).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
                 arguments = arguments("hub", hub).add("object", object).add("hints", hints);
             } else if (hintInfo.exact) {
                 HotSpotKlassOop[] hints = createHints(hintInfo);
@@ -287,16 +294,16 @@
                 arguments = arguments("hub", hub).add("object", object);
             } else {
                 HotSpotKlassOop[] hints = createHints(hintInfo);
-                key = new Key(secondary).add("hints", multiple(Object.class, hints.length)).add("checkNull", checkNull);
+                key = new Key(secondary).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
                 arguments = arguments("hub", hub).add("object", object).add("hints", hints);
             }
 
             SnippetTemplate template = cache.get(key);
             Debug.log("Lowering checkcast in %s: node=%s, template=%s, arguments=%s", graph, checkcast, template, arguments);
-            template.instantiate(runtime, checkcast, checkcast, arguments);
+            template.instantiate(runtime, checkcast, arguments);
         }
 
-        private static HotSpotKlassOop[] createHints(TypeCheckHints hints) {
+        static HotSpotKlassOop[] createHints(TypeCheckHints hints) {
             HotSpotKlassOop[] hintHubs = new HotSpotKlassOop[hints.types.length];
             for (int i = 0; i < hintHubs.length; i++) {
                 hintHubs[i] = ((HotSpotJavaType) hints.types[i]).klassOop();
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/InstanceOfSnippets.java	Fri Aug 31 17:57:30 2012 +0200
@@ -0,0 +1,492 @@
+/*
+ * 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.snippets;
+import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.*;
+import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.Templates.*;
+import static com.oracle.graal.snippets.Snippet.Varargs.*;
+import static com.oracle.graal.snippets.SnippetTemplate.Arguments.*;
+import static com.oracle.graal.snippets.nodes.JumpNode.*;
+
+import com.oracle.graal.api.code.*;
+import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.*;
+import com.oracle.graal.graph.*;
+import com.oracle.graal.hotspot.*;
+import com.oracle.graal.hotspot.meta.*;
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.extended.*;
+import com.oracle.graal.nodes.java.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.snippets.*;
+import com.oracle.graal.snippets.Snippet.ConstantParameter;
+import com.oracle.graal.snippets.Snippet.Fold;
+import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.Snippet.VarargsParameter;
+import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates;
+import com.oracle.graal.snippets.SnippetTemplate.Arguments;
+import com.oracle.graal.snippets.SnippetTemplate.Key;
+import com.oracle.graal.snippets.nodes.*;
+
+/**
+ * Snippets used for implementing the type test of an instanceof instruction.
+ * Since instanceof is a floating node, it is lowered separately for each of
+ * its usages.
+ *
+ * The type tests implemented are described in the paper <a href="http://dl.acm.org/citation.cfm?id=583821">
+ * Fast subtype checking in the HotSpot JVM</a> by Cliff Click and John Rose.
+ */
+public class InstanceOfSnippets implements SnippetsInterface {
+
+    /**
+     * A test against a final type with the result being {@linkplain MaterializeNode materialized}.
+     */
+    @Snippet
+    public static Object materializeExact(
+                    @Parameter("object") Object object,
+                    @Parameter("exactHub") Object exactHub,
+                    @Parameter("trueValue") Object trueValue,
+                    @Parameter("falseValue") Object falseValue,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            return falseValue;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        if (objectHub != exactHub) {
+            exactMiss.inc();
+            return falseValue;
+        }
+        exactHit.inc();
+        return trueValue;
+    }
+
+    /**
+     * A test against a final type with the result being {@linkplain IfNode branched} upon.
+     */
+    @Snippet
+    public static void ifExact(
+                    @Parameter("object") Object object,
+                    @Parameter("exactHub") Object exactHub,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        if (objectHub != exactHub) {
+            exactMiss.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        exactHit.inc();
+        jump(IfNode.TRUE_EDGE);
+    }
+
+    /**
+     * A test against a primary type with the result being {@linkplain MaterializeNode materialized}.
+     */
+    @Snippet
+    public static Object materializePrimary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @Parameter("trueValue") Object trueValue,
+                    @Parameter("falseValue") Object falseValue,
+                    @ConstantParameter("checkNull") boolean checkNull,
+                    @ConstantParameter("superCheckOffset") int superCheckOffset) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            return falseValue;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        if (UnsafeLoadNode.loadObject(objectHub, 0, superCheckOffset, true) != hub) {
+            displayMiss.inc();
+            return falseValue;
+        }
+        displayHit.inc();
+        return trueValue;
+    }
+
+    /**
+     * A test against a primary type with the result being {@linkplain IfNode branched} upon.
+     */
+    @Snippet
+    public static void ifPrimary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @ConstantParameter("checkNull") boolean checkNull,
+                    @ConstantParameter("superCheckOffset") int superCheckOffset) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        if (UnsafeLoadNode.loadObject(objectHub, 0, superCheckOffset, true) != hub) {
+            displayMiss.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        displayHit.inc();
+        jump(IfNode.TRUE_EDGE);
+        return;
+    }
+
+    /**
+     * A test against a restricted secondary type type with the result being {@linkplain MaterializeNode materialized}.
+     */
+    @Snippet
+    public static Object materializeSecondary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @Parameter("trueValue") Object trueValue,
+                    @Parameter("falseValue") Object falseValue,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            return falseValue;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        // if we get an exact match: succeed immediately
+        ExplodeLoopNode.explodeLoop();
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
+            if (hintHub == objectHub) {
+                hintsHit.inc();
+                return trueValue;
+            }
+        }
+        if (!checkSecondarySubType(hub, objectHub)) {
+            return falseValue;
+        }
+        return trueValue;
+    }
+
+    /**
+     * A test against a restricted secondary type with the result being {@linkplain IfNode branched} upon.
+     */
+    @Snippet
+    public static void ifSecondary(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        // if we get an exact match: succeed immediately
+        ExplodeLoopNode.explodeLoop();
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
+            if (hintHub == objectHub) {
+                hintsHit.inc();
+                jump(IfNode.TRUE_EDGE);
+                return;
+            }
+        }
+        if (!checkSecondarySubType(hub, objectHub)) {
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        jump(IfNode.TRUE_EDGE);
+        return;
+    }
+
+    /**
+     * A test against an unknown (at compile time) type with the result being {@linkplain MaterializeNode materialized}.
+     */
+    @Snippet
+    public static Object materializeUnknown(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @Parameter("trueValue") Object trueValue,
+                    @Parameter("falseValue") Object falseValue,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            return falseValue;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        // if we get an exact match: succeed immediately
+        ExplodeLoopNode.explodeLoop();
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
+            if (hintHub == objectHub) {
+                hintsHit.inc();
+                return trueValue;
+            }
+        }
+        if (!checkUnknownSubType(hub, objectHub)) {
+            return falseValue;
+        }
+        return trueValue;
+    }
+
+    /**
+     * A test against an unknown (at compile time) type with the result being {@linkplain IfNode branched} upon.
+     */
+    @Snippet
+    public static void ifUnknown(
+                    @Parameter("hub") Object hub,
+                    @Parameter("object") Object object,
+                    @VarargsParameter("hints") Object[] hints,
+                    @ConstantParameter("checkNull") boolean checkNull) {
+        if (checkNull && object == null) {
+            isNull.inc();
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
+        // if we get an exact match: succeed immediately
+        ExplodeLoopNode.explodeLoop();
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
+            if (hintHub == objectHub) {
+                hintsHit.inc();
+                jump(IfNode.TRUE_EDGE);
+                return;
+            }
+        }
+        if (!checkUnknownSubType(hub, objectHub)) {
+            jump(IfNode.FALSE_EDGE);
+            return;
+        }
+        jump(IfNode.TRUE_EDGE);
+        return;
+    }
+
+    static boolean checkSecondarySubType(Object t, Object s) {
+        // if (S.cache == T) return true
+        if (UnsafeLoadNode.loadObject(s, 0, secondarySuperCacheOffset(), true) == t) {
+            cacheHit.inc();
+            return true;
+        }
+
+        // if (T == S) return true
+        if (s == t) {
+            T_equals_S.inc();
+            return true;
+        }
+
+        // if (S.scan_s_s_array(T)) { S.cache = T; return true; }
+        Object[] secondarySupers = UnsafeCastNode.cast(UnsafeLoadNode.loadObject(s, 0, secondarySupersOffset(), true), Object[].class);
+
+        for (int i = 0; i < secondarySupers.length; i++) {
+            if (t == loadNonNullObjectElement(secondarySupers, i)) {
+                DirectObjectStoreNode.storeObject(s, secondarySuperCacheOffset(), 0, t);
+                secondariesHit.inc();
+                return true;
+            }
+        }
+        secondariesMiss.inc();
+        return false;
+    }
+
+    static boolean checkUnknownSubType(Object t, Object s) {
+        // int off = T.offset
+        int superCheckOffset = UnsafeLoadNode.load(t, 0, superCheckOffsetOffset(), Kind.Int);
+        boolean primary = superCheckOffset != secondarySuperCacheOffset();
+
+        // if (T = S[off]) return true
+        if (UnsafeLoadNode.loadObject(s, 0, superCheckOffset, true) == t) {
+            if (primary) {
+                cacheHit.inc();
+            } else {
+                displayHit.inc();
+            }
+            return true;
+        }
+
+        // if (off != &cache) return false
+        if (primary) {
+            displayMiss.inc();
+            return false;
+        }
+
+        // if (T == S) return true
+        if (s == t) {
+            T_equals_S.inc();
+            return true;
+        }
+
+        // if (S.scan_s_s_array(T)) { S.cache = T; return true; }
+        Object[] secondarySupers = UnsafeCastNode.cast(UnsafeLoadNode.loadObject(s, 0, secondarySupersOffset(), true), Object[].class);
+        for (int i = 0; i < secondarySupers.length; i++) {
+            if (t == loadNonNullObjectElement(secondarySupers, i)) {
+                DirectObjectStoreNode.storeObject(s, secondarySuperCacheOffset(), 0, t);
+                secondariesHit.inc();
+                return true;
+            }
+        }
+
+        secondariesMiss.inc();
+        return false;
+    }
+
+    @Fold
+    private static int superCheckOffsetOffset() {
+        return HotSpotGraalRuntime.getInstance().getConfig().superCheckOffsetOffset;
+    }
+
+    @Fold
+    private static int secondarySuperCacheOffset() {
+        return HotSpotGraalRuntime.getInstance().getConfig().secondarySuperCacheOffset;
+    }
+
+    @Fold
+    private static int secondarySupersOffset() {
+        return HotSpotGraalRuntime.getInstance().getConfig().secondarySupersOffset;
+    }
+
+    @Fold
+    private static int hubOffset() {
+        return HotSpotGraalRuntime.getInstance().getConfig().hubOffset;
+    }
+
+
+    public static class Templates extends AbstractTemplates<InstanceOfSnippets> {
+
+        private final ResolvedJavaMethod ifExact;
+        private final ResolvedJavaMethod ifPrimary;
+        private final ResolvedJavaMethod ifSecondary;
+        private final ResolvedJavaMethod ifUnknown;
+        private final ResolvedJavaMethod materializeExact;
+        private final ResolvedJavaMethod materializePrimary;
+        private final ResolvedJavaMethod materializeSecondary;
+        private final ResolvedJavaMethod materializeUnknown;
+
+        public Templates(CodeCacheProvider runtime) {
+            super(runtime, InstanceOfSnippets.class);
+            ifExact = snippet("ifExact", Object.class, Object.class, boolean.class);
+            ifPrimary = snippet("ifPrimary", Object.class, Object.class, boolean.class, int.class);
+            ifSecondary = snippet("ifSecondary", Object.class, Object.class, Object[].class, boolean.class);
+            ifUnknown = snippet("ifUnknown", Object.class, Object.class, Object[].class, boolean.class);
+
+            materializeExact = snippet("materializeExact", Object.class, Object.class, Object.class, Object.class, boolean.class);
+            materializePrimary = snippet("materializePrimary", Object.class, Object.class, Object.class, Object.class, boolean.class, int.class);
+            materializeSecondary = snippet("materializeSecondary", Object.class, Object.class, Object.class, Object.class, Object[].class, boolean.class);
+            materializeUnknown = snippet("materializeUnknown", Object.class, Object.class, Object.class, Object.class, Object[].class, boolean.class);
+        }
+
+        public void lower(InstanceOfNode instanceOf, LoweringTool tool) {
+            ValueNode hub = instanceOf.targetClassInstruction();
+            ValueNode object = instanceOf.object();
+            TypeCheckHints hintInfo = new TypeCheckHints(instanceOf.targetClass(), instanceOf.profile(), tool.assumptions(), GraalOptions.CheckcastMinHintHitProbability, GraalOptions.CheckcastMaxHints);
+            final HotSpotResolvedJavaType target = (HotSpotResolvedJavaType) instanceOf.targetClass();
+            boolean checkNull = !object.stamp().nonNull();
+
+            for (Node usage : instanceOf.usages().snapshot()) {
+                Arguments arguments = null;
+                Key key = null;
+
+                // instanceof nodes are lowered separately for each usage. To simply graph modifications,
+                // we duplicate the instanceof node for each usage.
+                InstanceOfNode duplicate = instanceOf.graph().add(new InstanceOfNode(instanceOf.targetClassInstruction(), instanceOf.targetClass(), instanceOf.object(), instanceOf.profile()));
+                usage.replaceFirstInput(instanceOf, duplicate);
+
+                if (usage instanceof IfNode) {
+
+                    IfNode ifNode = (IfNode) usage;
+                    if (target == null) {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        key = new Key(ifUnknown).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
+                        arguments = arguments("hub", hub).add("object", object).add("hints", hints);
+                    } else if (hintInfo.exact) {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        assert hints.length == 1;
+                        key = new Key(ifExact).add("checkNull", checkNull);
+                        arguments = arguments("object", object).add("exactHub", hints[0]);
+                    } else if (target.isPrimaryType()) {
+                        key = new Key(ifPrimary).add("checkNull", checkNull).add("superCheckOffset", target.superCheckOffset());
+                        arguments = arguments("hub", hub).add("object", object);
+                    } else {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        key = new Key(ifSecondary).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
+                        arguments = arguments("hub", hub).add("object", object).add("hints", hints);
+                    }
+
+                    SnippetTemplate template = cache.get(key);
+                    template.instantiate(runtime, duplicate, ifNode, arguments);
+                    assert ifNode.isDeleted();
+
+                } else if (usage instanceof MaterializeNode) {
+
+                    MaterializeNode materialize = (MaterializeNode) usage;
+                    materialize.replaceAtUsages(duplicate);
+                    ValueNode falseValue = materialize.falseValue();
+                    ValueNode trueValue = materialize.trueValue();
+
+                    // The materialize node is no longer connected to anyone -> kill it
+                    materialize.clearInputs();
+                    assert materialize.usages().isEmpty();
+                    GraphUtil.killWithUnusedFloatingInputs(materialize);
+
+                    if (target == null) {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        key = new Key(materializeUnknown).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
+                        arguments = arguments("hub", hub).add("object", object).add("hints", hints).add("trueValue", trueValue).add("falseValue", falseValue);
+                    } else if (hintInfo.exact) {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        assert hints.length == 1;
+                        key = new Key(materializeExact).add("checkNull", checkNull);
+                        arguments = arguments("object", object).add("exactHub", hints[0]).add("trueValue", trueValue).add("falseValue", falseValue);
+                    } else if (target.isPrimaryType()) {
+                        key = new Key(materializePrimary).add("checkNull", checkNull).add("superCheckOffset", target.superCheckOffset());
+                        arguments = arguments("hub", hub).add("object", object).add("trueValue", trueValue).add("falseValue", falseValue);
+                    } else {
+                        HotSpotKlassOop[] hints = createHints(hintInfo);
+                        key = new Key(materializeSecondary).add("hints", vargargs(Object.class, hints.length)).add("checkNull", checkNull);
+                        arguments = arguments("hub", hub).add("object", object).add("hints", hints).add("trueValue", trueValue).add("falseValue", falseValue);
+                    }
+
+                    SnippetTemplate template = cache.get(key);
+                    template.instantiate(runtime, duplicate, tool.lastFixedNode(), arguments);
+                } else {
+                    throw new GraalInternalError("Unexpected usage of %s: %s", instanceOf, usage);
+                }
+            }
+
+            assert !instanceOf.isDeleted();
+            assert instanceOf.usages().isEmpty();
+            GraphUtil.killWithUnusedFloatingInputs(instanceOf);
+        }
+    }
+
+    private static final SnippetCounter.Group counters = GraalOptions.SnippetCounters ? new SnippetCounter.Group("Checkcast") : null;
+    private static final SnippetCounter hintsHit = new SnippetCounter(counters, "hintsHit", "hit a hint type");
+    private static final SnippetCounter exactHit = new SnippetCounter(counters, "exactHit", "exact type test succeeded");
+    private static final SnippetCounter exactMiss = new SnippetCounter(counters, "exactMiss", "exact type test failed");
+    private static final SnippetCounter isNull = new SnippetCounter(counters, "isNull", "object tested was null");
+    private static final SnippetCounter cacheHit = new SnippetCounter(counters, "cacheHit", "secondary type cache hit");
+    private static final SnippetCounter secondariesHit = new SnippetCounter(counters, "secondariesHit", "secondaries scan succeeded");
+    private static final SnippetCounter secondariesMiss = new SnippetCounter(counters, "secondariesMiss", "secondaries scan failed");
+    private static final SnippetCounter displayHit = new SnippetCounter(counters, "displayHit", "primary type test succeeded");
+    private static final SnippetCounter displayMiss = new SnippetCounter(counters, "displayMiss", "primary type test failed");
+    private static final SnippetCounter T_equals_S = new SnippetCounter(counters, "T_equals_S", "object type was equal to secondary type");
+}
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/NewObjectSnippets.java	Fri Aug 31 17:57:30 2012 +0200
@@ -35,7 +35,6 @@
 import com.oracle.graal.api.meta.*;
 import com.oracle.graal.compiler.*;
 import com.oracle.graal.debug.*;
-import com.oracle.graal.graph.*;
 import com.oracle.graal.hotspot.*;
 import com.oracle.graal.hotspot.meta.*;
 import com.oracle.graal.hotspot.nodes.*;
@@ -46,8 +45,8 @@
 import com.oracle.graal.snippets.Snippet.ConstantParameter;
 import com.oracle.graal.snippets.Snippet.Fold;
 import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.SnippetTemplate.AbstractTemplates;
 import com.oracle.graal.snippets.SnippetTemplate.Arguments;
-import com.oracle.graal.snippets.SnippetTemplate.Cache;
 import com.oracle.graal.snippets.SnippetTemplate.Key;
 
 /**
@@ -208,32 +207,25 @@
         }
     }
 
-    public static class Templates {
+    public static class Templates extends AbstractTemplates<NewObjectSnippets> {
 
-        private final Cache cache;
         private final ResolvedJavaMethod allocate;
         private final ResolvedJavaMethod initializeObject;
         private final ResolvedJavaMethod initializeObjectArray;
         private final ResolvedJavaMethod initializePrimitiveArray;
         private final ResolvedJavaMethod allocateArrayAndInitialize;
         private final TargetDescription target;
-        private final CodeCacheProvider runtime;
         private final boolean useTLAB;
 
         public Templates(CodeCacheProvider runtime, TargetDescription target, boolean useTLAB) {
-            this.runtime = runtime;
+            super(runtime, NewObjectSnippets.class);
             this.target = target;
-            this.cache = new Cache(runtime);
             this.useTLAB = useTLAB;
-            try {
-                allocate = runtime.getResolvedJavaMethod(NewObjectSnippets.class.getDeclaredMethod("allocate", int.class));
-                initializeObject = runtime.getResolvedJavaMethod(NewObjectSnippets.class.getDeclaredMethod("initializeObject", Word.class, Object.class, Word.class, int.class));
-                initializeObjectArray = runtime.getResolvedJavaMethod(NewObjectSnippets.class.getDeclaredMethod("initializeObjectArray", Word.class, Object.class, int.class, int.class, Word.class, int.class));
-                initializePrimitiveArray = runtime.getResolvedJavaMethod(NewObjectSnippets.class.getDeclaredMethod("initializePrimitiveArray", Word.class, Object.class, int.class, int.class, Word.class, int.class));
-                allocateArrayAndInitialize = runtime.getResolvedJavaMethod(NewObjectSnippets.class.getDeclaredMethod("allocateArrayAndInitialize", int.class, int.class, int.class, int.class, ResolvedJavaType.class, Kind.class));
-            } catch (NoSuchMethodException e) {
-                throw new GraalInternalError(e);
-            }
+            allocate = snippet("allocate", int.class);
+            initializeObject = snippet("initializeObject", Word.class, Object.class, Word.class, int.class);
+            initializeObjectArray = snippet("initializeObjectArray", Word.class, Object.class, int.class, int.class, Word.class, int.class);
+            initializePrimitiveArray = snippet("initializePrimitiveArray", Word.class, Object.class, int.class, int.class, Word.class, int.class);
+            allocateArrayAndInitialize = snippet("allocateArrayAndInitialize", int.class, int.class, int.class, int.class, ResolvedJavaType.class, Kind.class);
         }
 
         /**
@@ -301,7 +293,7 @@
                 Arguments arguments = new Arguments().add("length", lengthNode);
                 SnippetTemplate template = cache.get(key);
                 Debug.log("Lowering allocateArrayAndInitialize in %s: node=%s, template=%s, arguments=%s", graph, newArrayNode, template, arguments);
-                template.instantiate(runtime, newArrayNode, newArrayNode, arguments);
+                template.instantiate(runtime, newArrayNode, arguments);
             }
         }
 
@@ -313,7 +305,7 @@
             Arguments arguments = arguments("size", size);
             SnippetTemplate template = cache.get(key);
             Debug.log("Lowering fastAllocate in %s: node=%s, template=%s, arguments=%s", graph, tlabAllocateNode, template, arguments);
-            template.instantiate(runtime, tlabAllocateNode, tlabAllocateNode, arguments);
+            template.instantiate(runtime, tlabAllocateNode, arguments);
         }
 
         @SuppressWarnings("unused")
@@ -330,7 +322,7 @@
             Arguments arguments = arguments("memory", memory).add("hub", hub).add("initialMarkWord", type.initialMarkWord());
             SnippetTemplate template = cache.get(key);
             Debug.log("Lowering initializeObject in %s: node=%s, template=%s, arguments=%s", graph, initializeNode, template, arguments);
-            template.instantiate(runtime, initializeNode, initializeNode, arguments);
+            template.instantiate(runtime, initializeNode, arguments);
         }
 
         @SuppressWarnings("unused")
@@ -347,7 +339,7 @@
             Arguments arguments = arguments("memory", memory).add("hub", hub).add("initialMarkWord", type.initialMarkWord()).add("size", initializeNode.size()).add("length", initializeNode.length());
             SnippetTemplate template = cache.get(key);
             Debug.log("Lowering initializeObjectArray in %s: node=%s, template=%s, arguments=%s", graph, initializeNode, template, arguments);
-            template.instantiate(runtime, initializeNode, initializeNode, arguments);
+            template.instantiate(runtime, initializeNode, arguments);
         }
     }
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/StructuredGraph.java	Fri Aug 31 17:57:30 2012 +0200
@@ -298,13 +298,17 @@
         node.safeDelete();
     }
 
-    public void addAfterFixed(FixedWithNextNode node, FixedWithNextNode newNode) {
+    public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) {
         assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node;
-        assert newNode.next() == null;
         newNode.setProbability(node.probability());
         FixedNode next = node.next();
         node.setNext(newNode);
-        newNode.setNext(next);
+        if (next != null) {
+            assert newNode instanceof FixedWithNextNode;
+            FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode;
+            assert newFixedWithNext.next() == null;
+            newFixedWithNext.setNext(next);
+        }
     }
 
     public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) {
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/java/InstanceOfNode.java	Fri Aug 31 17:57:30 2012 +0200
@@ -31,7 +31,7 @@
 /**
  * The {@code InstanceOfNode} represents an instanceof test.
  */
-public final class InstanceOfNode extends BooleanNode implements Canonicalizable, LIRLowerable {
+public final class InstanceOfNode extends BooleanNode implements Canonicalizable, Lowerable, LIRLowerable {
 
     @Input private ValueNode object;
     @Input private ValueNode targetClassInstruction;
@@ -63,6 +63,11 @@
     }
 
     @Override
+    public void lower(LoweringTool tool) {
+        tool.getRuntime().lower(this, tool);
+    }
+
+    @Override
     public ValueNode canonical(CanonicalizerTool tool) {
         assert object() != null : this;
 
--- a/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.nodes/src/com/oracle/graal/nodes/spi/LoweringTool.java	Fri Aug 31 17:57:30 2012 +0200
@@ -28,10 +28,14 @@
 
 public interface LoweringTool {
     GraalCodeCacheProvider getRuntime();
-    ValueNode getGuardAnchor();
     ValueNode createNullCheckGuard(ValueNode object, long leafGraphId);
     ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, long leafGraphId);
     ValueNode createGuard(BooleanNode condition, DeoptimizationReason deoptReason, DeoptimizationAction action, boolean negated, long leafGraphId);
     Assumptions assumptions();
+
+    /**
+     * Gets the closest fixed node preceding the node currently being lowered.
+     */
+    FixedWithNextNode lastFixedNode();
 }
 
--- a/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/GraphPrinterDumpHandler.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.printer/src/com/oracle/graal/printer/GraphPrinterDumpHandler.java	Fri Aug 31 17:57:30 2012 +0200
@@ -83,7 +83,7 @@
             }
             TTY.println("Dumping IGV graphs to %s", fileName);
         } catch (IOException e) {
-            TTY.println("Faild to open %s to dump IGV graphs : %s", fileName, e);
+            TTY.println("Failed to open %s to dump IGV graphs : %s", fileName, e);
             failuresCount++;
             printer = null;
         }
--- a/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/InstanceOfTest.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.snippets.test/src/com/oracle/graal/snippets/InstanceOfTest.java	Fri Aug 31 17:57:30 2012 +0200
@@ -27,6 +27,7 @@
 import org.junit.*;
 
 import com.oracle.graal.api.meta.*;
+import com.oracle.graal.compiler.phases.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.java.*;
 
@@ -37,6 +38,11 @@
 public class InstanceOfTest extends TypeCheckTest {
 
     @Override
+    protected void editPhasePlan(ResolvedJavaMethod method, StructuredGraph graph, PhasePlan phasePlan) {
+        phasePlan.disablePhase(InliningPhase.class);
+    }
+
+    @Override
     protected void replaceProfile(StructuredGraph graph, JavaTypeProfile profile) {
         InstanceOfNode ion = graph.getNodes().filter(InstanceOfNode.class).first();
         if (ion != null) {
@@ -64,6 +70,15 @@
     }
 
     @Test
+    public void test2_1() {
+        test("isStringIntComplex",    profile(),                        "object");
+        test("isStringIntComplex",    profile(String.class),            "object");
+
+        test("isStringIntComplex",    profile(),                        Object.class);
+        test("isStringIntComplex",    profile(String.class),            Object.class);
+    }
+
+    @Test
     public void test3() {
         Throwable throwable = new Exception();
         test("isThrowable",    profile(),                             throwable);
@@ -76,6 +91,15 @@
     }
 
     @Test
+    public void test3_1() {
+        onlyFirstIsException(new Exception(), new Error());
+        test("onlyFirstIsException",    profile(),                             new Exception(), new Error());
+        test("onlyFirstIsException",    profile(),                             new Error(), new Exception());
+        test("onlyFirstIsException",    profile(),                             new Exception(), new Exception());
+        test("onlyFirstIsException",    profile(),                             new Error(), new Error());
+    }
+
+    @Test
     public void test4() {
         Throwable throwable = new Exception();
         test("isThrowableInt",    profile(),                             throwable);
@@ -117,19 +141,41 @@
 
     public static int isStringInt(Object o) {
         if (o instanceof String) {
-            return 1;
+            return id(0);
         }
-        return 0;
+        return id(0);
+    }
+
+    public static int isStringIntComplex(Object o) {
+        if (o instanceof String || o instanceof Integer) {
+            return id(o instanceof String ? 1 : 0);
+        }
+        return id(0);
+    }
+
+    public static int id(int value) {
+        return value;
     }
 
     public static boolean isThrowable(Object o) {
-        return o instanceof Throwable;
+        return ((Throwable) o) instanceof Exception;
     }
 
+    public static int onlyFirstIsException(Throwable t1, Throwable t2) {
+        if (t1 instanceof Exception ^ t2 instanceof Exception) {
+            return t1 instanceof Exception ? 1 : -1;
+        }
+        return -1;
+    }
+
+
     public static int isThrowableInt(Object o) {
         if (o instanceof Throwable) {
             return 1;
         }
+        if (o instanceof Throwable) {
+            return 2;
+        }
         return 0;
     }
 
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Fri Aug 31 17:57:30 2012 +0200
@@ -119,17 +119,25 @@
          * The name of this parameter.
          */
         String value();
+    }
 
+    /**
+     * Denotes a snippet parameter representing 0 or more arguments that will be bound during snippet
+     * template {@linkplain SnippetTemplate#instantiate instantiation}. During snippet template creation,
+     * its value must be an array whose length specifies the number of arguments (the contents
+     * of the array are ignored) bound to the parameter during {@linkplain SnippetTemplate#instantiate instantiation}.
+     *
+     * Such a parameter must be used in a counted loop in the snippet preceded by a call
+     * to {@link ExplodeLoopNode#explodeLoop()}. The counted looped must be a
+     * standard iteration over all the loop's elements (i.e. {@code for (T e : arr) ... }).
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.PARAMETER)
+    public @interface VarargsParameter {
         /**
-         * Determines if this parameter represents 0 or more arguments. During snippet template creation,
-         * its value must be an array whose length specifies the number of arguments (the contents
-         * of the array are ignored) bound to the parameter during {@linkplain SnippetTemplate#instantiate instantiation}.
-         *
-         * Such a parameter must be used in a counted loop in the snippet preceded by a call
-         * to {@link ExplodeLoopNode#explodeLoop()}. The counted looped must be a
-         * standard iteration over all the loop's elements (i.e. {@code for (T e : arr) ... }).
+         * The name of this parameter.
          */
-        boolean multiple() default false;
+        String value();
     }
 
     /**
@@ -146,18 +154,18 @@
     }
 
     /**
-     * Wrapper for the prototype value of a {@linkplain Parameter#multiple() multiple} parameter.
+     * Wrapper for the prototype value of a {@linkplain VarargsParameter varargs} parameter.
      */
-    public static class Multiple {
+    public static class Varargs {
         public final Object array;
         private final Class componentType;
         private final int length;
 
-        public static Multiple multiple(Class componentType, int length) {
-            return new Multiple(Array.newInstance(componentType, length));
+        public static Varargs vargargs(Class componentType, int length) {
+            return new Varargs(Array.newInstance(componentType, length));
         }
 
-        public Multiple(Object array) {
+        public Varargs(Object array) {
             assert array != null;
             this.componentType = array.getClass().getComponentType();
             assert this.componentType != null;
@@ -167,8 +175,8 @@
 
         @Override
         public boolean equals(Object obj) {
-            if (obj instanceof Multiple) {
-                Multiple other = (Multiple) obj;
+            if (obj instanceof Varargs) {
+                Varargs other = (Varargs) obj;
                 return other.componentType == componentType &&
                         other.length == length;
             }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java	Fri Aug 31 17:57:30 2012 +0200
@@ -35,13 +35,15 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.calc.*;
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.type.*;
 import com.oracle.graal.nodes.util.*;
 import com.oracle.graal.snippets.Snippet.ConstantParameter;
-import com.oracle.graal.snippets.Snippet.Multiple;
 import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.Snippet.Varargs;
+import com.oracle.graal.snippets.Snippet.VarargsParameter;
 import com.oracle.graal.snippets.nodes.*;
 
 /**
@@ -109,6 +111,10 @@
         public String toString() {
             return MetaUtil.format("%h.%n", method) + map.toString();
         }
+
+        public Set<String> names() {
+            return map.keySet();
+        }
     }
 
     /**
@@ -172,8 +178,28 @@
             }
             return template;
         }
+    }
 
-    }
+    public abstract static class AbstractTemplates<T extends SnippetsInterface> {
+        protected final Cache cache;
+        protected final CodeCacheProvider runtime;
+        protected Class<T> snippetsClass;
+        public AbstractTemplates(CodeCacheProvider runtime, Class<T> snippetsClass) {
+            this.runtime = runtime;
+            this.snippetsClass = snippetsClass;
+            this.cache = new Cache(runtime);
+        }
+
+        protected ResolvedJavaMethod snippet(String name, Class<?>... parameterTypes) {
+            try {
+                ResolvedJavaMethod snippet = runtime.getResolvedJavaMethod(snippetsClass.getDeclaredMethod(name, parameterTypes));
+                assert snippet.getAnnotation(Snippet.class) != null : "snippet is not annotated with @" + Snippet.class.getSimpleName();
+                return snippet;
+            } catch (NoSuchMethodException e) {
+                throw new GraalInternalError(e);
+            }
+        }
+}
 
     /**
      * Determines if any parameter of a given method is annotated with {@link ConstantParameter}.
@@ -202,7 +228,10 @@
         replacements.put(snippetGraph.start(), snippetCopy.start());
 
         int parameterCount = signature.argumentCount(false);
+        assert checkTemplate(key, parameterCount, method, signature);
+
         Parameter[] parameterAnnotations = new Parameter[parameterCount];
+        VarargsParameter[] varargsParameterAnnotations = new VarargsParameter[parameterCount];
         ConstantNode[] placeholders = new ConstantNode[parameterCount];
         for (int i = 0; i < parameterCount; i++) {
             ConstantParameter c = MetaUtil.getParameterAnnotation(ConstantParameter.class, i, method);
@@ -210,23 +239,19 @@
                 String name = c.value();
                 Object arg = key.get(name);
                 Kind kind = signature.argumentKindAt(i);
-                assert checkConstantArgument(method, signature, i, name, arg, kind);
                 replacements.put(snippetGraph.getLocal(i), ConstantNode.forConstant(Constant.forBoxed(kind, arg), runtime, snippetCopy));
             } else {
-                Parameter p = MetaUtil.getParameterAnnotation(Parameter.class, i, method);
-                assert p != null : method + ": parameter " + i + " must be annotated with either @" + ConstantParameter.class.getSimpleName() +
-                                " or @" + Parameter.class.getSimpleName();
-                String name = p.value();
-                if (p.multiple()) {
-                    Object multiple = key.get(name);
-                    assert multiple != null : method + ": requires a Multiple named " + name;
-                    assert checkMultipleArgument(method, signature, i, name, multiple);
-                    Object array = ((Multiple) multiple).array;
+                VarargsParameter vp = MetaUtil.getParameterAnnotation(VarargsParameter.class, i, method);
+                if (vp != null) {
+                    String name = vp.value();
+                    Object array = ((Varargs) key.get(name)).array;
                     ConstantNode placeholder = ConstantNode.forObject(array, runtime, snippetCopy);
                     replacements.put(snippetGraph.getLocal(i), placeholder);
                     placeholders[i] = placeholder;
+                    varargsParameterAnnotations[i] = vp;
+                } else {
+                    parameterAnnotations[i] = MetaUtil.getParameterAnnotation(Parameter.class, i, method);
                 }
-                parameterAnnotations[i] = p;
             }
         }
         snippetCopy.addDuplicates(snippetGraph.getNodes(), replacements);
@@ -242,34 +267,35 @@
         // Gather the template parameters
         parameters = new HashMap<>();
         for (int i = 0; i < parameterCount; i++) {
-            Parameter p = parameterAnnotations[i];
-            if (p != null) {
-                if (p.multiple()) {
-                    assert snippetCopy.getLocal(i) == null;
-                    Object array = ((Multiple) key.get(p.value())).array;
-                    int length = Array.getLength(array);
-                    LocalNode[] locals = new LocalNode[length];
-                    Stamp stamp = StampFactory.forKind(runtime.getResolvedJavaType(array.getClass().getComponentType()).kind());
-                    for (int j = 0; j < length; j++) {
-                        assert (parameterCount & 0xFFFF) == parameterCount;
-                        int idx = i << 16 | j;
-                        LocalNode local = snippetCopy.unique(new LocalNode(idx, stamp));
-                        locals[j] = local;
+            VarargsParameter vp = varargsParameterAnnotations[i];
+            if (vp != null) {
+                assert snippetCopy.getLocal(i) == null;
+                Object array = ((Varargs) key.get(vp.value())).array;
+                int length = Array.getLength(array);
+                LocalNode[] locals = new LocalNode[length];
+                Stamp stamp = StampFactory.forKind(runtime.getResolvedJavaType(array.getClass().getComponentType()).kind());
+                for (int j = 0; j < length; j++) {
+                    assert (parameterCount & 0xFFFF) == parameterCount;
+                    int idx = i << 16 | j;
+                    LocalNode local = snippetCopy.unique(new LocalNode(idx, stamp));
+                    locals[j] = local;
+                }
+                parameters.put(vp.value(), locals);
+
+                ConstantNode placeholder = placeholders[i];
+                assert placeholder != null;
+                for (Node usage : placeholder.usages().snapshot()) {
+                    if (usage instanceof LoadIndexedNode) {
+                        LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
+                        Debug.dump(snippetCopy, "Before replacing %s", loadIndexed);
+                        LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(locals, loadIndexed.index(), loadIndexed.stamp()));
+                        snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
+                        Debug.dump(snippetCopy, "After replacing %s", loadIndexed);
                     }
-                    parameters.put(p.value(), locals);
-
-                    ConstantNode placeholder = placeholders[i];
-                    assert placeholder != null;
-                    for (Node usage : placeholder.usages().snapshot()) {
-                        if (usage instanceof LoadIndexedNode) {
-                            LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
-                            Debug.dump(snippetCopy, "Before replacing %s", loadIndexed);
-                            LoadSnippetParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetParameterNode(locals, loadIndexed.index(), loadIndexed.stamp()));
-                            snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
-                            Debug.dump(snippetCopy, "After replacing %s", loadIndexed);
-                        }
-                    }
-                } else {
+                }
+            } else {
+                Parameter p = parameterAnnotations[i];
+                if (p != null) {
                     LocalNode local = snippetCopy.getLocal(i);
                     assert local != null;
                     parameters.put(p.value(), local);
@@ -314,26 +340,56 @@
 
         new DeadCodeEliminationPhase().apply(snippetCopy);
 
-        assert checkAllMultipleParameterPlaceholdersAreDeleted(parameterCount, placeholders);
+        assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders);
+
+        this.snippet = snippetCopy;
+        ReturnNode retNode = null;
+        StartNode entryPointNode = snippet.start();
+
+        Map<Integer, JumpNode[]> jumpsMap = new HashMap<>();
+        for (JumpNode jump : snippet.getNodes().filter(JumpNode.class).snapshot()) {
+            FixedNode next = jump.next();
 
-        this.graph = snippetCopy;
-        nodes = new ArrayList<>(graph.getNodeCount());
-        ReturnNode retNode = null;
-        StartNode entryPointNode = graph.start();
-        for (Node node : graph.getNodes()) {
+            // Remove the nodes after the jump
+            jump.setNext(null);
+            GraphUtil.killCFG(next);
+            JumpNode[] jumpsForIndex = jumpsMap.get(jump.successorIndex());
+            if (jumpsForIndex == null) {
+                jumpsMap.put(jump.successorIndex(), new JumpNode[] {jump});
+            } else {
+                jumpsForIndex = Arrays.copyOf(jumpsForIndex, jumpsForIndex.length + 1);
+                jumpsForIndex[jumpsForIndex.length - 1] = jump;
+                jumpsMap.put(jump.successorIndex(), jumpsForIndex);
+            }
+        }
+
+        this.jumps = new JumpNode[jumpsMap.size()][];
+        for (Map.Entry<Integer, JumpNode[]> e : jumpsMap.entrySet()) {
+            int successorIndex = e.getKey();
+            assert successorIndex >= 0 && successorIndex < this.jumps.length;
+            assert this.jumps[successorIndex] == null;
+            this.jumps[successorIndex] = e.getValue();
+        }
+
+        new DeadCodeEliminationPhase().apply(snippetCopy);
+
+        nodes = new ArrayList<>(snippet.getNodeCount());
+        for (Node node : snippet.getNodes()) {
             if (node == entryPointNode || node == entryPointNode.stateAfter()) {
                 // Do nothing.
             } else {
                 nodes.add(node);
                 if (node instanceof ReturnNode) {
+                    assert this.jumps.length == 0 : "snippet with Jump node(s) cannot have a return node";
                     retNode = (ReturnNode) node;
                 }
             }
         }
+
         this.returnNode = retNode;
     }
 
-    private static boolean checkAllMultipleParameterPlaceholdersAreDeleted(int parameterCount, ConstantNode[] placeholders) {
+    private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, ConstantNode[] placeholders) {
         for (int i = 0; i < parameterCount; i++) {
             if (placeholders[i] != null) {
                 assert placeholders[i].isDeleted() : placeholders[i];
@@ -354,12 +410,11 @@
         return true;
     }
 
-    private static boolean checkMultipleArgument(final ResolvedJavaMethod method, Signature signature, int i, String name, Object multiple) {
-        assert multiple instanceof Multiple;
-        Object arg = ((Multiple) multiple).array;
+    private static boolean checkVarargs(final ResolvedJavaMethod method, Signature signature, int i, String name, Varargs varargs) {
+        Object arg = varargs.array;
         ResolvedJavaType type = (ResolvedJavaType) signature.argumentTypeAt(i, method.holder());
         Class< ? > javaType = type.toJava();
-        assert javaType.isArray() : "multiple parameter must be an array type";
+        assert javaType.isArray() : "varargs parameter must be an array type";
         assert javaType.isInstance(arg) : "value for " + name + " is not a " + javaType.getName() + " instance: " + arg;
         return true;
     }
@@ -367,7 +422,7 @@
     /**
      * The graph built from the snippet method.
      */
-    private final StructuredGraph graph;
+    private final StructuredGraph snippet;
 
     /**
      * The named parameters of this template that must be bound to values during instantiation.
@@ -386,6 +441,12 @@
     private final ArrayList<Node> nodes;
 
     /**
+     * The {@link JumpNode}s in the snippet, indexed by {@linkplain ControlSplitNode#blockSuccessor(int) successor} indexes.
+     * There may be more than one jump per successor index which explains why this is a 2-dimensional array.
+     */
+    private final JumpNode[][] jumps;
+
+    /**
      * Gets the instantiation-time bindings to this template's parameters.
      *
      * @return the map that will be used to bind arguments to parameters when inlining this template
@@ -427,32 +488,30 @@
     }
 
     /**
-     * Replaces a given node with this specialized snippet.
+     * Replaces a given fixed node with this specialized snippet.
      *
      * @param runtime
      * @param replacee the node that will be replaced
-     * @param anchor the control flow replacee
      * @param args the arguments to be bound to the flattened positional parameters of the snippet
      */
     public void instantiate(CodeCacheProvider runtime,
-                    Node replacee,
-                    FixedWithNextNode anchor, SnippetTemplate.Arguments args) {
+                    FixedWithNextNode replacee, SnippetTemplate.Arguments args) {
 
         // Inline the snippet nodes, replacing parameters with the given args in the process
-        String name = graph.name == null ? "{copy}" : graph.name + "{copy}";
-        StructuredGraph graphCopy = new StructuredGraph(name, graph.method());
-        StartNode entryPointNode = graph.start();
+        String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}";
+        StructuredGraph snippetCopy = new StructuredGraph(name, snippet.method());
+        StartNode entryPointNode = snippet.start();
         FixedNode firstCFGNode = entryPointNode.next();
         StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph();
         IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, runtime, args);
         Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, replacements);
-        Debug.dump(replaceeGraph, "After inlining snippet %s", graphCopy.method());
+        Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method());
 
         // Re-wire the control flow graph around the replacee
         FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
-        anchor.replaceAtPredecessor(firstCFGNodeDuplicate);
-        FixedNode next = anchor.next();
-        anchor.setNext(null);
+        replacee.replaceAtPredecessor(firstCFGNodeDuplicate);
+        FixedNode next = replacee.next();
+        replacee.setNext(null);
 
         // Replace all usages of the replacee with the value returned by the snippet
         Node returnValue = null;
@@ -473,21 +532,120 @@
         // Remove the replacee from its graph
         replacee.clearInputs();
         replacee.replaceAtUsages(null);
-        if (replacee instanceof FixedNode) {
-            GraphUtil.killCFG((FixedNode) replacee);
+            GraphUtil.killCFG(replacee);
+
+        Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this);
+    }
+
+    /**
+     * Replaces a given floating node with this specialized snippet.
+     *
+     * @param runtime
+     * @param replacee the node that will be replaced
+     * @param lastFixedNode the CFG of the snippet is inserted after this node
+     * @param args the arguments to be bound to the flattened positional parameters of the snippet
+     */
+    public void instantiate(CodeCacheProvider runtime,
+                    FloatingNode replacee,
+                    FixedWithNextNode lastFixedNode, SnippetTemplate.Arguments args) {
+
+        // Inline the snippet nodes, replacing parameters with the given args in the process
+        String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}";
+        StructuredGraph snippetCopy = new StructuredGraph(name, snippet.method());
+        StartNode entryPointNode = snippet.start();
+        FixedNode firstCFGNode = entryPointNode.next();
+        StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph();
+        IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, runtime, args);
+        Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, replacements);
+        Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method());
+
+        assert lastFixedNode != null : replaceeGraph;
+        FixedNode next = lastFixedNode.next();
+        lastFixedNode.setNext(null);
+        FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
+        replaceeGraph.addAfterFixed(lastFixedNode, firstCFGNodeDuplicate);
+
+        // Replace all usages of the replacee with the value returned by the snippet
+        assert returnNode != null : replaceeGraph;
+        Node returnValue = null;
+        if (returnNode.result() instanceof LocalNode) {
+            returnValue = replacements.get(returnNode.result());
         } else {
-            GraphUtil.killWithUnusedFloatingInputs(replacee);
+            returnValue = duplicates.get(returnNode.result());
         }
-        if (anchor != replacee) {
-            GraphUtil.killCFG(anchor);
-        }
+        assert returnValue != null || replacee.usages().isEmpty();
+        replacee.replaceAtUsages(returnValue);
+
+        Node returnDuplicate = duplicates.get(returnNode);
+        returnDuplicate.clearInputs();
+        returnDuplicate.replaceAndDelete(next);
 
         Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this);
     }
 
+    /**
+     * Replaces a given floating node that is an input to a {@link ControlSplitNode} with this specialized snippet.
+     * The {@linkplain JumpNode jumps} in the snippet are connected to the successors of the control split node.
+     *
+     * @param replacee the node that will be replaced
+     * @param controlSplitNode the node replaced by this wheCFG of the snippet is inserted after this node
+     * @param args the arguments to be bound to the flattened positional parameters of the snippet
+     */
+    public void instantiate(CodeCacheProvider runtime,
+                    FloatingNode replacee,
+                    ControlSplitNode controlSplitNode,
+                    SnippetTemplate.Arguments args) {
+
+        // Inline the snippet nodes, replacing parameters with the given args in the process
+        String name = snippet.name == null ? "{copy}" : snippet.name + "{copy}";
+        StructuredGraph snippetCopy = new StructuredGraph(name, snippet.method());
+        StartNode entryPointNode = snippet.start();
+        FixedNode firstCFGNode = entryPointNode.next();
+        StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph();
+        IdentityHashMap<Node, Node> replacements = bind(replaceeGraph, runtime, args);
+        Map<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, replacements);
+        Debug.dump(replaceeGraph, "After inlining snippet %s", snippetCopy.method());
+
+
+        int successorIndex = 0;
+        for (JumpNode[] jumpsForIndex : jumps) {
+            fixEdge(controlSplitNode, jumpsForIndex, successorIndex++, duplicates);
+        }
+
+        FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
+        controlSplitNode.replaceAtPredecessor(firstCFGNodeDuplicate);
+        controlSplitNode.replaceAtUsages(null);
+
+        assert returnNode == null : replaceeGraph;
+        GraphUtil.killCFG(controlSplitNode);
+
+        Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this);
+    }
+
+    private static void fixEdge(ControlSplitNode splitAnchor, JumpNode[] jumpsForIndex, int successorIndex, Map<Node, Node> duplicates) {
+        BeginNode blockSuccessor = splitAnchor.blockSuccessor(successorIndex);
+        splitAnchor.setBlockSuccessor(successorIndex, null);
+        if (jumpsForIndex.length == 1) {
+            JumpNode jump = (JumpNode) duplicates.get(jumpsForIndex[0]);
+            jump.replaceAtPredecessor(blockSuccessor);
+            GraphUtil.killCFG(jump);
+        } else {
+            StructuredGraph graph = (StructuredGraph) splitAnchor.graph();
+            MergeNode merge = graph.add(new MergeNode());
+            for (int i = 0; i < jumpsForIndex.length; i++) {
+                EndNode end = graph.add(new EndNode());
+                JumpNode jump = (JumpNode) duplicates.get(jumpsForIndex[i]);
+                jump.replaceAtPredecessor(end);
+                merge.addForwardEnd(end);
+                GraphUtil.killCFG(jump);
+            }
+            merge.setNext(blockSuccessor);
+        }
+    }
+
     @Override
     public String toString() {
-        StringBuilder buf = new StringBuilder(graph.toString()).append('(');
+        StringBuilder buf = new StringBuilder(snippet.toString()).append('(');
         String sep = "";
         for (Map.Entry<String, Object> e : parameters.entrySet()) {
             String name = e.getKey();
@@ -505,5 +663,45 @@
         }
         return buf.append(')').toString();
     }
+
+    private static boolean checkTemplate(SnippetTemplate.Key key, int parameterCount, ResolvedJavaMethod method, Signature signature) {
+        Set<String> expected = new HashSet<>();
+        for (int i = 0; i < parameterCount; i++) {
+            ConstantParameter c = MetaUtil.getParameterAnnotation(ConstantParameter.class, i, method);
+            VarargsParameter vp = MetaUtil.getParameterAnnotation(VarargsParameter.class, i, method);
+            Parameter p = MetaUtil.getParameterAnnotation(Parameter.class, i, method);
+            if (c != null) {
+                assert vp == null && p == null;
+                String name = c.value();
+                expected.add(name);
+                Kind kind = signature.argumentKindAt(i);
+                assert key.names().contains(name) : "key for " + method + " is missing \"" + name + "\": " + key;
+                assert checkConstantArgument(method, signature, i, c.value(), key.get(name), kind);
+            } else if (vp != null) {
+                assert p == null;
+                String name = vp.value();
+                expected.add(name);
+                assert key.names().contains(name) : "key for " + method + " is missing \"" + name + "\": " + key;
+                assert key.get(name) instanceof Varargs;
+                Varargs varargs = (Varargs) key.get(name);
+                assert checkVarargs(method, signature, i, name, varargs);
+            } else {
+                assert p != null : method + ": parameter " + i + " must be annotated with exactly one of " +
+                    "@" + ConstantParameter.class.getSimpleName() + " or " +
+                    "@" + VarargsParameter.class.getSimpleName() + " or " +
+                    "@" + Parameter.class.getSimpleName();
+            }
+        }
+        if (!key.names().containsAll(expected)) {
+            expected.removeAll(key.names());
+            assert false : expected + " missing from key " + key;
+        }
+        if (!expected.containsAll(key.names())) {
+            Set<String> namesCopy = new HashSet<>(key.names());
+            namesCopy.removeAll(expected);
+            assert false : "parameter(s) " + namesCopy + " should be annotated with @" + ConstantParameter.class.getSimpleName() +
+                " or @" + VarargsParameter.class.getSimpleName() + " in " + MetaUtil.format("%H.%n(%p)", method);
+        }
+        return true;
+    }
 }
-
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/ExplodeLoopNode.java	Wed Aug 29 13:05:43 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/ExplodeLoopNode.java	Fri Aug 31 17:57:30 2012 +0200
@@ -27,13 +27,13 @@
 import com.oracle.graal.graph.*;
 import com.oracle.graal.nodes.*;
 import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.Snippet.VarargsParameter;
 
 /**
  * Placeholder node to denote to snippet preparation that the following loop
  * must be completely unrolled.
  *
- * @see Parameter#multiple()
+ * @see VarargsParameter
  */
 public final class ExplodeLoopNode extends FixedWithNextNode {
 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/JumpNode.java	Fri Aug 31 17:57:30 2012 +0200
@@ -0,0 +1,67 @@
+/*
+ * 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.snippets.nodes;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.type.*;
+
+/**
+ * Delimits a control flow path in a snippet that will be connected to a
+ * {@link ControlSplitNode} successor upon snippet instantiation.
+ * This node can only appear in snippets with a void return type.
+ */
+public class JumpNode extends FixedWithNextNode {
+
+    /**
+     * Index of {@link ControlSplitNode} successor to which this label will be connected.
+     */
+    private final int successorIndex;
+
+    public JumpNode(int successorIndex) {
+        super(StampFactory.forVoid());
+        this.successorIndex = successorIndex;
+    }
+
+    public int successorIndex() {
+        return successorIndex;
+    }
+
+    @Override
+    public String toString(Verbosity verbosity) {
+        if (verbosity == Verbosity.Name) {
+            return super.toString(Verbosity.Name) + "{" + successorIndex() + "}";
+        } else {
+            return super.toString(verbosity);
+        }
+    }
+
+    /**
+     * There must be a return statement immediately following a call to this method.
+     *
+     * @param successorIndex e.g. {@link IfNode#TRUE_EDGE}
+     */
+    @NodeIntrinsic
+    public static void jump(@ConstantNodeParameter int successorIndex) {
+        throw new UnsupportedOperationException();
+    }
+}
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/LoadSnippetParameterNode.java	Wed Aug 29 13:05:43 2012 +0200
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,52 +0,0 @@
-/*
- * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
- * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
- *
- * This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- *
- * You should have received a copy of the GNU General Public License version
- * 2 along with this work; if not, write to the Free Software Foundation,
- * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
- * or visit www.oracle.com if you need additional information or have any
- * questions.
- */
-package com.oracle.graal.snippets.nodes;
-
-import com.oracle.graal.nodes.*;
-import com.oracle.graal.nodes.spi.*;
-import com.oracle.graal.nodes.type.*;
-import com.oracle.graal.snippets.Snippet.Parameter;
-
-/**
- * Implements the semantics of a snippet {@link Parameter} whose {@link Parameter#multiple()} element is {@code true}.
- */
-public final class LoadSnippetParameterNode extends FixedWithNextNode implements Canonicalizable {
-
-    @Input private ValueNode index;
-
-    private final LocalNode[] locals;
-
-    public LoadSnippetParameterNode(LocalNode[] locals, ValueNode index, Stamp stamp) {
-        super(stamp);
-        this.index = index;
-        this.locals = locals;
-    }
-
-    @Override
-    public ValueNode canonical(CanonicalizerTool tool) {
-        if (index.isConstant()) {
-            return locals[index.asConstant().asInt()];
-        }
-        return this;
-    }
-}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/LoadSnippetVarargParameterNode.java	Fri Aug 31 17:57:30 2012 +0200
@@ -0,0 +1,52 @@
+/*
+ * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+package com.oracle.graal.snippets.nodes;
+
+import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.spi.*;
+import com.oracle.graal.nodes.type.*;
+import com.oracle.graal.snippets.Snippet.VarargsParameter;
+
+/**
+ * Implements the semantics of {@link VarargsParameter}.
+ */
+public final class LoadSnippetVarargParameterNode extends FixedWithNextNode implements Canonicalizable {
+
+    @Input private ValueNode index;
+
+    private final LocalNode[] locals;
+
+    public LoadSnippetVarargParameterNode(LocalNode[] locals, ValueNode index, Stamp stamp) {
+        super(stamp);
+        this.index = index;
+        this.locals = locals;
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        if (index.isConstant()) {
+            return locals[index.asConstant().asInt()];
+        }
+        return this;
+    }
+}