[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