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 package org.graalvm.compiler.replacements;
24
25 import static org.graalvm.compiler.core.common.GraalOptions.UseSnippetGraphCache;
26 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
27 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineIntrinsicsDuringParsing;
28 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
29 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
30 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
31
32 import java.util.concurrent.ConcurrentHashMap;
33 import java.util.concurrent.ConcurrentMap;
34
35 import org.graalvm.compiler.api.replacements.Fold;
36 import org.graalvm.compiler.api.replacements.MethodSubstitution;
37 import org.graalvm.compiler.api.replacements.Snippet;
38 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
39 import org.graalvm.compiler.api.replacements.SnippetTemplateCache;
40 import org.graalvm.compiler.bytecode.Bytecode;
41 import org.graalvm.compiler.bytecode.BytecodeProvider;
42 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
43 import org.graalvm.compiler.core.common.GraalOptions;
44 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
45 import org.graalvm.compiler.debug.Debug;
46 import org.graalvm.compiler.debug.Debug.Scope;
47 import org.graalvm.compiler.debug.DebugCloseable;
48 import org.graalvm.compiler.debug.DebugTimer;
49 import org.graalvm.compiler.debug.GraalError;
50 import org.graalvm.compiler.graph.Node;
51 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
52 import org.graalvm.compiler.java.GraphBuilderPhase;
53 import org.graalvm.compiler.java.GraphBuilderPhase.Instance;
54 import org.graalvm.compiler.nodes.CallTargetNode;
55 import org.graalvm.compiler.nodes.Invoke;
56 import org.graalvm.compiler.nodes.StateSplit;
57 import org.graalvm.compiler.nodes.StructuredGraph;
58 import org.graalvm.compiler.nodes.ValueNode;
59 import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;
60 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
61 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
62 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
63 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
64 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
65 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
66 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
67 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
68 import org.graalvm.compiler.nodes.spi.Replacements;
69 import org.graalvm.compiler.nodes.spi.StampProvider;
75 import org.graalvm.compiler.phases.tiers.PhaseContext;
76 import org.graalvm.compiler.phases.util.Providers;
77 import org.graalvm.compiler.word.Word;
78 import org.graalvm.compiler.word.WordOperationPlugin;
79 import org.graalvm.util.EconomicMap;
80 import org.graalvm.util.Equivalence;
81
82 import jdk.vm.ci.code.TargetDescription;
83 import jdk.vm.ci.meta.ConstantReflectionProvider;
84 import jdk.vm.ci.meta.MetaAccessProvider;
85 import jdk.vm.ci.meta.ResolvedJavaMethod;
86 import jdk.vm.ci.meta.ResolvedJavaType;
87
88 public class ReplacementsImpl implements Replacements, InlineInvokePlugin {
89
90 protected final OptionValues options;
91 public final Providers providers;
92 public final SnippetReflectionProvider snippetReflection;
93 public final TargetDescription target;
94 private GraphBuilderConfiguration.Plugins graphBuilderPlugins;
95
96 @Override
97 public OptionValues getOptions() {
98 return options;
99 }
100
101 /**
102 * The preprocessed replacement graphs.
103 */
104 protected final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphs;
105
106 /**
107 * The default {@link BytecodeProvider} to use for accessing the bytecode of a replacement if
108 * the replacement doesn't provide another {@link BytecodeProvider}.
109 */
110 protected final BytecodeProvider defaultBytecodeProvider;
111
112 public void setGraphBuilderPlugins(GraphBuilderConfiguration.Plugins plugins) {
113 assert this.graphBuilderPlugins == null;
114 this.graphBuilderPlugins = plugins;
163 if (b.parsingIntrinsic()) {
164 IntrinsicContext intrinsic = b.getIntrinsic();
165 if (!intrinsic.isCallToOriginal(method)) {
166 if (hasGeneratedInvocationPluginAnnotation(method)) {
167 throw new GraalError("%s should have been handled by a %s", method.format("%H.%n(%p)"), GeneratedInvocationPlugin.class.getSimpleName());
168 }
169 if (hasGenericInvocationPluginAnnotation(method)) {
170 throw new GraalError("%s should have been handled by %s", method.format("%H.%n(%p)"), WordOperationPlugin.class.getSimpleName());
171 }
172
173 throw new GraalError("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s",
174 intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)"));
175 }
176 }
177 }
178
179 // This map is key'ed by a class name instead of a Class object so that
180 // it is stable across VM executions (in support of replay compilation).
181 private final EconomicMap<String, SnippetTemplateCache> snippetTemplateCache;
182
183 public ReplacementsImpl(OptionValues options, Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider, TargetDescription target) {
184 this.options = options;
185 this.providers = providers.copyWith(this);
186 this.snippetReflection = snippetReflection;
187 this.target = target;
188 this.graphs = new ConcurrentHashMap<>();
189 this.snippetTemplateCache = EconomicMap.create(Equivalence.DEFAULT);
190 this.defaultBytecodeProvider = bytecodeProvider;
191 }
192
193 private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime");
194
195 @Override
196 public StructuredGraph getSnippet(ResolvedJavaMethod method, Object[] args) {
197 return getSnippet(method, null, args);
198 }
199
200 @Override
201 @SuppressWarnings("try")
202 public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args) {
203 assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName();
204 assert method.hasBytecodes() : "Snippet must not be abstract or native";
205
206 StructuredGraph graph = UseSnippetGraphCache.getValue(options) ? graphs.get(method) : null;
207 if (graph == null) {
208 try (DebugCloseable a = SnippetPreparationTime.start()) {
209 StructuredGraph newGraph = makeGraph(defaultBytecodeProvider, method, args, recursiveEntry);
210 Debug.counter("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount());
211 if (!UseSnippetGraphCache.getValue(options) || args != null) {
212 return newGraph;
213 }
214 graphs.putIfAbsent(method, newGraph);
215 graph = graphs.get(method);
216 }
217 }
218 return graph;
219 }
220
221 @Override
222 public void registerSnippet(ResolvedJavaMethod method) {
223 // No initialization needed as snippet graphs are created on demand in getSnippet
224 }
225
226 @Override
227 public boolean hasSubstitution(ResolvedJavaMethod method, int invokeBci) {
228 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
229 return plugin != null && (!plugin.inlineOnly() || invokeBci >= 0);
230 }
231
232 @Override
233 public BytecodeProvider getDefaultReplacementBytecodeProvider() {
239 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
240 if (plugin instanceof MethodSubstitutionPlugin) {
241 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
242 ResolvedJavaMethod substitute = msPlugin.getSubstitute(providers.getMetaAccess());
243 return msPlugin.getBytecodeProvider().getBytecode(substitute);
244 }
245 return null;
246 }
247
248 @Override
249 public StructuredGraph getSubstitution(ResolvedJavaMethod method, int invokeBci) {
250 StructuredGraph result;
251 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
252 if (plugin != null && (!plugin.inlineOnly() || invokeBci >= 0)) {
253 MetaAccessProvider metaAccess = providers.getMetaAccess();
254 if (plugin instanceof MethodSubstitutionPlugin) {
255 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
256 ResolvedJavaMethod substitute = msPlugin.getSubstitute(metaAccess);
257 StructuredGraph graph = graphs.get(substitute);
258 if (graph == null) {
259 graph = makeGraph(msPlugin.getBytecodeProvider(), substitute, null, method);
260 graph.freeze();
261 graphs.putIfAbsent(substitute, graph);
262 graph = graphs.get(substitute);
263 }
264 assert graph.isFrozen();
265 result = graph;
266 } else {
267 Bytecode code = new ResolvedJavaMethodBytecode(method);
268 ConstantReflectionProvider constantReflection = providers.getConstantReflection();
269 ConstantFieldProvider constantFieldProvider = providers.getConstantFieldProvider();
270 StampProvider stampProvider = providers.getStampProvider();
271 result = new IntrinsicGraphBuilder(options, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci).buildGraph(plugin);
272 }
273 } else {
274 result = null;
275 }
276 return result;
277 }
278
279 /**
280 * Creates a preprocessed graph for a snippet or method substitution.
281 *
282 * @param bytecodeProvider how to access the bytecode of {@code method}
283 * @param method the snippet or method substitution for which a graph will be created
284 * @param args
285 * @param original the original method if {@code method} is a {@linkplain MethodSubstitution
286 * substitution} otherwise null
287 */
288 public StructuredGraph makeGraph(BytecodeProvider bytecodeProvider, ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
289 return createGraphMaker(method, original).makeGraph(bytecodeProvider, args);
290 }
291
292 /**
293 * Can be overridden to return an object that specializes various parts of graph preprocessing.
294 */
295 protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) {
296 return new GraphMaker(this, substitute, original);
297 }
298
299 /**
300 * Creates and preprocesses a graph for a replacement.
301 */
302 public static class GraphMaker {
303
304 /** The replacements object that the graphs are created for. */
305 protected final ReplacementsImpl replacements;
306
307 /**
308 * The method for which a graph is being created.
309 */
310 protected final ResolvedJavaMethod method;
311
312 /**
313 * The original method which {@link #method} is substituting. Calls to {@link #method} or
314 * {@link #substitutedMethod} will be replaced with a forced inline of
315 * {@link #substitutedMethod}.
316 */
317 protected final ResolvedJavaMethod substitutedMethod;
318
319 protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
320 this.replacements = replacements;
321 this.method = substitute;
322 this.substitutedMethod = substitutedMethod;
323 }
324
325 @SuppressWarnings("try")
326 public StructuredGraph makeGraph(BytecodeProvider bytecodeProvider, Object[] args) {
327 try (Scope s = Debug.scope("BuildSnippetGraph", method)) {
328 assert method.hasBytecodes() : method;
329 StructuredGraph graph = buildInitialGraph(bytecodeProvider, method, args);
330
331 finalizeGraph(graph);
332
333 Debug.dump(Debug.INFO_LEVEL, graph, "%s: Final", method.getName());
334
335 return graph;
336 } catch (Throwable e) {
337 throw Debug.handle(e);
338 }
339 }
340
341 /**
342 * Does final processing of a snippet graph.
343 */
344 protected void finalizeGraph(StructuredGraph graph) {
345 if (!GraalOptions.SnippetCounters.getValue(replacements.options) || graph.getNodes().filter(SnippetCounterNode.class).isEmpty()) {
346 int sideEffectCount = 0;
347 assert (sideEffectCount = graph.getNodes().filter(e -> hasSideEffect(e)).count()) >= 0;
348 new ConvertDeoptimizeToGuardPhase().apply(graph, null);
349 assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node";
350
351 new DeadCodeEliminationPhase(Required).apply(graph);
352 } else {
353 // ConvertDeoptimizeToGuardPhase will eliminate snippet counters on paths
354 // that terminate in a deopt so we disable it if the graph contains
355 // snippet counters. The trade off is that we miss out on guard
356 // coalescing opportunities.
357 }
373 if (callTarget instanceof MethodCallTargetNode) {
374 ResolvedJavaMethod targetMethod = ((MethodCallTargetNode) callTarget).targetMethod();
375 if (targetMethod.isConstructor()) {
376 ResolvedJavaType throwableType = replacements.providers.getMetaAccess().lookupJavaType(Throwable.class);
377 return !throwableType.isAssignableFrom(targetMethod.getDeclaringClass());
378 }
379 }
380 }
381 // Not an exception constructor call
382 return true;
383 }
384 }
385 // Not a StateSplit
386 return false;
387 }
388
389 /**
390 * Builds the initial graph for a replacement.
391 */
392 @SuppressWarnings("try")
393 protected StructuredGraph buildInitialGraph(BytecodeProvider bytecodeProvider, final ResolvedJavaMethod methodToParse, Object[] args) {
394 // Replacements cannot have optimistic assumptions since they have
395 // to be valid for the entire run of the VM.
396
397 final StructuredGraph graph = new StructuredGraph.Builder(replacements.options).method(methodToParse).build();
398
399 // They are not user code so they do not participate in unsafe access tracking
400 graph.disableUnsafeAccessTracking();
401
402 try (Scope s = Debug.scope("buildInitialGraph", graph)) {
403 MetaAccessProvider metaAccess = replacements.providers.getMetaAccess();
404
405 Plugins plugins = new Plugins(replacements.graphBuilderPlugins);
406 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
407 if (args != null) {
408 plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(args, metaAccess, replacements.snippetReflection));
409 }
410
411 IntrinsicContext initialIntrinsicContext = null;
412 if (method.getAnnotation(Snippet.class) == null) {
413 // Post-parse inlined intrinsic
414 initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, bytecodeProvider, INLINE_AFTER_PARSING);
415 } else {
416 // Snippet
417 ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method;
418 initialIntrinsicContext = new IntrinsicContext(original, method, bytecodeProvider, INLINE_AFTER_PARSING);
419 }
420
421 createGraphBuilder(metaAccess, replacements.providers.getStampProvider(), replacements.providers.getConstantReflection(), replacements.providers.getConstantFieldProvider(), config,
422 OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph);
423
424 new CanonicalizerPhase().apply(graph, new PhaseContext(replacements.providers));
425 } catch (Throwable e) {
426 throw Debug.handle(e);
427 }
428 return graph;
429 }
430
431 protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
432 GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
433 return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, constantFieldProvider, graphBuilderConfig, optimisticOpts,
434 initialIntrinsicContext);
435 }
436 }
437
438 @Override
439 public void registerSnippetTemplateCache(SnippetTemplateCache templates) {
440 assert snippetTemplateCache.get(templates.getClass().getName()) == null;
441 snippetTemplateCache.put(templates.getClass().getName(), templates);
442 }
443
444 @Override
445 public <T extends SnippetTemplateCache> T getSnippetTemplateCache(Class<T> templatesClass) {
446 SnippetTemplateCache ret = snippetTemplateCache.get(templatesClass.getName());
|
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 package org.graalvm.compiler.replacements;
24
25 import static org.graalvm.compiler.core.common.GraalOptions.UseSnippetGraphCache;
26 import static org.graalvm.compiler.debug.DebugContext.DEFAULT_LOG_STREAM;
27 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineDuringParsing;
28 import static org.graalvm.compiler.java.BytecodeParserOptions.InlineIntrinsicsDuringParsing;
29 import static org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo.createIntrinsicInlineInfo;
30 import static org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext.CompilationContext.INLINE_AFTER_PARSING;
31 import static org.graalvm.compiler.phases.common.DeadCodeEliminationPhase.Optionality.Required;
32
33 import java.util.Collections;
34 import java.util.List;
35 import java.util.concurrent.ConcurrentHashMap;
36 import java.util.concurrent.ConcurrentMap;
37 import java.util.concurrent.atomic.AtomicInteger;
38
39 import org.graalvm.compiler.api.replacements.Fold;
40 import org.graalvm.compiler.api.replacements.MethodSubstitution;
41 import org.graalvm.compiler.api.replacements.Snippet;
42 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider;
43 import org.graalvm.compiler.api.replacements.SnippetTemplateCache;
44 import org.graalvm.compiler.bytecode.Bytecode;
45 import org.graalvm.compiler.bytecode.BytecodeProvider;
46 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode;
47 import org.graalvm.compiler.core.common.GraalOptions;
48 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider;
49 import org.graalvm.compiler.debug.DebugCloseable;
50 import org.graalvm.compiler.debug.DebugHandlersFactory;
51 import org.graalvm.compiler.debug.DebugContext;
52 import org.graalvm.compiler.debug.DebugContext.Description;
53 import org.graalvm.compiler.debug.GraalError;
54 import org.graalvm.compiler.debug.TimerKey;
55 import org.graalvm.compiler.graph.Node;
56 import org.graalvm.compiler.graph.Node.NodeIntrinsic;
57 import org.graalvm.compiler.java.GraphBuilderPhase;
58 import org.graalvm.compiler.java.GraphBuilderPhase.Instance;
59 import org.graalvm.compiler.nodes.CallTargetNode;
60 import org.graalvm.compiler.nodes.Invoke;
61 import org.graalvm.compiler.nodes.StateSplit;
62 import org.graalvm.compiler.nodes.StructuredGraph;
63 import org.graalvm.compiler.nodes.ValueNode;
64 import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin;
65 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration;
66 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins;
67 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext;
68 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin;
69 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext;
70 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin;
71 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin;
72 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
73 import org.graalvm.compiler.nodes.spi.Replacements;
74 import org.graalvm.compiler.nodes.spi.StampProvider;
80 import org.graalvm.compiler.phases.tiers.PhaseContext;
81 import org.graalvm.compiler.phases.util.Providers;
82 import org.graalvm.compiler.word.Word;
83 import org.graalvm.compiler.word.WordOperationPlugin;
84 import org.graalvm.util.EconomicMap;
85 import org.graalvm.util.Equivalence;
86
87 import jdk.vm.ci.code.TargetDescription;
88 import jdk.vm.ci.meta.ConstantReflectionProvider;
89 import jdk.vm.ci.meta.MetaAccessProvider;
90 import jdk.vm.ci.meta.ResolvedJavaMethod;
91 import jdk.vm.ci.meta.ResolvedJavaType;
92
93 public class ReplacementsImpl implements Replacements, InlineInvokePlugin {
94
95 protected final OptionValues options;
96 public final Providers providers;
97 public final SnippetReflectionProvider snippetReflection;
98 public final TargetDescription target;
99 private GraphBuilderConfiguration.Plugins graphBuilderPlugins;
100 private final DebugHandlersFactory debugHandlersFactory;
101
102 @Override
103 public OptionValues getOptions() {
104 return options;
105 }
106
107 /**
108 * The preprocessed replacement graphs.
109 */
110 protected final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphs;
111
112 /**
113 * The default {@link BytecodeProvider} to use for accessing the bytecode of a replacement if
114 * the replacement doesn't provide another {@link BytecodeProvider}.
115 */
116 protected final BytecodeProvider defaultBytecodeProvider;
117
118 public void setGraphBuilderPlugins(GraphBuilderConfiguration.Plugins plugins) {
119 assert this.graphBuilderPlugins == null;
120 this.graphBuilderPlugins = plugins;
169 if (b.parsingIntrinsic()) {
170 IntrinsicContext intrinsic = b.getIntrinsic();
171 if (!intrinsic.isCallToOriginal(method)) {
172 if (hasGeneratedInvocationPluginAnnotation(method)) {
173 throw new GraalError("%s should have been handled by a %s", method.format("%H.%n(%p)"), GeneratedInvocationPlugin.class.getSimpleName());
174 }
175 if (hasGenericInvocationPluginAnnotation(method)) {
176 throw new GraalError("%s should have been handled by %s", method.format("%H.%n(%p)"), WordOperationPlugin.class.getSimpleName());
177 }
178
179 throw new GraalError("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s",
180 intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)"));
181 }
182 }
183 }
184
185 // This map is key'ed by a class name instead of a Class object so that
186 // it is stable across VM executions (in support of replay compilation).
187 private final EconomicMap<String, SnippetTemplateCache> snippetTemplateCache;
188
189 public ReplacementsImpl(OptionValues options, DebugHandlersFactory debugHandlersFactory, Providers providers, SnippetReflectionProvider snippetReflection, BytecodeProvider bytecodeProvider,
190 TargetDescription target) {
191 this.options = options;
192 this.providers = providers.copyWith(this);
193 this.snippetReflection = snippetReflection;
194 this.target = target;
195 this.graphs = new ConcurrentHashMap<>();
196 this.snippetTemplateCache = EconomicMap.create(Equivalence.DEFAULT);
197 this.defaultBytecodeProvider = bytecodeProvider;
198 this.debugHandlersFactory = debugHandlersFactory;
199
200 }
201
202 private static final TimerKey SnippetPreparationTime = DebugContext.timer("SnippetPreparationTime");
203
204 @Override
205 public StructuredGraph getSnippet(ResolvedJavaMethod method, Object[] args) {
206 return getSnippet(method, null, args);
207 }
208
209 private static final AtomicInteger nextDebugContextId = new AtomicInteger();
210
211 protected DebugContext openDebugContext(String idPrefix, ResolvedJavaMethod method) {
212 DebugContext outer = DebugContext.forCurrentThread();
213 Description description = new Description(method, idPrefix + nextDebugContextId.incrementAndGet());
214 List<DebugHandlersFactory> factories = debugHandlersFactory == null ? Collections.emptyList() : Collections.singletonList(debugHandlersFactory);
215 return DebugContext.create(options, description, outer.getGlobalMetrics(), DEFAULT_LOG_STREAM, factories);
216 }
217
218 @Override
219 @SuppressWarnings("try")
220 public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args) {
221 assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName();
222 assert method.hasBytecodes() : "Snippet must not be abstract or native";
223
224 StructuredGraph graph = UseSnippetGraphCache.getValue(options) ? graphs.get(method) : null;
225 if (graph == null) {
226 try (DebugContext debug = openDebugContext("Snippet_", method);
227 DebugCloseable a = SnippetPreparationTime.start(debug)) {
228 StructuredGraph newGraph = makeGraph(debug, defaultBytecodeProvider, method, args, recursiveEntry);
229 DebugContext.counter("SnippetNodeCount[%#s]", method).add(newGraph.getDebug(), newGraph.getNodeCount());
230 if (!UseSnippetGraphCache.getValue(options) || args != null) {
231 return newGraph;
232 }
233 newGraph.freeze();
234 graphs.putIfAbsent(method, newGraph);
235 graph = graphs.get(method);
236 }
237 }
238 return graph;
239 }
240
241 @Override
242 public void registerSnippet(ResolvedJavaMethod method) {
243 // No initialization needed as snippet graphs are created on demand in getSnippet
244 }
245
246 @Override
247 public boolean hasSubstitution(ResolvedJavaMethod method, int invokeBci) {
248 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
249 return plugin != null && (!plugin.inlineOnly() || invokeBci >= 0);
250 }
251
252 @Override
253 public BytecodeProvider getDefaultReplacementBytecodeProvider() {
259 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
260 if (plugin instanceof MethodSubstitutionPlugin) {
261 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
262 ResolvedJavaMethod substitute = msPlugin.getSubstitute(providers.getMetaAccess());
263 return msPlugin.getBytecodeProvider().getBytecode(substitute);
264 }
265 return null;
266 }
267
268 @Override
269 public StructuredGraph getSubstitution(ResolvedJavaMethod method, int invokeBci) {
270 StructuredGraph result;
271 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method);
272 if (plugin != null && (!plugin.inlineOnly() || invokeBci >= 0)) {
273 MetaAccessProvider metaAccess = providers.getMetaAccess();
274 if (plugin instanceof MethodSubstitutionPlugin) {
275 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin;
276 ResolvedJavaMethod substitute = msPlugin.getSubstitute(metaAccess);
277 StructuredGraph graph = graphs.get(substitute);
278 if (graph == null) {
279 try (DebugContext debug = openDebugContext("Substitution_", method)) {
280 graph = makeGraph(debug, msPlugin.getBytecodeProvider(), substitute, null, method);
281 graph.freeze();
282 graphs.putIfAbsent(substitute, graph);
283 graph = graphs.get(substitute);
284 }
285 }
286 assert graph.isFrozen();
287 result = graph;
288 } else {
289 Bytecode code = new ResolvedJavaMethodBytecode(method);
290 ConstantReflectionProvider constantReflection = providers.getConstantReflection();
291 ConstantFieldProvider constantFieldProvider = providers.getConstantFieldProvider();
292 StampProvider stampProvider = providers.getStampProvider();
293 try (DebugContext debug = openDebugContext("Substitution_", method)) {
294 result = new IntrinsicGraphBuilder(options, debug, metaAccess, constantReflection, constantFieldProvider, stampProvider, code, invokeBci).buildGraph(plugin);
295 }
296 }
297 } else {
298 result = null;
299 }
300 return result;
301 }
302
303 /**
304 * Creates a preprocessed graph for a snippet or method substitution.
305 *
306 * @param bytecodeProvider how to access the bytecode of {@code method}
307 * @param method the snippet or method substitution for which a graph will be created
308 * @param args
309 * @param original the original method if {@code method} is a {@linkplain MethodSubstitution
310 * substitution} otherwise null
311 */
312 public StructuredGraph makeGraph(DebugContext debug, BytecodeProvider bytecodeProvider, ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) {
313 return createGraphMaker(method, original).makeGraph(debug, bytecodeProvider, args);
314 }
315
316 /**
317 * Can be overridden to return an object that specializes various parts of graph preprocessing.
318 */
319 protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) {
320 return new GraphMaker(this, substitute, original);
321 }
322
323 /**
324 * Creates and preprocesses a graph for a replacement.
325 */
326 public static class GraphMaker {
327
328 /** The replacements object that the graphs are created for. */
329 protected final ReplacementsImpl replacements;
330
331 /**
332 * The method for which a graph is being created.
333 */
334 protected final ResolvedJavaMethod method;
335
336 /**
337 * The original method which {@link #method} is substituting. Calls to {@link #method} or
338 * {@link #substitutedMethod} will be replaced with a forced inline of
339 * {@link #substitutedMethod}.
340 */
341 protected final ResolvedJavaMethod substitutedMethod;
342
343 protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) {
344 this.replacements = replacements;
345 this.method = substitute;
346 this.substitutedMethod = substitutedMethod;
347 }
348
349 @SuppressWarnings("try")
350 public StructuredGraph makeGraph(DebugContext debug, BytecodeProvider bytecodeProvider, Object[] args) {
351 try (DebugContext.Scope s = debug.scope("BuildSnippetGraph", method)) {
352 assert method.hasBytecodes() : method;
353 StructuredGraph graph = buildInitialGraph(debug, bytecodeProvider, method, args);
354
355 finalizeGraph(graph);
356
357 debug.dump(DebugContext.INFO_LEVEL, graph, "%s: Final", method.getName());
358
359 return graph;
360 } catch (Throwable e) {
361 throw debug.handle(e);
362 }
363 }
364
365 /**
366 * Does final processing of a snippet graph.
367 */
368 protected void finalizeGraph(StructuredGraph graph) {
369 if (!GraalOptions.SnippetCounters.getValue(replacements.options) || graph.getNodes().filter(SnippetCounterNode.class).isEmpty()) {
370 int sideEffectCount = 0;
371 assert (sideEffectCount = graph.getNodes().filter(e -> hasSideEffect(e)).count()) >= 0;
372 new ConvertDeoptimizeToGuardPhase().apply(graph, null);
373 assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node";
374
375 new DeadCodeEliminationPhase(Required).apply(graph);
376 } else {
377 // ConvertDeoptimizeToGuardPhase will eliminate snippet counters on paths
378 // that terminate in a deopt so we disable it if the graph contains
379 // snippet counters. The trade off is that we miss out on guard
380 // coalescing opportunities.
381 }
397 if (callTarget instanceof MethodCallTargetNode) {
398 ResolvedJavaMethod targetMethod = ((MethodCallTargetNode) callTarget).targetMethod();
399 if (targetMethod.isConstructor()) {
400 ResolvedJavaType throwableType = replacements.providers.getMetaAccess().lookupJavaType(Throwable.class);
401 return !throwableType.isAssignableFrom(targetMethod.getDeclaringClass());
402 }
403 }
404 }
405 // Not an exception constructor call
406 return true;
407 }
408 }
409 // Not a StateSplit
410 return false;
411 }
412
413 /**
414 * Builds the initial graph for a replacement.
415 */
416 @SuppressWarnings("try")
417 protected StructuredGraph buildInitialGraph(DebugContext debug, BytecodeProvider bytecodeProvider, final ResolvedJavaMethod methodToParse, Object[] args) {
418 // Replacements cannot have optimistic assumptions since they have
419 // to be valid for the entire run of the VM.
420 final StructuredGraph graph = new StructuredGraph.Builder(replacements.options, debug).method(methodToParse).build();
421
422 // Replacements are not user code so they do not participate in unsafe access
423 // tracking
424 graph.disableUnsafeAccessTracking();
425
426 try (DebugContext.Scope s = debug.scope("buildInitialGraph", graph)) {
427 MetaAccessProvider metaAccess = replacements.providers.getMetaAccess();
428
429 Plugins plugins = new Plugins(replacements.graphBuilderPlugins);
430 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins);
431 if (args != null) {
432 plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(args, metaAccess, replacements.snippetReflection));
433 }
434
435 IntrinsicContext initialIntrinsicContext = null;
436 if (method.getAnnotation(Snippet.class) == null) {
437 // Post-parse inlined intrinsic
438 initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, bytecodeProvider, INLINE_AFTER_PARSING);
439 } else {
440 // Snippet
441 ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method;
442 initialIntrinsicContext = new IntrinsicContext(original, method, bytecodeProvider, INLINE_AFTER_PARSING);
443 }
444
445 createGraphBuilder(metaAccess, replacements.providers.getStampProvider(), replacements.providers.getConstantReflection(), replacements.providers.getConstantFieldProvider(), config,
446 OptimisticOptimizations.NONE, initialIntrinsicContext).apply(graph);
447
448 new CanonicalizerPhase().apply(graph, new PhaseContext(replacements.providers));
449 } catch (Throwable e) {
450 throw debug.handle(e);
451 }
452 return graph;
453 }
454
455 protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, ConstantFieldProvider constantFieldProvider,
456 GraphBuilderConfiguration graphBuilderConfig, OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) {
457 return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, constantFieldProvider, graphBuilderConfig, optimisticOpts,
458 initialIntrinsicContext);
459 }
460 }
461
462 @Override
463 public void registerSnippetTemplateCache(SnippetTemplateCache templates) {
464 assert snippetTemplateCache.get(templates.getClass().getName()) == null;
465 snippetTemplateCache.put(templates.getClass().getName(), templates);
466 }
467
468 @Override
469 public <T extends SnippetTemplateCache> T getSnippetTemplateCache(Class<T> templatesClass) {
470 SnippetTemplateCache ret = snippetTemplateCache.get(templatesClass.getName());
|