use of org.graalvm.compiler.nodes.LoopBeginNode in project graal by oracle.
the class BytecodeParser method checkLoopExit.
private Target checkLoopExit(FixedNode target, BciBlock targetBlock, FrameStateBuilder state) {
if (currentBlock != null) {
long exits = currentBlock.loops & ~targetBlock.loops;
if (exits != 0) {
LoopExitNode firstLoopExit = null;
LoopExitNode lastLoopExit = null;
int pos = 0;
ArrayList<BciBlock> exitLoops = new ArrayList<>(Long.bitCount(exits));
do {
long lMask = 1L << pos;
if ((exits & lMask) != 0) {
exitLoops.add(blockMap.getLoopHeader(pos));
exits &= ~lMask;
}
pos++;
} while (exits != 0);
Collections.sort(exitLoops, new Comparator<BciBlock>() {
@Override
public int compare(BciBlock o1, BciBlock o2) {
return Long.bitCount(o2.loops) - Long.bitCount(o1.loops);
}
});
int bci = targetBlock.startBci;
if (targetBlock instanceof ExceptionDispatchBlock) {
bci = ((ExceptionDispatchBlock) targetBlock).deoptBci;
}
FrameStateBuilder newState = state.copy();
for (BciBlock loop : exitLoops) {
LoopBeginNode loopBegin = (LoopBeginNode) getFirstInstruction(loop);
LoopExitNode loopExit = graph.add(new LoopExitNode(loopBegin));
if (lastLoopExit != null) {
lastLoopExit.setNext(loopExit);
}
if (firstLoopExit == null) {
firstLoopExit = loopExit;
}
lastLoopExit = loopExit;
debug.log("Target %s Exits %s, scanning framestates...", targetBlock, loop);
newState.clearNonLiveLocals(targetBlock, liveness, true);
newState.insertLoopProxies(loopExit, getEntryState(loop));
loopExit.setStateAfter(newState.create(bci, loopExit));
}
lastLoopExit.setNext(target);
return new Target(firstLoopExit, newState);
}
}
return new Target(target, state);
}
use of org.graalvm.compiler.nodes.LoopBeginNode in project graal by oracle.
the class BytecodeParser method iterateBytecodesForBlock.
@SuppressWarnings("try")
protected void iterateBytecodesForBlock(BciBlock block) {
if (block.isLoopHeader) {
// Create the loop header block, which later will merge the backward branches of
// the loop.
controlFlowSplit = true;
LoopBeginNode loopBegin = appendLoopBegin(this.lastInstr, block.startBci);
lastInstr = loopBegin;
// Create phi functions for all local variables and operand stack slots.
frameState.insertLoopPhis(liveness, block.loopId, loopBegin, forceLoopPhis(), stampFromValueForForcedPhis());
loopBegin.setStateAfter(createFrameState(block.startBci, loopBegin));
/*
* We have seen all forward branches. All subsequent backward branches will merge to the
* loop header. This ensures that the loop header has exactly one non-loop predecessor.
*/
setFirstInstruction(block, loopBegin);
/*
* We need to preserve the frame state builder of the loop header so that we can merge
* values for phi functions, so make a copy of it.
*/
setEntryState(block, frameState.copy());
debug.log(" created loop header %s", loopBegin);
} else if (lastInstr instanceof MergeNode) {
/*
* All inputs of non-loop phi nodes are known by now. We can infer the stamp for the
* phi, so that parsing continues with more precise type information.
*/
frameState.inferPhiStamps((AbstractMergeNode) lastInstr);
}
assert lastInstr.next() == null : "instructions already appended at block " + block;
debug.log(" frameState: %s", frameState);
lastInstr = finishInstruction(lastInstr, frameState);
int endBCI = stream.endBCI();
stream.setBCI(block.startBci);
int bci = block.startBci;
BytecodesParsed.add(debug, block.endBci - bci);
/* Reset line number for new block */
if (graphBuilderConfig.insertFullInfopoints()) {
previousLineNumber = -1;
}
while (bci < endBCI) {
try (DebugCloseable context = openNodeContext()) {
if (graphBuilderConfig.insertFullInfopoints() && !parsingIntrinsic()) {
currentLineNumber = lnt != null ? lnt.getLineNumber(bci) : -1;
if (currentLineNumber != previousLineNumber) {
genInfoPointNode(InfopointReason.BYTECODE_POSITION, null);
previousLineNumber = currentLineNumber;
}
}
// read the opcode
int opcode = stream.currentBC();
assert traceState();
assert traceInstruction(bci, opcode, bci == block.startBci);
if (parent == null && bci == entryBCI) {
if (block.getJsrScope() != JsrScope.EMPTY_SCOPE) {
throw new JsrNotSupportedBailout("OSR into a JSR scope is not supported");
}
EntryMarkerNode x = append(new EntryMarkerNode());
frameState.insertProxies(value -> graph.unique(new EntryProxyNode(value, x)));
x.setStateAfter(createFrameState(bci, x));
}
processBytecode(bci, opcode);
} catch (BailoutException e) {
// Don't wrap bailouts as parser errors
throw e;
} catch (Throwable e) {
throw throwParserError(e);
}
if (lastInstr == null || lastInstr.next() != null) {
break;
}
stream.next();
bci = stream.currentBCI();
assert block == currentBlock;
assert checkLastInstruction();
lastInstr = finishInstruction(lastInstr, frameState);
if (bci < endBCI) {
if (bci > block.endBci) {
assert !block.getSuccessor(0).isExceptionEntry;
assert block.numNormalSuccessors() == 1;
// we fell through to the next block, add a goto and break
appendGoto(block.getSuccessor(0));
break;
}
}
}
}
use of org.graalvm.compiler.nodes.LoopBeginNode in project graal by oracle.
the class DefaultLoopPolicies method shouldPartiallyUnroll.
@Override
public boolean shouldPartiallyUnroll(LoopEx loop) {
LoopBeginNode loopBegin = loop.loopBegin();
if (!loop.isCounted()) {
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s isn't counted", loopBegin);
return false;
}
OptionValues options = loop.entryPoint().getOptions();
int maxNodes = Options.ExactPartialUnrollMaxNodes.getValue(options);
maxNodes = Math.min(maxNodes, Math.max(0, MaximumDesiredSize.getValue(options) - loop.loopBegin().graph().getNodeCount()));
int size = Math.max(1, loop.size() - 1 - loop.loopBegin().phis().count());
int unrollFactor = loopBegin.getUnrollFactor();
if (unrollFactor == 1) {
double loopFrequency = loopBegin.loopFrequency();
if (loopBegin.isSimpleLoop() && loopFrequency < 5.0) {
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s frequency too low %s ", loopBegin, loopFrequency);
return false;
}
loopBegin.setLoopOrigFrequency(loopFrequency);
}
int maxUnroll = Options.UnrollMaxIterations.getValue(options);
// Now correct size for the next unroll. UnrollMaxIterations == 1 means perform the
// pre/main/post transformation but don't actually unroll the main loop.
size += size;
if (maxUnroll == 1 && loopBegin.isSimpleLoop() || size <= maxNodes && unrollFactor < maxUnroll) {
// Will the next unroll fit?
if ((int) loopBegin.loopOrigFrequency() < (unrollFactor * 2)) {
return false;
}
// Check whether we're allowed to unroll this loop
for (Node node : loop.inside().nodes()) {
if (node instanceof ControlFlowAnchorNode) {
return false;
}
if (node instanceof InvokeNode) {
return false;
}
}
return true;
} else {
loopBegin.getDebug().log(DebugContext.VERBOSE_LEVEL, "shouldPartiallyUnroll %s unrolled loop is too large %s ", loopBegin, size);
return false;
}
}
use of org.graalvm.compiler.nodes.LoopBeginNode in project graal by oracle.
the class LoopTransformations method isUnrollableLoop.
public static boolean isUnrollableLoop(LoopEx loop) {
if (!loop.isCounted() || !loop.counted().getCounter().isConstantStride() || !loop.loop().getChildren().isEmpty()) {
return false;
}
LoopBeginNode loopBegin = loop.loopBegin();
LogicNode condition = loop.counted().getLimitTest().condition();
if (!(condition instanceof CompareNode)) {
return false;
}
if (((CompareNode) condition).condition() == CanonicalCondition.EQ) {
condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s condition unsupported %s ", loopBegin, ((CompareNode) condition).condition());
return false;
}
if (loopBegin.isMainLoop() || loopBegin.isSimpleLoop()) {
// as well.
if (loop.loop().getBlocks().size() < 3) {
return true;
}
condition.getDebug().log(DebugContext.VERBOSE_LEVEL, "isUnrollableLoop %s too large to unroll %s ", loopBegin, loop.loop().getBlocks().size());
}
return false;
}
use of org.graalvm.compiler.nodes.LoopBeginNode 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;
}
Aggregations