[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