[llvm] [VPlan] Implement VPlan-based unit-strideness speculation (PR #182595)
Andrei Elovikov via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 3 12:46:56 PDT 2026
================
@@ -5521,6 +5531,191 @@ void VPlanTransforms::makeMemOpWideningDecisions(VPlan &Plan, VFRange &Range,
});
}
+void VPlanTransforms::multiversionForUnitStridedMemOps(
+ VPlan &Plan, VPCostContext &CostCtx, VPRecipeBuilder &RecipeBuilder,
+ VFRange &Range, SmallVectorImpl<VPInstruction *> &MemOps) {
+ ScalarEvolution *SE = CostCtx.PSE.getSE();
+ PredicatedScalarEvolution StrideMVPSE(*SE, const_cast<Loop &>(*CostCtx.L));
+ SCEVUnionPredicate StridePredicates({}, *SE);
+
+ for (VPInstruction *VPI : MemOps) {
+ auto *PtrOp = VPI->getOpcode() == Instruction::Load ? VPI->getOperand(0)
+ : VPI->getOperand(1);
+
+ const SCEV *PtrSCEV =
+ vputils::getSCEVExprForVPValue(PtrOp, CostCtx.PSE, CostCtx.L);
+ const SCEV *Start, *Stride;
+
+ if (!match(PtrSCEV, m_scev_AffineAddRec(m_SCEV(Start), m_SCEV(Stride),
+ m_SpecificLoop(CostCtx.L))))
+ continue;
+
+ Type *ScalarTy = VPI->getOpcode() == Instruction::Load
+ ? VPI->getScalarType()
+ : VPI->getOperand(0)->getScalarType();
+
+ const auto *TypeSize = cast<SCEVConstant>(SE->getSizeOfExpr(
+ Stride->getType(), SE->getDataLayout().getTypeAllocSize(ScalarTy)));
+
+ if (isa<SCEVConstant>(Stride))
+ continue;
+
+ const SCEVConstant *StrideConstantMultiplier;
+ const SCEV *StrideNonConstantMultiplier;
+
+ const SCEV *ToMultiVersion = Stride;
+ const SCEV *MVConst = TypeSize;
+ if (match(Stride, m_scev_c_Mul(m_SCEVConstant(StrideConstantMultiplier),
+ m_SCEV(StrideNonConstantMultiplier)))) {
+ if (TypeSize != StrideConstantMultiplier) {
+ // TODO: Support `TypeSize = N * StrideConstantMultiplier`,
+ // including negative `N`. For now, only process when they're equal,
+ // which matches the useful part of the legacy behavior that
+ // multiversiones GEP index for stride one.
+ continue;
+ }
+ ToMultiVersion = StrideNonConstantMultiplier;
+ MVConst = SE->getOne(ToMultiVersion->getType());
+ } else if (!TypeSize->isOne()) {
+ // Likewise - try to match legacy behavior.
+ continue;
+ }
+
+ while (auto *C = dyn_cast<SCEVIntegralCastExpr>(ToMultiVersion)) {
+ ToMultiVersion = C->getOperand();
+ MVConst = SE->getTruncateOrSignExtend(MVConst, ToMultiVersion->getType());
+ }
+
+ if (match(ToMultiVersion, m_scev_UndefOrPoison()))
+ continue;
+
+ if (!isa<SCEVUnknown>(ToMultiVersion)) {
+ // Match legacy behavior.
+ // If/when changed, make sure that explicit poison/undef in the defining
+ // expression doesn't cause any issues.
+ continue;
+ }
+
+ Value *StrideVal = cast<SCEVUnknown>(ToMultiVersion)->getValue();
+
+ const SCEVPredicate *NewPred =
+ SE->getComparePredicate(CmpInst::ICMP_EQ, ToMultiVersion, MVConst);
+
+ // Check if new predicate implies that backedge is never taken. If so, there
+ // is no reason to multiversion for it.
+ auto *PredicatedMaxBTC = SE->rewriteUsingPredicate(
+ SE->getSymbolicMaxBackedgeTakenCount(CostCtx.L), CostCtx.L,
+ StridePredicates.getUnionWith(NewPred, *SE)
+ .getUnionWith(&CostCtx.PSE.getPredicate(), *SE));
+
+ if (LoopVectorizationPlanner::getDecisionAndClampRange(
+ [&](ElementCount VF) {
+ return SE->isKnownPredicate(
+ ICmpInst::ICMP_ULT, PredicatedMaxBTC,
+ SE->getConstant(PredicatedMaxBTC->getType(),
+ VF.isScalable() ? 1
+ : VF.getFixedValue() - 1));
+ },
+ Range))
+ continue;
+
+ if (VPI->getMask()) {
+ Instruction *I = VPI->getUnderlyingInstr();
+ bool IsLoad = VPI->getOpcode() == Instruction::Load;
+ // No reason to speculate unitstrideness if that won't improve vector code
+ // as we'd pay the price of not taking vector loop if the runtime
+ // condition is false for no benefits.
+ //
+ // We perform this clamping here because we don't want to
+ // `getDecisionAndClampRange` too early - here is better because we have
+ // another clamping right above.
+ if (!LoopVectorizationPlanner::getDecisionAndClampRange(
+ [&](ElementCount VF) -> bool {
+ return CostCtx.Config.isLegalMaskedLoadOrStore(
+ IsLoad, ScalarTy, getLoadStoreAlignment(I),
+ getLoadStoreAddressSpace(I));
+ },
+ Range))
+ continue;
+ }
+
+ StridePredicates = StridePredicates.getUnionWith(NewPred, *SE);
+
+ auto ReplaceMVUses = [&](Value *V) {
+ VPValue *From = Plan.getLiveIn(V);
+ if (!From)
+ return;
+ VPValue *To = Plan.getConstantInt(
+ From->getScalarType(),
+ cast<SCEVConstant>(MVConst)->getAPInt().getLimitedValue());
+
+ // Original scalar loop can still use `From`, make sure to only rewrite
+ // uses inside the vector loop that we guard with the checks.
+ From->replaceUsesWithIf(To, [&](VPUser &U, unsigned) {
+ auto *R = cast<VPRecipeBase>(&U);
+ return R->getRegion() ||
+ R->getParent() ==
+ Plan.getVectorLoopRegion()->getSinglePredecessor();
+ });
+ };
+
+ ReplaceMVUses(StrideVal);
+ for (auto *U : StrideVal->users())
+ if (isa<SExtInst, ZExtInst, TruncInst>(U))
+ ReplaceMVUses(U);
+ }
+
+ if (StridePredicates.isAlwaysTrue())
+ return;
+
+ VPBasicBlock *Entry = Plan.getEntry();
+ VPBuilder Builder(Entry);
+ DebugLoc DL = cast<VPIRBasicBlock>(Entry)
+ ->getIRBasicBlock()
+ ->getTerminator()
+ ->getDebugLoc();
+ VPSCEVExpander Expander(Builder, *SE, DL);
+ VPValue *Pred = Expander.tryToExpandPredicate(&StridePredicates);
+ assert(Pred && "Must be expandable!");
+
+ auto *StridesCheckBB = Plan.createVPBasicBlock("strides.check");
+ VPBasicBlock *ScalarPH = Plan.getScalarPreheader();
+ VPBlockUtils::insertBlockBefore(StridesCheckBB, Plan.getVectorPreheader());
+ VPBlockUtils::connectBlocks(StridesCheckBB, ScalarPH);
+ // SCEVExpander/VPSCEVExpander::expandCodeForPredicate negate the condition,
+ // so scalar preheader should be the first successor.
+ std::swap(StridesCheckBB->getSuccessors()[0],
+ StridesCheckBB->getSuccessors()[1]);
+ Builder.setInsertPoint(StridesCheckBB);
+ Builder.createNaryOp(VPInstruction::BranchOnCond, Pred);
+
+ for (VPRecipeBase &R : ScalarPH->phis()) {
+ auto &Phi = cast<VPPhi>(R);
+ Phi.addIncoming(Phi.getIncomingValueForBlock(Entry));
+ }
+
+ for (auto &R : make_early_inc_range(*Entry)) {
+ auto *ExpandSCEV = dyn_cast<VPExpandSCEVRecipe>(&R);
+ if (!ExpandSCEV)
+ continue;
+
+ const SCEV *S = ExpandSCEV->getSCEV();
+ Builder.setInsertPoint(ExpandSCEV);
+ const SCEV *NewS =
+ SE->rewriteUsingPredicate(S, CostCtx.L, StridePredicates);
+ if (NewS == S)
+ return;
----------------
eas wrote:
Yes, thanks!
https://github.com/llvm/llvm-project/pull/182595
More information about the llvm-commits
mailing list