[llvm] [VPlan] Make simplifyRecipe more like InstCombine (PR #212968)

Luke Lau via llvm-commits llvm-commits at lists.llvm.org
Mon Aug 3 01:22:16 PDT 2026


https://github.com/lukel97 updated https://github.com/llvm/llvm-project/pull/212968

>From 0d78fb5d33d5447fa0a7d4c69ae1ca3464322237 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Thu, 30 Jul 2026 17:13:43 +0800
Subject: [PATCH 1/2] [VPlan] Make simplifyRecipe more like InstCombine

Most combines in simplifyRecipe RAUW a value, but not all of them erase the old recipe.
Unify them and bring it in line with InstCombine by having it return a VPValue, which simplifyRecipes can then call RAUW with, and automatically erase the old recipe.

Similarly to InstCombine, combines that modify a recipe should return the same recipe.
---
 .../Transforms/Vectorize/VPlanTransforms.cpp  | 262 ++++++++----------
 .../LoopVectorize/VPlan/constant-fold.ll      |  11 -
 .../epilog-vectorization-reductions.ll        |   2 -
 3 files changed, 113 insertions(+), 162 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 9990fd142df02..6280514152ee5 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -1119,8 +1119,9 @@ static void removeRedundantExpandSCEVRecipes(VPlan &Plan) {
 }
 
 /// Try to simplify logical and bitwise recipes in \p Def.
-static bool simplifyLogicalRecipe(VPSingleDefRecipe *Def, VPBuilder &Builder,
-                                  bool CanCreateNewRecipe) {
+static VPValue *simplifyLogicalRecipe(VPSingleDefRecipe *Def,
+                                      VPBuilder &Builder,
+                                      bool CanCreateNewRecipe) {
   VPlan *Plan = Def->getParent()->getPlan();
 
   // Simplify (X && Y) | (X && !Y) -> X.
@@ -1130,53 +1131,36 @@ static bool simplifyLogicalRecipe(VPSingleDefRecipe *Def, VPBuilder &Builder,
   VPValue *X, *Y, *Z;
   if (match(Def,
             m_c_BinaryOr(m_LogicalAnd(m_VPValue(X), m_VPValue(Y)),
-                         m_LogicalAnd(m_Deferred(X), m_Not(m_Deferred(Y)))))) {
-    Def->replaceAllUsesWith(X);
-    Def->eraseFromParent();
-    return true;
-  }
+                         m_LogicalAnd(m_Deferred(X), m_Not(m_Deferred(Y))))))
+    return X;
 
   // x | AllOnes -> AllOnes
-  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_AllOnes()))) {
-    Def->replaceAllUsesWith(Plan->getAllOnesValue(Def->getScalarType()));
-    return true;
-  }
+  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_AllOnes())))
+    return Plan->getAllOnesValue(Def->getScalarType());
 
   // x | 0 -> x
-  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_ZeroInt()))) {
-    Def->replaceAllUsesWith(X);
-    return true;
-  }
+  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_ZeroInt())))
+    return X;
 
   // x | !x -> AllOnes
-  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_Not(m_Deferred(X))))) {
-    Def->replaceAllUsesWith(Plan->getAllOnesValue(Def->getScalarType()));
-    return true;
-  }
+  if (match(Def, m_c_BinaryOr(m_VPValue(X), m_Not(m_Deferred(X)))))
+    return Plan->getAllOnesValue(Def->getScalarType());
 
   // x & 0 -> 0
-  if (match(Def, m_c_BinaryAnd(m_VPValue(X), m_ZeroInt()))) {
-    Def->replaceAllUsesWith(Plan->getZero(Def->getScalarType()));
-    return true;
-  }
+  if (match(Def, m_c_BinaryAnd(m_VPValue(X), m_ZeroInt())))
+    return Plan->getZero(Def->getScalarType());
 
   // x & AllOnes -> x
-  if (match(Def, m_c_BinaryAnd(m_VPValue(X), m_AllOnes()))) {
-    Def->replaceAllUsesWith(X);
-    return true;
-  }
+  if (match(Def, m_c_BinaryAnd(m_VPValue(X), m_AllOnes())))
+    return X;
 
   // x && false -> false
-  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_False()))) {
-    Def->replaceAllUsesWith(Plan->getFalse());
-    return true;
-  }
+  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_False())))
+    return Plan->getFalse();
 
   // x && true -> x
-  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_True()))) {
-    Def->replaceAllUsesWith(X);
-    return true;
-  }
+  if (match(Def, m_c_LogicalAnd(m_VPValue(X), m_True())))
+    return X;
 
   // (x && y) | (x && z) -> x && (y | z)
   if (CanCreateNewRecipe &&
@@ -1185,51 +1169,38 @@ static bool simplifyLogicalRecipe(VPSingleDefRecipe *Def, VPBuilder &Builder,
       // Simplify only if one of the operands has one use to avoid creating an
       // extra recipe.
       (!Def->getOperand(0)->hasMoreThanOneUniqueUser() ||
-       !Def->getOperand(1)->hasMoreThanOneUniqueUser())) {
-    Def->replaceAllUsesWith(
-        Builder.createLogicalAnd(X, Builder.createOr(Y, Z)));
-    return true;
-  }
+       !Def->getOperand(1)->hasMoreThanOneUniqueUser()))
+    return Builder.createLogicalAnd(X, Builder.createOr(Y, Z));
 
   // x && (x && y) -> x && y
   if (match(Def, m_LogicalAnd(m_VPValue(X),
-                              m_LogicalAnd(m_Deferred(X), m_VPValue())))) {
-    Def->replaceAllUsesWith(Def->getOperand(1));
-    return true;
-  }
+                              m_LogicalAnd(m_Deferred(X), m_VPValue()))))
+    return Def->getOperand(1);
 
   // x && (y && x) -> x && y
   if (match(Def, m_LogicalAnd(m_VPValue(X),
-                              m_LogicalAnd(m_VPValue(Y), m_Deferred(X))))) {
-    Def->replaceAllUsesWith(Builder.createLogicalAnd(X, Y));
-    return true;
-  }
+                              m_LogicalAnd(m_VPValue(Y), m_Deferred(X)))))
+    return Builder.createLogicalAnd(X, Y);
 
   // x && !x -> 0
-  if (match(Def, m_LogicalAnd(m_VPValue(X), m_Not(m_Deferred(X))))) {
-    Def->replaceAllUsesWith(Plan->getFalse());
-    return true;
-  }
+  if (match(Def, m_LogicalAnd(m_VPValue(X), m_Not(m_Deferred(X)))))
+    return Plan->getFalse();
 
-  if (match(Def, m_Select(m_VPValue(), m_VPValue(X), m_Deferred(X)))) {
-    Def->replaceAllUsesWith(X);
-    return true;
-  }
+  if (match(Def, m_Select(m_VPValue(), m_VPValue(X), m_Deferred(X))))
+    return X;
 
   // select c, false, true -> not c
   VPValue *C;
   if (CanCreateNewRecipe &&
-      match(Def, m_Select(m_VPValue(C), m_False(), m_True()))) {
-    Def->replaceAllUsesWith(Builder.createNot(C));
-    return true;
-  }
+      match(Def, m_Select(m_VPValue(C), m_False(), m_True())))
+    return Builder.createNot(C);
 
   // select !c, x, y -> select c, y, x
   if (match(Def, m_Select(m_Not(m_VPValue(C)), m_VPValue(X), m_VPValue(Y)))) {
     Def->setOperand(0, C);
     Def->setOperand(1, Y);
     Def->setOperand(2, X);
-    return true;
+    return Def;
   }
 
   // select x, (i1 y | z), y -> y | (x && z)
@@ -1237,11 +1208,8 @@ static bool simplifyLogicalRecipe(VPSingleDefRecipe *Def, VPBuilder &Builder,
       match(Def, m_Select(m_VPValue(X),
                           m_OneUse(m_c_BinaryOr(m_VPValue(Y), m_VPValue(Z))),
                           m_Deferred(Y))) &&
-      Y->getScalarType()->isIntegerTy(1)) {
-    Def->replaceAllUsesWith(
-        Builder.createOr(Y, Builder.createLogicalAnd(X, Z)));
-    return true;
-  }
+      Y->getScalarType()->isIntegerTy(1))
+    return Builder.createOr(Y, Builder.createLogicalAnd(X, Z));
 
   // select %M0, (select %M1, %X, %Y), %Y -> select (%M0 && %M1), %X, %Y
   VPValue *Mask0, *Mask1;
@@ -1250,31 +1218,30 @@ static bool simplifyLogicalRecipe(VPSingleDefRecipe *Def, VPBuilder &Builder,
             m_SelectLike(m_VPValue(Mask0),
                          m_OneUse(m_SelectLike(m_VPValue(Mask1), m_VPValue(X),
                                                m_VPValue(Y))),
-                         m_Deferred(Y)))) {
-    auto *Select = Builder.createSelect(Builder.createLogicalAnd(Mask0, Mask1),
-                                        X, Y, Def->getDebugLoc());
-    Def->replaceAllUsesWith(Select);
-    return true;
-  }
+                         m_Deferred(Y))))
+    return Builder.createSelect(Builder.createLogicalAnd(Mask0, Mask1), X, Y,
+                                Def->getDebugLoc());
 
-  return false;
+  return nullptr;
 }
 
-/// Try to simplify VPSingleDefRecipe \p Def.
-static void simplifyRecipe(VPSingleDefRecipe *Def) {
+/// Try to simplify VPSingleDefRecipe \p Def. Returns a new recipe if it should
+/// be replaced, or the existing recipe if it was modified. Returns nullptr if
+/// nothing was simplified.
+static VPValue *simplifyRecipe(VPSingleDefRecipe *Def) {
   VPlan *Plan = Def->getParent()->getPlan();
 
   // Simplification of live-in IR values for SingleDef recipes using
   // InstSimplifyFolder.
   const DataLayout &DL = Plan->getDataLayout();
   if (VPValue *V = vputils::tryToFoldLiveIns(*Def, Def->operands(), DL))
-    return Def->replaceAllUsesWith(V);
+    return V;
 
   // Fold PredPHI LiveIn -> LiveIn.
   if (auto *PredPHI = dyn_cast<VPPredInstPHIRecipe>(Def)) {
     VPValue *Op = PredPHI->getOperand(0);
     if (isa<VPIRValue>(Op))
-      PredPHI->replaceAllUsesWith(Op);
+      return Op;
   }
 
   // Drop the mask of a predicated store masked by the header mask (which is
@@ -1290,9 +1257,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
         RepR->isSingleScalar(), /*Mask=*/nullptr, *RepR, *RepR,
         RepR->getDebugLoc());
     Unmasked->insertBefore(RepR);
-    RepR->replaceAllUsesWith(Unmasked);
-    RepR->eraseFromParent();
-    return;
+    return Unmasked;
   }
 
   VPBuilder Builder(Def);
@@ -1317,7 +1282,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
     } else {
       // Don't replace a non-widened cast recipe with a widened cast.
       if (!isa<VPWidenCastRecipe>(Def))
-        return;
+        return nullptr;
       if (ATy->getScalarSizeInBits() < TruncTy->getScalarSizeInBits()) {
 
         unsigned ExtOpcode = match(Def->getOperand(0), m_SExt(m_VPValue()))
@@ -1337,25 +1302,25 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
     }
   }
 
-  if (simplifyLogicalRecipe(Def, Builder, CanCreateNewRecipe))
-    return;
+  if (VPValue *V = simplifyLogicalRecipe(Def, Builder, CanCreateNewRecipe))
+    return V;
 
   VPValue *X, *Y, *C;
   if (match(Def, m_c_Add(m_VPValue(A), m_ZeroInt())))
-    return Def->replaceAllUsesWith(A);
+    return A;
 
   if (match(Def, m_c_Mul(m_VPValue(A), m_One())))
-    return Def->replaceAllUsesWith(A);
+    return A;
 
   if (match(Def, m_c_Mul(m_VPValue(A), m_ZeroInt())))
-    return Def->replaceAllUsesWith(Plan->getZero(Def->getScalarType()));
+    return Plan->getZero(Def->getScalarType());
 
   if (CanCreateNewRecipe && match(Def, m_c_Mul(m_VPValue(A), m_AllOnes()))) {
     // Preserve nsw from the Mul on the new Sub.
     VPIRFlags::WrapFlagsTy NW = {
         false, cast<VPRecipeWithIRFlags>(Def)->hasNoSignedWrap()};
-    return Def->replaceAllUsesWith(Builder.createSub(
-        Plan->getZero(A->getScalarType()), A, Def->getDebugLoc(), "", NW));
+    return Builder.createSub(Plan->getZero(A->getScalarType()), A,
+                             Def->getDebugLoc(), "", NW);
   }
 
   if (CanCreateNewRecipe &&
@@ -1367,8 +1332,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
         cast<VPRecipeWithIRFlags>(Def)->hasNoSignedWrap() &&
             cast<VPRecipeWithIRFlags>(Def->getOperand(Def->getOperand(0) == X))
                 ->hasNoSignedWrap()};
-    return Def->replaceAllUsesWith(
-        Builder.createSub(X, Y, Def->getDebugLoc(), "", NW));
+    return Builder.createSub(X, Y, Def->getDebugLoc(), "", NW);
   }
 
   const APInt *APC;
@@ -1379,22 +1343,22 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
     VPIRFlags::WrapFlagsTy NW(MulR->hasNoUnsignedWrap(),
                               MulR->hasNoSignedWrap() &&
                                   ShiftAmt != APC->getBitWidth() - 1);
-    return Def->replaceAllUsesWith(Builder.createNaryOp(
+    return Builder.createNaryOp(
         Instruction::Shl,
         {A, Plan->getConstantInt(APC->getBitWidth(), ShiftAmt)}, NW,
-        Def->getDebugLoc()));
+        Def->getDebugLoc());
   }
 
   if (CanCreateNewRecipe && match(Def, m_UDiv(m_VPValue(A), m_APInt(APC))) &&
       APC->isPowerOf2())
-    return Def->replaceAllUsesWith(Builder.createNaryOp(
+    return Builder.createNaryOp(
         Instruction::LShr,
         {A, Plan->getConstantInt(APC->getBitWidth(), APC->exactLogBase2())},
-        *cast<VPRecipeWithIRFlags>(Def), Def->getDebugLoc()));
+        *cast<VPRecipeWithIRFlags>(Def), Def->getDebugLoc());
 
   if (match(Def, m_Not(m_VPValue(A)))) {
     if (match(A, m_Not(m_VPValue(A))))
-      return Def->replaceAllUsesWith(A);
+      return A;
 
     // Try to fold Not into compares by adjusting the predicate in-place.
     CmpPredicate Pred;
@@ -1423,6 +1387,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
           Cmp->setDebugLoc(Def->getDebugLoc());
       }
     }
+    return Def;
   }
 
   // Fold any-of (fcmp uno %A, %A), (fcmp uno %B, %B), ... ->
@@ -1450,7 +1415,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
 
     if (NewOps.size() < Def->getNumOperands()) {
       VPValue *NewAnyOf = Builder.createNaryOp(VPInstruction::AnyOf, NewOps);
-      return Def->replaceAllUsesWith(NewAnyOf);
+      return NewAnyOf;
     }
   }
 
@@ -1458,27 +1423,24 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
   // This is useful for fmax/fmin without fast-math flags, where we need to
   // check if any operand is NaN.
   if (CanCreateNewRecipe &&
-      match(Def, m_BinaryOr(m_SpecificCmp(CmpInst::FCMP_UNO, m_VPValue(X),
-                                          m_Deferred(X)),
-                            m_SpecificCmp(CmpInst::FCMP_UNO, m_VPValue(Y),
-                                          m_Deferred(Y))))) {
-    VPValue *NewCmp = Builder.createFCmp(CmpInst::FCMP_UNO, X, Y);
-    return Def->replaceAllUsesWith(NewCmp);
-  }
+      match(Def,
+            m_BinaryOr(
+                m_SpecificCmp(CmpInst::FCMP_UNO, m_VPValue(X), m_Deferred(X)),
+                m_SpecificCmp(CmpInst::FCMP_UNO, m_VPValue(Y), m_Deferred(Y)))))
+    return Builder.createFCmp(CmpInst::FCMP_UNO, X, Y);
 
   // Remove redundant DerviedIVs, that is 0 + A * 1 -> A and 0 + 0 * x -> 0.
   if ((match(Def, m_DerivedIV(m_ZeroInt(), m_VPValue(A), m_One())) ||
        match(Def, m_DerivedIV(m_ZeroInt(), m_ZeroInt(), m_VPValue()))) &&
       Def->getOperand(1)->getScalarType() == Def->getScalarType())
-    return Def->replaceAllUsesWith(Def->getOperand(1));
+    return Def->getOperand(1);
 
   if (match(Def, m_VPInstruction<VPInstruction::WideIVStep>(m_VPValue(X),
                                                             m_One()))) {
     Type *WideStepTy = Def->getScalarType();
     if (X->getScalarType() != WideStepTy)
       X = Builder.createWidenCast(Instruction::Trunc, X, WideStepTy);
-    Def->replaceAllUsesWith(X);
-    return;
+    return X;
   }
 
   // For i1 vp.merges produced by AnyOf reductions:
@@ -1489,53 +1451,45 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
       Def->getScalarType()->isIntegerTy(1)) {
     Def->setOperand(1, Def->getOperand(0));
     Def->setOperand(0, Y);
-    return;
+    return Def;
   }
 
   // Simplify MaskedCond with no block mask to its single operand.
   if (match(Def, m_VPInstruction<VPInstruction::MaskedCond>()) &&
       !cast<VPInstruction>(Def)->isMasked())
-    return Def->replaceAllUsesWith(Def->getOperand(0));
+    return Def->getOperand(0);
 
   // Look through ExtractLastLane.
   if (match(Def, m_ExtractLastLane(m_VPValue(A)))) {
     if (match(A, m_BuildVector())) {
       auto *BuildVector = cast<VPInstruction>(A);
-      Def->replaceAllUsesWith(
-          BuildVector->getOperand(BuildVector->getNumOperands() - 1));
-      return;
+      return BuildVector->getOperand(BuildVector->getNumOperands() - 1);
     }
 
     if (match(A, m_Broadcast(m_VPValue(X))))
-      return Def->replaceAllUsesWith(X);
+      return X;
 
     if (isa<VPInstruction, VPReplicateRecipe>(A) && vputils::isSingleScalar(A))
-      return Def->replaceAllUsesWith(A);
+      return A;
 
     if (Plan->hasScalarVFOnly())
-      return Def->replaceAllUsesWith(A);
+      return A;
   }
 
   // Look through ExtractPenultimateElement (BuildVector ....).
   if (match(Def, m_ExtractPenultimateElement(m_BuildVector()))) {
     auto *BuildVector = cast<VPInstruction>(Def->getOperand(0));
-    Def->replaceAllUsesWith(
-        BuildVector->getOperand(BuildVector->getNumOperands() - 2));
-    return;
+    return BuildVector->getOperand(BuildVector->getNumOperands() - 2);
   }
 
   uint64_t Idx;
   if (match(Def, m_ExtractElement(m_BuildVector(), m_ConstantInt(Idx)))) {
     auto *BuildVector = cast<VPInstruction>(Def->getOperand(0));
-    Def->replaceAllUsesWith(BuildVector->getOperand(Idx));
-    return;
+    return BuildVector->getOperand(Idx);
   }
 
-  if (match(Def, m_BuildVector()) && all_equal(Def->operands())) {
-    Def->replaceAllUsesWith(
-        Builder.createNaryOp(VPInstruction::Broadcast, Def->getOperand(0)));
-    return;
-  }
+  if (match(Def, m_BuildVector()) && all_equal(Def->operands()))
+    return Builder.createNaryOp(VPInstruction::Broadcast, Def->getOperand(0));
 
   // Replace uses of a BuildVector by users that only use its first lane with
   // its first operand directly.
@@ -1543,6 +1497,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
     Def->replaceUsesWithIf(Def->getOperand(0), [Def](VPUser &U, unsigned) {
       return U.usesFirstLaneOnly(Def);
     });
+    return Def;
   }
 
   // Look through broadcast of single-scalar when used as select conditions; in
@@ -1552,34 +1507,35 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
     assert(vputils::isSingleScalar(C) &&
            "broadcast operand must be single-scalar");
     Def->setOperand(0, C);
-    return;
+    return Def;
   }
 
-  if (match(Def, m_Broadcast(m_VPValue(X))))
-    return Def->replaceUsesWithIf(
+  if (match(Def, m_Broadcast(m_VPValue(X)))) {
+    Def->replaceUsesWithIf(
         X, [Def](const VPUser &U, unsigned) { return U.usesScalars(Def); });
+    return Def;
+  }
 
   if (isa<VPPhi, VPWidenPHIRecipe, VPHeaderPHIRecipe>(Def)) {
     if (Def->getNumOperands() == 1) {
-      Def->replaceAllUsesWith(Def->getOperand(0));
-      return;
+      return Def->getOperand(0);
     }
     if (auto *Phi = dyn_cast<VPFirstOrderRecurrencePHIRecipe>(Def)) {
       if (all_equal(Phi->incoming_values()))
-        Phi->replaceAllUsesWith(Phi->getOperand(0));
+        return Phi->getOperand(0);
     }
-    return;
+    return nullptr;
   }
 
   VPIRValue *IRV;
   if (Def->getNumOperands() == 1 &&
       match(Def, m_ComputeReductionResult(m_VPIRValue(IRV))))
-    return Def->replaceAllUsesWith(IRV);
+    return IRV;
 
   // Some simplifications can only be applied after unrolling. Perform them
   // below.
   if (!Plan->isUnrolled())
-    return;
+    return nullptr;
 
   // After unrolling, extract-lane may be used to extract values from multiple
   // scalar sources. Only simplify when extracting from a single scalar source.
@@ -1587,19 +1543,18 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
   if (match(Def, m_ExtractLane(m_VPValue(LaneToExtract), m_VPValue(A)))) {
     // Simplify extract-lane(%lane_num, %scalar_val) -> %scalar_val.
     if (vputils::isSingleScalar(A))
-      return Def->replaceAllUsesWith(A);
+      return A;
 
     // Replace extract-lane(0, canonical-WIDEN-INDUCTION) with the region's
     // scalar canonical IV.
     VPWidenIntOrFpInductionRecipe *WidenIV;
     if (match(LaneToExtract, m_ZeroInt()) &&
         match(A, m_CanonicalWidenIV(WidenIV)))
-      return Def->replaceAllUsesWith(WidenIV->getRegion()->getCanonicalIV());
+      return WidenIV->getRegion()->getCanonicalIV();
 
     // Simplify extract-lane with single source to extract-element.
-    Def->replaceAllUsesWith(Builder.createNaryOp(
-        Instruction::ExtractElement, {A, LaneToExtract}, Def->getDebugLoc()));
-    return;
+    return Builder.createNaryOp(Instruction::ExtractElement, {A, LaneToExtract},
+                                Def->getDebugLoc());
   }
 
   // Look for cycles where Def is of the form:
@@ -1623,7 +1578,7 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
         if (Inc)
           Inc->replaceAllUsesWith(Phi);
         Phi->setOperand(0, Y);
-        return;
+        return Def;
       }
     }
   }
@@ -1632,16 +1587,14 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
   // just the pointer operand.
   if (auto *VPR = dyn_cast<VPVectorPointerRecipe>(Def))
     if (!VPR->getVFxPart() || match(VPR->getVFxPart(), m_ZeroInt()))
-      return VPR->replaceAllUsesWith(VPR->getOperand(0));
+      return VPR->getOperand(0);
 
   // VPScalarIVSteps after unrolling can be replaced by their start value, if
   // the start index is zero and only the first lane 0 is demanded.
-  if (auto *Steps = dyn_cast<VPScalarIVStepsRecipe>(Def)) {
-    if (!Steps->getStartIndex() && vputils::onlyFirstLaneUsed(Steps)) {
-      Steps->replaceAllUsesWith(Steps->getOperand(0));
-      return;
-    }
-  }
+  if (auto *Steps = dyn_cast<VPScalarIVStepsRecipe>(Def))
+    if (!Steps->getStartIndex() && vputils::onlyFirstLaneUsed(Steps))
+      return Steps->getOperand(0);
+
   // Simplify redundant ReductionStartVector recipes after unrolling.
   VPValue *StartV;
   if (match(Def, m_VPInstruction<VPInstruction::ReductionStartVector>(
@@ -1650,11 +1603,13 @@ static void simplifyRecipe(VPSingleDefRecipe *Def) {
       auto *PhiR = dyn_cast<VPReductionPHIRecipe>(&U);
       return PhiR && PhiR->isInLoop();
     });
-    return;
+    return Def;
   }
 
   if (Plan->getConcreteUF() == 1 && match(Def, m_ExtractLastPart(m_VPValue(A))))
-    return Def->replaceAllUsesWith(A);
+    return A;
+
+  return nullptr;
 }
 
 void VPlanTransforms::simplifyRecipes(VPlan &Plan) {
@@ -1663,7 +1618,16 @@ void VPlanTransforms::simplifyRecipes(VPlan &Plan) {
   for (VPBasicBlock *VPBB : VPBlockUtils::blocksOnly<VPBasicBlock>(RPOT)) {
     for (VPRecipeBase &R : make_early_inc_range(*VPBB))
       if (auto *Def = dyn_cast<VPSingleDefRecipe>(&R))
-        simplifyRecipe(Def);
+        if (VPValue *New = simplifyRecipe(Def)) {
+          if (New != Def) {
+            // Replace the recipe with a new one.
+            Def->replaceAllUsesWith(New);
+            Def->eraseFromParent();
+          } else if (vputils::isDeadRecipe(R)) {
+            // Recipe was modified - it may be dead now.
+            Def->eraseFromParent();
+          }
+        }
   }
 }
 
diff --git a/llvm/test/Transforms/LoopVectorize/VPlan/constant-fold.ll b/llvm/test/Transforms/LoopVectorize/VPlan/constant-fold.ll
index 92bb9704c4fd2..f648b52602637 100644
--- a/llvm/test/Transforms/LoopVectorize/VPlan/constant-fold.ll
+++ b/llvm/test/Transforms/LoopVectorize/VPlan/constant-fold.ll
@@ -24,14 +24,11 @@ define void @f1() {
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  bb2:
 ; CHECK-NEXT:    EMIT-SCALAR ir<%c.1.0> = phi [ ir<0>, vector.ph ], [ ir<%_tmp9>, bb2 ]
-; CHECK-NEXT:    EMIT-SCALAR ir<%_tmp1> = zext ir<0> to i64
-; CHECK-NEXT:    EMIT ir<%_tmp2> = getelementptr ir<@a>, ir<0>, ir<0>
 ; CHECK-NEXT:    EMIT-SCALAR ir<%_tmp6> = sext ir<%c.1.0> to i64
 ; CHECK-NEXT:    EMIT ir<%_tmp7> = getelementptr ir<@b>, ir<0>, ir<%_tmp6>
 ; CHECK-NEXT:    EMIT store ir<@a>, ir<%_tmp7>
 ; CHECK-NEXT:    EMIT ir<%_tmp9> = add nsw ir<%c.1.0>, ir<1>
 ; CHECK-NEXT:    EMIT ir<%_tmp11> = icmp sge ir<%_tmp9>, ir<2>
-; CHECK-NEXT:    EMIT vp<{{.+}}> = not ir<%_tmp11>
 ; CHECK-NEXT:    EMIT branch-on-cond ir<%_tmp11>
 ; CHECK-NEXT:  Successor(s): middle.block, bb2
 ;
@@ -74,8 +71,6 @@ define void @redundant_or_1(ptr %dst, i1 %c.0, i1 %c.1) {
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  then.1:
 ; CHECK-NEXT:    EMIT ir<%cmp> = icmp eq ir<%iv>, ir<2>
-; CHECK-NEXT:    EMIT ir<%or> = or ir<%cmp>, ir<true>
-; CHECK-NEXT:    EMIT ir<%cond> = select ir<true>, ir<%c.1>, ir<false>
 ; CHECK-NEXT:    EMIT branch-on-cond ir<%c.1>
 ; CHECK-NEXT:  Successor(s): then.2, loop.latch
 ;
@@ -125,8 +120,6 @@ define void @redundant_or_2(ptr %dst, i1 %c.0, i1 %c.1) {
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  then.1:
 ; CHECK-NEXT:    EMIT ir<%cmp> = icmp eq ir<%iv>, ir<2>
-; CHECK-NEXT:    EMIT ir<%or> = or ir<true>, ir<%cmp>
-; CHECK-NEXT:    EMIT ir<%cond> = select ir<true>, ir<%c.1>, ir<false>
 ; CHECK-NEXT:    EMIT branch-on-cond ir<%c.1>
 ; CHECK-NEXT:  Successor(s): then.2, loop.latch
 ;
@@ -176,7 +169,6 @@ define void @redundant_and_1(ptr %dst, i1 %c.0, i1 %c.1) {
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  then.1:
 ; CHECK-NEXT:    EMIT ir<%cmp> = icmp eq ir<%iv>, ir<2>
-; CHECK-NEXT:    EMIT ir<%or> = or ir<%cmp>, ir<false>
 ; CHECK-NEXT:    EMIT ir<%cond> = select ir<%cmp>, ir<%c.1>, ir<false>
 ; CHECK-NEXT:    EMIT branch-on-cond ir<%cond>
 ; CHECK-NEXT:  Successor(s): then.2, loop.latch
@@ -228,8 +220,6 @@ define void @redundant_and_2(ptr %dst, i1 %c.0, i1 %c.1) {
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  then.1:
 ; CHECK-NEXT:    EMIT ir<%cmp> = icmp eq ir<%iv>, ir<2>
-; CHECK-NEXT:    EMIT ir<%or> = and ir<false>, ir<%cmp>
-; CHECK-NEXT:    EMIT ir<%cond> = select ir<false>, ir<%c.1>, ir<false>
 ; CHECK-NEXT:    EMIT branch-on-cond ir<false>
 ; CHECK-NEXT:  Successor(s): then.2, loop.latch
 ;
@@ -280,7 +270,6 @@ define void @fold_replicating_umax_equal_live_ins(ptr noalias %dst, ptr %cond, i
 ; CHECK-NEXT:  Successor(s): then, latch
 ; CHECK-EMPTY:
 ; CHECK-NEXT:  then:
-; CHECK-NEXT:    EMIT ir<%m> = call ir<%x>, ir<%x>, ir<@llvm.umax.i32>
 ; CHECK-NEXT:    EMIT ir<%gep> = getelementptr ir<%dst>, ir<%iv>
 ; CHECK-NEXT:    EMIT store ir<%x>, ir<%gep>
 ; CHECK-NEXT:  Successor(s): latch
diff --git a/llvm/test/Transforms/LoopVectorize/epilog-vectorization-reductions.ll b/llvm/test/Transforms/LoopVectorize/epilog-vectorization-reductions.ll
index 20e18d018892a..846c78db79100 100644
--- a/llvm/test/Transforms/LoopVectorize/epilog-vectorization-reductions.ll
+++ b/llvm/test/Transforms/LoopVectorize/epilog-vectorization-reductions.ll
@@ -1115,7 +1115,6 @@ define i32 @test_foldable_reduction(i64 %N) {
 ; CHECK-NEXT:    br i1 [[TMP1]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP32:![0-9]+]]
 ; CHECK:       [[MIDDLE_BLOCK]]:
 ; CHECK-NEXT:    [[TMP2:%.*]] = call i32 @llvm.vector.reduce.or.v8i32(<8 x i32> [[VEC_PHI]])
-; CHECK-NEXT:    [[TMP3:%.*]] = call i1 @llvm.vector.reduce.or.v8i1(<8 x i1> splat (i1 true))
 ; CHECK-NEXT:    [[CMP_N:%.*]] = icmp eq i64 [[TMP0]], [[N_VEC]]
 ; CHECK-NEXT:    br i1 [[CMP_N]], label %[[EXIT:.*]], label %[[VEC_EPILOG_ITER_CHECK:.*]]
 ; CHECK:       [[VEC_EPILOG_ITER_CHECK]]:
@@ -1136,7 +1135,6 @@ define i32 @test_foldable_reduction(i64 %N) {
 ; CHECK-NEXT:    br i1 [[TMP6]], label %[[VEC_EPILOG_MIDDLE_BLOCK:.*]], label %[[VEC_EPILOG_VECTOR_BODY]], !llvm.loop [[LOOP33:![0-9]+]]
 ; CHECK:       [[VEC_EPILOG_MIDDLE_BLOCK]]:
 ; CHECK-NEXT:    [[TMP7:%.*]] = call i32 @llvm.vector.reduce.or.v4i32(<4 x i32> [[VEC_PHI6]])
-; CHECK-NEXT:    [[TMP8:%.*]] = call i1 @llvm.vector.reduce.or.v4i1(<4 x i1> splat (i1 true))
 ; CHECK-NEXT:    [[CMP_N9:%.*]] = icmp eq i64 [[TMP0]], [[N_VEC4]]
 ; CHECK-NEXT:    br i1 [[CMP_N9]], label %[[EXIT]], label %[[VEC_EPILOG_SCALAR_PH]]
 ; CHECK:       [[VEC_EPILOG_SCALAR_PH]]:

>From 2a3f74fa60131097570b925ee451e7960278b614 Mon Sep 17 00:00:00 2001
From: Luke Lau <luke at igalia.com>
Date: Thu, 30 Jul 2026 22:18:50 +0800
Subject: [PATCH 2/2] Update trunc combine, fix m_Not return position

---
 llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
index 0101d43adda81..21bbe02c0ca07 100644
--- a/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
+++ b/llvm/lib/Transforms/Vectorize/VPlanTransforms.cpp
@@ -1274,7 +1274,7 @@ static VPValue *simplifyRecipe(VPSingleDefRecipe *Def) {
     Type *TruncTy = Def->getScalarType();
     Type *ATy = A->getScalarType();
     if (TruncTy == ATy) {
-      Def->replaceAllUsesWith(A);
+      return A;
     } else {
       // Don't replace a non-widened cast recipe with a widened cast.
       if (!isa<VPWidenCastRecipe>(Def))
@@ -1290,10 +1290,10 @@ static VPValue *simplifyRecipe(VPSingleDefRecipe *Def) {
           // UnderlyingExt has distinct return type, used to retain legacy cost.
           Ext->setUnderlyingValue(UnderlyingExt);
         }
-        Def->replaceAllUsesWith(Ext);
+        return Ext;
       } else if (ATy->getScalarSizeInBits() > TruncTy->getScalarSizeInBits()) {
         auto *Trunc = Builder.createWidenCast(Instruction::Trunc, A, TruncTy);
-        Def->replaceAllUsesWith(Trunc);
+        return Trunc;
       }
     }
   }
@@ -1381,9 +1381,9 @@ static VPValue *simplifyRecipe(VPSingleDefRecipe *Def) {
         // to preserve the location.
         if (!Cmp->getDebugLoc() && Def->getDebugLoc())
           Cmp->setDebugLoc(Def->getDebugLoc());
+        return Def;
       }
     }
-    return Def;
   }
 
   // Fold any-of (fcmp uno %A, %A), (fcmp uno %B, %B), ... ->



More information about the llvm-commits mailing list