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