1 /*
   2  * Copyright (c) 2015, 2019, 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) || scope.method.equals(callInlinedAgnosticMethod)) {
 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.isGeneratedFromFoldOrNodeIntrinsic();
 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     private final ResolvedJavaMethod callInlinedAgnosticMethod;
 589     protected final SourceLanguagePositionProvider sourceLanguagePositionProvider;
 590 
 591     public PEGraphDecoder(Architecture architecture, StructuredGraph graph, CoreProviders providers, LoopExplosionPlugin loopExplosionPlugin, InvocationPlugins invocationPlugins,
 592                     InlineInvokePlugin[] inlineInvokePlugins,
 593                     ParameterPlugin parameterPlugin,
 594                     NodePlugin[] nodePlugins, ResolvedJavaMethod callInlinedMethod, ResolvedJavaMethod callInlinedAgnosticMethod, SourceLanguagePositionProvider sourceLanguagePositionProvider) {
 595         super(architecture, graph, providers, true);
 596         this.loopExplosionPlugin = loopExplosionPlugin;
 597         this.invocationPlugins = invocationPlugins;
 598         this.inlineInvokePlugins = inlineInvokePlugins;
 599         this.parameterPlugin = parameterPlugin;
 600         this.nodePlugins = nodePlugins;
 601         this.callInlinedAgnosticMethod = callInlinedAgnosticMethod;
 602         this.specialCallTargetCache = EconomicMap.create(Equivalence.DEFAULT);
 603         this.invocationPluginCache = EconomicMap.create(Equivalence.DEFAULT);
 604         this.callInlinedMethod = callInlinedMethod;
 605         this.sourceLanguagePositionProvider = sourceLanguagePositionProvider;
 606     }
 607 
 608     protected static LoopExplosionKind loopExplosionKind(ResolvedJavaMethod method, LoopExplosionPlugin loopExplosionPlugin) {
 609         if (loopExplosionPlugin == null) {
 610             return LoopExplosionKind.NONE;
 611         } else {
 612             return loopExplosionPlugin.loopExplosionKind(method);
 613         }
 614     }
 615 
 616     @SuppressWarnings("try")
 617     public void decode(ResolvedJavaMethod method, boolean isSubstitution, boolean trackNodeSourcePosition) {
 618         try (DebugContext.Scope scope = debug.scope("PEGraphDecode", graph)) {
 619             EncodedGraph encodedGraph = lookupEncodedGraph(method, null, null, isSubstitution, trackNodeSourcePosition);
 620             PEMethodScope methodScope = new PEMethodScope(graph, null, null, encodedGraph, method, null, 0, loopExplosionPlugin, null);
 621             decode(createInitialLoopScope(methodScope, null));
 622             cleanupGraph(methodScope);
 623 
 624             debug.dump(DebugContext.VERBOSE_LEVEL, graph, "After graph cleanup");
 625             assert graph.verify();
 626         } catch (Throwable t) {
 627             throw debug.handle(t);
 628         }
 629 
 630         try {
 631             /* Check that the control flow graph can be computed, to catch problems early. */
 632             assert CFGVerifier.verify(ControlFlowGraph.compute(graph, true, true, true, true));
 633         } catch (Throwable ex) {
 634             throw GraalError.shouldNotReachHere("Control flow graph not valid after partial evaluation");
 635         }
 636     }
 637 
 638     @Override
 639     protected void cleanupGraph(MethodScope methodScope) {
 640         super.cleanupGraph(methodScope);
 641 
 642         for (FrameState frameState : graph.getNodes(FrameState.TYPE)) {
 643             if (frameState.bci == BytecodeFrame.UNWIND_BCI) {
 644                 /*
 645                  * handleMissingAfterExceptionFrameState is called during graph decoding from
 646                  * InliningUtil.processFrameState - but during graph decoding it does not do
 647                  * anything because the usages of the frameState are not available yet. So we need
 648                  * to call it again.
 649                  */
 650                 PEMethodScope peMethodScope = (PEMethodScope) methodScope;
 651                 Invoke invoke = peMethodScope.invokeData != null ? peMethodScope.invokeData.invoke : null;
 652                 InliningUtil.handleMissingAfterExceptionFrameState(frameState, invoke, null, true);
 653 
 654                 /*
 655                  * The frameState must be gone now, because it is not a valid deoptimization point.
 656                  */
 657                 assert frameState.isDeleted();
 658             }
 659         }
 660     }
 661 
 662     @Override
 663     protected void checkLoopExplosionIteration(MethodScope s, LoopScope loopScope) {
 664         PEMethodScope methodScope = (PEMethodScope) s;
 665 
 666         if (loopScope.loopIteration > Options.MaximumLoopExplosionCount.getValue(options)) {
 667             throw tooManyLoopExplosionIterations(methodScope, options);
 668         }
 669     }
 670 
 671     private static RuntimeException tooManyLoopExplosionIterations(PEMethodScope methodScope, OptionValues options) {
 672         String message = "too many loop explosion iterations - does the explosion not terminate for method " + methodScope.method + "?";
 673         RuntimeException bailout = Options.FailedLoopExplosionIsFatal.getValue(options) ? new RuntimeException(message) : new PermanentBailoutException(message);
 674         throw GraphUtil.createBailoutException(message, bailout, GraphUtil.approxSourceStackTraceElement(methodScope.getCallerBytecodePosition()));
 675     }
 676 
 677     @Override
 678     protected LoopScope handleInvoke(MethodScope s, LoopScope loopScope, InvokeData invokeData) {
 679         PEMethodScope methodScope = (PEMethodScope) s;
 680 
 681         /*
 682          * Decode the call target, but do not add it to the graph yet. This avoids adding usages for
 683          * all the arguments, which are expensive to remove again when we can inline the method.
 684          */
 685         assert invokeData.invoke.callTarget() == null : "callTarget edge is ignored during decoding of Invoke";
 686         CallTargetNode callTarget = (CallTargetNode) decodeFloatingNode(methodScope, loopScope, invokeData.callTargetOrderId);
 687         if (callTarget instanceof MethodCallTargetNode) {
 688             MethodCallTargetNode methodCall = (MethodCallTargetNode) callTarget;
 689             if (methodCall.invokeKind().hasReceiver()) {
 690                 invokeData.constantReceiver = methodCall.arguments().get(0).asJavaConstant();
 691                 NodeSourcePosition invokePosition = invokeData.invoke.asNode().getNodeSourcePosition();
 692                 if (invokeData.constantReceiver != null && invokePosition != null) {
 693                     // new NodeSourcePosition(invokeData.constantReceiver,
 694                     // invokePosition.getCaller(), invokePosition.getMethod(),
 695                     // invokePosition.getBCI());
 696                 }
 697             }
 698             LoopScope inlineLoopScope = trySimplifyInvoke(methodScope, loopScope, invokeData, (MethodCallTargetNode) callTarget);
 699             if (inlineLoopScope != null) {
 700                 return inlineLoopScope;
 701             }
 702         }
 703 
 704         /* We know that we need an invoke, so now we can add the call target to the graph. */
 705         graph.add(callTarget);
 706         registerNode(loopScope, invokeData.callTargetOrderId, callTarget, false, false);
 707         return super.handleInvoke(methodScope, loopScope, invokeData);
 708     }
 709 
 710     protected LoopScope trySimplifyInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 711         // attempt to devirtualize the call
 712         ResolvedJavaMethod specialCallTarget = getSpecialCallTarget(invokeData, callTarget);
 713         if (specialCallTarget != null) {
 714             callTarget.setTargetMethod(specialCallTarget);
 715             callTarget.setInvokeKind(InvokeKind.Special);
 716         }
 717 
 718         if (tryInvocationPlugin(methodScope, loopScope, invokeData, callTarget)) {
 719             /*
 720              * The invocation plugin handled the call, so decoding continues in the calling method.
 721              */
 722             return loopScope;
 723         }
 724         LoopScope inlineLoopScope = tryInline(methodScope, loopScope, invokeData, callTarget);
 725         if (inlineLoopScope != null) {
 726             /*
 727              * We can inline the call, so decoding continues in the inlined method.
 728              */
 729             return inlineLoopScope;
 730         }
 731 
 732         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 733             plugin.notifyNotInlined(new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke), callTarget.targetMethod(), invokeData.invoke);
 734         }
 735         return null;
 736     }
 737 
 738     private ResolvedJavaMethod getSpecialCallTarget(InvokeData invokeData, MethodCallTargetNode callTarget) {
 739         if (callTarget.invokeKind().isDirect()) {
 740             return null;
 741         }
 742 
 743         // check for trivial cases (e.g. final methods, nonvirtual methods)
 744         if (callTarget.targetMethod().canBeStaticallyBound()) {
 745             return callTarget.targetMethod();
 746         }
 747 
 748         SpecialCallTargetCacheKey key = new SpecialCallTargetCacheKey(callTarget.invokeKind(), callTarget.targetMethod(), invokeData.contextType, callTarget.receiver().stamp(NodeView.DEFAULT));
 749         Object specialCallTarget = specialCallTargetCache.get(key);
 750         if (specialCallTarget == null) {
 751             specialCallTarget = MethodCallTargetNode.devirtualizeCall(key.invokeKind, key.targetMethod, key.contextType, graph.getAssumptions(),
 752                             key.receiverStamp);
 753             if (specialCallTarget == null) {
 754                 specialCallTarget = CACHED_NULL_VALUE;
 755             }
 756             specialCallTargetCache.put(key, specialCallTarget);
 757         }
 758 
 759         return specialCallTarget == CACHED_NULL_VALUE ? null : (ResolvedJavaMethod) specialCallTarget;
 760     }
 761 
 762     protected boolean tryInvocationPlugin(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 763         if (invocationPlugins == null || invocationPlugins.isEmpty()) {
 764             return false;
 765         }
 766 
 767         Invoke invoke = invokeData.invoke;
 768 
 769         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
 770         if (loopScope.methodScope.encodedGraph.isCallToOriginal(targetMethod)) {
 771             return false;
 772         }
 773 
 774         InvocationPlugin invocationPlugin = getInvocationPlugin(targetMethod);
 775         if (invocationPlugin == null) {
 776             return false;
 777         }
 778 
 779         if (loopScope.methodScope.encodedGraph.isCallToOriginal(targetMethod)) {
 780             return false;
 781         }
 782 
 783         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
 784         FixedWithNextNode invokePredecessor = (FixedWithNextNode) invoke.asNode().predecessor();
 785 
 786         /*
 787          * Remove invoke from graph so that invocation plugin can append nodes to the predecessor.
 788          */
 789         invoke.asNode().replaceAtPredecessor(null);
 790 
 791         PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, null, targetMethod, invokeData, methodScope.inliningDepth + 1, loopExplosionPlugin, arguments);
 792 
 793         JavaType returnType = targetMethod.getSignature().getReturnType(methodScope.method.getDeclaringClass());
 794         PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, invokePredecessor, callTarget.invokeKind(), returnType);
 795         InvocationPluginReceiver invocationPluginReceiver = new InvocationPluginReceiver(graphBuilderContext);
 796 
 797         if (invocationPlugin.execute(graphBuilderContext, targetMethod, invocationPluginReceiver.init(targetMethod, arguments), arguments)) {
 798 
 799             if (graphBuilderContext.invokeConsumed) {
 800                 /* Nothing to do. */
 801             } else if (graphBuilderContext.lastInstr != null) {
 802                 registerNode(loopScope, invokeData.invokeOrderId, graphBuilderContext.pushedNode, true, true);
 803                 invoke.asNode().replaceAtUsages(graphBuilderContext.pushedNode);
 804                 graphBuilderContext.lastInstr.setNext(nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(graphBuilderContext.lastInstr)));
 805                 deleteInvoke(invoke);
 806             } else {
 807                 assert graphBuilderContext.pushedNode == null : "Why push a node when the invoke does not return anyway?";
 808                 invoke.asNode().replaceAtUsages(null);
 809                 deleteInvoke(invoke);
 810             }
 811             return true;
 812 
 813         } else {
 814             /* Intrinsification failed, restore original state: invoke is in Graph. */
 815             invokePredecessor.setNext(invoke.asNode());
 816             return false;
 817         }
 818     }
 819 
 820     private InvocationPlugin getInvocationPlugin(ResolvedJavaMethod targetMethod) {
 821         Object invocationPlugin = invocationPluginCache.get(targetMethod);
 822         if (invocationPlugin == null) {
 823             invocationPlugin = invocationPlugins.lookupInvocation(targetMethod);
 824             if (invocationPlugin == null) {
 825                 invocationPlugin = CACHED_NULL_VALUE;
 826             }
 827             invocationPluginCache.put(targetMethod, invocationPlugin);
 828         }
 829 
 830         return invocationPlugin == CACHED_NULL_VALUE ? null : (InvocationPlugin) invocationPlugin;
 831     }
 832 
 833     protected LoopScope tryInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, MethodCallTargetNode callTarget) {
 834         if (!callTarget.invokeKind().isDirect()) {
 835             return null;
 836         }
 837 
 838         ResolvedJavaMethod targetMethod = callTarget.targetMethod();
 839         if (targetMethod.hasNeverInlineDirective()) {
 840             return null;
 841         }
 842 
 843         ValueNode[] arguments = callTarget.arguments().toArray(new ValueNode[0]);
 844         GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, invokeData.invoke);
 845 
 846         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 847             InlineInfo inlineInfo = plugin.shouldInlineInvoke(graphBuilderContext, targetMethod, arguments);
 848             if (inlineInfo != null) {
 849                 if (inlineInfo.allowsInlining()) {
 850                     return doInline(methodScope, loopScope, invokeData, inlineInfo, arguments);
 851                 } else {
 852                     return null;
 853                 }
 854             }
 855         }
 856         return null;
 857     }
 858 
 859     protected LoopScope doInline(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, InlineInfo inlineInfo, ValueNode[] arguments) {
 860         if (!invokeData.invoke.useForInlining()) {
 861             return null;
 862         }
 863         ResolvedJavaMethod inlineMethod = inlineInfo.getMethodToInline();
 864         EncodedGraph graphToInline = lookupEncodedGraph(inlineMethod, inlineInfo.getPlugin(), inlineInfo.getIntrinsicBytecodeProvider(), inlineInfo.isSubstitution(), graph.trackNodeSourcePosition());
 865         if (graphToInline == null) {
 866             return null;
 867         }
 868 
 869         assert !graph.trackNodeSourcePosition() || graphToInline.trackNodeSourcePosition() : graph + " " + graphToInline;
 870         if (methodScope.inliningDepth > Options.InliningDepthError.getValue(options)) {
 871             throw tooDeepInlining(methodScope);
 872         }
 873 
 874         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
 875             plugin.notifyBeforeInline(inlineMethod);
 876         }
 877 
 878         Invoke invoke = invokeData.invoke;
 879         FixedNode invokeNode = invoke.asNode();
 880         FixedWithNextNode predecessor = (FixedWithNextNode) invokeNode.predecessor();
 881         invokeNode.replaceAtPredecessor(null);
 882 
 883         PEMethodScope inlineScope = new PEMethodScope(graph, methodScope, loopScope, graphToInline, inlineMethod, invokeData, methodScope.inliningDepth + 1,
 884                         loopExplosionPlugin, arguments);
 885 
 886         if (!inlineMethod.isStatic()) {
 887             if (StampTool.isPointerAlwaysNull(arguments[0])) {
 888                 /*
 889                  * The receiver is null, so we can unconditionally throw a NullPointerException
 890                  * instead of performing any inlining.
 891                  */
 892                 DeoptimizeNode deoptimizeNode = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateReprofile, DeoptimizationReason.NullCheckException));
 893                 predecessor.setNext(deoptimizeNode);
 894                 finishInlining(inlineScope);
 895                 /* Continue decoding in the caller. */
 896                 return loopScope;
 897 
 898             } else if (!StampTool.isPointerNonNull(arguments[0])) {
 899                 /* The receiver might be null, so we need to insert a null check. */
 900                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(inlineScope, predecessor);
 901                 arguments[0] = graphBuilderContext.nullCheckedValue(arguments[0]);
 902                 predecessor = graphBuilderContext.lastInstr;
 903             }
 904         }
 905 
 906         LoopScope inlineLoopScope = createInitialLoopScope(inlineScope, predecessor);
 907 
 908         /*
 909          * The GraphEncoder assigns parameters a nodeId immediately after the fixed nodes.
 910          * Initializing createdNodes here avoid decoding and immediately replacing the
 911          * ParameterNodes.
 912          */
 913         int firstArgumentNodeId = inlineScope.maxFixedNodeOrderId + 1;
 914         for (int i = 0; i < arguments.length; i++) {
 915             inlineLoopScope.createdNodes[firstArgumentNodeId + i] = arguments[i];
 916         }
 917 
 918         // Copy assumptions from inlinee to caller
 919         Assumptions assumptions = graph.getAssumptions();
 920         Assumptions inlinedAssumptions = graphToInline.getAssumptions();
 921         if (assumptions != null) {
 922             if (inlinedAssumptions != null) {
 923                 assumptions.record(inlinedAssumptions);
 924             }
 925         } else {
 926             assert inlinedAssumptions == null : String.format("cannot inline graph (%s) which makes assumptions into a graph (%s) that doesn't", inlineMethod, graph);
 927         }
 928 
 929         // Copy inlined methods from inlinee to caller
 930         List<ResolvedJavaMethod> inlinedMethods = graphToInline.getInlinedMethods();
 931         if (inlinedMethods != null) {
 932             for (ResolvedJavaMethod other : inlinedMethods) {
 933                 graph.recordMethod(other);
 934             }
 935         }
 936 
 937         if (graphToInline.getFields() != null) {
 938             for (ResolvedJavaField field : graphToInline.getFields()) {
 939                 graph.recordField(field);
 940             }
 941         }
 942         if (graphToInline.hasUnsafeAccess()) {
 943             graph.markUnsafeAccess();
 944         }
 945 
 946         /*
 947          * Do the actual inlining by returning the initial loop scope for the inlined method scope.
 948          */
 949         return inlineLoopScope;
 950     }
 951 
 952     @Override
 953     protected void finishInlining(MethodScope is) {
 954         PEMethodScope inlineScope = (PEMethodScope) is;
 955         ResolvedJavaMethod inlineMethod = inlineScope.method;
 956         PEMethodScope methodScope = inlineScope.caller;
 957         LoopScope loopScope = inlineScope.callerLoopScope;
 958         InvokeData invokeData = inlineScope.invokeData;
 959         Invoke invoke = invokeData.invoke;
 960         FixedNode invokeNode = invoke.asNode();
 961 
 962         ValueNode exceptionValue = null;
 963         int returnNodeCount = 0;
 964         int unwindNodeCount = 0;
 965         List<ControlSinkNode> returnAndUnwindNodes = inlineScope.returnAndUnwindNodes;
 966         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
 967             FixedNode fixedNode = returnAndUnwindNodes.get(i);
 968             if (fixedNode instanceof ReturnNode) {
 969                 returnNodeCount++;
 970             } else if (fixedNode.isAlive()) {
 971                 assert fixedNode instanceof UnwindNode;
 972                 unwindNodeCount++;
 973             }
 974         }
 975 
 976         if (unwindNodeCount > 0) {
 977             FixedNode unwindReplacement;
 978             if (invoke instanceof InvokeWithExceptionNode) {
 979                 /* Decoding continues for the exception handler. */
 980                 unwindReplacement = makeStubNode(methodScope, loopScope, invokeData.exceptionNextOrderId);
 981             } else {
 982                 /* No exception handler available, so the only thing we can do is deoptimize. */
 983                 unwindReplacement = graph.add(new DeoptimizeNode(DeoptimizationAction.InvalidateRecompile, DeoptimizationReason.NotCompiledExceptionHandler));
 984             }
 985 
 986             if (unwindNodeCount == 1) {
 987                 /* Only one UnwindNode, we can use the exception directly. */
 988                 UnwindNode unwindNode = getSingleMatchingNode(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class);
 989                 exceptionValue = unwindNode.exception();
 990                 unwindNode.replaceAndDelete(unwindReplacement);
 991 
 992             } else {
 993                 /*
 994                  * More than one UnwindNode. This can happen with the loop explosion strategy
 995                  * FULL_EXPLODE_UNTIL_RETURN, where we keep exploding after the loop and therefore
 996                  * also explode exception paths. Merge the exception in a similar way as multiple
 997                  * return values.
 998                  */
 999                 MergeNode unwindMergeNode = graph.add(new MergeNode());
1000                 exceptionValue = InliningUtil.mergeValueProducers(unwindMergeNode, getMatchingNodes(returnAndUnwindNodes, returnNodeCount > 0, UnwindNode.class, unwindNodeCount),
1001                                 null, unwindNode -> unwindNode.exception());
1002                 unwindMergeNode.setNext(unwindReplacement);
1003 
1004                 ensureExceptionStateDecoded(inlineScope);
1005                 unwindMergeNode.setStateAfter(inlineScope.exceptionState.duplicateModified(JavaKind.Object, JavaKind.Object, exceptionValue));
1006             }
1007         }
1008 
1009         assert invoke.next() == null;
1010         assert !(invoke instanceof InvokeWithExceptionNode) || ((InvokeWithExceptionNode) invoke).exceptionEdge() == null;
1011 
1012         ValueNode returnValue;
1013         if (returnNodeCount == 0) {
1014             returnValue = null;
1015         } else if (returnNodeCount == 1) {
1016             ReturnNode returnNode = getSingleMatchingNode(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class);
1017             returnValue = returnNode.result();
1018             FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, AbstractBeginNode.prevBegin(returnNode));
1019             returnNode.replaceAndDelete(n);
1020         } else {
1021             AbstractMergeNode merge = graph.add(new MergeNode());
1022             merge.setStateAfter((FrameState) ensureNodeCreated(methodScope, loopScope, invokeData.stateAfterOrderId));
1023             returnValue = InliningUtil.mergeReturns(merge, getMatchingNodes(returnAndUnwindNodes, unwindNodeCount > 0, ReturnNode.class, returnNodeCount));
1024             FixedNode n = nodeAfterInvoke(methodScope, loopScope, invokeData, merge);
1025             merge.setNext(n);
1026         }
1027         invokeNode.replaceAtUsages(returnValue);
1028 
1029         /*
1030          * Usage the handles that we have on the return value and the exception to update the
1031          * orderId->Node table.
1032          */
1033         registerNode(loopScope, invokeData.invokeOrderId, returnValue, true, true);
1034         if (invoke instanceof InvokeWithExceptionNode) {
1035             registerNode(loopScope, invokeData.exceptionOrderId, exceptionValue, true, true);
1036         }
1037         if (inlineScope.exceptionPlaceholderNode != null) {
1038             inlineScope.exceptionPlaceholderNode.replaceAtUsagesAndDelete(exceptionValue);
1039         }
1040         deleteInvoke(invoke);
1041 
1042         for (InlineInvokePlugin plugin : inlineInvokePlugins) {
1043             plugin.notifyAfterInline(inlineMethod);
1044         }
1045     }
1046 
1047     @SuppressWarnings("unchecked")
1048     private static <T> T getSingleMatchingNode(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz) {
1049         if (!hasNonMatchingEntries) {
1050             assert returnAndUnwindNodes.size() == 1;
1051             return (T) returnAndUnwindNodes.get(0);
1052         }
1053 
1054         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
1055             ControlSinkNode node = returnAndUnwindNodes.get(i);
1056             if (clazz.isInstance(node)) {
1057                 return (T) node;
1058             }
1059         }
1060         throw GraalError.shouldNotReachHere();
1061     }
1062 
1063     @SuppressWarnings("unchecked")
1064     private static <T> List<T> getMatchingNodes(List<ControlSinkNode> returnAndUnwindNodes, boolean hasNonMatchingEntries, Class<T> clazz, int resultCount) {
1065         if (!hasNonMatchingEntries) {
1066             return (List<T>) returnAndUnwindNodes;
1067         }
1068 
1069         List<T> result = new ArrayList<>(resultCount);
1070         for (int i = 0; i < returnAndUnwindNodes.size(); i++) {
1071             ControlSinkNode node = returnAndUnwindNodes.get(i);
1072             if (clazz.isInstance(node)) {
1073                 result.add((T) node);
1074             }
1075         }
1076         assert result.size() == resultCount;
1077         return result;
1078     }
1079 
1080     private static RuntimeException tooDeepInlining(PEMethodScope methodScope) {
1081         HashMap<ResolvedJavaMethod, Integer> methodCounts = new HashMap<>();
1082         for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
1083             Integer oldCount = methodCounts.get(cur.method);
1084             methodCounts.put(cur.method, oldCount == null ? 1 : oldCount + 1);
1085         }
1086 
1087         List<Map.Entry<ResolvedJavaMethod, Integer>> methods = new ArrayList<>(methodCounts.entrySet());
1088         methods.sort((e1, e2) -> -Integer.compare(e1.getValue(), e2.getValue()));
1089 
1090         StringBuilder msg = new StringBuilder("Too deep inlining, probably caused by recursive inlining.").append(System.lineSeparator()).append("== Inlined methods ordered by inlining frequency:");
1091         for (Map.Entry<ResolvedJavaMethod, Integer> entry : methods) {
1092             msg.append(System.lineSeparator()).append(entry.getKey().format("%H.%n(%p) [")).append(entry.getValue()).append("]");
1093         }
1094         msg.append(System.lineSeparator()).append("== Complete stack trace of inlined methods:");
1095         int lastBci = 0;
1096         for (PEMethodScope cur = methodScope; cur != null; cur = cur.caller) {
1097             msg.append(System.lineSeparator()).append(cur.method.asStackTraceElement(lastBci));
1098             if (cur.invokeData != null) {
1099                 lastBci = cur.invokeData.invoke.bci();
1100             } else {
1101                 lastBci = 0;
1102             }
1103         }
1104 
1105         throw new PermanentBailoutException(msg.toString());
1106     }
1107 
1108     public FixedNode nodeAfterInvoke(PEMethodScope methodScope, LoopScope loopScope, InvokeData invokeData, AbstractBeginNode lastBlock) {
1109         assert lastBlock.isAlive();
1110         FixedNode n;
1111         if (invokeData.invoke instanceof InvokeWithExceptionNode) {
1112             registerNode(loopScope, invokeData.nextOrderId, lastBlock, false, false);
1113             n = makeStubNode(methodScope, loopScope, invokeData.nextNextOrderId);
1114         } else {
1115             n = makeStubNode(methodScope, loopScope, invokeData.nextOrderId);
1116         }
1117         return n;
1118     }
1119 
1120     private static void deleteInvoke(Invoke invoke) {
1121         /*
1122          * Clean up unused nodes. We cannot just call killCFG on the invoke node because that can
1123          * kill too much: nodes that are decoded later can use values that appear unused by now.
1124          */
1125         FrameState frameState = invoke.stateAfter();
1126         invoke.asNode().safeDelete();
1127         assert invoke.callTarget() == null : "must not have been added to the graph yet";
1128         if (frameState != null && frameState.hasNoUsages()) {
1129             frameState.safeDelete();
1130         }
1131     }
1132 
1133     protected abstract EncodedGraph lookupEncodedGraph(ResolvedJavaMethod method, MethodSubstitutionPlugin plugin, BytecodeProvider intrinsicBytecodeProvider, boolean isSubstitution,
1134                     boolean trackNodeSourcePosition);
1135 
1136     @Override
1137     protected void handleFixedNode(MethodScope s, LoopScope loopScope, int nodeOrderId, FixedNode node) {
1138         PEMethodScope methodScope = (PEMethodScope) s;
1139 
1140         if (node instanceof ForeignCallNode) {
1141             ForeignCallNode foreignCall = (ForeignCallNode) node;
1142             if (foreignCall.getBci() == BytecodeFrame.UNKNOWN_BCI && methodScope.invokeData != null) {
1143                 foreignCall.setBci(methodScope.invokeData.invoke.bci());
1144             }
1145         }
1146 
1147         super.handleFixedNode(methodScope, loopScope, nodeOrderId, node);
1148     }
1149 
1150     @SuppressWarnings("try")
1151     @Override
1152     protected Node canonicalizeFixedNode(MethodScope s, Node node) {
1153         PEMethodScope methodScope = (PEMethodScope) s;
1154 
1155         Node replacedNode = node;
1156         if (nodePlugins != null && nodePlugins.length > 0) {
1157             if (node instanceof LoadFieldNode) {
1158                 LoadFieldNode loadFieldNode = (LoadFieldNode) node;
1159                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadFieldNode);
1160                 ResolvedJavaField field = loadFieldNode.field();
1161                 if (loadFieldNode.isStatic()) {
1162                     for (NodePlugin nodePlugin : nodePlugins) {
1163                         if (nodePlugin.handleLoadStaticField(graphBuilderContext, field)) {
1164                             replacedNode = graphBuilderContext.pushedNode;
1165                             break;
1166                         }
1167                     }
1168                 } else {
1169                     ValueNode object = loadFieldNode.object();
1170                     for (NodePlugin nodePlugin : nodePlugins) {
1171                         if (nodePlugin.handleLoadField(graphBuilderContext, object, field)) {
1172                             replacedNode = graphBuilderContext.pushedNode;
1173                             break;
1174                         }
1175                     }
1176                 }
1177             } else if (node instanceof StoreFieldNode) {
1178                 StoreFieldNode storeFieldNode = (StoreFieldNode) node;
1179                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeFieldNode);
1180                 ResolvedJavaField field = storeFieldNode.field();
1181                 if (storeFieldNode.isStatic()) {
1182                     ValueNode value = storeFieldNode.value();
1183                     for (NodePlugin nodePlugin : nodePlugins) {
1184                         if (nodePlugin.handleStoreStaticField(graphBuilderContext, field, value)) {
1185                             replacedNode = graphBuilderContext.pushedNode;
1186                             break;
1187                         }
1188                     }
1189                 } else {
1190                     ValueNode object = storeFieldNode.object();
1191                     ValueNode value = storeFieldNode.value();
1192                     for (NodePlugin nodePlugin : nodePlugins) {
1193                         if (nodePlugin.handleStoreField(graphBuilderContext, object, field, value)) {
1194                             replacedNode = graphBuilderContext.pushedNode;
1195                             break;
1196                         }
1197                     }
1198                 }
1199             } else if (node instanceof LoadIndexedNode) {
1200                 LoadIndexedNode loadIndexedNode = (LoadIndexedNode) node;
1201                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, loadIndexedNode);
1202                 ValueNode array = loadIndexedNode.array();
1203                 ValueNode index = loadIndexedNode.index();
1204                 for (NodePlugin nodePlugin : nodePlugins) {
1205                     if (nodePlugin.handleLoadIndexed(graphBuilderContext, array, index, loadIndexedNode.getBoundsCheck(), loadIndexedNode.elementKind())) {
1206                         replacedNode = graphBuilderContext.pushedNode;
1207                         break;
1208                     }
1209                 }
1210             } else if (node instanceof StoreIndexedNode) {
1211                 StoreIndexedNode storeIndexedNode = (StoreIndexedNode) node;
1212                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, storeIndexedNode);
1213                 ValueNode array = storeIndexedNode.array();
1214                 ValueNode index = storeIndexedNode.index();
1215                 ValueNode value = storeIndexedNode.value();
1216                 for (NodePlugin nodePlugin : nodePlugins) {
1217                     if (nodePlugin.handleStoreIndexed(graphBuilderContext, array, index, storeIndexedNode.getBoundsCheck(), storeIndexedNode.getStoreCheck(), storeIndexedNode.elementKind(), value)) {
1218                         replacedNode = graphBuilderContext.pushedNode;
1219                         break;
1220                     }
1221                 }
1222             } else if (node instanceof NewInstanceNode) {
1223                 NewInstanceNode newInstanceNode = (NewInstanceNode) node;
1224                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newInstanceNode);
1225                 ResolvedJavaType type = newInstanceNode.instanceClass();
1226                 for (NodePlugin nodePlugin : nodePlugins) {
1227                     if (nodePlugin.handleNewInstance(graphBuilderContext, type)) {
1228                         replacedNode = graphBuilderContext.pushedNode;
1229                         break;
1230                     }
1231                 }
1232             } else if (node instanceof NewArrayNode) {
1233                 NewArrayNode newArrayNode = (NewArrayNode) node;
1234                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode);
1235                 ResolvedJavaType elementType = newArrayNode.elementType();
1236                 ValueNode length = newArrayNode.length();
1237                 for (NodePlugin nodePlugin : nodePlugins) {
1238                     if (nodePlugin.handleNewArray(graphBuilderContext, elementType, length)) {
1239                         replacedNode = graphBuilderContext.pushedNode;
1240                         break;
1241                     }
1242                 }
1243             } else if (node instanceof NewMultiArrayNode) {
1244                 NewMultiArrayNode newArrayNode = (NewMultiArrayNode) node;
1245                 PEAppendGraphBuilderContext graphBuilderContext = new PEAppendGraphBuilderContext(methodScope, newArrayNode);
1246                 ResolvedJavaType elementType = newArrayNode.type();
1247                 ValueNode[] dimensions = newArrayNode.dimensions().toArray(new ValueNode[0]);
1248                 for (NodePlugin nodePlugin : nodePlugins) {
1249                     if (nodePlugin.handleNewMultiArray(graphBuilderContext, elementType, dimensions)) {
1250                         replacedNode = graphBuilderContext.pushedNode;
1251                         break;
1252                     }
1253                 }
1254             }
1255         }
1256 
1257         return super.canonicalizeFixedNode(methodScope, replacedNode);
1258     }
1259 
1260     @Override
1261     protected Node handleFloatingNodeBeforeAdd(MethodScope s, LoopScope loopScope, Node n) {
1262         PEMethodScope methodScope = (PEMethodScope) s;
1263 
1264         Node node = n;
1265         if (node instanceof ParameterNode) {
1266             ParameterNode param = (ParameterNode) node;
1267             if (methodScope.isInlinedMethod()) {
1268                 throw GraalError.shouldNotReachHere("Parameter nodes are already registered when the inlined scope is created");
1269 
1270             } else if (parameterPlugin != null) {
1271                 assert !methodScope.isInlinedMethod();
1272                 GraphBuilderContext graphBuilderContext = new PENonAppendGraphBuilderContext(methodScope, null);
1273                 Node result = parameterPlugin.interceptParameter(graphBuilderContext, param.index(),
1274                                 StampPair.create(param.stamp(NodeView.DEFAULT), param.uncheckedStamp()));
1275                 if (result != null) {
1276                     return result;
1277                 }
1278             }
1279             node = param.copyWithInputs();
1280         }
1281 
1282         return super.handleFloatingNodeBeforeAdd(methodScope, loopScope, node);
1283     }
1284 
1285     protected void ensureOuterStateDecoded(PEMethodScope methodScope) {
1286         if (methodScope.outerState == null && methodScope.caller != null) {
1287             FrameState stateAtReturn = methodScope.invokeData.invoke.stateAfter();
1288             if (stateAtReturn == null) {
1289                 stateAtReturn = (FrameState) decodeFloatingNode(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId);
1290             }
1291 
1292             JavaKind invokeReturnKind = methodScope.invokeData.invoke.asNode().getStackKind();
1293             FrameState outerState = stateAtReturn.duplicateModified(graph, methodScope.invokeData.invoke.bci(), stateAtReturn.rethrowException(), true, invokeReturnKind, null, null);
1294 
1295             /*
1296              * When the encoded graph has methods inlining, we can already have a proper caller
1297              * state. If not, we set the caller state here.
1298              */
1299             if (outerState.outerFrameState() == null && methodScope.caller != null) {
1300                 ensureOuterStateDecoded(methodScope.caller);
1301                 outerState.setOuterFrameState(methodScope.caller.outerState);
1302             }
1303             methodScope.outerState = outerState;
1304         }
1305     }
1306 
1307     protected void ensureStateAfterDecoded(PEMethodScope methodScope) {
1308         if (methodScope.invokeData.invoke.stateAfter() == null) {
1309             methodScope.invokeData.invoke.setStateAfter((FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.stateAfterOrderId));
1310         }
1311     }
1312 
1313     protected void ensureExceptionStateDecoded(PEMethodScope methodScope) {
1314         if (methodScope.exceptionState == null && methodScope.caller != null && methodScope.invokeData.invoke instanceof InvokeWithExceptionNode) {
1315             ensureStateAfterDecoded(methodScope);
1316 
1317             assert methodScope.exceptionPlaceholderNode == null;
1318             methodScope.exceptionPlaceholderNode = graph.add(new ExceptionPlaceholderNode());
1319             registerNode(methodScope.callerLoopScope, methodScope.invokeData.exceptionOrderId, methodScope.exceptionPlaceholderNode, false, false);
1320             FrameState exceptionState = (FrameState) ensureNodeCreated(methodScope.caller, methodScope.callerLoopScope, methodScope.invokeData.exceptionStateOrderId);
1321 
1322             if (exceptionState.outerFrameState() == null && methodScope.caller != null) {
1323                 ensureOuterStateDecoded(methodScope.caller);
1324                 exceptionState.setOuterFrameState(methodScope.caller.outerState);
1325             }
1326             methodScope.exceptionState = exceptionState;
1327         }
1328     }
1329 
1330     @Override
1331     protected Node handleFloatingNodeAfterAdd(MethodScope s, LoopScope loopScope, Node node) {
1332         PEMethodScope methodScope = (PEMethodScope) s;
1333 
1334         if (methodScope.isInlinedMethod()) {
1335             if (node instanceof FrameState) {
1336                 FrameState frameState = (FrameState) node;
1337 
1338                 ensureOuterStateDecoded(methodScope);
1339                 if (frameState.bci < 0) {
1340                     ensureExceptionStateDecoded(methodScope);
1341                 }
1342                 List<ValueNode> invokeArgsList = null;
1343                 if (frameState.bci == BytecodeFrame.BEFORE_BCI) {
1344                     /*
1345                      * We know that the argument list is only used in this case, so avoid the List
1346                      * allocation for "normal" bcis.
1347                      */
1348                     invokeArgsList = Arrays.asList(methodScope.arguments);
1349                 }
1350                 return InliningUtil.processFrameState(frameState, methodScope.invokeData.invoke, null, methodScope.method, methodScope.exceptionState, methodScope.outerState, true,
1351                                 methodScope.method, invokeArgsList);
1352 
1353             } else if (node instanceof MonitorIdNode) {
1354                 ensureOuterStateDecoded(methodScope);
1355                 InliningUtil.processMonitorId(methodScope.outerState, (MonitorIdNode) node);
1356                 return node;
1357             }
1358         }
1359 
1360         return node;
1361     }
1362 }