1 /*
2  * Copyright (c) 2018, 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  */
23 
24 
25 package org.graalvm.compiler.hotspot;
26 
27 import static jdk.vm.ci.runtime.JVMCI.getRuntime;
28 import static jdk.vm.ci.services.Services.IS_BUILDING_NATIVE_IMAGE;
29 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
30 import static org.graalvm.compiler.core.common.GraalOptions.UseEncodedGraphs;
31 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
32 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.Collections;
37 import java.util.HashMap;
38 import java.util.HashSet;
39 import java.util.List;
40 import java.util.Map;
41 import java.util.Objects;
42 import java.util.Set;
43 import java.util.concurrent.ConcurrentHashMap;
44 
45 import org.graalvm.compiler.api.replacements.Fold;
46 import org.graalvm.compiler.api.replacements.MethodSubstitution;
47 import org.graalvm.compiler.api.replacements.Snippet;
48 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
49 import org.graalvm.compiler.api.runtime.GraalJVMCICompiler;
50 import org.graalvm.compiler.api.runtime.GraalRuntime;
51 import org.graalvm.compiler.bytecode.BytecodeProvider;
52 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
53 import org.graalvm.compiler.core.common.type.AbstractObjectStamp;
54 import org.graalvm.compiler.core.common.type.Stamp;
55 import org.graalvm.compiler.core.common.type.StampPair;
56 import org.graalvm.compiler.core.common.type.SymbolicJVMCIReference;
57 import org.graalvm.compiler.debug.DebugContext;
58 import org.graalvm.compiler.debug.GraalError;
59 import org.graalvm.compiler.graph.Node;
60 import org.graalvm.compiler.graph.NodeClass;
61 import org.graalvm.compiler.graph.NodeMap;
62 import org.graalvm.compiler.graph.NodeSourcePosition;
63 import org.graalvm.compiler.hotspot.meta.HotSpotForeignCallsProvider;
64 import org.graalvm.compiler.hotspot.meta.HotSpotProviders;
65 import org.graalvm.compiler.java.BytecodeParser;
66 import org.graalvm.compiler.java.GraphBuilderPhase;
67 import org.graalvm.compiler.nodeinfo.Verbosity;
68 import org.graalvm.compiler.nodes.CallTargetNode;
69 import org.graalvm.compiler.nodes.ConstantNode;
70 import org.graalvm.compiler.nodes.EncodedGraph;
71 import org.graalvm.compiler.nodes.FrameState;
72 import org.graalvm.compiler.nodes.FullInfopointNode;
73 import org.graalvm.compiler.nodes.GraphEncoder;
74 import org.graalvm.compiler.nodes.ParameterNode;
75 import org.graalvm.compiler.nodes.ProxyNode;
76 import org.graalvm.compiler.nodes.StructuredGraph;
77 import org.graalvm.compiler.nodes.ValueNode;
78 import org.graalvm.compiler.nodes.cfg.Block;
79 import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;
80 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
81 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
82 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
83 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
84 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
85 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins;
86 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
87 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin;
88 import org.graalvm.compiler.nodes.graphbuilderconf.ParameterPlugin;
89 import org.graalvm.compiler.nodes.java.AccessFieldNode;
90 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
91 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
92 import org.graalvm.compiler.options.OptionValues;
93 import org.graalvm.compiler.phases.OptimisticOptimizations;
94 import org.graalvm.compiler.phases.schedule.SchedulePhase;
95 import org.graalvm.compiler.phases.util.Providers;
96 import org.graalvm.compiler.replacements.ConstantBindingParameterPlugin;
97 import org.graalvm.compiler.replacements.PEGraphDecoder;
98 import org.graalvm.compiler.replacements.ReplacementsImpl;
99 import org.graalvm.compiler.replacements.SnippetCounter;
100 import org.graalvm.compiler.replacements.SnippetIntegerHistogram;
101 
102 import jdk.vm.ci.code.TargetDescription;
103 import jdk.vm.ci.hotspot.HotSpotJVMCIRuntime;
104 import jdk.vm.ci.hotspot.HotSpotResolvedJavaField;
105 import jdk.vm.ci.hotspot.HotSpotResolvedJavaMethod;
106 import jdk.vm.ci.hotspot.HotSpotResolvedJavaType;
107 import jdk.vm.ci.hotspot.HotSpotSignature;
108 import jdk.vm.ci.meta.Constant;
109 import jdk.vm.ci.meta.ConstantReflectionProvider;
110 import jdk.vm.ci.meta.JavaConstant;
111 import jdk.vm.ci.meta.JavaKind;
112 import jdk.vm.ci.meta.JavaType;
113 import jdk.vm.ci.meta.MemoryAccessProvider;
114 import jdk.vm.ci.meta.MethodHandleAccessProvider;
115 import jdk.vm.ci.meta.ResolvedJavaField;
116 import jdk.vm.ci.meta.ResolvedJavaMethod;
117 import jdk.vm.ci.meta.ResolvedJavaType;
118 import jdk.vm.ci.meta.UnresolvedJavaField;
119 import jdk.vm.ci.meta.UnresolvedJavaMethod;
120 import jdk.vm.ci.meta.UnresolvedJavaType;
121 
122 /**
123  * This class performs graph encoding using {@link GraphEncoder} but also converts JVMCI type and
124  * method references into a symbolic form that can be resolved at graph decode time using
125  * {@link SymbolicJVMCIReference}.
126  */
127 public class SymbolicSnippetEncoder {
128 
129     /**
130      * This is a customized HotSpotReplacementsImpl intended only for parsing snippets and method
131      * substitutions for graph encoding.
132      */
133     private final HotSpotSnippetReplacementsImpl snippetReplacements;
134 
135     /**
136      * The set of all snippet methods that have been encoded.
137      */
138     private final Set<ResolvedJavaMethod> snippetMethods = Collections.synchronizedSet(new HashSet<>());
139 
140     /**
141      * A mapping from the method substitution method to the original method name. The string key and
142      * values are produced using {@link #methodKey(ResolvedJavaMethod)}.
143      */
144     private final Map<String, String> originalMethods = new ConcurrentHashMap<>();
145 
146     private final HotSpotReplacementsImpl originalReplacements;
147 
148     /**
149      * The current count of graphs encoded. Used to detect when new graphs have been enqueued for
150      * encoding.
151      */
152     private int encodedGraphs = 0;
153 
154     /**
155      * All the graphs parsed so far.
156      */
157     private Map<String, StructuredGraph> preparedSnippetGraphs = new HashMap<>();
158 
159     /**
160      * The invocation plugins which were delayed during graph preparation.
161      */
162     private Set<ResolvedJavaMethod> delayedInvocationPluginMethods = new HashSet<>();
163 
addDelayedInvocationPluginMethod(ResolvedJavaMethod method)164     void addDelayedInvocationPluginMethod(ResolvedJavaMethod method) {
165         delayedInvocationPluginMethods.add(method);
166     }
167 
getSnippetMethods()168     Set<ResolvedJavaMethod> getSnippetMethods() {
169         return snippetMethods;
170     }
171 
172     protected class SnippetInlineInvokePlugin implements InlineInvokePlugin {
173 
174         @Override
shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args)175         public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args) {
176             if (method.getAnnotation(Fold.class) != null) {
177                 delayedInvocationPluginMethods.add(method);
178                 return InlineInfo.DO_NOT_INLINE_NO_EXCEPTION;
179             }
180 
181             if (snippetReplacements.getIntrinsifyingPlugin(method) != null) {
182                 delayedInvocationPluginMethods.add(method);
183                 return InlineInfo.DO_NOT_INLINE_NO_EXCEPTION;
184             }
185 
186             // Force inlining when parsing replacements
187             return createIntrinsicInlineInfo(method, snippetReplacements.getDefaultReplacementBytecodeProvider());
188         }
189 
190         @Override
notifyAfterInline(ResolvedJavaMethod methodToInline)191         public void notifyAfterInline(ResolvedJavaMethod methodToInline) {
192             assert methodToInline.getAnnotation(Fold.class) == null : methodToInline;
193         }
194     }
195 
196     public static class SnippetInvocationPlugins extends InvocationPlugins {
197 
SnippetInvocationPlugins(InvocationPlugins invocationPlugins)198         SnippetInvocationPlugins(InvocationPlugins invocationPlugins) {
199             super(invocationPlugins);
200         }
201 
202         @Override
lookupInvocation(ResolvedJavaMethod method)203         public InvocationPlugin lookupInvocation(ResolvedJavaMethod method) {
204             if (method.getAnnotation(Fold.class) != null) {
205                 return null;
206             }
207             return super.lookupInvocation(method);
208         }
209     }
210 
211     /**
212      * This plugin disables the snippet counter machinery.
213      */
214     private class SnippetCounterPlugin implements NodePlugin {
215         String snippetCounterName = 'L' + SnippetCounter.class.getName().replace('.', '/') + ';';
216         String snippetIntegerHistogramName = 'L' + SnippetIntegerHistogram.class.getName().replace('.', '/') + ';';
217 
218         @Override
handleLoadField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field)219         public boolean handleLoadField(GraphBuilderContext b, ValueNode object, ResolvedJavaField field) {
220             if (field.getName().equals("group") && field.getDeclaringClass().getName().equals(snippetCounterName)) {
221                 b.addPush(JavaKind.Object, ConstantNode.forConstant(JavaConstant.NULL_POINTER, b.getMetaAccess()));
222                 return true;
223             }
224             if (field.getType().getName().equals(snippetCounterName)) {
225                 b.addPush(JavaKind.Object, ConstantNode.forConstant(snippetReplacements.snippetReflection.forObject(SnippetCounter.DISABLED_COUNTER), b.getMetaAccess()));
226                 return true;
227             }
228 
229             if (field.getType().getName().equals(snippetIntegerHistogramName)) {
230                 b.addPush(JavaKind.Object, ConstantNode.forConstant(snippetReplacements.snippetReflection.forObject(SnippetIntegerHistogram.DISABLED_COUNTER), b.getMetaAccess()));
231                 return true;
232             }
233             return false;
234         }
235     }
236 
237     /**
238      * Generate a String name for a method including all type information. Used as a symbolic key
239      * for lookup.
240      */
methodKey(ResolvedJavaMethod method)241     private static String methodKey(ResolvedJavaMethod method) {
242         return method.format("%f %H.%n(%P)");
243     }
244 
SymbolicSnippetEncoder(HotSpotReplacementsImpl replacements)245     SymbolicSnippetEncoder(HotSpotReplacementsImpl replacements) {
246         this.originalReplacements = replacements;
247         GraphBuilderConfiguration.Plugins plugins = replacements.getGraphBuilderPlugins();
248         SnippetInvocationPlugins invocationPlugins = new SnippetInvocationPlugins(plugins.getInvocationPlugins());
249         GraphBuilderConfiguration.Plugins copy = new GraphBuilderConfiguration.Plugins(plugins, invocationPlugins);
250         copy.clearInlineInvokePlugins();
251         copy.appendInlineInvokePlugin(new SnippetInlineInvokePlugin());
252         copy.appendNodePlugin(new SnippetCounterPlugin());
253         HotSpotProviders providers = (HotSpotProviders) replacements.getProviders().copyWith(new HotSpotSubstrateConstantReflectionProvider(replacements.getProviders().getConstantReflection()));
254         this.snippetReplacements = new HotSpotSnippetReplacementsImpl(replacements, providers.copyWith(copy));
255         this.snippetReplacements.setGraphBuilderPlugins(copy);
256     }
257 
258     /**
259      * Compiles the snippet and stores the graph.
260      */
registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options)261     synchronized void registerMethodSubstitution(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, IntrinsicContext.CompilationContext context, OptionValues options) {
262         ResolvedJavaMethod method = plugin.getSubstitute(snippetReplacements.getProviders().getMetaAccess());
263         assert method.getAnnotation(MethodSubstitution.class) != null : "MethodSubstitution must be annotated with @" + MethodSubstitution.class.getSimpleName();
264         StructuredGraph subst = buildGraph(method, original, null, true, false, context, options);
265         snippetMethods.add(method);
266         originalMethods.put(methodKey(method), methodKey(original));
267         preparedSnippetGraphs.put(plugin.toString() + context, subst);
268     }
269 
270     static class EncodedSnippets {
271         private byte[] snippetEncoding;
272         private Object[] snippetObjects;
273         private NodeClass<?>[] snippetNodeClasses;
274         private Map<String, Integer> snippetStartOffsets;
275         private Map<String, String> originalMethods;
276 
EncodedSnippets(byte[] snippetEncoding, Object[] snippetObjects, NodeClass<?>[] snippetNodeClasses, Map<String, Integer> snippetStartOffsets, Map<String, String> originalMethods)277         EncodedSnippets(byte[] snippetEncoding, Object[] snippetObjects, NodeClass<?>[] snippetNodeClasses, Map<String, Integer> snippetStartOffsets, Map<String, String> originalMethods) {
278             this.snippetEncoding = snippetEncoding;
279             this.snippetObjects = snippetObjects;
280             this.snippetNodeClasses = snippetNodeClasses;
281             this.snippetStartOffsets = snippetStartOffsets;
282             this.originalMethods = originalMethods;
283         }
284 
getMethodSubstitutionGraph(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, ReplacementsImpl replacements, IntrinsicContext.CompilationContext context, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options)285         StructuredGraph getMethodSubstitutionGraph(MethodSubstitutionPlugin plugin, ResolvedJavaMethod original, ReplacementsImpl replacements, IntrinsicContext.CompilationContext context,
286                         StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
287             Integer startOffset = snippetStartOffsets.get(plugin.toString() + context);
288             if (startOffset == null) {
289                 throw GraalError.shouldNotReachHere("plugin graph not found: " + plugin + " with " + context);
290             }
291 
292             ResolvedJavaType accessingClass = replacements.getProviders().getMetaAccess().lookupJavaType(plugin.getDeclaringClass());
293             return decodeGraph(original, accessingClass, startOffset, replacements, context, allowAssumptions, options);
294         }
295 
296         @SuppressWarnings("try")
decodeGraph(ResolvedJavaMethod method, ResolvedJavaType accessingClass, int startOffset, ReplacementsImpl replacements, IntrinsicContext.CompilationContext context, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options)297         private StructuredGraph decodeGraph(ResolvedJavaMethod method, ResolvedJavaType accessingClass, int startOffset, ReplacementsImpl replacements,
298                         IntrinsicContext.CompilationContext context, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
299             Providers providers = replacements.getProviders();
300             EncodedGraph encodedGraph = new SymbolicEncodedGraph(snippetEncoding, startOffset, snippetObjects, snippetNodeClasses,
301                             methodKey(method), accessingClass, method.getDeclaringClass());
302             try (DebugContext debug = replacements.openDebugContext("SVMSnippet_", method, options)) {
303                 StructuredGraph result = new StructuredGraph.Builder(options, debug, allowAssumptions).method(method).setIsSubstitution(true).build();
304                 PEGraphDecoder graphDecoder = new SubstitutionGraphDecoder(providers, result, replacements, null, method, context, encodedGraph);
305 
306                 graphDecoder.decode(method, result.isSubstitution(), encodedGraph.trackNodeSourcePosition());
307 
308                 assert result.verify();
309                 return result;
310             }
311         }
312 
getEncodedSnippet(ResolvedJavaMethod method, ReplacementsImpl replacements, Object[] args, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options)313         StructuredGraph getEncodedSnippet(ResolvedJavaMethod method, ReplacementsImpl replacements, Object[] args, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
314             Integer startOffset = null;
315             if (snippetStartOffsets != null) {
316                 startOffset = snippetStartOffsets.get(methodKey(method));
317             }
318             if (startOffset == null) {
319                 if (IS_IN_NATIVE_IMAGE) {
320                     throw GraalError.shouldNotReachHere("snippet not found: " + method.format("%H.%n(%p)"));
321                 } else {
322                     return null;
323                 }
324             }
325 
326             SymbolicEncodedGraph encodedGraph = new SymbolicEncodedGraph(snippetEncoding, startOffset, snippetObjects, snippetNodeClasses,
327                             originalMethods.get(methodKey(method)), method.getDeclaringClass());
328             return decodeSnippetGraph(encodedGraph, method, replacements, args, allowAssumptions, options);
329         }
330 
331     }
332 
333     private static class SubstitutionGraphDecoder extends PEGraphDecoder {
334         private final ResolvedJavaMethod method;
335         private final EncodedGraph encodedGraph;
336         private IntrinsicContext intrinsic;
337 
SubstitutionGraphDecoder(Providers providers, StructuredGraph result, ReplacementsImpl replacements, ParameterPlugin parameterPlugin, ResolvedJavaMethod method, IntrinsicContext.CompilationContext context, EncodedGraph encodedGraph)338         SubstitutionGraphDecoder(Providers providers, StructuredGraph result, ReplacementsImpl replacements, ParameterPlugin parameterPlugin, ResolvedJavaMethod method,
339                         IntrinsicContext.CompilationContext context, EncodedGraph encodedGraph) {
340             super(providers.getCodeCache().getTarget().arch, result, providers, null,
341                             replacements.getGraphBuilderPlugins().getInvocationPlugins(), new InlineInvokePlugin[0], parameterPlugin,
342                             null, null, null);
343             this.method = method;
344             this.encodedGraph = encodedGraph;
345             intrinsic = new IntrinsicContext(method, null, replacements.getDefaultReplacementBytecodeProvider(), context, false);
346         }
347 
348         @Override
lookupEncodedGraph(ResolvedJavaMethod lookupMethod, MethodSubstitutionPlugin plugin, BytecodeProvider intrinsicBytecodeProvider, boolean isSubstitution, boolean trackNodeSourcePosition)349         protected EncodedGraph lookupEncodedGraph(ResolvedJavaMethod lookupMethod,
350                         MethodSubstitutionPlugin plugin,
351                         BytecodeProvider intrinsicBytecodeProvider,
352                         boolean isSubstitution,
353                         boolean trackNodeSourcePosition) {
354             if (lookupMethod.equals(method)) {
355                 return encodedGraph;
356             } else {
357                 throw GraalError.shouldNotReachHere(method.format("%H.%n(%p)"));
358             }
359         }
360 
361         @Override
getIntrinsic()362         protected IntrinsicContext getIntrinsic() {
363             return intrinsic;
364         }
365     }
366 
buildGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean requireInlining, boolean trackNodeSourcePosition, IntrinsicContext.CompilationContext context, OptionValues options)367     private StructuredGraph buildGraph(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean requireInlining, boolean trackNodeSourcePosition,
368                     IntrinsicContext.CompilationContext context, OptionValues options) {
369         assert method.hasBytecodes() : "Snippet must not be abstract or native";
370         Object[] args = null;
371         if (receiver != null) {
372             args = new Object[method.getSignature().getParameterCount(true)];
373             args[0] = receiver;
374         }
375         try (DebugContext debug = openDebugContext("Snippet_", method, options)) {
376             StructuredGraph graph = snippetReplacements.makeGraph(debug, snippetReplacements.getDefaultReplacementBytecodeProvider(), method, args, original, trackNodeSourcePosition, null, context);
377 
378             // Check if all methods which should be inlined are really inlined.
379             for (MethodCallTargetNode callTarget : graph.getNodes(MethodCallTargetNode.TYPE)) {
380                 ResolvedJavaMethod callee = callTarget.targetMethod();
381                 if (requireInlining && !delayedInvocationPluginMethods.contains(callee) && !Objects.equals(callee, original)) {
382                     throw GraalError.shouldNotReachHere("method " + callee.format("%H.%n") + " not inlined in snippet " + method.getName() + " (maybe not final?)");
383                 }
384             }
385             assert verifySnippetEncodeDecode(method, original, trackNodeSourcePosition, graph);
386             debug.dump(DebugContext.VERBOSE_LEVEL, graph, "After buildGraph");
387             return graph;
388         }
389     }
390 
391     @SuppressWarnings("try")
decodeSnippetGraph(SymbolicEncodedGraph encodedGraph, ResolvedJavaMethod method, ReplacementsImpl replacements, Object[] args, StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options)392     private static StructuredGraph decodeSnippetGraph(SymbolicEncodedGraph encodedGraph, ResolvedJavaMethod method, ReplacementsImpl replacements, Object[] args,
393                     StructuredGraph.AllowAssumptions allowAssumptions, OptionValues options) {
394         Providers providers = replacements.getProviders();
395         ParameterPlugin parameterPlugin = null;
396         if (args != null) {
397             parameterPlugin = new ConstantBindingParameterPlugin(args, providers.getMetaAccess(), replacements.snippetReflection);
398         }
399 
400         try (DebugContext debug = replacements.openDebugContext("SVMSnippet_", method, options)) {
401             // @formatter:off
402             StructuredGraph result = new StructuredGraph.Builder(options, debug, allowAssumptions)
403                     .method(method)
404                     .trackNodeSourcePosition(encodedGraph.trackNodeSourcePosition())
405                     .setIsSubstitution(true)
406                     .build();
407             // @formatter:on
408             try (DebugContext.Scope scope = debug.scope("DecodeSnippetGraph", result)) {
409                 PEGraphDecoder graphDecoder = new SubstitutionGraphDecoder(providers, result, replacements, parameterPlugin, method, INLINE_AFTER_PARSING, encodedGraph);
410 
411                 graphDecoder.decode(method, result.isSubstitution(), encodedGraph.trackNodeSourcePosition());
412                 debug.dump(DebugContext.VERBOSE_LEVEL, result, "After decoding");
413 
414                 assert result.verify();
415                 return result;
416             } catch (Throwable t) {
417                 throw debug.handle(t);
418             }
419         }
420     }
421 
422     @SuppressWarnings("try")
verifySnippetEncodeDecode(ResolvedJavaMethod method, ResolvedJavaMethod original, boolean trackNodeSourcePosition, StructuredGraph graph)423     private boolean verifySnippetEncodeDecode(ResolvedJavaMethod method, ResolvedJavaMethod original, boolean trackNodeSourcePosition, StructuredGraph graph) {
424         // Verify the encoding and decoding process
425         EncodedGraph encodedGraph = GraphEncoder.encodeSingleGraph(graph, HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getTarget().arch);
426 
427         try (DebugContext debug = snippetReplacements.openDebugContext("VerifySnippetEncodeDecode_", method, graph.getOptions())) {
428             HotSpotProviders originalProvider = (HotSpotProviders) snippetReplacements.getProviders();
429 
430             SnippetReflectionProvider snippetReflection = originalProvider.getSnippetReflection();
431             SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider constantReflection = new SymbolicSnippetEncoder.HotSpotSubstrateConstantReflectionProvider(
432                             originalProvider.getConstantReflection());
433             HotSpotProviders newProviders = new HotSpotProviders(originalProvider.getMetaAccess(), originalProvider.getCodeCache(), constantReflection,
434                             originalProvider.getConstantFieldProvider(), originalProvider.getForeignCalls(), originalProvider.getLowerer(), null, originalProvider.getSuites(),
435                             originalProvider.getRegisters(), snippetReflection, originalProvider.getWordTypes(), originalProvider.getGraphBuilderPlugins());
436             HotSpotSnippetReplacementsImpl filteringReplacements = new HotSpotSnippetReplacementsImpl(newProviders, snippetReflection,
437                             originalProvider.getReplacements().getDefaultReplacementBytecodeProvider(), originalProvider.getCodeCache().getTarget());
438             filteringReplacements.setGraphBuilderPlugins(originalProvider.getReplacements().getGraphBuilderPlugins());
439             try (DebugContext.Scope scaope = debug.scope("VerifySnippetEncodeDecode", graph)) {
440                 for (int i = 0; i < encodedGraph.getNumObjects(); i++) {
441                     filterSnippetObject(encodedGraph.getObject(i));
442                 }
443                 StructuredGraph snippet = filteringReplacements.makeGraph(debug, filteringReplacements.getDefaultReplacementBytecodeProvider(), method, null, original,
444                                 trackNodeSourcePosition, null);
445                 SymbolicEncodedGraph symbolicGraph = new SymbolicEncodedGraph(encodedGraph, method.getDeclaringClass(), original != null ? methodKey(original) : null);
446                 StructuredGraph decodedSnippet = decodeSnippetGraph(symbolicGraph, original != null ? original : method, originalReplacements, null,
447                                 StructuredGraph.AllowAssumptions.ifNonNull(graph.getAssumptions()), graph.getOptions());
448                 String snippetString = getCanonicalGraphString(snippet, true, false);
449                 String decodedSnippetString = getCanonicalGraphString(decodedSnippet, true, false);
450                 if (snippetString.equals(decodedSnippetString)) {
451                     debug.log("Snippet decode for %s produces exactly same graph", method);
452                     debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
453                 } else {
454                     debug.log("Snippet decode for %s produces different graph", method);
455                     debug.log("%s", compareGraphStrings(snippet, snippetString, decodedSnippet, decodedSnippetString));
456                     debug.dump(DebugContext.VERBOSE_LEVEL, snippet, "Snippet graph for %s", method);
457                     debug.dump(DebugContext.VERBOSE_LEVEL, graph, "Encoded snippet graph for %s", method);
458                     debug.dump(DebugContext.VERBOSE_LEVEL, decodedSnippet, "Decoded snippet graph for %s", method);
459                 }
460             } catch (Throwable t) {
461                 throw debug.handle(t);
462             }
463         }
464         return true;
465     }
466 
467     /**
468      * If there are new graphs waiting to be encoded, reencode all the graphs and return the result.
469      */
470     @SuppressWarnings("try")
maybeEncodeSnippets(OptionValues options)471     private synchronized EncodedSnippets maybeEncodeSnippets(OptionValues options) {
472         Map<String, StructuredGraph> graphs = this.preparedSnippetGraphs;
473         if (encodedGraphs != graphs.size()) {
474             DebugContext debug = openDebugContext("SnippetEncoder", null, options);
475             try (DebugContext.Scope scope = debug.scope("SnippetSupportEncode")) {
476                 encodedGraphs = graphs.size();
477                 for (StructuredGraph graph : graphs.values()) {
478                     for (Node node : graph.getNodes()) {
479                         node.setNodeSourcePosition(null);
480                     }
481                 }
482                 return encodeSnippets(debug);
483             }
484         }
485         return null;
486     }
487 
registerSnippet(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean trackNodeSourcePosition, OptionValues options)488     synchronized void registerSnippet(ResolvedJavaMethod method, ResolvedJavaMethod original, Object receiver, boolean trackNodeSourcePosition, OptionValues options) {
489         if (IS_BUILDING_NATIVE_IMAGE || UseEncodedGraphs.getValue(options)) {
490             assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName();
491             String key = methodKey(method);
492             if (!preparedSnippetGraphs.containsKey(key)) {
493                 if (original != null) {
494                     originalMethods.put(key, methodKey(original));
495                 }
496                 StructuredGraph snippet = buildGraph(method, original, receiver, true, trackNodeSourcePosition, INLINE_AFTER_PARSING, options);
497                 snippetMethods.add(method);
498                 preparedSnippetGraphs.put(key, snippet);
499             }
500         }
501 
502     }
503 
encodeSnippets(DebugContext debug)504     private synchronized EncodedSnippets encodeSnippets(DebugContext debug) {
505         GraphEncoder encoder = new GraphEncoder(HotSpotJVMCIRuntime.runtime().getHostJVMCIBackend().getTarget().arch, debug);
506         for (StructuredGraph graph : preparedSnippetGraphs.values()) {
507             encoder.prepare(graph);
508         }
509         encoder.finishPrepare();
510 
511         byte[] snippetEncoding;
512         Object[] snippetObjects;
513         NodeClass<?>[] snippetNodeClasses;
514         Map<String, Integer> snippetStartOffsets;
515 
516         snippetStartOffsets = new HashMap<>();
517         for (Map.Entry<String, StructuredGraph> entry : preparedSnippetGraphs.entrySet()) {
518             snippetStartOffsets.put(entry.getKey(), encoder.encode(entry.getValue()));
519         }
520         snippetEncoding = encoder.getEncoding();
521         snippetObjects = encoder.getObjects();
522         snippetNodeClasses = encoder.getNodeClasses();
523         for (int i = 0; i < snippetObjects.length; i++) {
524             Object o = filterSnippetObject(snippetObjects[i]);
525             debug.log("snippetObjects[%d] = %s -> %s", i, o != null ? o.getClass().getSimpleName() : null, o);
526             snippetObjects[i] = o;
527         }
528         debug.log("Encoded %d snippet preparedSnippetGraphs using %d bytes with %d objects", snippetStartOffsets.size(), snippetEncoding.length, snippetObjects.length);
529         return new EncodedSnippets(snippetEncoding, snippetObjects, snippetNodeClasses, snippetStartOffsets, originalMethods);
530     }
531 
532     /**
533      * Encode any outstanding graphs and return true if any work was done.
534      */
535     @SuppressWarnings("try")
encode(OptionValues options)536     public boolean encode(OptionValues options) {
537         EncodedSnippets encodedSnippets = maybeEncodeSnippets(options);
538         if (encodedSnippets != null) {
539             HotSpotReplacementsImpl.setEncodedSnippets(encodedSnippets);
540             return true;
541         }
542         return false;
543     }
544 
openDebugContext(String idPrefix, ResolvedJavaMethod method, OptionValues options)545     private DebugContext openDebugContext(String idPrefix, ResolvedJavaMethod method, OptionValues options) {
546         return snippetReplacements.openDebugContext(idPrefix, method, options);
547     }
548 
549     static class SymbolicEncodedGraph extends EncodedGraph {
550 
551         private final ResolvedJavaType[] accessingClasses;
552         private final String originalMethod;
553 
SymbolicEncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClass<?>[] types, String originalMethod, ResolvedJavaType... accessingClasses)554         SymbolicEncodedGraph(byte[] encoding, int startOffset, Object[] objects, NodeClass<?>[] types, String originalMethod, ResolvedJavaType... accessingClasses) {
555             super(encoding, startOffset, objects, types, null, null, null, false, false);
556             this.accessingClasses = accessingClasses;
557             this.originalMethod = originalMethod;
558         }
559 
SymbolicEncodedGraph(EncodedGraph encodedGraph, ResolvedJavaType declaringClass, String originalMethod)560         SymbolicEncodedGraph(EncodedGraph encodedGraph, ResolvedJavaType declaringClass, String originalMethod) {
561             this(encodedGraph.getEncoding(), encodedGraph.getStartOffset(), encodedGraph.getObjects(), encodedGraph.getNodeClasses(),
562                             originalMethod, declaringClass);
563         }
564 
565         @Override
getObject(int i)566         public Object getObject(int i) {
567             Object o = objects[i];
568             Object replacement = null;
569             if (o instanceof SymbolicJVMCIReference) {
570                 for (ResolvedJavaType type : accessingClasses) {
571                     try {
572                         replacement = ((SymbolicJVMCIReference<?>) o).resolve(type);
573                         break;
574                     } catch (NoClassDefFoundError e) {
575                     }
576                 }
577             } else if (o instanceof UnresolvedJavaType) {
578                 for (ResolvedJavaType type : accessingClasses) {
579                     try {
580                         replacement = ((UnresolvedJavaType) o).resolve(type);
581                         break;
582                     } catch (NoClassDefFoundError e) {
583                     }
584                 }
585             } else if (o instanceof UnresolvedJavaMethod) {
586                 throw new InternalError(o.toString());
587             } else if (o instanceof UnresolvedJavaField) {
588                 for (ResolvedJavaType type : accessingClasses) {
589                     try {
590                         replacement = ((UnresolvedJavaField) o).resolve(type);
591                         break;
592                     } catch (NoClassDefFoundError e) {
593                     }
594                 }
595             } else if (o instanceof GraalCapability) {
596                 replacement = ((GraalCapability) o).resolve(((GraalJVMCICompiler) getRuntime().getCompiler()).getGraalRuntime());
597             } else {
598                 return o;
599             }
600             if (replacement != null) {
601                 objects[i] = o = replacement;
602             } else {
603                 throw new GraalError("Can't resolve " + o);
604             }
605             return o;
606         }
607 
608         @Override
isCallToOriginal(ResolvedJavaMethod callTarget)609         public boolean isCallToOriginal(ResolvedJavaMethod callTarget) {
610             if (originalMethod != null && originalMethod.equals(methodKey(callTarget))) {
611                 return true;
612             }
613             return super.isCallToOriginal(callTarget);
614         }
615     }
616 
617     /**
618      * Symbolic reference to an object which can be retrieved from
619      * {@link GraalRuntime#getCapability(Class)}.
620      */
621     static class GraalCapability {
622         final Class<?> capabilityClass;
623 
GraalCapability(Class<?> capabilityClass)624         GraalCapability(Class<?> capabilityClass) {
625             this.capabilityClass = capabilityClass;
626         }
627 
resolve(GraalRuntime runtime)628         public Object resolve(GraalRuntime runtime) {
629             Object capability = runtime.getCapability(this.capabilityClass);
630             if (capability != null) {
631                 assert capability.getClass() == capabilityClass;
632                 return capability;
633             }
634             throw new InternalError(this.capabilityClass.getName());
635         }
636     }
637 
638     static class SymbolicResolvedJavaMethod implements SymbolicJVMCIReference<ResolvedJavaMethod> {
639         final UnresolvedJavaType type;
640         final String methodName;
641         final String signature;
642 
SymbolicResolvedJavaMethod(ResolvedJavaMethod method)643         SymbolicResolvedJavaMethod(ResolvedJavaMethod method) {
644             this.type = UnresolvedJavaType.create(method.getDeclaringClass().getName());
645             this.methodName = method.getName();
646             this.signature = method.getSignature().toMethodDescriptor();
647         }
648 
649         @Override
toString()650         public String toString() {
651             return "SymbolicResolvedJavaMethod{" +
652                             "declaringType='" + type.getName() + '\'' +
653                             ", methodName='" + methodName + '\'' +
654                             ", signature='" + signature + '\'' +
655                             '}';
656         }
657 
658         @Override
resolve(ResolvedJavaType accessingClass)659         public ResolvedJavaMethod resolve(ResolvedJavaType accessingClass) {
660             ResolvedJavaType resolvedType = type.resolve(accessingClass);
661             if (resolvedType == null) {
662                 throw new InternalError("Could not resolve " + this + " in context of " + accessingClass.toJavaName());
663             }
664             for (ResolvedJavaMethod method : methodName.equals("<init>") ? resolvedType.getDeclaredConstructors() : resolvedType.getDeclaredMethods()) {
665                 if (method.getName().equals(methodName) && method.getSignature().toMethodDescriptor().equals(signature)) {
666                     return method;
667                 }
668             }
669             throw new InternalError("Could not resolve " + this + " in context of " + accessingClass.toJavaName());
670         }
671     }
672 
673     static class SymbolicResolvedJavaField implements SymbolicJVMCIReference<ResolvedJavaField> {
674         final UnresolvedJavaType declaringType;
675         final String name;
676         final UnresolvedJavaType signature;
677         private final boolean isStatic;
678 
SymbolicResolvedJavaField(ResolvedJavaField field)679         SymbolicResolvedJavaField(ResolvedJavaField field) {
680             this.declaringType = UnresolvedJavaType.create(field.getDeclaringClass().getName());
681             this.name = field.getName();
682             this.signature = UnresolvedJavaType.create(field.getType().getName());
683             this.isStatic = field.isStatic();
684         }
685 
686         @Override
resolve(ResolvedJavaType accessingClass)687         public ResolvedJavaField resolve(ResolvedJavaType accessingClass) {
688             ResolvedJavaType resolvedType = declaringType.resolve(accessingClass);
689             ResolvedJavaType resolvedFieldType = signature.resolve(accessingClass);
690             ResolvedJavaField[] fields = isStatic ? resolvedType.getStaticFields() : resolvedType.getInstanceFields(true);
691             for (ResolvedJavaField field : fields) {
692                 if (field.getName().equals(name)) {
693                     if (field.getType().equals(resolvedFieldType)) {
694                         return field;
695                     }
696                 }
697             }
698             throw new InternalError("Could not resolve " + this + " in context of " + accessingClass.toJavaName());
699         }
700 
701         @Override
toString()702         public String toString() {
703             return "SymbolicResolvedJavaField{" +
704                             signature.getName() + ' ' +
705                             declaringType.getName() + '.' +
706                             name +
707                             '}';
708         }
709     }
710 
711     static class SymbolicResolvedJavaMethodBytecode implements SymbolicJVMCIReference<ResolvedJavaMethodBytecode> {
712         SymbolicResolvedJavaMethod method;
713 
SymbolicResolvedJavaMethodBytecode(ResolvedJavaMethodBytecode bytecode)714         SymbolicResolvedJavaMethodBytecode(ResolvedJavaMethodBytecode bytecode) {
715             method = new SymbolicResolvedJavaMethod(bytecode.getMethod());
716         }
717 
718         @Override
resolve(ResolvedJavaType accessingClass)719         public ResolvedJavaMethodBytecode resolve(ResolvedJavaType accessingClass) {
720             return new ResolvedJavaMethodBytecode(method.resolve(accessingClass));
721         }
722     }
723 
724     static class SymbolicStampPair implements SymbolicJVMCIReference<StampPair> {
725         Object trustedStamp;
726         Object uncheckdStamp;
727 
SymbolicStampPair(StampPair stamp)728         SymbolicStampPair(StampPair stamp) {
729             this.trustedStamp = maybeMakeSymbolic(stamp.getTrustedStamp());
730             this.uncheckdStamp = maybeMakeSymbolic(stamp.getUncheckedStamp());
731         }
732 
733         @Override
resolve(ResolvedJavaType accessingClass)734         public StampPair resolve(ResolvedJavaType accessingClass) {
735             return StampPair.create(resolveStamp(accessingClass, trustedStamp), resolveStamp(accessingClass, uncheckdStamp));
736         }
737     }
738 
maybeMakeSymbolic(Stamp trustedStamp)739     private static Object maybeMakeSymbolic(Stamp trustedStamp) {
740         if (trustedStamp != null) {
741             SymbolicJVMCIReference<?> symbolicJVMCIReference = trustedStamp.makeSymbolic();
742             if (symbolicJVMCIReference != null) {
743                 return symbolicJVMCIReference;
744             }
745         }
746         return trustedStamp;
747     }
748 
resolveStamp(ResolvedJavaType accessingClass, Object stamp)749     private static Stamp resolveStamp(ResolvedJavaType accessingClass, Object stamp) {
750         if (stamp == null) {
751             return null;
752         }
753         if (stamp instanceof Stamp) {
754             return (Stamp) stamp;
755         }
756         return (Stamp) ((SymbolicJVMCIReference<?>) stamp).resolve(accessingClass);
757     }
758 
759     public static class HotSpotSubstrateConstantReflectionProvider implements ConstantReflectionProvider {
760 
761         private final ConstantReflectionProvider constantReflection;
762 
HotSpotSubstrateConstantReflectionProvider(ConstantReflectionProvider constantReflection)763         HotSpotSubstrateConstantReflectionProvider(ConstantReflectionProvider constantReflection) {
764             this.constantReflection = constantReflection;
765         }
766 
767         HashSet<JavaConstant> safeConstants = new HashSet<>();
768 
769         @Override
constantEquals(Constant x, Constant y)770         public Boolean constantEquals(Constant x, Constant y) {
771             return constantReflection.constantEquals(x, y);
772         }
773 
774         @Override
readArrayLength(JavaConstant array)775         public Integer readArrayLength(JavaConstant array) {
776             return constantReflection.readArrayLength(array);
777         }
778 
779         @Override
readArrayElement(JavaConstant array, int index)780         public JavaConstant readArrayElement(JavaConstant array, int index) {
781             return constantReflection.readArrayElement(array, index);
782         }
783 
784         @Override
readFieldValue(ResolvedJavaField field, JavaConstant receiver)785         public JavaConstant readFieldValue(ResolvedJavaField field, JavaConstant receiver) {
786             JavaConstant javaConstant = constantReflection.readFieldValue(field, receiver);
787             if (!safeConstants.contains(receiver) && !field.getDeclaringClass().getName().contains("graalvm") && !field.getDeclaringClass().getName().contains("jdk/vm/ci/") &&
788                             !field.getName().equals("TYPE")) {
789                 // Only permit constant reflection on compiler classes. This is necessary primarily
790                 // because of the boxing snippets which are compiled as snippets but are really just
791                 // regular JDK java sources that are being compiled like a snippet. These shouldn't
792                 // permit constant folding during graph preparation as that embeds constants from
793                 // the runtime into a compiler graph.
794                 return null;
795             }
796             if (javaConstant.getJavaKind() == JavaKind.Object) {
797                 safeConstants.add(javaConstant);
798             }
799             return javaConstant;
800         }
801 
802         @Override
boxPrimitive(JavaConstant source)803         public JavaConstant boxPrimitive(JavaConstant source) {
804             return constantReflection.boxPrimitive(source);
805         }
806 
807         @Override
unboxPrimitive(JavaConstant source)808         public JavaConstant unboxPrimitive(JavaConstant source) {
809             return constantReflection.unboxPrimitive(source);
810         }
811 
812         @Override
forString(String value)813         public JavaConstant forString(String value) {
814             return constantReflection.forString(value);
815         }
816 
817         @Override
asJavaType(Constant constant)818         public ResolvedJavaType asJavaType(Constant constant) {
819             return constantReflection.asJavaType(constant);
820         }
821 
822         @Override
getMethodHandleAccess()823         public MethodHandleAccessProvider getMethodHandleAccess() {
824             return constantReflection.getMethodHandleAccess();
825         }
826 
827         @Override
getMemoryAccessProvider()828         public MemoryAccessProvider getMemoryAccessProvider() {
829             return constantReflection.getMemoryAccessProvider();
830         }
831 
832         @Override
asJavaClass(ResolvedJavaType type)833         public JavaConstant asJavaClass(ResolvedJavaType type) {
834             return constantReflection.asJavaClass(type);
835         }
836 
837         @Override
asObjectHub(ResolvedJavaType type)838         public Constant asObjectHub(ResolvedJavaType type) {
839             return constantReflection.asObjectHub(type);
840         }
841     }
842 
843     /**
844      * Objects embedded in encoded graphs might need to converted into a symbolic form so convert
845      * the object or pass it through.
846      */
filterSnippetObject(Object o)847     private static Object filterSnippetObject(Object o) {
848         if (o instanceof HotSpotResolvedJavaMethod) {
849             return new SymbolicResolvedJavaMethod((HotSpotResolvedJavaMethod) o);
850         } else if (o instanceof HotSpotResolvedJavaField) {
851             return new SymbolicResolvedJavaField((HotSpotResolvedJavaField) o);
852         } else if (o instanceof HotSpotResolvedJavaType) {
853             return UnresolvedJavaType.create(((ResolvedJavaType) o).getName());
854         } else if (o instanceof NodeSourcePosition) {
855             // Filter these out for now. These can't easily be handled because these positions
856             // description snippet methods which might not be available in the runtime.
857             return null;
858         } else if (o instanceof HotSpotForeignCallsProvider || o instanceof GraalHotSpotVMConfig) {
859             return new GraalCapability(o.getClass());
860         } else if (o instanceof Stamp) {
861             SymbolicJVMCIReference<?> ref = ((Stamp) o).makeSymbolic();
862             if (ref != null) {
863                 return ref;
864             }
865             return o;
866         } else if (o instanceof StampPair) {
867             if (((StampPair) o).getTrustedStamp() instanceof AbstractObjectStamp) {
868                 return new SymbolicStampPair((StampPair) o);
869             }
870         } else if (o instanceof ResolvedJavaMethodBytecode) {
871             return new SymbolicResolvedJavaMethodBytecode((ResolvedJavaMethodBytecode) o);
872         } else if (o instanceof HotSpotSignature) {
873             throw new GraalError(o.toString());
874         }
875         return o;
876     }
877 
compareGraphStrings(StructuredGraph expectedGraph, String expectedString, StructuredGraph actualGraph, String actualString)878     private static String compareGraphStrings(StructuredGraph expectedGraph, String expectedString, StructuredGraph actualGraph, String actualString) {
879         if (!expectedString.equals(actualString)) {
880             String[] expectedLines = expectedString.split("\n");
881             String[] actualLines = actualString.split("\n");
882             int diffIndex = -1;
883             int limit = Math.min(actualLines.length, expectedLines.length);
884             String marker = " <<<";
885             for (int i = 0; i < limit; i++) {
886                 if (!expectedLines[i].equals(actualLines[i])) {
887                     diffIndex = i;
888                     break;
889                 }
890             }
891             if (diffIndex == -1) {
892                 // Prefix is the same so add some space after the prefix
893                 diffIndex = limit;
894                 if (actualLines.length == limit) {
895                     actualLines = Arrays.copyOf(actualLines, limit + 1);
896                     actualLines[diffIndex] = "";
897                 } else {
898                     assert expectedLines.length == limit;
899                     expectedLines = Arrays.copyOf(expectedLines, limit + 1);
900                     expectedLines[diffIndex] = "";
901                 }
902             }
903             // Place a marker next to the first line that differs
904             expectedLines[diffIndex] = expectedLines[diffIndex] + marker;
905             actualLines[diffIndex] = actualLines[diffIndex] + marker;
906             String ediff = String.join("\n", expectedLines);
907             String adiff = String.join("\n", actualLines);
908             return "mismatch in preparedSnippetGraphs:\n========= expected (" + expectedGraph + ") =========\n" + ediff + "\n\n========= actual (" + actualGraph + ") =========\n" + adiff;
909         } else {
910             return "mismatch in preparedSnippetGraphs";
911         }
912     }
913 
getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants)914     private static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) {
915         SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST);
916         schedule.apply(graph);
917         StructuredGraph.ScheduleResult scheduleResult = graph.getLastSchedule();
918 
919         NodeMap<Integer> canonicalId = graph.createNodeMap();
920         int nextId = 0;
921 
922         List<String> constantsLines = new ArrayList<>();
923 
924         StringBuilder result = new StringBuilder();
925         for (Block block : scheduleResult.getCFG().getBlocks()) {
926             result.append("Block ").append(block).append(' ');
927             if (block == scheduleResult.getCFG().getStartBlock()) {
928                 result.append("* ");
929             }
930             result.append("-> ");
931             for (Block succ : block.getSuccessors()) {
932                 result.append(succ).append(' ');
933             }
934             result.append('\n');
935             for (Node node : scheduleResult.getBlockToNodesMap().get(block)) {
936                 if (node instanceof ValueNode && node.isAlive()) {
937                     if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode || node instanceof ParameterNode)) {
938                         if (node instanceof ConstantNode) {
939                             if (checkConstants) {
940                                 String name = node.toString(Verbosity.Name);
941                                 if (excludeVirtual) {
942                                     constantsLines.add(name);
943                                 } else {
944                                     constantsLines.add(name + "    (" + filteredUsageCount(node) + ")");
945                                 }
946                             }
947                         } else {
948                             int id;
949                             if (canonicalId.get(node) != null) {
950                                 id = canonicalId.get(node);
951                             } else {
952                                 id = nextId++;
953                                 canonicalId.set(node, id);
954                             }
955                             String name = node.getClass().getSimpleName();
956                             result.append("  ").append(id).append('|').append(name);
957                             if (node instanceof AccessFieldNode) {
958                                 result.append('#');
959                                 result.append(((AccessFieldNode) node).field());
960                             }
961                             if (!excludeVirtual) {
962                                 result.append("    (");
963                                 result.append(filteredUsageCount(node));
964                                 result.append(')');
965                             }
966                             result.append('\n');
967                         }
968                     }
969                 }
970             }
971         }
972 
973         StringBuilder constantsLinesResult = new StringBuilder();
974         if (checkConstants) {
975             constantsLinesResult.append(constantsLines.size()).append(" constants:\n");
976         }
977         Collections.sort(constantsLines);
978         for (String s : constantsLines) {
979             constantsLinesResult.append(s);
980             constantsLinesResult.append('\n');
981         }
982 
983         return constantsLinesResult.toString() + result.toString();
984     }
985 
filteredUsageCount(Node node)986     private static int filteredUsageCount(Node node) {
987         return node.usages().filter(n -> !(n instanceof FrameState)).count();
988     }
989 
990     /**
991      * This horror show of classes exists solely get {@link HotSpotSnippetBytecodeParser} to be used
992      * as the parser for these snippets.
993      */
994     static class HotSpotSnippetReplacementsImpl extends HotSpotReplacementsImpl {
HotSpotSnippetReplacementsImpl(HotSpotReplacementsImpl replacements, Providers providers)995         HotSpotSnippetReplacementsImpl(HotSpotReplacementsImpl replacements, Providers providers) {
996             super(replacements, providers);
997         }
998 
HotSpotSnippetReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target)999         HotSpotSnippetReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target) {
1000             super(providers, snippetReflection, bytecodeProvider, target);
1001         }
1002 
1003         @Override
createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original)1004         protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) {
1005             return new SnippetGraphMaker(this, substitute, original);
1006         }
1007     }
1008 
1009     static class SnippetGraphMaker extends ReplacementsImpl.GraphMaker {
SnippetGraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod)1010         SnippetGraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
1011             super(replacements, substitute, substitutedMethod);
1012         }
1013 
1014         @Override
createGraphBuilder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext)1015         protected GraphBuilderPhase.Instance createGraphBuilder(Providers providers, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts,
1016                         IntrinsicContext initialIntrinsicContext) {
1017             return new HotSpotSnippetGraphBuilderPhase(providers, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
1018         }
1019     }
1020 
1021     static class HotSpotSnippetGraphBuilderPhase extends GraphBuilderPhase.Instance {
HotSpotSnippetGraphBuilderPhase(Providers theProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext)1022         HotSpotSnippetGraphBuilderPhase(Providers theProviders, GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
1023             super(theProviders, graphBuilderConfig, optimisticOpts, initialIntrinsicContext);
1024         }
1025 
1026         @Override
createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext)1027         protected BytecodeParser createBytecodeParser(StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext) {
1028             return new HotSpotSnippetBytecodeParser(this, graph, parent, method, entryBCI, intrinsicContext);
1029         }
1030     }
1031 
1032     static class HotSpotSnippetBytecodeParser extends BytecodeParser {
HotSpotSnippetBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI, IntrinsicContext intrinsicContext)1033         HotSpotSnippetBytecodeParser(GraphBuilderPhase.Instance graphBuilderInstance, StructuredGraph graph, BytecodeParser parent, ResolvedJavaMethod method, int entryBCI,
1034                         IntrinsicContext intrinsicContext) {
1035             super(graphBuilderInstance, graph, parent, method, entryBCI, intrinsicContext);
1036         }
1037 
1038         @Override
canDeferPlugin(GeneratedInvocationPlugin plugin)1039         public boolean canDeferPlugin(GeneratedInvocationPlugin plugin) {
1040             // Fold is always deferred but NodeIntrinsics may have to wait if all their arguments
1041             // aren't constant yet.
1042             return plugin.getSource().equals(Fold.class) || plugin.getSource().equals(Node.NodeIntrinsic.class);
1043         }
1044 
1045         @Override
canInlinePartialIntrinsicExit()1046         protected boolean canInlinePartialIntrinsicExit() {
1047             return false;
1048         }
1049 
1050         @Override
tryInvocationPlugin(CallTargetNode.InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType, JavaType returnType)1051         protected boolean tryInvocationPlugin(CallTargetNode.InvokeKind invokeKind, ValueNode[] args, ResolvedJavaMethod targetMethod, JavaKind resultType, JavaType returnType) {
1052             if (intrinsicContext != null && intrinsicContext.isCallToOriginal(targetMethod)) {
1053                 return false;
1054             }
1055             if (targetMethod.getAnnotation(Fold.class) != null) {
1056                 // Always defer Fold until decode time but NodeIntrinsics may fold if they are able.
1057                 return false;
1058             }
1059             return super.tryInvocationPlugin(invokeKind, args, targetMethod, resultType, returnType);
1060         }
1061     }
1062 }
1063