1/*
2 * Copyright (c) 2012, 2016, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation.
8 *
9 * This code is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
12 * version 2 for more details (a copy is included in the LICENSE file that
13 * accompanied this code).
14 *
15 * You should have received a copy of the GNU General Public License version
16 * 2 along with this work; if not, write to the Free Software Foundation,
17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
18 *
19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
20 * or visit www.oracle.com if you need additional information or have any
21 * questions.
22 */
23package org.graalvm.compiler.replacements;
24
25import static java.util.FormattableFlags.ALTERNATE;
26import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
27import static org.graalvm.compiler.debug.DebugContext.applyFormattingFlagsAndWidth;
28import static org.graalvm.compiler.debug.DebugOptions.DebugStubsAndSnippets;
29import static org.graalvm.compiler.graph.iterators.NodePredicates.isNotA;
30import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED;
31import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED;
32import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
33import static org.graalvm.word.LocationIdentity.any;
34
35import java.lang.reflect.Array;
36import java.lang.reflect.Method;
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.Collection;
40import java.util.Collections;
41import java.util.Formattable;
42import java.util.Formatter;
43import java.util.LinkedHashMap;
44import java.util.List;
45import java.util.Map;
46import java.util.concurrent.atomic.AtomicInteger;
47import java.util.concurrent.atomic.AtomicReference;
48import java.util.function.Predicate;
49
50import org.graalvm.compiler.api.replacements.Snippet;
51import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter;
52import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter;
53import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter;
54import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
55import org.graalvm.compiler.core.common.GraalOptions;
56import org.graalvm.compiler.core.common.type.Stamp;
57import org.graalvm.compiler.core.common.type.StampFactory;
58import org.graalvm.compiler.core.common.type.StampPair;
59import org.graalvm.compiler.core.common.type.TypeReference;
60import org.graalvm.compiler.debug.CounterKey;
61import org.graalvm.compiler.debug.DebugCloseable;
62import org.graalvm.compiler.debug.DebugHandlersFactory;
63import org.graalvm.compiler.debug.DebugContext;
64import org.graalvm.compiler.debug.DebugContext.Description;
65import org.graalvm.compiler.debug.GraalError;
66import org.graalvm.compiler.debug.TimerKey;
67import org.graalvm.compiler.graph.Graph.Mark;
68import org.graalvm.compiler.graph.Node;
69import org.graalvm.compiler.graph.NodeClass;
70import org.graalvm.compiler.graph.Position;
71import org.graalvm.compiler.loop.LoopEx;
72import org.graalvm.compiler.loop.LoopsData;
73import org.graalvm.compiler.loop.phases.LoopTransformations;
74import org.graalvm.compiler.nodeinfo.InputType;
75import org.graalvm.compiler.nodeinfo.NodeInfo;
76import org.graalvm.compiler.nodes.AbstractBeginNode;
77import org.graalvm.compiler.nodes.AbstractMergeNode;
78import org.graalvm.compiler.nodes.ConstantNode;
79import org.graalvm.compiler.nodes.ControlSinkNode;
80import org.graalvm.compiler.nodes.DeoptimizingNode;
81import org.graalvm.compiler.nodes.FixedNode;
82import org.graalvm.compiler.nodes.FixedWithNextNode;
83import org.graalvm.compiler.nodes.FrameState;
84import org.graalvm.compiler.nodes.LoopBeginNode;
85import org.graalvm.compiler.nodes.MergeNode;
86import org.graalvm.compiler.nodes.ParameterNode;
87import org.graalvm.compiler.nodes.PhiNode;
88import org.graalvm.compiler.nodes.PiNode.Placeholder;
89import org.graalvm.compiler.nodes.PiNode.PlaceholderStamp;
90import org.graalvm.compiler.nodes.ReturnNode;
91import org.graalvm.compiler.nodes.StartNode;
92import org.graalvm.compiler.nodes.StateSplit;
93import org.graalvm.compiler.nodes.StructuredGraph;
94import org.graalvm.compiler.nodes.StructuredGraph.GuardsStage;
95import org.graalvm.compiler.nodes.ValueNode;
96import org.graalvm.compiler.nodes.ValueNodeUtil;
97import org.graalvm.compiler.nodes.calc.FloatingNode;
98import org.graalvm.compiler.nodes.java.LoadIndexedNode;
99import org.graalvm.compiler.nodes.java.StoreIndexedNode;
100import org.graalvm.compiler.nodes.memory.MemoryAccess;
101import org.graalvm.compiler.nodes.memory.MemoryAnchorNode;
102import org.graalvm.compiler.nodes.memory.MemoryCheckpoint;
103import org.graalvm.compiler.nodes.memory.MemoryMap;
104import org.graalvm.compiler.nodes.memory.MemoryMapNode;
105import org.graalvm.compiler.nodes.memory.MemoryNode;
106import org.graalvm.compiler.nodes.memory.MemoryPhiNode;
107import org.graalvm.compiler.nodes.spi.ArrayLengthProvider;
108import org.graalvm.compiler.nodes.spi.LoweringTool;
109import org.graalvm.compiler.nodes.spi.MemoryProxy;
110import org.graalvm.compiler.nodes.util.GraphUtil;
111import org.graalvm.compiler.options.Option;
112import org.graalvm.compiler.options.OptionKey;
113import org.graalvm.compiler.options.OptionValues;
114import org.graalvm.compiler.phases.common.CanonicalizerPhase;
115import org.graalvm.compiler.phases.common.DeadCodeEliminationPhase;
116import org.graalvm.compiler.phases.common.FloatingReadPhase;
117import org.graalvm.compiler.phases.common.FloatingReadPhase.MemoryMapImpl;
118import org.graalvm.compiler.phases.common.GuardLoweringPhase;
119import org.graalvm.compiler.phases.common.LoweringPhase;
120import org.graalvm.compiler.phases.common.RemoveValueProxyPhase;
121import org.graalvm.compiler.phases.common.inlining.InliningUtil;
122import org.graalvm.compiler.phases.tiers.PhaseContext;
123import org.graalvm.compiler.phases.util.Providers;
124import org.graalvm.compiler.replacements.nodes.ExplodeLoopNode;
125import org.graalvm.compiler.replacements.nodes.LoadSnippetVarargParameterNode;
126import org.graalvm.util.CollectionsUtil;
127import org.graalvm.util.EconomicMap;
128import org.graalvm.util.EconomicSet;
129import org.graalvm.util.Equivalence;
130import org.graalvm.util.UnmodifiableEconomicMap;
131import org.graalvm.word.LocationIdentity;
132import org.graalvm.word.WordBase;
133
134import jdk.vm.ci.code.TargetDescription;
135import jdk.vm.ci.meta.Constant;
136import jdk.vm.ci.meta.JavaConstant;
137import jdk.vm.ci.meta.JavaKind;
138import jdk.vm.ci.meta.Local;
139import jdk.vm.ci.meta.LocalVariableTable;
140import jdk.vm.ci.meta.MetaAccessProvider;
141import jdk.vm.ci.meta.ResolvedJavaMethod;
142import jdk.vm.ci.meta.ResolvedJavaMethod.Parameter;
143import jdk.vm.ci.meta.ResolvedJavaType;
144import jdk.vm.ci.meta.Signature;
145
146/**
147 * A snippet template is a graph created by parsing a snippet method and then specialized by binding
148 * constants to the snippet's {@link ConstantParameter} parameters.
149 *
150 * Snippet templates can be managed in a cache maintained by {@link AbstractTemplates}.
151 */
152public class SnippetTemplate {
153
154    private boolean mayRemoveLocation = false;
155
156    /**
157     * Holds the {@link ResolvedJavaMethod} of the snippet, together with some information about the
158     * method that needs to be computed only once. The {@link SnippetInfo} should be created once
159     * per snippet and then cached.
160     */
161    public abstract static class SnippetInfo {
162
163        protected final ResolvedJavaMethod method;
164        protected ResolvedJavaMethod original;
165        protected final LocationIdentity[] privateLocations;
166
167        /**
168         * Lazily constructed parts of {@link SnippetInfo}.
169         */
170        static class Lazy {
171            Lazy(ResolvedJavaMethod method) {
172                int count = method.getSignature().getParameterCount(false);
173                constantParameters = new boolean[count];
174                varargsParameters = new boolean[count];
175                nonNullParameters = new boolean[count];
176                for (int i = 0; i < count; i++) {
177                    constantParameters[i] = method.getParameterAnnotation(ConstantParameter.class, i) != null;
178                    varargsParameters[i] = method.getParameterAnnotation(VarargsParameter.class, i) != null;
179                    nonNullParameters[i] = method.getParameterAnnotation(NonNullParameter.class, i) != null;
180
181                    assert !constantParameters[i] || !varargsParameters[i] : "Parameter cannot be annotated with both @" + ConstantParameter.class.getSimpleName() + " and @" +
182                                    VarargsParameter.class.getSimpleName();
183                }
184
185                // Retrieve the names only when assertions are turned on.
186                assert initNames(method, count);
187            }
188
189            final boolean[] constantParameters;
190            final boolean[] varargsParameters;
191            final boolean[] nonNullParameters;
192
193            /**
194             * The parameter names, taken from the local variables table. Only used for assertion
195             * checking, so use only within an assert statement.
196             */
197            String[] names;
198
199            private boolean initNames(ResolvedJavaMethod method, int parameterCount) {
200                names = new String[parameterCount];
201                Parameter[] params = method.getParameters();
202                if (params != null) {
203                    for (int i = 0; i < names.length; i++) {
204                        if (params[i].isNamePresent()) {
205                            names[i] = params[i].getName();
206                        }
207                    }
208                } else {
209                    int slotIdx = 0;
210                    LocalVariableTable localVariableTable = method.getLocalVariableTable();
211                    if (localVariableTable != null) {
212                        for (int i = 0; i < names.length; i++) {
213                            Local local = localVariableTable.getLocal(slotIdx, 0);
214                            if (local != null) {
215                                names[i] = local.getName();
216                            }
217                            JavaKind kind = method.getSignature().getParameterKind(i);
218                            slotIdx += kind.getSlotCount();
219                        }
220                    }
221                }
222                return true;
223            }
224        }
225
226        /**
227         * Times instantiations of all templates derived form this snippet.
228         *
229         * @see SnippetTemplate#instantiationTimer
230         */
231        private final TimerKey instantiationTimer;
232
233        /**
234         * Counts instantiations of all templates derived from this snippet.
235         *
236         * @see SnippetTemplate#instantiationCounter
237         */
238        private final CounterKey instantiationCounter;
239
240        protected abstract Lazy lazy();
241
242        protected SnippetInfo(ResolvedJavaMethod method, LocationIdentity[] privateLocations) {
243            this.method = method;
244            this.privateLocations = privateLocations;
245            instantiationCounter = DebugContext.counter("SnippetInstantiationCount[%s]", method.getName());
246            instantiationTimer = DebugContext.timer("SnippetInstantiationTime[%s]", method.getName());
247            assert method.isStatic() : "snippet method must be static: " + method.format("%H.%n");
248        }
249
250        public ResolvedJavaMethod getMethod() {
251            return method;
252        }
253
254        public int getParameterCount() {
255            return lazy().constantParameters.length;
256        }
257
258        public void setOriginalMethod(ResolvedJavaMethod original) {
259            this.original = original;
260        }
261
262        public boolean isConstantParameter(int paramIdx) {
263            return lazy().constantParameters[paramIdx];
264        }
265
266        public boolean isVarargsParameter(int paramIdx) {
267            return lazy().varargsParameters[paramIdx];
268        }
269
270        public boolean isNonNullParameter(int paramIdx) {
271            return lazy().nonNullParameters[paramIdx];
272        }
273
274        public String getParameterName(int paramIdx) {
275            String[] names = lazy().names;
276            if (names != null) {
277                return names[paramIdx];
278            }
279            return null;
280        }
281
282        @Override
283        public String toString() {
284            return getClass().getSimpleName() + ":" + method.format("%h.%n");
285        }
286    }
287
288    protected static class LazySnippetInfo extends SnippetInfo {
289        protected final AtomicReference<Lazy> lazy = new AtomicReference<>(null);
290
291        protected LazySnippetInfo(ResolvedJavaMethod method, LocationIdentity[] privateLocations) {
292            super(method, privateLocations);
293        }
294
295        @Override
296        protected Lazy lazy() {
297            if (lazy.get() == null) {
298                lazy.compareAndSet(null, new Lazy(method));
299            }
300            return lazy.get();
301        }
302    }
303
304    protected static class EagerSnippetInfo extends SnippetInfo {
305        protected final Lazy lazy;
306
307        protected EagerSnippetInfo(ResolvedJavaMethod method, LocationIdentity[] privateLocations) {
308            super(method, privateLocations);
309            lazy = new Lazy(method);
310        }
311
312        @Override
313        protected Lazy lazy() {
314            return lazy;
315        }
316    }
317
318    /**
319     * Values that are bound to the snippet method parameters. The methods {@link #add},
320     * {@link #addConst}, and {@link #addVarargs} must be called in the same order as in the
321     * signature of the snippet method. The parameter name is passed to the add methods for
322     * assertion checking, i.e., to enforce that the order matches. Which method needs to be called
323     * depends on the annotation of the snippet method parameter:
324     * <ul>
325     * <li>Use {@link #add} for a parameter without an annotation. The value is bound when the
326     * {@link SnippetTemplate} is {@link SnippetTemplate#instantiate instantiated}.
327     * <li>Use {@link #addConst} for a parameter annotated with {@link ConstantParameter}. The value
328     * is bound when the {@link SnippetTemplate} is {@link SnippetTemplate#SnippetTemplate created}.
329     * <li>Use {@link #addVarargs} for an array parameter annotated with {@link VarargsParameter}. A
330     * separate {@link SnippetTemplate} is {@link SnippetTemplate#SnippetTemplate created} for every
331     * distinct array length. The actual values are bound when the {@link SnippetTemplate} is
332     * {@link SnippetTemplate#instantiate instantiated}
333     * </ul>
334     */
335    public static class Arguments implements Formattable {
336
337        protected final SnippetInfo info;
338        protected final CacheKey cacheKey;
339        protected final Object[] values;
340        protected final Stamp[] constStamps;
341        protected boolean cacheable;
342
343        protected int nextParamIdx;
344
345        public Arguments(SnippetInfo info, GuardsStage guardsStage, LoweringTool.LoweringStage loweringStage) {
346            this.info = info;
347            this.cacheKey = new CacheKey(info, guardsStage, loweringStage);
348            this.values = new Object[info.getParameterCount()];
349            this.constStamps = new Stamp[info.getParameterCount()];
350            this.cacheable = true;
351        }
352
353        public Arguments add(String name, Object value) {
354            assert check(name, false, false);
355            values[nextParamIdx] = value;
356            nextParamIdx++;
357            return this;
358        }
359
360        public Arguments addConst(String name, Object value) {
361            assert value != null;
362            return addConst(name, value, null);
363        }
364
365        public Arguments addConst(String name, Object value, Stamp stamp) {
366            assert check(name, true, false);
367            values[nextParamIdx] = value;
368            constStamps[nextParamIdx] = stamp;
369            cacheKey.setParam(nextParamIdx, value);
370            nextParamIdx++;
371            return this;
372        }
373
374        public Arguments addVarargs(String name, Class<?> componentType, Stamp argStamp, Object value) {
375            assert check(name, false, true);
376            Varargs varargs = new Varargs(componentType, argStamp, value);
377            values[nextParamIdx] = varargs;
378            // A separate template is necessary for every distinct array length
379            cacheKey.setParam(nextParamIdx, varargs.length);
380            nextParamIdx++;
381            return this;
382        }
383
384        public void setCacheable(boolean cacheable) {
385            this.cacheable = cacheable;
386        }
387
388        private boolean check(String name, boolean constParam, boolean varargsParam) {
389            assert nextParamIdx < info.getParameterCount() : "too many parameters: " + name + "  " + this;
390            assert info.getParameterName(nextParamIdx) == null || info.getParameterName(nextParamIdx).equals(name) : "wrong parameter name: " + name + "  " + this;
391            assert constParam == info.isConstantParameter(nextParamIdx) : "Parameter " + (constParam ? "not " : "") + "annotated with @" + ConstantParameter.class.getSimpleName() + ": " + name +
392                            "  " + this;
393            assert varargsParam == info.isVarargsParameter(nextParamIdx) : "Parameter " + (varargsParam ? "not " : "") + "annotated with @" + VarargsParameter.class.getSimpleName() + ": " + name +
394                            "  " + this;
395            return true;
396        }
397
398        @Override
399        public String toString() {
400            StringBuilder result = new StringBuilder();
401            result.append("Parameters<").append(info.method.format("%h.%n")).append(" [");
402            String sep = "";
403            for (int i = 0; i < info.getParameterCount(); i++) {
404                result.append(sep);
405                if (info.isConstantParameter(i)) {
406                    result.append("const ");
407                } else if (info.isVarargsParameter(i)) {
408                    result.append("varargs ");
409                }
410                result.append(info.getParameterName(i)).append(" = ").append(values[i]);
411                sep = ", ";
412            }
413            result.append(">");
414            return result.toString();
415        }
416
417        @Override
418        public void formatTo(Formatter formatter, int flags, int width, int precision) {
419            if ((flags & ALTERNATE) == 0) {
420                formatter.format(applyFormattingFlagsAndWidth(toString(), flags, width));
421            } else {
422                StringBuilder sb = new StringBuilder();
423                sb.append(info.method.getName()).append('(');
424                String sep = "";
425                for (int i = 0; i < info.getParameterCount(); i++) {
426                    if (info.isConstantParameter(i)) {
427                        sb.append(sep);
428                        if (info.getParameterName(i) != null) {
429                            sb.append(info.getParameterName(i));
430                        } else {
431                            sb.append(i);
432                        }
433                        sb.append('=').append(values[i]);
434                        sep = ", ";
435                    }
436                }
437                sb.append(")");
438                String string = sb.toString();
439                if (string.indexOf('%') != -1) {
440                    // Quote any % signs
441                    string = string.replace("%", "%%");
442                }
443                formatter.format(applyFormattingFlagsAndWidth(string, flags & ~ALTERNATE, width));
444            }
445        }
446    }
447
448    /**
449     * Wrapper for the prototype value of a {@linkplain VarargsParameter varargs} parameter.
450     */
451    static class Varargs {
452
453        protected final Class<?> componentType;
454        protected final Stamp stamp;
455        protected final Object value;
456        protected final int length;
457
458        protected Varargs(Class<?> componentType, Stamp stamp, Object value) {
459            this.componentType = componentType;
460            this.stamp = stamp;
461            this.value = value;
462            if (value instanceof List) {
463                this.length = ((List<?>) value).size();
464            } else {
465                this.length = Array.getLength(value);
466            }
467        }
468
469        @Override
470        public String toString() {
471            if (value instanceof boolean[]) {
472                return Arrays.toString((boolean[]) value);
473            }
474            if (value instanceof byte[]) {
475                return Arrays.toString((byte[]) value);
476            }
477            if (value instanceof char[]) {
478                return Arrays.toString((char[]) value);
479            }
480            if (value instanceof short[]) {
481                return Arrays.toString((short[]) value);
482            }
483            if (value instanceof int[]) {
484                return Arrays.toString((int[]) value);
485            }
486            if (value instanceof long[]) {
487                return Arrays.toString((long[]) value);
488            }
489            if (value instanceof float[]) {
490                return Arrays.toString((float[]) value);
491            }
492            if (value instanceof double[]) {
493                return Arrays.toString((double[]) value);
494            }
495            if (value instanceof Object[]) {
496                return Arrays.toString((Object[]) value);
497            }
498            return String.valueOf(value);
499        }
500    }
501
502    @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED)
503    static final class VarargsPlaceholderNode extends FloatingNode implements ArrayLengthProvider {
504
505        public static final NodeClass<VarargsPlaceholderNode> TYPE = NodeClass.create(VarargsPlaceholderNode.class);
506        protected final Varargs varargs;
507
508        protected VarargsPlaceholderNode(Varargs varargs, MetaAccessProvider metaAccess) {
509            super(TYPE, StampFactory.objectNonNull(TypeReference.createExactTrusted(metaAccess.lookupJavaType(varargs.componentType).getArrayClass())));
510            this.varargs = varargs;
511        }
512
513        @Override
514        public ValueNode length() {
515            return ConstantNode.forInt(varargs.length);
516        }
517    }
518
519    static class CacheKey {
520
521        private final ResolvedJavaMethod method;
522        private final Object[] values;
523        private final GuardsStage guardsStage;
524        private final LoweringTool.LoweringStage loweringStage;
525        private int hash;
526
527        protected CacheKey(SnippetInfo info, GuardsStage guardsStage, LoweringTool.LoweringStage loweringStage) {
528            this.method = info.method;
529            this.guardsStage = guardsStage;
530            this.loweringStage = loweringStage;
531            this.values = new Object[info.getParameterCount()];
532            this.hash = info.method.hashCode() + 31 * guardsStage.ordinal();
533        }
534
535        protected void setParam(int paramIdx, Object value) {
536            values[paramIdx] = value;
537            hash = (hash * 31) ^ (value == null ? 0 : value.hashCode());
538        }
539
540        @Override
541        public boolean equals(Object obj) {
542            if (!(obj instanceof CacheKey)) {
543                return false;
544            }
545            CacheKey other = (CacheKey) obj;
546            if (!method.equals(other.method)) {
547                return false;
548            }
549            if (guardsStage != other.guardsStage || loweringStage != other.loweringStage) {
550                return false;
551            }
552            for (int i = 0; i < values.length; i++) {
553                if (values[i] != null && !values[i].equals(other.values[i])) {
554                    return false;
555                }
556            }
557            return true;
558        }
559
560        @Override
561        public int hashCode() {
562            return hash;
563        }
564    }
565
566    private static final TimerKey SnippetTemplateCreationTime = DebugContext.timer("SnippetTemplateCreationTime");
567    private static final CounterKey SnippetTemplates = DebugContext.counter("SnippetTemplateCount");
568
569    static class Options {
570        @Option(help = "Use a LRU cache for snippet templates.")//
571        static final OptionKey<Boolean> UseSnippetTemplateCache = new OptionKey<>(true);
572
573        @Option(help = "")//
574        static final OptionKey<Integer> MaxTemplatesPerSnippet = new OptionKey<>(50);
575    }
576
577    /**
578     * Base class for snippet classes. It provides a cache for {@link SnippetTemplate}s.
579     */
580    public abstract static class AbstractTemplates implements org.graalvm.compiler.api.replacements.SnippetTemplateCache {
581
582        protected final OptionValues options;
583        protected final Providers providers;
584        protected final SnippetReflectionProvider snippetReflection;
585        protected final Iterable<DebugHandlersFactory> factories;
586        protected final TargetDescription target;
587        private final Map<CacheKey, SnippetTemplate> templates;
588
589        protected AbstractTemplates(OptionValues options, Iterable<DebugHandlersFactory> factories, Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
590            this.options = options;
591            this.providers = providers;
592            this.snippetReflection = snippetReflection;
593            this.target = target;
594            this.factories = factories;
595            if (Options.UseSnippetTemplateCache.getValue(options)) {
596                int size = Options.MaxTemplatesPerSnippet.getValue(options);
597                this.templates = Collections.synchronizedMap(new LRUCache<>(size, size));
598            } else {
599                this.templates = null;
600            }
601        }
602
603        public static Method findMethod(Class<? extends Snippets> declaringClass, String methodName, Method except) {
604            for (Method m : declaringClass.getDeclaredMethods()) {
605                if (m.getName().equals(methodName) && !m.equals(except)) {
606                    return m;
607                }
608            }
609            return null;
610        }
611
612        /**
613         * Finds the unique method in {@code declaringClass} named {@code methodName} annotated by
614         * {@link Snippet} and returns a {@link SnippetInfo} value describing it. There must be
615         * exactly one snippet method in {@code declaringClass}.
616         */
617        protected SnippetInfo snippet(Class<? extends Snippets> declaringClass, String methodName, LocationIdentity... initialPrivateLocations) {
618            assert methodName != null;
619            Method method = findMethod(declaringClass, methodName, null);
620            assert method != null : "did not find @" + Snippet.class.getSimpleName() + " method in " + declaringClass + " named " + methodName;
621            assert method.getAnnotation(Snippet.class) != null : method + " must be annotated with @" + Snippet.class.getSimpleName();
622            assert findMethod(declaringClass, methodName, method) == null : "found more than one method named " + methodName + " in " + declaringClass;
623            ResolvedJavaMethod javaMethod = providers.getMetaAccess().lookupJavaMethod(method);
624            providers.getReplacements().registerSnippet(javaMethod);
625            LocationIdentity[] privateLocations = GraalOptions.SnippetCounters.getValue(options) ? SnippetCounterNode.addSnippetCounters(initialPrivateLocations) : initialPrivateLocations;
626            if (GraalOptions.EagerSnippets.getValue(options)) {
627                return new EagerSnippetInfo(javaMethod, privateLocations);
628            } else {
629                return new LazySnippetInfo(javaMethod, privateLocations);
630            }
631        }
632
633        static final AtomicInteger nextSnippetTemplateId = new AtomicInteger();
634
635        private DebugContext openDebugContext(DebugContext outer, Arguments args) {
636            if (DebugStubsAndSnippets.getValue(options)) {
637                Description description = new Description(args.cacheKey.method, "SnippetTemplate_" + nextSnippetTemplateId.incrementAndGet());
638                return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, factories);
639            }
640            return DebugContext.DISABLED;
641        }
642
643        /**
644         * Gets a template for a given key, creating it first if necessary.
645         */
646        @SuppressWarnings("try")
647        protected SnippetTemplate template(DebugContext outer, final Arguments args) {
648            SnippetTemplate template = Options.UseSnippetTemplateCache.getValue(options) && args.cacheable ? templates.get(args.cacheKey) : null;
649            if (template == null) {
650                try (DebugContext debug = openDebugContext(outer, args)) {
651                    try (DebugCloseable a = SnippetTemplateCreationTime.start(debug); DebugContext.Scope s = debug.scope("SnippetSpecialization", args.info.method)) {
652                        SnippetTemplates.increment(debug);
653                        template = new SnippetTemplate(options, debug, providers, snippetReflection, args);
654                        if (Options.UseSnippetTemplateCache.getValue(options) && args.cacheable) {
655                            templates.put(args.cacheKey, template);
656                        }
657                    } catch (Throwable e) {
658                        throw debug.handle(e);
659                    }
660                }
661            }
662            return template;
663        }
664    }
665
666    private static final class LRUCache<K, V> extends LinkedHashMap<K, V> {
667        private static final long serialVersionUID = 1L;
668        private final int maxCacheSize;
669
670        LRUCache(int initialCapacity, int maxCacheSize) {
671            super(initialCapacity, 0.75F, true);
672            this.maxCacheSize = maxCacheSize;
673        }
674
675        @Override
676        protected boolean removeEldestEntry(java.util.Map.Entry<K, V> eldest) {
677            return size() > maxCacheSize;
678        }
679    }
680
681    // These values must be compared with equals() not '==' to support replay compilation.
682    private static final Object UNUSED_PARAMETER = "UNUSED_PARAMETER";
683    private static final Object CONSTANT_PARAMETER = "CONSTANT_PARAMETER";
684
685    /**
686     * Determines if any parameter of a given method is annotated with {@link ConstantParameter}.
687     */
688    public static boolean hasConstantParameter(ResolvedJavaMethod method) {
689        for (ConstantParameter p : method.getParameterAnnotations(ConstantParameter.class)) {
690            if (p != null) {
691                return true;
692            }
693        }
694        return false;
695    }
696
697    private final SnippetReflectionProvider snippetReflection;
698
699    /**
700     * Creates a snippet template.
701     */
702    @SuppressWarnings("try")
703    protected SnippetTemplate(OptionValues options, DebugContext debug, final Providers providers, SnippetReflectionProvider snippetReflection, Arguments args) {
704        this.snippetReflection = snippetReflection;
705        this.info = args.info;
706
707        Object[] constantArgs = getConstantArgs(args);
708        StructuredGraph snippetGraph = providers.getReplacements().getSnippet(args.info.method, args.info.original, constantArgs);
709        instantiationTimer = DebugContext.timer("SnippetTemplateInstantiationTime[%#s]", args);
710        instantiationCounter = DebugContext.counter("SnippetTemplateInstantiationCount[%#s]", args);
711
712        ResolvedJavaMethod method = snippetGraph.method();
713        Signature signature = method.getSignature();
714
715        PhaseContext phaseContext = new PhaseContext(providers);
716
717        // Copy snippet graph, replacing constant parameters with given arguments
718        final StructuredGraph snippetCopy = new StructuredGraph.Builder(options, debug).name(snippetGraph.name).method(snippetGraph.method()).build();
719
720        try (DebugContext.Scope scope = debug.scope("SpecializeSnippet", snippetCopy)) {
721            if (!snippetGraph.isUnsafeAccessTrackingEnabled()) {
722                snippetCopy.disableUnsafeAccessTracking();
723            }
724
725            EconomicMap<Node, Node> nodeReplacements = EconomicMap.create(Equivalence.IDENTITY);
726            nodeReplacements.put(snippetGraph.start(), snippetCopy.start());
727
728            MetaAccessProvider metaAccess = providers.getMetaAccess();
729            assert checkTemplate(metaAccess, args, method, signature);
730
731            int parameterCount = args.info.getParameterCount();
732            VarargsPlaceholderNode[] placeholders = new VarargsPlaceholderNode[parameterCount];
733
734            for (int i = 0; i < parameterCount; i++) {
735                ParameterNode parameter = snippetGraph.getParameter(i);
736                if (parameter != null) {
737                    if (args.info.isConstantParameter(i)) {
738                        Object arg = args.values[i];
739                        JavaKind kind = signature.getParameterKind(i);
740                        ConstantNode constantNode;
741                        if (arg instanceof Constant) {
742                            Stamp stamp = args.constStamps[i];
743                            if (stamp == null) {
744                                assert arg instanceof JavaConstant : "could not determine type of constant " + arg;
745                                constantNode = ConstantNode.forConstant((JavaConstant) arg, metaAccess, snippetCopy);
746                            } else {
747                                constantNode = ConstantNode.forConstant(stamp, (Constant) arg, metaAccess, snippetCopy);
748                            }
749                        } else {
750                            constantNode = ConstantNode.forConstant(snippetReflection.forBoxed(kind, arg), metaAccess, snippetCopy);
751                        }
752                        nodeReplacements.put(parameter, constantNode);
753                    } else if (args.info.isVarargsParameter(i)) {
754                        Varargs varargs = (Varargs) args.values[i];
755                        VarargsPlaceholderNode placeholder = snippetCopy.unique(new VarargsPlaceholderNode(varargs, providers.getMetaAccess()));
756                        nodeReplacements.put(parameter, placeholder);
757                        placeholders[i] = placeholder;
758                    } else if (args.info.isNonNullParameter(i)) {
759                        parameter.setStamp(parameter.stamp().join(StampFactory.objectNonNull()));
760                    }
761                }
762            }
763            snippetCopy.addDuplicates(snippetGraph.getNodes(), snippetGraph, snippetGraph.getNodeCount(), nodeReplacements);
764
765            debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before specialization");
766
767            // Gather the template parameters
768            parameters = new Object[parameterCount];
769            for (int i = 0; i < parameterCount; i++) {
770                if (args.info.isConstantParameter(i)) {
771                    parameters[i] = CONSTANT_PARAMETER;
772                } else if (args.info.isVarargsParameter(i)) {
773                    assert snippetCopy.getParameter(i) == null;
774                    Varargs varargs = (Varargs) args.values[i];
775                    int length = varargs.length;
776                    ParameterNode[] params = new ParameterNode[length];
777                    Stamp stamp = varargs.stamp;
778                    for (int j = 0; j < length; j++) {
779                        // Use a decimal friendly numbering make it more obvious how values map
780                        assert parameterCount < 10000;
781                        int idx = (i + 1) * 10000 + j;
782                        assert idx >= parameterCount : "collision in parameter numbering";
783                        ParameterNode local = snippetCopy.addOrUnique(new ParameterNode(idx, StampPair.createSingle(stamp)));
784                        params[j] = local;
785                    }
786                    parameters[i] = params;
787
788                    VarargsPlaceholderNode placeholder = placeholders[i];
789                    if (placeholder != null) {
790                        for (Node usage : placeholder.usages().snapshot()) {
791                            if (usage instanceof LoadIndexedNode) {
792                                LoadIndexedNode loadIndexed = (LoadIndexedNode) usage;
793                                debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "Before replacing %s", loadIndexed);
794                                LoadSnippetVarargParameterNode loadSnippetParameter = snippetCopy.add(new LoadSnippetVarargParameterNode(params, loadIndexed.index(), loadIndexed.stamp()));
795                                snippetCopy.replaceFixedWithFixed(loadIndexed, loadSnippetParameter);
796                                debug.dump(DebugContext.INFO_LEVEL, snippetCopy, "After replacing %s", loadIndexed);
797                            } else if (usage instanceof StoreIndexedNode) {
798                                /*
799                                 * The template lowering doesn't really treat this as an array so
800                                 * you can't store back into the varargs. Allocate your own array if
801                                 * you really need this and EA should eliminate it.
802                                 */
803                                throw new GraalError("Can't store into VarargsParameter array");
804                            }
805                        }
806                    }
807                } else {
808                    ParameterNode local = snippetCopy.getParameter(i);
809                    if (local == null) {
810                        // Parameter value was eliminated
811                        parameters[i] = UNUSED_PARAMETER;
812                    } else {
813                        parameters[i] = local;
814                    }
815                }
816            }
817
818            explodeLoops(snippetCopy, phaseContext);
819
820            GuardsStage guardsStage = args.cacheKey.guardsStage;
821            // Perform lowering on the snippet
822            if (!guardsStage.allowsFloatingGuards()) {
823                new GuardLoweringPhase().apply(snippetCopy, null);
824            }
825            snippetCopy.setGuardsStage(guardsStage);
826            try (DebugContext.Scope s = debug.scope("LoweringSnippetTemplate", snippetCopy)) {
827                new LoweringPhase(new CanonicalizerPhase(), args.cacheKey.loweringStage).apply(snippetCopy, phaseContext);
828            } catch (Throwable e) {
829                throw debug.handle(e);
830            }
831
832            ArrayList<StateSplit> curSideEffectNodes = new ArrayList<>();
833            ArrayList<DeoptimizingNode> curDeoptNodes = new ArrayList<>();
834            ArrayList<ValueNode> curPlaceholderStampedNodes = new ArrayList<>();
835            for (Node node : snippetCopy.getNodes()) {
836                if (node instanceof ValueNode) {
837                    ValueNode valueNode = (ValueNode) node;
838                    if (valueNode.stamp() == PlaceholderStamp.singleton()) {
839                        curPlaceholderStampedNodes.add(valueNode);
840                    }
841                }
842
843                if (node instanceof StateSplit) {
844                    StateSplit stateSplit = (StateSplit) node;
845                    FrameState frameState = stateSplit.stateAfter();
846                    if (stateSplit.hasSideEffect()) {
847                        curSideEffectNodes.add((StateSplit) node);
848                    }
849                    if (frameState != null) {
850                        stateSplit.setStateAfter(null);
851                    }
852                }
853                if (node instanceof DeoptimizingNode) {
854                    DeoptimizingNode deoptNode = (DeoptimizingNode) node;
855                    if (deoptNode.canDeoptimize()) {
856                        curDeoptNodes.add(deoptNode);
857                    }
858                }
859            }
860
861            new DeadCodeEliminationPhase(Required).apply(snippetCopy);
862
863            assert checkAllVarargPlaceholdersAreDeleted(parameterCount, placeholders);
864
865            new FloatingReadPhase(true, true).apply(snippetCopy);
866            new RemoveValueProxyPhase().apply(snippetCopy);
867
868            MemoryAnchorNode anchor = snippetCopy.add(new MemoryAnchorNode());
869            snippetCopy.start().replaceAtUsages(InputType.Memory, anchor);
870
871            this.snippet = snippetCopy;
872
873            StartNode entryPointNode = snippet.start();
874            if (anchor.hasNoUsages()) {
875                anchor.safeDelete();
876                this.memoryAnchor = null;
877            } else {
878                // Find out if all the return memory maps point to the anchor (i.e., there's no kill
879                // anywhere)
880                boolean needsMemoryMaps = false;
881                for (ReturnNode retNode : snippet.getNodes(ReturnNode.TYPE)) {
882                    MemoryMapNode memoryMap = retNode.getMemoryMap();
883                    if (memoryMap.getLocations().size() > 1 || memoryMap.getLastLocationAccess(LocationIdentity.any()) != anchor) {
884                        needsMemoryMaps = true;
885                        break;
886                    }
887                }
888                boolean needsAnchor;
889                if (needsMemoryMaps) {
890                    needsAnchor = true;
891                } else {
892                    // Check that all those memory maps where the only usages of the anchor
893                    needsAnchor = anchor.usages().filter(isNotA(MemoryMapNode.class)).isNotEmpty();
894                    // Remove the useless memory map
895                    MemoryMapNode memoryMap = null;
896                    for (ReturnNode retNode : snippet.getNodes(ReturnNode.TYPE)) {
897                        if (memoryMap == null) {
898                            memoryMap = retNode.getMemoryMap();
899                        } else {
900                            assert memoryMap == retNode.getMemoryMap();
901                        }
902                        retNode.setMemoryMap(null);
903                    }
904                    memoryMap.safeDelete();
905                }
906                if (needsAnchor) {
907                    snippetCopy.addAfterFixed(snippetCopy.start(), anchor);
908                    this.memoryAnchor = anchor;
909                } else {
910                    anchor.safeDelete();
911                    this.memoryAnchor = null;
912                }
913            }
914            debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate after fixing memory anchoring");
915
916            List<ReturnNode> returnNodes = snippet.getNodes(ReturnNode.TYPE).snapshot();
917            if (returnNodes.isEmpty()) {
918                this.returnNode = null;
919            } else if (returnNodes.size() == 1) {
920                this.returnNode = returnNodes.get(0);
921            } else {
922                AbstractMergeNode merge = snippet.add(new MergeNode());
923                List<MemoryMapNode> memMaps = new ArrayList<>();
924                for (ReturnNode retNode : returnNodes) {
925                    MemoryMapNode memoryMapNode = retNode.getMemoryMap();
926                    if (memoryMapNode != null) {
927                        memMaps.add(memoryMapNode);
928                    }
929                }
930
931                ValueNode returnValue = InliningUtil.mergeReturns(merge, returnNodes);
932                this.returnNode = snippet.add(new ReturnNode(returnValue));
933                if (!memMaps.isEmpty()) {
934                    MemoryMapImpl mmap = FloatingReadPhase.mergeMemoryMaps(merge, memMaps);
935                    MemoryMapNode memoryMap = snippet.unique(new MemoryMapNode(mmap.getMap()));
936                    this.returnNode.setMemoryMap(memoryMap);
937                    for (MemoryMapNode mm : memMaps) {
938                        if (mm != memoryMap && mm.isAlive()) {
939                            assert mm.hasNoUsages();
940                            GraphUtil.killWithUnusedFloatingInputs(mm);
941                        }
942                    }
943                }
944                merge.setNext(this.returnNode);
945            }
946
947            this.sideEffectNodes = curSideEffectNodes;
948            this.deoptNodes = curDeoptNodes;
949            this.placeholderStampedNodes = curPlaceholderStampedNodes;
950
951            nodes = new ArrayList<>(snippet.getNodeCount());
952            for (Node node : snippet.getNodes()) {
953                if (node != entryPointNode && node != entryPointNode.stateAfter()) {
954                    nodes.add(node);
955                }
956            }
957
958            if (debug.areMetricsEnabled()) {
959                DebugContext.counter("SnippetTemplateNodeCount[%#s]", args).add(debug, nodes.size());
960            }
961            debug.dump(DebugContext.INFO_LEVEL, snippet, "SnippetTemplate final state");
962            this.snippet.freeze();
963
964        } catch (Throwable ex) {
965            throw debug.handle(ex);
966        }
967    }
968
969    public static void explodeLoops(final StructuredGraph snippetCopy, PhaseContext phaseContext) {
970        // Do any required loop explosion
971        boolean exploded = false;
972        do {
973            exploded = false;
974            ExplodeLoopNode explodeLoop = snippetCopy.getNodes().filter(ExplodeLoopNode.class).first();
975            if (explodeLoop != null) { // Earlier canonicalization may have removed the loop
976                // altogether
977                LoopBeginNode loopBegin = explodeLoop.findLoopBegin();
978                if (loopBegin != null) {
979                    LoopEx loop = new LoopsData(snippetCopy).loop(loopBegin);
980                    Mark mark = snippetCopy.getMark();
981                    LoopTransformations.fullUnroll(loop, phaseContext, new CanonicalizerPhase());
982                    new CanonicalizerPhase().applyIncremental(snippetCopy, phaseContext, mark);
983                    loop.deleteUnusedNodes();
984                }
985                GraphUtil.removeFixedWithUnusedInputs(explodeLoop);
986                exploded = true;
987            }
988        } while (exploded);
989    }
990
991    protected Object[] getConstantArgs(Arguments args) {
992        Object[] constantArgs = args.values.clone();
993        for (int i = 0; i < args.info.getParameterCount(); i++) {
994            if (!args.info.isConstantParameter(i)) {
995                constantArgs[i] = null;
996            } else {
997                assert constantArgs[i] != null : "Can't pass raw null through as argument";
998            }
999        }
1000        return constantArgs;
1001    }
1002
1003    private static boolean checkAllVarargPlaceholdersAreDeleted(int parameterCount, VarargsPlaceholderNode[] placeholders) {
1004        for (int i = 0; i < parameterCount; i++) {
1005            if (placeholders[i] != null) {
1006                assert placeholders[i].isDeleted() : placeholders[i];
1007            }
1008        }
1009        return true;
1010    }
1011
1012    private static boolean checkConstantArgument(MetaAccessProvider metaAccess, final ResolvedJavaMethod method, Signature signature, int i, String name, Object arg, JavaKind kind) {
1013        ResolvedJavaType type = signature.getParameterType(i, method.getDeclaringClass()).resolve(method.getDeclaringClass());
1014        if (metaAccess.lookupJavaType(WordBase.class).isAssignableFrom(type)) {
1015            assert arg instanceof JavaConstant : method + ": word constant parameters must be passed boxed in a Constant value: " + arg;
1016            return true;
1017        }
1018        if (kind != JavaKind.Object) {
1019            assert arg != null && kind.toBoxedJavaClass() == arg.getClass() : method + ": wrong value kind for " + name + ": expected " + kind + ", got " +
1020                            (arg == null ? "null" : arg.getClass().getSimpleName());
1021        }
1022        return true;
1023    }
1024
1025    private static boolean checkVarargs(MetaAccessProvider metaAccess, final ResolvedJavaMethod method, Signature signature, int i, String name, Varargs varargs) {
1026        ResolvedJavaType type = (ResolvedJavaType) signature.getParameterType(i, method.getDeclaringClass());
1027        assert type.isArray() : "varargs parameter must be an array type";
1028        assert type.getComponentType().isAssignableFrom(metaAccess.lookupJavaType(varargs.componentType)) : "componentType for " + name + " not matching " + type.toJavaName() + " instance: " +
1029                        varargs.componentType;
1030        return true;
1031    }
1032
1033    /**
1034     * The graph built from the snippet method.
1035     */
1036    private final StructuredGraph snippet;
1037
1038    private final SnippetInfo info;
1039
1040    /**
1041     * The named parameters of this template that must be bound to values during instantiation. For
1042     * a parameter that is still live after specialization, the value in this map is either a
1043     * {@link ParameterNode} instance or a {@link ParameterNode} array. For an eliminated parameter,
1044     * the value is identical to the key.
1045     */
1046    private final Object[] parameters;
1047
1048    /**
1049     * The return node (if any) of the snippet.
1050     */
1051    private final ReturnNode returnNode;
1052
1053    /**
1054     * The memory anchor (if any) of the snippet.
1055     */
1056    private final MemoryAnchorNode memoryAnchor;
1057
1058    /**
1059     * Nodes that inherit the {@link StateSplit#stateAfter()} from the replacee during
1060     * instantiation.
1061     */
1062    private final ArrayList<StateSplit> sideEffectNodes;
1063
1064    /**
1065     * Nodes that inherit a deoptimization {@link FrameState} from the replacee during
1066     * instantiation.
1067     */
1068    private final ArrayList<DeoptimizingNode> deoptNodes;
1069
1070    /**
1071     * Nodes that have a stamp originating from a {@link Placeholder}.
1072     */
1073    private final ArrayList<ValueNode> placeholderStampedNodes;
1074
1075    /**
1076     * The nodes to be inlined when this specialization is instantiated.
1077     */
1078    private final ArrayList<Node> nodes;
1079
1080    /**
1081     * Times instantiations of this template.
1082     *
1083     * @see SnippetInfo#instantiationTimer
1084     */
1085    private final TimerKey instantiationTimer;
1086
1087    /**
1088     * Counts instantiations of this template.
1089     *
1090     * @see SnippetInfo#instantiationCounter
1091     */
1092    private final CounterKey instantiationCounter;
1093
1094    /**
1095     * Gets the instantiation-time bindings to this template's parameters.
1096     *
1097     * @return the map that will be used to bind arguments to parameters when inlining this template
1098     */
1099    private EconomicMap<Node, Node> bind(StructuredGraph replaceeGraph, MetaAccessProvider metaAccess, Arguments args) {
1100        EconomicMap<Node, Node> replacements = EconomicMap.create(Equivalence.IDENTITY);
1101        assert args.info.getParameterCount() == parameters.length : "number of args (" + args.info.getParameterCount() + ") != number of parameters (" + parameters.length + ")";
1102        for (int i = 0; i < parameters.length; i++) {
1103            Object parameter = parameters[i];
1104            assert parameter != null : this + " has no parameter named " + args.info.getParameterName(i);
1105            Object argument = args.values[i];
1106            if (parameter instanceof ParameterNode) {
1107                if (argument instanceof ValueNode) {
1108                    replacements.put((ParameterNode) parameter, (ValueNode) argument);
1109                } else {
1110                    JavaKind kind = ((ParameterNode) parameter).getStackKind();
1111                    assert argument != null || kind == JavaKind.Object : this + " cannot accept null for non-object parameter named " + args.info.getParameterName(i);
1112                    JavaConstant constant = forBoxed(argument, kind);
1113                    replacements.put((ParameterNode) parameter, ConstantNode.forConstant(constant, metaAccess, replaceeGraph));
1114                }
1115            } else if (parameter instanceof ParameterNode[]) {
1116                ParameterNode[] params = (ParameterNode[]) parameter;
1117                Varargs varargs = (Varargs) argument;
1118                int length = params.length;
1119                List<?> list = null;
1120                Object array = null;
1121                if (varargs.value instanceof List) {
1122                    list = (List<?>) varargs.value;
1123                    assert list.size() == length : length + " != " + list.size();
1124                } else {
1125                    array = varargs.value;
1126                    assert array != null && array.getClass().isArray();
1127                    assert Array.getLength(array) == length : length + " != " + Array.getLength(array);
1128                }
1129
1130                for (int j = 0; j < length; j++) {
1131                    ParameterNode param = params[j];
1132                    assert param != null;
1133                    Object value = list != null ? list.get(j) : Array.get(array, j);
1134                    if (value instanceof ValueNode) {
1135                        replacements.put(param, (ValueNode) value);
1136                    } else {
1137                        JavaConstant constant = forBoxed(value, param.getStackKind());
1138                        ConstantNode element = ConstantNode.forConstant(constant, metaAccess, replaceeGraph);
1139                        replacements.put(param, element);
1140                    }
1141                }
1142            } else {
1143                assert parameter.equals(CONSTANT_PARAMETER) || parameter.equals(UNUSED_PARAMETER) : "unexpected entry for parameter: " + args.info.getParameterName(i) + " -> " + parameter;
1144            }
1145        }
1146        return replacements;
1147    }
1148
1149    /**
1150     * Converts a Java boxed value to a {@link JavaConstant} of the right kind. This adjusts for the
1151     * limitation that a {@link Local}'s kind is a {@linkplain JavaKind#getStackKind() stack kind}
1152     * and so cannot be used for re-boxing primitives smaller than an int.
1153     *
1154     * @param argument a Java boxed value
1155     * @param localKind the kind of the {@link Local} to which {@code argument} will be bound
1156     */
1157    protected JavaConstant forBoxed(Object argument, JavaKind localKind) {
1158        assert localKind == localKind.getStackKind();
1159        if (localKind == JavaKind.Int) {
1160            return JavaConstant.forBoxedPrimitive(argument);
1161        }
1162        return snippetReflection.forBoxed(localKind, argument);
1163    }
1164
1165    /**
1166     * Logic for replacing a snippet-lowered node at its usages with the return value of the
1167     * snippet. An alternative to the {@linkplain SnippetTemplate#DEFAULT_REPLACER default}
1168     * replacement logic can be used to handle mismatches between the stamp of the node being
1169     * lowered and the stamp of the snippet's return value.
1170     */
1171    public interface UsageReplacer {
1172        /**
1173         * Replaces all usages of {@code oldNode} with direct or indirect usages of {@code newNode}.
1174         */
1175        void replace(ValueNode oldNode, ValueNode newNode);
1176    }
1177
1178    /**
1179     * Represents the default {@link UsageReplacer usage replacer} logic which simply delegates to
1180     * {@link Node#replaceAtUsages(Node)}.
1181     */
1182    public static final UsageReplacer DEFAULT_REPLACER = new UsageReplacer() {
1183
1184        @Override
1185        public void replace(ValueNode oldNode, ValueNode newNode) {
1186            if (newNode == null) {
1187                assert oldNode.hasNoUsages();
1188            } else {
1189                oldNode.replaceAtUsages(newNode);
1190            }
1191        }
1192    };
1193
1194    private boolean assertSnippetKills(ValueNode replacee) {
1195        if (!replacee.graph().isAfterFloatingReadPhase()) {
1196            // no floating reads yet, ignore locations created while lowering
1197            return true;
1198        }
1199        if (returnNode == null) {
1200            // The snippet terminates control flow
1201            return true;
1202        }
1203        MemoryMapNode memoryMap = returnNode.getMemoryMap();
1204        if (memoryMap == null || memoryMap.isEmpty()) {
1205            // there are no kills in the snippet graph
1206            return true;
1207        }
1208
1209        EconomicSet<LocationIdentity> kills = EconomicSet.create(Equivalence.DEFAULT);
1210        kills.addAll(memoryMap.getLocations());
1211
1212        if (replacee instanceof MemoryCheckpoint.Single) {
1213            // check if some node in snippet graph also kills the same location
1214            LocationIdentity locationIdentity = ((MemoryCheckpoint.Single) replacee).getLocationIdentity();
1215            if (locationIdentity.isAny()) {
1216                assert !(memoryMap.getLastLocationAccess(any()) instanceof MemoryAnchorNode) : replacee + " kills ANY_LOCATION, but snippet does not";
1217                // if the replacee kills ANY_LOCATION, the snippet can kill arbitrary locations
1218                return true;
1219            }
1220            assert kills.contains(locationIdentity) : replacee + " kills " + locationIdentity + ", but snippet doesn't contain a kill to this location";
1221            kills.remove(locationIdentity);
1222        }
1223        assert !(replacee instanceof MemoryCheckpoint.Multi) : replacee + " multi not supported (yet)";
1224
1225        // remove ANY_LOCATION if it's just a kill by the start node
1226        if (memoryMap.getLastLocationAccess(any()) instanceof MemoryAnchorNode) {
1227            kills.remove(any());
1228        }
1229
1230        // node can only lower to a ANY_LOCATION kill if the replacee also kills ANY_LOCATION
1231        assert !kills.contains(any()) : "snippet graph contains a kill to ANY_LOCATION, but replacee (" + replacee + ") doesn't kill ANY_LOCATION.  kills: " + kills;
1232
1233        /*
1234         * Kills to private locations are safe, since there can be no floating read to these
1235         * locations except reads that are introduced by the snippet itself or related snippets in
1236         * the same lowering round. These reads are anchored to a MemoryAnchor at the beginning of
1237         * their snippet, so they can not float above a kill in another instance of the same
1238         * snippet.
1239         */
1240        for (LocationIdentity p : this.info.privateLocations) {
1241            kills.remove(p);
1242        }
1243
1244        assert kills.isEmpty() : "snippet graph kills non-private locations " + kills + " that replacee (" + replacee + ") doesn't kill";
1245        return true;
1246    }
1247
1248    private static class MemoryInputMap implements MemoryMap {
1249
1250        private final LocationIdentity locationIdentity;
1251        private final MemoryNode lastLocationAccess;
1252
1253        MemoryInputMap(ValueNode replacee) {
1254            if (replacee instanceof MemoryAccess) {
1255                MemoryAccess access = (MemoryAccess) replacee;
1256                locationIdentity = access.getLocationIdentity();
1257                lastLocationAccess = access.getLastLocationAccess();
1258            } else {
1259                locationIdentity = null;
1260                lastLocationAccess = null;
1261            }
1262        }
1263
1264        @Override
1265        public MemoryNode getLastLocationAccess(LocationIdentity location) {
1266            if (locationIdentity != null && locationIdentity.equals(location)) {
1267                return lastLocationAccess;
1268            } else {
1269                return null;
1270            }
1271        }
1272
1273        @Override
1274        public Collection<LocationIdentity> getLocations() {
1275            if (locationIdentity == null) {
1276                return Collections.emptySet();
1277            } else {
1278                return Collections.singleton(locationIdentity);
1279            }
1280        }
1281    }
1282
1283    private class MemoryOutputMap extends MemoryInputMap {
1284
1285        private final UnmodifiableEconomicMap<Node, Node> duplicates;
1286
1287        MemoryOutputMap(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1288            super(replacee);
1289            this.duplicates = duplicates;
1290        }
1291
1292        @Override
1293        public MemoryNode getLastLocationAccess(LocationIdentity locationIdentity) {
1294            MemoryMapNode memoryMap = returnNode.getMemoryMap();
1295            assert memoryMap != null : "no memory map stored for this snippet graph (snippet doesn't have a ReturnNode?)";
1296            MemoryNode lastLocationAccess = memoryMap.getLastLocationAccess(locationIdentity);
1297            assert lastLocationAccess != null : locationIdentity;
1298            if (lastLocationAccess == memoryAnchor) {
1299                return super.getLastLocationAccess(locationIdentity);
1300            } else {
1301                return (MemoryNode) duplicates.get(ValueNodeUtil.asNode(lastLocationAccess));
1302            }
1303        }
1304
1305        @Override
1306        public Collection<LocationIdentity> getLocations() {
1307            return returnNode.getMemoryMap().getLocations();
1308        }
1309    }
1310
1311    private void rewireMemoryGraph(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1312        if (replacee.graph().isAfterFloatingReadPhase()) {
1313            // rewire outgoing memory edges
1314            replaceMemoryUsages(replacee, new MemoryOutputMap(replacee, duplicates));
1315
1316            if (returnNode != null) {
1317                ReturnNode ret = (ReturnNode) duplicates.get(returnNode);
1318                if (ret != null) {
1319                    MemoryMapNode memoryMap = ret.getMemoryMap();
1320                    if (memoryMap != null) {
1321                        ret.setMemoryMap(null);
1322                        memoryMap.safeDelete();
1323                    }
1324                }
1325            }
1326            if (memoryAnchor != null) {
1327                // rewire incoming memory edges
1328                MemoryAnchorNode memoryDuplicate = (MemoryAnchorNode) duplicates.get(memoryAnchor);
1329                replaceMemoryUsages(memoryDuplicate, new MemoryInputMap(replacee));
1330
1331                if (memoryDuplicate.hasNoUsages()) {
1332                    if (memoryDuplicate.next() != null) {
1333                        memoryDuplicate.graph().removeFixed(memoryDuplicate);
1334                    } else {
1335                        // this was a dummy memory node used when instantiating pure data-flow
1336                        // snippets: it was not attached to the control flow.
1337                        memoryDuplicate.safeDelete();
1338                    }
1339                }
1340            }
1341        }
1342    }
1343
1344    private static LocationIdentity getLocationIdentity(Node node) {
1345        if (node instanceof MemoryAccess) {
1346            return ((MemoryAccess) node).getLocationIdentity();
1347        } else if (node instanceof MemoryProxy) {
1348            return ((MemoryProxy) node).getLocationIdentity();
1349        } else if (node instanceof MemoryPhiNode) {
1350            return ((MemoryPhiNode) node).getLocationIdentity();
1351        } else {
1352            return null;
1353        }
1354    }
1355
1356    private void replaceMemoryUsages(ValueNode node, MemoryMap map) {
1357        for (Node usage : node.usages().snapshot()) {
1358            if (usage instanceof MemoryMapNode) {
1359                continue;
1360            }
1361
1362            LocationIdentity location = getLocationIdentity(usage);
1363            if (location != null) {
1364                for (Position pos : usage.inputPositions()) {
1365                    if (pos.getInputType() == InputType.Memory && pos.get(usage) == node) {
1366                        MemoryNode replacement = map.getLastLocationAccess(location);
1367                        if (replacement == null) {
1368                            assert mayRemoveLocation || LocationIdentity.any().equals(location) ||
1369                                            CollectionsUtil.anyMatch(info.privateLocations, Predicate.isEqual(location)) : "Snippet " +
1370                                                            info.method.format("%h.%n") + " contains access to the non-private location " +
1371                                                            location + ", but replacee doesn't access this location." + map.getLocations();
1372                        } else {
1373                            pos.set(usage, replacement.asNode());
1374                        }
1375                    }
1376                }
1377            }
1378        }
1379    }
1380
1381    /**
1382     * Replaces a given fixed node with this specialized snippet.
1383     *
1384     * @param metaAccess
1385     * @param replacee the node that will be replaced
1386     * @param replacer object that replaces the usages of {@code replacee}
1387     * @param args the arguments to be bound to the flattened positional parameters of the snippet
1388     * @return the map of duplicated nodes (original -&gt; duplicate)
1389     */
1390    @SuppressWarnings("try")
1391    public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args) {
1392        return instantiate(metaAccess, replacee, replacer, args, true);
1393    }
1394
1395    /**
1396     * Replaces a given fixed node with this specialized snippet.
1397     *
1398     * @param metaAccess
1399     * @param replacee the node that will be replaced
1400     * @param replacer object that replaces the usages of {@code replacee}
1401     * @param args the arguments to be bound to the flattened positional parameters of the snippet
1402     * @param killReplacee is true, the replacee node is deleted
1403     * @return the map of duplicated nodes (original -&gt; duplicate)
1404     */
1405    @SuppressWarnings("try")
1406    public UnmodifiableEconomicMap<Node, Node> instantiate(MetaAccessProvider metaAccess, FixedNode replacee, UsageReplacer replacer, Arguments args, boolean killReplacee) {
1407        DebugContext debug = replacee.getDebug();
1408        assert assertSnippetKills(replacee);
1409        try (DebugCloseable a = args.info.instantiationTimer.start(debug); DebugCloseable b = instantiationTimer.start(debug)) {
1410            args.info.instantiationCounter.increment(debug);
1411            instantiationCounter.increment(debug);
1412            // Inline the snippet nodes, replacing parameters with the given args in the process
1413            StartNode entryPointNode = snippet.start();
1414            FixedNode firstCFGNode = entryPointNode.next();
1415            StructuredGraph replaceeGraph = replacee.graph();
1416            EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1417            replacements.put(entryPointNode, AbstractBeginNode.prevBegin(replacee));
1418            UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
1419            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
1420
1421            // Re-wire the control flow graph around the replacee
1422            FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
1423            replacee.replaceAtPredecessor(firstCFGNodeDuplicate);
1424
1425            rewireFrameStates(replacee, duplicates);
1426
1427            if (replacee instanceof DeoptimizingNode) {
1428                DeoptimizingNode replaceeDeopt = (DeoptimizingNode) replacee;
1429
1430                FrameState stateBefore = null;
1431                FrameState stateDuring = null;
1432                FrameState stateAfter = null;
1433                if (replaceeDeopt.canDeoptimize()) {
1434                    if (replaceeDeopt instanceof DeoptimizingNode.DeoptBefore) {
1435                        stateBefore = ((DeoptimizingNode.DeoptBefore) replaceeDeopt).stateBefore();
1436                    }
1437                    if (replaceeDeopt instanceof DeoptimizingNode.DeoptDuring) {
1438                        stateDuring = ((DeoptimizingNode.DeoptDuring) replaceeDeopt).stateDuring();
1439                    }
1440                    if (replaceeDeopt instanceof DeoptimizingNode.DeoptAfter) {
1441                        stateAfter = ((DeoptimizingNode.DeoptAfter) replaceeDeopt).stateAfter();
1442                    }
1443                }
1444
1445                for (DeoptimizingNode deoptNode : deoptNodes) {
1446                    DeoptimizingNode deoptDup = (DeoptimizingNode) duplicates.get(deoptNode.asNode());
1447                    if (deoptDup.canDeoptimize()) {
1448                        if (deoptDup instanceof DeoptimizingNode.DeoptBefore) {
1449                            ((DeoptimizingNode.DeoptBefore) deoptDup).setStateBefore(stateBefore);
1450                        }
1451                        if (deoptDup instanceof DeoptimizingNode.DeoptDuring) {
1452                            DeoptimizingNode.DeoptDuring deoptDupDuring = (DeoptimizingNode.DeoptDuring) deoptDup;
1453                            if (stateDuring != null) {
1454                                deoptDupDuring.setStateDuring(stateDuring);
1455                            } else if (stateAfter != null) {
1456                                deoptDupDuring.computeStateDuring(stateAfter);
1457                            } else if (stateBefore != null) {
1458                                assert !deoptDupDuring.hasSideEffect() : "can't use stateBefore as stateDuring for state split " + deoptDupDuring;
1459                                deoptDupDuring.setStateDuring(stateBefore);
1460                            }
1461                        }
1462                        if (deoptDup instanceof DeoptimizingNode.DeoptAfter) {
1463                            DeoptimizingNode.DeoptAfter deoptDupAfter = (DeoptimizingNode.DeoptAfter) deoptDup;
1464                            if (stateAfter != null) {
1465                                deoptDupAfter.setStateAfter(stateAfter);
1466                            } else {
1467                                assert !deoptDupAfter.hasSideEffect() : "can't use stateBefore as stateAfter for state split " + deoptDupAfter;
1468                                deoptDupAfter.setStateAfter(stateBefore);
1469                            }
1470
1471                        }
1472                    }
1473                }
1474            }
1475
1476            updateStamps(replacee, duplicates);
1477
1478            rewireMemoryGraph(replacee, duplicates);
1479
1480            // Replace all usages of the replacee with the value returned by the snippet
1481            ValueNode returnValue = null;
1482            if (returnNode != null && !(replacee instanceof ControlSinkNode)) {
1483                ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
1484                returnValue = returnDuplicate.result();
1485                if (returnValue == null && replacee.usages().isNotEmpty() && replacee instanceof MemoryCheckpoint) {
1486                    replacer.replace(replacee, null);
1487                } else {
1488                    assert returnValue != null || replacee.hasNoUsages();
1489                    replacer.replace(replacee, returnValue);
1490                }
1491                if (returnDuplicate.isAlive()) {
1492                    FixedNode next = null;
1493                    if (replacee instanceof FixedWithNextNode) {
1494                        FixedWithNextNode fwn = (FixedWithNextNode) replacee;
1495                        next = fwn.next();
1496                        fwn.setNext(null);
1497                    }
1498                    returnDuplicate.replaceAndDelete(next);
1499                }
1500            }
1501
1502            if (killReplacee) {
1503                // Remove the replacee from its graph
1504                GraphUtil.killCFG(replacee);
1505            }
1506
1507            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1508            return duplicates;
1509        }
1510    }
1511
1512    private void propagateStamp(Node node) {
1513        if (node instanceof PhiNode) {
1514            PhiNode phi = (PhiNode) node;
1515            if (phi.inferStamp()) {
1516                for (Node usage : node.usages()) {
1517                    propagateStamp(usage);
1518                }
1519            }
1520        }
1521    }
1522
1523    private void updateStamps(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1524        for (ValueNode node : placeholderStampedNodes) {
1525            ValueNode dup = (ValueNode) duplicates.get(node);
1526            Stamp replaceeStamp = replacee.stamp();
1527            if (node instanceof Placeholder) {
1528                Placeholder placeholderDup = (Placeholder) dup;
1529                placeholderDup.makeReplacement(replaceeStamp);
1530            } else {
1531                dup.setStamp(replaceeStamp);
1532            }
1533        }
1534        for (ParameterNode paramNode : snippet.getNodes(ParameterNode.TYPE)) {
1535            for (Node usage : paramNode.usages()) {
1536                Node usageDup = duplicates.get(usage);
1537                propagateStamp(usageDup);
1538            }
1539        }
1540    }
1541
1542    /**
1543     * Gets a copy of the specialized graph.
1544     */
1545    public StructuredGraph copySpecializedGraph(DebugContext debugForCopy) {
1546        return (StructuredGraph) snippet.copy(debugForCopy);
1547    }
1548
1549    /**
1550     * Replaces a given floating node with this specialized snippet.
1551     *
1552     * @param metaAccess
1553     * @param replacee the node that will be replaced
1554     * @param replacer object that replaces the usages of {@code replacee}
1555     * @param tool lowering tool used to insert the snippet into the control-flow
1556     * @param args the arguments to be bound to the flattened positional parameters of the snippet
1557     */
1558    @SuppressWarnings("try")
1559    public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, LoweringTool tool, Arguments args) {
1560        DebugContext debug = replacee.getDebug();
1561        assert assertSnippetKills(replacee);
1562        try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
1563            args.info.instantiationCounter.increment(debug);
1564            instantiationCounter.increment(debug);
1565
1566            // Inline the snippet nodes, replacing parameters with the given args in the process
1567            StartNode entryPointNode = snippet.start();
1568            FixedNode firstCFGNode = entryPointNode.next();
1569            StructuredGraph replaceeGraph = replacee.graph();
1570            EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1571            replacements.put(entryPointNode, tool.getCurrentGuardAnchor().asNode());
1572            UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(nodes, snippet, snippet.getNodeCount(), replacements);
1573            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
1574
1575            FixedWithNextNode lastFixedNode = tool.lastFixedNode();
1576            assert lastFixedNode != null && lastFixedNode.isAlive() : replaceeGraph + " lastFixed=" + lastFixedNode;
1577            FixedNode next = lastFixedNode.next();
1578            lastFixedNode.setNext(null);
1579            FixedNode firstCFGNodeDuplicate = (FixedNode) duplicates.get(firstCFGNode);
1580            replaceeGraph.addAfterFixed(lastFixedNode, firstCFGNodeDuplicate);
1581
1582            rewireFrameStates(replacee, duplicates);
1583            updateStamps(replacee, duplicates);
1584
1585            rewireMemoryGraph(replacee, duplicates);
1586
1587            // Replace all usages of the replacee with the value returned by the snippet
1588            ReturnNode returnDuplicate = (ReturnNode) duplicates.get(returnNode);
1589            ValueNode returnValue = returnDuplicate.result();
1590            assert returnValue != null || replacee.hasNoUsages();
1591            replacer.replace(replacee, returnValue);
1592
1593            if (returnDuplicate.isAlive()) {
1594                returnDuplicate.replaceAndDelete(next);
1595            }
1596
1597            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1598        }
1599    }
1600
1601    /**
1602     * Replaces a given floating node with this specialized snippet.
1603     *
1604     * This snippet must be pure data-flow
1605     *
1606     * @param metaAccess
1607     * @param replacee the node that will be replaced
1608     * @param replacer object that replaces the usages of {@code replacee}
1609     * @param args the arguments to be bound to the flattened positional parameters of the snippet
1610     */
1611    @SuppressWarnings("try")
1612    public void instantiate(MetaAccessProvider metaAccess, FloatingNode replacee, UsageReplacer replacer, Arguments args) {
1613        DebugContext debug = replacee.getDebug();
1614        assert assertSnippetKills(replacee);
1615        try (DebugCloseable a = args.info.instantiationTimer.start(debug)) {
1616            args.info.instantiationCounter.increment(debug);
1617            instantiationCounter.increment(debug);
1618
1619            // Inline the snippet nodes, replacing parameters with the given args in the process
1620            StartNode entryPointNode = snippet.start();
1621            assert entryPointNode.next() == (memoryAnchor == null ? returnNode : memoryAnchor) : entryPointNode.next();
1622            StructuredGraph replaceeGraph = replacee.graph();
1623            EconomicMap<Node, Node> replacements = bind(replaceeGraph, metaAccess, args);
1624            MemoryAnchorNode anchorDuplicate = null;
1625            if (memoryAnchor != null) {
1626                anchorDuplicate = replaceeGraph.add(new MemoryAnchorNode());
1627                replacements.put(memoryAnchor, anchorDuplicate);
1628            }
1629            List<Node> floatingNodes = new ArrayList<>(nodes.size() - 2);
1630            for (Node n : nodes) {
1631                if (n != entryPointNode && n != returnNode) {
1632                    floatingNodes.add(n);
1633                }
1634            }
1635            UnmodifiableEconomicMap<Node, Node> duplicates = replaceeGraph.addDuplicates(floatingNodes, snippet, floatingNodes.size(), replacements);
1636            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After inlining snippet %s", snippet.method());
1637
1638            rewireFrameStates(replacee, duplicates);
1639            updateStamps(replacee, duplicates);
1640
1641            rewireMemoryGraph(replacee, duplicates);
1642            assert anchorDuplicate == null || anchorDuplicate.isDeleted();
1643
1644            // Replace all usages of the replacee with the value returned by the snippet
1645            ValueNode returnValue = (ValueNode) duplicates.get(returnNode.result());
1646            replacer.replace(replacee, returnValue);
1647
1648            debug.dump(DebugContext.DETAILED_LEVEL, replaceeGraph, "After lowering %s with %s", replacee, this);
1649        }
1650    }
1651
1652    protected void rewireFrameStates(ValueNode replacee, UnmodifiableEconomicMap<Node, Node> duplicates) {
1653        if (replacee instanceof StateSplit) {
1654            for (StateSplit sideEffectNode : sideEffectNodes) {
1655                assert ((StateSplit) replacee).hasSideEffect();
1656                Node sideEffectDup = duplicates.get(sideEffectNode.asNode());
1657                ((StateSplit) sideEffectDup).setStateAfter(((StateSplit) replacee).stateAfter());
1658            }
1659        }
1660    }
1661
1662    @Override
1663    public String toString() {
1664        StringBuilder buf = new StringBuilder(snippet.toString()).append('(');
1665        String sep = "";
1666        for (int i = 0; i < parameters.length; i++) {
1667            String name = "[" + i + "]";
1668            Object value = parameters[i];
1669            buf.append(sep);
1670            sep = ", ";
1671            if (value == null) {
1672                buf.append("<null> ").append(name);
1673            } else if (value.equals(UNUSED_PARAMETER)) {
1674                buf.append("<unused> ").append(name);
1675            } else if (value.equals(CONSTANT_PARAMETER)) {
1676                buf.append("<constant> ").append(name);
1677            } else if (value instanceof ParameterNode) {
1678                ParameterNode param = (ParameterNode) value;
1679                buf.append(param.getStackKind().getJavaName()).append(' ').append(name);
1680            } else {
1681                ParameterNode[] params = (ParameterNode[]) value;
1682                String kind = params.length == 0 ? "?" : params[0].getStackKind().getJavaName();
1683                buf.append(kind).append('[').append(params.length).append("] ").append(name);
1684            }
1685        }
1686        return buf.append(')').toString();
1687    }
1688
1689    private static boolean checkTemplate(MetaAccessProvider metaAccess, Arguments args, ResolvedJavaMethod method, Signature signature) {
1690        for (int i = 0; i < args.info.getParameterCount(); i++) {
1691            if (args.info.isConstantParameter(i)) {
1692                JavaKind kind = signature.getParameterKind(i);
1693                assert checkConstantArgument(metaAccess, method, signature, i, args.info.getParameterName(i), args.values[i], kind);
1694
1695            } else if (args.info.isVarargsParameter(i)) {
1696                assert args.values[i] instanceof Varargs;
1697                Varargs varargs = (Varargs) args.values[i];
1698                assert checkVarargs(metaAccess, method, signature, i, args.info.getParameterName(i), varargs);
1699            }
1700        }
1701        return true;
1702    }
1703
1704    public void setMayRemoveLocation(boolean mayRemoveLocation) {
1705        this.mayRemoveLocation = mayRemoveLocation;
1706    }
1707}
1708