[llvm] [LV] Vectorize early exit loops with stores using masking (PR #178454)
Graham Hunter via llvm-commits
llvm-commits at lists.llvm.org
Fri May 1 07:59:19 PDT 2026
================
@@ -3969,17 +3970,202 @@ void VPlanTransforms::convertToConcreteRecipes(VPlan &Plan) {
R->eraseFromParent();
}
-void VPlanTransforms::handleUncountableEarlyExits(VPlan &Plan,
- VPBasicBlock *HeaderVPBB,
- VPBasicBlock *LatchVPBB,
- VPBasicBlock *MiddleVPBB,
- UncountableExitStyle Style) {
- struct EarlyExitInfo {
- VPBasicBlock *EarlyExitingVPBB;
- VPIRBasicBlock *EarlyExitVPBB;
- VPValue *CondToExit;
- };
+struct EarlyExitInfo {
+ VPBasicBlock *EarlyExitingVPBB;
+ VPIRBasicBlock *EarlyExitVPBB;
+ VPValue *CondToExit;
+};
+
+/// Update \p Plan to mask memory operations in the loop based on whether the
+/// early exit is taken or not.
+static bool handleUncountableExitsWithSideEffects(
+ VPlan &Plan, SmallVectorImpl<EarlyExitInfo> &Exits,
+ VPBasicBlock *HeaderVPBB, VPBasicBlock *LatchVPBB, VPBasicBlock *MiddleVPBB,
+ Loop *TheLoop, PredicatedScalarEvolution &PSE, DominatorTree &DT,
+ AssumptionCache *AC) {
+ if (Plan.hasScalarVFOnly())
+ return false;
+
+ Plan.setHasUncountableExitWithSideEffects();
+
+ // Disconnect early exiting blocks from successors, remove branches. We
+ // currently don't support multiple uses for recipes involved in creating
+ // the uncountable exit condition.
+ for (auto &Exit : Exits) {
+ if (Exit.EarlyExitingVPBB == LatchVPBB)
+ continue;
+
+ for (VPRecipeBase &R : Exit.EarlyExitVPBB->phis())
+ cast<VPIRPhi>(&R)->removeIncomingValueFor(Exit.EarlyExitingVPBB);
+ Exit.EarlyExitingVPBB->getTerminator()->eraseFromParent();
+ VPBlockUtils::disconnectBlocks(Exit.EarlyExitingVPBB, Exit.EarlyExitVPBB);
+ }
+
+ // We can abandon a vplan entirely if we return false here, so we shouldn't
+ // crash if some earlier assumptions on scalar IR don't hold for the vplan
+ // version of the loop.
+ SmallVector<VPInstruction *, 2> GEPs;
+ SmallVector<VPInstruction *, 8> ConditionRecipes;
+
+ std::optional<VPValue *> Cond =
+ vputils::getRecipesForUncountableExit(ConditionRecipes, GEPs, LatchVPBB);
+ if (!Cond)
+ return false;
+
+ // Find load contributing to condition.
+ VPRecipeBase *CondLoad = nullptr;
+ for (auto *Recipe : ConditionRecipes) {
+ if (match(Recipe, m_VPInstruction<Instruction::Load>(m_VPValue()))) {
+ // TODO: Support more than one load. Needs legality updates too.
+ assert(CondLoad == nullptr && "Too many condition loads\n");
+ CondLoad = Recipe;
+ }
+ }
+ assert(CondLoad && "Couldn't find load\n");
+
+ // Ensure that we are guaranteed to be able to dereference the memory used
+ // for determining the uncountable exit for the maximum possible number of
+ // scalar iterations of the loop.
+ //
+ // TODO: Support first-faulting loads in cases where we don't know whether
+ // all possible addresses are dereferenceable.
+ {
+ SmallVector<const SCEVPredicate *, 4> Predicates;
+ VPSingleDefRecipe *Load = cast<VPSingleDefRecipe>(CondLoad);
+ VPValue *Ptr = Load->getOperand(0);
+ const SCEV *PtrSCEV = vputils::getSCEVExprForVPValue(Ptr, PSE, TheLoop);
+ const DataLayout &DL = Plan.getDataLayout();
+ VPTypeAnalysis TypeInfo(Plan);
+ APInt EltSize(
+ DL.getIndexTypeSizeInBits(TypeInfo.inferScalarType(Ptr)),
+ DL.getTypeStoreSize(TypeInfo.inferScalarType(Load)).getFixedValue());
+ if (!isDereferenceableAndAlignedInLoop(
+ PtrSCEV, cast<LoadInst>(Load->getUnderlyingInstr())->getAlign(),
+ PSE.getSE()->getConstant(EltSize), TheLoop, *PSE.getSE(), DT, AC,
+ &Predicates))
+ return false;
+ }
+
+ // Check GEPs to see if we can link them to a widen IV recipe with a step of
+ // 1; we're only interested in contiguous accesses for the condition load
+ // right now.
+ for (auto *GEP : GEPs) {
+ VPValue *MaybeIV = nullptr;
+ if (!match(GEP, m_VPInstruction<Instruction::GetElementPtr>(
+ m_LiveIn(), m_VPValue(MaybeIV))))
+ return false;
+
+ auto *WIV = dyn_cast<VPWidenInductionRecipe>(MaybeIV);
+ if (!WIV)
+ return false;
+
+ auto *ConstStart = dyn_cast<VPConstantInt>(WIV->getStartValue());
+ if (!ConstStart || !ConstStart->isZero())
+ return false;
+
+ auto *ConstStep = dyn_cast<VPConstantInt>(WIV->getStepValue());
+ if (!ConstStep || !ConstStep->isOne())
+ return false;
+ }
+
+ // Find an insertion point. Default to the end of the header (we haven't
+ // performed if-conversion yet, so there will likely be more than one block),
+ // but override if we find a memory op that needs masking before the
+ // condition load.
+ auto InsertIt = HeaderVPBB->end();
+ VPRecipeBase *CondR = (*Cond)->getDefiningRecipe();
+ bool CondMoveNeeded = CondR->getParent() != HeaderVPBB;
+ VPDominatorTree VPDT(Plan);
+ for (VPRecipeBase &R : make_early_inc_range(*HeaderVPBB)) {
+ if (&R == CondLoad)
+ continue;
+
+ if (match(&R, m_CombineOr(m_VPInstruction<Instruction::Load>(m_VPValue()),
+ m_VPInstruction<Instruction::Store>(
+ m_VPValue(), m_VPValue())))) {
+ if (!VPDT.properlyDominates(CondR, &R)) {
+ CondMoveNeeded = true;
+ InsertIt = R.getIterator();
+ }
+ break;
+ }
+ }
+
+ // If another memory operation would take place before the comparison to
+ // determine whether to exit early or the comparison doesn't take place in
+ // the header, move the comparison (and supporting recipes).
+ if (CondMoveNeeded) {
+ for (auto *Recipe : reverse(ConditionRecipes))
+ Recipe->moveBefore(*HeaderVPBB, InsertIt);
+
+ CondR->moveBefore(*HeaderVPBB, InsertIt);
+ }
+
+ // Create a mask to represent all lanes that fully execute in the vector loop,
+ // stopping short of any early exit.
+ VPBuilder MaskBuilder(HeaderVPBB, InsertIt);
+ VPValue *FirstActive =
+ MaskBuilder.createNaryOp(VPInstruction::FirstActiveLane, *Cond);
+ VPTypeAnalysis TypeInfo(Plan);
+ VPValue *IV = cast<VPSingleDefRecipe>(&HeaderVPBB->front());
+ Type *IVScalarTy = TypeInfo.inferScalarType(IV);
+ VPValue *ALMMultiplier = Plan.getConstantInt(IVScalarTy, 1);
+ VPValue *Zero = Plan.getConstantInt(IVScalarTy, 0);
+ VPValue *Mask = MaskBuilder.createNaryOp(VPInstruction::ActiveLaneMask,
+ {Zero, FirstActive, ALMMultiplier},
+ nullptr, "uncountable.exit.mask");
+
+ // Convert all other memory operations to use the mask.
+ ReversePostOrderTraversal<VPBlockShallowTraversalWrapper<VPBlockBase *>> RPOT(
+ HeaderVPBB);
+ for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(RPOT)) {
+ for (VPRecipeBase &R : make_early_inc_range(*VPBB)) {
----------------
huntergr-arm wrote:
removed.
https://github.com/llvm/llvm-project/pull/178454
More information about the llvm-commits
mailing list