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