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