[llvm] feat: Scev predicated overflow btc and testing (PR #208581)

Bavly Peter via llvm-commits llvm-commits at lists.llvm.org
Thu Jul 30 10:46:05 PDT 2026


https://github.com/bavly-bob updated https://github.com/llvm/llvm-project/pull/208581

>From 9d2e6318c6b74e64381955c111ce3f05efe1a55c Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Thu, 9 Jul 2026 23:15:49 +0300
Subject: [PATCH 1/3] feat: Scev predicated overflow btc and testing

---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 22 +++++++--
 .../ScalarEvolution/fwrapv-assume-safety.ll   | 34 ++++++++++++++
 .../LoopVectorize/fwrapv-assume-safety.ll     | 45 +++++++++++++++++++
 .../Analysis/ScalarEvolutionTest.cpp          | 32 +++++++++++++
 4 files changed, 130 insertions(+), 3 deletions(-)
 create mode 100644 llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
 create mode 100644 llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ea2d72db97fb0..69f0b9c315a30 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13567,11 +13567,27 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
         Stride = getUMaxExpr(Stride, getOne(Stride->getType()));
       }
     }
-  } else if (!NoWrap) {
+  }else if (!NoWrap) {
     // Avoid proven overflow cases: this will ensure that the backedge taken
     // count will not generate any unsigned overflow.
-    if (canIVOverflowOnLT(RHS, Stride, IsSigned))
-      return getCouldNotCompute();
+    if (canIVOverflowOnLT(RHS, Stride, IsSigned)) {
+      if (!AllowPredicates)
+        return getCouldNotCompute();
+
+      unsigned BitWidth = getTypeSizeInBits(RHS->getType());
+      const SCEV *One = getOne(Stride->getType());
+      const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
+
+      APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
+                                         : getUnsignedRangeMax(StrideMinusOne);
+      APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
+                              : APInt::getMaxValue(BitWidth)) -
+                    MaxStrideMinusOne;
+
+      Predicates.push_back(getComparePredicate(
+          IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE, RHS,
+          getConstant(Limit)));
+    }
   }
 
   // On all paths just preceeding, we established the following invariant:
diff --git a/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll b/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
new file mode 100644
index 0000000000000..f8f7778cda507
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/fwrapv-assume-safety.ll
@@ -0,0 +1,34 @@
+; RUN: opt < %s -disable-output "-passes=print<scalar-evolution>" 2>&1 | FileCheck %s
+
+; Verify that ScalarEvolution can compute a predicated backedge-taken count
+; for a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
+; induction variable (e.g. from compilation under -fwrapv).
+; Without the predicate, the backedge-taken count is unpredictable.
+
+define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: Determining loop execution counts for: @fwrapv_stride3
+; CHECK-NEXT:  Loop %loop.body: Unpredictable backedge-taken count.
+; CHECK-NEXT:  Loop %loop.body: Unpredictable constant max backedge-taken count.
+; CHECK-NEXT:  Loop %loop.body: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop.body: Predicated backedge-taken count is
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %u sle) 2147483645
+
+entry:
+  %cmp1 = icmp slt i32 %l, %u
+  br i1 %cmp1, label %loop.body, label %exit
+
+loop.body:
+  %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+  %idxprom = sext i32 %i to i64
+  %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+  %val = load i32, ptr %arrayidx, align 4
+  %inc = add nsw i32 %val, 1
+  store i32 %inc, ptr %arrayidx, align 4
+  %i.next = add i32 %i, 3
+  %cmp = icmp slt i32 %i.next, %u
+  br i1 %cmp, label %loop.body, label %exit
+
+exit:
+  ret void
+}
diff --git a/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
new file mode 100644
index 0000000000000..6987c4ba66201
--- /dev/null
+++ b/llvm/test/Transforms/LoopVectorize/fwrapv-assume-safety.ll
@@ -0,0 +1,45 @@
+; RUN: opt -passes=loop-vectorize -force-vector-width=4 -force-vector-interleave=1 -S < %s | FileCheck %s
+
+; Verify that a loop with a non-unit stride (stride = 3) and no 'nsw' flag on the
+; induction variable (e.g. from compilation with -fwrapv) can be vectorized
+; by generating a runtime SCEV check to verify the lack of overflow/wrapping.
+
+define void @fwrapv_stride3(ptr noalias %x, i32 %l, i32 %u) {
+; CHECK-LABEL: @fwrapv_stride3(
+; CHECK:       vector.scevcheck:
+; CHECK:         %ident.check = icmp sgt i32 %u, 2147483645
+; CHECK:         %mul = call { i32, i1 } @llvm.umul.with.overflow.i32(i32 3, i32 {{.*}})
+; CHECK:         %mul.result = extractvalue { i32, i1 } %mul, 0
+; CHECK:         %mul.overflow = extractvalue { i32, i1 } %mul, 1
+; CHECK:         [[IDENT:%.*]] = add i32 %l, %mul.result
+; CHECK:         [[OVERFLOW:%.*]] = icmp slt i32 [[IDENT]], %l
+; CHECK:         [[OVERFLOW2:%.*]] = or i1 [[OVERFLOW]], %mul.overflow
+; CHECK:         [[CHECK:%.*]] = or i1 %ident.check, [[OVERFLOW2]]
+; CHECK:         br i1 [[CHECK]], label %scalar.ph, label %vector.ph
+; CHECK:       vector.body:
+; CHECK:         br i1 {{.*}}, label %middle.block, label %vector.body
+; CHECK:       scalar.ph:
+; CHECK:         [[RESUME_PHI:%.*]] = phi i32
+; CHECK:         br label %loop.body
+
+entry:
+  %cmp1 = icmp slt i32 %l, %u
+  br i1 %cmp1, label %loop.body, label %exit
+
+loop.body:
+  %i = phi i32 [ %l, %entry ], [ %i.next, %loop.body ]
+  %idxprom = sext i32 %i to i64
+  %arrayidx = getelementptr inbounds i32, ptr %x, i64 %idxprom
+  %val = load i32, ptr %arrayidx, align 4
+  %inc = add nsw i32 %val, 1
+  store i32 %inc, ptr %arrayidx, align 4
+  %i.next = add i32 %i, 3
+  %cmp = icmp slt i32 %i.next, %u
+  br i1 %cmp, label %loop.body, label %exit, !llvm.loop !0
+
+exit:
+  ret void
+}
+
+!0 = distinct !{!0, !1}
+!1 = !{!"llvm.loop.vectorize.enable", i1 true}
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 4bd4463dda787..849a7fec8fe51 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -1495,6 +1495,38 @@ TEST_F(ScalarEvolutionsTest, ImpliedCond) {
   });
 }
 
+TEST_F(ScalarEvolutionsTest, PredicatedBackedgeTakenCountForOverflowingStride) {
+  LLVMContext C;
+  SMDiagnostic Err;
+  std::unique_ptr<Module> M = parseAssemblyString(
+      "define void @foo(i32 %n) { "
+      "entry: "
+      "  br label %loop "
+      "loop: "
+      "  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ] "
+      "  %iv.next = add i32 %iv, 3 "
+      "  %cmp = icmp slt i32 %iv, %n "
+      "  br i1 %cmp, label %loop, label %exit "
+      "exit: "
+      "  ret void "
+      "}",
+      Err, C);
+
+  ASSERT_TRUE(M && "Could not parse module?");
+  ASSERT_TRUE(!verifyModule(*M) && "Must have been well formed!");
+
+  runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+    const Loop *L = LI.begin() != LI.end() ? *LI.begin() : nullptr;
+    ASSERT_NE(L, nullptr);
+
+    SmallVector<const SCEVPredicate *, 4> Predicates;
+    const SCEV *BTC = SE.getPredicatedBackedgeTakenCount(L, Predicates);
+
+    EXPECT_FALSE(isa<SCEVCouldNotCompute>(BTC));
+    EXPECT_FALSE(Predicates.empty());
+  });
+}
+
 TEST_F(ScalarEvolutionsTest, MatchURem) {
   LLVMContext C;
   SMDiagnostic Err;

>From c726c54a679e176bb2b9104eb5663a797154a313 Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Fri, 10 Jul 2026 17:12:07 +0300
Subject: [PATCH 2/3] [ScalarEvolution][test] Update expected test output

---
 .../addrec-may-wrap-udiv-canonicalize.ll      |  45 +++++++
 .../ScalarEvolution/max-trip-count.ll         |  23 ++++
 .../trip-count-non-unit-stride.ll             | 112 ++++++++++++++++++
 3 files changed, 180 insertions(+)

diff --git a/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll b/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
index e041c96371762..1132e7478c82a 100644
--- a/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
+++ b/llvm/test/Analysis/ScalarEvolution/addrec-may-wrap-udiv-canonicalize.ll
@@ -28,6 +28,15 @@ define void @test_step2_div4(i64 %n) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is ((1 + (0 smax %n))<nuw> /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387903
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is ((1 + (0 smax %n))<nuw> /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
 ;
 entry:
   br label %loop
@@ -78,6 +87,15 @@ define void @test_step3_div6(i64 %n) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is ((((-1 * (1 umin (0 smax %n)))<nuw><nsw> + (0 smax %n)) /u 3) + (1 umin (0 smax %n)))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775805
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 3074457345618258602
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775805
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is ((((-1 * (1 umin (0 smax %n)))<nuw><nsw> + (0 smax %n)) /u 3) + (1 umin (0 smax %n)))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775805
 ;
 entry:
   br label %loop
@@ -137,6 +155,15 @@ define void @test_step4_div4(i64 %n) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is ((3 + (0 smax %n))<nuw> /u 4)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775804
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 2305843009213693951
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775804
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is ((3 + (0 smax %n))<nuw> /u 4)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775804
 ;
 entry:
   br label %loop
@@ -219,6 +246,15 @@ define void @test_step2_start_outer_add_rec_step_16(i64 %n, i64 %m) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (({1,+,-16}<%outer.header> + ({0,+,16}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775807
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (({1,+,-16}<%outer.header> + ({0,+,16}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable symbolic max backedge-taken count.
@@ -335,6 +371,15 @@ define void @test_step2_div4_start_outer_add_rec_step_2(i64 %n, i64 %m) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (({1,+,-2}<%outer.header> + ({0,+,2}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775807
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (({1,+,-2}<%outer.header> + ({0,+,2}<%outer.header> smax %n)) /u 2)
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 9223372036854775806
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %outer.header: Unpredictable symbolic max backedge-taken count.
diff --git a/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll b/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
index 501aa963f9294..49051471297d0 100644
--- a/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
+++ b/llvm/test/Analysis/ScalarEvolution/max-trip-count.ll
@@ -446,6 +446,12 @@ define void @changing_end_bound4(i32 %start, ptr %n_addr, ptr %addr) {
 ; CHECK-NEXT:  Loop %loop: Unpredictable backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i32 1073741823
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 2147483644
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is i32 1073741823
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 2147483644
 ;
 entry:
   br label %loop
@@ -573,10 +579,27 @@ define void @max_overflow_me(i8 %n) mustprogress {
 ; CHECK-NEXT:  Loop %loop: <multiple exits> Unpredictable backedge-taken count.
 ; CHECK-NEXT:    exit count for loop: i8 1
 ; CHECK-NEXT:    exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for latch: i8 0
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 65
+; CHECK-EMPTY:
 ; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i8 1
 ; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i8 1
 ; CHECK-NEXT:    symbolic max exit count for loop: i8 1
 ; CHECK-NEXT:    symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for latch: i8 0
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 65
+; CHECK-EMPTY:
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is i8 0
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 65
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i8 0
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 65
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is i8 0
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %n sle) 65
 ;
 entry:
   br label %loop
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
index b52b4a984296c..ef4d9a3ac43f6 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count-non-unit-stride.ll
@@ -14,11 +14,39 @@ define void @test_preinc_ult(i64 %len) {
 ; CHECK-NEXT:  Determining loop execution counts for: @test_preinc_ult
 ; CHECK-NEXT:  Loop %loop: <multiple exits> Unpredictable backedge-taken count.
 ; CHECK-NEXT:    exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for loop: ((1 + %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:    exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
 ; CHECK-NEXT:    symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for loop: ((1 + %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:    symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (((1 + %len) /u 2) umin ((-1 + (2 umax %len)) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775806
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (((1 + %len) /u 2) umin ((-1 + (2 umax %len)) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
 ;
 start:
   br label %loop
@@ -49,11 +77,39 @@ define void @test_postinc_ult(i64 %len) {
 ; CHECK-NEXT:  Determining loop execution counts for: @test_postinc_ult
 ; CHECK-NEXT:  Loop %loop: <multiple exits> Unpredictable backedge-taken count.
 ; CHECK-NEXT:    exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for loop: ((1 umax %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:    exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
 ; CHECK-NEXT:    symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for loop: ((1 umax %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
 ; CHECK-NEXT:    symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for latch: ((-1 + (2 umax %len)) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-EMPTY:
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (((-1 + (2 umax %len)) /u 2) umin ((1 umax %len) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 9223372036854775806
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (((-1 + (2 umax %len)) /u 2) umin ((1 umax %len) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len ule) -2
+; CHECK-NEXT:      Compare predicate: %len ule) -2
 ;
 start:
   br label %loop
@@ -83,11 +139,39 @@ define void @test_preinc_slt(i64 %len) {
 ; CHECK-NEXT:  Determining loop execution counts for: @test_preinc_slt
 ; CHECK-NEXT:  Loop %loop: <multiple exits> Unpredictable backedge-taken count.
 ; CHECK-NEXT:    exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for loop: ((1 + (0 smax %len))<nuw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:    exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
 ; CHECK-NEXT:    symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for loop: ((1 + (0 smax %len))<nuw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:    symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (((1 + (0 smax %len))<nuw> /u 2) umin ((-1 + (2 smax %len))<nsw> /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387902
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (((1 + (0 smax %len))<nuw> /u 2) umin ((-1 + (2 smax %len))<nsw> /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
 ;
 start:
   br label %loop
@@ -118,11 +202,39 @@ define void @test_postinc_slt(i64 %len) {
 ; CHECK-NEXT:  Determining loop execution counts for: @test_postinc_slt
 ; CHECK-NEXT:  Loop %loop: <multiple exits> Unpredictable backedge-taken count.
 ; CHECK-NEXT:    exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for loop: ((1 smax %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:    exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:  Loop %loop: Unpredictable constant max backedge-taken count.
 ; CHECK-NEXT:  Loop %loop: Unpredictable symbolic max backedge-taken count.
 ; CHECK-NEXT:    symbolic max exit count for loop: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for loop: ((1 smax %len) /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
 ; CHECK-NEXT:    symbolic max exit count for latch: ***COULDNOTCOMPUTE***
+; CHECK-NEXT:    predicated symbolic max exit count for latch: ((-1 + (2 smax %len))<nsw> /u 2)
+; CHECK-NEXT:     Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-EMPTY:
+; CHECK-NEXT:  Loop %loop: Predicated backedge-taken count is (((-1 + (2 smax %len))<nsw> /u 2) umin ((1 smax %len) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated constant max backedge-taken count is i64 4611686018427387902
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:  Loop %loop: Predicated symbolic max backedge-taken count is (((-1 + (2 smax %len))<nsw> /u 2) umin ((1 smax %len) /u 2))
+; CHECK-NEXT:   Predicates:
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
+; CHECK-NEXT:      Compare predicate: %len sle) 9223372036854775806
 ;
 start:
   br label %loop

>From 224f0409a32ab02d508657f876f61904469b6ed9 Mon Sep 17 00:00:00 2001
From: bavly-bob <bavlypeter84 at gmail.com>
Date: Thu, 30 Jul 2026 20:45:12 +0300
Subject: [PATCH 3/3] [SCEV] Avoid creating compare predicates before pointer
 IV normalization howManyLessThans() can receive pointer-typed induction
 variables. Creating a compare predicate before converting the pointer SCEV to
 an integer SCEV violates SCEVComparePredicate's type equality invariant. Move
 predicate creation after pointer normalization so both operands have matching
 integer types.

---
 llvm/lib/Analysis/ScalarEvolution.cpp | 38 ++++++++++++++++-----------
 1 file changed, 22 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index b06bc2512f735..276732718e1fd 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13535,26 +13535,12 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
         Stride = getUMaxExpr(Stride, getOne(Stride->getType()));
       }
     }
-  }else if (!NoWrap) {
+  } else if (!NoWrap) {
     // Avoid proven overflow cases: this will ensure that the backedge taken
     // count will not generate any unsigned overflow.
     if (canIVOverflowOnLT(RHS, Stride, IsSigned)) {
       if (!AllowPredicates)
         return getCouldNotCompute();
-
-      unsigned BitWidth = getTypeSizeInBits(RHS->getType());
-      const SCEV *One = getOne(Stride->getType());
-      const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
-
-      APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
-                                         : getUnsignedRangeMax(StrideMinusOne);
-      APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
-                              : APInt::getMaxValue(BitWidth)) -
-                    MaxStrideMinusOne;
-
-      Predicates.push_back(getComparePredicate(
-          IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE, RHS,
-          getConstant(Limit)));
     }
   }
 
@@ -13586,6 +13572,26 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
       return RHS;
   }
 
+  // Now that RHS has been converted to an integer type (if it was a pointer),
+  // we can safely build the overflow predicate.  SCEVComparePredicate requires
+  // both operands to have identical LLVM types, which is guaranteed here
+  // because getConstant(Limit) uses the same BitWidth as RHS->getType().
+  if (!NoWrap && canIVOverflowOnLT(OrigRHS, Stride, IsSigned)) {
+    unsigned BitWidth = getTypeSizeInBits(RHS->getType());
+    const SCEV *One = getOne(Stride->getType());
+    const SCEV *StrideMinusOne = getMinusSCEV(Stride, One);
+
+    APInt MaxStrideMinusOne = IsSigned ? getSignedRangeMax(StrideMinusOne)
+                                       : getUnsignedRangeMax(StrideMinusOne);
+    APInt Limit = (IsSigned ? APInt::getSignedMaxValue(BitWidth)
+                            : APInt::getMaxValue(BitWidth)) -
+                  MaxStrideMinusOne;
+
+    Predicates.push_back(
+        getComparePredicate(IsSigned ? ICmpInst::ICMP_SLE : ICmpInst::ICMP_ULE,
+                            RHS, getConstant(Limit)));
+  }
+
   const SCEV *End = nullptr, *BECount = nullptr,
              *BECountIfBackedgeTaken = nullptr;
   if (!isLoopInvariant(RHS, L)) {
@@ -16497,4 +16503,4 @@ const SCEV *ScalarEvolution::applyLoopGuards(const SCEV *Expr, const Loop *L) {
 const SCEV *ScalarEvolution::applyLoopGuards(const SCEV *Expr,
                                              const LoopGuards &Guards) {
   return Guards.rewrite(Expr);
-}
+}
\ No newline at end of file



More information about the llvm-commits mailing list