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