[llvm] [LV] Vectorize selecting last IV of min/max element. (PR #141431)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Fri Nov 28 14:07:03 PST 2025
================
@@ -1001,3 +1005,138 @@ bool VPlanTransforms::handleMaxMinNumReductions(VPlan &Plan) {
MiddleTerm->setOperand(0, NewCond);
return true;
}
+
+bool VPlanTransforms::handleMultiUseReductions(VPlan &Plan) {
+ for (auto &PhiR : make_early_inc_range(
+ Plan.getVectorLoopRegion()->getEntryBasicBlock()->phis())) {
+ auto *MinMaxPhiR = dyn_cast<VPReductionPHIRecipe>(&PhiR);
+ // TODO: check for multi-uses in VPlan directly.
+ if (!MinMaxPhiR || !MinMaxPhiR->hasLoopUsesOutsideReductionChain())
+ continue;
+
+ // MinMaxPhiR has users outside the reduction cycle in the loop. Check if
+ // the only other user is a FindLastIV reduction. MinMaxPhiR must have
+ // exactly 3 users: 1) the min/max operation, the compare of a FindLastIV
+ // reduction and ComputeReductionResult. The comparisom must compare
+ // MinMaxPhiR against the min/max operand used for the min/max reduction
+ // and only be used by the select of the FindLastIV reduction.
+ RecurKind RdxKind = MinMaxPhiR->getRecurrenceKind();
+ assert(
+ RecurrenceDescriptor::isIntMinMaxRecurrenceKind(RdxKind) &&
+ "only min/max recurrences support users outside the reduction chain");
+
+ auto *MinMaxOp =
+ dyn_cast<VPRecipeWithIRFlags>(MinMaxPhiR->getBackedgeValue());
+ if (!MinMaxOp)
+ return false;
+
+ // MinMaxOp must have 2 users: 1) MinMaxPhiR and 2) ComputeReductionResult
+ // (asserted below).
+ assert(MinMaxOp->getNumUsers() == 2 &&
+ "MinMaxOp must have exactly 2 users");
+
+ assert((isa<VPWidenIntrinsicRecipe>(MinMaxOp) ||
+ (isa<VPReplicateRecipe>(MinMaxOp) &&
+ isa<IntrinsicInst>(
+ cast<VPReplicateRecipe>(MinMaxOp)->getUnderlyingValue()))) &&
+ "MinMaxOp must be a wide or scalar intrinsic");
+ VPValue *MinMaxOpA = MinMaxOp->getOperand(0);
+ VPValue *MinMaxOpB = MinMaxOp->getOperand(1);
+ if (MinMaxOpA != MinMaxPhiR)
+ std::swap(MinMaxOpA, MinMaxOpB);
+ assert(MinMaxOpA == MinMaxPhiR &&
+ "one of MinMaxOp's operands must be the phi");
+
+ VPValue *CmpOpA;
+ VPValue *CmpOpB;
+ CmpPredicate Pred;
+ auto *Cmp = dyn_cast_or_null<VPRecipeWithIRFlags>(findUserOf(
+ MinMaxPhiR, m_Cmp(Pred, m_VPValue(CmpOpA), m_VPValue(CmpOpB))));
+ if (!Cmp || Cmp->getNumUsers() != 1 ||
+ (CmpOpA != MinMaxOpB && CmpOpB != MinMaxOpB))
+ return false;
+
+ // MinMaxPhiR must have exactly 3 users:
+ // * MinMaxOp,
+ // * Cmp (that's part of a FindLastIV chain),
+ // * ComputeReductionResult.
+ if (MinMaxPhiR->getNumUsers() != 3)
+ return false;
+
+ VPInstruction *MinMaxResult =
+ findUserOf<VPInstruction::ComputeReductionResult>(MinMaxPhiR);
+ assert(is_contained(MinMaxPhiR->users(), MinMaxOp) &&
+ "one user must be MinMaxOp");
+ assert(is_contained(MinMaxPhiR->users(), Cmp) && "one user must be Cmp");
+ assert(is_contained(MinMaxPhiR->users(), MinMaxResult) &&
+ "one user must be MinMaxResult");
+ assert(is_contained(MinMaxOp->users(), MinMaxPhiR) &&
+ "one user must be MinMaxPhiR");
+ assert(is_contained(MinMaxOp->users(), MinMaxResult) &&
+ "one user must be MinMaxResult");
+
+ // TODO: Strict predicates need to find the first IV value for which the
+ // predicate holds, not the last.
+ if (Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE ||
+ ICmpInst::isLT(Pred) || ICmpInst::isGT(Pred))
+ return false;
+
+ // Cmp must be used by the select of a FindLastIV chain.
+ VPValue *Sel = dyn_cast<VPSingleDefRecipe>(Cmp->getSingleUser());
+ VPValue *IVOp, *FindIV;
+ if (!Sel ||
+ !match(Sel,
+ m_Select(m_Specific(Cmp), m_VPValue(IVOp), m_VPValue(FindIV))) ||
+ Sel->getNumUsers() != 2)
+ return false;
+
+ auto *FindIVPhiR = dyn_cast<VPReductionPHIRecipe>(FindIV);
+ if (!FindIVPhiR || !RecurrenceDescriptor::isFindLastIVRecurrenceKind(
+ FindIVPhiR->getRecurrenceKind()))
+ return false;
+
+ assert(isa<VPWidenIntOrFpInductionRecipe>(IVOp) &&
+ "IVOp must be a wide induction");
+ assert(!FindIVPhiR->isInLoop() && !FindIVPhiR->isOrdered() &&
+ "cannot handle inloop/ordered reductions yet");
+
+ // The reduction using MinMaxPhiR needs adjusting to compute the correct
+ // result:
+ // 1. We need to find the last IV for which the condition based on the
+ // min/max recurrence is true,
+ // 2. Compare the partial min/max reduction result to its final value and,
+ // 3. Select the lanes of the partial FindLastIV reductions which
+ // correspond to the lanes matching the min/max reduction result.
+ //
+ // For example, this transforms
+ // vp<%min.result> = compute-reduction-result ir<%min.val>,
+ // ir<%min.val.next>
+ // vp<%find.iv.result = compute-find-iv-result ir<%min.idx>, ir<0>,
+ // SENTINEL, vp<%min.idx.next>
+ //
+ // into:
+ //
+ // vp<min.result> = compute-reduction-result ir<%min.val>, ir<%min.val.next>
----------------
fhahn wrote:
yep, currently the ocpode includes a reference to the reduction phi recipe, to retrieve RdxDesc if needed
https://github.com/llvm/llvm-project/pull/141431
More information about the llvm-commits
mailing list