[VPlan] Construct immutable VPIRBBs for exit blocks at construction(NFC) (#128374)

Constract immutable VPIRBasicBlocks for all exit blocks up front and
keep a list of them. Same as the scalar header, they are leaf nodes of
the VPlan and won't change. Some exit blocks may be unreachable, e.g. if
the scalar epilogue always executes or depending on optimizations.

This simplifies both the way we retrieve the exit blocks as well as
hooking up the exit blocks.

PR: https://github.com/llvm/llvm-project/pull/128374
This commit is contained in:
Florian Hahn 2025-02-25 14:23:27 +00:00 committed by GitHub
parent d23da7d630
commit 522b05afb6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 33 additions and 27 deletions

View File

@ -9132,6 +9132,10 @@ collectUsersInExitBlocks(Loop *OrigLoop, VPRecipeBuilder &Builder,
VPlan &Plan) {
SetVector<VPIRInstruction *> ExitUsersToFix;
for (VPIRBasicBlock *ExitVPBB : Plan.getExitBlocks()) {
// Nothing to do for unreachable exit blocks.
if (ExitVPBB->getNumPredecessors() == 0)
continue;
for (VPRecipeBase &R : *ExitVPBB) {
auto *ExitIRI = dyn_cast<VPIRInstruction>(&R);
if (!ExitIRI)

View File

@ -850,6 +850,11 @@ void VPRegionBlock::print(raw_ostream &O, const Twine &Indent,
VPlan::VPlan(Loop *L) {
setEntry(createVPIRBasicBlock(L->getLoopPreheader()));
ScalarHeader = createVPIRBasicBlock(L->getHeader());
SmallVector<BasicBlock *> IRExitBlocks;
L->getExitBlocks(IRExitBlocks);
for (BasicBlock *EB : IRExitBlocks)
ExitBlocks.push_back(createVPIRBasicBlock(EB));
}
VPlan::~VPlan() {
@ -931,7 +936,7 @@ VPlanPtr VPlan::createInitialVPlan(Type *InductionTy,
// we unconditionally branch to the scalar preheader. Do nothing.
// 3) Otherwise, construct a runtime check.
BasicBlock *IRExitBlock = TheLoop->getUniqueLatchExitBlock();
auto *VPExitBlock = Plan->createVPIRBasicBlock(IRExitBlock);
VPIRBasicBlock *VPExitBlock = Plan->getExitBlock(IRExitBlock);
// The connection order corresponds to the operands of the conditional branch.
VPBlockUtils::insertBlockAfter(VPExitBlock, MiddleVPBB);
VPBlockUtils::connectBlocks(MiddleVPBB, ScalarPH);
@ -983,6 +988,14 @@ void VPlan::prepareToExecute(Value *TripCountV, Value *VectorTripCountV,
}
}
VPIRBasicBlock *VPlan::getExitBlock(BasicBlock *IRBB) const {
auto Iter = find_if(getExitBlocks(), [IRBB](const VPIRBasicBlock *VPIRBB) {
return VPIRBB->getIRBasicBlock() == IRBB;
});
assert(Iter != getExitBlocks().end() && "no exit block found");
return *Iter;
}
bool VPlan::isExitBlock(VPBlockBase *VPBB) {
return isa<VPIRBasicBlock>(VPBB) && VPBB->getNumSuccessors() == 0;
}

View File

@ -3426,6 +3426,11 @@ class VPlan {
/// VPIRBasicBlock wrapping the header of the original scalar loop.
VPIRBasicBlock *ScalarHeader;
/// Immutable list of VPIRBasicBlocks wrapping the exit blocks of the original
/// scalar loop. Note that some exit blocks may be unreachable at the moment,
/// e.g. if the scalar epilogue always executes.
SmallVector<VPIRBasicBlock *, 2> ExitBlocks;
/// Holds the VFs applicable to this VPlan.
SmallSetVector<ElementCount, 2> VFs;
@ -3559,11 +3564,13 @@ public:
/// Return the VPIRBasicBlock wrapping the header of the scalar loop.
VPIRBasicBlock *getScalarHeader() const { return ScalarHeader; }
/// Return an iterator range over the VPIRBasicBlock wrapping the exit blocks
/// of the VPlan, that is leaf nodes except the scalar header. Defined in
/// VPlanHCFG, as the definition of the type needs access to the definitions
/// of VPBlockShallowTraversalWrapper.
auto getExitBlocks();
/// Return an ArrayRef containing VPIRBasicBlocks wrapping the exit blocks of
/// the original scalar loop.
ArrayRef<VPIRBasicBlock *> getExitBlocks() const { return ExitBlocks; }
/// Return the VPIRBasicBlock corresponding to \p IRBB. \p IRBB must be an
/// exit block.
VPIRBasicBlock *getExitBlock(BasicBlock *IRBB) const;
/// Returns true if \p VPBB is an exit block.
bool isExitBlock(VPBlockBase *VPBB);

View File

@ -307,15 +307,6 @@ template <> struct GraphTraits<VPlan *> {
}
};
inline auto VPlan::getExitBlocks() {
VPBlockBase *ScalarHeader = getScalarHeader();
return make_filter_range(
VPBlockUtils::blocksOnly<VPIRBasicBlock>(
vp_depth_first_shallow(getVectorLoopRegion()->getSingleSuccessor())),
[ScalarHeader](VPIRBasicBlock *VPIRBB) {
return VPIRBB != ScalarHeader && VPIRBB->getNumSuccessors() == 0;
});
}
} // namespace llvm
#endif // LLVM_TRANSFORMS_VECTORIZE_VPLANCFG_H

View File

@ -2049,18 +2049,9 @@ void VPlanTransforms::handleUncountableEarlyExit(
cast<BranchInst>(UncountableExitingBlock->getTerminator());
BasicBlock *TrueSucc = EarlyExitingBranch->getSuccessor(0);
BasicBlock *FalseSucc = EarlyExitingBranch->getSuccessor(1);
// The early exit block may or may not be the same as the "countable" exit
// block. Creates a new VPIRBB for the early exit block in case it is distinct
// from the countable exit block.
// TODO: Introduce both exit blocks during VPlan skeleton construction.
VPIRBasicBlock *VPEarlyExitBlock;
if (OrigLoop->getUniqueExitBlock()) {
VPEarlyExitBlock = cast<VPIRBasicBlock>(MiddleVPBB->getSuccessors()[0]);
} else {
VPEarlyExitBlock = Plan.createVPIRBasicBlock(
!OrigLoop->contains(TrueSucc) ? TrueSucc : FalseSucc);
}
BasicBlock *EarlyExitIRBB =
!OrigLoop->contains(TrueSucc) ? TrueSucc : FalseSucc;
VPIRBasicBlock *VPEarlyExitBlock = Plan.getExitBlock(EarlyExitIRBB);
VPValue *EarlyExitNotTakenCond = RecipeBuilder.getBlockInMask(
OrigLoop->contains(TrueSucc) ? TrueSucc : FalseSucc);