1 /*
2  * Copyright (c) 2011, 2020, 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.nodes;
26 
27 import static jdk.vm.ci.services.Services.IS_IN_NATIVE_IMAGE;
28 
29 import java.util.ArrayList;
30 import java.util.Collections;
31 import java.util.EnumSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.SortedSet;
35 import java.util.TreeSet;
36 import java.util.concurrent.atomic.AtomicLong;
37 import java.util.function.Consumer;
38 import java.util.stream.Collectors;
39 
40 import jdk.internal.vm.compiler.collections.EconomicMap;
41 import jdk.internal.vm.compiler.collections.EconomicSet;
42 import jdk.internal.vm.compiler.collections.Equivalence;
43 import jdk.internal.vm.compiler.collections.UnmodifiableEconomicMap;
44 import org.graalvm.compiler.api.replacements.MethodSubstitution;
45 import org.graalvm.compiler.api.replacements.Snippet;
46 import org.graalvm.compiler.core.common.CancellationBailoutException;
47 import org.graalvm.compiler.core.common.CompilationIdentifier;
48 import org.graalvm.compiler.core.common.GraalOptions;
49 import org.graalvm.compiler.core.common.cfg.BlockMap;
50 import org.graalvm.compiler.core.common.type.Stamp;
51 import org.graalvm.compiler.debug.DebugContext;
52 import org.graalvm.compiler.debug.JavaMethodContext;
53 import org.graalvm.compiler.debug.TTY;
54 import org.graalvm.compiler.graph.Graph;
55 import org.graalvm.compiler.graph.Node;
56 import org.graalvm.compiler.graph.NodeMap;
57 import org.graalvm.compiler.graph.NodeSourcePosition;
58 import org.graalvm.compiler.nodes.calc.FloatingNode;
59 import org.graalvm.compiler.nodes.cfg.Block;
60 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
61 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
62 import org.graalvm.compiler.nodes.spi.VirtualizableAllocation;
63 import org.graalvm.compiler.nodes.util.GraphUtil;
64 import org.graalvm.compiler.options.OptionValues;
65 
66 import jdk.vm.ci.code.BytecodeFrame;
67 import jdk.vm.ci.meta.Assumptions;
68 import jdk.vm.ci.meta.Assumptions.Assumption;
69 import jdk.vm.ci.meta.DefaultProfilingInfo;
70 import jdk.vm.ci.meta.JavaMethod;
71 import jdk.vm.ci.meta.ProfilingInfo;
72 import jdk.vm.ci.meta.ResolvedJavaField;
73 import jdk.vm.ci.meta.ResolvedJavaMethod;
74 import jdk.vm.ci.meta.SpeculationLog;
75 import jdk.vm.ci.meta.TriState;
76 import jdk.vm.ci.runtime.JVMCICompiler;
77 
78 /**
79  * A graph that contains at least one distinguished node : the {@link #start() start} node. This
80  * node is the start of the control flow of the graph.
81  */
82 public final class StructuredGraph extends Graph implements JavaMethodContext {
83 
84     /**
85      * The different stages of the compilation of a {@link Graph} regarding the status of
86      * {@link GuardNode guards}, {@link DeoptimizingNode deoptimizations} and {@link FrameState
87      * frame states}. The stage of a graph progresses monotonously.
88      */
89     public enum GuardsStage {
90         /**
91          * During this stage, there can be {@link FloatingNode floating} {@link DeoptimizingNode}
92          * such as {@link GuardNode GuardNodes}. New {@link DeoptimizingNode DeoptimizingNodes} can
93          * be introduced without constraints. {@link FrameState} nodes are associated with
94          * {@link StateSplit} nodes.
95          */
96         FLOATING_GUARDS,
97         /**
98          * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be
99          * {@link FixedNode fixed} but new {@link DeoptimizingNode DeoptimizingNodes} can still be
100          * introduced. {@link FrameState} nodes are still associated with {@link StateSplit} nodes.
101          */
102         FIXED_DEOPTS,
103         /**
104          * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be
105          * {@link FixedNode fixed}. New {@link DeoptimizingNode DeoptimizingNodes} can not be
106          * introduced any more. {@link FrameState} nodes are now associated with
107          * {@link DeoptimizingNode} nodes.
108          */
109         AFTER_FSA;
110 
allowsFloatingGuards()111         public boolean allowsFloatingGuards() {
112             return this == FLOATING_GUARDS;
113         }
114 
allowsGuardInsertion()115         public boolean allowsGuardInsertion() {
116             return this.ordinal() <= FIXED_DEOPTS.ordinal();
117         }
118 
areFrameStatesAtDeopts()119         public boolean areFrameStatesAtDeopts() {
120             return this == AFTER_FSA;
121         }
122 
areFrameStatesAtSideEffects()123         public boolean areFrameStatesAtSideEffects() {
124             return !this.areFrameStatesAtDeopts();
125         }
126 
areDeoptsFixed()127         public boolean areDeoptsFixed() {
128             return this.ordinal() >= FIXED_DEOPTS.ordinal();
129         }
130 
requiresValueProxies()131         public boolean requiresValueProxies() {
132             return this != AFTER_FSA;
133         }
134     }
135 
136     /**
137      * Constants denoting whether or not {@link Assumption}s can be made while processing a graph.
138      */
139     public enum AllowAssumptions {
140         YES,
141         NO;
142 
ifTrue(boolean flag)143         public static AllowAssumptions ifTrue(boolean flag) {
144             return flag ? YES : NO;
145         }
146 
ifNonNull(Assumptions assumptions)147         public static AllowAssumptions ifNonNull(Assumptions assumptions) {
148             return assumptions != null ? YES : NO;
149         }
150     }
151 
152     public static class ScheduleResult {
153         private final ControlFlowGraph cfg;
154         private final NodeMap<Block> nodeToBlockMap;
155         private final BlockMap<List<Node>> blockToNodesMap;
156 
ScheduleResult(ControlFlowGraph cfg, NodeMap<Block> nodeToBlockMap, BlockMap<List<Node>> blockToNodesMap)157         public ScheduleResult(ControlFlowGraph cfg, NodeMap<Block> nodeToBlockMap, BlockMap<List<Node>> blockToNodesMap) {
158             this.cfg = cfg;
159             this.nodeToBlockMap = nodeToBlockMap;
160             this.blockToNodesMap = blockToNodesMap;
161         }
162 
getCFG()163         public ControlFlowGraph getCFG() {
164             return cfg;
165         }
166 
getNodeToBlockMap()167         public NodeMap<Block> getNodeToBlockMap() {
168             return nodeToBlockMap;
169         }
170 
getBlockToNodesMap()171         public BlockMap<List<Node>> getBlockToNodesMap() {
172             return blockToNodesMap;
173         }
174 
nodesFor(Block block)175         public List<Node> nodesFor(Block block) {
176             return blockToNodesMap.get(block);
177         }
178     }
179 
180     /**
181      * Object used to create a {@link StructuredGraph}.
182      */
183     public static class Builder {
184         private String name;
185         private final Assumptions assumptions;
186         private SpeculationLog speculationLog;
187         private ResolvedJavaMethod rootMethod;
188         private CompilationIdentifier compilationId = CompilationIdentifier.INVALID_COMPILATION_ID;
189         private int entryBCI = JVMCICompiler.INVOCATION_ENTRY_BCI;
190         private boolean useProfilingInfo = true;
191         private boolean recordInlinedMethods = true;
192         private boolean trackNodeSourcePosition;
193         private final OptionValues options;
194         private Cancellable cancellable = null;
195         private final DebugContext debug;
196         private NodeSourcePosition callerContext;
197         private boolean isSubstitution;
198 
199         /**
200          * Creates a builder for a graph.
201          */
Builder(OptionValues options, DebugContext debug, AllowAssumptions allowAssumptions)202         public Builder(OptionValues options, DebugContext debug, AllowAssumptions allowAssumptions) {
203             this.options = options;
204             this.debug = debug;
205             this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null;
206             this.trackNodeSourcePosition = Graph.trackNodeSourcePositionDefault(options, debug);
207         }
208 
209         /**
210          * Creates a builder for a graph that does not support {@link Assumptions}.
211          */
Builder(OptionValues options, DebugContext debug)212         public Builder(OptionValues options, DebugContext debug) {
213             this.options = options;
214             this.debug = debug;
215             this.assumptions = null;
216             this.trackNodeSourcePosition = Graph.trackNodeSourcePositionDefault(options, debug);
217         }
218 
getName()219         public String getName() {
220             return name;
221         }
222 
name(String s)223         public Builder name(String s) {
224             this.name = s;
225             return this;
226         }
227 
228         /**
229          * @see StructuredGraph#isSubstitution
230          */
setIsSubstitution(boolean flag)231         public Builder setIsSubstitution(boolean flag) {
232             this.isSubstitution = flag;
233             return this;
234         }
235 
getMethod()236         public ResolvedJavaMethod getMethod() {
237             return rootMethod;
238         }
239 
method(ResolvedJavaMethod method)240         public Builder method(ResolvedJavaMethod method) {
241             this.rootMethod = method;
242             return this;
243         }
244 
getDebug()245         public DebugContext getDebug() {
246             return debug;
247         }
248 
getSpeculationLog()249         public SpeculationLog getSpeculationLog() {
250             return speculationLog;
251         }
252 
speculationLog(SpeculationLog log)253         public Builder speculationLog(SpeculationLog log) {
254             this.speculationLog = log;
255             return this;
256         }
257 
getCompilationId()258         public CompilationIdentifier getCompilationId() {
259             return compilationId;
260         }
261 
compilationId(CompilationIdentifier id)262         public Builder compilationId(CompilationIdentifier id) {
263             this.compilationId = id;
264             return this;
265         }
266 
getCancellable()267         public Cancellable getCancellable() {
268             return cancellable;
269         }
270 
cancellable(Cancellable cancel)271         public Builder cancellable(Cancellable cancel) {
272             this.cancellable = cancel;
273             return this;
274         }
275 
getEntryBCI()276         public int getEntryBCI() {
277             return entryBCI;
278         }
279 
entryBCI(int bci)280         public Builder entryBCI(int bci) {
281             this.entryBCI = bci;
282             return this;
283         }
284 
getUseProfilingInfo()285         public boolean getUseProfilingInfo() {
286             return useProfilingInfo;
287         }
288 
useProfilingInfo(boolean flag)289         public Builder useProfilingInfo(boolean flag) {
290             this.useProfilingInfo = flag;
291             return this;
292         }
293 
getRecordInlinedMethods()294         public boolean getRecordInlinedMethods() {
295             return recordInlinedMethods;
296         }
297 
recordInlinedMethods(boolean flag)298         public Builder recordInlinedMethods(boolean flag) {
299             this.recordInlinedMethods = flag;
300             return this;
301         }
302 
trackNodeSourcePosition(boolean flag)303         public Builder trackNodeSourcePosition(boolean flag) {
304             if (flag) {
305                 this.trackNodeSourcePosition = true;
306             }
307             return this;
308         }
309 
callerContext(NodeSourcePosition context)310         public Builder callerContext(NodeSourcePosition context) {
311             this.callerContext = context;
312             return this;
313         }
314 
build()315         public StructuredGraph build() {
316             List<ResolvedJavaMethod> inlinedMethods = recordInlinedMethods ? new ArrayList<>() : null;
317             // @formatter:off
318             return new StructuredGraph(name,
319                             rootMethod,
320                             entryBCI,
321                             assumptions,
322                             speculationLog,
323                             useProfilingInfo,
324                             isSubstitution,
325                             inlinedMethods,
326                             trackNodeSourcePosition,
327                             compilationId,
328                             options,
329                             debug,
330                             cancellable,
331                             callerContext);
332             // @formatter:on
333         }
334     }
335 
336     public static final long INVALID_GRAPH_ID = -1;
337     private static final AtomicLong uniqueGraphIds = new AtomicLong();
338 
339     private StartNode start;
340     private ResolvedJavaMethod rootMethod;
341     private final long graphId;
342     private final CompilationIdentifier compilationId;
343     private final int entryBCI;
344     private GuardsStage guardsStage = GuardsStage.FLOATING_GUARDS;
345     private boolean isAfterFloatingReadPhase = false;
346     private boolean isAfterFixedReadPhase = false;
347     private boolean hasValueProxies = true;
348     private boolean isAfterExpandLogic = false;
349     private FrameStateVerification frameStateVerification;
350 
351     /**
352      * Different node types verified during {@linkplain FrameStateVerification}. See
353      * {@linkplain FrameStateVerification} for details.
354      */
355     public enum FrameStateVerificationFeature {
356         STATE_SPLITS,
357         MERGES,
358         LOOP_BEGINS,
359         LOOP_EXITS
360     }
361 
362     /**
363      * The different stages of the compilation of a {@link Graph} regarding the status of
364      * {@linkplain FrameState} verification of {@linkplain AbstractStateSplit} state after.
365      * Verification starts with the mode {@linkplain FrameStateVerification#ALL}, i.e., all state
366      * splits with side-effects, merges and loop exits need a proper state after. The verification
367      * mode progresses monotonously until the {@linkplain FrameStateVerification#NONE} mode is
368      * reached. From there on, no further {@linkplain AbstractStateSplit#stateAfter} verification
369      * happens.
370      */
371     public enum FrameStateVerification {
372         /**
373          * Verify all {@linkplain AbstractStateSplit} nodes that return {@code true} for
374          * {@linkplain AbstractStateSplit#hasSideEffect()} have a
375          * {@linkplain AbstractStateSplit#stateAfter} assigned. Additionally, verify
376          * {@linkplain LoopExitNode} and {@linkplain AbstractMergeNode} have a valid
377          * {@linkplain AbstractStateSplit#stateAfter}. This is necessary to avoid missing
378          * {@linkplain FrameState} after optimizations. See {@link GraphUtil#mayRemoveSplit} for
379          * more details.
380          *
381          * This stage is the initial verification stage for every graph.
382          */
383         ALL(EnumSet.allOf(FrameStateVerificationFeature.class)),
384         /**
385          * Same as {@linkplain #ALL} except that {@linkplain LoopExitNode} nodes are no longer
386          * verified.
387          */
388         ALL_EXCEPT_LOOP_EXIT(EnumSet.complementOf(EnumSet.of(FrameStateVerificationFeature.LOOP_EXITS))),
389         /**
390          * Same as {@linkplain #ALL_EXCEPT_LOOP_EXIT} except that {@linkplain LoopBeginNode} are no
391          * longer verified.
392          */
393         ALL_EXCEPT_LOOPS(EnumSet.complementOf(EnumSet.of(FrameStateVerificationFeature.LOOP_BEGINS, FrameStateVerificationFeature.LOOP_EXITS))),
394         /**
395          * Verification is disabled. Typically used after assigning {@linkplain FrameState} to
396          * {@linkplain DeoptimizeNode} or for {@linkplain Snippet} compilations.
397          */
398         NONE(EnumSet.noneOf(FrameStateVerificationFeature.class));
399 
400         private EnumSet<FrameStateVerificationFeature> features;
401 
FrameStateVerification(EnumSet<FrameStateVerificationFeature> features)402         FrameStateVerification(EnumSet<FrameStateVerificationFeature> features) {
403             this.features = features;
404         }
405 
406         /**
407          * Determines if the current verification mode implies this feature.
408          *
409          * @param feature the other verification feature to check
410          * @return {@code true} if this verification mode implies the feature, {@code false}
411          *         otherwise
412          */
implies(FrameStateVerificationFeature feature)413         boolean implies(FrameStateVerificationFeature feature) {
414             return this.features.contains(feature);
415         }
416 
417     }
418 
419     private final boolean useProfilingInfo;
420     private final Cancellable cancellable;
421     private final boolean isSubstitution;
422 
423     /**
424      * The assumptions made while constructing and transforming this graph.
425      */
426     private final Assumptions assumptions;
427 
428     private SpeculationLog speculationLog;
429 
430     private ScheduleResult lastSchedule;
431 
432     private final InliningLog inliningLog;
433 
434     /**
435      * Call stack (context) leading to construction of this graph.
436      */
437     private final NodeSourcePosition callerContext;
438 
439     /**
440      * Records the methods that were used while constructing this graph, one entry for each time a
441      * specific method is used. This will be {@code null} if recording of inlined methods is
442      * disabled for the graph.
443      */
444     private final List<ResolvedJavaMethod> methods;
445 
446     /**
447      * Records the fields that were accessed while constructing this graph.
448      */
449     private EconomicSet<ResolvedJavaField> fields = null;
450 
451     private enum UnsafeAccessState {
452         NO_ACCESS,
453         HAS_ACCESS,
454         DISABLED
455     }
456 
457     private UnsafeAccessState hasUnsafeAccess = UnsafeAccessState.NO_ACCESS;
458 
459     public static final boolean USE_PROFILING_INFO = true;
460 
461     public static final boolean NO_PROFILING_INFO = false;
462 
StructuredGraph(String name, ResolvedJavaMethod method, int entryBCI, Assumptions assumptions, SpeculationLog speculationLog, boolean useProfilingInfo, boolean isSubstitution, List<ResolvedJavaMethod> methods, boolean trackNodeSourcePosition, CompilationIdentifier compilationId, OptionValues options, DebugContext debug, Cancellable cancellable, NodeSourcePosition context)463     private StructuredGraph(String name,
464                     ResolvedJavaMethod method,
465                     int entryBCI,
466                     Assumptions assumptions,
467                     SpeculationLog speculationLog,
468                     boolean useProfilingInfo,
469                     boolean isSubstitution,
470                     List<ResolvedJavaMethod> methods,
471                     boolean trackNodeSourcePosition,
472                     CompilationIdentifier compilationId,
473                     OptionValues options,
474                     DebugContext debug,
475                     Cancellable cancellable,
476                     NodeSourcePosition context) {
477         super(name, options, debug, trackNodeSourcePosition);
478         this.setStart(add(new StartNode()));
479         this.rootMethod = method;
480         this.graphId = uniqueGraphIds.incrementAndGet();
481         this.compilationId = compilationId;
482         this.entryBCI = entryBCI;
483         this.assumptions = assumptions;
484         this.methods = methods;
485         this.speculationLog = speculationLog;
486         this.useProfilingInfo = useProfilingInfo;
487         this.isSubstitution = isSubstitution;
488         assert checkIsSubstitutionInvariants(method, isSubstitution);
489         this.cancellable = cancellable;
490         this.inliningLog = new InliningLog(rootMethod, GraalOptions.TraceInlining.getValue(options), debug);
491         this.callerContext = context;
492         this.frameStateVerification = isSubstitution ? FrameStateVerification.NONE : FrameStateVerification.ALL;
493     }
494 
checkIsSubstitutionInvariants(ResolvedJavaMethod method, boolean isSubstitution)495     private static boolean checkIsSubstitutionInvariants(ResolvedJavaMethod method, boolean isSubstitution) {
496         if (!IS_IN_NATIVE_IMAGE) {
497             if (method != null) {
498                 if (method.getAnnotation(Snippet.class) != null || method.getAnnotation(MethodSubstitution.class) != null) {
499                     assert isSubstitution : "Graph for method " + method.format("%H.%n(%p)") +
500                                     " annotated by " + Snippet.class.getName() + " or " +
501                                     MethodSubstitution.class.getName() +
502                                     " must have its `isSubstitution` field set to true";
503                 }
504             }
505         }
506         return true;
507     }
508 
setLastSchedule(ScheduleResult result)509     public void setLastSchedule(ScheduleResult result) {
510         lastSchedule = result;
511     }
512 
getLastSchedule()513     public ScheduleResult getLastSchedule() {
514         return lastSchedule;
515     }
516 
clearLastSchedule()517     public void clearLastSchedule() {
518         setLastSchedule(null);
519     }
520 
521     @Override
maybeCompress()522     public boolean maybeCompress() {
523         if (super.maybeCompress()) {
524             /*
525              * The schedule contains a NodeMap which is unusable after compression.
526              */
527             clearLastSchedule();
528             return true;
529         }
530         return false;
531     }
532 
getReturnStamp()533     public Stamp getReturnStamp() {
534         Stamp returnStamp = null;
535         for (ReturnNode returnNode : getNodes(ReturnNode.TYPE)) {
536             ValueNode result = returnNode.result();
537             if (result != null) {
538                 if (returnStamp == null) {
539                     returnStamp = result.stamp(NodeView.DEFAULT);
540                 } else {
541                     returnStamp = returnStamp.meet(result.stamp(NodeView.DEFAULT));
542                 }
543             }
544         }
545         return returnStamp;
546     }
547 
548     @Override
toString()549     public String toString() {
550         StringBuilder buf = new StringBuilder(getClass().getSimpleName() + ":" + graphId);
551         String sep = "{";
552         if (name != null) {
553             buf.append(sep);
554             buf.append(name);
555             sep = ", ";
556         }
557         if (method() != null) {
558             buf.append(sep);
559             buf.append(method());
560             sep = ", ";
561         }
562 
563         if (!sep.equals("{")) {
564             buf.append("}");
565         }
566         return buf.toString();
567     }
568 
start()569     public StartNode start() {
570         return start;
571     }
572 
573     /**
574      * Gets the root method from which this graph was built.
575      *
576      * @return null if this method was not built from a method or the method is not available
577      */
method()578     public ResolvedJavaMethod method() {
579         return rootMethod;
580     }
581 
getEntryBCI()582     public int getEntryBCI() {
583         return entryBCI;
584     }
585 
getCancellable()586     public Cancellable getCancellable() {
587         return cancellable;
588     }
589 
checkCancellation()590     public void checkCancellation() {
591         if (cancellable != null && cancellable.isCancelled()) {
592             CancellationBailoutException.cancelCompilation();
593         }
594     }
595 
isOSR()596     public boolean isOSR() {
597         return entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
598     }
599 
graphId()600     public long graphId() {
601         return graphId;
602     }
603 
604     /**
605      * @see CompilationIdentifier
606      */
compilationId()607     public CompilationIdentifier compilationId() {
608         return compilationId;
609     }
610 
setStart(StartNode start)611     public void setStart(StartNode start) {
612         this.start = start;
613     }
614 
getInliningLog()615     public InliningLog getInliningLog() {
616         return inliningLog;
617     }
618 
logInliningTree()619     public void logInliningTree() {
620         if (GraalOptions.TraceInlining.getValue(getOptions())) {
621             String formattedTree = getInliningLog().formatAsTree(true);
622             if (formattedTree != null) {
623                 TTY.println(formattedTree);
624             }
625         }
626     }
627 
628     /**
629      * Creates a copy of this graph.
630      *
631      * If a node contains an array of objects, only shallow copy of the field is applied.
632      *
633      * @param newName the name of the copy, used for debugging purposes (can be null)
634      * @param duplicationMapCallback consumer of the duplication map created during the copying
635      * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
636      *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
637      *            accessed by multiple threads).
638      */
639     @Override
copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy)640     protected Graph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, DebugContext debugForCopy) {
641         return copy(newName, duplicationMapCallback, compilationId, debugForCopy);
642     }
643 
644     @SuppressWarnings("try")
copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId, DebugContext debugForCopy)645     private StructuredGraph copy(String newName, Consumer<UnmodifiableEconomicMap<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId, DebugContext debugForCopy) {
646         AllowAssumptions allowAssumptions = allowAssumptions();
647         StructuredGraph copy = new StructuredGraph(newName,
648                         method(),
649                         entryBCI,
650                         assumptions == null ? null : new Assumptions(),
651                         speculationLog,
652                         useProfilingInfo,
653                         isSubstitution,
654                         methods != null ? new ArrayList<>(methods) : null,
655                         trackNodeSourcePosition,
656                         newCompilationId,
657                         getOptions(), debugForCopy, null, callerContext);
658         if (allowAssumptions == AllowAssumptions.YES && assumptions != null) {
659             copy.assumptions.record(assumptions);
660         }
661         copy.hasUnsafeAccess = hasUnsafeAccess;
662         copy.setGuardsStage(getGuardsStage());
663         copy.isAfterFloatingReadPhase = isAfterFloatingReadPhase;
664         copy.hasValueProxies = hasValueProxies;
665         copy.isAfterExpandLogic = isAfterExpandLogic;
666         copy.trackNodeSourcePosition = trackNodeSourcePosition;
667         if (fields != null) {
668             copy.fields = createFieldSet(fields);
669         }
670         EconomicMap<Node, Node> replacements = EconomicMap.create(Equivalence.IDENTITY);
671         replacements.put(start, copy.start);
672         UnmodifiableEconomicMap<Node, Node> duplicates;
673         try (InliningLog.UpdateScope scope = copy.getInliningLog().openDefaultUpdateScope()) {
674             duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), replacements);
675             if (scope != null) {
676                 copy.getInliningLog().replaceLog(duplicates, this.getInliningLog());
677             }
678         }
679         if (duplicationMapCallback != null) {
680             duplicationMapCallback.accept(duplicates);
681         }
682         return copy;
683     }
684 
685     /**
686      * @param debugForCopy the debug context for the graph copy. This must not be the debug for this
687      *            graph if this graph can be accessed from multiple threads (e.g., it's in a cache
688      *            accessed by multiple threads).
689      */
copyWithIdentifier(CompilationIdentifier newCompilationId, DebugContext debugForCopy)690     public StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId, DebugContext debugForCopy) {
691         return copy(name, null, newCompilationId, debugForCopy);
692     }
693 
getParameter(int index)694     public ParameterNode getParameter(int index) {
695         for (ParameterNode param : getNodes(ParameterNode.TYPE)) {
696             if (param.index() == index) {
697                 return param;
698             }
699         }
700         return null;
701     }
702 
getInvokes()703     public Iterable<Invoke> getInvokes() {
704         final Iterator<MethodCallTargetNode> callTargets = getNodes(MethodCallTargetNode.TYPE).iterator();
705         return new Iterable<Invoke>() {
706 
707             private Invoke next;
708 
709             @Override
710             public Iterator<Invoke> iterator() {
711                 return new Iterator<Invoke>() {
712 
713                     @Override
714                     public boolean hasNext() {
715                         if (next == null) {
716                             while (callTargets.hasNext()) {
717                                 Invoke i = callTargets.next().invoke();
718                                 if (i != null) {
719                                     next = i;
720                                     return true;
721                                 }
722                             }
723                             return false;
724                         } else {
725                             return true;
726                         }
727                     }
728 
729                     @Override
730                     public Invoke next() {
731                         try {
732                             return next;
733                         } finally {
734                             next = null;
735                         }
736                     }
737 
738                     @Override
739                     public void remove() {
740                         throw new UnsupportedOperationException();
741                     }
742                 };
743             }
744         };
745     }
746 
747     public boolean hasLoops() {
748         return hasNode(LoopBeginNode.TYPE);
749     }
750 
751     /**
752      * Unlinks a node from all its control flow neighbors and then removes it from its graph. The
753      * node must have no {@linkplain Node#usages() usages}.
754      *
755      * @param node the node to be unlinked and removed
756      */
757     @SuppressWarnings("static-method")
758     public void removeFixed(FixedWithNextNode node) {
759         assert node != null;
760         if (node instanceof AbstractBeginNode) {
761             ((AbstractBeginNode) node).prepareDelete();
762         }
763         assert node.hasNoUsages() : node + " " + node.getUsageCount() + ", " + node.usages().first();
764         GraphUtil.unlinkFixedNode(node);
765         node.safeDelete();
766     }
767 
768     public void replaceFixed(FixedWithNextNode node, Node replacement) {
769         if (replacement instanceof FixedWithNextNode) {
770             replaceFixedWithFixed(node, (FixedWithNextNode) replacement);
771         } else {
772             assert replacement != null : "cannot replace " + node + " with null";
773             assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement;
774             replaceFixedWithFloating(node, (FloatingNode) replacement);
775         }
776     }
777 
778     public void replaceFixedWithFixed(FixedWithNextNode node, FixedWithNextNode replacement) {
779         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
780         FixedNode next = node.next();
781         node.setNext(null);
782         replacement.setNext(next);
783         node.replaceAndDelete(replacement);
784         if (node == start) {
785             setStart((StartNode) replacement);
786         }
787     }
788 
789     @SuppressWarnings("static-method")
790     public void replaceFixedWithFloating(FixedWithNextNode node, ValueNode replacement) {
791         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
792         GraphUtil.unlinkFixedNode(node);
793         node.replaceAtUsagesAndDelete(replacement);
794     }
795 
796     @SuppressWarnings("static-method")
797     public void removeSplit(ControlSplitNode node, AbstractBeginNode survivingSuccessor) {
798         assert node != null;
799         assert node.hasNoUsages();
800         assert survivingSuccessor != null;
801         node.clearSuccessors();
802         node.replaceAtPredecessor(survivingSuccessor);
803         node.safeDelete();
804     }
805 
806     @SuppressWarnings("static-method")
807     public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) {
808         assert node != null;
809         assert node.hasNoUsages();
810         assert survivingSuccessor != null;
811         List<Node> snapshot = node.successors().snapshot();
812         node.clearSuccessors();
813         node.replaceAtPredecessor(survivingSuccessor);
814         node.safeDelete();
815         for (Node successor : snapshot) {
816             if (successor != null && successor.isAlive()) {
817                 if (successor != survivingSuccessor) {
818                     GraphUtil.killCFG((FixedNode) successor);
819                 }
820             }
821         }
822     }
823 
824     public void replaceSplit(ControlSplitNode node, Node replacement, AbstractBeginNode survivingSuccessor) {
825         if (replacement instanceof FixedWithNextNode) {
826             replaceSplitWithFixed(node, (FixedWithNextNode) replacement, survivingSuccessor);
827         } else {
828             assert replacement != null : "cannot replace " + node + " with null";
829             assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement;
830             replaceSplitWithFloating(node, (FloatingNode) replacement, survivingSuccessor);
831         }
832     }
833 
834     @SuppressWarnings("static-method")
835     public void replaceSplitWithFixed(ControlSplitNode node, FixedWithNextNode replacement, AbstractBeginNode survivingSuccessor) {
836         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
837         assert survivingSuccessor != null;
838         node.clearSuccessors();
839         replacement.setNext(survivingSuccessor);
840         node.replaceAndDelete(replacement);
841     }
842 
843     @SuppressWarnings("static-method")
844     public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, AbstractBeginNode survivingSuccessor) {
845         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
846         assert survivingSuccessor != null;
847         node.clearSuccessors();
848         node.replaceAtPredecessor(survivingSuccessor);
849         node.replaceAtUsagesAndDelete(replacement);
850     }
851 
852     @SuppressWarnings("static-method")
853     public void replaceWithExceptionSplit(WithExceptionNode node, WithExceptionNode replacement) {
854         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
855         node.replaceAtPredecessor(replacement);
856         AbstractBeginNode next = node.next();
857         AbstractBeginNode exceptionEdge = node.exceptionEdge();
858         node.replaceAtUsagesAndDelete(replacement);
859         replacement.setNext(next);
860         replacement.setExceptionEdge(exceptionEdge);
861     }
862 
863     @SuppressWarnings("static-method")
864     public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) {
865         assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node;
866         FixedNode next = node.next();
867         node.setNext(newNode);
868         if (next != null) {
869             assert newNode instanceof FixedWithNextNode;
870             FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode;
871             assert newFixedWithNext.next() == null;
872             newFixedWithNext.setNext(next);
873         }
874     }
875 
876     @SuppressWarnings("static-method")
877     public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) {
878         assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " before " + node;
879         assert node.predecessor() != null && node.predecessor() instanceof FixedWithNextNode : "cannot add " + newNode + " before " + node;
880         assert newNode.next() == null : newNode;
881         assert !(node instanceof AbstractMergeNode);
882         FixedWithNextNode pred = (FixedWithNextNode) node.predecessor();
883         pred.setNext(newNode);
884         newNode.setNext(node);
885     }
886 
887     public void reduceDegenerateLoopBegin(LoopBeginNode begin) {
888         assert begin.loopEnds().isEmpty() : "Loop begin still has backedges";
889         if (begin.forwardEndCount() == 1) { // bypass merge and remove
890             reduceTrivialMerge(begin);
891         } else { // convert to merge
892             AbstractMergeNode merge = this.add(new MergeNode());
893             for (EndNode end : begin.forwardEnds()) {
894                 merge.addForwardEnd(end);
895             }
896             this.replaceFixedWithFixed(begin, merge);
897         }
898     }
899 
900     @SuppressWarnings("static-method")
901     public void reduceTrivialMerge(AbstractMergeNode merge) {
902         assert merge.forwardEndCount() == 1;
903         assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty();
904         for (PhiNode phi : merge.phis().snapshot()) {
905             assert phi.valueCount() == 1;
906             ValueNode singleValue = phi.valueAt(0);
907             if (phi.hasUsages()) {
908                 phi.replaceAtUsagesAndDelete(singleValue);
909             } else {
910                 phi.safeDelete();
911                 if (singleValue != null) {
912                     GraphUtil.tryKillUnused(singleValue);
913                 }
914             }
915         }
916         // remove loop exits
917         if (merge instanceof LoopBeginNode) {
918             ((LoopBeginNode) merge).removeExits();
919         }
920         AbstractEndNode singleEnd = merge.forwardEndAt(0);
921         FixedNode sux = merge.next();
922         FrameState stateAfter = merge.stateAfter();
923         // evacuateGuards
924         merge.prepareDelete((FixedNode) singleEnd.predecessor());
925         merge.safeDelete();
926         if (stateAfter != null) {
927             GraphUtil.tryKillUnused(stateAfter);
928         }
929         if (sux == null) {
930             singleEnd.replaceAtPredecessor(null);
931             singleEnd.safeDelete();
932         } else {
933             singleEnd.replaceAndDelete(sux);
934         }
935     }
936 
937     public GuardsStage getGuardsStage() {
938         return guardsStage;
939     }
940 
941     public void setGuardsStage(GuardsStage guardsStage) {
942         assert guardsStage.ordinal() >= this.guardsStage.ordinal();
943         this.guardsStage = guardsStage;
944     }
945 
946     public boolean isAfterFloatingReadPhase() {
947         return isAfterFloatingReadPhase;
948     }
949 
950     public boolean isAfterFixedReadPhase() {
951         return isAfterFixedReadPhase;
952     }
953 
954     public void setAfterFloatingReadPhase(boolean state) {
955         assert state : "cannot 'unapply' floating read phase on graph";
956         isAfterFloatingReadPhase = state;
957     }
958 
959     public void setAfterFixReadPhase(boolean state) {
960         assert state : "cannot 'unapply' fix reads phase on graph";
961         isAfterFixedReadPhase = state;
962     }
963 
964     public boolean hasValueProxies() {
965         return hasValueProxies;
966     }
967 
968     public void setHasValueProxies(boolean state) {
969         assert !state : "cannot 'unapply' value proxy removal on graph";
970         hasValueProxies = state;
971     }
972 
973     public boolean isAfterExpandLogic() {
974         return isAfterExpandLogic;
975     }
976 
977     public void setAfterExpandLogic() {
978         isAfterExpandLogic = true;
979     }
980 
981     /**
982      * Determines if {@link ProfilingInfo} is used during construction of this graph.
983      */
984     public boolean useProfilingInfo() {
985         return useProfilingInfo;
986     }
987 
988     /**
989      * Returns true if this graph is built without parsing the {@linkplain #method() root method} or
990      * if the root method is annotated by {@link Snippet} or {@link MethodSubstitution}. This is
991      * preferred over querying annotations directly as querying annotations can cause class loading.
992      */
993     public boolean isSubstitution() {
994         return isSubstitution;
995     }
996 
997     /**
998      * Gets the profiling info for the {@linkplain #method() root method} of this graph.
999      */
1000     public ProfilingInfo getProfilingInfo() {
1001         return getProfilingInfo(method());
1002     }
1003 
1004     /**
1005      * Gets the profiling info for a given method that is or will be part of this graph, taking into
1006      * account {@link #useProfilingInfo()}.
1007      */
1008     public ProfilingInfo getProfilingInfo(ResolvedJavaMethod m) {
1009         if (useProfilingInfo && m != null) {
1010             return m.getProfilingInfo();
1011         } else {
1012             return DefaultProfilingInfo.get(TriState.UNKNOWN);
1013         }
1014     }
1015 
1016     /**
1017      * Gets the object for recording assumptions while constructing of this graph.
1018      *
1019      * @return {@code null} if assumptions cannot be made for this graph
1020      */
1021     public Assumptions getAssumptions() {
1022         return assumptions;
1023     }
1024 
1025     /**
1026      * Returns the AllowAssumptions status for this graph.
1027      *
1028      * @return {@code AllowAssumptions.YES} if this graph allows recording assumptions,
1029      *         {@code AllowAssumptions.NO} otherwise
1030      */
1031     public AllowAssumptions allowAssumptions() {
1032         return AllowAssumptions.ifNonNull(assumptions);
1033     }
1034 
1035     /**
1036      * Checks that any method referenced from a {@link FrameState} is also in the set of methods
1037      * parsed while building this graph.
1038      */
1039     private boolean checkFrameStatesAgainstInlinedMethods() {
1040         for (FrameState fs : getNodes(FrameState.TYPE)) {
1041             if (!BytecodeFrame.isPlaceholderBci(fs.bci)) {
1042                 ResolvedJavaMethod m = fs.code.getMethod();
1043                 if (!m.equals(rootMethod) && !methods.contains(m)) {
1044                     SortedSet<String> haystack = new TreeSet<>();
1045                     if (!methods.contains(rootMethod)) {
1046                         haystack.add(rootMethod.format("%H.%n(%p)"));
1047                     }
1048                     for (ResolvedJavaMethod e : methods) {
1049                         haystack.add(e.format("%H.%n(%p)"));
1050                     }
1051                     throw new AssertionError(String.format("Could not find %s from %s in set(%s)", m.format("%H.%n(%p)"), fs, haystack.stream().collect(Collectors.joining(System.lineSeparator()))));
1052                 }
1053             }
1054         }
1055         return true;
1056     }
1057 
1058     private static EconomicSet<ResolvedJavaField> createFieldSet(EconomicSet<ResolvedJavaField> init) {
1059         // Multiple ResolvedJavaField objects can represent the same field so they
1060         // need to be compared with equals().
1061         if (init != null) {
1062             return EconomicSet.create(Equivalence.DEFAULT, init);
1063         }
1064         return EconomicSet.create(Equivalence.DEFAULT);
1065     }
1066 
1067     /**
1068      * Gets an unmodifiable view of the methods that were inlined while constructing this graph.
1069      */
1070     public List<ResolvedJavaMethod> getMethods() {
1071         if (methods != null) {
1072             assert isSubstitution || checkFrameStatesAgainstInlinedMethods();
1073             return Collections.unmodifiableList(methods);
1074         }
1075         return Collections.emptyList();
1076     }
1077 
1078     /**
1079      * Records that {@code method} was used to build this graph.
1080      */
1081     public void recordMethod(ResolvedJavaMethod method) {
1082         if (methods != null) {
1083             methods.add(method);
1084         }
1085     }
1086 
1087     /**
1088      * Updates the {@linkplain #getMethods() methods} used to build this graph with the methods used
1089      * to build another graph.
1090      */
1091     public void updateMethods(StructuredGraph other) {
1092         if (methods != null) {
1093             if (other.rootMethod != null) {
1094                 methods.add(other.rootMethod);
1095             }
1096             for (ResolvedJavaMethod m : other.methods) {
1097                 methods.add(m);
1098             }
1099         }
1100     }
1101 
1102     /**
1103      * Gets an unmodifiable view of the fields that were accessed while constructing this graph.
1104      *
1105      * @return {@code null} if no field accesses were recorded
1106      */
1107     public EconomicSet<ResolvedJavaField> getFields() {
1108         return fields;
1109     }
1110 
1111     /**
1112      * Records that {@code field} was accessed in this graph.
1113      */
1114     public void recordField(ResolvedJavaField field) {
1115         assert GraalOptions.GeneratePIC.getValue(getOptions());
1116         if (this.fields == null) {
1117             this.fields = createFieldSet(null);
1118         }
1119         fields.add(field);
1120     }
1121 
1122     /**
1123      * Updates the {@linkplain #getFields() fields} of this graph with the accessed fields of
1124      * another graph.
1125      */
1126     public void updateFields(StructuredGraph other) {
1127         assert this != other;
1128         assert GraalOptions.GeneratePIC.getValue(getOptions());
1129         if (other.fields != null) {
1130             if (this.fields == null) {
1131                 this.fields = createFieldSet(null);
1132             }
1133             this.fields.addAll(other.fields);
1134         }
1135     }
1136 
1137     /**
1138      * Gets the input bytecode {@linkplain ResolvedJavaMethod#getCodeSize() size} from which this
1139      * graph is constructed. This ignores how many bytecodes in each constituent method are actually
1140      * parsed (which may be none for methods whose IR is retrieved from a cache or less than the
1141      * full amount for any given method due to profile guided branch pruning).
1142      */
1143     public int getBytecodeSize() {
1144         int res = 0;
1145         if (rootMethod != null) {
1146             res += rootMethod.getCodeSize();
1147         }
1148         if (methods != null) {
1149             for (ResolvedJavaMethod e : methods) {
1150                 res += e.getCodeSize();
1151             }
1152         }
1153         return res;
1154     }
1155 
1156     @Override
1157     public JavaMethod asJavaMethod() {
1158         return method();
1159     }
1160 
1161     public boolean hasUnsafeAccess() {
1162         return hasUnsafeAccess == UnsafeAccessState.HAS_ACCESS;
1163     }
1164 
1165     public void markUnsafeAccess() {
1166         if (hasUnsafeAccess == UnsafeAccessState.DISABLED) {
1167             return;
1168         }
1169         hasUnsafeAccess = UnsafeAccessState.HAS_ACCESS;
1170     }
1171 
1172     public void disableUnsafeAccessTracking() {
1173         hasUnsafeAccess = UnsafeAccessState.DISABLED;
1174     }
1175 
1176     public boolean isUnsafeAccessTrackingEnabled() {
1177         return hasUnsafeAccess != UnsafeAccessState.DISABLED;
1178     }
1179 
1180     public SpeculationLog getSpeculationLog() {
1181         return speculationLog;
1182     }
1183 
1184     public FrameStateVerification getFrameStateVerification() {
1185         return frameStateVerification;
1186     }
1187 
1188     public void weakenFrameStateVerification(FrameStateVerification newFrameStateVerification) {
1189         if (frameStateVerification == FrameStateVerification.NONE) {
1190             return;
1191         }
1192         if (newFrameStateVerification == FrameStateVerification.NONE) {
1193             /*
1194              * Unit tests and substitution graphs can disable verification early on in the
1195              * compilation pipeline.
1196              */
1197             frameStateVerification = FrameStateVerification.NONE;
1198             return;
1199         }
1200         assert frameStateVerification.ordinal() < newFrameStateVerification.ordinal() : "Old verification " + frameStateVerification + " must imply new verification " + newFrameStateVerification +
1201                         ", i.e., verification can only be relaxed over the course of compilation";
1202         frameStateVerification = newFrameStateVerification;
1203     }
1204 
1205     public void disableFrameStateVerification() {
1206         weakenFrameStateVerification(FrameStateVerification.NONE);
1207     }
1208 
1209     public void clearAllStateAfter() {
1210         weakenFrameStateVerification(FrameStateVerification.NONE);
1211         for (Node node : getNodes()) {
1212             if (node instanceof StateSplit) {
1213                 FrameState stateAfter = ((StateSplit) node).stateAfter();
1214                 if (stateAfter != null) {
1215                     ((StateSplit) node).setStateAfter(null);
1216                     // 2 nodes referencing the same frame state
1217                     if (stateAfter.isAlive()) {
1218                         GraphUtil.killWithUnusedFloatingInputs(stateAfter);
1219                     }
1220                 }
1221             }
1222         }
1223     }
1224 
1225     public boolean hasVirtualizableAllocation() {
1226         for (Node n : getNodes()) {
1227             if (n instanceof VirtualizableAllocation) {
1228                 return true;
1229             }
1230         }
1231         return false;
1232     }
1233 
1234     @Override
1235     protected void afterRegister(Node node) {
1236         assert hasValueProxies() || !(node instanceof ValueProxyNode);
1237         if (GraalOptions.TraceInlining.getValue(getOptions())) {
1238             if (node instanceof Invokable) {
1239                 ((Invokable) node).updateInliningLogAfterRegister(this);
1240             }
1241         }
1242     }
1243 
1244     public NodeSourcePosition getCallerContext() {
1245         return callerContext;
1246     }
1247 }
1248