001/* 002 * Copyright (c) 2011, 2013, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.replacements; 024 025import static com.oracle.graal.compiler.common.GraalOptions.*; 026import static com.oracle.graal.graphbuilderconf.IntrinsicContext.CompilationContext.*; 027import static com.oracle.graal.java.BytecodeParser.Options.*; 028import static com.oracle.graal.phases.common.DeadCodeEliminationPhase.Optionality.*; 029import static java.lang.String.*; 030import static jdk.internal.jvmci.meta.MetaUtil.*; 031 032import java.lang.reflect.*; 033import java.util.*; 034import java.util.concurrent.*; 035import java.util.concurrent.atomic.*; 036 037import jdk.internal.jvmci.code.*; 038import jdk.internal.jvmci.common.*; 039import com.oracle.graal.debug.*; 040import com.oracle.graal.debug.Debug.*; 041import jdk.internal.jvmci.meta.*; 042import jdk.internal.jvmci.options.*; 043import jdk.internal.jvmci.options.OptionValue.*; 044import sun.misc.*; 045 046import com.oracle.graal.api.replacements.*; 047import com.oracle.graal.compiler.common.*; 048import com.oracle.graal.graph.*; 049import com.oracle.graal.graph.Node.NodeIntrinsic; 050import com.oracle.graal.graphbuilderconf.*; 051import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; 052import com.oracle.graal.java.*; 053import com.oracle.graal.java.GraphBuilderPhase.Instance; 054import com.oracle.graal.nodes.*; 055import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 056import com.oracle.graal.nodes.java.*; 057import com.oracle.graal.nodes.spi.*; 058import com.oracle.graal.phases.*; 059import com.oracle.graal.phases.common.*; 060import com.oracle.graal.phases.tiers.*; 061import com.oracle.graal.phases.util.*; 062import com.oracle.graal.word.*; 063 064public class ReplacementsImpl implements Replacements, InlineInvokePlugin { 065 066 public final Providers providers; 067 public final SnippetReflectionProvider snippetReflection; 068 public final TargetDescription target; 069 private GraphBuilderConfiguration.Plugins graphBuilderPlugins; 070 071 /** 072 * The preprocessed replacement graphs. 073 */ 074 protected final ConcurrentMap<ResolvedJavaMethod, StructuredGraph> graphs; 075 076 public void setGraphBuilderPlugins(GraphBuilderConfiguration.Plugins plugins) { 077 assert this.graphBuilderPlugins == null; 078 this.graphBuilderPlugins = plugins; 079 } 080 081 public GraphBuilderConfiguration.Plugins getGraphBuilderPlugins() { 082 return graphBuilderPlugins; 083 } 084 085 protected boolean hasGenericInvocationPluginAnnotation(ResolvedJavaMethod method) { 086 return method.getAnnotation(Node.NodeIntrinsic.class) != null || method.getAnnotation(Word.Operation.class) != null || method.getAnnotation(Fold.class) != null; 087 } 088 089 private static final int MAX_GRAPH_INLINING_DEPTH = 100; // more than enough 090 091 /** 092 * Determines whether a given method should be inlined based on whether it has a substitution or 093 * whether the inlining context is already within a substitution. 094 * 095 * @return an object specifying how {@code method} is to be inlined or null if it should not be 096 * inlined based on substitution related criteria 097 */ 098 @Override 099 public InlineInfo shouldInlineInvoke(GraphBuilderContext b, ResolvedJavaMethod method, ValueNode[] args, JavaType returnType) { 100 ResolvedJavaMethod subst = getSubstitutionMethod(method); 101 if (subst != null) { 102 if (b.parsingIntrinsic() || InlineDuringParsing.getValue() || InlineIntrinsicsDuringParsing.getValue()) { 103 // Forced inlining of intrinsics 104 return new InlineInfo(subst, true); 105 } 106 return null; 107 } 108 if (b.parsingIntrinsic()) { 109 assert !hasGenericInvocationPluginAnnotation(method) : format("%s should have been handled by %s", method.format("%H.%n(%p)"), NodeIntrinsificationPlugin.class.getName()); 110 111 assert b.getDepth() < MAX_GRAPH_INLINING_DEPTH : "inlining limit exceeded"; 112 113 if (method.getName().startsWith("$jacoco")) { 114 throw new JVMCIError("Found call to JaCoCo instrumentation method " + method.format("%H.%n(%p)") + ". Placing \"//JaCoCo Exclude\" anywhere in " + 115 b.getMethod().getDeclaringClass().getSourceFileName() + " should fix this."); 116 } 117 118 // Force inlining when parsing replacements 119 return new InlineInfo(method, true); 120 } else { 121 assert method.getAnnotation(NodeIntrinsic.class) == null : String.format("@%s method %s must only be called from within a replacement%n%s", NodeIntrinsic.class.getSimpleName(), 122 method.format("%h.%n"), b); 123 } 124 return null; 125 } 126 127 @Override 128 public void notifyNotInlined(GraphBuilderContext b, ResolvedJavaMethod method, Invoke invoke) { 129 if (b.parsingIntrinsic()) { 130 IntrinsicContext intrinsic = b.getIntrinsic(); 131 assert intrinsic.isCallToOriginal(method) : format("All non-recursive calls in the intrinsic %s must be inlined or intrinsified: found call to %s", 132 intrinsic.getIntrinsicMethod().format("%H.%n(%p)"), method.format("%h.%n(%p)")); 133 } 134 } 135 136 /** 137 * Encapsulates method and macro substitutions for a single class. 138 */ 139 protected class ClassReplacements { 140 public final Map<ResolvedJavaMethod, ResolvedJavaMethod> methodSubstitutions = CollectionsFactory.newMap(); 141 142 public ClassReplacements(Class<?>[] substitutionClasses, AtomicReference<ClassReplacements> ref) { 143 for (Class<?> substitutionClass : substitutionClasses) { 144 ClassSubstitution classSubstitution = substitutionClass.getAnnotation(ClassSubstitution.class); 145 assert !Snippets.class.isAssignableFrom(substitutionClass); 146 SubstitutionGuard defaultGuard = getGuard(classSubstitution.defaultGuard()); 147 for (Method substituteMethod : substitutionClass.getDeclaredMethods()) { 148 if (ref.get() != null) { 149 // Bail if another thread beat us creating the substitutions 150 return; 151 } 152 MethodSubstitution methodSubstitution = substituteMethod.getAnnotation(MethodSubstitution.class); 153 if (methodSubstitution == null) { 154 continue; 155 } 156 157 int modifiers = substituteMethod.getModifiers(); 158 if (!Modifier.isStatic(modifiers)) { 159 throw new JVMCIError("Substitution methods must be static: " + substituteMethod); 160 } 161 162 if (methodSubstitution != null) { 163 SubstitutionGuard guard = getGuard(methodSubstitution.guard()); 164 if (guard == null) { 165 guard = defaultGuard; 166 } 167 168 if (Modifier.isAbstract(modifiers) || Modifier.isNative(modifiers)) { 169 throw new JVMCIError("Substitution method must not be abstract or native: " + substituteMethod); 170 } 171 String originalName = originalName(substituteMethod, methodSubstitution.value()); 172 JavaSignature originalSignature = originalSignature(substituteMethod, methodSubstitution.signature(), methodSubstitution.isStatic()); 173 Executable[] originalMethods = originalMethods(classSubstitution, classSubstitution.optional(), originalName, originalSignature); 174 if (originalMethods != null) { 175 for (Executable originalMethod : originalMethods) { 176 if (originalMethod != null && (guard == null || guard.execute())) { 177 registerMethodSubstitution(this, originalMethod, substituteMethod); 178 } 179 } 180 } 181 } 182 } 183 } 184 } 185 186 private JavaSignature originalSignature(Method substituteMethod, String methodSubstitution, boolean isStatic) { 187 Class<?>[] parameters; 188 Class<?> returnType; 189 if (methodSubstitution.isEmpty()) { 190 parameters = substituteMethod.getParameterTypes(); 191 if (!isStatic) { 192 assert parameters.length > 0 : "must be a static method with the 'this' object as its first parameter"; 193 parameters = Arrays.copyOfRange(parameters, 1, parameters.length); 194 } 195 returnType = substituteMethod.getReturnType(); 196 } else { 197 Signature signature = providers.getMetaAccess().parseMethodDescriptor(methodSubstitution); 198 parameters = new Class[signature.getParameterCount(false)]; 199 for (int i = 0; i < parameters.length; i++) { 200 parameters[i] = resolveClass(signature.getParameterType(i, null)); 201 } 202 returnType = resolveClass(signature.getReturnType(null)); 203 } 204 return new JavaSignature(returnType, parameters); 205 } 206 207 private Executable[] originalMethods(ClassSubstitution classSubstitution, boolean optional, String name, JavaSignature signature) { 208 Class<?> originalClass = classSubstitution.value(); 209 if (originalClass == ClassSubstitution.class) { 210 ArrayList<Executable> result = new ArrayList<>(); 211 for (String className : classSubstitution.className()) { 212 originalClass = resolveClass(className, classSubstitution.optional()); 213 if (originalClass != null) { 214 result.add(lookupOriginalMethod(originalClass, name, signature, optional)); 215 } 216 } 217 if (result.size() == 0) { 218 // optional class was not found 219 return null; 220 } 221 return result.toArray(new Executable[result.size()]); 222 } 223 Executable original = lookupOriginalMethod(originalClass, name, signature, optional); 224 if (original != null) { 225 return new Executable[]{original}; 226 } 227 return null; 228 } 229 230 private Executable lookupOriginalMethod(Class<?> originalClass, String name, JavaSignature signature, boolean optional) throws JVMCIError { 231 try { 232 if (name.equals("<init>")) { 233 assert signature.returnType.equals(void.class) : signature; 234 Constructor<?> original = originalClass.getDeclaredConstructor(signature.parameters); 235 return original; 236 } else { 237 Method original = originalClass.getDeclaredMethod(name, signature.parameters); 238 if (!original.getReturnType().equals(signature.returnType)) { 239 throw new NoSuchMethodException(originalClass.getName() + "." + name + signature); 240 } 241 return original; 242 } 243 } catch (NoSuchMethodException | SecurityException e) { 244 if (optional) { 245 return null; 246 } 247 throw new JVMCIError(e); 248 } 249 } 250 } 251 252 /** 253 * Per-class replacements. The entries in these maps are all fully initialized during 254 * single-threaded compiler startup and so do not need to be concurrent. 255 */ 256 private final Map<String, AtomicReference<ClassReplacements>> classReplacements; 257 private final Map<String, Class<?>[]> internalNameToSubstitutionClasses; 258 259 // This map is key'ed by a class name instead of a Class object so that 260 // it is stable across VM executions (in support of replay compilation). 261 private final Map<String, SnippetTemplateCache> snippetTemplateCache; 262 263 public ReplacementsImpl(Providers providers, SnippetReflectionProvider snippetReflection, TargetDescription target) { 264 this.providers = providers.copyWith(this); 265 this.classReplacements = CollectionsFactory.newMap(); 266 this.internalNameToSubstitutionClasses = CollectionsFactory.newMap(); 267 this.snippetReflection = snippetReflection; 268 this.target = target; 269 this.graphs = new ConcurrentHashMap<>(); 270 this.snippetTemplateCache = CollectionsFactory.newMap(); 271 } 272 273 private static final boolean UseSnippetGraphCache = Boolean.parseBoolean(System.getProperty("graal.useSnippetGraphCache", "true")); 274 private static final DebugTimer SnippetPreparationTime = Debug.timer("SnippetPreparationTime"); 275 276 /** 277 * Gets the method and macro replacements for a given class. This method will parse the 278 * replacements in the substitution classes associated with {@code internalName} the first time 279 * this method is called for {@code internalName}. 280 */ 281 protected ClassReplacements getClassReplacements(String internalName) { 282 Class<?>[] substitutionClasses = internalNameToSubstitutionClasses.get(internalName); 283 if (substitutionClasses != null) { 284 AtomicReference<ClassReplacements> crRef = classReplacements.get(internalName); 285 if (crRef.get() == null) { 286 crRef.compareAndSet(null, new ClassReplacements(substitutionClasses, crRef)); 287 } 288 return crRef.get(); 289 } 290 return null; 291 } 292 293 public StructuredGraph getSnippet(ResolvedJavaMethod method, Object[] args) { 294 return getSnippet(method, null, args); 295 } 296 297 @Override 298 public StructuredGraph getSnippet(ResolvedJavaMethod method, ResolvedJavaMethod recursiveEntry, Object[] args) { 299 assert method.getAnnotation(Snippet.class) != null : "Snippet must be annotated with @" + Snippet.class.getSimpleName(); 300 assert method.hasBytecodes() : "Snippet must not be abstract or native"; 301 302 StructuredGraph graph = UseSnippetGraphCache ? graphs.get(method) : null; 303 if (graph == null) { 304 try (DebugCloseable a = SnippetPreparationTime.start()) { 305 StructuredGraph newGraph = makeGraph(method, args, recursiveEntry); 306 Debug.metric("SnippetNodeCount[%#s]", method).add(newGraph.getNodeCount()); 307 if (!UseSnippetGraphCache || args != null) { 308 return newGraph; 309 } 310 graphs.putIfAbsent(method, newGraph); 311 graph = graphs.get(method); 312 } 313 } 314 return graph; 315 } 316 317 @Override 318 public void registerSnippet(ResolvedJavaMethod method) { 319 // No initialization needed as snippet graphs are created on demand in getSnippet 320 } 321 322 @Override 323 public StructuredGraph getSubstitution(ResolvedJavaMethod original, boolean fromBytecodeOnly, int invokeBci) { 324 ResolvedJavaMethod substitute = null; 325 if (!fromBytecodeOnly) { 326 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(original); 327 if (plugin != null) { 328 if (!plugin.inlineOnly() || invokeBci >= 0) { 329 if (plugin instanceof MethodSubstitutionPlugin) { 330 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin; 331 substitute = msPlugin.getSubstitute(providers.getMetaAccess()); 332 } else { 333 StructuredGraph graph = new IntrinsicGraphBuilder(providers.getMetaAccess(), providers.getConstantReflection(), providers.getStampProvider(), original, invokeBci).buildGraph(plugin); 334 if (graph != null) { 335 return graph; 336 } 337 } 338 } 339 } 340 } 341 if (substitute == null) { 342 ClassReplacements cr = getClassReplacements(original.getDeclaringClass().getName()); 343 substitute = cr == null ? null : cr.methodSubstitutions.get(original); 344 } 345 if (substitute == null) { 346 return null; 347 } 348 StructuredGraph graph = graphs.get(substitute); 349 if (graph == null) { 350 graph = makeGraph(substitute, null, original); 351 graph.freeze(); 352 graphs.putIfAbsent(substitute, graph); 353 graph = graphs.get(substitute); 354 } 355 assert graph.isFrozen(); 356 return graph; 357 358 } 359 360 private SubstitutionGuard getGuard(Class<? extends SubstitutionGuard> guardClass) { 361 if (guardClass != SubstitutionGuard.class) { 362 Constructor<?>[] constructors = guardClass.getConstructors(); 363 if (constructors.length != 1) { 364 throw new JVMCIError("Substitution guard " + guardClass.getSimpleName() + " must have a single public constructor"); 365 } 366 Constructor<?> constructor = constructors[0]; 367 Class<?>[] paramTypes = constructor.getParameterTypes(); 368 // Check for supported constructor signatures 369 try { 370 Object[] args = new Object[constructor.getParameterCount()]; 371 for (int i = 0; i < args.length; i++) { 372 Object arg = snippetReflection.getSubstitutionGuardParameter(paramTypes[i]); 373 if (arg != null) { 374 args[i] = arg; 375 } else if (paramTypes[i].isInstance(target.arch)) { 376 args[i] = target.arch; 377 } else { 378 throw new JVMCIError("Unsupported type %s in substitution guard constructor: %s", paramTypes[i].getName(), constructor); 379 } 380 } 381 382 return (SubstitutionGuard) constructor.newInstance(args); 383 } catch (Exception e) { 384 throw new JVMCIError(e); 385 } 386 } 387 return null; 388 } 389 390 private static boolean checkSubstitutionInternalName(Class<?> substitutions, String internalName) { 391 ClassSubstitution cs = substitutions.getAnnotation(ClassSubstitution.class); 392 assert cs != null : substitutions + " must be annotated by " + ClassSubstitution.class.getSimpleName(); 393 if (cs.value() == ClassSubstitution.class) { 394 for (String className : cs.className()) { 395 if (toInternalName(className).equals(internalName)) { 396 return true; 397 } 398 } 399 assert false : internalName + " not found in " + Arrays.toString(cs.className()); 400 } else { 401 String originalInternalName = toInternalName(cs.value().getName()); 402 assert originalInternalName.equals(internalName) : originalInternalName + " != " + internalName; 403 } 404 return true; 405 } 406 407 public void registerSubstitutions(Type original, Class<?> substitutionClass) { 408 String internalName = toInternalName(original.getTypeName()); 409 assert checkSubstitutionInternalName(substitutionClass, internalName); 410 Class<?>[] classes = internalNameToSubstitutionClasses.get(internalName); 411 if (classes == null) { 412 classes = new Class<?>[]{substitutionClass}; 413 } else { 414 assert !Arrays.asList(classes).contains(substitutionClass); 415 classes = Arrays.copyOf(classes, classes.length + 1); 416 classes[classes.length - 1] = substitutionClass; 417 } 418 internalNameToSubstitutionClasses.put(internalName, classes); 419 AtomicReference<ClassReplacements> existing = classReplacements.put(internalName, new AtomicReference<>()); 420 assert existing == null || existing.get() == null; 421 } 422 423 /** 424 * Registers a method substitution. 425 * 426 * @param originalMember a method or constructor being substituted 427 * @param substituteMethod the substitute method 428 * @return the original method 429 */ 430 protected ResolvedJavaMethod registerMethodSubstitution(ClassReplacements cr, Executable originalMember, Method substituteMethod) { 431 MetaAccessProvider metaAccess = providers.getMetaAccess(); 432 ResolvedJavaMethod substitute = metaAccess.lookupJavaMethod(substituteMethod); 433 ResolvedJavaMethod original = metaAccess.lookupJavaMethod(originalMember); 434 if (Debug.isLogEnabled()) { 435 Debug.log("substitution: %s --> %s", original.format("%H.%n(%p) %r"), substitute.format("%H.%n(%p) %r")); 436 } 437 438 cr.methodSubstitutions.put(original, substitute); 439 return original; 440 } 441 442 /** 443 * Creates a preprocessed graph for a snippet or method substitution. 444 * 445 * @param method the snippet or method substitution for which a graph will be created 446 * @param args 447 * @param original the original method if {@code method} is a {@linkplain MethodSubstitution 448 * substitution} otherwise null 449 */ 450 public StructuredGraph makeGraph(ResolvedJavaMethod method, Object[] args, ResolvedJavaMethod original) { 451 try (OverrideScope s = OptionValue.override(DeoptALot, false)) { 452 return createGraphMaker(method, original).makeGraph(args); 453 } 454 } 455 456 /** 457 * Can be overridden to return an object that specializes various parts of graph preprocessing. 458 */ 459 protected GraphMaker createGraphMaker(ResolvedJavaMethod substitute, ResolvedJavaMethod original) { 460 return new GraphMaker(this, substitute, original); 461 } 462 463 /** 464 * Creates and preprocesses a graph for a replacement. 465 */ 466 public static class GraphMaker { 467 /** The replacements object that the graphs are created for. */ 468 protected final ReplacementsImpl replacements; 469 470 /** 471 * The method for which a graph is being created. 472 */ 473 protected final ResolvedJavaMethod method; 474 475 /** 476 * The original method which {@link #method} is substituting. Calls to {@link #method} or 477 * {@link #substitutedMethod} will be replaced with a forced inline of 478 * {@link #substitutedMethod}. 479 */ 480 protected final ResolvedJavaMethod substitutedMethod; 481 482 protected GraphMaker(ReplacementsImpl replacements, ResolvedJavaMethod substitute, ResolvedJavaMethod substitutedMethod) { 483 this.replacements = replacements; 484 this.method = substitute; 485 this.substitutedMethod = substitutedMethod; 486 } 487 488 public StructuredGraph makeGraph(Object[] args) { 489 try (Scope s = Debug.scope("BuildSnippetGraph", method)) { 490 assert method.hasBytecodes() : method; 491 StructuredGraph graph = buildInitialGraph(method, args); 492 493 finalizeGraph(graph); 494 495 Debug.dump(graph, "%s: Final", method.getName()); 496 497 return graph; 498 } catch (Throwable e) { 499 throw Debug.handle(e); 500 } 501 } 502 503 /** 504 * Does final processing of a snippet graph. 505 */ 506 protected void finalizeGraph(StructuredGraph graph) { 507 if (!GraalOptions.SnippetCounters.getValue() || graph.getNodes().filter(SnippetCounterNode.class).isEmpty()) { 508 int sideEffectCount = 0; 509 assert (sideEffectCount = graph.getNodes().filter(e -> hasSideEffect(e)).count()) >= 0; 510 new ConvertDeoptimizeToGuardPhase().apply(graph, null); 511 assert sideEffectCount == graph.getNodes().filter(e -> hasSideEffect(e)).count() : "deleted side effecting node"; 512 513 new DeadCodeEliminationPhase(Required).apply(graph); 514 } else { 515 // ConvertDeoptimizeToGuardPhase will eliminate snippet counters on paths 516 // that terminate in a deopt so we disable it if the graph contains 517 // snippet counters. The trade off is that we miss out on guard 518 // coalescing opportunities. 519 } 520 } 521 522 /** 523 * Filter nodes which have side effects and shouldn't be deleted from snippets when 524 * converting deoptimizations to guards. Currently this only allows exception constructors 525 * to be eliminated to cover the case when Java assertions are in the inlined code. 526 * 527 * @param node 528 * @return true for nodes that have side effects and are unsafe to delete 529 */ 530 private boolean hasSideEffect(Node node) { 531 if (node instanceof StateSplit) { 532 if (((StateSplit) node).hasSideEffect()) { 533 if (node instanceof Invoke) { 534 CallTargetNode callTarget = ((Invoke) node).callTarget(); 535 if (callTarget instanceof MethodCallTargetNode) { 536 ResolvedJavaMethod targetMethod = ((MethodCallTargetNode) callTarget).targetMethod(); 537 if (targetMethod.isConstructor()) { 538 ResolvedJavaType throwableType = replacements.providers.getMetaAccess().lookupJavaType(Throwable.class); 539 return !throwableType.isAssignableFrom(targetMethod.getDeclaringClass()); 540 } 541 } 542 } 543 // Not an exception constructor call 544 return true; 545 } 546 } 547 // Not a StateSplit 548 return false; 549 } 550 551 /** 552 * Builds the initial graph for a snippet. 553 */ 554 protected StructuredGraph buildInitialGraph(final ResolvedJavaMethod methodToParse, Object[] args) { 555 // Replacements cannot have optimistic assumptions since they have 556 // to be valid for the entire run of the VM. 557 final StructuredGraph graph = new StructuredGraph(methodToParse, AllowAssumptions.NO); 558 559 // They will also never evolve or have breakpoints set in them 560 graph.disableInlinedMethodRecording(); 561 // They are not user code so they do not participate in unsafe access tracking 562 graph.disableUnsafeAccessTracking(); 563 564 try (Scope s = Debug.scope("buildInitialGraph", graph)) { 565 MetaAccessProvider metaAccess = replacements.providers.getMetaAccess(); 566 567 Plugins plugins = new Plugins(replacements.graphBuilderPlugins); 568 GraphBuilderConfiguration config = GraphBuilderConfiguration.getSnippetDefault(plugins); 569 if (args != null) { 570 plugins.prependParameterPlugin(new ConstantBindingParameterPlugin(args, metaAccess, replacements.snippetReflection)); 571 } 572 573 IntrinsicContext initialIntrinsicContext = null; 574 if (method.getAnnotation(Snippet.class) == null) { 575 // Post-parse inlined intrinsic 576 initialIntrinsicContext = new IntrinsicContext(substitutedMethod, method, INLINE_AFTER_PARSING); 577 } else { 578 // Snippet 579 ResolvedJavaMethod original = substitutedMethod != null ? substitutedMethod : method; 580 initialIntrinsicContext = new IntrinsicContext(original, method, INLINE_AFTER_PARSING); 581 } 582 583 createGraphBuilder(metaAccess, replacements.providers.getStampProvider(), replacements.providers.getConstantReflection(), config, OptimisticOptimizations.NONE, initialIntrinsicContext).apply( 584 graph); 585 586 if (OptCanonicalizer.getValue()) { 587 new CanonicalizerPhase().apply(graph, new PhaseContext(replacements.providers)); 588 } 589 } catch (Throwable e) { 590 throw Debug.handle(e); 591 } 592 return graph; 593 } 594 595 protected Instance createGraphBuilder(MetaAccessProvider metaAccess, StampProvider stampProvider, ConstantReflectionProvider constantReflection, GraphBuilderConfiguration graphBuilderConfig, 596 OptimisticOptimizations optimisticOpts, IntrinsicContext initialIntrinsicContext) { 597 return new GraphBuilderPhase.Instance(metaAccess, stampProvider, constantReflection, graphBuilderConfig, optimisticOpts, initialIntrinsicContext); 598 } 599 } 600 601 private static String originalName(Method substituteMethod, String methodSubstitution) { 602 if (methodSubstitution.isEmpty()) { 603 return substituteMethod.getName(); 604 } else { 605 return methodSubstitution; 606 } 607 } 608 609 /** 610 * Resolves a name to a class. 611 * 612 * @param className the name of the class to resolve 613 * @param optional if true, resolution failure returns null 614 * @return the resolved class or null if resolution fails and {@code optional} is true 615 */ 616 public static Class<?> resolveClass(String className, boolean optional) { 617 try { 618 // Need to use launcher class path to handle classes 619 // that are not on the boot class path 620 ClassLoader cl = Launcher.getLauncher().getClassLoader(); 621 return Class.forName(className, false, cl); 622 } catch (ClassNotFoundException e) { 623 if (optional) { 624 return null; 625 } 626 throw new JVMCIError("Could not resolve type " + className); 627 } 628 } 629 630 private static Class<?> resolveClass(JavaType type) { 631 JavaType base = type; 632 int dimensions = 0; 633 while (base.getComponentType() != null) { 634 base = base.getComponentType(); 635 dimensions++; 636 } 637 638 Class<?> baseClass = base.getKind() != Kind.Object ? base.getKind().toJavaClass() : resolveClass(base.toJavaName(), false); 639 return dimensions == 0 ? baseClass : Array.newInstance(baseClass, new int[dimensions]).getClass(); 640 } 641 642 static class JavaSignature { 643 final Class<?> returnType; 644 final Class<?>[] parameters; 645 646 public JavaSignature(Class<?> returnType, Class<?>[] parameters) { 647 this.parameters = parameters; 648 this.returnType = returnType; 649 } 650 651 @Override 652 public String toString() { 653 StringBuilder sb = new StringBuilder("("); 654 for (int i = 0; i < parameters.length; i++) { 655 if (i != 0) { 656 sb.append(", "); 657 } 658 sb.append(parameters[i].getName()); 659 } 660 return sb.append(") ").append(returnType.getName()).toString(); 661 } 662 } 663 664 @Override 665 public Collection<ResolvedJavaMethod> getAllReplacements() { 666 HashSet<ResolvedJavaMethod> result = new HashSet<>(); 667 for (String internalName : classReplacements.keySet()) { 668 ClassReplacements cr = getClassReplacements(internalName); 669 result.addAll(cr.methodSubstitutions.keySet()); 670 } 671 return result; 672 } 673 674 public boolean hasSubstitution(ResolvedJavaMethod method, boolean fromBytecodeOnly, int callerBci) { 675 if (!fromBytecodeOnly) { 676 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(method); 677 if (plugin != null) { 678 if (!plugin.inlineOnly() || callerBci >= 0) { 679 return true; 680 } 681 } 682 } 683 return getSubstitutionMethod(method) != null; 684 } 685 686 public ResolvedJavaMethod getSubstitutionMethod(ResolvedJavaMethod original) { 687 InvocationPlugin plugin = graphBuilderPlugins.getInvocationPlugins().lookupInvocation(original); 688 if (plugin instanceof MethodSubstitutionPlugin) { 689 MethodSubstitutionPlugin msPlugin = (MethodSubstitutionPlugin) plugin; 690 return msPlugin.getSubstitute(providers.getMetaAccess()); 691 } 692 ClassReplacements cr = getClassReplacements(original.getDeclaringClass().getName()); 693 return cr == null ? null : cr.methodSubstitutions.get(original); 694 } 695 696 @Override 697 public void registerSnippetTemplateCache(SnippetTemplateCache templates) { 698 assert snippetTemplateCache.get(templates.getClass().getName()) == null; 699 snippetTemplateCache.put(templates.getClass().getName(), templates); 700 } 701 702 @Override 703 public <T extends SnippetTemplateCache> T getSnippetTemplateCache(Class<T> templatesClass) { 704 SnippetTemplateCache ret = snippetTemplateCache.get(templatesClass.getName()); 705 return templatesClass.cast(ret); 706 } 707}