-
Notifications
You must be signed in to change notification settings - Fork 15.1k
[VPlan] Handle early exit before forming regions. (NFC) #138393
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 2 commits
d0d2c2e
76c470a
80a629c
56d576a
ce06761
2289a5e
4747678
b74e363
dd38677
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 |
|---|---|---|
|
|
@@ -9384,7 +9384,8 @@ LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(VFRange &Range) { | |
| VPlanTransforms::prepareForVectorization( | ||
| *Plan, Legal->getWidestInductionType(), PSE, RequiresScalarEpilogueCheck, | ||
| CM.foldTailByMasking(), OrigLoop, | ||
| getDebugLocFromInstOrOperands(Legal->getPrimaryInduction())); | ||
| getDebugLocFromInstOrOperands(Legal->getPrimaryInduction()), | ||
| Legal->hasUncountableEarlyExit(), Range); | ||
|
Collaborator
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. Does passing
Contributor
Author
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.
If it is false, it means there are no uncountable early exits and we require a scalar epilogue
Collaborator
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, thanks for clarifying! |
||
| VPlanTransforms::createLoopRegions(*Plan); | ||
|
|
||
| // Don't use getDecisionAndClampRange here, because we don't know the UF | ||
|
|
@@ -9582,12 +9583,6 @@ LoopVectorizationPlanner::tryToBuildVPlanWithVPRecipes(VFRange &Range) { | |
| R->setOperand(1, WideIV->getStepValue()); | ||
| } | ||
|
|
||
| if (auto *UncountableExitingBlock = | ||
| Legal->getUncountableEarlyExitingBlock()) { | ||
| VPlanTransforms::runPass(VPlanTransforms::handleUncountableEarlyExit, *Plan, | ||
| OrigLoop, UncountableExitingBlock, RecipeBuilder, | ||
| Range); | ||
| } | ||
| DenseMap<VPValue *, VPValue *> IVEndValues; | ||
| addScalarResumePhis(RecipeBuilder, *Plan, IVEndValues); | ||
| SetVector<VPIRInstruction *> ExitUsersToFix = | ||
|
|
@@ -9685,7 +9680,8 @@ VPlanPtr LoopVectorizationPlanner::tryToBuildVPlan(VFRange &Range) { | |
| auto Plan = VPlanTransforms::buildPlainCFG(OrigLoop, *LI, VPB2IRBB); | ||
| VPlanTransforms::prepareForVectorization( | ||
| *Plan, Legal->getWidestInductionType(), PSE, true, false, OrigLoop, | ||
| getDebugLocFromInstOrOperands(Legal->getPrimaryInduction())); | ||
| getDebugLocFromInstOrOperands(Legal->getPrimaryInduction()), false, | ||
| Range); | ||
| VPlanTransforms::createLoopRegions(*Plan); | ||
|
|
||
| for (ElementCount VF : Range) | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -460,11 +460,10 @@ static void addCanonicalIVRecipes(VPlan &Plan, VPBasicBlock *HeaderVPBB, | |||||||||
| {CanonicalIVIncrement, &Plan.getVectorTripCount()}, DL); | ||||||||||
| } | ||||||||||
|
|
||||||||||
| void VPlanTransforms::prepareForVectorization(VPlan &Plan, Type *InductionTy, | ||||||||||
| PredicatedScalarEvolution &PSE, | ||||||||||
| bool RequiresScalarEpilogueCheck, | ||||||||||
| bool TailFolded, Loop *TheLoop, | ||||||||||
| DebugLoc IVDL) { | ||||||||||
| void VPlanTransforms::prepareForVectorization( | ||||||||||
| VPlan &Plan, Type *InductionTy, PredicatedScalarEvolution &PSE, | ||||||||||
| bool RequiresScalarEpilogueCheck, bool TailFolded, Loop *TheLoop, | ||||||||||
| DebugLoc IVDL, bool HasUncountableEarlyExit, VFRange &Range) { | ||||||||||
| VPDominatorTree VPDT; | ||||||||||
| VPDT.recalculate(Plan); | ||||||||||
|
|
||||||||||
|
|
@@ -491,19 +490,35 @@ void VPlanTransforms::prepareForVectorization(VPlan &Plan, Type *InductionTy, | |||||||||
| 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()) { | ||||||||||
|
Contributor
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. Perhaps worth replacing the old comment with a new one explaining what the loop is doing at a high level?
Contributor
Author
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. Done thanks |
||||||||||
| [[maybe_unused]] bool HandledUncountableEarlyExit = false; | ||||||||||
| for (VPIRBasicBlock *EB : Plan.getExitBlocks()) { | ||||||||||
| for (VPBlockBase *Pred : to_vector(EB->getPredecessors())) { | ||||||||||
| if (Pred == MiddleVPBB) | ||||||||||
| 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.
dropped, thanks
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.
Independent note: this condition disregards the actual Pred->EB edge itself.
Outdated
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.
| // Convert VPlans with early exits to a form exiting only via the latch | |
| // here, including adjusting the exit condition of the latch. |
Explained above, plus can document the function itself.
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.
Thanks, removed
Outdated
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.
| continue; |
All early exits are disconnected.
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.
We could fall exit the loop here, but then the verification would be more work. For all supported loops, we should leave the loop after the continue.
Outdated
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.
| // Otherwise all early exits must be countable and we require at least one | |
| // iteration in the scalar epilogue. Disconnect all edges to exit blocks | |
| // other than from the middle block. |
Explanation above suffices?
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.
Removed, thanks
Outdated
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.
Note that this essentially checks if any early exit was found.
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.
Tried to make this clearer in the message: missed an uncountable exit that must be handled
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -2458,64 +2458,60 @@ void VPlanTransforms::convertToConcreteRecipes(VPlan &Plan, | |||||
| R->eraseFromParent(); | ||||||
| } | ||||||
|
|
||||||
| void VPlanTransforms::handleUncountableEarlyExit( | ||||||
| VPlan &Plan, Loop *OrigLoop, BasicBlock *UncountableExitingBlock, | ||||||
| VPRecipeBuilder &RecipeBuilder, VFRange &Range) { | ||||||
| VPRegionBlock *LoopRegion = Plan.getVectorLoopRegion(); | ||||||
| auto *LatchVPBB = cast<VPBasicBlock>(LoopRegion->getExiting()); | ||||||
| VPBuilder Builder(LatchVPBB->getTerminator()); | ||||||
| auto *MiddleVPBB = Plan.getMiddleBlock(); | ||||||
| VPValue *IsEarlyExitTaken = nullptr; | ||||||
|
|
||||||
| // Process the uncountable exiting block. Update IsEarlyExitTaken, which | ||||||
| // tracks if the uncountable early exit has been taken. Also split the middle | ||||||
| // block and have it conditionally branch to the early exit block if | ||||||
| // EarlyExitTaken. | ||||||
| auto *EarlyExitingBranch = | ||||||
| cast<BranchInst>(UncountableExitingBlock->getTerminator()); | ||||||
| BasicBlock *TrueSucc = EarlyExitingBranch->getSuccessor(0); | ||||||
| BasicBlock *FalseSucc = EarlyExitingBranch->getSuccessor(1); | ||||||
| BasicBlock *EarlyExitIRBB = | ||||||
| !OrigLoop->contains(TrueSucc) ? TrueSucc : FalseSucc; | ||||||
| VPIRBasicBlock *VPEarlyExitBlock = Plan.getExitBlock(EarlyExitIRBB); | ||||||
|
|
||||||
| VPValue *EarlyExitNotTakenCond = RecipeBuilder.getBlockInMask( | ||||||
| OrigLoop->contains(TrueSucc) ? TrueSucc : FalseSucc); | ||||||
| auto *EarlyExitTakenCond = Builder.createNot(EarlyExitNotTakenCond); | ||||||
| IsEarlyExitTaken = | ||||||
| Builder.createNaryOp(VPInstruction::AnyOf, {EarlyExitTakenCond}); | ||||||
| void VPlanTransforms::handleUncountableEarlyExit(VPBasicBlock *EarlyExitingVPBB, | ||||||
| VPBasicBlock *EarlyExitVPBB, | ||||||
|
|
||||||
|
||||||
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.
removed thanks
Outdated
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.
| EarlyExitVPBB->getPredecessors()[0] != MiddleVPBB) { | |
| EarlyExitVPBB->getPredecessors()[0] == EarlyExitingVPBB) { |
Clearer and more consistent with explanation below? Or
EarlyExitVPBB->getPredecessors()[1] != EarlyExitingVPBB
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.
Updated thanks
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.
Is it worth adding an assert that MiddleVPBB is actually the second predecessor?
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.
done, thanks
Outdated
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.
| // has two predecessors and MiddleVPBB isn't the first, swap the operands of | |
| // has two predecessors and EarlyExitingVPBB is the first, swap the operands of |
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.
Updated thanks
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.
This is making sure the operand corresponding to the middle block is always first, right? I guess in future if we do want to support multiple early exits this will get a bit more complicated, but swapping operands works for now.
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.
Yep
Outdated
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.
This could be left to the caller, as it is common to dealing with early exits via scalar epilog?
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.
Done thanks
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.
Waiting for a commit?
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.
Ah yes, should be included in the latest update
Outdated
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.
| EarlyExitingVPBB->getTerminator()->eraseFromParent(); | |
| VPBlockUtils::disconnectBlocks(EarlyExitingVPBB, EarlyExitVPBB); |
Have the caller take care of this, for all early exits consistently?
Outdated
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.
| // block if EarlyExitTaken. | |
| // block if CondToEarlyExit. |
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.
Updated, thanks
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.
Independent:
| VPBasicBlock *NewMiddle = Plan.createVPBasicBlock("middle.split"); | |
| VPBasicBlock *MiddleSplit = Plan.createVPBasicBlock("middle.split"); |
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.
Can do separately, thanks
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.
Is this comment still valid given you've already swapped operands above?
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.
ep, this just re-states the expected order, as explanation for setting early exit index below.
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.
Not relevant for this patch, but the name extractLastLaneOfFirstOperand confused me at first because I was expecting it to return a value. It feels like it should be something like updatedFirstOperandToExtractLastLane.
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.
Might be good to improve the naming, but it may be even better to create all required extracts up-front separately, which is something I am looking into
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -69,7 +69,8 @@ struct VPlanTransforms { | |||||
| PredicatedScalarEvolution &PSE, | ||||||
| bool RequiresScalarEpilogueCheck, | ||||||
| bool TailFolded, Loop *TheLoop, | ||||||
| DebugLoc IVDL); | ||||||
| DebugLoc IVDL, bool HandleUncountableExit, | ||||||
|
||||||
| DebugLoc IVDL, bool HandleUncountableExit, | |
| DebugLoc IVDL, bool HasUncountableEarlyExit, |
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.
Updated, thanks
Outdated
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.
| /// * updating the condition exiting the vector loop to include the early | |
| /// * updating the condition exiting the loop via the latch to include the early |
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.
Done thanks
Outdated
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.
| /// exit conditions | |
| /// exit condition. |
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.
updated
Outdated
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.
| /// if taken. | |
| /// conditionally - according to the early exit condition. |
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.
updated thanks
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.
(Independent) Should
prepareForVectorizationbe renamed to a more informative name, perhapscanonicalizeTopLoop, as it takes care of canonicalizing header and latch blocks, introducing and connecting preheader, middle-block, scalar preheader, canonical IV recipes and trip-count value.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.
Will to separately.