1 /* 2 * Copyright (c) 2015, 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.replacements; 26 27 import static org.graalvm.compiler.debug.GraalError.unimplemented; 28 import static org.graalvm.compiler.nodeinfo.NodeCycles.CYCLES_IGNORED; 29 import static org.graalvm.compiler.nodeinfo.NodeSize.SIZE_IGNORED; 30 31 import java.net.URI; 32 import java.util.ArrayList; 33 import java.util.Arrays; 34 import java.util.Formatter; 35 import java.util.HashMap; 36 import java.util.List; 37 import java.util.Map; 38 39 import jdk.internal.vm.compiler.collections.EconomicMap; 40 import jdk.internal.vm.compiler.collections.Equivalence; 41 import org.graalvm.compiler.api.replacements.Fold; 42 import org.graalvm.compiler.bytecode.Bytecode; 43 import org.graalvm.compiler.bytecode.BytecodeProvider; 44 import org.graalvm.compiler.bytecode.ResolvedJavaMethodBytecode; 45 import org.graalvm.compiler.core.common.PermanentBailoutException; 46 import org.graalvm.compiler.core.common.cfg.CFGVerifier; 47 import org.graalvm.compiler.core.common.spi.ConstantFieldProvider; 48 import org.graalvm.compiler.core.common.type.Stamp; 49 import org.graalvm.compiler.core.common.type.StampFactory; 50 import org.graalvm.compiler.core.common.type.StampPair; 51 import org.graalvm.compiler.debug.DebugCloseable; 52 import org.graalvm.compiler.debug.DebugContext; 53 import org.graalvm.compiler.debug.GraalError; 54 import org.graalvm.compiler.graph.Node; 55 import org.graalvm.compiler.graph.Node.NodeIntrinsic; 56 import org.graalvm.compiler.graph.NodeClass; 57 import org.graalvm.compiler.graph.NodeSourcePosition; 58 import org.graalvm.compiler.graph.SourceLanguagePosition; 59 import org.graalvm.compiler.graph.SourceLanguagePositionProvider; 60 import org.graalvm.compiler.graph.spi.Canonicalizable; 61 import org.graalvm.compiler.java.GraphBuilderPhase; 62 import org.graalvm.compiler.nodeinfo.NodeInfo; 63 import org.graalvm.compiler.nodes.AbstractBeginNode; 64 import org.graalvm.compiler.nodes.AbstractMergeNode; 65 import org.graalvm.compiler.nodes.CallTargetNode; 66 import org.graalvm.compiler.nodes.CallTargetNode.InvokeKind; 67 import org.graalvm.compiler.nodes.ControlSinkNode; 68 import org.graalvm.compiler.nodes.DeoptimizeNode; 69 import org.graalvm.compiler.nodes.EncodedGraph; 70 import org.graalvm.compiler.nodes.FixedNode; 71 import org.graalvm.compiler.nodes.FixedWithNextNode; 72 import org.graalvm.compiler.nodes.FrameState; 73 import org.graalvm.compiler.nodes.IfNode; 74 import org.graalvm.compiler.nodes.Invoke; 75 import org.graalvm.compiler.nodes.InvokeWithExceptionNode; 76 import org.graalvm.compiler.nodes.MergeNode; 77 import org.graalvm.compiler.nodes.NodeView; 78 import org.graalvm.compiler.nodes.ParameterNode; 79 import org.graalvm.compiler.nodes.ReturnNode; 80 import org.graalvm.compiler.nodes.SimplifyingGraphDecoder; 81 import org.graalvm.compiler.nodes.StateSplit; 82 import org.graalvm.compiler.nodes.StructuredGraph; 83 import org.graalvm.compiler.nodes.UnwindNode; 84 import org.graalvm.compiler.nodes.ValueNode; 85 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 86 import org.graalvm.compiler.nodes.extended.ForeignCallNode; 87 import org.graalvm.compiler.nodes.extended.IntegerSwitchNode; 88 import org.graalvm.compiler.nodes.graphbuilderconf.GeneratedInvocationPlugin; 89 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderContext; 90 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin; 91 import org.graalvm.compiler.nodes.graphbuilderconf.InlineInvokePlugin.InlineInfo; 92 import org.graalvm.compiler.nodes.graphbuilderconf.IntrinsicContext; 93 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugin; 94 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 95 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins.InvocationPluginReceiver; 96 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin; 97 import org.graalvm.compiler.nodes.graphbuilderconf.LoopExplosionPlugin.LoopExplosionKind; 98 import org.graalvm.compiler.nodes.graphbuilderconf.MethodSubstitutionPlugin; 99 import org.graalvm.compiler.nodes.graphbuilderconf.NodePlugin; 100 import org.graalvm.compiler.nodes.graphbuilderconf.ParameterPlugin; 101 import org.graalvm.compiler.nodes.java.LoadFieldNode; 102 import org.graalvm.compiler.nodes.java.LoadIndexedNode; 103 import org.graalvm.compiler.nodes.java.MethodCallTargetNode; 104 import org.graalvm.compiler.nodes.java.MonitorIdNode; 105 import org.graalvm.compiler.nodes.java.NewArrayNode; 106 import org.graalvm.compiler.nodes.java.NewInstanceNode; 107 import org.graalvm.compiler.nodes.java.NewMultiArrayNode; 108 import org.graalvm.compiler.nodes.java.StoreFieldNode; 109 import org.graalvm.compiler.nodes.java.StoreIndexedNode; 110 import org.graalvm.compiler.nodes.spi.CoreProviders; 111 import org.graalvm.compiler.nodes.spi.Replacements; 112 import org.graalvm.compiler.nodes.spi.StampProvider; 113 import org.graalvm.compiler.nodes.type.StampTool; 114 import org.graalvm.compiler.nodes.util.GraphUtil; 115 import org.graalvm.compiler.options.Option; 116 import org.graalvm.compiler.options.OptionKey; 117 import org.graalvm.compiler.options.OptionType; 118 import org.graalvm.compiler.options.OptionValues; 119 import org.graalvm.compiler.phases.common.inlining.InliningUtil; 120 121 import jdk.vm.ci.code.Architecture; 122 import jdk.vm.ci.code.BailoutException; 123 import jdk.vm.ci.code.BytecodeFrame; 124 import jdk.vm.ci.meta.Assumptions; 125 import jdk.vm.ci.meta.ConstantReflectionProvider; 126 import jdk.vm.ci.meta.DeoptimizationAction; 127 import jdk.vm.ci.meta.DeoptimizationReason; 128 import jdk.vm.ci.meta.JavaConstant; 129 import jdk.vm.ci.meta.JavaKind; 130 import jdk.vm.ci.meta.JavaType; 131 import jdk.vm.ci.meta.MetaAccessProvider; 132 import jdk.vm.ci.meta.ResolvedJavaField; 133 import jdk.vm.ci.meta.ResolvedJavaMethod; 134 import jdk.vm.ci.meta.ResolvedJavaType; 135 136 /** 137 * A graph decoder that performs partial evaluation, i.e., that performs method inlining and 138 * canonicalization/simplification of nodes during decoding. 139 * 140 * Inlining and loop explosion are configured via the plugin mechanism also used by the 141 * {@link GraphBuilderPhase}. However, not all callback methods defined in 142 * {@link GraphBuilderContext} are available since decoding is more limited than graph building. 143 * 144 * The standard {@link Canonicalizable#canonical node canonicalization} interface is used to 145 * canonicalize nodes during decoding. Additionally, {@link IfNode branches} and 146 * {@link IntegerSwitchNode switches} with constant conditions are simplified. 147 */ 148 public abstract class PEGraphDecoder extends SimplifyingGraphDecoder { 149 150 private static final Object CACHED_NULL_VALUE = new Object(); 151 152 public static class Options { 153 @Option(help = "Maximum inlining depth during partial evaluation before reporting an infinite recursion")// 154 public static final OptionKey<Integer> InliningDepthError = new OptionKey<>(1000); 155 156 @Option(help = "Max number of loop explosions per method.", type = OptionType.Debug)// 157 public static final OptionKey<Integer> MaximumLoopExplosionCount = new OptionKey<>(10000); 158 159 @Option(help = "Do not bail out but throw an exception on failed loop explosion.", type = OptionType.Debug)// 160 public static final OptionKey<Boolean> FailedLoopExplosionIsFatal = new OptionKey<>(false); 161 } 162 163 protected class PEMethodScope extends MethodScope { 164 /** The state of the caller method. Only non-null during method inlining. */ 165 protected final PEMethodScope caller; 166 protected final ResolvedJavaMethod method; 167 protected final InvokeData invokeData; 168 protected final int inliningDepth; 169 170 protected final ValueNode[] arguments; 171 private SourceLanguagePosition sourceLanguagePosition = UnresolvedSourceLanguagePosition.INSTANCE; 172 173 protected FrameState outerState; 174 protected FrameState exceptionState; 175 protected ExceptionPlaceholderNode exceptionPlaceholderNode; 176 protected NodeSourcePosition callerBytecodePosition; 177 178 protected PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData, 179 int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, ValueNode[] arguments) { 180 super(callerLoopScope, targetGraph, encodedGraph, loopExplosionKind(method, loopExplosionPlugin)); 181 182 this.caller = caller; 183 this.method = method; 184 this.invokeData = invokeData; 185 this.inliningDepth = inliningDepth; 186 this.arguments = arguments; 187 } 188 189 @Override 190 public boolean isInlinedMethod() { 191 return caller != null; 192 } 193 194 @Override 195 public NodeSourcePosition getCallerBytecodePosition(NodeSourcePosition position) { 196 if (caller == null) { 197 return position; 198 } 199 if (callerBytecodePosition == null) { 200 NodeSourcePosition invokePosition = invokeData.invoke.asNode().getNodeSourcePosition(); 201 if (invokePosition == null) { 202 assert position == null : "should only happen when tracking is disabled"; 203 return null; 204 } 205 callerBytecodePosition = invokePosition; 206 } 207 if (position != null) { 208 return position.addCaller(caller.resolveSourceLanguagePosition(), callerBytecodePosition); 209 } 210 final SourceLanguagePosition pos = caller.resolveSourceLanguagePosition(); 211 if (pos != null && callerBytecodePosition != null) { 212 return new NodeSourcePosition(pos, callerBytecodePosition.getCaller(), callerBytecodePosition.getMethod(), callerBytecodePosition.getBCI()); 213 } 214 return callerBytecodePosition; 215 } 216 217 private SourceLanguagePosition resolveSourceLanguagePosition() { 218 SourceLanguagePosition res = sourceLanguagePosition; 219 if (res == UnresolvedSourceLanguagePosition.INSTANCE) { 220 res = null; 221 if (arguments != null && method.hasReceiver() && arguments.length > 0 && arguments[0].isJavaConstant()) { 222 JavaConstant constantArgument = arguments[0].asJavaConstant(); 223 res = sourceLanguagePositionProvider.getPosition(constantArgument); 224 } 225 sourceLanguagePosition = res; 226 } 227 return res; 228 } 229 } 230 231 private static final class UnresolvedSourceLanguagePosition implements SourceLanguagePosition { 232 static final SourceLanguagePosition INSTANCE = new UnresolvedSourceLanguagePosition(); 233 234 @Override 235 public String toShortString() { 236 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 237 } 238 239 @Override 240 public int getOffsetEnd() { 241 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 242 } 243 244 @Override 245 public int getOffsetStart() { 246 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 247 } 248 249 @Override 250 public int getLineNumber() { 251 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 252 } 253 254 @Override 255 public URI getURI() { 256 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 257 } 258 259 @Override 260 public String getLanguage() { 261 throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable."); 262 } 263 } 264 265 protected class PENonAppendGraphBuilderContext implements GraphBuilderContext { 266 protected final PEMethodScope methodScope; 267 protected final Invoke invoke; 268 269 @Override 270 public ExternalInliningContext getExternalInliningContext() { 271 return new ExternalInliningContext() { 272 @Override 273 public int getInlinedDepth() { 274 int count = 0; 275 PEGraphDecoder.PEMethodScope scope = methodScope; 276 while (scope != null) { 277 if (scope.method.equals(callInlinedMethod)) { 278 count++; 279 } 280 scope = scope.caller; 281 } 282 return count; 283 } 284 }; 285 } 286 287 public PENonAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke) { 288 this.methodScope = methodScope; 289 this.invoke = invoke; 290 } 291 292 /** 293 * {@link Fold} and {@link NodeIntrinsic} can be deferred during parsing/decoding. Only by 294 * the end of {@linkplain SnippetTemplate#instantiate Snippet instantiation} do they need to 295 * have been processed. 296 * 297 * This is how SVM handles snippets. They are parsed with plugins disabled and then encoded 298 * and stored in the image. When the snippet is needed at runtime the graph is decoded and 299 * the plugins are run during the decoding process. If they aren't handled at this point 300 * then they will never be handled. 301 */ 302 @Override 303 public boolean canDeferPlugin(GeneratedInvocationPlugin plugin) { 304 return plugin.getSource().equals(Fold.class) || plugin.getSource().equals(Node.NodeIntrinsic.class); 305 } 306 307 @Override 308 public BailoutException bailout(String string) { 309 BailoutException bailout = new PermanentBailoutException(string); 310 throw GraphUtil.createBailoutException(string, bailout, GraphUtil.approxSourceStackTraceElement(methodScope.getCallerBytecodePosition())); 311 } 312 313 @Override 314 public StampProvider getStampProvider() { 315 return providers.getStampProvider(); 316 } 317 318 @Override 319 public MetaAccessProvider getMetaAccess() { 320 return providers.getMetaAccess(); 321 } 322 323 @Override 324 public ConstantReflectionProvider getConstantReflection() { 325 return providers.getConstantReflection(); 326 } 327 328 @Override 329 public ConstantFieldProvider getConstantFieldProvider() { 330 return providers.getConstantFieldProvider(); 331 } 332 333 @Override 334 public Replacements getReplacements() { 335 return providers.getReplacements(); 336 } 337 338 @Override 339 public StructuredGraph getGraph() { 340 return graph; 341 } 342 343 @Override 344 public int getDepth() { 345 return methodScope.inliningDepth; 346 } 347 348 @Override 349 public IntrinsicContext getIntrinsic() { 350 return PEGraphDecoder.this.getIntrinsic(); 351 } 352 353 @Override 354 public <T extends ValueNode> T append(T value) { 355 throw unimplemented(); 356 } 357 358 @Override 359 public void push(JavaKind kind, ValueNode value) { 360 throw unimplemented(); 361 } 362 363 @Override 364 public Invoke handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean inlineEverything) { 365 throw unimplemented(); 366 } 367 368 @Override 369 public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) { 370 throw unimplemented(); 371 } 372 373 @Override 374 public boolean intrinsify(BytecodeProvider bytecodeProvider, ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, InvocationPlugin.Receiver receiver, ValueNode[] args) { 375 return false; 376 } 377 378 @Override 379 public boolean intrinsify(ResolvedJavaMethod targetMethod, StructuredGraph substituteGraph, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) { 380 return false; 381 } 382 383 @Override 384 public void setStateAfter(StateSplit stateSplit) { 385 throw unimplemented(); 386 } 387 388 @Override 389 public GraphBuilderContext getParent() { 390 throw unimplemented(); 391 } 392 393 @Override 394 public Bytecode getCode() { 395 throw unimplemented(); 396 } 397 398 @Override 399 public ResolvedJavaMethod getMethod() { 400 throw unimplemented(); 401 } 402 403 @Override 404 public int bci() { 405 return invoke.bci(); 406 } 407 408 @Override 409 public InvokeKind getInvokeKind() { 410 throw unimplemented(); 411 } 412 413 @Override 414 public JavaType getInvokeReturnType() { 415 throw unimplemented(); 416 } 417 418 @Override 419 public String toString() { 420 Formatter fmt = new Formatter(); 421 PEMethodScope scope = this.methodScope; 422 fmt.format("%s", new ResolvedJavaMethodBytecode(scope.method).asStackTraceElement(invoke.bci())); 423 NodeSourcePosition callers = scope.getCallerBytecodePosition(); 424 if (callers != null) { 425 fmt.format("%n%s", callers); 426 } 427 return fmt.toString(); 428 } 429 } 430 431 protected IntrinsicContext getIntrinsic() { 432 return null; 433 } 434 435 protected class PEAppendGraphBuilderContext extends PENonAppendGraphBuilderContext { 436 protected FixedWithNextNode lastInstr; 437 protected ValueNode pushedNode; 438 protected boolean invokeConsumed; 439 protected final InvokeKind invokeKind; 440 protected final JavaType invokeReturnType; 441 442 public PEAppendGraphBuilderContext(PEMethodScope inlineScope, FixedWithNextNode lastInstr) { 443 this(inlineScope, lastInstr, null, null); 444 } 445 446 public PEAppendGraphBuilderContext(PEMethodScope inlineScope, FixedWithNextNode lastInstr, InvokeKind invokeKind, JavaType invokeReturnType) { 447 super(inlineScope, inlineScope.invokeData != null ? inlineScope.invokeData.invoke : null); 448 this.lastInstr = lastInstr; 449 this.invokeKind = invokeKind; 450 this.invokeReturnType = invokeReturnType; 451 } 452 453 @Override 454 public void push(JavaKind kind, ValueNode value) { 455 if (pushedNode != null) { 456 throw unimplemented("Only one push is supported"); 457 } 458 pushedNode = value; 459 } 460 461 @Override 462 public void setStateAfter(StateSplit stateSplit) { 463 Node stateAfter = decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); 464 getGraph().add(stateAfter); 465 FrameState fs = (FrameState) handleFloatingNodeAfterAdd(methodScope.caller, methodScope.callerLoopScope, stateAfter); 466 stateSplit.setStateAfter(fs); 467 } 468 469 @SuppressWarnings("try") 470 @Override 471 public <T extends ValueNode> T append(T v) { 472 if (v.graph() != null) { 473 return v; 474 } 475 try (DebugCloseable position = withNodeSoucePosition()) { 476 T added = getGraph().addOrUniqueWithInputs(v); 477 if (added == v) { 478 updateLastInstruction(v); 479 } 480 return added; 481 } 482 } 483 484 private DebugCloseable withNodeSoucePosition() { 485 if (getGraph().trackNodeSourcePosition()) { 486 NodeSourcePosition callerBytecodePosition = methodScope.getCallerBytecodePosition(); 487 if (callerBytecodePosition != null) { 488 return getGraph().withNodeSourcePosition(callerBytecodePosition); 489 } 490 } 491 return null; 492 } 493 494 private <T extends ValueNode> void updateLastInstruction(T v) { 495 if (v instanceof FixedNode) { 496 FixedNode fixedNode = (FixedNode) v; 497 if (lastInstr != null) { 498 lastInstr.setNext(fixedNode); 499 } 500 if (fixedNode instanceof FixedWithNextNode) { 501 FixedWithNextNode fixedWithNextNode = (FixedWithNextNode) fixedNode; 502 assert fixedWithNextNode.next() == null : "cannot append instruction to instruction which isn't end"; 503 lastInstr = fixedWithNextNode; 504 } else { 505 lastInstr = null; 506 } 507 } 508 } 509 510 @Override 511 public InvokeKind getInvokeKind() { 512 if (invokeKind != null) { 513 return invokeKind; 514 } 515 return super.getInvokeKind(); 516 } 517 518 @Override 519 public JavaType getInvokeReturnType() { 520 if (invokeReturnType != null) { 521 return invokeReturnType; 522 } 523 return super.getInvokeReturnType(); 524 } 525 526 @Override 527 public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) { 528 if (invokeConsumed) { 529 throw unimplemented("handleReplacedInvoke can be called only once"); 530 } 531 invokeConsumed = true; 532 533 appendInvoke(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData, callTarget); 534 updateLastInstruction(invoke.asNode()); 535 } 536 537 @Override 538 public GraphBuilderContext getNonIntrinsicAncestor() { 539 return null; 540 } 541 } 542 543 @NodeInfo(cycles = CYCLES_IGNORED, size = SIZE_IGNORED) 544 static class ExceptionPlaceholderNode extends ValueNode { 545 public static final NodeClass<ExceptionPlaceholderNode> TYPE = NodeClass.create(ExceptionPlaceholderNode.class); 546 547 protected ExceptionPlaceholderNode() { 548 super(TYPE, StampFactory.object()); 549 } 550 } 551 552 protected static class SpecialCallTargetCacheKey { 553 private final InvokeKind invokeKind; 554 private final ResolvedJavaMethod targetMethod; 555 private final ResolvedJavaType contextType; 556 private final Stamp receiverStamp; 557 558 public SpecialCallTargetCacheKey(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ResolvedJavaType contextType, Stamp receiverStamp) { 559 this.invokeKind = invokeKind; 560 this.targetMethod = targetMethod; 561 this.contextType = contextType; 562 this.receiverStamp = receiverStamp; 563 } 564 565 @Override 566 public int hashCode() { 567 return invokeKind.hashCode() ^ targetMethod.hashCode() ^ contextType.hashCode() ^ receiverStamp.hashCode(); 568 } 569 570 @Override 571 public boolean equals(Object obj) { 572 if (obj instanceof SpecialCallTargetCacheKey) { 573 SpecialCallTargetCacheKey key = (SpecialCallTargetCacheKey) obj; 574 return key.invokeKind.equals(this.invokeKind) && key.targetMethod.equals(this.targetMethod) && key.contextType.equals(this.contextType) && key.receiverStamp.equals(this.receiverStamp); 575 } 576 return false; 577 } 578 } 579 580 private final LoopExplosionPlugin loopExplosionPlugin; 581 private final InvocationPlugins invocationPlugins; 582 private final InlineInvokePlugin[] inlineInvokePlugins; 583 private final ParameterPlugin parameterPlugin; 584 private final NodePlugin[] nodePlugins; 585 private final EconomicMap<SpecialCallTargetCacheKey, Object> specialCallTargetCache; 586 private final EconomicMap<ResolvedJavaMethod, Object> invocationPluginCache; 587 private final ResolvedJavaMethod callInlinedMethod; 588 protected final SourceLanguagePositionProvider sourceLanguagePositionProvider; 589 590 public PEGraphDecoder(Architecture architecture, StructuredGraph graph, CoreProviders providers, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins, 591 InlineInvokePlugin[] inlineInvokePlugins, 592 ParameterPlugin parameterPlugin, 593 NodePlugin[] nodePlugins, ResolvedJavaMethod callInlinedMethod, SourceLanguagePositionProvider sourceLanguagePositionProvider) { 594 super(architecture, graph, providers, true); 595 this.loopExplosionPlugin = loopExplosionPlugin; 596 this.invocationPlugins = invocationPlugins; 597 this.inlineInvokePlugins = inlineInvokePlugins; 598 this.parameterPlugin = parameterPlugin; 599 this.nodePlugins = nodePlugins; 600 this.specialCallTargetCache = EconomicMap.create(Equivalence.DEFAULT); 601 this.invocationPluginCache = EconomicMap.create(Equivalence.DEFAULT); 602 this.callInlinedMethod = callInlinedMethod; 603 this.sourceLanguagePositionProvider = sourceLanguagePositionProvider; 604 } 605 606 protected static LoopExplosionKind loopExplosionKind(ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin) { 607 if (loopExplosionPlugin == null) { 608 return LoopExplosionKind.NONE; 609 } else { 610 return loopExplosionPlugin.loopExplosionKind(method); 611 } 612 } 613 614 @SuppressWarnings("try") 615 public void decode(ResolvedJavaMethod method, boolean isSubstitution, boolean trackNodeSourcePosition) { 616 try (DebugContext.Scope scope = debug.scope("PEGraphDecode", graph)) { 617 EncodedGraph encodedGraph = lookupEncodedGraph(method, null, null, isSubstitution, trackNodeSourcePosition); 618 PEMethodScope methodScope = new PEMethodScope(graph, null, null, encodedGraph, method, null, 0, loopExplosionPlugin, null); 619 decode(createInitialLoopScope(methodScope, null)); 620 cleanupGraph(methodScope); 621 622 debug.dump(DebugContext.VERBOSE_LEVEL, graph, "After graph cleanup"); 623 assert graph.verify(); 624 } catch (Throwable t) { 625 throw debug.handle(t); 626 } 627 628 try { 629 /* Check that the control flow graph can be computed, to catch problems early. */ 630 assert CFGVerifier.verify(ControlFlowGraph.compute(graph, true, true, true, true)); 631 } catch (Throwable ex) { 632 throw GraalError.shouldNotReachHere("Control flow graph not valid after partial evaluation"); 633 } 634 } 635 636 @Override 637 protected void cleanupGraph(MethodScope methodScope) { 638 super.cleanupGraph(methodScope); 639 640 for (FrameState frameState : graph.getNodes(FrameState.TYPE)) { 641 if (frameState.bci == BytecodeFrame.UNWIND_BCI) { 642 /* 643 * handleMissingAfterExceptionFrameState is called during graph decoding from 644 * InliningUtil.processFrameState - but during graph decoding it does not do 645 * anything because the usages of the frameState are not available yet. So we need 646 * to call it again. 647 */ 648 PEMethodScope peMethodScope = (PEMethodScope) methodScope; 649 Invoke invoke = peMethodScope.invokeData != null ? peMethodScope.invokeData.invoke : null; 650 InliningUtil.handleMissingAfterExceptionFrameState(frameState, invoke, null, true); 651 652 /* 653 * The frameState must be gone now, because it is not a valid deoptimization point. 654 */ 655 assert frameState.isDeleted(); 656 } 657 } 658 } 659 660 @Override 661 protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) { 662 PEMethodScope methodScope = (PEMethodScope) s; 663 664 if (loopScope.loopIteration > Options.MaximumLoopExplosionCount.getValue(options)) { 665 throw tooManyLoopExplosionIterations(methodScope, options); 666 } 667 } 668 669 private static RuntimeException tooManyLoopExplosionIterations(PEMethodScope methodScope, OptionValues options) { 670 String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?"; 671 RuntimeException bailout = Options.FailedLoopExplosionIsFatal.getValue(options) ? new RuntimeException(message) : new PermanentBailoutException(message); 672 throw GraphUtil.createBailoutException(message, bailout, GraphUtil.approxSourceStackTraceElement(methodScope.getCallerBytecodePosition())); 673 } 674 675 @Override 676 protected LoopScope handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) { 677 PEMethodScope methodScope = (PEMethodScope) s; 678 679 /* 680 * Decode the call target, but do not add it to the graph yet. This avoids adding usages for 681 * all the arguments, which are expensive to remove again when we can inline the method. 682 */ 683 assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke"; 684 CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId); 685 if (callTarget instanceof MethodCallTargetNode) { 686 MethodCallTargetNode methodCall = (MethodCallTargetNode) callTarget; 687 if (methodCall.invokeKind().hasReceiver()) { 688 invokeData.constantReceiver = methodCall.arguments().get(0).asJavaConstant(); 689 NodeSourcePosition invokePosition = invokeData.invoke.asNode().getNodeSourcePosition(); 690 if (invokeData.constantReceiver != null && invokePosition != null) { 691 // new NodeSourcePosition(invokeData.constantReceiver, 692 // invokePosition.getCaller(), invokePosition.getMethod(), 693 // invokePosition.getBCI()); 694 } 695 } 696 LoopScope inlineLoopScope = trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget); 697 if (inlineLoopScope != null) { 698 return inlineLoopScope; 699 } 700 } 701 702 /* We know that we need an invoke, so now we can add the call target to the graph. */ 703 graph.add(callTarget); 704 registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false); 705 return super.handleInvoke(methodScope, loopScope, invokeData); 706 } 707 708 protected LoopScope trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { 709 // attempt to devirtualize the call 710 ResolvedJavaMethod specialCallTarget = getSpecialCallTarget(invokeData, callTarget); 711 if (specialCallTarget != null) { 712 callTarget.setTargetMethod(specialCallTarget); 713 callTarget.setInvokeKind(InvokeKind.Special); 714 } 715 716 if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) { 717 /* 718 * The invocation plugin handled the call, so decoding continues in the calling method. 719 */ 720 return loopScope; 721 } 722 LoopScope inlineLoopScope = tryInline(methodScope, loopScope, invokeData, callTarget); 723 if (inlineLoopScope != null) { 724 /* 725 * We can inline the call, so decoding continues in the inlined method. 726 */ 727 return inlineLoopScope; 728 } 729 730 for (InlineInvokePlugin plugin : inlineInvokePlugins) { 731 plugin.notifyNotInlined(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke); 732 } 733 return null; 734 } 735 736 private ResolvedJavaMethod getSpecialCallTarget(InvokeData invokeData, MethodCallTargetNode callTarget) { 737 if (callTarget.invokeKind().isDirect()) { 738 return null; 739 } 740 741 // check for trivial cases (e.g. final methods, nonvirtual methods) 742 if (callTarget.targetMethod().canBeStaticallyBound()) { 743 return callTarget.targetMethod(); 744 } 745 746 SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp(NodeView.DEFAULT)); 747 Object specialCallTarget = specialCallTargetCache.get(key); 748 if (specialCallTarget == null) { 749 specialCallTarget = MethodCallTargetNode.devirtualizeCall(key.invokeKind, key.targetMethod, key.contextType, graph.getAssumptions(), 750 key.receiverStamp); 751 if (specialCallTarget == null) { 752 specialCallTarget = CACHED_NULL_VALUE; 753 } 754 specialCallTargetCache.put(key, specialCallTarget); 755 } 756 757 return specialCallTarget == CACHED_NULL_VALUE ? null : (ResolvedJavaMethod) specialCallTarget; 758 } 759 760 protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { 761 if (invocationPlugins == null || invocationPlugins.isEmpty()) { 762 return false; 763 } 764 765 Invoke invoke = invokeData.invoke; 766 767 ResolvedJavaMethod targetMethod = callTarget.targetMethod(); 768 if (loopScope.methodScope.encodedGraph.isCallToOriginal(targetMethod)) { 769 return false; 770 } 771 772 InvocationPlugin invocationPlugin = getInvocationPlugin(targetMethod); 773 if (invocationPlugin == null) { 774 return false; 775 } 776 777 if (loopScope.methodScope.encodedGraph.isCallToOriginal(targetMethod)) { 778 return false; 779 } 780 781 ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); 782 FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor(); 783 784 /* 785 * Remove invoke from graph so that invocation plugin can append nodes to the predecessor. 786 */ 787 invoke.asNode().replaceAtPredecessor(null); 788 789 PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, loopExplosionPlugin, arguments); 790 791 JavaType returnType = targetMethod.getSignature().getReturnType(methodScope.method.getDeclaringClass()); 792 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor, callTarget.invokeKind(), returnType); 793 InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext); 794 795 if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) { 796 797 if (graphBuilderContext.invokeConsumed) { 798 /* Nothing to do. */ 799 } else if (graphBuilderContext.lastInstr != null) { 800 registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true); 801 invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode); 802 graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(graphBuilderContext.lastInstr))); 803 deleteInvoke(invoke); 804 } else { 805 assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?"; 806 invoke.asNode().replaceAtUsages(null); 807 deleteInvoke(invoke); 808 } 809 return true; 810 811 } else { 812 /* Intrinsification failed, restore original state: invoke is in Graph. */ 813 invokePredecessor.setNext(invoke.asNode()); 814 return false; 815 } 816 } 817 818 private InvocationPlugin getInvocationPlugin(ResolvedJavaMethod targetMethod) { 819 Object invocationPlugin = invocationPluginCache.get(targetMethod); 820 if (invocationPlugin == null) { 821 invocationPlugin = invocationPlugins.lookupInvocation(targetMethod); 822 if (invocationPlugin == null) { 823 invocationPlugin = CACHED_NULL_VALUE; 824 } 825 invocationPluginCache.put(targetMethod, invocationPlugin); 826 } 827 828 return invocationPlugin == CACHED_NULL_VALUE ? null : (InvocationPlugin) invocationPlugin; 829 } 830 831 protected LoopScope tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) { 832 if (!callTarget.invokeKind().isDirect()) { 833 return null; 834 } 835 836 ResolvedJavaMethod targetMethod = callTarget.targetMethod(); 837 if (targetMethod.hasNeverInlineDirective()) { 838 return null; 839 } 840 841 ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]); 842 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke); 843 844 for (InlineInvokePlugin plugin : inlineInvokePlugins) { 845 InlineInfo inlineInfo = plugin.shouldInlineInvoke(graphBuilderContext, targetMethod, arguments); 846 if (inlineInfo != null) { 847 if (inlineInfo.allowsInlining()) { 848 return doInline(methodScope, loopScope, invokeData, inlineInfo, arguments); 849 } else { 850 return null; 851 } 852 } 853 } 854 return null; 855 } 856 857 protected LoopScope doInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, InlineInfo inlineInfo, ValueNode[] arguments) { 858 if (!invokeData.invoke.useForInlining()) { 859 return null; 860 } 861 ResolvedJavaMethod inlineMethod = inlineInfo.getMethodToInline(); 862 EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod, inlineInfo.getPlugin(), inlineInfo.getIntrinsicBytecodeProvider(), inlineInfo.isSubstitution(), graph.trackNodeSourcePosition()); 863 if (graphToInline == null) { 864 return null; 865 } 866 867 assert !graph.trackNodeSourcePosition() || graphToInline.trackNodeSourcePosition() : graph + " " + graphToInline; 868 if (methodScope.inliningDepth > Options.InliningDepthError.getValue(options)) { 869 throw tooDeepInlining(methodScope); 870 } 871 872 for (InlineInvokePlugin plugin : inlineInvokePlugins) { 873 plugin.notifyBeforeInline(inlineMethod); 874 } 875 876 Invoke invoke = invokeData.invoke; 877 FixedNode invokeNode = invoke.asNode(); 878 FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor(); 879 invokeNode.replaceAtPredecessor(null); 880 881 PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1, 882 loopExplosionPlugin, arguments); 883 884 if (!inlineMethod.isStatic()) { 885 if (StampTool.isPointerAlwaysNull(arguments[0])) { 886 /* 887 * The receiver is null, so we can unconditionally throw a NullPointerException 888 * instead of performing any inlining. 889 */ 890 DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException)); 891 predecessor.setNext(deoptimizeNode); 892 finishInlining(inlineScope); 893 /* Continue decoding in the caller. */ 894 return loopScope; 895 896 } else if (!StampTool.isPointerNonNull(arguments[0])) { 897 /* The receiver might be null, so we need to insert a null check. */ 898 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, predecessor); 899 arguments[0] = graphBuilderContext.nullCheckedValue(arguments[0]); 900 predecessor = graphBuilderContext.lastInstr; 901 } 902 } 903 904 LoopScope inlineLoopScope = createInitialLoopScope(inlineScope, predecessor); 905 906 /* 907 * The GraphEncoder assigns parameters a nodeId immediately after the fixed nodes. 908 * Initializing createdNodes here avoid decoding and immediately replacing the 909 * ParameterNodes. 910 */ 911 int firstArgumentNodeId = inlineScope.maxFixedNodeOrderId + 1; 912 for (int i = 0; i < arguments.length; i++) { 913 inlineLoopScope.createdNodes[firstArgumentNodeId + i] = arguments[i]; 914 } 915 916 // Copy assumptions from inlinee to caller 917 Assumptions assumptions = graph.getAssumptions(); 918 Assumptions inlinedAssumptions = graphToInline.getAssumptions(); 919 if (assumptions != null) { 920 if (inlinedAssumptions != null) { 921 assumptions.record(inlinedAssumptions); 922 } 923 } else { 924 assert inlinedAssumptions == null : String.format("cannot inline graph (%s) which makes assumptions into a graph (%s) that doesn't", inlineMethod, graph); 925 } 926 927 // Copy inlined methods from inlinee to caller 928 List<ResolvedJavaMethod> inlinedMethods = graphToInline.getInlinedMethods(); 929 if (inlinedMethods != null) { 930 for (ResolvedJavaMethod other : inlinedMethods) { 931 graph.recordMethod(other); 932 } 933 } 934 935 if (graphToInline.getFields() != null) { 936 for (ResolvedJavaField field : graphToInline.getFields()) { 937 graph.recordField(field); 938 } 939 } 940 if (graphToInline.hasUnsafeAccess()) { 941 graph.markUnsafeAccess(); 942 } 943 944 /* 945 * Do the actual inlining by returning the initial loop scope for the inlined method scope. 946 */ 947 return inlineLoopScope; 948 } 949 950 @Override 951 protected void finishInlining(MethodScope is) { 952 PEMethodScope inlineScope = (PEMethodScope) is; 953 ResolvedJavaMethod inlineMethod = inlineScope.method; 954 PEMethodScope methodScope = inlineScope.caller; 955 LoopScope loopScope = inlineScope.callerLoopScope; 956 InvokeData invokeData = inlineScope.invokeData; 957 Invoke invoke = invokeData.invoke; 958 FixedNode invokeNode = invoke.asNode(); 959 960 ValueNode exceptionValue = null; 961 int returnNodeCount = 0; 962 int unwindNodeCount = 0; 963 List<ControlSinkNode> returnAndUnwindNodes = inlineScope.returnAndUnwindNodes; 964 for (int i = 0; i < returnAndUnwindNodes.size(); i++) { 965 FixedNode fixedNode = returnAndUnwindNodes.get(i); 966 if (fixedNode instanceof ReturnNode) { 967 returnNodeCount++; 968 } else if (fixedNode.isAlive()) { 969 assert fixedNode instanceof UnwindNode; 970 unwindNodeCount++; 971 } 972 } 973 974 if (unwindNodeCount > 0) { 975 FixedNode unwindReplacement; 976 if (invoke instanceof InvokeWithExceptionNode) { 977 /* Decoding continues for the exception handler. */ 978 unwindReplacement = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId); 979 } else { 980 /* No exception handler available, so the only thing we can do is deoptimize. */ 981 unwindReplacement = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler)); 982 } 983 984 if (unwindNodeCount == 1) { 985 /* Only one UnwindNode, we can use the exception directly. */ 986 UnwindNode unwindNode = getSingleMatchingNode(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class); 987 exceptionValue = unwindNode.exception(); 988 unwindNode.replaceAndDelete(unwindReplacement); 989 990 } else { 991 /* 992 * More than one UnwindNode. This can happen with the loop explosion strategy 993 * FULL_EXPLODE_UNTIL_RETURN, where we keep exploding after the loop and therefore 994 * also explode exception paths. Merge the exception in a similar way as multiple 995 * return values. 996 */ 997 MergeNode unwindMergeNode = graph.add(new MergeNode()); 998 exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, getMatchingNodes(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class, unwindNodeCount), 999 null, unwindNode -> unwindNode.exception()); 1000 unwindMergeNode.setNext(unwindReplacement); 1001 1002 ensureExceptionStateDecoded(inlineScope); 1003 unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue)); 1004 } 1005 } 1006 1007 assert invoke.next() == null; 1008 assert !(invoke instanceof InvokeWithExceptionNode) || ((InvokeWithExceptionNode) invoke).exceptionEdge() == null; 1009 1010 ValueNode returnValue; 1011 if (returnNodeCount == 0) { 1012 returnValue = null; 1013 } else if (returnNodeCount == 1) { 1014 ReturnNode returnNode = getSingleMatchingNode(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class); 1015 returnValue = returnNode.result(); 1016 FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode)); 1017 returnNode.replaceAndDelete(n); 1018 } else { 1019 AbstractMergeNode merge = graph.add(new MergeNode()); 1020 merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId)); 1021 returnValue = InliningUtil.mergeReturns(merge, getMatchingNodes(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class, returnNodeCount)); 1022 FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge); 1023 merge.setNext(n); 1024 } 1025 invokeNode.replaceAtUsages(returnValue); 1026 1027 /* 1028 * Usage the handles that we have on the return value and the exception to update the 1029 * orderId->Node table. 1030 */ 1031 registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true); 1032 if (invoke instanceof InvokeWithExceptionNode) { 1033 registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true); 1034 } 1035 if (inlineScope.exceptionPlaceholderNode != null) { 1036 inlineScope.exceptionPlaceholderNode.replaceAtUsagesAndDelete(exceptionValue); 1037 } 1038 deleteInvoke(invoke); 1039 1040 for (InlineInvokePlugin plugin : inlineInvokePlugins) { 1041 plugin.notifyAfterInline(inlineMethod); 1042 } 1043 } 1044 1045 @SuppressWarnings("unchecked") 1046 private static <T> T getSingleMatchingNode(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz) { 1047 if (!hasNonMatchingEntries) { 1048 assert returnAndUnwindNodes.size() == 1; 1049 return (T) returnAndUnwindNodes.get(0); 1050 } 1051 1052 for (int i = 0; i < returnAndUnwindNodes.size(); i++) { 1053 ControlSinkNode node = returnAndUnwindNodes.get(i); 1054 if (clazz.isInstance(node)) { 1055 return (T) node; 1056 } 1057 } 1058 throw GraalError.shouldNotReachHere(); 1059 } 1060 1061 @SuppressWarnings("unchecked") 1062 private static <T> List<T> getMatchingNodes(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz, int resultCount) { 1063 if (!hasNonMatchingEntries) { 1064 return (List<T>) returnAndUnwindNodes; 1065 } 1066 1067 List<T> result = new ArrayList<>(resultCount); 1068 for (int i = 0; i < returnAndUnwindNodes.size(); i++) { 1069 ControlSinkNode node = returnAndUnwindNodes.get(i); 1070 if (clazz.isInstance(node)) { 1071 result.add((T) node); 1072 } 1073 } 1074 assert result.size() == resultCount; 1075 return result; 1076 } 1077 1078 private static RuntimeException tooDeepInlining(PEMethodScope methodScope) { 1079 HashMap<ResolvedJavaMethod, Integer> methodCounts = new HashMap<>(); 1080 for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) { 1081 Integer oldCount = methodCounts.get(cur.method); 1082 methodCounts.put(cur.method, oldCount == null ? 1 : oldCount + 1); 1083 } 1084 1085 List<Map.Entry<ResolvedJavaMethod, Integer>> methods = new ArrayList<>(methodCounts.entrySet()); 1086 methods.sort((e1, e2) -> -Integer.compare(e1.getValue(), e2.getValue())); 1087 1088 StringBuilder msg = new StringBuilder("Too deep inlining, probably caused by recursive inlining.").append(System.lineSeparator()).append("== Inlined methods ordered by inlining frequency:"); 1089 for (Map.Entry<ResolvedJavaMethod, Integer> entry : methods) { 1090 msg.append(System.lineSeparator()).append(entry.getKey().format("%H.%n(%p) [")).append(entry.getValue()).append("]"); 1091 } 1092 msg.append(System.lineSeparator()).append("== Complete stack trace of inlined methods:"); 1093 int lastBci = 0; 1094 for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) { 1095 msg.append(System.lineSeparator()).append(cur.method.asStackTraceElement(lastBci)); 1096 if (cur.invokeData != null) { 1097 lastBci = cur.invokeData.invoke.bci(); 1098 } else { 1099 lastBci = 0; 1100 } 1101 } 1102 1103 throw new PermanentBailoutException(msg.toString()); 1104 } 1105 1106 public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, AbstractBeginNode lastBlock) { 1107 assert lastBlock.isAlive(); 1108 FixedNode n; 1109 if (invokeData.invoke instanceof InvokeWithExceptionNode) { 1110 registerNode(loopScope, invokeData.nextOrderId, lastBlock, false, false); 1111 n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId); 1112 } else { 1113 n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId); 1114 } 1115 return n; 1116 } 1117 1118 private static void deleteInvoke(Invoke invoke) { 1119 /* 1120 * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can 1121 * kill too much: nodes that are decoded later can use values that appear unused by now. 1122 */ 1123 FrameState frameState = invoke.stateAfter(); 1124 invoke.asNode().safeDelete(); 1125 assert invoke.callTarget() == null : "must not have been added to the graph yet"; 1126 if (frameState != null && frameState.hasNoUsages()) { 1127 frameState.safeDelete(); 1128 } 1129 } 1130 1131 protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method, MethodSubstitutionPlugin plugin, BytecodeProvider intrinsicBytecodeProvider, boolean isSubstitution, 1132 boolean trackNodeSourcePosition); 1133 1134 @Override 1135 protected void handleFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) { 1136 PEMethodScope methodScope = (PEMethodScope) s; 1137 1138 if (node instanceof ForeignCallNode) { 1139 ForeignCallNode foreignCall = (ForeignCallNode) node; 1140 if (foreignCall.getBci() == BytecodeFrame.UNKNOWN_BCI && methodScope.invokeData != null) { 1141 foreignCall.setBci(methodScope.invokeData.invoke.bci()); 1142 } 1143 } 1144 1145 super.handleFixedNode(methodScope, loopScope, nodeOrderId, node); 1146 } 1147 1148 @SuppressWarnings("try") 1149 @Override 1150 protected Node canonicalizeFixedNode(MethodScope s, Node node) { 1151 PEMethodScope methodScope = (PEMethodScope) s; 1152 1153 Node replacedNode = node; 1154 if (nodePlugins != null && nodePlugins.length > 0) { 1155 if (node instanceof LoadFieldNode) { 1156 LoadFieldNode loadFieldNode = (LoadFieldNode) node; 1157 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadFieldNode); 1158 ResolvedJavaField field = loadFieldNode.field(); 1159 if (loadFieldNode.isStatic()) { 1160 for (NodePlugin nodePlugin : nodePlugins) { 1161 if (nodePlugin.handleLoadStaticField(graphBuilderContext, field)) { 1162 replacedNode = graphBuilderContext.pushedNode; 1163 break; 1164 } 1165 } 1166 } else { 1167 ValueNode object = loadFieldNode.object(); 1168 for (NodePlugin nodePlugin : nodePlugins) { 1169 if (nodePlugin.handleLoadField(graphBuilderContext, object, field)) { 1170 replacedNode = graphBuilderContext.pushedNode; 1171 break; 1172 } 1173 } 1174 } 1175 } else if (node instanceof StoreFieldNode) { 1176 StoreFieldNode storeFieldNode = (StoreFieldNode) node; 1177 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeFieldNode); 1178 ResolvedJavaField field = storeFieldNode.field(); 1179 if (storeFieldNode.isStatic()) { 1180 ValueNode value = storeFieldNode.value(); 1181 for (NodePlugin nodePlugin : nodePlugins) { 1182 if (nodePlugin.handleStoreStaticField(graphBuilderContext, field, value)) { 1183 replacedNode = graphBuilderContext.pushedNode; 1184 break; 1185 } 1186 } 1187 } else { 1188 ValueNode object = storeFieldNode.object(); 1189 ValueNode value = storeFieldNode.value(); 1190 for (NodePlugin nodePlugin : nodePlugins) { 1191 if (nodePlugin.handleStoreField(graphBuilderContext, object, field, value)) { 1192 replacedNode = graphBuilderContext.pushedNode; 1193 break; 1194 } 1195 } 1196 } 1197 } else if (node instanceof LoadIndexedNode) { 1198 LoadIndexedNode loadIndexedNode = (LoadIndexedNode) node; 1199 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadIndexedNode); 1200 ValueNode array = loadIndexedNode.array(); 1201 ValueNode index = loadIndexedNode.index(); 1202 for (NodePlugin nodePlugin : nodePlugins) { 1203 if (nodePlugin.handleLoadIndexed(graphBuilderContext, array, index, loadIndexedNode.getBoundsCheck(), loadIndexedNode.elementKind())) { 1204 replacedNode = graphBuilderContext.pushedNode; 1205 break; 1206 } 1207 } 1208 } else if (node instanceof StoreIndexedNode) { 1209 StoreIndexedNode storeIndexedNode = (StoreIndexedNode) node; 1210 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeIndexedNode); 1211 ValueNode array = storeIndexedNode.array(); 1212 ValueNode index = storeIndexedNode.index(); 1213 ValueNode value = storeIndexedNode.value(); 1214 for (NodePlugin nodePlugin : nodePlugins) { 1215 if (nodePlugin.handleStoreIndexed(graphBuilderContext, array, index, storeIndexedNode.getBoundsCheck(), storeIndexedNode.getStoreCheck(), storeIndexedNode.elementKind(), value)) { 1216 replacedNode = graphBuilderContext.pushedNode; 1217 break; 1218 } 1219 } 1220 } else if (node instanceof NewInstanceNode) { 1221 NewInstanceNode newInstanceNode = (NewInstanceNode) node; 1222 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newInstanceNode); 1223 ResolvedJavaType type = newInstanceNode.instanceClass(); 1224 for (NodePlugin nodePlugin : nodePlugins) { 1225 if (nodePlugin.handleNewInstance(graphBuilderContext, type)) { 1226 replacedNode = graphBuilderContext.pushedNode; 1227 break; 1228 } 1229 } 1230 } else if (node instanceof NewArrayNode) { 1231 NewArrayNode newArrayNode = (NewArrayNode) node; 1232 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode); 1233 ResolvedJavaType elementType = newArrayNode.elementType(); 1234 ValueNode length = newArrayNode.length(); 1235 for (NodePlugin nodePlugin : nodePlugins) { 1236 if (nodePlugin.handleNewArray(graphBuilderContext, elementType, length)) { 1237 replacedNode = graphBuilderContext.pushedNode; 1238 break; 1239 } 1240 } 1241 } else if (node instanceof NewMultiArrayNode) { 1242 NewMultiArrayNode newArrayNode = (NewMultiArrayNode) node; 1243 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode); 1244 ResolvedJavaType elementType = newArrayNode.type(); 1245 ValueNode[] dimensions = newArrayNode.dimensions().toArray(new ValueNode[0]); 1246 for (NodePlugin nodePlugin : nodePlugins) { 1247 if (nodePlugin.handleNewMultiArray(graphBuilderContext, elementType, dimensions)) { 1248 replacedNode = graphBuilderContext.pushedNode; 1249 break; 1250 } 1251 } 1252 } 1253 } 1254 1255 return super.canonicalizeFixedNode(methodScope, replacedNode); 1256 } 1257 1258 @Override 1259 protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node n) { 1260 PEMethodScope methodScope = (PEMethodScope) s; 1261 1262 Node node = n; 1263 if (node instanceof ParameterNode) { 1264 ParameterNode param = (ParameterNode) node; 1265 if (methodScope.isInlinedMethod()) { 1266 throw GraalError.shouldNotReachHere("Parameter nodes are already registered when the inlined scope is created"); 1267 1268 } else if (parameterPlugin != null) { 1269 assert !methodScope.isInlinedMethod(); 1270 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null); 1271 Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(), 1272 StampPair.create(param.stamp(NodeView.DEFAULT), param.uncheckedStamp())); 1273 if (result != null) { 1274 return result; 1275 } 1276 } 1277 node = param.copyWithInputs(); 1278 } 1279 1280 return super.handleFloatingNodeBeforeAdd(methodScope, loopScope, node); 1281 } 1282 1283 protected void ensureOuterStateDecoded(PEMethodScope methodScope) { 1284 if (methodScope.outerState == null && methodScope.caller != null) { 1285 FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter(); 1286 if (stateAtReturn == null) { 1287 stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId); 1288 } 1289 1290 JavaKind invokeReturnKind = methodScope.invokeData.invoke.asNode().getStackKind(); 1291 FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null); 1292 1293 /* 1294 * When the encoded graph has methods inlining, we can already have a proper caller 1295 * state. If not, we set the caller state here. 1296 */ 1297 if (outerState.outerFrameState() == null && methodScope.caller != null) { 1298 ensureOuterStateDecoded(methodScope.caller); 1299 outerState.setOuterFrameState(methodScope.caller.outerState); 1300 } 1301 methodScope.outerState = outerState; 1302 } 1303 } 1304 1305 protected void ensureStateAfterDecoded(PEMethodScope methodScope) { 1306 if (methodScope.invokeData.invoke.stateAfter() == null) { 1307 methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId)); 1308 } 1309 } 1310 1311 protected void ensureExceptionStateDecoded(PEMethodScope methodScope) { 1312 if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) { 1313 ensureStateAfterDecoded(methodScope); 1314 1315 assert methodScope.exceptionPlaceholderNode == null; 1316 methodScope.exceptionPlaceholderNode = graph.add(new ExceptionPlaceholderNode()); 1317 registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false); 1318 FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId); 1319 1320 if (exceptionState.outerFrameState() == null && methodScope.caller != null) { 1321 ensureOuterStateDecoded(methodScope.caller); 1322 exceptionState.setOuterFrameState(methodScope.caller.outerState); 1323 } 1324 methodScope.exceptionState = exceptionState; 1325 } 1326 } 1327 1328 @Override 1329 protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) { 1330 PEMethodScope methodScope = (PEMethodScope) s; 1331 1332 if (methodScope.isInlinedMethod()) { 1333 if (node instanceof FrameState) { 1334 FrameState frameState = (FrameState) node; 1335 1336 ensureOuterStateDecoded(methodScope); 1337 if (frameState.bci < 0) { 1338 ensureExceptionStateDecoded(methodScope); 1339 } 1340 List<ValueNode> invokeArgsList = null; 1341 if (frameState.bci == BytecodeFrame.BEFORE_BCI) { 1342 /* 1343 * We know that the argument list is only used in this case, so avoid the List 1344 * allocation for "normal" bcis. 1345 */ 1346 invokeArgsList = Arrays.asList(methodScope.arguments); 1347 } 1348 return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, null, methodScope.method, methodScope.exceptionState, methodScope.outerState, true, 1349 methodScope.method, invokeArgsList); 1350 1351 } else if (node instanceof MonitorIdNode) { 1352 ensureOuterStateDecoded(methodScope); 1353 InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node); 1354 return node; 1355 } 1356 } 1357 1358 return node; 1359 } 1360 }