001/*
002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved.
003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004 *
005 * This code is free software; you can redistribute it and/or modify it
006 * under the terms of the GNU General Public License version 2 only, as
007 * published by the Free Software Foundation.
008 *
009 * This code is distributed in the hope that it will be useful, but WITHOUT
010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
011 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
012 * version 2 for more details (a copy is included in the LICENSE file that
013 * accompanied this code).
014 *
015 * You should have received a copy of the GNU General Public License version
016 * 2 along with this work; if not, write to the Free Software Foundation,
017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
018 *
019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
020 * or visit www.oracle.com if you need additional information or have any
021 * questions.
022 */
023package com.oracle.graal.replacements;
024
025import static com.oracle.graal.compiler.common.GraalOptions.*;
026import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*;
027import static com.oracle.graal.java.BytecodeParser.Options.*;
028import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*;
029import static java.lang.String.*;
030import static jdk.internal.jvmci.meta.MetaUtil.*;
031
032import java.lang.reflect.*;
033import java.util.*;
034import java.util.concurrent.*;
035import java.util.concurrent.atomic.*;
036
037import jdk.internal.jvmci.code.*;
038import jdk.internal.jvmci.common.*;
039import com.oracle.graal.debug.*;
040import com.oracle.graal.debug.Debug.*;
041import jdk.internal.jvmci.meta.*;
042import jdk.internal.jvmci.options.*;
043import jdk.internal.jvmci.options.OptionValue.*;
044import sun.misc.*;
045
046import com.oracle.graal.api.replacements.*;
047import com.oracle.graal.compiler.common.*;
048import com.oracle.graal.graph.*;
049import com.oracle.graal.graph.Node.NodeIntrinsic;
050import com.oracle.graal.graphbuilderconf.*;
051import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins;
052import com.oracle.graal.java.*;
053import com.oracle.graal.java.GraphBuilderPhase.Instance;
054import com.oracle.graal.nodes.*;
055import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions;
056import com.oracle.graal.nodes.java.*;
057import com.oracle.graal.nodes.spi.*;
058import com.oracle.graal.phases.*;
059import com.oracle.graal.phases.common.*;
060import com.oracle.graal.phases.tiers.*;
061import com.oracle.graal.phases.util.*;
062import com.oracle.graal.word.*;
063
064public class ReplacementsImpl implements Replacements, InlineInvokePlugin {
065
066    public final Providers providers;
067    public final SnippetReflectionProvider snippetReflection;
068    public final TargetDescription target;
069    private GraphBuilderConfiguration.Plugins graphBuilderPlugins;
070
071    /**
072     * The preprocessed replacement graphs.
073     */
074    protected final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphs;
075
076    public void setGraphBuilderPlugins(GraphBuilderConfiguration.Plugins plugins) {
077        assert this.graphBuilderPlugins == null;
078        this.graphBuilderPlugins = plugins;
079    }
080
081    public GraphBuilderConfiguration.Plugins getGraphBuilderPlugins() {
082        return graphBuilderPlugins;
083    }
084
085    protected boolean hasGenericInvocationPluginAnnotation(ResolvedJavaMethod method) {
086        return method.getAnnotation(Node.NodeIntrinsic.class) != null || method.getAnnotation(Word.Operation.class) != null || method.getAnnotation(Fold.class) != null;
087    }
088
089    private static final int MAX_GRAPH_INLINING_DEPTH = 100; // more than enough
090
091    /**
092     * Determines whether a given method should be inlined based on whether it has a substitution or
093     * whether the inlining context is already within a substitution.
094     *
095     * @return an object specifying how {@code method} is to be inlined or null if it should not be
096     *         inlined based on substitution related criteria
097     */
098    @Override
099    public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) {
100        ResolvedJavaMethod subst = getSubstitutionMethod(method);
101        if (subst != null) {
102            if (b.parsingIntrinsic() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) {
103                // Forced inlining of intrinsics
104                return new InlineInfo(subst, true);
105            }
106            return null;
107        }
108        if (b.parsingIntrinsic()) {
109            assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), NodeIntrinsificationPlugin.class.getName());
110
111            assert b.getDepth() < MAX_GRAPH_INLINING_DEPTH : "inlining limit exceeded";
112
113            if (method.getName().startsWith("$jacoco")) {
114                throw new JVMCIError("Found call to JaCoCo instrumentation method " + method.format("%H.%n(%p)") + ". Placing \"//JaCoCo Exclude\" anywhere in " +
115                                b.getMethod().getDeclaringClass().getSourceFileName() + " should fix this.");
116            }
117
118            // Force inlining when parsing replacements
119            return new InlineInfo(method, true);
120        } else {
121            assert method.getAnnotation(NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", NodeIntrinsic.class.getSimpleName(),
122                            method.format("%h.%n"), b);
123        }
124        return null;
125    }
126
127    @Override
128    public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) {
129        if (b.parsingIntrinsic()) {
130            IntrinsicContext intrinsic = b.getIntrinsic();
131            assert intrinsic.isCallToOriginal(method) : format("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s",
132                            intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)"));
133        }
134    }
135
136    /**
137     * Encapsulates method and macro substitutions for a single class.
138     */
139    protected class ClassReplacements {
140        public final Map<ResolvedJavaMethod, ResolvedJavaMethod> methodSubstitutions = CollectionsFactory.newMap();
141
142        public ClassReplacements(Class<?>[] substitutionClasses, AtomicReference<ClassReplacements> ref) {
143            for (Class<?> substitutionClass : substitutionClasses) {
144                ClassSubstitution classSubstitution = substitutionClass.getAnnotation(ClassSubstitution.class);
145                assert !Snippets.class.isAssignableFrom(substitutionClass);
146                SubstitutionGuard defaultGuard = getGuard(classSubstitution.defaultGuard());
147                for (Method substituteMethod : substitutionClass.getDeclaredMethods()) {
148                    if (ref.get() != null) {
149                        // Bail if another thread beat us creating the substitutions
150                        return;
151                    }
152                    MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class);
153                    if (methodSubstitution == null) {
154                        continue;
155                    }
156
157                    int modifiers = substituteMethod.getModifiers();
158                    if (!Modifier.isStatic(modifiers)) {
159                        throw new JVMCIError("Substitution methods must be static: " + substituteMethod);
160                    }
161
162                    if (methodSubstitution != null) {
163                        SubstitutionGuard guard = getGuard(methodSubstitution.guard());
164                        if (guard == null) {
165                            guard = defaultGuard;
166                        }
167
168                        if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) {
169                            throw new JVMCIError("Substitution method must not be abstract or native: " + substituteMethod);
170                        }
171                        String originalName = originalName(substituteMethod, methodSubstitution.value());
172                        JavaSignature originalSignature = originalSignature(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic());
173                        Executable[] originalMethods = originalMethods(classSubstitution, classSubstitution.optional(), originalName, originalSignature);
174                        if (originalMethods != null) {
175                            for (Executable originalMethod : originalMethods) {
176                                if (originalMethod != null && (guard == null || guard.execute())) {
177                                    registerMethodSubstitution(this, originalMethod, substituteMethod);
178                                }
179                            }
180                        }
181                    }
182                }
183            }
184        }
185
186        private JavaSignature originalSignature(Method substituteMethod, String methodSubstitution, boolean isStatic) {
187            Class<?>[] parameters;
188            Class<?> returnType;
189            if (methodSubstitution.isEmpty()) {
190                parameters = substituteMethod.getParameterTypes();
191                if (!isStatic) {
192                    assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter";
193                    parameters = Arrays.copyOfRange(parameters, 1, parameters.length);
194                }
195                returnType = substituteMethod.getReturnType();
196            } else {
197                Signature signature = providers.getMetaAccess().parseMethodDescriptor(methodSubstitution);
198                parameters = new Class[signature.getParameterCount(false)];
199                for (int i = 0; i < parameters.length; i++) {
200                    parameters[i] = resolveClass(signature.getParameterType(i, null));
201                }
202                returnType = resolveClass(signature.getReturnType(null));
203            }
204            return new JavaSignature(returnType, parameters);
205        }
206
207        private Executable[] originalMethods(ClassSubstitution classSubstitution, boolean optional, String name, JavaSignature signature) {
208            Class<?> originalClass = classSubstitution.value();
209            if (originalClass == ClassSubstitution.class) {
210                ArrayList<Executable> result = new ArrayList<>();
211                for (String className : classSubstitution.className()) {
212                    originalClass = resolveClass(className, classSubstitution.optional());
213                    if (originalClass != null) {
214                        result.add(lookupOriginalMethod(originalClass, name, signature, optional));
215                    }
216                }
217                if (result.size() == 0) {
218                    // optional class was not found
219                    return null;
220                }
221                return result.toArray(new Executable[result.size()]);
222            }
223            Executable original = lookupOriginalMethod(originalClass, name, signature, optional);
224            if (original != null) {
225                return new Executable[]{original};
226            }
227            return null;
228        }
229
230        private Executable lookupOriginalMethod(Class<?> originalClass, String name, JavaSignature signature, boolean optional) throws JVMCIError {
231            try {
232                if (name.equals("<init>")) {
233                    assert signature.returnType.equals(void.class) : signature;
234                    Constructor<?> original = originalClass.getDeclaredConstructor(signature.parameters);
235                    return original;
236                } else {
237                    Method original = originalClass.getDeclaredMethod(name, signature.parameters);
238                    if (!original.getReturnType().equals(signature.returnType)) {
239                        throw new NoSuchMethodException(originalClass.getName() + "." + name + signature);
240                    }
241                    return original;
242                }
243            } catch (NoSuchMethodException | SecurityException e) {
244                if (optional) {
245                    return null;
246                }
247                throw new JVMCIError(e);
248            }
249        }
250    }
251
252    /**
253     * Per-class replacements. The entries in these maps are all fully initialized during
254     * single-threaded compiler startup and so do not need to be concurrent.
255     */
256    private final Map<String, AtomicReference<ClassReplacements>> classReplacements;
257    private final Map<String, Class<?>[]> internalNameToSubstitutionClasses;
258
259    // This map is key'ed by a class name instead of a Class object so that
260    // it is stable across VM executions (in support of replay compilation).
261    private final Map<String, SnippetTemplateCache> snippetTemplateCache;
262
263    public ReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) {
264        this.providers = providers.copyWith(this);
265        this.classReplacements = CollectionsFactory.newMap();
266        this.internalNameToSubstitutionClasses = CollectionsFactory.newMap();
267        this.snippetReflection = snippetReflection;
268        this.target = target;
269        this.graphs = new ConcurrentHashMap<>();
270        this.snippetTemplateCache = CollectionsFactory.newMap();
271    }
272
273    private static final boolean UseSnippetGraphCache = Boolean.parseBoolean(System.getProperty("graal.useSnippetGraphCache", "true"));
274    private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime");
275
276    /**
277     * Gets the method and macro replacements for a given class. This method will parse the
278     * replacements in the substitution classes associated with {@code internalName} the first time
279     * this method is called for {@code internalName}.
280     */
281    protected ClassReplacements getClassReplacements(String internalName) {
282        Class<?>[] substitutionClasses = internalNameToSubstitutionClasses.get(internalName);
283        if (substitutionClasses != null) {
284            AtomicReference<ClassReplacements> crRef = classReplacements.get(internalName);
285            if (crRef.get() == null) {
286                crRef.compareAndSet(null, new ClassReplacements(substitutionClasses, crRef));
287            }
288            return crRef.get();
289        }
290        return null;
291    }
292
293    public StructuredGraph getSnippet(ResolvedJavaMethod method, Object[] args) {
294        return getSnippet(method, null, args);
295    }
296
297    @Override
298    public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args) {
299        assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName();
300        assert method.hasBytecodes() : "Snippet must not be abstract or native";
301
302        StructuredGraph graph = UseSnippetGraphCache ? graphs.get(method) : null;
303        if (graph == null) {
304            try (DebugCloseable a = SnippetPreparationTime.start()) {
305                StructuredGraph newGraph = makeGraph(method, args, recursiveEntry);
306                Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
307                if (!UseSnippetGraphCache || args != null) {
308                    return newGraph;
309                }
310                graphs.putIfAbsent(method, newGraph);
311                graph = graphs.get(method);
312            }
313        }
314        return graph;
315    }
316
317    @Override
318    public void registerSnippet(ResolvedJavaMethod method) {
319        // No initialization needed as snippet graphs are created on demand in getSnippet
320    }
321
322    @Override
323    public StructuredGraph getSubstitution(ResolvedJavaMethod original, boolean fromBytecodeOnly, int invokeBci) {
324        ResolvedJavaMethod substitute = null;
325        if (!fromBytecodeOnly) {
326            InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(original);
327            if (plugin != null) {
328                if (!plugin.inlineOnly() || invokeBci >= 0) {
329                    if (plugin instanceof MethodSubstitutionPlugin) {
330                        MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
331                        substitute = msPlugin.getSubstitute(providers.getMetaAccess());
332                    } else {
333                        StructuredGraph graph = new IntrinsicGraphBuilder(providers.getMetaAccess(), providers.getConstantReflection(), providers.getStampProvider(), original, invokeBci).buildGraph(plugin);
334                        if (graph != null) {
335                            return graph;
336                        }
337                    }
338                }
339            }
340        }
341        if (substitute == null) {
342            ClassReplacements cr = getClassReplacements(original.getDeclaringClass().getName());
343            substitute = cr == null ? null : cr.methodSubstitutions.get(original);
344        }
345        if (substitute == null) {
346            return null;
347        }
348        StructuredGraph graph = graphs.get(substitute);
349        if (graph == null) {
350            graph = makeGraph(substitute, null, original);
351            graph.freeze();
352            graphs.putIfAbsent(substitute, graph);
353            graph = graphs.get(substitute);
354        }
355        assert graph.isFrozen();
356        return graph;
357
358    }
359
360    private SubstitutionGuard getGuard(Class<? extends SubstitutionGuard> guardClass) {
361        if (guardClass != SubstitutionGuard.class) {
362            Constructor<?>[] constructors = guardClass.getConstructors();
363            if (constructors.length != 1) {
364                throw new JVMCIError("Substitution guard " + guardClass.getSimpleName() + " must have a single public constructor");
365            }
366            Constructor<?> constructor = constructors[0];
367            Class<?>[] paramTypes = constructor.getParameterTypes();
368            // Check for supported constructor signatures
369            try {
370                Object[] args = new Object[constructor.getParameterCount()];
371                for (int i = 0; i < args.length; i++) {
372                    Object arg = snippetReflection.getSubstitutionGuardParameter(paramTypes[i]);
373                    if (arg != null) {
374                        args[i] = arg;
375                    } else if (paramTypes[i].isInstance(target.arch)) {
376                        args[i] = target.arch;
377                    } else {
378                        throw new JVMCIError("Unsupported type %s in substitution guard constructor: %s", paramTypes[i].getName(), constructor);
379                    }
380                }
381
382                return (SubstitutionGuard) constructor.newInstance(args);
383            } catch (Exception e) {
384                throw new JVMCIError(e);
385            }
386        }
387        return null;
388    }
389
390    private static boolean checkSubstitutionInternalName(Class<?> substitutions, String internalName) {
391        ClassSubstitution cs = substitutions.getAnnotation(ClassSubstitution.class);
392        assert cs != null : substitutions + " must be annotated by " + ClassSubstitution.class.getSimpleName();
393        if (cs.value() == ClassSubstitution.class) {
394            for (String className : cs.className()) {
395                if (toInternalName(className).equals(internalName)) {
396                    return true;
397                }
398            }
399            assert false : internalName + " not found in " + Arrays.toString(cs.className());
400        } else {
401            String originalInternalName = toInternalName(cs.value().getName());
402            assert originalInternalName.equals(internalName) : originalInternalName + " != " + internalName;
403        }
404        return true;
405    }
406
407    public void registerSubstitutions(Type original, Class<?> substitutionClass) {
408        String internalName = toInternalName(original.getTypeName());
409        assert checkSubstitutionInternalName(substitutionClass, internalName);
410        Class<?>[] classes = internalNameToSubstitutionClasses.get(internalName);
411        if (classes == null) {
412            classes = new Class<?>[]{substitutionClass};
413        } else {
414            assert !Arrays.asList(classes).contains(substitutionClass);
415            classes = Arrays.copyOf(classes, classes.length + 1);
416            classes[classes.length - 1] = substitutionClass;
417        }
418        internalNameToSubstitutionClasses.put(internalName, classes);
419        AtomicReference<ClassReplacements> existing = classReplacements.put(internalName, new AtomicReference<>());
420        assert existing == null || existing.get() == null;
421    }
422
423    /**
424     * Registers a method substitution.
425     *
426     * @param originalMember a method or constructor being substituted
427     * @param substituteMethod the substitute method
428     * @return the original method
429     */
430    protected ResolvedJavaMethod registerMethodSubstitution(ClassReplacements cr, Executable originalMember, Method substituteMethod) {
431        MetaAccessProvider metaAccess = providers.getMetaAccess();
432        ResolvedJavaMethod substitute = metaAccess.lookupJavaMethod(substituteMethod);
433        ResolvedJavaMethod original = metaAccess.lookupJavaMethod(originalMember);
434        if (Debug.isLogEnabled()) {
435            Debug.log("substitution: %s --> %s", original.format("%H.%n(%p) %r"), substitute.format("%H.%n(%p) %r"));
436        }
437
438        cr.methodSubstitutions.put(original, substitute);
439        return original;
440    }
441
442    /**
443     * Creates a preprocessed graph for a snippet or method substitution.
444     *
445     * @param method the snippet or method substitution for which a graph will be created
446     * @param args
447     * @param original the original method if {@code method} is a {@linkplain MethodSubstitution
448     *            substitution} otherwise null
449     */
450    public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
451        try (OverrideScope s = OptionValue.override(DeoptALot, false)) {
452            return createGraphMaker(method, original).makeGraph(args);
453        }
454    }
455
456    /**
457     * Can be overridden to return an object that specializes various parts of graph preprocessing.
458     */
459    protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) {
460        return new GraphMaker(this, substitute, original);
461    }
462
463    /**
464     * Creates and preprocesses a graph for a replacement.
465     */
466    public static class GraphMaker {
467        /** The replacements object that the graphs are created for. */
468        protected final ReplacementsImpl replacements;
469
470        /**
471         * The method for which a graph is being created.
472         */
473        protected final ResolvedJavaMethod method;
474
475        /**
476         * The original method which {@link #method} is substituting. Calls to {@link #method} or
477         * {@link #substitutedMethod} will be replaced with a forced inline of
478         * {@link #substitutedMethod}.
479         */
480        protected final ResolvedJavaMethod substitutedMethod;
481
482        protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
483            this.replacements = replacements;
484            this.method = substitute;
485            this.substitutedMethod = substitutedMethod;
486        }
487
488        public StructuredGraph makeGraph(Object[] args) {
489            try (Scope s = Debug.scope("BuildSnippetGraph", method)) {
490                assert method.hasBytecodes() : method;
491                StructuredGraph graph = buildInitialGraph(method, args);
492
493                finalizeGraph(graph);
494
495                Debug.dump(graph, "%s: Final", method.getName());
496
497                return graph;
498            } catch (Throwable e) {
499                throw Debug.handle(e);
500            }
501        }
502
503        /**
504         * Does final processing of a snippet graph.
505         */
506        protected void finalizeGraph(StructuredGraph graph) {
507            if (!GraalOptions.SnippetCounters.getValue() || graph.getNodes().filter(SnippetCounterNode.class).isEmpty()) {
508                int sideEffectCount = 0;
509                assert (sideEffectCount = graph.getNodes().filter(e -> hasSideEffect(e)).count()) >= 0;
510                new ConvertDeoptimizeToGuardPhase().apply(graph, null);
511                assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node";
512
513                new DeadCodeEliminationPhase(Required).apply(graph);
514            } else {
515                // ConvertDeoptimizeToGuardPhase will eliminate snippet counters on paths
516                // that terminate in a deopt so we disable it if the graph contains
517                // snippet counters. The trade off is that we miss out on guard
518                // coalescing opportunities.
519            }
520        }
521
522        /**
523         * Filter nodes which have side effects and shouldn't be deleted from snippets when
524         * converting deoptimizations to guards. Currently this only allows exception constructors
525         * to be eliminated to cover the case when Java assertions are in the inlined code.
526         *
527         * @param node
528         * @return true for nodes that have side effects and are unsafe to delete
529         */
530        private boolean hasSideEffect(Node node) {
531            if (node instanceof StateSplit) {
532                if (((StateSplit) node).hasSideEffect()) {
533                    if (node instanceof Invoke) {
534                        CallTargetNode callTarget = ((Invoke) node).callTarget();
535                        if (callTarget instanceof MethodCallTargetNode) {
536                            ResolvedJavaMethod targetMethod = ((MethodCallTargetNode) callTarget).targetMethod();
537                            if (targetMethod.isConstructor()) {
538                                ResolvedJavaType throwableType = replacements.providers.getMetaAccess().lookupJavaType(Throwable.class);
539                                return !throwableType.isAssignableFrom(targetMethod.getDeclaringClass());
540                            }
541                        }
542                    }
543                    // Not an exception constructor call
544                    return true;
545                }
546            }
547            // Not a StateSplit
548            return false;
549        }
550
551        /**
552         * Builds the initial graph for a snippet.
553         */
554        protected StructuredGraph buildInitialGraph(final ResolvedJavaMethod methodToParse, Object[] args) {
555            // Replacements cannot have optimistic assumptions since they have
556            // to be valid for the entire run of the VM.
557            final StructuredGraph graph = new StructuredGraph(methodToParse, AllowAssumptions.NO);
558
559            // They will also never evolve or have breakpoints set in them
560            graph.disableInlinedMethodRecording();
561            // They are not user code so they do not participate in unsafe access tracking
562            graph.disableUnsafeAccessTracking();
563
564            try (Scope s = Debug.scope("buildInitialGraph", graph)) {
565                MetaAccessProvider metaAccess = replacements.providers.getMetaAccess();
566
567                Plugins plugins = new Plugins(replacements.graphBuilderPlugins);
568                GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
569                if (args != null) {
570                    plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(args, metaAccess, replacements.snippetReflection));
571                }
572
573                IntrinsicContext initialIntrinsicContext = null;
574                if (method.getAnnotation(Snippet.class) == null) {
575                    // Post-parse inlined intrinsic
576                    initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, INLINE_AFTER_PARSING);
577                } else {
578                    // Snippet
579                    ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method;
580                    initialIntrinsicContext = new IntrinsicContext(original, method, INLINE_AFTER_PARSING);
581                }
582
583                createGraphBuilder(metaAccess, replacements.providers.getStampProvider(), replacements.providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply(
584                                graph);
585
586                if (OptCanonicalizer.getValue()) {
587                    new CanonicalizerPhase().apply(graph, new PhaseContext(replacements.providers));
588                }
589            } catch (Throwable e) {
590                throw Debug.handle(e);
591            }
592            return graph;
593        }
594
595        protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig,
596                        OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
597            return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
598        }
599    }
600
601    private static String originalName(Method substituteMethod, String methodSubstitution) {
602        if (methodSubstitution.isEmpty()) {
603            return substituteMethod.getName();
604        } else {
605            return methodSubstitution;
606        }
607    }
608
609    /**
610     * Resolves a name to a class.
611     *
612     * @param className the name of the class to resolve
613     * @param optional if true, resolution failure returns null
614     * @return the resolved class or null if resolution fails and {@code optional} is true
615     */
616    public static Class<?> resolveClass(String className, boolean optional) {
617        try {
618            // Need to use launcher class path to handle classes
619            // that are not on the boot class path
620            ClassLoader cl = Launcher.getLauncher().getClassLoader();
621            return Class.forName(className, false, cl);
622        } catch (ClassNotFoundException e) {
623            if (optional) {
624                return null;
625            }
626            throw new JVMCIError("Could not resolve type " + className);
627        }
628    }
629
630    private static Class<?> resolveClass(JavaType type) {
631        JavaType base = type;
632        int dimensions = 0;
633        while (base.getComponentType() != null) {
634            base = base.getComponentType();
635            dimensions++;
636        }
637
638        Class<?> baseClass = base.getKind() != Kind.Object ? base.getKind().toJavaClass() : resolveClass(base.toJavaName(), false);
639        return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass();
640    }
641
642    static class JavaSignature {
643        final Class<?> returnType;
644        final Class<?>[] parameters;
645
646        public JavaSignature(Class<?> returnType, Class<?>[] parameters) {
647            this.parameters = parameters;
648            this.returnType = returnType;
649        }
650
651        @Override
652        public String toString() {
653            StringBuilder sb = new StringBuilder("(");
654            for (int i = 0; i < parameters.length; i++) {
655                if (i != 0) {
656                    sb.append(", ");
657                }
658                sb.append(parameters[i].getName());
659            }
660            return sb.append(") ").append(returnType.getName()).toString();
661        }
662    }
663
664    @Override
665    public Collection<ResolvedJavaMethod> getAllReplacements() {
666        HashSet<ResolvedJavaMethod> result = new HashSet<>();
667        for (String internalName : classReplacements.keySet()) {
668            ClassReplacements cr = getClassReplacements(internalName);
669            result.addAll(cr.methodSubstitutions.keySet());
670        }
671        return result;
672    }
673
674    public boolean hasSubstitution(ResolvedJavaMethod method, boolean fromBytecodeOnly, int callerBci) {
675        if (!fromBytecodeOnly) {
676            InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
677            if (plugin != null) {
678                if (!plugin.inlineOnly() || callerBci >= 0) {
679                    return true;
680                }
681            }
682        }
683        return getSubstitutionMethod(method) != null;
684    }
685
686    public ResolvedJavaMethod getSubstitutionMethod(ResolvedJavaMethod original) {
687        InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(original);
688        if (plugin instanceof MethodSubstitutionPlugin) {
689            MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
690            return msPlugin.getSubstitute(providers.getMetaAccess());
691        }
692        ClassReplacements cr = getClassReplacements(original.getDeclaringClass().getName());
693        return cr == null ? null : cr.methodSubstitutions.get(original);
694    }
695
696    @Override
697    public void registerSnippetTemplateCache(SnippetTemplateCache templates) {
698        assert snippetTemplateCache.get(templates.getClass().getName()) == null;
699        snippetTemplateCache.put(templates.getClass().getName(), templates);
700    }
701
702    @Override
703    public <T extends SnippetTemplateCache> T getSnippetTemplateCache(Class<T> templatesClass) {
704        SnippetTemplateCache ret = snippetTemplateCache.get(templatesClass.getName());
705        return templatesClass.cast(ret);
706    }
707}