[llvm] [SCEV] Bound the max BE count by the range of End - Start. (PR #222752)
Florian Hahn via llvm-commits
llvm-commits at lists.llvm.org
Thu Sep 17 02:55:14 PDT 2026
https://github.com/fhahn updated https://github.com/llvm/llvm-project/pull/222752
>From c4e8749f330945ceae320c15b0f08382996588c6 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 10 Sep 2026 18:38:34 +0100
Subject: [PATCH 1/3] [SCEV] Add tests
---
...max-backedge-taken-count-correlated-end.ll | 140 ++++++++++++++++++
1 file changed, 140 insertions(+)
create mode 100644 llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
diff --git a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
new file mode 100644
index 0000000000000..3e2e30d502ff3
--- /dev/null
+++ b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
@@ -0,0 +1,140 @@
+; NOTE: Assertions have been autogenerated by utils/update_analyze_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes='print<scalar-evolution>' -scalar-evolution-classify-expressions=0 -disable-output %s 2>&1 | FileCheck %s
+
+define void @lt_end_is_start_plus_bounded_n(i8 %nraw, i8 %a) {
+; CHECK-LABEL: 'lt_end_is_start_plus_bounded_n'
+; CHECK-NEXT: Determining loop execution counts for: @lt_end_is_start_plus_bounded_n
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a) umax %a))
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a) umax %a))
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %n = and i8 %nraw, 7
+ %end = add i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = add i8 %iv, 1
+ %c = icmp ult i8 %iv, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @slt_end_is_start_plus_bounded_n(i8 %nraw, i8 %a) {
+; CHECK-LABEL: 'slt_end_is_start_plus_bounded_n'
+; CHECK-NEXT: Determining loop execution counts for: @slt_end_is_start_plus_bounded_n
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nsw> smax %a))
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nsw> smax %a))
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %n = and i8 %nraw, 7
+ %end = add nsw i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = add nsw i8 %iv, 1
+ %c = icmp slt i8 %iv, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @lt_end_is_start_plus_bounded_n_stride_3(i8 %nraw, i8 %a) {
+; CHECK-LABEL: 'lt_end_is_start_plus_bounded_n_stride_3'
+; CHECK-NEXT: Determining loop execution counts for: @lt_end_is_start_plus_bounded_n_stride_3
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((((-1 * (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))<nuw><nsw> + (-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a)) /u 3) + (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 85
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((((-1 * (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))<nuw><nsw> + (-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a)) /u 3) + (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %n = and i8 %nraw, 7
+ %end = add nuw nsw i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = add nuw nsw i8 %iv, 3
+ %c = icmp ult i8 %iv, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+
+define void @lt_difference_may_wrap(i8 %nraw, i8 %a) {
+; CHECK-LABEL: 'lt_difference_may_wrap'
+; CHECK-NEXT: Determining loop execution counts for: @lt_difference_may_wrap
+; CHECK-NEXT: Loop %loop: backedge-taken count is (-1 + (-1 * %a) + ((1 + %a) umax ((zext i3 (trunc i8 %nraw to i3) to i8) + %a)))
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is (-1 + (-1 * %a) + ((1 + %a) umax ((zext i3 (trunc i8 %nraw to i3) to i8) + %a)))
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %n = and i8 %nraw, 7
+ %end = add i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = add i8 %iv, 1
+ %c = icmp ult i8 %iv.next, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @lt_end_is_start_plus_unbounded_n(i8 %n, i8 %a) {
+; CHECK-LABEL: 'lt_end_is_start_plus_unbounded_n'
+; CHECK-NEXT: Determining loop execution counts for: @lt_end_is_start_plus_unbounded_n
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * %a) + ((%n + %a) umax %a))
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * %a) + ((%n + %a) umax %a))
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %end = add i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = add i8 %iv, 1
+ %c = icmp ult i8 %iv, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @gt_end_is_start_minus_bounded_n(i8 %nraw, i8 %a) {
+; CHECK-LABEL: 'gt_end_is_start_minus_bounded_n'
+; CHECK-NEXT: Determining loop execution counts for: @gt_end_is_start_minus_bounded_n
+; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * (((-1 * (zext i3 (trunc i8 %nraw to i3) to i8))<nsw> + %a) umin %a)) + %a)
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * (((-1 * (zext i3 (trunc i8 %nraw to i3) to i8))<nsw> + %a) umin %a)) + %a)
+; CHECK-NEXT: Loop %loop: Trip multiple is 1
+;
+entry:
+ %n = and i8 %nraw, 7
+ %end = sub i8 %a, %n
+ br label %loop
+
+loop:
+ %iv = phi i8 [ %a, %entry ], [ %iv.next, %loop ]
+ %iv.next = sub i8 %iv, 1
+ %c = icmp ugt i8 %iv, %end
+ br i1 %c, label %loop, label %exit
+
+exit:
+ ret void
+}
>From 59d61e2f030a1ef10675f382f575c2c077f873d6 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 10 Sep 2026 18:41:04 +0100
Subject: [PATCH 2/3] [SCEV] Bound the max BE count by the range of End -
Start.
In same cases, like A < A + N, End - Start can give a tighter maximum
bound due to folding that gets missed when just subtracting the ranges.
This improves results in a few cases:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1256.
Found while working on unifying howManyLessThans & howManyGreaterThans.
The latter uses a hand-rolled variant of computeMaxBECountForLT for GT,
which computes the delta like that.
---
llvm/lib/Analysis/ScalarEvolution.cpp | 11 +++++++++--
.../max-backedge-taken-count-correlated-end.ll | 6 +++---
2 files changed, 12 insertions(+), 5 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 30b5adfa266a2..d33ab93f42f0f 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13364,8 +13364,15 @@ const SCEV *ScalarEvolution::computeMaxBECountForLT(const SCEV *Start,
MaxEnd = IsSigned ? APIntOps::smax(MaxEnd, MinStart)
: APIntOps::umax(MaxEnd, MinStart);
- return getUDivCeilSCEV(getConstant(MaxEnd - MinStart) /* Delta */,
- getConstant(StrideForMaxBECount) /* Step */);
+ APInt Delta = MaxEnd - MinStart;
+
+ // Try to refine Delta in case End - Start gives a tighter bound after
+ // folding.
+ if (End->getType() == Start->getType())
+ Delta =
+ APIntOps::umin(Delta, getUnsignedRangeMax(getMinusSCEV(End, Start)));
+
+ return getUDivCeilSCEV(getConstant(Delta), getConstant(StrideForMaxBECount));
}
ScalarEvolution::ExitLimit
diff --git a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
index 3e2e30d502ff3..9bfa7c3299ec0 100644
--- a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
+++ b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-correlated-end.ll
@@ -5,7 +5,7 @@ define void @lt_end_is_start_plus_bounded_n(i8 %nraw, i8 %a) {
; CHECK-LABEL: 'lt_end_is_start_plus_bounded_n'
; CHECK-NEXT: Determining loop execution counts for: @lt_end_is_start_plus_bounded_n
; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a) umax %a))
-; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 7
; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a) umax %a))
; CHECK-NEXT: Loop %loop: Trip multiple is 1
;
@@ -28,7 +28,7 @@ define void @slt_end_is_start_plus_bounded_n(i8 %nraw, i8 %a) {
; CHECK-LABEL: 'slt_end_is_start_plus_bounded_n'
; CHECK-NEXT: Determining loop execution counts for: @slt_end_is_start_plus_bounded_n
; CHECK-NEXT: Loop %loop: backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nsw> smax %a))
-; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 -1
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 7
; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nsw> smax %a))
; CHECK-NEXT: Loop %loop: Trip multiple is 1
;
@@ -51,7 +51,7 @@ define void @lt_end_is_start_plus_bounded_n_stride_3(i8 %nraw, i8 %a) {
; CHECK-LABEL: 'lt_end_is_start_plus_bounded_n_stride_3'
; CHECK-NEXT: Determining loop execution counts for: @lt_end_is_start_plus_bounded_n_stride_3
; CHECK-NEXT: Loop %loop: backedge-taken count is ((((-1 * (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))<nuw><nsw> + (-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a)) /u 3) + (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))
-; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 85
+; CHECK-NEXT: Loop %loop: constant max backedge-taken count is i8 3
; CHECK-NEXT: Loop %loop: symbolic max backedge-taken count is ((((-1 * (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))<nuw><nsw> + (-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a)) /u 3) + (1 umin ((-1 * %a) + (((zext i3 (trunc i8 %nraw to i3) to i8) + %a)<nuw><nsw> umax %a))))
; CHECK-NEXT: Loop %loop: Trip multiple is 1
;
>From 1ff35d775943f3856d13c37812e4a8a9d0fe68ee Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 17 Sep 2026 10:54:42 +0100
Subject: [PATCH 3/3] !fixup drop unneeded type check
---
llvm/lib/Analysis/ScalarEvolution.cpp | 5 +----
1 file changed, 1 insertion(+), 4 deletions(-)
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index d33ab93f42f0f..7819aeef7c222 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13367,10 +13367,7 @@ const SCEV *ScalarEvolution::computeMaxBECountForLT(const SCEV *Start,
APInt Delta = MaxEnd - MinStart;
// Try to refine Delta in case End - Start gives a tighter bound after
- // folding.
- if (End->getType() == Start->getType())
- Delta =
- APIntOps::umin(Delta, getUnsignedRangeMax(getMinusSCEV(End, Start)));
+ Delta = APIntOps::umin(Delta, getUnsignedRangeMax(getMinusSCEV(End, Start)));
return getUDivCeilSCEV(getConstant(Delta), getConstant(StrideForMaxBECount));
}
More information about the llvm-commits
mailing list