1 /*
   2  * Copyright (c) 2011, 2015, 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 package org.graalvm.compiler.nodes;
  24 
  25 import java.util.ArrayList;
  26 import java.util.HashSet;
  27 import java.util.Iterator;
  28 import java.util.List;
  29 import java.util.Map;
  30 import java.util.Set;
  31 import java.util.concurrent.atomic.AtomicLong;
  32 import java.util.function.Consumer;
  33 
  34 import org.graalvm.compiler.core.common.CompilationIdentifier;
  35 import org.graalvm.compiler.core.common.cfg.BlockMap;
  36 import org.graalvm.compiler.core.common.type.Stamp;
  37 import org.graalvm.compiler.debug.JavaMethodContext;
  38 import org.graalvm.compiler.graph.Graph;
  39 import org.graalvm.compiler.graph.Node;
  40 import org.graalvm.compiler.graph.NodeMap;
  41 import org.graalvm.compiler.graph.spi.SimplifierTool;
  42 import org.graalvm.compiler.nodes.calc.FloatingNode;
  43 import org.graalvm.compiler.nodes.cfg.Block;
  44 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  45 import org.graalvm.compiler.nodes.java.MethodCallTargetNode;
  46 import org.graalvm.compiler.nodes.spi.VirtualizableAllocation;
  47 import org.graalvm.compiler.nodes.util.GraphUtil;
  48 
  49 import jdk.vm.ci.meta.Assumptions;
  50 import jdk.vm.ci.meta.Assumptions.Assumption;
  51 import jdk.vm.ci.meta.DefaultProfilingInfo;
  52 import jdk.vm.ci.meta.JavaMethod;
  53 import jdk.vm.ci.meta.ProfilingInfo;
  54 import jdk.vm.ci.meta.ResolvedJavaField;
  55 import jdk.vm.ci.meta.ResolvedJavaMethod;
  56 import jdk.vm.ci.meta.SpeculationLog;
  57 import jdk.vm.ci.meta.TriState;
  58 import jdk.vm.ci.runtime.JVMCICompiler;
  59 
  60 /**
  61  * A graph that contains at least one distinguished node : the {@link #start() start} node. This
  62  * node is the start of the control flow of the graph.
  63  */
  64 public class StructuredGraph extends Graph implements JavaMethodContext {
  65 
  66     /**
  67      * The different stages of the compilation of a {@link Graph} regarding the status of
  68      * {@link GuardNode guards}, {@link DeoptimizingNode deoptimizations} and {@link FrameState
  69      * framestates}. The stage of a graph progresses monotonously.
  70      *
  71      */
  72     public enum GuardsStage {
  73         /**
  74          * During this stage, there can be {@link FloatingNode floating} {@link DeoptimizingNode}
  75          * such as {@link GuardNode GuardNodes}. New {@link DeoptimizingNode DeoptimizingNodes} can
  76          * be introduced without constraints. {@link FrameState} nodes are associated with
  77          * {@link StateSplit} nodes.
  78          */
  79         FLOATING_GUARDS,
  80         /**
  81          * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be
  82          * {@link FixedNode fixed} but new {@link DeoptimizingNode DeoptimizingNodes} can still be
  83          * introduced. {@link FrameState} nodes are still associated with {@link StateSplit} nodes.
  84          */
  85         FIXED_DEOPTS,
  86         /**
  87          * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be
  88          * {@link FixedNode fixed}. New {@link DeoptimizingNode DeoptimizingNodes} can not be
  89          * introduced any more. {@link FrameState} nodes are now associated with
  90          * {@link DeoptimizingNode} nodes.
  91          */
  92         AFTER_FSA;
  93 
  94         public boolean allowsFloatingGuards() {
  95             return this == FLOATING_GUARDS;
  96         }
  97 
  98         public boolean areFrameStatesAtDeopts() {
  99             return this == AFTER_FSA;
 100         }
 101 
 102         public boolean areFrameStatesAtSideEffects() {
 103             return !this.areFrameStatesAtDeopts();
 104         }
 105 
 106         public boolean areDeoptsFixed() {
 107             return this.ordinal() >= FIXED_DEOPTS.ordinal();
 108         }
 109     }
 110 
 111     /**
 112      * Constants denoting whether or not {@link Assumption}s can be made while processing a graph.
 113      */
 114     public enum AllowAssumptions {
 115         YES,
 116         NO;
 117         public static AllowAssumptions from(boolean flag) {
 118             return flag ? YES : NO;
 119         }
 120     }
 121 
 122     public static class ScheduleResult {
 123         private final ControlFlowGraph cfg;
 124         private final NodeMap<Block> nodeToBlockMap;
 125         private final BlockMap<List<Node>> blockToNodesMap;
 126 
 127         public ScheduleResult(ControlFlowGraph cfg, NodeMap<Block> nodeToBlockMap, BlockMap<List<Node>> blockToNodesMap) {
 128             this.cfg = cfg;
 129             this.nodeToBlockMap = nodeToBlockMap;
 130             this.blockToNodesMap = blockToNodesMap;
 131         }
 132 
 133         public ControlFlowGraph getCFG() {
 134             return cfg;
 135         }
 136 
 137         public NodeMap<Block> getNodeToBlockMap() {
 138             return nodeToBlockMap;
 139         }
 140 
 141         public BlockMap<List<Node>> getBlockToNodesMap() {
 142             return blockToNodesMap;
 143         }
 144 
 145         public List<Node> nodesFor(Block block) {
 146             return blockToNodesMap.get(block);
 147         }
 148     }
 149 
 150     public static final long INVALID_GRAPH_ID = -1;
 151     private static final AtomicLong uniqueGraphIds = new AtomicLong();
 152 
 153     private StartNode start;
 154     private ResolvedJavaMethod rootMethod;
 155     private final long graphId;
 156     private final CompilationIdentifier compilationId;
 157     private final int entryBCI;
 158     private GuardsStage guardsStage = GuardsStage.FLOATING_GUARDS;
 159     private boolean isAfterFloatingReadPhase = false;
 160     private boolean hasValueProxies = true;
 161     private final boolean useProfilingInfo;
 162 
 163     /**
 164      * The assumptions made while constructing and transforming this graph.
 165      */
 166     private final Assumptions assumptions;
 167 
 168     private final SpeculationLog speculationLog;
 169 
 170     private ScheduleResult lastSchedule;
 171 
 172     /**
 173      * Records the methods that were used while constructing this graph, one entry for each time a
 174      * specific method is used.
 175      */
 176     private final List<ResolvedJavaMethod> methods = new ArrayList<>();
 177 
 178     /**
 179      * Records the fields that were accessed while constructing this graph.
 180      */
 181 
 182     private final Set<ResolvedJavaField> fields = new HashSet<>();
 183 
 184     private enum UnsafeAccessState {
 185         NO_ACCESS,
 186         HAS_ACCESS,
 187         DISABLED
 188     }
 189 
 190     private UnsafeAccessState hasUnsafeAccess = UnsafeAccessState.NO_ACCESS;
 191 
 192     /**
 193      * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start()
 194      * start} node.
 195      */
 196     public StructuredGraph(AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) {
 197         this(null, null, allowAssumptions, compilationId);
 198     }
 199 
 200     public static final boolean USE_PROFILING_INFO = true;
 201 
 202     public static final boolean NO_PROFILING_INFO = false;
 203 
 204     private static final SpeculationLog NO_SPECULATION_LOG = null;
 205 
 206     /**
 207      * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start()
 208      * start} node.
 209      */
 210     public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) {
 211         this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, USE_PROFILING_INFO, compilationId);
 212     }
 213 
 214     public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) {
 215         this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId);
 216     }
 217 
 218     public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo, CompilationIdentifier compilationId) {
 219         this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, useProfilingInfo, compilationId);
 220     }
 221 
 222     public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) {
 223         this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, USE_PROFILING_INFO, compilationId);
 224     }
 225 
 226     public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, boolean useProfilingInfo, CompilationIdentifier compilationId) {
 227         this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, useProfilingInfo, compilationId);
 228     }
 229 
 230     public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) {
 231         this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId);
 232     }
 233 
 234     public StructuredGraph(ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) {
 235         this(null, method, entryBCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId);
 236     }
 237 
 238     public StructuredGraph(ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo, CompilationIdentifier compilationId) {
 239         this(null, method, entryBCI, allowAssumptions, speculationLog, useProfilingInfo, compilationId);
 240     }
 241 
 242     private StructuredGraph(String name, ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo,
 243                     CompilationIdentifier compilationId) {
 244         super(name);
 245         this.setStart(add(new StartNode()));
 246         this.rootMethod = method;
 247         this.graphId = uniqueGraphIds.incrementAndGet();
 248         this.compilationId = compilationId;
 249         this.entryBCI = entryBCI;
 250         this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null;
 251         this.speculationLog = speculationLog;
 252         this.useProfilingInfo = useProfilingInfo;
 253     }
 254 
 255     public void setLastSchedule(ScheduleResult result) {
 256         lastSchedule = result;
 257     }
 258 
 259     public ScheduleResult getLastSchedule() {
 260         return lastSchedule;
 261     }
 262 
 263     public void clearLastSchedule() {
 264         setLastSchedule(null);
 265     }
 266 
 267     @Override
 268     public boolean maybeCompress() {
 269         if (super.maybeCompress()) {
 270             /*
 271              * The schedule contains a NodeMap which is unusable after compression.
 272              */
 273             clearLastSchedule();
 274             return true;
 275         }
 276         return false;
 277     }
 278 
 279     public Stamp getReturnStamp() {
 280         Stamp returnStamp = null;
 281         for (ReturnNode returnNode : getNodes(ReturnNode.TYPE)) {
 282             ValueNode result = returnNode.result();
 283             if (result != null) {
 284                 if (returnStamp == null) {
 285                     returnStamp = result.stamp();
 286                 } else {
 287                     returnStamp = returnStamp.meet(result.stamp());
 288                 }
 289             }
 290         }
 291         return returnStamp;
 292     }
 293 
 294     @Override
 295     public String toString() {
 296         StringBuilder buf = new StringBuilder(getClass().getSimpleName() + ":" + graphId);
 297         String sep = "{";
 298         if (name != null) {
 299             buf.append(sep);
 300             buf.append(name);
 301             sep = ", ";
 302         }
 303         if (method() != null) {
 304             buf.append(sep);
 305             buf.append(method());
 306             sep = ", ";
 307         }
 308 
 309         if (!sep.equals("{")) {
 310             buf.append("}");
 311         }
 312         return buf.toString();
 313     }
 314 
 315     public StartNode start() {
 316         return start;
 317     }
 318 
 319     /**
 320      * Gets the root method from which this graph was built.
 321      *
 322      * @return null if this method was not built from a method or the method is not available
 323      */
 324     public ResolvedJavaMethod method() {
 325         return rootMethod;
 326     }
 327 
 328     public int getEntryBCI() {
 329         return entryBCI;
 330     }
 331 
 332     public boolean isOSR() {
 333         return entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI;
 334     }
 335 
 336     public long graphId() {
 337         return graphId;
 338     }
 339 
 340     /**
 341      * @see CompilationIdentifier
 342      */
 343     public CompilationIdentifier compilationId() {
 344         return compilationId;
 345     }
 346 
 347     public void setStart(StartNode start) {
 348         this.start = start;
 349     }
 350 
 351     /**
 352      * Creates a copy of this graph.
 353      *
 354      * @param newName the name of the copy, used for debugging purposes (can be null)
 355      * @param duplicationMapCallback consumer of the duplication map created during the copying
 356      */
 357     @Override
 358     protected Graph copy(String newName, Consumer<Map<Node, Node>> duplicationMapCallback) {
 359         return copy(newName, duplicationMapCallback, compilationId);
 360     }
 361 
 362     private StructuredGraph copy(String newName, Consumer<Map<Node, Node>> duplicationMapCallback, CompilationIdentifier newCompilationId) {
 363         AllowAssumptions allowAssumptions = AllowAssumptions.from(assumptions != null);
 364         StructuredGraph copy = new StructuredGraph(newName, method(), entryBCI, allowAssumptions, speculationLog, useProfilingInfo, newCompilationId);
 365         if (allowAssumptions == AllowAssumptions.YES && assumptions != null) {
 366             copy.assumptions.record(assumptions);
 367         }
 368         copy.hasUnsafeAccess = hasUnsafeAccess;
 369         copy.setGuardsStage(getGuardsStage());
 370         copy.isAfterFloatingReadPhase = isAfterFloatingReadPhase;
 371         copy.hasValueProxies = hasValueProxies;
 372         Map<Node, Node> replacements = Node.newMap();
 373         replacements.put(start, copy.start);
 374         Map<Node, Node> duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), replacements);
 375         if (duplicationMapCallback != null) {
 376             duplicationMapCallback.accept(duplicates);
 377         }
 378         return copy;
 379     }
 380 
 381     public final StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId) {
 382         return copy(name, null, newCompilationId);
 383     }
 384 
 385     public ParameterNode getParameter(int index) {
 386         for (ParameterNode param : getNodes(ParameterNode.TYPE)) {
 387             if (param.index() == index) {
 388                 return param;
 389             }
 390         }
 391         return null;
 392     }
 393 
 394     public Iterable<Invoke> getInvokes() {
 395         final Iterator<MethodCallTargetNode> callTargets = getNodes(MethodCallTargetNode.TYPE).iterator();
 396         return new Iterable<Invoke>() {
 397 
 398             private Invoke next;
 399 
 400             @Override
 401             public Iterator<Invoke> iterator() {
 402                 return new Iterator<Invoke>() {
 403 
 404                     @Override
 405                     public boolean hasNext() {
 406                         if (next == null) {
 407                             while (callTargets.hasNext()) {
 408                                 Invoke i = callTargets.next().invoke();
 409                                 if (i != null) {
 410                                     next = i;
 411                                     return true;
 412                                 }
 413                             }
 414                             return false;
 415                         } else {
 416                             return true;
 417                         }
 418                     }
 419 
 420                     @Override
 421                     public Invoke next() {
 422                         try {
 423                             return next;
 424                         } finally {
 425                             next = null;
 426                         }
 427                     }
 428 
 429                     @Override
 430                     public void remove() {
 431                         throw new UnsupportedOperationException();
 432                     }
 433                 };
 434             }
 435         };
 436     }
 437 
 438     public boolean hasLoops() {
 439         return hasNode(LoopBeginNode.TYPE);
 440     }
 441 
 442     /**
 443      * Unlinks a node from all its control flow neighbors and then removes it from its graph. The
 444      * node must have no {@linkplain Node#usages() usages}.
 445      *
 446      * @param node the node to be unlinked and removed
 447      */
 448     public void removeFixed(FixedWithNextNode node) {
 449         assert node != null;
 450         if (node instanceof AbstractBeginNode) {
 451             ((AbstractBeginNode) node).prepareDelete();
 452         }
 453         assert node.hasNoUsages() : node + " " + node.usages().count() + ", " + node.usages().first();
 454         GraphUtil.unlinkFixedNode(node);
 455         node.safeDelete();
 456     }
 457 
 458     public void replaceFixed(FixedWithNextNode node, Node replacement) {
 459         if (replacement instanceof FixedWithNextNode) {
 460             replaceFixedWithFixed(node, (FixedWithNextNode) replacement);
 461         } else {
 462             assert replacement != null : "cannot replace " + node + " with null";
 463             assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement;
 464             replaceFixedWithFloating(node, (FloatingNode) replacement);
 465         }
 466     }
 467 
 468     public void replaceFixedWithFixed(FixedWithNextNode node, FixedWithNextNode replacement) {
 469         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
 470         FixedNode next = node.next();
 471         node.setNext(null);
 472         replacement.setNext(next);
 473         node.replaceAndDelete(replacement);
 474         if (node == start) {
 475             setStart((StartNode) replacement);
 476         }
 477     }
 478 
 479     public void replaceFixedWithFloating(FixedWithNextNode node, FloatingNode replacement) {
 480         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
 481         GraphUtil.unlinkFixedNode(node);
 482         node.replaceAtUsagesAndDelete(replacement);
 483     }
 484 
 485     public void removeSplit(ControlSplitNode node, AbstractBeginNode survivingSuccessor) {
 486         assert node != null;
 487         assert node.hasNoUsages();
 488         assert survivingSuccessor != null;
 489         node.clearSuccessors();
 490         node.replaceAtPredecessor(survivingSuccessor);
 491         node.safeDelete();
 492     }
 493 
 494     public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) {
 495         removeSplitPropagate(node, survivingSuccessor, null);
 496     }
 497 
 498     public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor, SimplifierTool tool) {
 499         assert node != null;
 500         assert node.hasNoUsages();
 501         assert survivingSuccessor != null;
 502         List<Node> snapshot = node.successors().snapshot();
 503         node.clearSuccessors();
 504         node.replaceAtPredecessor(survivingSuccessor);
 505         node.safeDelete();
 506         for (Node successor : snapshot) {
 507             if (successor != null && successor.isAlive()) {
 508                 if (successor != survivingSuccessor) {
 509                     GraphUtil.killCFG((FixedNode) successor, tool);
 510                 }
 511             }
 512         }
 513     }
 514 
 515     public void replaceSplit(ControlSplitNode node, Node replacement, AbstractBeginNode survivingSuccessor) {
 516         if (replacement instanceof FixedWithNextNode) {
 517             replaceSplitWithFixed(node, (FixedWithNextNode) replacement, survivingSuccessor);
 518         } else {
 519             assert replacement != null : "cannot replace " + node + " with null";
 520             assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement;
 521             replaceSplitWithFloating(node, (FloatingNode) replacement, survivingSuccessor);
 522         }
 523     }
 524 
 525     public void replaceSplitWithFixed(ControlSplitNode node, FixedWithNextNode replacement, AbstractBeginNode survivingSuccessor) {
 526         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
 527         assert survivingSuccessor != null;
 528         node.clearSuccessors();
 529         replacement.setNext(survivingSuccessor);
 530         node.replaceAndDelete(replacement);
 531     }
 532 
 533     public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, AbstractBeginNode survivingSuccessor) {
 534         assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement;
 535         assert survivingSuccessor != null;
 536         node.clearSuccessors();
 537         node.replaceAtPredecessor(survivingSuccessor);
 538         node.replaceAtUsagesAndDelete(replacement);
 539     }
 540 
 541     public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) {
 542         assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node;
 543         FixedNode next = node.next();
 544         node.setNext(newNode);
 545         if (next != null) {
 546             assert newNode instanceof FixedWithNextNode;
 547             FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode;
 548             assert newFixedWithNext.next() == null;
 549             newFixedWithNext.setNext(next);
 550         }
 551     }
 552 
 553     public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) {
 554         assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " before " + node;
 555         assert node.predecessor() != null && node.predecessor() instanceof FixedWithNextNode : "cannot add " + newNode + " before " + node;
 556         assert newNode.next() == null : newNode;
 557         assert !(node instanceof AbstractMergeNode);
 558         FixedWithNextNode pred = (FixedWithNextNode) node.predecessor();
 559         pred.setNext(newNode);
 560         newNode.setNext(node);
 561     }
 562 
 563     public void reduceDegenerateLoopBegin(LoopBeginNode begin) {
 564         assert begin.loopEnds().isEmpty() : "Loop begin still has backedges";
 565         if (begin.forwardEndCount() == 1) { // bypass merge and remove
 566             reduceTrivialMerge(begin);
 567         } else { // convert to merge
 568             AbstractMergeNode merge = this.add(new MergeNode());
 569             for (EndNode end : begin.forwardEnds()) {
 570                 merge.addForwardEnd(end);
 571             }
 572             this.replaceFixedWithFixed(begin, merge);
 573         }
 574     }
 575 
 576     public void reduceTrivialMerge(AbstractMergeNode merge) {
 577         assert merge.forwardEndCount() == 1;
 578         assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty();
 579         for (PhiNode phi : merge.phis().snapshot()) {
 580             assert phi.valueCount() == 1;
 581             ValueNode singleValue = phi.valueAt(0);
 582             if (phi.hasUsages()) {
 583                 phi.replaceAtUsagesAndDelete(singleValue);
 584             } else {
 585                 phi.safeDelete();
 586                 if (singleValue != null) {
 587                     GraphUtil.tryKillUnused(singleValue);
 588                 }
 589             }
 590         }
 591         // remove loop exits
 592         if (merge instanceof LoopBeginNode) {
 593             ((LoopBeginNode) merge).removeExits();
 594         }
 595         AbstractEndNode singleEnd = merge.forwardEndAt(0);
 596         FixedNode sux = merge.next();
 597         FrameState stateAfter = merge.stateAfter();
 598         // evacuateGuards
 599         merge.prepareDelete((FixedNode) singleEnd.predecessor());
 600         merge.safeDelete();
 601         if (stateAfter != null && stateAfter.isAlive() && stateAfter.hasNoUsages()) {
 602             GraphUtil.killWithUnusedFloatingInputs(stateAfter);
 603         }
 604         if (sux == null) {
 605             singleEnd.replaceAtPredecessor(null);
 606             singleEnd.safeDelete();
 607         } else {
 608             singleEnd.replaceAndDelete(sux);
 609         }
 610     }
 611 
 612     public GuardsStage getGuardsStage() {
 613         return guardsStage;
 614     }
 615 
 616     public void setGuardsStage(GuardsStage guardsStage) {
 617         assert guardsStage.ordinal() >= this.guardsStage.ordinal();
 618         this.guardsStage = guardsStage;
 619     }
 620 
 621     public boolean isAfterFloatingReadPhase() {
 622         return isAfterFloatingReadPhase;
 623     }
 624 
 625     public void setAfterFloatingReadPhase(boolean state) {
 626         assert state : "cannot 'unapply' floating read phase on graph";
 627         isAfterFloatingReadPhase = state;
 628     }
 629 
 630     public boolean hasValueProxies() {
 631         return hasValueProxies;
 632     }
 633 
 634     public void setHasValueProxies(boolean state) {
 635         assert !state : "cannot 'unapply' value proxy removal on graph";
 636         hasValueProxies = state;
 637     }
 638 
 639     /**
 640      * Determines if {@link ProfilingInfo} is used during construction of this graph.
 641      */
 642     public boolean useProfilingInfo() {
 643         return useProfilingInfo;
 644     }
 645 
 646     /**
 647      * Gets the profiling info for the {@linkplain #method() root method} of this graph.
 648      */
 649     public ProfilingInfo getProfilingInfo() {
 650         return getProfilingInfo(method());
 651     }
 652 
 653     /**
 654      * Gets the profiling info for a given method that is or will be part of this graph, taking into
 655      * account {@link #useProfilingInfo()}.
 656      */
 657     public ProfilingInfo getProfilingInfo(ResolvedJavaMethod m) {
 658         if (useProfilingInfo && m != null) {
 659             return m.getProfilingInfo();
 660         } else {
 661             return DefaultProfilingInfo.get(TriState.UNKNOWN);
 662         }
 663     }
 664 
 665     /**
 666      * Gets the object for recording assumptions while constructing of this graph.
 667      *
 668      * @return {@code null} if assumptions cannot be made for this graph
 669      */
 670     public Assumptions getAssumptions() {
 671         return assumptions;
 672     }
 673 
 674     /**
 675      * Gets the methods that were inlined while constructing this graph.
 676      */
 677     public List<ResolvedJavaMethod> getMethods() {
 678         return methods;
 679     }
 680 
 681     /**
 682      * Records that {@code method} was used to build this graph.
 683      */
 684     public void recordMethod(ResolvedJavaMethod method) {
 685         methods.add(method);
 686     }
 687 
 688     /**
 689      * Updates the {@linkplain #getMethods() methods} used to build this graph with the methods used
 690      * to build another graph.
 691      */
 692     public void updateMethods(StructuredGraph other) {
 693         assert this != other;
 694         this.methods.addAll(other.methods);
 695     }
 696 
 697     /**
 698      * Gets the fields that were accessed while constructing this graph.
 699      */
 700     public Set<ResolvedJavaField> getFields() {
 701         return fields;
 702     }
 703 
 704     /**
 705      * Records that {@code field} was accessed in this graph.
 706      */
 707     public void recordField(ResolvedJavaField field) {
 708         fields.add(field);
 709     }
 710 
 711     /**
 712      * Updates the {@linkplain #getFields() fields} of this graph with the accessed fields of
 713      * another graph.
 714      */
 715     public void updateFields(StructuredGraph other) {
 716         assert this != other;
 717         this.fields.addAll(other.fields);
 718     }
 719 
 720     /**
 721      * Gets the input bytecode {@linkplain ResolvedJavaMethod#getCodeSize() size} from which this
 722      * graph is constructed. This ignores how many bytecodes in each constituent method are actually
 723      * parsed (which may be none for methods whose IR is retrieved from a cache or less than the
 724      * full amount for any given method due to profile guided branch pruning).
 725      */
 726     public int getBytecodeSize() {
 727         int res = 0;
 728         for (ResolvedJavaMethod e : methods) {
 729             res += e.getCodeSize();
 730         }
 731         return res;
 732     }
 733 
 734     /**
 735      *
 736      * @return true if the graph contains only a {@link StartNode} and {@link ReturnNode}
 737      */
 738     public boolean isTrivial() {
 739         return !(start.next() instanceof ReturnNode);
 740     }
 741 
 742     @Override
 743     public JavaMethod asJavaMethod() {
 744         return method();
 745     }
 746 
 747     public boolean hasUnsafeAccess() {
 748         return hasUnsafeAccess == UnsafeAccessState.HAS_ACCESS;
 749     }
 750 
 751     public void markUnsafeAccess() {
 752         if (hasUnsafeAccess == UnsafeAccessState.DISABLED) {
 753             return;
 754         }
 755         hasUnsafeAccess = UnsafeAccessState.HAS_ACCESS;
 756     }
 757 
 758     public void disableUnsafeAccessTracking() {
 759         hasUnsafeAccess = UnsafeAccessState.DISABLED;
 760     }
 761 
 762     public boolean isUnsafeAccessTrackingEnabled() {
 763         return hasUnsafeAccess != UnsafeAccessState.DISABLED;
 764     }
 765 
 766     public SpeculationLog getSpeculationLog() {
 767         return speculationLog;
 768     }
 769 
 770     public final void clearAllStateAfter() {
 771         for (Node node : getNodes()) {
 772             if (node instanceof StateSplit) {
 773                 FrameState stateAfter = ((StateSplit) node).stateAfter();
 774                 if (stateAfter != null) {
 775                     ((StateSplit) node).setStateAfter(null);
 776                     // 2 nodes referencing the same framestate
 777                     if (stateAfter.isAlive()) {
 778                         GraphUtil.killWithUnusedFloatingInputs(stateAfter);
 779                     }
 780                 }
 781             }
 782         }
 783     }
 784 
 785     public final boolean hasVirtualizableAllocation() {
 786         for (Node n : getNodes()) {
 787             if (n instanceof VirtualizableAllocation) {
 788                 return true;
 789             }
 790         }
 791         return false;
 792     }
 793 }