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 
PEMethodScope(StructuredGraph targetGraph, PEMethodScope caller, LoopScope callerLoopScope, EncodedGraph encodedGraph, ResolvedJavaMethod method, InvokeData invokeData, int inliningDepth, LoopExplosionPlugin loopExplosionPlugin, ValueNode[] arguments)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
isInlinedMethod()190         public boolean isInlinedMethod() {
191             return caller != null;
192         }
193 
194         @Override
getCallerBytecodePosition(NodeSourcePosition position)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 
resolveSourceLanguagePosition()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
toShortString()235         public String toShortString() {
236             throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable.");
237         }
238 
239         @Override
getOffsetEnd()240         public int getOffsetEnd() {
241             throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable.");
242         }
243 
244         @Override
getOffsetStart()245         public int getOffsetStart() {
246             throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable.");
247         }
248 
249         @Override
getLineNumber()250         public int getLineNumber() {
251             throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable.");
252         }
253 
254         @Override
getURI()255         public URI getURI() {
256             throw new IllegalStateException(getClass().getSimpleName() + " should not be reachable.");
257         }
258 
259         @Override
getLanguage()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
getExternalInliningContext()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 
PENonAppendGraphBuilderContext(PEMethodScope methodScope, Invoke invoke)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
canDeferPlugin(GeneratedInvocationPlugin plugin)303         public boolean canDeferPlugin(GeneratedInvocationPlugin plugin) {
304             return plugin.getSource().equals(Fold.class) || plugin.getSource().equals(Node.NodeIntrinsic.class);
305         }
306 
307         @Override
bailout(String string)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
getStampProvider()314         public StampProvider getStampProvider() {
315             return providers.getStampProvider();
316         }
317 
318         @Override
getMetaAccess()319         public MetaAccessProvider getMetaAccess() {
320             return providers.getMetaAccess();
321         }
322 
323         @Override
getConstantReflection()324         public ConstantReflectionProvider getConstantReflection() {
325             return providers.getConstantReflection();
326         }
327 
328         @Override
getConstantFieldProvider()329         public ConstantFieldProvider getConstantFieldProvider() {
330             return providers.getConstantFieldProvider();
331         }
332 
333         @Override
getReplacements()334         public Replacements getReplacements() {
335             return providers.getReplacements();
336         }
337 
338         @Override
getGraph()339         public StructuredGraph getGraph() {
340             return graph;
341         }
342 
343         @Override
getDepth()344         public int getDepth() {
345             return methodScope.inliningDepth;
346         }
347 
348         @Override
getIntrinsic()349         public IntrinsicContext getIntrinsic() {
350             return PEGraphDecoder.this.getIntrinsic();
351         }
352 
353         @Override
append(T value)354         public <T extends ValueNode> T append(T value) {
355             throw unimplemented();
356         }
357 
358         @Override
push(JavaKind kind, ValueNode value)359         public void push(JavaKind kind, ValueNode value) {
360             throw unimplemented();
361         }
362 
363         @Override
handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean inlineEverything)364         public Invoke handleReplacedInvoke(InvokeKind invokeKind, ResolvedJavaMethod targetMethod, ValueNode[] args, boolean inlineEverything) {
365             throw unimplemented();
366         }
367 
368         @Override
handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType)369         public void handleReplacedInvoke(CallTargetNode callTarget, JavaKind resultType) {
370             throw unimplemented();
371         }
372 
373         @Override
intrinsify(BytecodeProvider bytecodeProvider, ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, InvocationPlugin.Receiver receiver, ValueNode[] args)374         public boolean intrinsify(BytecodeProvider bytecodeProvider, ResolvedJavaMethod targetMethod, ResolvedJavaMethod substitute, InvocationPlugin.Receiver receiver, ValueNode[] args) {
375             return false;
376         }
377 
378         @Override
intrinsify(ResolvedJavaMethod targetMethod, StructuredGraph substituteGraph, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver)379         public boolean intrinsify(ResolvedJavaMethod targetMethod, StructuredGraph substituteGraph, InvocationPlugin.Receiver receiver, ValueNode[] argsIncludingReceiver) {
380             return false;
381         }
382 
383         @Override
setStateAfter(StateSplit stateSplit)384         public void setStateAfter(StateSplit stateSplit) {
385             throw unimplemented();
386         }
387 
388         @Override
getParent()389         public GraphBuilderContext getParent() {
390             throw unimplemented();
391         }
392 
393         @Override
getCode()394         public Bytecode getCode() {
395             throw unimplemented();
396         }
397 
398         @Override
getMethod()399         public ResolvedJavaMethod getMethod() {
400             throw unimplemented();
401         }
402 
403         @Override
bci()404         public int bci() {
405             return invoke.bci();
406         }
407 
408         @Override
getInvokeKind()409         public InvokeKind getInvokeKind() {
410             throw unimplemented();
411         }
412 
413         @Override
getInvokeReturnType()414         public JavaType getInvokeReturnType() {
415             throw unimplemented();
416         }
417 
418         @Override
toString()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 }
1361