changeset 5496:80abea6e5e27

Merge.
author Thomas Wuerthinger <thomas.wuerthinger@oracle.com>
date Wed, 06 Jun 2012 17:20:15 +0200
parents c78cf0abfdff (current diff) e26e6dca0bcf (diff)
children a4dfee0b8fbd
files
diffstat 8 files changed, 552 insertions(+), 341 deletions(-) [+]
line wrap: on
line diff
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/NodeBitMap.java	Wed Jun 06 17:20:15 2012 +0200
@@ -24,9 +24,11 @@
 
 import java.util.*;
 
+import com.oracle.graal.graph.iterators.*;
 
 
-public final class NodeBitMap implements Iterable<Node>{
+
+public final class NodeBitMap extends NodeIterable<Node>{
     private final boolean autoGrow;
     private final BitMap bitMap;
     private final Graph graph;
@@ -186,4 +188,19 @@
     public NodeBitMap copy() {
         return new NodeBitMap(graph, autoGrow, bitMap.copy());
     }
+
+    @Override
+    public NodeIterable<Node> distinct() {
+        return this;
+    }
+
+    @Override
+    public int count() {
+        return bitMap.cardinality();
+    }
+
+    @Override
+    public boolean contains(Node node) {
+        return isMarked(node);
+    }
 }
--- a/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/iterators/NodeIterable.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.graph/src/com/oracle/graal/graph/iterators/NodeIterable.java	Wed Jun 06 17:20:15 2012 +0200
@@ -46,7 +46,7 @@
     public FilteredNodeIterable<T> nonNull() {
         return new FilteredNodeIterable<>(this).and(NodePredicates.isNotNull());
     }
-    public FilteredNodeIterable<T> distinct() {
+    public NodeIterable<T> distinct() {
         return new FilteredNodeIterable<>(this).distinct();
     }
     public List<T> snapshot() {
--- a/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.hotspot/src/com/oracle/graal/hotspot/snippets/CheckCastSnippets.java	Wed Jun 06 17:20:15 2012 +0200
@@ -23,12 +23,11 @@
 package com.oracle.graal.hotspot.snippets;
 import static com.oracle.graal.hotspot.snippets.ArrayCopySnippets.*;
 import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.Counter.*;
-import static com.oracle.graal.hotspot.snippets.CheckCastSnippets.TemplateFlag.*;
-import static com.oracle.graal.snippets.SnippetTemplate.*;
+import static com.oracle.graal.snippets.Snippet.Arguments.*;
+import static com.oracle.graal.snippets.Snippet.Multiple.*;
 
 import java.io.*;
 import java.util.*;
-import java.util.concurrent.*;
 
 import sun.misc.*;
 
@@ -44,6 +43,10 @@
 import com.oracle.graal.nodes.extended.*;
 import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.snippets.*;
+import com.oracle.graal.snippets.Snippet.Arguments;
+import com.oracle.graal.snippets.Snippet.Constant;
+import com.oracle.graal.snippets.Snippet.Parameter;
+import com.oracle.graal.snippets.SnippetTemplate.Cache;
 import com.oracle.graal.snippets.nodes.*;
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.cri.ri.*;
@@ -53,7 +56,7 @@
  * 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 paper <a href="http://dl.acm.org/citation.cfm?id=583821">
+ * checks 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 {
@@ -62,7 +65,7 @@
      * Type test used when the type being tested against is a final type.
      */
     @Snippet
-    public static Object checkcastExact(Object object, Object exactHub, boolean checkNull) {
+    public static Object checkcastExact(@Parameter("object") Object object, @Parameter("exactHub") Object exactHub, @Constant("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             return object;
         }
@@ -81,7 +84,7 @@
      * first.
      */
     @Snippet
-    public static Object checkcastPrimary(Object hub, Object object, boolean checkNull, int superCheckOffset) {
+    public static Object checkcastPrimary(@Parameter("hub") Object hub, @Parameter("object") Object object, @Constant("checkNull") boolean checkNull, @Constant("superCheckOffset") int superCheckOffset) {
         if (checkNull && object == null) {
             return object;
         }
@@ -96,15 +99,15 @@
      * Type test used when the type being tested against is a restricted secondary type.
      */
     @Snippet
-    public static Object checkcastSecondary(Object hub, Object object, Object[] hintHubs, boolean checkNull) {
+    public static Object checkcastSecondary(@Parameter("hub") Object hub, @Parameter("object") Object object, @Parameter(value = "hints", multiple = true) Object[] hints, @Constant("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             return object;
         }
         Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
         // if we get an exact match: succeed immediately
         ExplodeLoopNode.explodeLoop();
-        for (int i = 0; i < hintHubs.length; i++) {
-            Object hintHub = hintHubs[i];
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
             if (hintHub == objectHub) {
                 return object;
             }
@@ -120,15 +123,15 @@
      * in an object array store check).
      */
     @Snippet
-    public static Object checkcastUnknown(Object hub, Object object, Object[] hintHubs, boolean checkNull) {
+    public static Object checkcastUnknown(@Parameter("hub") Object hub, @Parameter("object") Object object, @Parameter(value = "hints", multiple = true) Object[] hints, @Constant("checkNull") boolean checkNull) {
         if (checkNull && object == null) {
             return object;
         }
         Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
         // if we get an exact match: succeed immediately
         ExplodeLoopNode.explodeLoop();
-        for (int i = 0; i < hintHubs.length; i++) {
-            Object hintHub = hintHubs[i];
+        for (int i = 0; i < hints.length; i++) {
+            Object hintHub = hints[i];
             if (hintHub == objectHub) {
                 return object;
             }
@@ -247,13 +250,13 @@
      * Type test used when {@link GraalOptions#CheckcastCounters} is enabled.
      */
     @Snippet
-    public static Object checkcastCounters(Object hub, Object object, Object[] hintHubs, boolean hintsAreExact) {
+    public static Object checkcastCounters(@Parameter("hub") Object hub, @Parameter("object") Object object, @Parameter(value = "hints", multiple = true) Object[] hints, @Constant("hintsAreExact") boolean hintsAreExact) {
         if (object == null) {
             isNull.inc();
             return object;
         }
         Object objectHub = UnsafeLoadNode.loadObject(object, 0, hubOffset(), true);
-        if (hintHubs.length == 0) {
+        if (hints.length == 0) {
             noHints.inc();
             if (!checkUnknownSubType(hub, objectHub)) {
                 exception.inc();
@@ -262,8 +265,8 @@
         } else {
             // if we get an exact match: succeed immediately
             ExplodeLoopNode.explodeLoop();
-            for (int i = 0; i < hintHubs.length; i++) {
-                Object hintHub = hintHubs[i];
+            for (int i = 0; i < hints.length; i++) {
+                Object hintHub = hints[i];
                 if (hintHub == objectHub) {
                     if (hintsAreExact) {
                         exactType.inc();
@@ -343,43 +346,9 @@
         }
     }
 
-    public enum TemplateFlag {
-        CHECK_NULL,
-        EXACT_HINTS,
-        COUNTERS,
-        PRIMARY_SUPER,
-        SECONDARY_SUPER,
-        UNKNOWN_SUPER;
-
-        public int bit(boolean value) {
-            if (value) {
-                return bit();
-            }
-            return 0;
-        }
-
-        public boolean bool(int flags) {
-            return (flags & bit()) != 0;
-        }
-
-        static final int FLAGS_BITS = values().length;
-        static final int FLAGS_MASK = (1 << FLAGS_BITS) - 1;
-
-        static final int NHINTS_SHIFT = FLAGS_BITS;
-        static final int NHINTS_BITS = 3;
-        static final int SUPER_CHECK_OFFSET_SHIFT = NHINTS_SHIFT + NHINTS_BITS;
-
-        public int bit() {
-            return 1 << ordinal();
-        }
-    }
-
-    /**
-     * Templates for partially specialized checkcast snippet graphs.
-     */
     public static class Templates {
 
-        private final ConcurrentHashMap<Integer, SnippetTemplate> templates;
+        private final Cache cache;
         private final RiResolvedMethod exact;
         private final RiResolvedMethod primary;
         private final RiResolvedMethod secondary;
@@ -389,7 +358,7 @@
 
         public Templates(RiRuntime runtime) {
             this.runtime = runtime;
-            this.templates = new ConcurrentHashMap<>();
+            this.cache = new Cache(runtime);
             try {
                 exact = runtime.getRiMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastExact", Object.class, Object.class, boolean.class));
                 primary = runtime.getRiMethod(CheckCastSnippets.class.getDeclaredMethod("checkcastPrimary", Object.class, Object.class, boolean.class, int.class));
@@ -402,110 +371,52 @@
         }
 
         /**
-         * Interface for lazily creating a snippet template.
-         */
-        abstract static class Factory {
-            abstract SnippetTemplate create(HotSpotKlassOop[] hints, int flags);
-        }
-
-        /**
-         * Gets a template from the template cache, creating and installing it first if necessary.
-         */
-        private SnippetTemplate getTemplate(int nHints, int flags, int superCheckOffset, Factory factory) {
-            assert (flags & ~FLAGS_MASK) == 0;
-            assert nHints >= 0 && nHints < (1 << NHINTS_BITS) - 1 : "nHints out of range";
-            assert superCheckOffset >= 0 && superCheckOffset == ((superCheckOffset << SUPER_CHECK_OFFSET_SHIFT) >>> SUPER_CHECK_OFFSET_SHIFT) : "superCheckOffset out of range";
-            Integer key = superCheckOffset << SUPER_CHECK_OFFSET_SHIFT | nHints << NHINTS_SHIFT | flags;
-            SnippetTemplate result = templates.get(key);
-            if (result == null) {
-                HotSpotKlassOop[] hints = new HotSpotKlassOop[nHints];
-                for (int i = 0; i < hints.length; i++) {
-                    hints[i] = new HotSpotKlassOop(null, Templates.class);
-                }
-                result = factory.create(hints, flags);
-                //System.err.println(result);
-                templates.put(key, result);
-            }
-            return result;
-        }
-
-        private static HotSpotKlassOop[] createHintHubs(TypeCheckHints hints) {
-            HotSpotKlassOop[] hintHubs = new HotSpotKlassOop[hints.types.length];
-            for (int i = 0; i < hintHubs.length; i++) {
-                hintHubs[i] = ((HotSpotType) hints.types[i]).klassOop();
-            }
-            return hintHubs;
-        }
-
-        /**
          * Lowers a checkcast node.
          */
         public void lower(CheckCastNode checkcast, CiLoweringTool tool) {
             StructuredGraph graph = (StructuredGraph) checkcast.graph();
             ValueNode hub = checkcast.targetClassInstruction();
             ValueNode object = checkcast.object();
-            TypeCheckHints hints = new TypeCheckHints(checkcast.targetClass(), checkcast.profile(), tool.assumptions(), GraalOptions.CheckcastMinHintHitProbability, GraalOptions.CheckcastMaxHints);
-            Debug.log("Lowering checkcast in %s: node=%s, hintsHubs=%s, exact=%b", graph, checkcast, Arrays.toString(hints.types), hints.exact);
+            TypeCheckHints hintInfo = new TypeCheckHints(checkcast.targetClass(), checkcast.profile(), tool.assumptions(), GraalOptions.CheckcastMinHintHitProbability, GraalOptions.CheckcastMaxHints);
+            final HotSpotTypeResolvedImpl target = (HotSpotTypeResolvedImpl) checkcast.targetClass();
+            boolean checkNull = !object.stamp().nonNull();
+            Arguments arguments;
+            SnippetTemplate.Key key;
 
-            final HotSpotTypeResolvedImpl target = (HotSpotTypeResolvedImpl) checkcast.targetClass();
-            int flags = CHECK_NULL.bit(!object.stamp().nonNull());
             if (GraalOptions.CheckcastCounters) {
-                HotSpotKlassOop[] hintHubs = createHintHubs(hints);
-                SnippetTemplate template = getTemplate(hintHubs.length, flags | EXACT_HINTS.bit(hints.exact) | COUNTERS.bit(), 0, new Factory() {
-                    @SuppressWarnings("hiding")
-                    @Override
-                    SnippetTemplate create(HotSpotKlassOop[] hints, int flags) {
-                        // checkcastCounters(Object hub, Object object, Object[] hintHubs, boolean hintsAreExact)
-                        return SnippetTemplate.create(runtime, counters, _, _, hints, EXACT_HINTS.bool(flags));
-                    }
-                });
-                template.instantiate(runtime, checkcast, checkcast, hub, object, hintHubs, hints.exact);
+                HotSpotKlassOop[] hints = createHints(hintInfo);
+                key = new SnippetTemplate.Key(counters).add("hints", multiple(Object.class, hints.length)).add("hintsAreExact", hintInfo.exact);
+                arguments = arguments("hub", hub).add("object", object).add("hints", hints);
             } else if (target == null) {
-                HotSpotKlassOop[] hintHubs = createHintHubs(hints);
-                SnippetTemplate template = getTemplate(hintHubs.length, flags | UNKNOWN_SUPER.bit(), 0, new Factory() {
-                    @SuppressWarnings("hiding")
-                    @Override
-                    SnippetTemplate create(HotSpotKlassOop[] hints, int flags) {
-                        // checkcastUnknown(Object hub, Object object, Object[] hintHubs, boolean checkNull)
-                        return SnippetTemplate.create(runtime, unknown, _, _, hints, CHECK_NULL.bool(flags));
-                    }
-                });
-                template.instantiate(runtime, checkcast, checkcast, hub, object, hintHubs);
-            } else if (hints.exact) {
-                HotSpotKlassOop[] hintHubs = createHintHubs(hints);
-                assert hintHubs.length == 1;
-                SnippetTemplate template = getTemplate(hintHubs.length, flags | EXACT_HINTS.bit(), 0, new Factory() {
-                    @SuppressWarnings("hiding")
-                    @Override
-                    SnippetTemplate create(HotSpotKlassOop[] hints, int flags) {
-                        // checkcastExact(Object object, Object exactHub, boolean checkNull)
-                        return SnippetTemplate.create(runtime, exact, _, hints[0], CHECK_NULL.bool(flags));
-                    }
-                });
-                template.instantiate(runtime, checkcast, checkcast, object, hintHubs[0]);
+                HotSpotKlassOop[] hints = createHints(hintInfo);
+                key = new SnippetTemplate.Key(unknown).add("hints", multiple(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 SnippetTemplate.Key(exact).add("checkNull", checkNull);
+                arguments = arguments("object", object).add("exactHub", hints[0]);
             } else if (target.isPrimaryType()) {
-                SnippetTemplate template = getTemplate(0, flags | PRIMARY_SUPER.bit(), target.superCheckOffset(), new Factory() {
-                    @SuppressWarnings("hiding")
-                    @Override
-                    SnippetTemplate create(HotSpotKlassOop[] hints, int flags) {
-                        // checkcastPrimary(Object hub, Object object, boolean checkNull, int superCheckOffset)
-                        return SnippetTemplate.create(runtime, primary, _, _, CHECK_NULL.bool(flags), target.superCheckOffset());
-                    }
-                });
-                template.instantiate(runtime, checkcast, checkcast, hub, object);
+                key = new SnippetTemplate.Key(primary).add("checkNull", checkNull).add("superCheckOffset", target.superCheckOffset());
+                arguments = arguments("hub", hub).add("object", object);
             } else {
-                HotSpotKlassOop[] hintHubs = createHintHubs(hints);
-                SnippetTemplate template = getTemplate(hintHubs.length, flags | SECONDARY_SUPER.bit(), 0, new Factory() {
-                    @SuppressWarnings("hiding")
-                    @Override
-                    SnippetTemplate create(HotSpotKlassOop[] hints, int flags) {
-                        // checkcastSecondary(Object hub, Object object, Object[] hintHubs, boolean checkNull)
-                        return SnippetTemplate.create(runtime, secondary, _, _, hints, CHECK_NULL.bool(flags));
-                    }
-                });
-                template.instantiate(runtime, checkcast, checkcast, hub, object, hintHubs);
+                HotSpotKlassOop[] hints = createHints(hintInfo);
+                key = new SnippetTemplate.Key(secondary).add("hints", multiple(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);
             new DeadCodeEliminationPhase().apply(graph);
         }
+
+        private static HotSpotKlassOop[] createHints(TypeCheckHints hints) {
+            HotSpotKlassOop[] hintHubs = new HotSpotKlassOop[hints.types.length];
+            for (int i = 0; i < hintHubs.length; i++) {
+                hintHubs[i] = ((HotSpotType) hints.types[i]).klassOop();
+            }
+            return hintHubs;
+        }
     }
 }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/Snippet.java	Wed Jun 06 17:20:15 2012 +0200
@@ -23,8 +23,12 @@
 package com.oracle.graal.snippets;
 
 import java.lang.annotation.*;
+import java.lang.reflect.*;
+import java.util.*;
+import java.util.Map.Entry;
 
 import com.oracle.graal.graph.Node.NodeIntrinsic;
+import com.oracle.graal.snippets.nodes.*;
 
 /**
  * A snippet is a Graal graph expressed as a Java source method. Graal snippets can be used for:
@@ -37,4 +41,113 @@
 @Retention(RetentionPolicy.RUNTIME)
 @Target(ElementType.METHOD)
 public @interface Snippet {
+
+    /**
+     * Denotes a snippet parameter that will be bound during snippet
+     * template {@linkplain SnippetTemplate#instantiate instantiation}.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.PARAMETER)
+    public @interface Parameter {
+        /**
+         * The name of this parameter.
+         */
+        String value();
+
+        /**
+         * 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) ... }).
+         */
+        boolean multiple() default false;
+    }
+
+    /**
+     * Denotes a snippet parameter that will bound to a constant value during
+     * snippet template {@linkplain SnippetTemplate#instantiate instantiation}.
+     */
+    @Retention(RetentionPolicy.RUNTIME)
+    @Target(ElementType.PARAMETER)
+    public @interface Constant {
+        /**
+         * The name of this constant.
+         */
+        String value();
+    }
+
+    /**
+     * Arguments used to instantiate a template.
+     */
+    public static class Arguments implements Iterable<Map.Entry<String, Object>> {
+        private final HashMap<String, Object> map = new HashMap<>();
+
+        public static Arguments arguments(String name, Object value) {
+            return new Arguments().add(name, value);
+        }
+
+        public Arguments add(String name, Object value) {
+            assert !map.containsKey(name);
+            map.put(name, value);
+            return this;
+        }
+
+        public int length() {
+            return map.size();
+        }
+
+        @Override
+        public Iterator<Entry<String, Object>> iterator() {
+            return map.entrySet().iterator();
+        }
+
+        @Override
+        public String toString() {
+            return map.toString();
+        }
+    }
+
+    /**
+     * Wrapper for the prototype value of a {@linkplain Parameter#multiple() multiple} parameter.
+     */
+    public static class Multiple {
+        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 Multiple(Object array) {
+            assert array != null;
+            this.componentType = array.getClass().getComponentType();
+            assert this.componentType != null;
+            this.length = java.lang.reflect.Array.getLength(array);
+            this.array = array;
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Multiple) {
+                Multiple other = (Multiple) obj;
+                return other.componentType == componentType &&
+                        other.length == length;
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return componentType.hashCode() ^ length;
+        }
+
+        @Override
+        public String toString() {
+            return componentType.getName() + "[" + length + "]";
+        }
+    }
 }
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/SnippetTemplate.java	Wed Jun 06 17:20:15 2012 +0200
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2011, Oracle and/or its affiliates. All rights reserved.
+ * 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
@@ -24,194 +24,257 @@
 
 import java.lang.reflect.*;
 import java.util.*;
+import java.util.Map.Entry;
 import java.util.concurrent.*;
 
 import com.oracle.graal.compiler.loop.*;
 import com.oracle.graal.compiler.phases.*;
-import com.oracle.graal.compiler.phases.CanonicalizerPhase.IsImmutablePredicate;
 import com.oracle.graal.debug.*;
 import com.oracle.graal.graph.*;
 import com.oracle.graal.graph.Node.Verbosity;
 import com.oracle.graal.lir.cfg.*;
 import com.oracle.graal.nodes.*;
+import com.oracle.graal.nodes.java.*;
 import com.oracle.graal.nodes.util.*;
+import com.oracle.graal.snippets.Snippet.Arguments;
+import com.oracle.graal.snippets.Snippet.Constant;
+import com.oracle.graal.snippets.Snippet.Multiple;
+import com.oracle.graal.snippets.Snippet.Parameter;
 import com.oracle.graal.snippets.nodes.*;
 import com.oracle.max.cri.ci.*;
 import com.oracle.max.cri.ri.*;
 
 /**
  * A snippet template is a graph created by parsing a snippet method and then
- * specialized by binding constants to some of the snippet arguments and applying
- * transformations such as canonicalization and loop peeling.
+ * specialized by binding constants to the snippet's {@link Constant} parameters.
+ *
+ * Snippet templates can be managed in a {@link Cache}.
  */
 public class SnippetTemplate {
 
     /**
-     * Special value denoting a non-specialized argument.
+     * A snippet template key encapsulates the method from which a snippet was built
+     * and the arguments used to specialized the snippet.
+     *
+     * @see Cache
      */
-    public static final Object _ = new Object() {
+    public static class Key implements Iterable<Map.Entry<String, Object>> {
+        public final RiResolvedMethod method;
+        private final HashMap<String, Object> map = new HashMap<>();
+        private int hash;
+
+        public Key(RiResolvedMethod method) {
+            this.method = method;
+            this.hash = method.hashCode();
+        }
+
+        public Key add(String name, Object value) {
+            assert value != null;
+            assert !map.containsKey(name);
+            map.put(name, value);
+            hash = hash ^ name.hashCode() * (value.hashCode() + 1);
+            return this;
+        }
+
+        public int length() {
+            return map.size();
+        }
+
+        public Object get(String name) {
+            return map.get(name);
+        }
+
+        @Override
+        public Iterator<Entry<String, Object>> iterator() {
+            return map.entrySet().iterator();
+        }
+
+        @Override
+        public boolean equals(Object obj) {
+            if (obj instanceof Key) {
+                Key other = (Key) obj;
+                return other.method == method && other.map.equals(map);
+            }
+            return false;
+        }
+
+        @Override
+        public int hashCode() {
+            return hash;
+        }
+
         @Override
         public String toString() {
-            return "_";
+            return CiUtil.format("%h.%n", method) + map.toString();
+        }
+    }
+
+    /**
+     * A collection of snippet templates accessed by a {@link Key} instance.
+     */
+    public static class Cache {
+
+        private final ConcurrentHashMap<SnippetTemplate.Key, SnippetTemplate> templates = new ConcurrentHashMap<>();
+        private final RiRuntime runtime;
+
+
+        public Cache(RiRuntime runtime) {
+            this.runtime = runtime;
         }
-    };
+
+        /**
+         * Gets a template for a given key, creating it first if necessary.
+         */
+        public SnippetTemplate get(final SnippetTemplate.Key key) {
+            SnippetTemplate template = templates.get(key);
+            if (template == null) {
+                template = Debug.scope("SnippetSpecialization", key.method, new Callable<SnippetTemplate>() {
+                    @Override
+                    public SnippetTemplate call() throws Exception {
+                        return new SnippetTemplate(runtime, key);
+                    }
+                });
+                //System.out.println(key + " -> " + template);
+                templates.put(key, template);
+            }
+            return template;
+        }
+
+    }
 
     /**
      * Creates a snippet template.
-     *
-     * @param runtime
-     * @param method the snippet method to create the initial graph from
-     * @param args the arguments used to specialize the graph
      */
-    public static SnippetTemplate create(final RiRuntime runtime, final RiResolvedMethod method, final Object... args) {
-        return Debug.scope("SnippetSpecialization", method, new Callable<SnippetTemplate>() {
-            @Override
-            public SnippetTemplate call() throws Exception {
-                assert Modifier.isStatic(method.accessFlags()) : "snippet method must be static: " + method;
-                RiSignature signature = method.signature();
-                assert signature.argumentCount(false) == args.length : "snippet method expects " + signature.argumentCount(false) + " arguments, " + args.length + " given";
-                StructuredGraph snippetGraph = (StructuredGraph) method.compilerStorage().get(Graph.class);
-
-                final Map<CiConstant, CiConstant> constantArrays = new IdentityHashMap<>();
-                IsImmutablePredicate immutabilityPredicate = new IsImmutablePredicate() {
-                    public boolean apply(CiConstant constant) {
-                        return constantArrays.containsKey(constant);
-                    }
-                };
+    public SnippetTemplate(RiRuntime runtime, SnippetTemplate.Key key) {
+        RiResolvedMethod method = key.method;
+        assert Modifier.isStatic(method.accessFlags()) : "snippet method must be static: " + method;
+        RiSignature signature = method.signature();
 
-                // 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();
-                    Object arg = args[index];
-                    if (arg != _) {
-                        CiKind kind = signature.argumentKindAt(index, false);
-                        assert kind.isObject() || (arg != null && kind.toBoxedJavaClass() == arg.getClass()) :
-                            "arg " + index + " for " + method + " has wrong kind: expected " + kind + ", got " + (arg == null ? "null" : arg.getClass().getSimpleName());
-                        CiConstant specializationArg = CiConstant.forBoxed(kind, arg);
-                        if (kind.isObject()) {
-                            assert arg == null || signature.argumentTypeAt(index, method.holder()).resolve(method.holder()).toJava().isInstance(arg) :
-                                String.format("argument %d is of wrong type: expected %s, got %s", index, signature.argumentTypeAt(index, method.holder()).resolve(method.holder()).toJava().getName(), arg.getClass().getName());
-                            if (arg != null) {
-                                if (arg.getClass().isArray()) {
-                                    constantArrays.put(specializationArg, specializationArg);
-                                }
-                            }
-                        }
+        // Copy snippet graph, replacing @Constant parameters with given arguments
+        StructuredGraph snippetGraph = (StructuredGraph) method.compilerStorage().get(Graph.class);
+        StructuredGraph snippetCopy = new StructuredGraph(snippetGraph.name, snippetGraph.method());
+        IdentityHashMap<Node, Node> replacements = new IdentityHashMap<>();
+        replacements.put(snippetGraph.start(), snippetCopy.start());
 
-                        ConstantNode argNode = ConstantNode.forCiConstant(specializationArg, runtime, snippetCopy);
-                        replacements.put(local, argNode);
-                    }
-                    localCount++;
+        int parameterCount = signature.argumentCount(false);
+        Parameter[] parameterAnnotations = new Parameter[parameterCount];
+        for (int i = 0; i < parameterCount; i++) {
+            Constant c = CiUtil.getParameterAnnotation(Constant.class, i, method);
+            if (c != null) {
+                String name = c.value();
+                Object arg = key.get(name);
+                assert arg != null : method + ": requires a constant named " + name;
+                CiKind kind = signature.argumentKindAt(i, false);
+                assert checkConstantArgument(method, signature, i, name, arg, kind);
+                replacements.put(snippetGraph.getLocal(i), ConstantNode.forCiConstant(CiConstant.forBoxed(kind, arg), runtime, snippetCopy));
+            } else {
+                Parameter p = CiUtil.getParameterAnnotation(Parameter.class, i, method);
+                assert p != null : method + ": parameter " + i + " must be annotated with either @Constant or @Parameter";
+                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;
+                    replacements.put(snippetGraph.getLocal(i), ConstantNode.forObject(array, runtime, snippetCopy));
                 }
-                assert localCount == args.length : "snippet argument count mismatch";
-                snippetCopy.addDuplicates(snippetGraph.getNodes(), replacements);
-
-                Debug.dump(snippetCopy, "Before specialization");
-
-                if (!replacements.isEmpty()) {
-                    new CanonicalizerPhase(null, runtime, null, 0, immutabilityPredicate).apply(snippetCopy);
-                }
+                parameterAnnotations[i] = p;
+            }
+        }
+        snippetCopy.addDuplicates(snippetGraph.getNodes(), replacements);
 
-                boolean exploded = false;
-                do {
-                    exploded = false;
-                    for (Node node : snippetCopy.getNodes()) {
-                        if (node instanceof ExplodeLoopNode) {
-                            final ExplodeLoopNode explodeLoop = (ExplodeLoopNode) node;
-                            LoopBeginNode loopBegin = explodeLoop.findLoopBegin();
-                            ControlFlowGraph cfg = ControlFlowGraph.compute(snippetCopy, true, true, false, false);
-                            for (Loop loop : cfg.getLoops()) {
-                                if (loop.loopBegin() == loopBegin) {
-                                    SuperBlock wholeLoop = LoopTransformUtil.wholeLoop(loop);
-                                    Debug.dump(snippetCopy, "Before exploding loop %s", loopBegin);
-                                    int peel = 0;
-                                    while (!loopBegin.isDeleted()) {
-                                        int mark = snippetCopy.getMark();
-                                        LoopTransformUtil.peel(loop, wholeLoop);
-                                        Debug.dump(snippetCopy, "After peel %d", peel);
-                                        new CanonicalizerPhase(null, runtime, null, mark, immutabilityPredicate).apply(snippetCopy);
-                                        peel++;
-                                    }
-                                    Debug.dump(snippetCopy, "After exploding loop %s", loopBegin);
-                                    exploded = true;
-                                    break;
-                                }
-                            }
+        Debug.dump(snippetCopy, "Before specialization");
+        if (!replacements.isEmpty()) {
+            new CanonicalizerPhase(null, runtime, null, 0, null).apply(snippetCopy);
+        }
 
-                            FixedNode explodeLoopNext = explodeLoop.next();
-                            explodeLoop.clearSuccessors();
-                            explodeLoop.replaceAtPredecessors(explodeLoopNext);
-                            explodeLoop.replaceAtUsages(null);
-                            GraphUtil.killCFG(explodeLoop);
+        // Gather the template parameters
+        parameters = new HashMap<>();
+        for (int i = 0; i < parameterCount; i++) {
+            Parameter p = parameterAnnotations[i];
+            if (p != null) {
+                ValueNode parameter;
+                if (p.multiple()) {
+                    parameter = null;
+                    assert snippetCopy.getLocal(i) == null;
+                    ConstantNode array = (ConstantNode) replacements.get(snippetGraph.getLocal(i));
+                    for (LoadIndexedNode loadIndexed : snippetCopy.getNodes(LoadIndexedNode.class)) {
+                        if (loadIndexed.array() == array) {
+                            Debug.dump(snippetCopy, "Before replacing %s", loadIndexed);
+                            LoadMultipleParameterNode lmp = new LoadMultipleParameterNode(array, i, loadIndexed.index(), loadIndexed.stamp());
+                            StructuredGraph g = (StructuredGraph) loadIndexed.graph();
+                            g.add(lmp);
+                            g.replaceFixedWithFixed(loadIndexed, lmp);
+                            parameter = lmp;
+                            Debug.dump(snippetCopy, "After replacing %s", loadIndexed);
                             break;
                         }
                     }
-                } while (exploded);
-
-                // Remove all frame states from inlined snippet graph. Snippets must be atomic (i.e. free
-                // of side-effects that prevent deoptimizing to a point before the snippet).
-                for (Node node : snippetCopy.getNodes()) {
-                    if (node instanceof StateSplit) {
-                        StateSplit stateSplit = (StateSplit) node;
-                        FrameState frameState = stateSplit.stateAfter();
-                        assert !stateSplit.hasSideEffect() : "snippets cannot contain side-effecting node " + node + "\n    " + frameState.toString(Verbosity.Debugger);
-                        if (frameState != null) {
-                            stateSplit.setStateAfter(null);
-                        }
-                    }
+                } else {
+                    parameter = snippetCopy.getLocal(i);
                 }
-
-                new DeadCodeEliminationPhase().apply(snippetCopy);
-                return new SnippetTemplate(args, snippetCopy);
+                assert parameter != null;
+                parameters.put(p.value(), parameter);
             }
-        });
-    }
-
-    SnippetTemplate(Object[] args, StructuredGraph graph) {
-        Object[] flattenedArgs = flatten(args);
-        this.graph = graph;
-        this.parameters = new Object[flattenedArgs.length];
+        }
 
-        // Find the constant nodes corresponding to the flattened positional parameters
-        for (ConstantNode node : graph.getNodes().filter(ConstantNode.class)) {
-            if (node.kind().isObject()) {
-                CiConstant constant = node.asConstant();
-                if (constant.kind.isObject() && !constant.isNull()) {
-                    for (int i = 0; i < flattenedArgs.length; i++) {
-                        if (flattenedArgs[i] == constant.asObject()) {
-                            parameters[i] = node;
+        // Do any required loop explosion
+        boolean exploded = false;
+        do {
+            exploded = false;
+            for (Node node : snippetCopy.getNodes()) {
+                if (node instanceof ExplodeLoopNode) {
+                    final ExplodeLoopNode explodeLoop = (ExplodeLoopNode) node;
+                    LoopBeginNode loopBegin = explodeLoop.findLoopBegin();
+                    if (loopBegin != null) {
+                        ControlFlowGraph cfg = ControlFlowGraph.compute(snippetCopy, true, true, false, false);
+                        for (Loop loop : cfg.getLoops()) {
+                            if (loop.loopBegin() == loopBegin) {
+                                SuperBlock wholeLoop = LoopTransformUtil.wholeLoop(loop);
+                                Debug.dump(snippetCopy, "Before exploding loop %s", loopBegin);
+                                int peel = 0;
+                                while (!loopBegin.isDeleted()) {
+                                    int mark = snippetCopy.getMark();
+                                    LoopTransformUtil.peel(loop, wholeLoop);
+                                    Debug.dump(snippetCopy, "After peel %d", peel);
+                                    new CanonicalizerPhase(null, runtime, null, mark, null).apply(snippetCopy);
+                                    peel++;
+                                }
+                                Debug.dump(snippetCopy, "After exploding loop %s", loopBegin);
+                                exploded = true;
+                                break;
+                            }
                         }
+                    } else {
+                        // Earlier canonicalization removed the loop altogether
                     }
+
+                    FixedNode explodeLoopNext = explodeLoop.next();
+                    explodeLoop.clearSuccessors();
+                    explodeLoop.replaceAtPredecessors(explodeLoopNext);
+                    explodeLoop.replaceAtUsages(null);
+                    GraphUtil.killCFG(explodeLoop);
+                    break;
+                }
+            }
+        } while (exploded);
+
+        // Remove all frame states from inlined snippet graph. Snippets must be atomic (i.e. free
+        // of side-effects that prevent deoptimizing to a point before the snippet).
+        for (Node node : snippetCopy.getNodes()) {
+            if (node instanceof StateSplit) {
+                StateSplit stateSplit = (StateSplit) node;
+                FrameState frameState = stateSplit.stateAfter();
+                assert !stateSplit.hasSideEffect() : "snippets cannot contain side-effecting node " + node + "\n    " + frameState.toString(Verbosity.Debugger);
+                if (frameState != null) {
+                    stateSplit.setStateAfter(null);
                 }
             }
         }
 
-        // Find the local nodes corresponding to the flattened positional parameters
-        int localIndex = 0;
-        for (int i = 0; i < flattenedArgs.length; i++) {
-            if (flattenedArgs[i] == _) {
-                for (LocalNode local : graph.getNodes(LocalNode.class)) {
-                    if (local.index() == localIndex) {
-                        assert parameters[i] == null;
-                        parameters[i] = local;
-                    }
-                }
-                localIndex++;
-            } else {
-                Object param = parameters[i];
-                if (param == null) {
-                    parameters[i] = flattenedArgs[i];
-                } else {
-                    assert param instanceof ConstantNode;
-                    assert ((ConstantNode) param).kind().isObject();
-                }
-            }
-        }
+        new DeadCodeEliminationPhase().apply(snippetCopy);
 
+        this.graph = snippetCopy;
         nodes = new ArrayList<>(graph.getNodeCount());
         ReturnNode retNode = null;
         StartNode entryPointNode = graph.start();
@@ -228,45 +291,84 @@
         this.returnNode = retNode;
     }
 
+    private static boolean checkConstantArgument(final RiResolvedMethod method, RiSignature signature, int i, String name, Object arg, CiKind kind) {
+        if (kind.isObject()) {
+            Class<?> type = signature.argumentTypeAt(i, method.holder()).resolve(method.holder()).toJava();
+            assert type.isInstance(arg) :
+                method + ": wrong value type for " + name + ": expected " + type.getName() + ", got " + arg.getClass().getName();
+        } else {
+            assert kind.toBoxedJavaClass() == arg.getClass() :
+                method + ": wrong value kind for " + name + ": expected " + kind + ", got " + arg.getClass().getSimpleName();
+        }
+        return true;
+    }
+
+    private static boolean checkMultipleArgument(final RiResolvedMethod method, RiSignature signature, int i, String name, Object multiple) {
+        assert multiple instanceof Multiple;
+        Object arg = ((Multiple) multiple).array;
+        RiResolvedType type = (RiResolvedType) signature.argumentTypeAt(i, method.holder());
+        Class< ? > javaType = type.toJava();
+        assert javaType.isArray() : "multiple parameter must be an array type";
+        assert javaType.isInstance(arg) : "value for " + name + " is not a " + javaType.getName() + " instance: " + arg;
+        return true;
+    }
+
     /**
      * The graph built from the snippet method.
      */
-    public final StructuredGraph graph;
+    private final StructuredGraph graph;
 
     /**
-     * The flattened positional parameters of this snippet.
-     * A {@link LocalNode} element is bound to a {@link ValueNode} to be supplied during
-     * instantiation, a {@link ConstantNode} is replaced by an object constant and any
-     * other element denotes an input fixed during specialization.
+     * The named parameters of this template that must be bound to values during instantiation.
+     * Each parameter is either a {@link LocalNode} or a {@link LoadMultipleParameterNode} instance.
      */
-    public final Object[] parameters;
+    private final Map<String, ValueNode> parameters;
 
     /**
      * The return node (if any) of the snippet.
      */
-    public final ReturnNode returnNode;
+    private final ReturnNode returnNode;
 
     /**
      * The nodes to be inlined when this specialization is instantiated.
      */
-    public final ArrayList<Node> nodes;
+    private final ArrayList<Node> nodes;
 
-    private IdentityHashMap<Node, Node> replacements(StructuredGraph replaceeGraph, RiRuntime runtime, Object... args) {
-        Object[] flattenedArgs = flatten(args);
+    /**
+     * 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
+     */
+    private IdentityHashMap<Node, Node> bind(StructuredGraph replaceeGraph, RiRuntime runtime, Snippet.Arguments args) {
         IdentityHashMap<Node, Node> replacements = new IdentityHashMap<>();
-        assert parameters.length >= flattenedArgs.length;
-        for (int i = 0; i < flattenedArgs.length; i++) {
-            Object param = parameters[i];
-            Object arg = flattenedArgs[i];
-            if (arg == null) {
-                assert !(param instanceof ValueNode) : param;
-            } else if (arg instanceof ValueNode) {
-                assert param instanceof LocalNode;
-                replacements.put((LocalNode) param, (ValueNode) arg);
-            } else if (param instanceof ConstantNode) {
-                replacements.put((ConstantNode) param, ConstantNode.forObject(arg, runtime, replaceeGraph));
+
+        for (Map.Entry<String, Object> e : args) {
+            String name = e.getKey();
+            ValueNode parameter = parameters.get(name);
+            assert parameter != null : this + " has no parameter named " + name;
+            Object argument = e.getValue();
+            if (parameter instanceof LocalNode) {
+                if (argument instanceof ValueNode) {
+                    replacements.put(parameter, (ValueNode) argument);
+                } else {
+                    CiKind kind = ((LocalNode) parameter).kind();
+                    CiConstant constant = CiConstant.forBoxed(kind, argument);
+                    replacements.put(parameter, ConstantNode.forCiConstant(constant, runtime, replaceeGraph));
+                }
             } else {
-                assert param.equals(arg) : param + " != " + arg;
+                assert parameter instanceof LoadMultipleParameterNode;
+                Object array = argument;
+                assert array != null && array.getClass().isArray();
+                int length = Array.getLength(array);
+                LoadMultipleParameterNode lmp = (LoadMultipleParameterNode) parameter;
+                assert length == lmp.getLocalCount() : length + " != " + lmp.getLocalCount();
+                for (int j = 0; j < length; j++) {
+                    LocalNode local = lmp.getLocal(j);
+                    assert local != null;
+                    CiConstant constant = CiConstant.forBoxed(lmp.kind(), Array.get(array, j));
+                    ConstantNode element = ConstantNode.forCiConstant(constant, runtime, replaceeGraph);
+                    replacements.put(local, element);
+                }
             }
         }
         return replacements;
@@ -282,7 +384,7 @@
      */
     public void instantiate(RiRuntime runtime,
                     Node replacee,
-                    FixedWithNextNode anchor, Object... args) {
+                    FixedWithNextNode anchor, Arguments args) {
 
         // Inline the snippet nodes, replacing parameters with the given args in the process
         String name = graph.name == null ? "{copy}" : graph.name + "{copy}";
@@ -290,7 +392,7 @@
         StartNode entryPointNode = graph.start();
         FixedNode firstCFGNode = entryPointNode.next();
         StructuredGraph replaceeGraph = (StructuredGraph) replacee.graph();
-        IdentityHashMap<Node, Node> replacements = replacements(replaceeGraph, runtime, args);
+        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());
 
@@ -328,37 +430,23 @@
         Debug.dump(replaceeGraph, "After lowering %s with %s", replacee, this);
     }
 
-    /**
-     * Flattens a list of objects by replacing any array in {@code args} with its elements.
-     */
-    private static Object[] flatten(Object... args) {
-        List<Object> list = new ArrayList<>(args.length * 2);
-        for (Object o : args) {
-            if (o instanceof Object[]) {
-                list.addAll(Arrays.asList((Object[]) o));
-            } else {
-                list.add(o);
-            }
-        }
-        return list.toArray(new Object[list.size()]);
-    }
-
     @Override
     public String toString() {
         StringBuilder buf = new StringBuilder(graph.toString()).append('(');
-        for (int i = 0; i < parameters.length; i++) {
-            Object param = parameters[i];
-            if (param instanceof ConstantNode) {
-                buf.append(((ConstantNode) param).asConstant().asObject());
-            } else if (param instanceof LocalNode) {
-                buf.append('_');
+        String sep = "";
+        for (Map.Entry<String, ValueNode> e : parameters.entrySet()) {
+            String name = e.getKey();
+            ValueNode value = e.getValue();
+            buf.append(sep);
+            sep = ", ";
+            if (value instanceof LocalNode) {
+                buf.append(value.kind().name()).append(' ').append(name);
             } else {
-                buf.append(param);
-            }
-            if (i != parameters.length - 1) {
-                buf.append(", ");
+                LoadMultipleParameterNode lmp = (LoadMultipleParameterNode) value;
+                buf.append(value.kind().name()).append('[').append(lmp.getLocalCount()).append("] ").append(name);
             }
         }
         return buf.append(')').toString();
     }
 }
+
--- a/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/ExplodeLoopNode.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/ExplodeLoopNode.java	Wed Jun 06 17:20:15 2012 +0200
@@ -27,10 +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;
 
 /**
  * Placeholder node to denote to snippet preparation that the following loop
  * must be completely unrolled.
+ *
+ * @see Parameter#multiple()
  */
 public final class ExplodeLoopNode extends FixedWithNextNode {
 
@@ -40,12 +43,17 @@
 
     public LoopBeginNode findLoopBegin() {
         Node next = next();
+        ArrayList<Node> succs = new ArrayList<>();
         while (!(next instanceof LoopBeginNode)) {
             assert next != null : "cannot find loop after " + this;
-            Iterator< ? extends Node> successors = next.cfgSuccessors().iterator();
-            assert successors.hasNext() : "cannot find loop after " + this;
-            next = successors.next();
-            assert !successors.hasNext() : "should only be straight line code after " + this;
+            for (Node n : next.cfgSuccessors()) {
+                succs.add(n);
+            }
+            if (succs.size() == 1) {
+                next = succs.get(0);
+            } else {
+                return null;
+            }
         }
         return (LoopBeginNode) next;
     }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/graal/com.oracle.graal.snippets/src/com/oracle/graal/snippets/nodes/LoadMultipleParameterNode.java	Wed Jun 06 17:20:15 2012 +0200
@@ -0,0 +1,71 @@
+/*
+ * 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 java.lang.reflect.*;
+
+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 LoadMultipleParameterNode extends FixedWithNextNode implements Canonicalizable {
+
+    @Input private ValueNode index;
+
+    private final LocalNode[] locals;
+
+    public ValueNode index() {
+        return index;
+    }
+
+    public LoadMultipleParameterNode(ConstantNode array, int localIndex, ValueNode index, Stamp stamp) {
+        super(stamp);
+        int length = Array.getLength(array.asConstant().asObject());
+        this.index = index;
+        locals = new LocalNode[length];
+        for (int i = 0; i < length; i++) {
+            int idx = localIndex << 16 | i;
+            LocalNode local = array.graph().unique(new LocalNode(idx, stamp()));
+            locals[i] = local;
+        }
+    }
+
+    public LocalNode getLocal(int idx) {
+        assert idx < locals.length;
+        return locals[idx];
+    }
+
+    public int getLocalCount() {
+        return locals.length;
+    }
+
+    @Override
+    public ValueNode canonical(CanonicalizerTool tool) {
+        assert index.isConstant();
+        return getLocal(index().asConstant().asInt());
+    }
+}
--- a/graal/com.oracle.graal.tests/src/com/oracle/graal/compiler/tests/CheckCastTest.java	Wed Jun 06 17:20:07 2012 +0200
+++ b/graal/com.oracle.graal.tests/src/com/oracle/graal/compiler/tests/CheckCastTest.java	Wed Jun 06 17:20:15 2012 +0200
@@ -162,6 +162,7 @@
     static class Depth11 extends Depth10 {}
     static class Depth12 extends Depth11 {}
     static class Depth13 extends Depth12 {}
+    static class Depth14 extends Depth12 {}
 
     public static Depth12 asDepth12(Object o) {
         return (Depth12) o;
@@ -179,6 +180,8 @@
     public void test9() {
         Object o = new Depth13();
         test("asDepth12",   profile(), o);
+        test("asDepth12",   profile(Depth13.class), o);
+        test("asDepth12",   profile(Depth13.class, Depth14.class), o);
     }
 
     @Test