[llvm] [VPlan] Detect and create partial reductions in VPlan. (NFCI) (PR #167851)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Mon Dec 8 01:44:35 PST 2025
================
@@ -991,3 +996,381 @@ bool VPlanTransforms::handleMaxMinNumReductions(VPlan &Plan) {
MiddleTerm->setOperand(0, NewCond);
return true;
}
+
+namespace {
+/// A VPlan-based chain of recipes that form a partial reduction.
+/// Designed to match either:
+/// reduction_bin_op (extend (A), accumulator), or
+/// reduction_bin_op (bin_op (extend (A), (extend (B))), accumulator).
+struct VPPartialReductionChain {
+ VPPartialReductionChain(VPWidenRecipe *Reduction, VPWidenCastRecipe *ExtendA,
+ VPWidenCastRecipe *ExtendB, VPWidenRecipe *ExtendUser)
+ : Reduction(Reduction), ExtendA(ExtendA), ExtendB(ExtendB),
+ ExtendUser(ExtendUser) {}
+ /// The top-level binary operation that forms the reduction to a scalar
+ /// after the loop body.
+ VPWidenRecipe *Reduction;
+ /// The extension of each of the inner binary operation's operands.
+ VPWidenCastRecipe *ExtendA;
+ VPWidenCastRecipe *ExtendB;
+
+ /// The user of the extends that is then reduced.
+ VPWidenRecipe *ExtendUser;
+};
+
+// Helper to transform a single widen recipe into a partial reduction recipe.
+// Returns true if transformation succeeded.
+static bool transformToPartialReduction(VPWidenRecipe *WidenRecipe,
+ unsigned ScaleFactor, VPlan &Plan) {
+ assert(WidenRecipe->getNumOperands() == 2 && "Expected binary operation");
+
+ VPValue *BinOp = WidenRecipe->getOperand(0);
+ VPValue *Accumulator = WidenRecipe->getOperand(1);
+
+ // Swap if needed to ensure Accumulator is the PHI or partial reduction.
+ VPRecipeBase *BinOpRecipe = BinOp->getDefiningRecipe();
+ if (BinOpRecipe && (isa<VPReductionPHIRecipe>(BinOpRecipe) ||
+ isa<VPPartialReductionRecipe>(BinOpRecipe)))
+ std::swap(BinOp, Accumulator);
+
+ // For chained reductions, only transform if accumulator is already a PHI or
+ // partial reduction. Otherwise, it needs to be transformed first.
+ VPRecipeBase *AccumRecipe = Accumulator->getDefiningRecipe();
+ if (!AccumRecipe || (!isa<VPReductionPHIRecipe>(AccumRecipe) &&
+ !isa<VPPartialReductionRecipe>(AccumRecipe)))
+ return false;
+
+ if (auto *RdxPhi = dyn_cast<VPReductionPHIRecipe>(AccumRecipe)) {
+ assert(RdxPhi->getVFScaleFactor() == 1 && "scale factor must not be set");
+ RdxPhi->setVFScaleFactor(ScaleFactor);
+
+ // Update ReductionStartVector instruction scale factor.
+ VPValue *StartValue = RdxPhi->getOperand(0);
+ auto *StartInst = cast<VPInstruction>(StartValue);
+ assert(StartInst->getOpcode() == VPInstruction::ReductionStartVector);
+ auto *NewScaleFactor = Plan.getConstantInt(32, ScaleFactor);
+ StartInst->setOperand(2, NewScaleFactor);
+ }
+
+ // Handle SUB by negating the operand and using ADD for the partial reduction.
+ unsigned ReductionOpcode = WidenRecipe->getOpcode();
+ if (ReductionOpcode == Instruction::Sub) {
+ VPBuilder Builder(WidenRecipe);
+
+ // Infer the scalar type for creating the zero constant
+ Type *ElemTy = VPTypeAnalysis(Plan).inferScalarType(BinOp);
+ auto *Zero = Plan.getConstantInt(ElemTy, 0);
+
+ // Create a negation: 0 - BinOp
+ VPIRFlags Flags;
+ if (auto *I = WidenRecipe->getUnderlyingInstr())
+ Flags = VPIRFlags(*I);
+
+ auto *NegRecipe = new VPWidenRecipe(Instruction::Sub, {Zero, BinOp}, Flags,
+ VPIRMetadata(), DebugLoc());
+ Builder.insert(NegRecipe);
+ BinOp = NegRecipe;
+ ReductionOpcode = Instruction::Add;
+ }
+
+ VPValue *Cond = nullptr;
+ VPValue *ExitValue = nullptr;
+ if (auto *RedPhiR = dyn_cast<VPReductionPHIRecipe>(Accumulator)) {
+ ExitValue = findComputeReductionResult(RedPhiR)->getOperand(1);
+ match(ExitValue, m_Select(m_VPValue(Cond), m_VPValue(), m_VPValue()));
----------------
fhahn wrote:
We need to handle the selects here, because they will get added during predication, so here we need to look through them; they will be gone and handled by VPReductionRecipe if they are created.
https://github.com/llvm/llvm-project/pull/167851
More information about the llvm-commits
mailing list