Skip to content

Commit 64510d8

Browse files
committed
[VPlan] Convert EVL loops to variable-length stepping after dissolution
Loop regions require fixed-length steps and rounded-up trip counts, but after dissolution creates explicit control flow, EVL loops can leverage variable-length stepping with original trip counts. This patch adds a post-dissolution transform pass to convert EVL loops from fixed-length to variable-length stepping .
1 parent a9d491b commit 64510d8

File tree

4 files changed

+68
-10
lines changed

4 files changed

+68
-10
lines changed

llvm/lib/Transforms/Vectorize/LoopVectorize.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7315,6 +7315,8 @@ DenseMap<const SCEV *, Value *> LoopVectorizationPlanner::executePlan(
73157315
// Regions are dissolved after optimizing for VF and UF, which completely
73167316
// removes unneeded loop regions first.
73177317
VPlanTransforms::dissolveLoopRegions(BestVPlan);
7318+
// Enable variable-length stepping for EVL loops after regions are dissolved
7319+
VPlanTransforms::simplifyEVLIVs(BestVPlan);
73187320
// Perform the actual loop transformation.
73197321
VPTransformState State(&TTI, BestVF, LI, DT, ILV.AC, ILV.Builder, &BestVPlan,
73207322
OrigLoop->getParentLoop(),

llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp

Lines changed: 52 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2390,6 +2390,58 @@ bool VPlanTransforms::tryAddExplicitVectorLength(
23902390
return true;
23912391
}
23922392

2393+
void VPlanTransforms::simplifyEVLIVs(VPlan &Plan) {
2394+
auto ConvertEVLPhi = [](VPlan &Plan, VPBasicBlock *Entry,
2395+
VPEVLBasedIVPHIRecipe *EVLPhi) {
2396+
using namespace llvm::VPlanPatternMatch;
2397+
VPValue *EVLIncrement = EVLPhi->getBackedgeValue();
2398+
2399+
// Convert EVLPhi to concrete recipe.
2400+
auto *ScalarR = VPBuilder(EVLPhi).createScalarPhi(
2401+
{EVLPhi->getStartValue(), EVLIncrement}, EVLPhi->getDebugLoc(),
2402+
"evl.based.iv");
2403+
EVLPhi->replaceAllUsesWith(ScalarR);
2404+
EVLPhi->eraseFromParent();
2405+
2406+
// Find the latch-exiting block and convert to variable-length stepping.
2407+
// Before: (branch-on-cond CanonicalIVInc, VectorTripCount)
2408+
// After: (branch-on-cond EVLIVInc, TripCount)
2409+
auto FindLatchExiting = [](VPBasicBlock *Entry) {
2410+
auto Range =
2411+
VPBlockUtils::blocksOnly<VPBasicBlock>(vp_depth_first_shallow(Entry));
2412+
auto It = find_if(Range, [&](VPBasicBlock *VPBB) {
2413+
return any_of(VPBB->successors(),
2414+
[&](VPBlockBase *Succ) { return Succ == Entry; });
2415+
});
2416+
return It != Range.end() ? *It : nullptr;
2417+
};
2418+
VPBasicBlock *LatchExiting = FindLatchExiting(Entry);
2419+
assert(LatchExiting && "LatchExiting is not found");
2420+
auto *LatchExitingBr = cast<VPInstruction>(LatchExiting->getTerminator());
2421+
VPValue *ScalarIVInc;
2422+
if (!LatchExitingBr ||
2423+
!match(LatchExitingBr,
2424+
m_BranchOnCount(m_VPValue(ScalarIVInc),
2425+
m_Specific(&Plan.getVectorTripCount()))))
2426+
return;
2427+
LatchExitingBr->setOperand(1, Plan.getTripCount());
2428+
ScalarIVInc->replaceAllUsesWith(EVLIncrement);
2429+
VPRecipeBase *IVIncR = ScalarIVInc->getDefiningRecipe();
2430+
VPRecipeBase *ScalarIV = IVIncR->getOperand(0)->getDefiningRecipe();
2431+
IVIncR->eraseFromParent();
2432+
ScalarIV->eraseFromParent();
2433+
};
2434+
2435+
// Find EVL loop entries by locating VPEVLBasedIVPHIRecipe
2436+
for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
2437+
vp_depth_first_shallow(Plan.getEntry())))
2438+
for (VPRecipeBase &R : VPBB->phis())
2439+
if (auto *PhiR = dyn_cast<VPEVLBasedIVPHIRecipe>(&R)) {
2440+
ConvertEVLPhi(Plan, VPBB, PhiR);
2441+
break;
2442+
}
2443+
}
2444+
23932445
void VPlanTransforms::dropPoisonGeneratingRecipes(
23942446
VPlan &Plan,
23952447
const std::function<bool(BasicBlock *)> &BlockNeedsPredication) {
@@ -2721,15 +2773,6 @@ void VPlanTransforms::convertToConcreteRecipes(VPlan &Plan,
27212773
for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(
27222774
vp_depth_first_deep(Plan.getEntry()))) {
27232775
for (VPRecipeBase &R : make_early_inc_range(*VPBB)) {
2724-
if (auto *PhiR = dyn_cast<VPEVLBasedIVPHIRecipe>(&R)) {
2725-
auto *ScalarR = VPBuilder(PhiR).createScalarPhi(
2726-
{PhiR->getStartValue(), PhiR->getBackedgeValue()},
2727-
PhiR->getDebugLoc(), "evl.based.iv");
2728-
PhiR->replaceAllUsesWith(ScalarR);
2729-
ToRemove.push_back(PhiR);
2730-
continue;
2731-
}
2732-
27332776
if (auto *WidenIVR = dyn_cast<VPWidenIntOrFpInductionRecipe>(&R)) {
27342777
expandVPWidenIntOrFpInduction(WidenIVR, TypeInfo);
27352778
ToRemove.push_back(WidenIVR);

llvm/lib/Transforms/Vectorize/VPlanTransforms.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,6 +209,17 @@ struct VPlanTransforms {
209209
/// Replace loop regions with explicit CFG.
210210
static void dissolveLoopRegions(VPlan &Plan);
211211

212+
/// Transform EVL loops to use variable-length stepping after region
213+
/// dissolution.
214+
///
215+
/// Once loop regions are replaced with explicit CFG, EVL loops can step with
216+
/// variable vector lengths instead of fixed lengths. This transformation:
217+
/// * EVL-Phi concretization (makes them concrete)
218+
/// * Replaces fixed-length stepping (branch-on-cond CanonicalIVInc,
219+
/// VectorTripCount) with variable-length stepping (branch-on-cond
220+
/// EVLIVInc, TripCount).
221+
static void simplifyEVLIVs(VPlan &Plan);
222+
212223
/// Lower abstract recipes to concrete ones, that can be codegen'd. Use \p
213224
/// CanonicalIVTy as type for all un-typed live-ins in VPTypeAnalysis.
214225
static void convertToConcreteRecipes(VPlan &Plan, Type &CanonicalIVTy);

llvm/lib/Transforms/Vectorize/VPlanVerifier.cpp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -193,7 +193,9 @@ bool VPlanVerifier::verifyEVLRecipe(const VPInstruction &EVL) const {
193193
errs() << "EVL used by unexpected VPInstruction\n";
194194
return false;
195195
}
196-
if (I->getNumUsers() != 1) {
196+
// EVLIVIncrement is only used by EVLIV & BranchOnCount.
197+
// More than two is unexpected.
198+
if (I->getNumUsers() > 2) {
197199
errs() << "EVL is used in VPInstruction with multiple users\n";
198200
return false;
199201
}

0 commit comments

Comments
 (0)