[llvm] [SCEV] Consider IVMayOverflow to use cheap BE-count formula. (PR #218694)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 09:59:22 PDT 2026


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

>From 42eb5a211d4c77370524941d74dc528ed49e0c15 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Tue, 25 Aug 2026 10:25:18 +0100
Subject: [PATCH 1/3] [SCEV] Consider IVMayOverflow to use cheap BE-count
 formula.

If !IVMayOverflow, we proven RHS + stride - 1 does not wrap. This means
adding the `Stride - 1` part of `RHS - (Start - Stride) - 1` does not wrap.

Together with the other condition `Start - Stride < RHS`, this should
guarantee `RHS - (Start - Stride) - 1` does not wrap and the cheap
formula can be used.

This triggers in a number of real-world cases, mostly resulting in
simpler SCEV expansions/checks, and extra unrolling. In a small number
of cases, expansion is slightly worse.

https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1043

Alive2 Proof: https://alive2.llvm.org/ce/z/u_KUx5

Depends on https://github.com/llvm/llvm-project/pull/218251 (included in
PR)
---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 25 ++++++++++++++-----
 .../ScalarEvolution/2008-11-18-Stride2.ll     |  4 +--
 2 files changed, 21 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 7ad84855d6cb6..f6445d30a0dba 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13546,8 +13546,10 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
     assert(isAvailableAtLoopEntry(OrigStartMinusStride, L) && "Must be!");
     assert(isAvailableAtLoopEntry(OrigStart, L) && "Must be!");
     assert(isAvailableAtLoopEntry(OrigRHS, L) && "Must be!");
-    // Can we prove Start - Stride < Start and Start - Stride < RHS?
-    if (isLoopEntryGuardedByCond(L, Cond, OrigStartMinusStride, OrigStart) &&
+    // Can we prove Start - Stride < RHS, and either Start - Stride < Start or
+    // (via !IVMayOverflow) that RHS + Stride - 1 does not overflow?
+    if ((!IVMayOverflow ||
+         isLoopEntryGuardedByCond(L, Cond, OrigStartMinusStride, OrigStart)) &&
         isLoopEntryGuardedByCond(L, Cond, OrigStartMinusStride, OrigRHS)) {
       // In this case, we can use a refined formula for computing backedge
       // taken count.  The general formula remains:
@@ -13555,7 +13557,7 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
       // We want to use the alternate formula:
       //   "((RHS - 1) - (Start - Stride)) /u Stride"
       // Let's do a quick case analysis to show these are equivalent under
-      // our preconditions that Start - Stride is below both Start and RHS.
+      // our preconditions.
       // * For RHS <= Start (End is Start), the backedge-taken count must be
       //   zero. Together with the precondition "Start - Stride < RHS", we have
       //   "Start - Stride < RHS <= Start". Subtracting Start - Stride from
@@ -13564,9 +13566,20 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
       //   So dividing that by Stride gives zero.
       //
       // * For RHS > Start (End is RHS), the backedge count must be
-      //   "RHS-Start /uceil Stride". Together with the precondition
-      //   "Start - Stride < Start", we have "RHS > Start > Start - Stride".
-      //   As such RHS - (Start - Stride) - 1 does not overflow.
+      //   "RHS-Start /uceil Stride", so it is sufficient to show that the
+      //   numerator "((RHS - 1) - (Start - Stride))" does not overflow.
+      //
+      //   If "Start - Stride < Start" holds, we have
+      //   "RHS > Start > Start - Stride". As such
+      //   "RHS - (Start - Stride) - 1" does not overflow, which reassociates to
+      //   "((RHS - 1) - (Start - Stride))".
+      //
+      //   Otherwise !IVMayOverflow guarantees
+      //   "RHS <= max unsigned value - (Stride - 1)".
+      //   With "RHS > Start" we have
+      //   "RHS - Start < RHS <= max unsigned value - (Stride - 1)".
+      //   As such "(RHS - Start) + (Stride - 1)" does not overflow, which
+      //   reassociates to "((RHS - 1) - (Start - Stride))".
       const SCEV *MinusOne = getMinusOne(Stride->getType());
       const SCEV *Numerator =
           getMinusSCEV(getAddExpr(RHS, MinusOne), getMinusSCEV(Start, Stride));
diff --git a/llvm/test/Analysis/ScalarEvolution/2008-11-18-Stride2.ll b/llvm/test/Analysis/ScalarEvolution/2008-11-18-Stride2.ll
index 86f1757c46002..ca539e5f3c626 100644
--- a/llvm/test/Analysis/ScalarEvolution/2008-11-18-Stride2.ll
+++ b/llvm/test/Analysis/ScalarEvolution/2008-11-18-Stride2.ll
@@ -8,9 +8,9 @@ define i32 @f(i32 %x) nounwind readnone {
 ;
 ; CHECK-LABEL: 'f'
 ; CHECK-NEXT:  Determining loop execution counts for: @f
-; CHECK-NEXT:  Loop %bb: backedge-taken count is ((-1 + (-1 * %x) + (1000 umax (3 + %x))) /u 3)
+; CHECK-NEXT:  Loop %bb: backedge-taken count is ((999 + (-1 * %x)) /u 3)
 ; CHECK-NEXT:  Loop %bb: constant max backedge-taken count is i32 334
-; CHECK-NEXT:  Loop %bb: symbolic max backedge-taken count is ((-1 + (-1 * %x) + (1000 umax (3 + %x))) /u 3)
+; CHECK-NEXT:  Loop %bb: symbolic max backedge-taken count is ((999 + (-1 * %x)) /u 3)
 ; CHECK-NEXT:  Loop %bb: Trip multiple is 1
 ;
 entry:

>From 31c63cef6345863259f3e7a0556fa569aa436123 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 28 Aug 2026 12:04:54 +0100
Subject: [PATCH 2/3] !fixup update comment

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

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index f6445d30a0dba..b771609a19bb1 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13571,15 +13571,15 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
       //
       //   If "Start - Stride < Start" holds, we have
       //   "RHS > Start > Start - Stride". As such
-      //   "RHS - (Start - Stride) - 1" does not overflow, which reassociates to
-      //   "((RHS - 1) - (Start - Stride))".
+      //   "RHS - (Start - Stride) - 1" does not overflow, which is the
+      //   reassociated numerator.
       //
-      //   Otherwise !IVMayOverflow guarantees
-      //   "RHS <= max unsigned value - (Stride - 1)".
-      //   With "RHS > Start" we have
-      //   "RHS - Start < RHS <= max unsigned value - (Stride - 1)".
-      //   As such "(RHS - Start) + (Stride - 1)" does not overflow, which
-      //   reassociates to "((RHS - 1) - (Start - Stride))".
+      //   Otherwise !IVMayOverflow guarantees "RHS + (Stride - 1) <= MaxV ",
+      //   where MaxV is the maximum signed/unsigned value. Let MinV be the
+      //   matching minimum value. "Start >= MinV" gives
+      //   "RHS + (Stride - 1) - Start <= MaxV - MinV", and as "MaxV - MinV" is
+      //   the largest unsigned value, the reassociated numerator does not
+      //   overflow.
       const SCEV *MinusOne = getMinusOne(Stride->getType());
       const SCEV *Numerator =
           getMinusSCEV(getAddExpr(RHS, MinusOne), getMinusSCEV(Start, Stride));

>From f78f942a7724534086ef525b010d3a5c12db3211 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Fri, 28 Aug 2026 17:58:58 +0100
Subject: [PATCH 3/3] !fixup remove stray space

---
 llvm/lib/Analysis/ScalarEvolution.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index b771609a19bb1..25e5ee3cd057a 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13574,7 +13574,7 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
       //   "RHS - (Start - Stride) - 1" does not overflow, which is the
       //   reassociated numerator.
       //
-      //   Otherwise !IVMayOverflow guarantees "RHS + (Stride - 1) <= MaxV ",
+      //   Otherwise !IVMayOverflow guarantees "RHS + (Stride - 1) <= MaxV",
       //   where MaxV is the maximum signed/unsigned value. Let MinV be the
       //   matching minimum value. "Start >= MinV" gives
       //   "RHS + (Stride - 1) - Start <= MaxV - MinV", and as "MaxV - MinV" is



More information about the llvm-commits mailing list