[llvm] [VPlan] Support tailfolded loops in multi-use-reductions (PR #214455)

Luke Lau via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 04:40:23 PDT 2026


================
@@ -2035,32 +2043,58 @@ bool VPlanTransforms::handleMultiUseReductions(VPlan &Plan,
 
     // MinOrMaxPhiR has users outside the reduction cycle in the loop. Check if
     // the only other user is a FindLastIV reduction. MinOrMaxPhiR must have
-    // exactly 2 users:
+    // exactly 2 users if not tailfolded:
     // 1) the min/max operation of the reduction cycle, and
     // 2) the compare of a FindLastIV reduction cycle. This compare must match
     // the min/max operation - comparing MinOrMaxPhiR with the operand of the
     // min/max operation, and be used only by the select of the FindLastIV
     // reduction cycle.
+    // There is an additional user if the loop was tailfolded:
+    // 3) the select operation of the vector.latch block. This select uses the
+    // the original MinOrMaxPhiR if the mask is zero.
     RecurKind RdxKind = MinOrMaxPhiR->getRecurrenceKind();
     assert(
         RecurrenceDescriptor::isIntMinMaxRecurrenceKind(RdxKind) &&
         "only min/max recurrences support users outside the reduction chain");
 
-    auto *MinOrMaxOp =
+    auto *MinOrMaxBackedgeR =
         dyn_cast<VPRecipeWithIRFlags>(MinOrMaxPhiR->getBackedgeValue());
-    if (!MinOrMaxOp)
+    if (!MinOrMaxBackedgeR)
       return false;
 
-    // Check that MinOrMaxOp is a VPWidenIntrinsicRecipe or VPReplicateRecipe
-    // with an intrinsic that matches the reduction kind.
+    // If the loop is tailfolded then the backedge won't be the
+    // reduction-intrinsic but the select in the vector.latch block that wraps
+    // the reduction-intrinsic.
+    auto *MinOrMaxOp = MinOrMaxBackedgeR;
+    VPValue *HeaderMask = Plan.getVectorLoopRegion()->getHeaderMask();
+    if (VPValue *MinOrMaxTailfold;
+        HeaderMask &&
+        match(MinOrMaxBackedgeR,
+              m_SelectLike(m_Specific(HeaderMask), m_VPValue(MinOrMaxTailfold),
+                           m_Specific(MinOrMaxPhiR)))) {
+      MinOrMaxOp =
+          dyn_cast<VPRecipeWithIRFlags>(MinOrMaxTailfold->getDefiningRecipe());
+      if (!MinOrMaxOp)
+        return false;
+    }
+
+    // Check that MinOrMaxOp is a VPWidenIntrinsicRecipe or
+    // VPReplicateRecipe with an intrinsic that matches the reduction kind.
     Intrinsic::ID ExpectedIntrinsicID = getMinMaxReductionIntrinsicOp(RdxKind);
     if (!match(MinOrMaxOp, m_Intrinsic(ExpectedIntrinsicID)))
       return false;
 
-    // MinOrMaxOp must have 2 users: 1) MinOrMaxPhiR and 2)
+    // If the loop is tailfolded MinOrMaxOp should only feed into the predicated
+    // select MinOrMaxBackedgeR. This is ensured by the MinOrMaxBackedgeR
+    // select-matching.
+    if ((!HeaderMask && MinOrMaxOp->getNumUsers() != 2) ||
+        (HeaderMask && MinOrMaxOp->getNumUsers() != 1))
+      return false;
----------------
lukel97 wrote:

Is this `return false` ever hit? I think it might be dead, and the `MinOrMaxBackedgeR` assert below is the important one to check. If it's dead then I would just omit it

https://github.com/llvm/llvm-project/pull/214455


More information about the llvm-commits mailing list