--- /dev/null 2016-05-31 09:42:47.975716356 -0700 +++ new/src/jdk.vm.compiler/share/classes/org.graalvm.compiler.nodes/src/org/graalvm/compiler/nodes/StructuredGraph.java 2016-12-09 00:55:09.306299852 -0800 @@ -0,0 +1,783 @@ +/* + * Copyright (c) 2011, 2015, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ +package org.graalvm.compiler.nodes; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.atomic.AtomicLong; +import java.util.function.Consumer; + +import org.graalvm.compiler.core.common.CompilationIdentifier; +import org.graalvm.compiler.core.common.cfg.BlockMap; +import org.graalvm.compiler.core.common.type.Stamp; +import org.graalvm.compiler.debug.JavaMethodContext; +import org.graalvm.compiler.graph.Graph; +import org.graalvm.compiler.graph.Node; +import org.graalvm.compiler.graph.NodeMap; +import org.graalvm.compiler.graph.spi.SimplifierTool; +import org.graalvm.compiler.nodes.calc.FloatingNode; +import org.graalvm.compiler.nodes.cfg.Block; +import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; +import org.graalvm.compiler.nodes.java.MethodCallTargetNode; +import org.graalvm.compiler.nodes.spi.VirtualizableAllocation; +import org.graalvm.compiler.nodes.util.GraphUtil; + +import jdk.vm.ci.meta.Assumptions; +import jdk.vm.ci.meta.Assumptions.Assumption; +import jdk.vm.ci.meta.DefaultProfilingInfo; +import jdk.vm.ci.meta.JavaMethod; +import jdk.vm.ci.meta.ProfilingInfo; +import jdk.vm.ci.meta.ResolvedJavaField; +import jdk.vm.ci.meta.ResolvedJavaMethod; +import jdk.vm.ci.meta.SpeculationLog; +import jdk.vm.ci.meta.TriState; +import jdk.vm.ci.runtime.JVMCICompiler; + +/** + * A graph that contains at least one distinguished node : the {@link #start() start} node. This + * node is the start of the control flow of the graph. + */ +public class StructuredGraph extends Graph implements JavaMethodContext { + + /** + * The different stages of the compilation of a {@link Graph} regarding the status of + * {@link GuardNode guards}, {@link DeoptimizingNode deoptimizations} and {@link FrameState + * framestates}. The stage of a graph progresses monotonously. + * + */ + public enum GuardsStage { + /** + * During this stage, there can be {@link FloatingNode floating} {@link DeoptimizingNode} + * such as {@link GuardNode GuardNodes}. New {@link DeoptimizingNode DeoptimizingNodes} can + * be introduced without constraints. {@link FrameState} nodes are associated with + * {@link StateSplit} nodes. + */ + FLOATING_GUARDS, + /** + * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be + * {@link FixedNode fixed} but new {@link DeoptimizingNode DeoptimizingNodes} can still be + * introduced. {@link FrameState} nodes are still associated with {@link StateSplit} nodes. + */ + FIXED_DEOPTS, + /** + * During this stage, all {@link DeoptimizingNode DeoptimizingNodes} must be + * {@link FixedNode fixed}. New {@link DeoptimizingNode DeoptimizingNodes} can not be + * introduced any more. {@link FrameState} nodes are now associated with + * {@link DeoptimizingNode} nodes. + */ + AFTER_FSA; + + public boolean allowsFloatingGuards() { + return this == FLOATING_GUARDS; + } + + public boolean areFrameStatesAtDeopts() { + return this == AFTER_FSA; + } + + public boolean areFrameStatesAtSideEffects() { + return !this.areFrameStatesAtDeopts(); + } + + public boolean areDeoptsFixed() { + return this.ordinal() >= FIXED_DEOPTS.ordinal(); + } + } + + /** + * Constants denoting whether or not {@link Assumption}s can be made while processing a graph. + */ + public enum AllowAssumptions { + YES, + NO; + public static AllowAssumptions from(boolean flag) { + return flag ? YES : NO; + } + } + + public static class ScheduleResult { + private final ControlFlowGraph cfg; + private final NodeMap nodeToBlockMap; + private final BlockMap> blockToNodesMap; + + public ScheduleResult(ControlFlowGraph cfg, NodeMap nodeToBlockMap, BlockMap> blockToNodesMap) { + this.cfg = cfg; + this.nodeToBlockMap = nodeToBlockMap; + this.blockToNodesMap = blockToNodesMap; + } + + public ControlFlowGraph getCFG() { + return cfg; + } + + public NodeMap getNodeToBlockMap() { + return nodeToBlockMap; + } + + public BlockMap> getBlockToNodesMap() { + return blockToNodesMap; + } + + public List nodesFor(Block block) { + return blockToNodesMap.get(block); + } + } + + public static final long INVALID_GRAPH_ID = -1; + private static final AtomicLong uniqueGraphIds = new AtomicLong(); + + private StartNode start; + private ResolvedJavaMethod rootMethod; + private final long graphId; + private final CompilationIdentifier compilationId; + private final int entryBCI; + private GuardsStage guardsStage = GuardsStage.FLOATING_GUARDS; + private boolean isAfterFloatingReadPhase = false; + private boolean hasValueProxies = true; + private final boolean useProfilingInfo; + + /** + * The assumptions made while constructing and transforming this graph. + */ + private final Assumptions assumptions; + + private final SpeculationLog speculationLog; + + private ScheduleResult lastSchedule; + + /** + * Records the methods that were used while constructing this graph, one entry for each time a + * specific method is used. + */ + private final List methods = new ArrayList<>(); + + /** + * Records the fields that were accessed while constructing this graph. + */ + + private final Set fields = new HashSet<>(); + + private enum UnsafeAccessState { + NO_ACCESS, + HAS_ACCESS, + DISABLED + } + + private UnsafeAccessState hasUnsafeAccess = UnsafeAccessState.NO_ACCESS; + + /** + * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start() + * start} node. + */ + public StructuredGraph(AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) { + this(null, null, allowAssumptions, compilationId); + } + + public static final boolean USE_PROFILING_INFO = true; + + public static final boolean NO_PROFILING_INFO = false; + + private static final SpeculationLog NO_SPECULATION_LOG = null; + + /** + * Creates a new Graph containing a single {@link AbstractBeginNode} as the {@link #start() + * start} node. + */ + public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) { + this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, USE_PROFILING_INFO, compilationId); + } + + public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) { + this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId); + } + + public StructuredGraph(String name, ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo, CompilationIdentifier compilationId) { + this(name, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, useProfilingInfo, compilationId); + } + + public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, CompilationIdentifier compilationId) { + this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, USE_PROFILING_INFO, compilationId); + } + + public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, boolean useProfilingInfo, CompilationIdentifier compilationId) { + this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, NO_SPECULATION_LOG, useProfilingInfo, compilationId); + } + + public StructuredGraph(ResolvedJavaMethod method, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) { + this(null, method, JVMCICompiler.INVOCATION_ENTRY_BCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId); + } + + public StructuredGraph(ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, CompilationIdentifier compilationId) { + this(null, method, entryBCI, allowAssumptions, speculationLog, USE_PROFILING_INFO, compilationId); + } + + public StructuredGraph(ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo, CompilationIdentifier compilationId) { + this(null, method, entryBCI, allowAssumptions, speculationLog, useProfilingInfo, compilationId); + } + + private StructuredGraph(String name, ResolvedJavaMethod method, int entryBCI, AllowAssumptions allowAssumptions, SpeculationLog speculationLog, boolean useProfilingInfo, + CompilationIdentifier compilationId) { + super(name); + this.setStart(add(new StartNode())); + this.rootMethod = method; + this.graphId = uniqueGraphIds.incrementAndGet(); + this.compilationId = compilationId; + this.entryBCI = entryBCI; + this.assumptions = allowAssumptions == AllowAssumptions.YES ? new Assumptions() : null; + this.speculationLog = speculationLog; + this.useProfilingInfo = useProfilingInfo; + } + + public void setLastSchedule(ScheduleResult result) { + lastSchedule = result; + } + + public ScheduleResult getLastSchedule() { + return lastSchedule; + } + + public void clearLastSchedule() { + setLastSchedule(null); + } + + @Override + public boolean maybeCompress() { + if (super.maybeCompress()) { + /* + * The schedule contains a NodeMap which is unusable after compression. + */ + clearLastSchedule(); + return true; + } + return false; + } + + public Stamp getReturnStamp() { + Stamp returnStamp = null; + for (ReturnNode returnNode : getNodes(ReturnNode.TYPE)) { + ValueNode result = returnNode.result(); + if (result != null) { + if (returnStamp == null) { + returnStamp = result.stamp(); + } else { + returnStamp = returnStamp.meet(result.stamp()); + } + } + } + return returnStamp; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(getClass().getSimpleName() + ":" + graphId); + String sep = "{"; + if (name != null) { + buf.append(sep); + buf.append(name); + sep = ", "; + } + if (method() != null) { + buf.append(sep); + buf.append(method()); + sep = ", "; + } + + if (!sep.equals("{")) { + buf.append("}"); + } + return buf.toString(); + } + + public StartNode start() { + return start; + } + + /** + * Gets the root method from which this graph was built. + * + * @return null if this method was not built from a method or the method is not available + */ + public ResolvedJavaMethod method() { + return rootMethod; + } + + public int getEntryBCI() { + return entryBCI; + } + + public boolean isOSR() { + return entryBCI != JVMCICompiler.INVOCATION_ENTRY_BCI; + } + + public long graphId() { + return graphId; + } + + /** + * @see CompilationIdentifier + */ + public CompilationIdentifier compilationId() { + return compilationId; + } + + public void setStart(StartNode start) { + this.start = start; + } + + /** + * Creates a copy of this graph. + * + * @param newName the name of the copy, used for debugging purposes (can be null) + * @param duplicationMapCallback consumer of the duplication map created during the copying + */ + @Override + protected Graph copy(String newName, Consumer> duplicationMapCallback) { + return copy(newName, duplicationMapCallback, compilationId); + } + + private StructuredGraph copy(String newName, Consumer> duplicationMapCallback, CompilationIdentifier newCompilationId) { + AllowAssumptions allowAssumptions = AllowAssumptions.from(assumptions != null); + StructuredGraph copy = new StructuredGraph(newName, method(), entryBCI, allowAssumptions, speculationLog, useProfilingInfo, newCompilationId); + if (allowAssumptions == AllowAssumptions.YES && assumptions != null) { + copy.assumptions.record(assumptions); + } + copy.hasUnsafeAccess = hasUnsafeAccess; + copy.setGuardsStage(getGuardsStage()); + copy.isAfterFloatingReadPhase = isAfterFloatingReadPhase; + copy.hasValueProxies = hasValueProxies; + Map replacements = Node.newMap(); + replacements.put(start, copy.start); + Map duplicates = copy.addDuplicates(getNodes(), this, this.getNodeCount(), replacements); + if (duplicationMapCallback != null) { + duplicationMapCallback.accept(duplicates); + } + return copy; + } + + public final StructuredGraph copyWithIdentifier(CompilationIdentifier newCompilationId) { + return copy(name, null, newCompilationId); + } + + public ParameterNode getParameter(int index) { + for (ParameterNode param : getNodes(ParameterNode.TYPE)) { + if (param.index() == index) { + return param; + } + } + return null; + } + + public Iterable getInvokes() { + final Iterator callTargets = getNodes(MethodCallTargetNode.TYPE).iterator(); + return new Iterable() { + + private Invoke next; + + @Override + public Iterator iterator() { + return new Iterator() { + + @Override + public boolean hasNext() { + if (next == null) { + while (callTargets.hasNext()) { + Invoke i = callTargets.next().invoke(); + if (i != null) { + next = i; + return true; + } + } + return false; + } else { + return true; + } + } + + @Override + public Invoke next() { + try { + return next; + } finally { + next = null; + } + } + + @Override + public void remove() { + throw new UnsupportedOperationException(); + } + }; + } + }; + } + + public boolean hasLoops() { + return hasNode(LoopBeginNode.TYPE); + } + + /** + * Unlinks a node from all its control flow neighbors and then removes it from its graph. The + * node must have no {@linkplain Node#usages() usages}. + * + * @param node the node to be unlinked and removed + */ + public void removeFixed(FixedWithNextNode node) { + assert node != null; + if (node instanceof AbstractBeginNode) { + ((AbstractBeginNode) node).prepareDelete(); + } + assert node.hasNoUsages() : node + " " + node.usages().count() + ", " + node.usages().first(); + GraphUtil.unlinkFixedNode(node); + node.safeDelete(); + } + + public void replaceFixed(FixedWithNextNode node, Node replacement) { + if (replacement instanceof FixedWithNextNode) { + replaceFixedWithFixed(node, (FixedWithNextNode) replacement); + } else { + assert replacement != null : "cannot replace " + node + " with null"; + assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; + replaceFixedWithFloating(node, (FloatingNode) replacement); + } + } + + public void replaceFixedWithFixed(FixedWithNextNode node, FixedWithNextNode replacement) { + assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; + FixedNode next = node.next(); + node.setNext(null); + replacement.setNext(next); + node.replaceAndDelete(replacement); + if (node == start) { + setStart((StartNode) replacement); + } + } + + public void replaceFixedWithFloating(FixedWithNextNode node, FloatingNode replacement) { + assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; + GraphUtil.unlinkFixedNode(node); + node.replaceAtUsagesAndDelete(replacement); + } + + public void removeSplit(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { + assert node != null; + assert node.hasNoUsages(); + assert survivingSuccessor != null; + node.clearSuccessors(); + node.replaceAtPredecessor(survivingSuccessor); + node.safeDelete(); + } + + public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor) { + removeSplitPropagate(node, survivingSuccessor, null); + } + + public void removeSplitPropagate(ControlSplitNode node, AbstractBeginNode survivingSuccessor, SimplifierTool tool) { + assert node != null; + assert node.hasNoUsages(); + assert survivingSuccessor != null; + List snapshot = node.successors().snapshot(); + node.clearSuccessors(); + node.replaceAtPredecessor(survivingSuccessor); + node.safeDelete(); + for (Node successor : snapshot) { + if (successor != null && successor.isAlive()) { + if (successor != survivingSuccessor) { + GraphUtil.killCFG(successor, tool); + } + } + } + } + + public void replaceSplit(ControlSplitNode node, Node replacement, AbstractBeginNode survivingSuccessor) { + if (replacement instanceof FixedWithNextNode) { + replaceSplitWithFixed(node, (FixedWithNextNode) replacement, survivingSuccessor); + } else { + assert replacement != null : "cannot replace " + node + " with null"; + assert replacement instanceof FloatingNode : "cannot replace " + node + " with " + replacement; + replaceSplitWithFloating(node, (FloatingNode) replacement, survivingSuccessor); + } + } + + public void replaceSplitWithFixed(ControlSplitNode node, FixedWithNextNode replacement, AbstractBeginNode survivingSuccessor) { + assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; + assert survivingSuccessor != null; + node.clearSuccessors(); + replacement.setNext(survivingSuccessor); + node.replaceAndDelete(replacement); + } + + public void replaceSplitWithFloating(ControlSplitNode node, FloatingNode replacement, AbstractBeginNode survivingSuccessor) { + assert node != null && replacement != null && node.isAlive() && replacement.isAlive() : "cannot replace " + node + " with " + replacement; + assert survivingSuccessor != null; + node.clearSuccessors(); + node.replaceAtPredecessor(survivingSuccessor); + node.replaceAtUsagesAndDelete(replacement); + } + + public void addAfterFixed(FixedWithNextNode node, FixedNode newNode) { + assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " after " + node; + FixedNode next = node.next(); + node.setNext(newNode); + if (next != null) { + assert newNode instanceof FixedWithNextNode; + FixedWithNextNode newFixedWithNext = (FixedWithNextNode) newNode; + assert newFixedWithNext.next() == null; + newFixedWithNext.setNext(next); + } + } + + public void addBeforeFixed(FixedNode node, FixedWithNextNode newNode) { + assert node != null && newNode != null && node.isAlive() && newNode.isAlive() : "cannot add " + newNode + " before " + node; + assert node.predecessor() != null && node.predecessor() instanceof FixedWithNextNode : "cannot add " + newNode + " before " + node; + assert newNode.next() == null : newNode; + assert !(node instanceof AbstractMergeNode); + FixedWithNextNode pred = (FixedWithNextNode) node.predecessor(); + pred.setNext(newNode); + newNode.setNext(node); + } + + public void reduceDegenerateLoopBegin(LoopBeginNode begin) { + assert begin.loopEnds().isEmpty() : "Loop begin still has backedges"; + if (begin.forwardEndCount() == 1) { // bypass merge and remove + reduceTrivialMerge(begin); + } else { // convert to merge + AbstractMergeNode merge = this.add(new MergeNode()); + this.replaceFixedWithFixed(begin, merge); + } + } + + public void reduceTrivialMerge(AbstractMergeNode merge) { + assert merge.forwardEndCount() == 1; + assert !(merge instanceof LoopBeginNode) || ((LoopBeginNode) merge).loopEnds().isEmpty(); + for (PhiNode phi : merge.phis().snapshot()) { + assert phi.valueCount() == 1; + ValueNode singleValue = phi.valueAt(0); + phi.replaceAtUsagesAndDelete(singleValue); + } + // remove loop exits + if (merge instanceof LoopBeginNode) { + ((LoopBeginNode) merge).removeExits(); + } + AbstractEndNode singleEnd = merge.forwardEndAt(0); + FixedNode sux = merge.next(); + FrameState stateAfter = merge.stateAfter(); + // evacuateGuards + merge.prepareDelete((FixedNode) singleEnd.predecessor()); + merge.safeDelete(); + if (stateAfter != null && stateAfter.isAlive() && stateAfter.hasNoUsages()) { + GraphUtil.killWithUnusedFloatingInputs(stateAfter); + } + if (sux == null) { + singleEnd.replaceAtPredecessor(null); + singleEnd.safeDelete(); + } else { + singleEnd.replaceAndDelete(sux); + } + } + + public GuardsStage getGuardsStage() { + return guardsStage; + } + + public void setGuardsStage(GuardsStage guardsStage) { + assert guardsStage.ordinal() >= this.guardsStage.ordinal(); + this.guardsStage = guardsStage; + } + + public boolean isAfterFloatingReadPhase() { + return isAfterFloatingReadPhase; + } + + public void setAfterFloatingReadPhase(boolean state) { + assert state : "cannot 'unapply' floating read phase on graph"; + isAfterFloatingReadPhase = state; + } + + public boolean hasValueProxies() { + return hasValueProxies; + } + + public void setHasValueProxies(boolean state) { + assert !state : "cannot 'unapply' value proxy removal on graph"; + hasValueProxies = state; + } + + /** + * Determines if {@link ProfilingInfo} is used during construction of this graph. + */ + public boolean useProfilingInfo() { + return useProfilingInfo; + } + + /** + * Gets the profiling info for the {@linkplain #method() root method} of this graph. + */ + public ProfilingInfo getProfilingInfo() { + return getProfilingInfo(method()); + } + + /** + * Gets the profiling info for a given method that is or will be part of this graph, taking into + * account {@link #useProfilingInfo()}. + */ + public ProfilingInfo getProfilingInfo(ResolvedJavaMethod m) { + if (useProfilingInfo && m != null) { + return m.getProfilingInfo(); + } else { + return DefaultProfilingInfo.get(TriState.UNKNOWN); + } + } + + /** + * Gets the object for recording assumptions while constructing of this graph. + * + * @return {@code null} if assumptions cannot be made for this graph + */ + public Assumptions getAssumptions() { + return assumptions; + } + + /** + * Gets the methods that were inlined while constructing this graph. + */ + public List getMethods() { + return methods; + } + + /** + * Records that {@code method} was used to build this graph. + */ + public void recordMethod(ResolvedJavaMethod method) { + methods.add(method); + } + + /** + * Updates the {@linkplain #getMethods() methods} used to build this graph with the methods used + * to build another graph. + */ + public void updateMethods(StructuredGraph other) { + assert this != other; + this.methods.addAll(other.methods); + } + + /** + * Gets the fields that were accessed while constructing this graph. + */ + public Set getFields() { + return fields; + } + + /** + * Records that {@code field} was accessed in this graph. + */ + public void recordField(ResolvedJavaField field) { + fields.add(field); + } + + /** + * Updates the {@linkplain #getFields() fields} of this graph with the accessed fields of + * another graph. + */ + public void updateFields(StructuredGraph other) { + assert this != other; + this.fields.addAll(other.fields); + } + + /** + * Gets the input bytecode {@linkplain ResolvedJavaMethod#getCodeSize() size} from which this + * graph is constructed. This ignores how many bytecodes in each constituent method are actually + * parsed (which may be none for methods whose IR is retrieved from a cache or less than the + * full amount for any given method due to profile guided branch pruning). + */ + public int getBytecodeSize() { + int res = 0; + for (ResolvedJavaMethod e : methods) { + res += e.getCodeSize(); + } + return res; + } + + /** + * + * @return true if the graph contains only a {@link StartNode} and {@link ReturnNode} + */ + public boolean isTrivial() { + return !(start.next() instanceof ReturnNode); + } + + @Override + public JavaMethod asJavaMethod() { + return method(); + } + + public boolean hasUnsafeAccess() { + return hasUnsafeAccess == UnsafeAccessState.HAS_ACCESS; + } + + public void markUnsafeAccess() { + if (hasUnsafeAccess == UnsafeAccessState.DISABLED) { + return; + } + hasUnsafeAccess = UnsafeAccessState.HAS_ACCESS; + } + + public void disableUnsafeAccessTracking() { + hasUnsafeAccess = UnsafeAccessState.DISABLED; + } + + public boolean isUnsafeAccessTrackingEnabled() { + return hasUnsafeAccess != UnsafeAccessState.DISABLED; + } + + public SpeculationLog getSpeculationLog() { + return speculationLog; + } + + public final void clearAllStateAfter() { + for (Node node : getNodes()) { + if (node instanceof StateSplit) { + FrameState stateAfter = ((StateSplit) node).stateAfter(); + if (stateAfter != null) { + ((StateSplit) node).setStateAfter(null); + // 2 nodes referencing the same framestate + if (stateAfter.isAlive()) { + GraphUtil.killWithUnusedFloatingInputs(stateAfter); + } + } + } + } + } + + public final boolean hasVirtualizableAllocation() { + for (Node n : getNodes()) { + if (n instanceof VirtualizableAllocation) { + return true; + } + } + return false; + } +}