[llvm] [SCEV] Directly use wrap flags in getUDivExpr (PR #217133)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 19 00:09:04 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/217133

>From 54db31e369c4764bfc60c389202f03306a5b17fa Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Tue, 18 Aug 2026 22:10:22 +0100
Subject: [PATCH 1/3] [SCEV] Directly use wrap flags in getUDivExpr (NFC)

Eliminate the roundabout and expensive zero extend expressions, and
use the wrap flags on the expression directly. In the case of the
AddRec, infer <NW> from ConstantRanges.

Proof: https://alive2.llvm.org/ce/z/n8rgAt
---
 llvm/lib/Analysis/ScalarEvolution.cpp | 45 ++++++---------------------
 1 file changed, 10 insertions(+), 35 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 1707c6c17fe23..f619c99946563 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3522,27 +3522,14 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
     if (!RHSC->getValue()->isZero()) {
       // Determine if the division can be folded into the operands of
       // its operands.
-      // TODO: Generalize this to non-constants by using known-bits information.
-      Type *Ty = LHS->getType();
-      unsigned LZ = RHSC->getAPInt().countl_zero();
-      unsigned MaxShiftAmt = getTypeSizeInBits(Ty) - LZ - 1;
-      // For non-power-of-two values, effectively round the value up to the
-      // nearest power of two.
-      if (!RHSC->getAPInt().isPowerOf2())
-        ++MaxShiftAmt;
-      IntegerType *ExtTy =
-        IntegerType::get(getContext(), getTypeSizeInBits(Ty) + MaxShiftAmt);
       if (const SCEVAddRecExpr *AR = dyn_cast<SCEVAddRecExpr>(LHS))
         if (const SCEVConstant *Step =
             dyn_cast<SCEVConstant>(AR->getStepRecurrence(*this))) {
           // {X,+,N}/C --> {X/C,+,N/C} if safe and N/C can be folded.
           const APInt &StepInt = Step->getAPInt();
           const APInt &DivInt = RHSC->getAPInt();
-          if (!StepInt.urem(DivInt) &&
-              getZeroExtendExpr(AR, ExtTy) ==
-              getAddRecExpr(getZeroExtendExpr(AR->getStart(), ExtTy),
-                            getZeroExtendExpr(Step, ExtTy),
-                            AR->getLoop(), SCEV::FlagAnyWrap)) {
+          inferNoWrapViaConstantRanges(AR);
+          if (!StepInt.urem(DivInt) && AR->hasNoSelfWrap()) {
             SmallVector<SCEVUse, 4> Operands;
             for (const SCEV *Op : AR->operands())
               Operands.push_back(getUDivExpr(Op, RHS));
@@ -3553,12 +3540,6 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
           const APInt *StartRem;
           if (!DivInt.urem(StepInt) && match(getURemExpr(AR->getStart(), Step),
                                              m_scev_APInt(StartRem))) {
-            bool NoWrap =
-                getZeroExtendExpr(AR, ExtTy) ==
-                getAddRecExpr(getZeroExtendExpr(AR->getStart(), ExtTy),
-                              getZeroExtendExpr(Step, ExtTy), AR->getLoop(),
-                              SCEV::FlagAnyWrap);
-
             // With N <= C and both N, C as powers-of-2, the transformation
             // {X,+,N}/C => {(X - X%N),+,N}/C preserves division results even
             // if wrapping occurs, as the division results remain equivalent for
@@ -3569,11 +3550,11 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
             // expression.
             const SCEV *NewStart =
                 getMinusSCEV(AR->getStart(), getConstant(*StartRem));
-            if (*StartRem != 0 && (NoWrap || CanFoldWithWrap) &&
+            if (*StartRem != 0 && (AR->hasNoSelfWrap() || CanFoldWithWrap) &&
                 !isa<SCEVAddExpr>(NewStart)) {
-              const SCEV *NewLHS =
-                  getAddRecExpr(NewStart, Step, AR->getLoop(),
-                                NoWrap ? SCEV::FlagNW : SCEV::FlagAnyWrap);
+              const SCEV *NewLHS = getAddRecExpr(
+                  NewStart, Step, AR->getLoop(),
+                  AR->hasNoSelfWrap() ? SCEV::FlagNW : SCEV::FlagAnyWrap);
               if (LHS != NewLHS)
                 return getUDivExpr(NewLHS, RHS);
             }
@@ -3581,16 +3562,13 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
         }
       // (A*B)/C --> A*(B/C) if safe and B/C can be folded.
       if (const SCEVMulExpr *M = dyn_cast<SCEVMulExpr>(LHS)) {
-        SmallVector<SCEVUse, 4> Operands;
-        for (const SCEV *Op : M->operands())
-          Operands.push_back(getZeroExtendExpr(Op, ExtTy));
-        if (getZeroExtendExpr(M, ExtTy) == getMulExpr(Operands)) {
+        if (M->hasNoUnsignedWrap()) {
           // Find an operand that's safely divisible.
           for (unsigned i = 0, e = M->getNumOperands(); i != e; ++i) {
             const SCEV *Op = M->getOperand(i);
             const SCEV *Div = getUDivExpr(Op, RHSC);
             if (!isa<SCEVUDivExpr>(Div) && getMulExpr(Div, RHSC) == Op) {
-              Operands = SmallVector<SCEVUse, 4>(M->operands());
+              SmallVector<SCEVUse, 4> Operands(M->operands());
               Operands[i] = Div;
               return getMulExpr(Operands);
             }
@@ -3628,11 +3606,8 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
 
       // (A+B)/C --> (A/C + B/C) if safe and A/C and B/C can be folded.
       if (const SCEVAddExpr *A = dyn_cast<SCEVAddExpr>(LHS)) {
-        SmallVector<SCEVUse, 4> Operands;
-        for (const SCEV *Op : A->operands())
-          Operands.push_back(getZeroExtendExpr(Op, ExtTy));
-        if (getZeroExtendExpr(A, ExtTy) == getAddExpr(Operands)) {
-          Operands.clear();
+        if (A->hasNoUnsignedWrap()) {
+          SmallVector<SCEVUse, 4> Operands;
           for (unsigned i = 0, e = A->getNumOperands(); i != e; ++i) {
             const SCEV *Op = getUDivExpr(A->getOperand(i), RHS);
             if (isa<SCEVUDivExpr>(Op) ||

>From 4016c42336c8e6e1fabb0ef54d1ce0d9ccf78ac0 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Tue, 18 Aug 2026 23:13:56 +0100
Subject: [PATCH 2/3] [LSR] Missed test update

---
 .../LoopStrengthReduce/X86/zext-signed-addrec.ll          | 8 +++-----
 1 file changed, 3 insertions(+), 5 deletions(-)

diff --git a/llvm/test/Transforms/LoopStrengthReduce/X86/zext-signed-addrec.ll b/llvm/test/Transforms/LoopStrengthReduce/X86/zext-signed-addrec.ll
index f2fa771ac6f29..fb5cfc7bd93be 100644
--- a/llvm/test/Transforms/LoopStrengthReduce/X86/zext-signed-addrec.ll
+++ b/llvm/test/Transforms/LoopStrengthReduce/X86/zext-signed-addrec.ll
@@ -31,27 +31,25 @@ define i32 @foo() {
 ; CHECK-NEXT:    [[TMP1:%.*]] = phi i32 [ [[INC:%.*]], %[[OUTER_LATCH:.*]] ], [ [[DOTPR]], %[[OUTER_HEADER_PREHEADER]] ]
 ; CHECK-NEXT:    br label %[[INNER_LOOP:.*]]
 ; CHECK:       [[INNER_LOOP]]:
-; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i32 [ [[LSR_IV_NEXT:%.*]], %[[INNER_LOOP]] ], [ 516, %[[OUTER_HEADER]] ]
+; CHECK-NEXT:    [[LSR_IV:%.*]] = phi i32 [ [[LSR_IV_NEXT:%.*]], %[[INNER_LOOP]] ], [ 258, %[[OUTER_HEADER]] ]
 ; CHECK-NEXT:    [[TMP2:%.*]] = phi i8 [ 1, %[[OUTER_HEADER]] ], [ [[DEC:%.*]], %[[INNER_LOOP]] ]
-; CHECK-NEXT:    [[SHL:%.*]] = add i32 [[LSR_IV]], -258
+; CHECK-NEXT:    [[SHL:%.*]] = and i32 [[LSR_IV]], 510
 ; CHECK-NEXT:    store i32 [[SHL]], ptr @c, align 4
 ; CHECK-NEXT:    [[DEC]] = add i8 [[TMP2]], -1
 ; CHECK-NEXT:    [[LSR_IV_NEXT]] = add nsw i32 [[LSR_IV]], -258
 ; CHECK-NEXT:    [[CMP2:%.*]] = icmp sgt i8 [[DEC]], -1
 ; CHECK-NEXT:    br i1 [[CMP2]], label %[[INNER_LOOP]], label %[[OUTER_LATCH]]
 ; CHECK:       [[OUTER_LATCH]]:
-; CHECK-NEXT:    [[LSR_IV_NEXT_LCSSA:%.*]] = phi i32 [ [[LSR_IV_NEXT]], %[[INNER_LOOP]] ]
 ; CHECK-NEXT:    store i32 0, ptr @d, align 4
 ; CHECK-NEXT:    [[INC]] = add nsw i32 [[TMP1]], 1
 ; CHECK-NEXT:    store i32 [[INC]], ptr @b, align 4
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[TMP1]], 0
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[OUTER_HEADER]], label %[[OUTER_EXIT:.*]]
 ; CHECK:       [[OUTER_EXIT]]:
-; CHECK-NEXT:    [[LSR_IV_NEXT_LCSSA_LCSSA:%.*]] = phi i32 [ [[LSR_IV_NEXT_LCSSA]], %[[OUTER_LATCH]] ]
 ; CHECK-NEXT:    store i8 [[DEC]], ptr @e, align 1
 ; CHECK-NEXT:    br label %[[MERGE]]
 ; CHECK:       [[MERGE]]:
-; CHECK-NEXT:    [[TMP3:%.*]] = phi i32 [ [[DOTPRE]], %[[ENTRY_ELSE]] ], [ [[LSR_IV_NEXT_LCSSA_LCSSA]], %[[OUTER_EXIT]] ]
+; CHECK-NEXT:    [[TMP3:%.*]] = phi i32 [ [[DOTPRE]], %[[ENTRY_ELSE]] ], [ [[SHL]], %[[OUTER_EXIT]] ]
 ; CHECK-NEXT:    [[CALL:%.*]] = tail call i32 @bar(i32 [[TMP3]])
 ; CHECK-NEXT:    br label %[[RETURN:.*]]
 ; CHECK:       [[P_ELSE]]:

>From 466803651d4ad93421b21be3692f7374122e7684 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Wed, 19 Aug 2026 08:06:42 +0100
Subject: [PATCH 3/3] [SCEV] Attempt to fix failure; return NoWrap

---
 llvm/include/llvm/Analysis/ScalarEvolution.h |  5 ++--
 llvm/lib/Analysis/ScalarEvolution.cpp        | 26 +++++++++++---------
 2 files changed, 17 insertions(+), 14 deletions(-)

diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 30e28a2aa41fa..c672156a42e50 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2402,8 +2402,9 @@ class ScalarEvolution {
   bool proveNoWrapByVaryingStart(const SCEV *Start, const SCEV *Step,
                                  const Loop *L);
 
-  /// Try to infer NSW or NUW on \p AR relying on ConstantRange manipulation.
-  void inferNoWrapViaConstantRanges(const SCEVAddRecExpr *AR);
+  /// Infer NW on \p AR relying on ConstantRanges. Return true if no-self-wrap
+  /// was inferred.
+  [[nodiscard]] bool inferNoWrapViaConstantRanges(const SCEVAddRecExpr *AR);
 
   /// Try to prove NSW on \p AR by proving facts about conditions known  on
   /// entry and backedge.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index f619c99946563..06e4cfac53673 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -3528,8 +3528,8 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
           // {X,+,N}/C --> {X/C,+,N/C} if safe and N/C can be folded.
           const APInt &StepInt = Step->getAPInt();
           const APInt &DivInt = RHSC->getAPInt();
-          inferNoWrapViaConstantRanges(AR);
-          if (!StepInt.urem(DivInt) && AR->hasNoSelfWrap()) {
+          bool NoWrap = inferNoWrapViaConstantRanges(AR);
+          if (!StepInt.urem(DivInt) && NoWrap) {
             SmallVector<SCEVUse, 4> Operands;
             for (const SCEV *Op : AR->operands())
               Operands.push_back(getUDivExpr(Op, RHS));
@@ -3550,11 +3550,11 @@ const SCEV *ScalarEvolution::getUDivExpr(SCEVUse LHS, SCEVUse RHS) {
             // expression.
             const SCEV *NewStart =
                 getMinusSCEV(AR->getStart(), getConstant(*StartRem));
-            if (*StartRem != 0 && (AR->hasNoSelfWrap() || CanFoldWithWrap) &&
+            if (*StartRem != 0 && (NoWrap || CanFoldWithWrap) &&
                 !isa<SCEVAddExpr>(NewStart)) {
-              const SCEV *NewLHS = getAddRecExpr(
-                  NewStart, Step, AR->getLoop(),
-                  AR->hasNoSelfWrap() ? SCEV::FlagNW : SCEV::FlagAnyWrap);
+              const SCEV *NewLHS =
+                  getAddRecExpr(NewStart, Step, AR->getLoop(),
+                                NoWrap ? SCEV::FlagNW : SCEV::FlagAnyWrap);
               if (LHS != NewLHS)
                 return getUDivExpr(NewLHS, RHS);
             }
@@ -5121,9 +5121,9 @@ class SCEVShiftRewriter : public SCEVRewriteVisitor<SCEVShiftRewriter> {
 
 } // end anonymous namespace
 
-void ScalarEvolution::inferNoWrapViaConstantRanges(const SCEVAddRecExpr *AR) {
+bool ScalarEvolution::inferNoWrapViaConstantRanges(const SCEVAddRecExpr *AR) {
   if (!AR->isAffine())
-    return;
+    return false;
 
   // Force computation of ranges, which will also perform range-based flag
   // inference.
@@ -5140,10 +5140,10 @@ void ScalarEvolution::inferNoWrapViaConstantRanges(const SCEVAddRecExpr *AR) {
       const APInt &BECountAP = BECountMax->getAPInt();
       unsigned NoOverflowBitWidth =
         BECountAP.getActiveBits() + StepCR.getMinSignedBits();
-      if (NoOverflowBitWidth <= getTypeSizeInBits(AR->getType()))
-        const_cast<SCEVAddRecExpr *>(AR)->setNoWrapFlags(SCEV::FlagNW);
+      return NoOverflowBitWidth <= getTypeSizeInBits(AR->getType());
     }
   }
+  return AR->hasNoSelfWrap();
 }
 
 SCEV::NoWrapFlags
@@ -5806,7 +5806,8 @@ const SCEV *ScalarEvolution::createSimpleAffineAddRec(PHINode *PN,
   insertValueToMap(PN, PHISCEV);
 
   if (auto *AR = dyn_cast<SCEVAddRecExpr>(PHISCEV))
-    inferNoWrapViaConstantRanges(AR);
+    if (inferNoWrapViaConstantRanges(AR))
+      const_cast<SCEVAddRecExpr *>(AR)->setNoWrapFlags(SCEV::FlagNW);
 
   // We can add Flags to the post-inc expression only if we
   // know that it is *undefined behavior* for BEValueV to
@@ -5934,7 +5935,8 @@ const SCEV *ScalarEvolution::createAddRecFromPHI(PHINode *PN) {
         insertValueToMap(PN, PHISCEV);
 
         if (auto *AR = dyn_cast<SCEVAddRecExpr>(PHISCEV))
-          inferNoWrapViaConstantRanges(AR);
+          if (inferNoWrapViaConstantRanges(AR))
+            const_cast<SCEVAddRecExpr *>(AR)->setNoWrapFlags(SCEV::FlagNW);
 
         // We can add Flags to the post-inc expression only if we
         // know that it is *undefined behavior* for BEValueV to



More information about the llvm-commits mailing list