-
Notifications
You must be signed in to change notification settings - Fork 13.5k
[VPlan] Retain exit conditions and edges in initial VPlan (NFC). #137709
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
de0fc2d
19bec82
55b4d5e
ca45f88
ddfdeef
1284bc6
79317a5
3cc4b31
bbb902e
c1902ed
0cadcf9
5fa6b7d
5d6729d
1211faf
50d7ee1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -182,11 +182,6 @@ void PlainCFGBuilder::createVPInstructionsForVPBB(VPBasicBlock *VPBB, | |
"Instruction shouldn't have been visited."); | ||
|
||
if (auto *Br = dyn_cast<BranchInst>(Inst)) { | ||
if (TheLoop->getLoopLatch() == BB || | ||
any_of(successors(BB), | ||
[this](BasicBlock *Succ) { return !TheLoop->contains(Succ); })) | ||
continue; | ||
|
||
// Conditional branch instruction are represented using BranchOnCond | ||
// recipes. | ||
if (Br->isConditional()) { | ||
|
@@ -251,6 +246,8 @@ std::unique_ptr<VPlan> PlainCFGBuilder::buildPlainCFG( | |
DenseMap<VPBlockBase *, BasicBlock *> &VPB2IRBB) { | ||
VPIRBasicBlock *Entry = cast<VPIRBasicBlock>(Plan->getEntry()); | ||
BB2VPBB[Entry->getIRBasicBlock()] = Entry; | ||
for (VPIRBasicBlock *ExitVPBB : Plan->getExitBlocks()) | ||
BB2VPBB[ExitVPBB->getIRBasicBlock()] = ExitVPBB; | ||
|
||
// 1. Scan the body of the loop in a topological order to visit each basic | ||
// block after having visited its predecessor basic blocks. Create a VPBB for | ||
|
@@ -276,7 +273,6 @@ std::unique_ptr<VPlan> PlainCFGBuilder::buildPlainCFG( | |
for (BasicBlock *BB : RPO) { | ||
// Create or retrieve the VPBasicBlock for this BB. | ||
VPBasicBlock *VPBB = getOrCreateVPBB(BB); | ||
Loop *LoopForBB = LI->getLoopFor(BB); | ||
// Set VPBB predecessors in the same order as they are in the incoming BB. | ||
setVPBBPredsFromBB(VPBB, BB); | ||
|
||
|
@@ -307,24 +303,12 @@ std::unique_ptr<VPlan> PlainCFGBuilder::buildPlainCFG( | |
BasicBlock *IRSucc1 = BI->getSuccessor(1); | ||
VPBasicBlock *Successor0 = getOrCreateVPBB(IRSucc0); | ||
VPBasicBlock *Successor1 = getOrCreateVPBB(IRSucc1); | ||
|
||
// Don't connect any blocks outside the current loop except the latches for | ||
// inner loops. | ||
// TODO: Also connect exit blocks during initial VPlan construction. | ||
if (LoopForBB == TheLoop || BB != LoopForBB->getLoopLatch()) { | ||
if (!LoopForBB->contains(IRSucc0)) { | ||
VPBB->setOneSuccessor(Successor1); | ||
continue; | ||
} | ||
if (!LoopForBB->contains(IRSucc1)) { | ||
VPBB->setOneSuccessor(Successor0); | ||
continue; | ||
} | ||
} | ||
|
||
VPBB->setTwoSuccessors(Successor0, Successor1); | ||
} | ||
|
||
for (auto *EB : Plan->getExitBlocks()) | ||
setVPBBPredsFromBB(EB, EB->getIRBasicBlock()); | ||
|
||
// 2. The whole CFG has been built at this point so all the input Values must | ||
// have a VPlan counterpart. Fix VPlan header phi by adding their | ||
// corresponding VPlan operands. | ||
|
@@ -424,22 +408,23 @@ static void createLoopRegion(VPlan &Plan, VPBlockBase *HeaderVPB) { | |
|
||
VPBlockUtils::disconnectBlocks(PreheaderVPBB, HeaderVPB); | ||
VPBlockUtils::disconnectBlocks(LatchVPBB, HeaderVPB); | ||
VPBlockBase *Succ = LatchVPBB->getSingleSuccessor(); | ||
assert(LatchVPBB->getNumSuccessors() <= 1 && | ||
"Latch has more than one successor"); | ||
if (Succ) | ||
VPBlockUtils::disconnectBlocks(LatchVPBB, Succ); | ||
|
||
auto *R = Plan.createVPRegionBlock(HeaderVPB, LatchVPBB, "", | ||
false /*isReplicator*/); | ||
// All VPBB's reachable shallowly from HeaderVPB belong to top level loop, | ||
// because VPlan is expected to end at top level latch disconnected above. | ||
VPBlockBase *LatchExitVPB = LatchVPBB->getSingleSuccessor(); | ||
assert(LatchExitVPB && "Latch expected to be left with a single successor"); | ||
|
||
// Create an empty region first and insert it between PreheaderVPBB and | ||
// LatchExitVPB, taking care to preserve the original predecessor & successor | ||
// order of blocks. Set region entry and exiting after both HeaderVPB and | ||
// LatchVPBB have been disconnected from their predecessors/successors. | ||
auto *R = Plan.createVPRegionBlock("", false /*isReplicator*/); | ||
VPBlockUtils::insertOnEdge(LatchVPBB, LatchExitVPB, R); | ||
VPBlockUtils::disconnectBlocks(LatchVPBB, R); | ||
VPBlockUtils::connectBlocks(PreheaderVPBB, R); | ||
R->setEntry(HeaderVPB); | ||
R->setExiting(LatchVPBB); | ||
|
||
// All VPBB's reachable shallowly from HeaderVPB belong to the current region. | ||
for (VPBlockBase *VPBB : vp_depth_first_shallow(HeaderVPB)) | ||
VPBB->setParent(R); | ||
|
||
VPBlockUtils::insertBlockAfter(R, PreheaderVPBB); | ||
if (Succ) | ||
VPBlockUtils::connectBlocks(R, Succ); | ||
} | ||
|
||
// Add the necessary canonical IV and branch recipes required to control the | ||
|
@@ -491,12 +476,34 @@ void VPlanTransforms::prepareForVectorization(VPlan &Plan, Type *InductionTy, | |
VPBlockUtils::insertBlockAfter(VecPreheader, Plan.getEntry()); | ||
|
||
VPBasicBlock *MiddleVPBB = Plan.createVPBasicBlock("middle.block"); | ||
VPBlockUtils::connectBlocks(LatchVPB, MiddleVPBB); | ||
LatchVPB->swapSuccessors(); | ||
// The canonical LatchVPB has the header block as last successor. If it has | ||
// another successor, this successor is an exit block - insert middle block on | ||
// its edge. Otherwise, add middle block as another successor retaining header | ||
// as last. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We can vectorize loops where the latch isn’t exiting, by requiring at least one iteration in the scalar loop. In that case, the original latch will There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah, right, thanks. Perhaps worth a comment. |
||
if (LatchVPB->getNumSuccessors() == 2) { | ||
VPBlockBase *LatchExitVPB = LatchVPB->getSuccessors()[0]; | ||
VPBlockUtils::insertOnEdge(LatchVPB, LatchExitVPB, MiddleVPBB); | ||
} else { | ||
VPBlockUtils::connectBlocks(LatchVPB, MiddleVPBB); | ||
LatchVPB->swapSuccessors(); | ||
} | ||
|
||
addCanonicalIVRecipes(Plan, cast<VPBasicBlock>(HeaderVPB), | ||
cast<VPBasicBlock>(LatchVPB), InductionTy, IVDL); | ||
|
||
// Disconnect all edges to exit blocks other than from the middle block. | ||
// TODO: VPlans with early exits should be explicitly converted to a form | ||
// exiting only via the latch here, including adjusting the exit condition, | ||
// instead of simply disconnecting the edges and adjusting the VPlan later. | ||
for (VPBlockBase *EB : Plan.getExitBlocks()) { | ||
for (VPBlockBase *Pred : to_vector(EB->getPredecessors())) { | ||
if (Pred == MiddleVPBB) | ||
continue; | ||
cast<VPBasicBlock>(Pred)->getTerminator()->eraseFromParent(); | ||
VPBlockUtils::disconnectBlocks(Pred, EB); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This looks a bit odd at first as it seems to be undoing what you did above with There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The edge from MiddleVPBB to LatchExitVPB is retained here explicitly via the early-continue exclusion. The edges removed here are early-exits from non-latch Pred block to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Above with The loop here disconnects all early exits and they will be handled later: either by requiring at least one scalar iteration, nothing more needs to be done, or introducing the early exit control flow to go to the early exit via the additional middle block. For the latter case, the VPlan is now incomplete/incorrect. To avoid this, we should directly handle the uncountable early exits here, which is done in #138393. This way, we do not need to rely on IR references in |
||
} | ||
} | ||
|
||
// Create SCEV and VPValue for the trip count. | ||
// We use the symbolic max backedge-taken-count, which works also when | ||
// vectorizing loops with uncountable early exits. | ||
|
@@ -523,6 +530,8 @@ void VPlanTransforms::prepareForVectorization(VPlan &Plan, Type *InductionTy, | |
// 3) Otherwise, construct a runtime check. | ||
|
||
if (!RequiresScalarEpilogueCheck) { | ||
if (auto *LatchExitVPB = MiddleVPBB->getSingleSuccessor()) | ||
VPBlockUtils::disconnectBlocks(MiddleVPBB, LatchExitVPB); | ||
VPBlockUtils::connectBlocks(MiddleVPBB, ScalarPH); | ||
// The exit blocks are unreachable, remove their recipes to make sure no | ||
// users remain that may pessimize transforms. | ||
|
@@ -533,10 +542,8 @@ void VPlanTransforms::prepareForVectorization(VPlan &Plan, Type *InductionTy, | |
return; | ||
} | ||
|
||
// The connection order corresponds to the operands of the conditional branch. | ||
BasicBlock *IRExitBlock = TheLoop->getUniqueLatchExitBlock(); | ||
auto *VPExitBlock = Plan.getExitBlock(IRExitBlock); | ||
VPBlockUtils::connectBlocks(MiddleVPBB, VPExitBlock); | ||
// The connection order corresponds to the operands of the conditional branch, | ||
// with the middle block already connected to the exit block. | ||
VPBlockUtils::connectBlocks(MiddleVPBB, ScalarPH); | ||
|
||
auto *ScalarLatchTerm = TheLoop->getLoopLatch()->getTerminator(); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,7 +51,7 @@ TEST_F(VPlanHCFGTest, testBuildHCFGInnerLoop) { | |
// Check that the region following the preheader consists of a block for the | ||
// original header and a separate latch. | ||
VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock(); | ||
EXPECT_EQ(10u, VecBB->size()); | ||
EXPECT_EQ(11u, VecBB->size()); | ||
EXPECT_EQ(0u, VecBB->getNumPredecessors()); | ||
EXPECT_EQ(0u, VecBB->getNumSuccessors()); | ||
EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB); | ||
|
@@ -129,6 +129,7 @@ compound=true | |
" EMIT store ir\<%res\>, ir\<%arr.idx\>\l" + | ||
" EMIT ir\<%indvars.iv.next\> = add ir\<%indvars.iv\>, ir\<1\>\l" + | ||
" EMIT ir\<%exitcond\> = icmp ir\<%indvars.iv.next\>, ir\<%N\>\l" + | ||
" EMIT vp\<%3\> = not ir\<%exitcond\>\l" + | ||
" EMIT vp\<%index.next\> = add nuw vp\<%2\>, vp\<%0\>\l" + | ||
" EMIT branch-on-count vp\<%index.next\>, vp\<%1\>\l" + | ||
"No successors\l" | ||
|
@@ -212,7 +213,7 @@ TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) { | |
// Check that the region following the preheader consists of a block for the | ||
// original header and a separate latch. | ||
VPBasicBlock *VecBB = Plan->getVectorLoopRegion()->getEntryBasicBlock(); | ||
EXPECT_EQ(11u, VecBB->size()); | ||
EXPECT_EQ(12u, VecBB->size()); | ||
EXPECT_EQ(0u, VecBB->getNumPredecessors()); | ||
EXPECT_EQ(0u, VecBB->getNumSuccessors()); | ||
EXPECT_EQ(VecBB->getParent()->getEntryBasicBlock(), VecBB); | ||
|
@@ -229,6 +230,7 @@ TEST_F(VPlanHCFGTest, testVPInstructionToVPRecipesInner) { | |
EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++)); | ||
EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++)); | ||
EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++)); | ||
EXPECT_NE(nullptr, dyn_cast<VPInstruction>(&*Iter++)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The previous case didn't check all instructions (that Iter reached VecBB->end()), so avoid checking the new last instruction, as done here? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. left as is for now, just carrying the curent behavior forward as this checks that we reach VecBB->end(). Might be something to be done separately? Or checking if we have the expected canonical instructions, which may make the test more brittle. |
||
EXPECT_EQ(VecBB->end(), Iter); | ||
} | ||
|
||
|
@@ -302,6 +304,7 @@ compound=true | |
" EMIT store ir\<%res\>, ir\<%arr.idx\>\l" + | ||
" EMIT ir\<%iv.next\> = add ir\<%iv\>, ir\<1\>\l" + | ||
" EMIT ir\<%exitcond\> = icmp ir\<%iv.next\>, ir\<%N\>\l" + | ||
" EMIT vp\<%3\> = not ir\<%exitcond\>\l" + | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should vp%3 be used, also above. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No, at this point we already added the canonical branch to control the loop; the old conditions will be cleaned up by DCE |
||
" EMIT vp\<%index.next\> = add nuw vp\<%2\>, vp\<%0\>\l" + | ||
" EMIT branch-on-count vp\<%index.next\>, vp\<%1\>\l" + | ||
"No successors\l" | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: seems a bit clearer to early continue?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, ignore, we continue also after handling a conditional branch.