[llvm] [VPlan] Split off VPReductionRecipe creation for in-loop reductions (NFC) (PR #168784)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Wed Dec 24 15:23:20 PST 2025
================
@@ -699,6 +700,164 @@ void VPlanTransforms::createHeaderPhiRecipes(
}
}
+void VPlanTransforms::createVPReductionRecipesForInLoopReductions(
+ VPlan &Plan, const DenseMap<VPBasicBlock *, VPValue *> &BlockMaskCache,
+ const DenseSet<BasicBlock *> &BlocksNeedingPredication,
+ ElementCount MinVF) {
+ VPTypeAnalysis TypeInfo(Plan);
+ VPBasicBlock *Header = Plan.getVectorLoopRegion()->getEntryBasicBlock();
+ SmallVector<VPRecipeBase *> ToDelete;
+
+ for (VPRecipeBase &R : Header->phis()) {
+ auto *PhiR = dyn_cast<VPReductionPHIRecipe>(&R);
+ if (!PhiR || !PhiR->isInLoop() || (MinVF.isScalar() && !PhiR->isOrdered()))
+ continue;
+
+ RecurKind Kind = PhiR->getRecurrenceKind();
+ assert(
+ !RecurrenceDescriptor::isAnyOfRecurrenceKind(Kind) &&
+ !RecurrenceDescriptor::isFindIVRecurrenceKind(Kind) &&
+ "AnyOf and FindIV reductions are not allowed for in-loop reductions");
+
+ bool IsFPRecurrence =
+ RecurrenceDescriptor::isFloatingPointRecurrenceKind(Kind);
+ FastMathFlags FMFs =
+ IsFPRecurrence ? FastMathFlags::getFast() : FastMathFlags();
+
+ // Collect the chain of "link" recipes for the reduction starting at PhiR.
+ SetVector<VPSingleDefRecipe *> Worklist;
+ Worklist.insert(PhiR);
+ for (unsigned I = 0; I != Worklist.size(); ++I) {
+ VPSingleDefRecipe *Cur = Worklist[I];
+ for (VPUser *U : Cur->users()) {
+ auto *UserRecipe = cast<VPSingleDefRecipe>(U);
+ if (!UserRecipe->getParent()->getEnclosingLoopRegion()) {
+ assert((UserRecipe->getParent() == Plan.getMiddleBlock() ||
+ UserRecipe->getParent() == Plan.getScalarPreheader()) &&
+ "U must be either in the loop region, the middle block or the "
+ "scalar preheader.");
+ continue;
+ }
+
+ // Stores using instructions will be sunk later.
+ if (match(UserRecipe, m_VPInstruction<Instruction::Store>()))
+ continue;
+ Worklist.insert(UserRecipe);
+ }
+ }
+
+ // Visit operation "Links" along the reduction chain top-down starting from
+ // the phi until LoopExitValue. We keep track of the previous item
+ // (PreviousLink) to tell which of the two operands of a Link will remain
+ // scalar and which will be reduced. For minmax by select(cmp), Link will be
+ // the select instructions. Blend recipes of in-loop reduction phi's will
+ // get folded to their non-phi operand, as the reduction recipe handles the
+ // condition directly.
+ VPSingleDefRecipe *PreviousLink = PhiR; // Aka Worklist[0].
+ for (VPSingleDefRecipe *CurrentLink : drop_begin(Worklist)) {
+ if (auto *Blend = dyn_cast<VPBlendRecipe>(CurrentLink)) {
+ assert(Blend->getNumIncomingValues() == 2 &&
+ "Blend must have 2 incoming values");
+ unsigned Idx = Blend->getIncomingValue(0) == PhiR ? 1 : 0;
+ assert(Blend->getIncomingValue(1 - Idx) == PhiR &&
+ "PhiR must be an operand of the blend");
+ Blend->replaceAllUsesWith(Blend->getIncomingValue(Idx));
+ continue;
+ }
+
+ if (IsFPRecurrence) {
+ FastMathFlags CurFMF =
+ cast<VPRecipeWithIRFlags>(CurrentLink)->getFastMathFlags();
+ if (match(CurrentLink, m_Select(m_VPValue(), m_VPValue(), m_VPValue())))
+ CurFMF |= cast<VPRecipeWithIRFlags>(CurrentLink->getOperand(0))
+ ->getFastMathFlags();
+ FMFs &= CurFMF;
+ }
+
+ Instruction *CurrentLinkI = CurrentLink->getUnderlyingInstr();
+
+ // Recognize a call to the llvm.fmuladd intrinsic.
+ bool IsFMulAdd = Kind == RecurKind::FMulAdd &&
+ RecurrenceDescriptor::isFMulAddIntrinsic(CurrentLinkI);
+ VPValue *VecOp;
+ VPBasicBlock *LinkVPBB = CurrentLink->getParent();
+ if (IsFMulAdd) {
+ assert(CurrentLink->getOperand(2) == PreviousLink &&
+ "expected a call where the previous link is the added operand");
+
+ // If the instruction is a call to the llvm.fmuladd intrinsic then we
+ // need to create an fmul recipe (multiplying the first two operands of
+ // the fmuladd together) to use as the vector operand for the fadd
+ // reduction.
+ auto *FMulRecipe = new VPInstruction(
+ Instruction::FMul,
+ {CurrentLink->getOperand(0), CurrentLink->getOperand(1)},
+ CurrentLinkI->getFastMathFlags());
+ LinkVPBB->insert(FMulRecipe, CurrentLink->getIterator());
+ VecOp = FMulRecipe;
+ } else if (Kind == RecurKind::AddChainWithSubs &&
+ match(CurrentLink, m_Sub(m_VPValue(), m_VPValue()))) {
+ Type *PhiTy = TypeInfo.inferScalarType(PhiR);
+ auto *Zero = Plan.getConstantInt(PhiTy, 0);
+ auto *Sub = new VPInstruction(Instruction::Sub,
+ {Zero, CurrentLink->getOperand(1)}, {},
+ {}, CurrentLinkI->getDebugLoc());
+ Sub->setUnderlyingValue(CurrentLinkI);
+ LinkVPBB->insert(Sub, CurrentLink->getIterator());
+ VecOp = Sub;
+ } else {
+ // Index of the first operand which holds a non-mask vector operand.
+ unsigned IndexOfFirstOperand = 0;
+ if (RecurrenceDescriptor::isMinMaxRecurrenceKind(Kind)) {
+ if (match(CurrentLink, m_Cmp(m_VPValue(), m_VPValue())))
+ continue;
+ assert(match(CurrentLink,
+ m_Select(m_VPValue(), m_VPValue(), m_VPValue())) &&
+ "must be a select recipe");
+ IndexOfFirstOperand = 1;
+ }
+ // Note that for non-commutable operands (cmp-selects), the semantics of
+ // the cmp-select are captured in the recurrence kind.
+ unsigned VecOpId =
+ CurrentLink->getOperand(IndexOfFirstOperand) == PreviousLink
+ ? IndexOfFirstOperand + 1
+ : IndexOfFirstOperand;
+ VecOp = CurrentLink->getOperand(VecOpId);
+ assert(VecOp != PreviousLink &&
+ CurrentLink->getOperand(CurrentLink->getNumOperands() - 1 -
+ (VecOpId - IndexOfFirstOperand)) ==
+ PreviousLink &&
+ "PreviousLink must be the operand other than VecOp");
+ }
+
+ // Get block mask from BlockMaskCache if the block needs predication.
+ VPValue *CondOp = nullptr;
+ if (BlocksNeedingPredication.contains(CurrentLinkI->getParent()))
+ CondOp = BlockMaskCache.lookup(LinkVPBB);
----------------
fhahn wrote:
we create a redundant one if we always use it, when removing `BlocksNeedingPredication.contains(` check
https://github.com/llvm/llvm-project/pull/168784
More information about the llvm-commits
mailing list