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