use of org.graalvm.compiler.nodes.PhiNode in project graal by oracle.
the class LoopTransformations method insertPrePostLoops.
// This function splits candidate loops into pre, main and post loops,
// dividing the iteration space to facilitate the majority of iterations
// being executed in a main loop, which will have RCE implemented upon it.
// The initial loop form is constrained to single entry/exit, but can have
// flow. The translation looks like:
//
// @formatter:off
//
// (Simple Loop entry) (Pre Loop Entry)
// | |
// (LoopBeginNode) (LoopBeginNode)
// | |
// (Loop Control Test)<------ ==> (Loop control Test)<------
// / \ \ / \ \
// (Loop Exit) (Loop Body) | (Loop Exit) (Loop Body) |
// | | | | | |
// (continue code) (Loop End) | if (M < length)* (Loop End) |
// \ / / \ \ /
// -----> / | ----->
// / if ( ... )*
// / / \
// / / \
// / / \
// | / (Main Loop Entry)
// | | |
// | | (LoopBeginNode)
// | | |
// | | (Loop Control Test)<------
// | | / \ \
// | | (Loop Exit) (Loop Body) |
// \ \ | | |
// \ \ | (Loop End) |
// \ \ | \ /
// \ \ | ------>
// \ \ |
// (Main Loop Merge)*
// |
// (Post Loop Entry)
// |
// (LoopBeginNode)
// |
// (Loop Control Test)<-----
// / \ \
// (Loop Exit) (Loop Body) |
// | | |
// (continue code) (Loop End) |
// \ /
// ----->
//
// Key: "*" = optional.
// @formatter:on
//
// The value "M" is the maximal value of the loop trip for the original
// loop. The value of "length" is applicable to the number of arrays found
// in the loop but is reduced if some or all of the arrays are known to be
// the same length as "M". The maximum number of tests can be equal to the
// number of arrays in the loop, where multiple instances of an array are
// subsumed into a single test for that arrays length.
//
// If the optional main loop entry tests are absent, the Pre Loop exit
// connects to the Main loops entry and there is no merge hanging off the
// main loops exit to converge flow from said tests. All split use data
// flow is mitigated through phi(s) in the main merge if present and
// passed through the main and post loop phi(s) from the originating pre
// loop with final phi(s) and data flow patched to the "continue code".
// The pre loop is constrained to one iteration for now and will likely
// be updated to produce vector alignment if applicable.
public static LoopBeginNode insertPrePostLoops(LoopEx loop) {
StructuredGraph graph = loop.loopBegin().graph();
graph.getDebug().log("LoopTransformations.insertPrePostLoops %s", loop);
LoopFragmentWhole preLoop = loop.whole();
CountedLoopInfo preCounted = loop.counted();
IfNode preLimit = preCounted.getLimitTest();
assert preLimit != null;
LoopBeginNode preLoopBegin = loop.loopBegin();
InductionVariable preIv = preCounted.getCounter();
LoopExitNode preLoopExitNode = preLoopBegin.getSingleLoopExit();
FixedNode continuationNode = preLoopExitNode.next();
// Each duplication is inserted after the original, ergo create the post loop first
LoopFragmentWhole mainLoop = preLoop.duplicate();
LoopFragmentWhole postLoop = preLoop.duplicate();
preLoopBegin.incrementSplits();
preLoopBegin.incrementSplits();
preLoopBegin.setPreLoop();
graph.getDebug().dump(DebugContext.VERBOSE_LEVEL, graph, "After duplication");
LoopBeginNode mainLoopBegin = mainLoop.getDuplicatedNode(preLoopBegin);
mainLoopBegin.setMainLoop();
LoopBeginNode postLoopBegin = postLoop.getDuplicatedNode(preLoopBegin);
postLoopBegin.setPostLoop();
EndNode postEndNode = getBlockEndAfterLoopExit(postLoopBegin);
AbstractMergeNode postMergeNode = postEndNode.merge();
LoopExitNode postLoopExitNode = postLoopBegin.getSingleLoopExit();
// Update the main loop phi initialization to carry from the pre loop
for (PhiNode prePhiNode : preLoopBegin.phis()) {
PhiNode mainPhiNode = mainLoop.getDuplicatedNode(prePhiNode);
mainPhiNode.setValueAt(0, prePhiNode);
}
EndNode mainEndNode = getBlockEndAfterLoopExit(mainLoopBegin);
AbstractMergeNode mainMergeNode = mainEndNode.merge();
AbstractEndNode postEntryNode = postLoopBegin.forwardEnd();
// In the case of no Bounds tests, we just flow right into the main loop
AbstractBeginNode mainLandingNode = BeginNode.begin(postEntryNode);
LoopExitNode mainLoopExitNode = mainLoopBegin.getSingleLoopExit();
mainLoopExitNode.setNext(mainLandingNode);
preLoopExitNode.setNext(mainLoopBegin.forwardEnd());
// Add and update any phi edges as per merge usage as needed and update usages
processPreLoopPhis(loop, mainLoop, postLoop);
continuationNode.predecessor().clearSuccessors();
postLoopExitNode.setNext(continuationNode);
cleanupMerge(postMergeNode, postLoopExitNode);
cleanupMerge(mainMergeNode, mainLandingNode);
// Change the preLoop to execute one iteration for now
updateMainLoopLimit(preLimit, preIv, mainLoop);
updatePreLoopLimit(preLimit, preIv, preCounted);
preLoopBegin.setLoopFrequency(1);
mainLoopBegin.setLoopFrequency(Math.max(0.0, mainLoopBegin.loopFrequency() - 2));
postLoopBegin.setLoopFrequency(Math.max(0.0, postLoopBegin.loopFrequency() - 1));
// The pre and post loops don't require safepoints at all
for (SafepointNode safepoint : preLoop.nodes().filter(SafepointNode.class)) {
graph.removeFixed(safepoint);
}
for (SafepointNode safepoint : postLoop.nodes().filter(SafepointNode.class)) {
graph.removeFixed(safepoint);
}
graph.getDebug().dump(DebugContext.DETAILED_LEVEL, graph, "InsertPrePostLoops %s", loop);
return mainLoopBegin;
}
use of org.graalvm.compiler.nodes.PhiNode in project graal by oracle.
the class LoopFragmentInside method patchPeeling.
private void patchPeeling(LoopFragmentInside peel) {
LoopBeginNode loopBegin = loop().loopBegin();
StructuredGraph graph = loopBegin.graph();
List<PhiNode> newPhis = new LinkedList<>();
NodeBitMap usagesToPatch = nodes.copy();
for (LoopExitNode exit : exits()) {
markStateNodes(exit, usagesToPatch);
for (ProxyNode proxy : exit.proxies()) {
usagesToPatch.markAndGrow(proxy);
}
}
markStateNodes(loopBegin, usagesToPatch);
List<PhiNode> oldPhis = loopBegin.phis().snapshot();
for (PhiNode phi : oldPhis) {
if (phi.hasNoUsages()) {
continue;
}
ValueNode first;
if (loopBegin.loopEnds().count() == 1) {
// back edge value
ValueNode b = phi.valueAt(loopBegin.loopEnds().first());
// corresponding value in the peel
first = peel.prim(b);
} else {
first = peel.mergedInitializers.get(phi);
}
// create a new phi (we don't patch the old one since some usages of the old one may
// still be valid)
PhiNode newPhi = patchPhi(graph, phi, loopBegin);
newPhi.addInput(first);
for (LoopEndNode end : loopBegin.orderedLoopEnds()) {
newPhi.addInput(phi.valueAt(end));
}
peel.putDuplicatedNode(phi, newPhi);
newPhis.add(newPhi);
for (Node usage : phi.usages().snapshot()) {
// patch only usages that should use the new phi ie usages that were peeled
if (usagesToPatch.isMarkedAndGrow(usage)) {
usage.replaceFirstInput(phi, newPhi);
}
}
}
// new corresponding phis
for (PhiNode phi : newPhis) {
for (int i = 0; i < phi.valueCount(); i++) {
ValueNode v = phi.valueAt(i);
if (loopBegin.isPhiAtMerge(v)) {
PhiNode newV = peel.getDuplicatedNode((ValuePhiNode) v);
if (newV != null) {
phi.setValueAt(i, newV);
}
}
}
}
boolean progress = true;
while (progress) {
progress = false;
int i = 0;
outer: while (i < oldPhis.size()) {
PhiNode oldPhi = oldPhis.get(i);
for (Node usage : oldPhi.usages()) {
if (usage instanceof PhiNode && oldPhis.contains(usage)) {
// Do not mark.
} else {
// Mark alive by removing from delete set.
oldPhis.remove(i);
progress = true;
continue outer;
}
}
i++;
}
}
for (PhiNode deadPhi : oldPhis) {
deadPhi.clearInputs();
}
for (PhiNode deadPhi : oldPhis) {
if (deadPhi.isAlive()) {
GraphUtil.killWithUnusedFloatingInputs(deadPhi);
}
}
}
use of org.graalvm.compiler.nodes.PhiNode in project graal by oracle.
the class BinaryGraphPrinter method nodeProperties.
@Override
@SuppressWarnings({ "unchecked", "rawtypes" })
public void nodeProperties(GraphInfo info, Node node, Map<String, Object> props) {
node.getDebugProperties((Map) props);
Graph graph = info.graph;
ControlFlowGraph cfg = info.cfg;
NodeMap<Block> nodeToBlocks = info.nodeToBlocks;
if (cfg != null && DebugOptions.PrintGraphProbabilities.getValue(graph.getOptions()) && node instanceof FixedNode) {
try {
props.put("probability", cfg.blockFor(node).probability());
} catch (Throwable t) {
props.put("probability", 0.0);
props.put("probability-exception", t);
}
}
try {
props.put("NodeCost-Size", node.estimatedNodeSize());
props.put("NodeCost-Cycles", node.estimatedNodeCycles());
} catch (Throwable t) {
props.put("node-cost-exception", t.getMessage());
}
if (nodeToBlocks != null) {
Object block = getBlockForNode(node, nodeToBlocks);
if (block != null) {
props.put("node-to-block", block);
}
}
if (node instanceof ControlSinkNode) {
props.put("category", "controlSink");
} else if (node instanceof ControlSplitNode) {
props.put("category", "controlSplit");
} else if (node instanceof AbstractMergeNode) {
props.put("category", "merge");
} else if (node instanceof AbstractBeginNode) {
props.put("category", "begin");
} else if (node instanceof AbstractEndNode) {
props.put("category", "end");
} else if (node instanceof FixedNode) {
props.put("category", "fixed");
} else if (node instanceof VirtualState) {
props.put("category", "state");
} else if (node instanceof PhiNode) {
props.put("category", "phi");
} else if (node instanceof ProxyNode) {
props.put("category", "proxy");
} else {
if (node instanceof ConstantNode) {
ConstantNode cn = (ConstantNode) node;
updateStringPropertiesForConstant((Map) props, cn);
}
props.put("category", "floating");
}
if (getSnippetReflectionProvider() != null) {
for (Map.Entry<String, Object> prop : props.entrySet()) {
if (prop.getValue() instanceof JavaConstantFormattable) {
props.put(prop.getKey(), ((JavaConstantFormattable) prop.getValue()).format(this));
}
}
}
}
use of org.graalvm.compiler.nodes.PhiNode in project graal by oracle.
the class GraphOrder method visitForward.
private static void visitForward(ArrayList<Node> nodes, NodeBitMap visited, Node node, boolean floatingOnly) {
try {
assert node == null || node.isAlive() : node + " not alive";
if (node != null && !visited.isMarked(node)) {
if (floatingOnly && node instanceof FixedNode) {
throw new GraalError("unexpected reference to fixed node: %s (this indicates an unexpected cycle)", node);
}
visited.mark(node);
FrameState stateAfter = null;
if (node instanceof StateSplit) {
stateAfter = ((StateSplit) node).stateAfter();
}
for (Node input : node.inputs()) {
if (input != stateAfter) {
visitForward(nodes, visited, input, true);
}
}
if (node instanceof EndNode) {
EndNode end = (EndNode) node;
for (PhiNode phi : end.merge().phis()) {
visitForward(nodes, visited, phi.valueAt(end), true);
}
}
nodes.add(node);
if (node instanceof AbstractMergeNode) {
for (PhiNode phi : ((AbstractMergeNode) node).phis()) {
visited.mark(phi);
nodes.add(phi);
}
}
if (stateAfter != null) {
visitForward(nodes, visited, stateAfter, true);
}
}
} catch (GraalError e) {
throw GraalGraphError.transformAndAddContext(e, node);
}
}
use of org.graalvm.compiler.nodes.PhiNode in project graal by oracle.
the class GraphOrder method assertSchedulableGraph.
/**
* This method schedules the graph and makes sure that, for every node, all inputs are available
* at the position where it is scheduled. This is a very expensive assertion.
*/
public static boolean assertSchedulableGraph(final StructuredGraph graph) {
assert graph.getGuardsStage() != GuardsStage.AFTER_FSA : "Cannot use the BlockIteratorClosure after FrameState Assignment, HIR Loop Data Structures are no longer valid.";
try {
final SchedulePhase schedulePhase = new SchedulePhase(SchedulingStrategy.LATEST_OUT_OF_LOOPS, true);
final EconomicMap<LoopBeginNode, NodeBitMap> loopEntryStates = EconomicMap.create(Equivalence.IDENTITY);
schedulePhase.apply(graph, false);
final ScheduleResult schedule = graph.getLastSchedule();
BlockIteratorClosure<NodeBitMap> closure = new BlockIteratorClosure<NodeBitMap>() {
@Override
protected List<NodeBitMap> processLoop(Loop<Block> loop, NodeBitMap initialState) {
return ReentrantBlockIterator.processLoop(this, loop, initialState).exitStates;
}
@Override
protected NodeBitMap processBlock(final Block block, final NodeBitMap currentState) {
final List<Node> list = graph.getLastSchedule().getBlockToNodesMap().get(block);
/*
* A stateAfter is not valid directly after its associated state split, but
* right before the next fixed node. Therefore a pending stateAfter is kept that
* will be checked at the correct position.
*/
FrameState pendingStateAfter = null;
for (final Node node : list) {
if (node instanceof ValueNode) {
FrameState stateAfter = node instanceof StateSplit ? ((StateSplit) node).stateAfter() : null;
if (node instanceof FullInfopointNode) {
stateAfter = ((FullInfopointNode) node).getState();
}
if (pendingStateAfter != null && node instanceof FixedNode) {
pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() {
@Override
public void apply(Node usage, Node nonVirtualNode) {
assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + " not available at virtualstate " + usage + " before " + node + " in block " + block + " \n" + list;
}
});
pendingStateAfter = null;
}
if (node instanceof AbstractMergeNode) {
// phis aren't scheduled, so they need to be added explicitly
currentState.markAll(((AbstractMergeNode) node).phis());
if (node instanceof LoopBeginNode) {
// remember the state at the loop entry, it's restored at exits
loopEntryStates.put((LoopBeginNode) node, currentState.copy());
}
} else if (node instanceof ProxyNode) {
assert false : "proxy nodes should not be in the schedule";
} else if (node instanceof LoopExitNode) {
if (graph.hasValueProxies()) {
for (ProxyNode proxy : ((LoopExitNode) node).proxies()) {
for (Node input : proxy.inputs()) {
if (input != proxy.proxyPoint()) {
assert currentState.isMarked(input) : input + " not available at " + proxy + " in block " + block + "\n" + list;
}
}
}
// loop contents are only accessible via proxies at the exit
currentState.clearAll();
currentState.markAll(loopEntryStates.get(((LoopExitNode) node).loopBegin()));
}
// Loop proxies aren't scheduled, so they need to be added
// explicitly
currentState.markAll(((LoopExitNode) node).proxies());
} else {
for (Node input : node.inputs()) {
if (input != stateAfter) {
if (input instanceof FrameState) {
((FrameState) input).applyToNonVirtual(new VirtualState.NodeClosure<Node>() {
@Override
public void apply(Node usage, Node nonVirtual) {
assert currentState.isMarked(nonVirtual) : nonVirtual + " not available at " + node + " in block " + block + "\n" + list;
}
});
} else {
assert currentState.isMarked(input) || input instanceof VirtualObjectNode || input instanceof ConstantNode : input + " not available at " + node + " in block " + block + "\n" + list;
}
}
}
}
if (node instanceof AbstractEndNode) {
AbstractMergeNode merge = ((AbstractEndNode) node).merge();
for (PhiNode phi : merge.phis()) {
ValueNode phiValue = phi.valueAt((AbstractEndNode) node);
assert phiValue == null || currentState.isMarked(phiValue) || phiValue instanceof ConstantNode : phiValue + " not available at phi " + phi + " / end " + node + " in block " + block;
}
}
if (stateAfter != null) {
assert pendingStateAfter == null;
pendingStateAfter = stateAfter;
}
currentState.mark(node);
}
}
if (pendingStateAfter != null) {
pendingStateAfter.applyToNonVirtual(new NodeClosure<Node>() {
@Override
public void apply(Node usage, Node nonVirtualNode) {
assert currentState.isMarked(nonVirtualNode) || nonVirtualNode instanceof VirtualObjectNode || nonVirtualNode instanceof ConstantNode : nonVirtualNode + " not available at virtualstate " + usage + " at end of block " + block + " \n" + list;
}
});
}
return currentState;
}
@Override
protected NodeBitMap merge(Block merge, List<NodeBitMap> states) {
NodeBitMap result = states.get(0);
for (int i = 1; i < states.size(); i++) {
result.intersect(states.get(i));
}
return result;
}
@Override
protected NodeBitMap getInitialState() {
NodeBitMap ret = graph.createNodeBitMap();
ret.markAll(graph.getNodes().filter(ConstantNode.class));
return ret;
}
@Override
protected NodeBitMap cloneState(NodeBitMap oldState) {
return oldState.copy();
}
};
ReentrantBlockIterator.apply(closure, schedule.getCFG().getStartBlock());
} catch (Throwable t) {
graph.getDebug().handle(t);
}
return true;
}
Aggregations