[llvm] [SCEV] Handle URem pattern in getRangeRef. (PR #174456)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Tue Jan 6 10:46:06 PST 2026


https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/174456

>From a5a62b61476dc22a4d8c09c1b2301fe97c0ce3c7 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 6 Jan 2026 10:45:33 +0000
Subject: [PATCH 1/5] [SCEV] Add urem tests with non-constant divisor.

---
 llvm/test/Analysis/ScalarEvolution/urem-0.ll | 55 ++++++++++++++++++++
 1 file changed, 55 insertions(+)

diff --git a/llvm/test/Analysis/ScalarEvolution/urem-0.ll b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
index 74031fe1eee73..ec5bfda99a06a 100644
--- a/llvm/test/Analysis/ScalarEvolution/urem-0.ll
+++ b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
@@ -86,3 +86,58 @@ define i32 @test_trunc_zext(i32 %arg) {
   %sub = sub i32 %arg, %zext
   ret i32 %sub
 }
+
+define i8 @urem_var_divisor_unknown(i8 %a, i8 %d) {
+; CHECK-LABEL: 'urem_var_divisor_unknown'
+; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_unknown
+; CHECK-NEXT:    %t0 = urem i8 %a, %d
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_unknown
+;
+  %t0 = urem i8 %a, %d
+  ret i8 %t0
+}
+
+define i8 @urem_var_divisor_ult_10(i8 %a, i8 range(i8 0, 10) %d) {
+; CHECK-LABEL: 'urem_var_divisor_ult_10'
+; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ult_10
+; CHECK-NEXT:    %t0 = urem i8 %a, %d
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ult_10
+;
+  %t0 = urem i8 %a, %d
+  ret i8 %t0
+}
+
+define i8 @urem_var_divisor_ugt_0_ult_10(i8 %a, i8 range(i8 1, 10) %d) {
+; CHECK-LABEL: 'urem_var_divisor_ugt_0_ult_10'
+; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ugt_0_ult_10
+; CHECK-NEXT:    %t0 = urem i8 %a, %d
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ugt_0_ult_10
+;
+  %t0 = urem i8 %a, %d
+  ret i8 %t0
+}
+
+define i8 @urem_var_divisor_ugt_1_ult_10(i8 %a, i8 range(i8 2, 10) %d) {
+; CHECK-LABEL: 'urem_var_divisor_ugt_1_ult_10'
+; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ugt_1_ult_10
+; CHECK-NEXT:    %t0 = urem i8 %a, %d
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ugt_1_ult_10
+;
+  %t0 = urem i8 %a, %d
+  ret i8 %t0
+}
+
+define i8 @urem_var_divisor_signed(i8 %a, i8 range(i8 -19, 10) %d) {
+; CHECK-LABEL: 'urem_var_divisor_signed'
+; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_signed
+; CHECK-NEXT:    %t0 = urem i8 %a, %d
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_signed
+;
+  %t0 = urem i8 %a, %d
+  ret i8 %t0
+}

>From 13496f9a30db274b405d7531ca75d2d1582ca77d Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Mon, 5 Jan 2026 12:30:55 +0000
Subject: [PATCH 2/5] [SCEV] URem range

https://alive2.llvm.org/ce/z/VPxe7C
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 15 +++++++++++++
 .../trip-multiple-guard-info.ll               |  2 +-
 .../Analysis/ScalarEvolution/trip-multiple.ll | 12 +++++------
 llvm/test/Analysis/ScalarEvolution/urem-0.ll  |  2 +-
 ...-vectorization-fix-scalar-resume-values.ll | 21 +++++++------------
 5 files changed, 30 insertions(+), 22 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 4dcf884823a61..3efdf5e436358 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -6777,6 +6777,21 @@ const ConstantRange &ScalarEvolution::getRangeRef(
   }
   case scAddExpr: {
     const SCEVAddExpr *Add = cast<SCEVAddExpr>(S);
+    // Check if this is a URem pattern: A - (A / B) * B, which is always < B.
+    const SCEV *URemLHS = nullptr, *URemRHS = nullptr;
+    if (SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED &&
+        match(S, m_scev_URem(m_SCEV(URemLHS), m_SCEV(URemRHS), *this))) {
+      ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
+      ConstantRange RHSRange = getRangeRef(URemRHS, SignHint, Depth + 1);
+      // URem result is always in [0, min(LHS, RHS - 1)] when RHS > 0.
+      if (RHSRange.getUnsignedMin().ugt(0)) {
+        APInt MaxVal = APIntOps::umin(LHSRange.getUnsignedMax(),
+                                      RHSRange.getUnsignedMax() - 1);
+        ConstantRange URemRange(APInt::getZero(BitWidth), MaxVal + 1);
+        ConservativeResult =
+            ConservativeResult.intersectWith(URemRange, RangeType);
+      }
+    }
     ConstantRange X = getRangeRef(Add->getOperand(0), SignHint, Depth + 1);
     unsigned WrapType = OBO::AnyWrap;
     if (Add->hasNoSignedWrap())
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll b/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
index f35c48b3e6fc5..e1807a716bb26 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
@@ -481,7 +481,7 @@ define void @test_trip_multiple_5(i32 %num) {
 ; CHECK-LABEL: 'test_trip_multiple_5'
 ; CHECK-NEXT:  Classifying expressions for: @test_trip_multiple_5
 ; CHECK-NEXT:    %u = urem i32 %num, 5
-; CHECK-NEXT:    --> ((-5 * (%num /u 5)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-5 * (%num /u 5)) + %num) U: [0,5) S: full-set
 ; CHECK-NEXT:    %i.010 = phi i32 [ 0, %entry ], [ %inc, %for.body ]
 ; CHECK-NEXT:    --> {0,+,1}<nuw><nsw><%for.body> U: [0,-2147483648) S: [0,-2147483648) Exits: (-1 + %num) LoopDispositions: { %for.body: Computable }
 ; CHECK-NEXT:    %inc = add nuw nsw i32 %i.010, 1
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-multiple.ll b/llvm/test/Analysis/ScalarEvolution/trip-multiple.ll
index bd0f603db942e..a7386066e096d 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-multiple.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-multiple.ll
@@ -16,7 +16,7 @@ define void @trip_multiple_3(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_3'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_3
 ; CHECK-NEXT:    %rem = urem i32 %num, 3
-; CHECK-NEXT:    --> ((-3 * (%num /u 3)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-3 * (%num /u 3)) + %num) U: [0,3) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
@@ -85,7 +85,7 @@ define void @trip_multiple_5(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_5'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_5
 ; CHECK-NEXT:    %rem = urem i32 %num, 5
-; CHECK-NEXT:    --> ((-5 * (%num /u 5)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-5 * (%num /u 5)) + %num) U: [0,5) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
@@ -120,7 +120,7 @@ define void @trip_multiple_6(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_6'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_6
 ; CHECK-NEXT:    %rem = urem i32 %num, 6
-; CHECK-NEXT:    --> ((-6 * (%num /u 6)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-6 * (%num /u 6)) + %num) U: [0,6) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
@@ -155,7 +155,7 @@ define void @trip_multiple_7(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_7'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_7
 ; CHECK-NEXT:    %rem = urem i32 %num, 7
-; CHECK-NEXT:    --> ((-7 * (%num /u 7)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-7 * (%num /u 7)) + %num) U: [0,7) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
@@ -224,7 +224,7 @@ define void @trip_multiple_9(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_9'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_9
 ; CHECK-NEXT:    %rem = urem i32 %num, 9
-; CHECK-NEXT:    --> ((-9 * (%num /u 9)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-9 * (%num /u 9)) + %num) U: [0,9) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
@@ -258,7 +258,7 @@ define void @trip_multiple_10(i32 noundef %num) {
 ; CHECK-LABEL: 'trip_multiple_10'
 ; CHECK-NEXT:  Classifying expressions for: @trip_multiple_10
 ; CHECK-NEXT:    %rem = urem i32 %num, 10
-; CHECK-NEXT:    --> ((-10 * (%num /u 10)) + %num) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-10 * (%num /u 10)) + %num) U: [0,10) S: full-set
 ; CHECK-NEXT:    %or.cond = and i1 %cmp, %cmp14
 ; CHECK-NEXT:    --> (%cmp14 umin %cmp) U: full-set S: full-set
 ; CHECK-NEXT:    %i.05 = phi i32 [ %inc, %for.body ], [ 0, %entry ]
diff --git a/llvm/test/Analysis/ScalarEvolution/urem-0.ll b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
index ec5bfda99a06a..657b32c157ac5 100644
--- a/llvm/test/Analysis/ScalarEvolution/urem-0.ll
+++ b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
@@ -5,7 +5,7 @@ define i8 @foo(i8 %a) {
 ; CHECK-LABEL: 'foo'
 ; CHECK-NEXT:  Classifying expressions for: @foo
 ; CHECK-NEXT:    %t0 = urem i8 %a, 27
-; CHECK-NEXT:    --> ((-27 * (%a /u 27)) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-27 * (%a /u 27)) + %a) U: [0,27) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @foo
 ;
   %t0 = urem i8 %a, 27
diff --git a/llvm/test/Transforms/LoopVectorize/AArch64/epilogue-vectorization-fix-scalar-resume-values.ll b/llvm/test/Transforms/LoopVectorize/AArch64/epilogue-vectorization-fix-scalar-resume-values.ll
index 4eacc55a99f72..5176bccfa388f 100644
--- a/llvm/test/Transforms/LoopVectorize/AArch64/epilogue-vectorization-fix-scalar-resume-values.ll
+++ b/llvm/test/Transforms/LoopVectorize/AArch64/epilogue-vectorization-fix-scalar-resume-values.ll
@@ -18,30 +18,23 @@ define void @epilogue_vectorization_fix_scalar_resume_values(ptr %dst, i64 %n) {
 ; CHECK-NEXT:    [[N_VEC:%.*]] = sub i64 [[REM]], [[N_MOD_VF]]
 ; CHECK-NEXT:    br label %[[VECTOR_BODY:.*]]
 ; CHECK:       [[VECTOR_BODY]]:
-; CHECK-NEXT:    [[INDEX:%.*]] = phi i64 [ 0, %[[VECTOR_PH]] ], [ [[INDEX_NEXT:%.*]], %[[VECTOR_BODY]] ]
-; CHECK-NEXT:    [[TMP0:%.*]] = getelementptr i8, ptr [[DST]], i64 [[INDEX]]
-; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[TMP0]], i64 16
-; CHECK-NEXT:    store <16 x i8> zeroinitializer, ptr [[TMP0]], align 1
+; CHECK-NEXT:    [[TMP1:%.*]] = getelementptr i8, ptr [[DST]], i64 16
+; CHECK-NEXT:    store <16 x i8> zeroinitializer, ptr [[DST]], align 1
 ; CHECK-NEXT:    store <16 x i8> zeroinitializer, ptr [[TMP1]], align 1
-; CHECK-NEXT:    [[INDEX_NEXT]] = add nuw i64 [[INDEX]], 32
-; CHECK-NEXT:    [[TMP2:%.*]] = icmp eq i64 [[INDEX_NEXT]], [[N_VEC]]
-; CHECK-NEXT:    br i1 [[TMP2]], label %[[MIDDLE_BLOCK:.*]], label %[[VECTOR_BODY]], !llvm.loop [[LOOP0:![0-9]+]]
+; CHECK-NEXT:    br label %[[MIDDLE_BLOCK:.*]]
 ; CHECK:       [[MIDDLE_BLOCK]]:
 ; CHECK-NEXT:    [[CMP_N:%.*]] = icmp eq i64 [[REM]], [[N_VEC]]
 ; CHECK-NEXT:    br i1 [[CMP_N]], label %[[EXIT:.*]], label %[[VEC_EPILOG_ITER_CHECK:.*]]
 ; CHECK:       [[VEC_EPILOG_ITER_CHECK]]:
 ; CHECK-NEXT:    [[MIN_EPILOG_ITERS_CHECK:%.*]] = icmp ult i64 [[N_MOD_VF]], 8
-; CHECK-NEXT:    br i1 [[MIN_EPILOG_ITERS_CHECK]], label %[[VEC_EPILOG_SCALAR_PH]], label %[[VEC_EPILOG_PH]], !prof [[PROF3:![0-9]+]]
+; CHECK-NEXT:    br i1 [[MIN_EPILOG_ITERS_CHECK]], label %[[VEC_EPILOG_SCALAR_PH]], label %[[VEC_EPILOG_PH]], !prof [[PROF0:![0-9]+]]
 ; CHECK:       [[VEC_EPILOG_PH]]:
-; CHECK-NEXT:    [[VEC_EPILOG_RESUME_VAL:%.*]] = phi i64 [ [[N_VEC]], %[[VEC_EPILOG_ITER_CHECK]] ], [ 0, %[[VECTOR_MAIN_LOOP_ITER_CHECK]] ]
+; CHECK-NEXT:    [[INDEX1:%.*]] = phi i64 [ [[N_VEC]], %[[VEC_EPILOG_ITER_CHECK]] ], [ 0, %[[VECTOR_MAIN_LOOP_ITER_CHECK]] ]
 ; CHECK-NEXT:    br label %[[VEC_EPILOG_VECTOR_BODY:.*]]
 ; CHECK:       [[VEC_EPILOG_VECTOR_BODY]]:
-; CHECK-NEXT:    [[INDEX1:%.*]] = phi i64 [ [[VEC_EPILOG_RESUME_VAL]], %[[VEC_EPILOG_PH]] ], [ [[INDEX_NEXT2:%.*]], %[[VEC_EPILOG_VECTOR_BODY]] ]
 ; CHECK-NEXT:    [[TMP3:%.*]] = getelementptr i8, ptr [[DST]], i64 [[INDEX1]]
 ; CHECK-NEXT:    store <8 x i8> zeroinitializer, ptr [[TMP3]], align 1
-; CHECK-NEXT:    [[INDEX_NEXT2]] = add nuw i64 [[INDEX1]], 8
-; CHECK-NEXT:    [[TMP4:%.*]] = icmp eq i64 [[INDEX_NEXT2]], 0
-; CHECK-NEXT:    br i1 [[TMP4]], label %[[VEC_EPILOG_MIDDLE_BLOCK:.*]], label %[[VEC_EPILOG_VECTOR_BODY]], !llvm.loop [[LOOP4:![0-9]+]]
+; CHECK-NEXT:    br label %[[VEC_EPILOG_MIDDLE_BLOCK:.*]]
 ; CHECK:       [[VEC_EPILOG_MIDDLE_BLOCK]]:
 ; CHECK-NEXT:    [[CMP_N3:%.*]] = icmp eq i64 [[REM]], 0
 ; CHECK-NEXT:    br i1 [[CMP_N3]], label %[[EXIT]], label %[[VEC_EPILOG_SCALAR_PH]]
@@ -54,7 +47,7 @@ define void @epilogue_vectorization_fix_scalar_resume_values(ptr %dst, i64 %n) {
 ; CHECK-NEXT:    store i8 0, ptr [[GEP_DST_IV]], align 1
 ; CHECK-NEXT:    [[IV_NEXT]] = add i64 [[IV]], 1
 ; CHECK-NEXT:    [[EXIT_COND:%.*]] = icmp eq i64 [[IV_NEXT]], [[REM]]
-; CHECK-NEXT:    br i1 [[EXIT_COND]], label %[[EXIT]], label %[[LOOP]], !llvm.loop [[LOOP5:![0-9]+]]
+; CHECK-NEXT:    br i1 [[EXIT_COND]], label %[[EXIT]], label %[[LOOP]], !llvm.loop [[LOOP1:![0-9]+]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret void
 ;

>From 1001dda2d45e8b727babf67a75496cd9188356cd Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 6 Jan 2026 10:54:23 +0000
Subject: [PATCH 3/5] !fixup use ConstantRange::urem

---
 llvm/lib/Analysis/ScalarEvolution.cpp        | 12 ++++--------
 llvm/test/Analysis/ScalarEvolution/urem-0.ll |  4 ++--
 2 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 3efdf5e436358..0e950dc7d7418 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -6781,15 +6781,11 @@ const ConstantRange &ScalarEvolution::getRangeRef(
     const SCEV *URemLHS = nullptr, *URemRHS = nullptr;
     if (SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED &&
         match(S, m_scev_URem(m_SCEV(URemLHS), m_SCEV(URemRHS), *this))) {
-      ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
       ConstantRange RHSRange = getRangeRef(URemRHS, SignHint, Depth + 1);
-      // URem result is always in [0, min(LHS, RHS - 1)] when RHS > 0.
-      if (RHSRange.getUnsignedMin().ugt(0)) {
-        APInt MaxVal = APIntOps::umin(LHSRange.getUnsignedMax(),
-                                      RHSRange.getUnsignedMax() - 1);
-        ConstantRange URemRange(APInt::getZero(BitWidth), MaxVal + 1);
-        ConservativeResult =
-            ConservativeResult.intersectWith(URemRange, RangeType);
+      if (!RHSRange.contains(APInt::getZero(RHSRange.getBitWidth()))) {
+        ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
+        ConservativeResult = ConservativeResult.intersectWith(
+            LHSRange.urem(RHSRange), RangeType);
       }
     }
     ConstantRange X = getRangeRef(Add->getOperand(0), SignHint, Depth + 1);
diff --git a/llvm/test/Analysis/ScalarEvolution/urem-0.ll b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
index 657b32c157ac5..7f1260477199f 100644
--- a/llvm/test/Analysis/ScalarEvolution/urem-0.ll
+++ b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
@@ -113,7 +113,7 @@ define i8 @urem_var_divisor_ugt_0_ult_10(i8 %a, i8 range(i8 1, 10) %d) {
 ; CHECK-LABEL: 'urem_var_divisor_ugt_0_ult_10'
 ; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ugt_0_ult_10
 ; CHECK-NEXT:    %t0 = urem i8 %a, %d
-; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: [0,9) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ugt_0_ult_10
 ;
   %t0 = urem i8 %a, %d
@@ -124,7 +124,7 @@ define i8 @urem_var_divisor_ugt_1_ult_10(i8 %a, i8 range(i8 2, 10) %d) {
 ; CHECK-LABEL: 'urem_var_divisor_ugt_1_ult_10'
 ; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ugt_1_ult_10
 ; CHECK-NEXT:    %t0 = urem i8 %a, %d
-; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: [0,9) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ugt_1_ult_10
 ;
   %t0 = urem i8 %a, %d

>From b1dcccb0f0cee299efbf6eb5b13cfb535aed05df Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 6 Jan 2026 17:27:12 +0000
Subject: [PATCH 4/5] !fixup remove 0 check

---
 llvm/lib/Analysis/ScalarEvolution.cpp                     | 8 +++-----
 .../Analysis/ScalarEvolution/trip-multiple-guard-info.ll  | 2 +-
 llvm/test/Analysis/ScalarEvolution/urem-0.ll              | 6 +++---
 3 files changed, 7 insertions(+), 9 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 0e950dc7d7418..dc6afaae60710 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -6781,12 +6781,10 @@ const ConstantRange &ScalarEvolution::getRangeRef(
     const SCEV *URemLHS = nullptr, *URemRHS = nullptr;
     if (SignHint == ScalarEvolution::HINT_RANGE_UNSIGNED &&
         match(S, m_scev_URem(m_SCEV(URemLHS), m_SCEV(URemRHS), *this))) {
+      ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
       ConstantRange RHSRange = getRangeRef(URemRHS, SignHint, Depth + 1);
-      if (!RHSRange.contains(APInt::getZero(RHSRange.getBitWidth()))) {
-        ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
-        ConservativeResult = ConservativeResult.intersectWith(
-            LHSRange.urem(RHSRange), RangeType);
-      }
+      ConservativeResult = ConservativeResult.intersectWith(
+          LHSRange.urem(RHSRange), RangeType);
     }
     ConstantRange X = getRangeRef(Add->getOperand(0), SignHint, Depth + 1);
     unsigned WrapType = OBO::AnyWrap;
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll b/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
index e1807a716bb26..aeda7f61a7575 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-multiple-guard-info.ll
@@ -721,7 +721,7 @@ define void @test_urem_non_constant(ptr %dst, i32 %a, i32 %b) {
 ; CHECK-LABEL: 'test_urem_non_constant'
 ; CHECK-NEXT:  Classifying expressions for: @test_urem_non_constant
 ; CHECK-NEXT:    %rem = urem i32 %a, %b
-; CHECK-NEXT:    --> ((-1 * (%a /u %b) * %b) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %b) * %b) + %a) U: [0,-1) S: full-set
 ; CHECK-NEXT:    %and.0 = and i1 %pre.0, %pre.1
 ; CHECK-NEXT:    --> (%pre.1 umin %pre.0) U: full-set S: full-set
 ; CHECK-NEXT:    %and.1 = and i1 %and.0, %pre.2
diff --git a/llvm/test/Analysis/ScalarEvolution/urem-0.ll b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
index 7f1260477199f..4730649297045 100644
--- a/llvm/test/Analysis/ScalarEvolution/urem-0.ll
+++ b/llvm/test/Analysis/ScalarEvolution/urem-0.ll
@@ -91,7 +91,7 @@ define i8 @urem_var_divisor_unknown(i8 %a, i8 %d) {
 ; CHECK-LABEL: 'urem_var_divisor_unknown'
 ; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_unknown
 ; CHECK-NEXT:    %t0 = urem i8 %a, %d
-; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: [0,-1) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_unknown
 ;
   %t0 = urem i8 %a, %d
@@ -102,7 +102,7 @@ define i8 @urem_var_divisor_ult_10(i8 %a, i8 range(i8 0, 10) %d) {
 ; CHECK-LABEL: 'urem_var_divisor_ult_10'
 ; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_ult_10
 ; CHECK-NEXT:    %t0 = urem i8 %a, %d
-; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: [0,9) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_ult_10
 ;
   %t0 = urem i8 %a, %d
@@ -135,7 +135,7 @@ define i8 @urem_var_divisor_signed(i8 %a, i8 range(i8 -19, 10) %d) {
 ; CHECK-LABEL: 'urem_var_divisor_signed'
 ; CHECK-NEXT:  Classifying expressions for: @urem_var_divisor_signed
 ; CHECK-NEXT:    %t0 = urem i8 %a, %d
-; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: full-set S: full-set
+; CHECK-NEXT:    --> ((-1 * (%a /u %d) * %d) + %a) U: [0,-1) S: full-set
 ; CHECK-NEXT:  Determining loop execution counts for: @urem_var_divisor_signed
 ;
   %t0 = urem i8 %a, %d

>From acb295033245a7c297899bb54dded4f9e0835181 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 6 Jan 2026 18:45:40 +0000
Subject: [PATCH 5/5] !fixup fix formatting

---
 llvm/lib/Analysis/ScalarEvolution.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index dc6afaae60710..3bb9c93deb0b3 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -6783,8 +6783,8 @@ const ConstantRange &ScalarEvolution::getRangeRef(
         match(S, m_scev_URem(m_SCEV(URemLHS), m_SCEV(URemRHS), *this))) {
       ConstantRange LHSRange = getRangeRef(URemLHS, SignHint, Depth + 1);
       ConstantRange RHSRange = getRangeRef(URemRHS, SignHint, Depth + 1);
-      ConservativeResult = ConservativeResult.intersectWith(
-          LHSRange.urem(RHSRange), RangeType);
+      ConservativeResult =
+          ConservativeResult.intersectWith(LHSRange.urem(RHSRange), RangeType);
     }
     ConstantRange X = getRangeRef(Add->getOperand(0), SignHint, Depth + 1);
     unsigned WrapType = OBO::AnyWrap;



More information about the llvm-commits mailing list