[llvm] [SCEV] Strip dead code in proving RHS > Start in howManyLT (NFC) (PR #222878)

Ramkumar Ramachandra via llvm-commits llvm-commits at lists.llvm.org
Fri Sep 11 05:39:52 PDT 2026


https://github.com/artagnon updated https://github.com/llvm/llvm-project/pull/222878

>From c28ea6509dd1610520e6197d73736f743e989b16 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 11 Sep 2026 09:06:22 +0100
Subject: [PATCH 1/2] [SCEV] Strip dead code in proving RHS > Start in
 howManyLT (NFC)

isLoopEntryGuardedByCond already handles the case.
---
 llvm/lib/Analysis/ScalarEvolution.cpp | 19 ++-----------------
 1 file changed, 2 insertions(+), 17 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 06f350fd2179b..9e32cb68d043c 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -13723,23 +13723,8 @@ ScalarEvolution::howManyLessThans(const SCEV *LHS, const SCEV *RHS,
         const SCEV *GuardedRHS = applyLoopGuards(OrigRHS, L);
         const SCEV *GuardedStart = applyLoopGuards(OrigStart, L);
 
-        if (isLoopEntryGuardedByCond(L, CondGE, OrigRHS, OrigStart) ||
-            isKnownPredicate(CondGE, GuardedRHS, GuardedStart))
-          return true;
-
-        // (RHS > Start - 1) implies RHS >= Start.
-        // * "RHS >= Start" is trivially equivalent to "RHS > Start - 1" if
-        //   "Start - 1" doesn't overflow.
-        // * For signed comparison, if Start - 1 does overflow, it's equal
-        //   to INT_MAX, and "RHS >s INT_MAX" is trivially false.
-        // * For unsigned comparison, if Start - 1 does overflow, it's equal
-        //   to UINT_MAX, and "RHS >u UINT_MAX" is trivially false.
-        //
-        // FIXME: Should isLoopEntryGuardedByCond do this for us?
-        auto CondGT = IsSigned ? ICmpInst::ICMP_SGT : ICmpInst::ICMP_UGT;
-        auto *StartMinusOne =
-            getAddExpr(OrigStart, getMinusOne(OrigStart->getType()));
-        return isLoopEntryGuardedByCond(L, CondGT, OrigRHS, StartMinusOne);
+        return isLoopEntryGuardedByCond(L, CondGE, OrigRHS, OrigStart) ||
+               isKnownPredicate(CondGE, GuardedRHS, GuardedStart);
       };
 
       // If we know that RHS >= Start in the context of loop, then we know

>From 76073bcf36d42038b7985e429ec42eb950208909 Mon Sep 17 00:00:00 2001
From: Ramkumar Ramachandra <artagnon at tenstorrent.com>
Date: Fri, 11 Sep 2026 13:16:07 +0100
Subject: [PATCH 2/2] [SCEV] Sharpen BTC computations with minus-one overflow
 trick

Add a special case for LHS >= RHS in isKnownPredicateWithNoOverflow in
order to sharpen the result for users doing BTC-computations. It should
be non-functional for other users.
---
 llvm/lib/Analysis/ScalarEvolution.cpp             | 15 ++++++++++++---
 .../max-backedge-taken-count-guard-info.ll        |  4 ++--
 .../test/Analysis/ScalarEvolution/trip-count13.ll |  8 ++++----
 .../Transforms/IndVarSimplify/trivial-checks.ll   | 10 +++-------
 4 files changed, 21 insertions(+), 16 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 9e32cb68d043c..058009faa9cbe 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11703,9 +11703,9 @@ bool ScalarEvolution::isKnownPredicateViaNoOverflow(CmpPredicate Pred,
   // C1 and C2 are constant integers. If either X or Y are not add expressions,
   // consider them as X + 0 and Y + 0 respectively. C1 and C2 are returned via
   // OutC1 and OutC2.
-  auto MatchBinaryAddToConst = [this](SCEVUse X, SCEVUse Y, APInt &OutC1,
-                                      APInt &OutC2,
-                                      SCEV::NoWrapFlags ExpectedFlags) {
+  auto MatchBinaryAddToConst = [this, &Pred](SCEVUse X, SCEVUse Y, APInt &OutC1,
+                                             APInt &OutC2,
+                                             SCEV::NoWrapFlags ExpectedFlags) {
     SCEVUse XNonConstOp, XConstOp;
     SCEVUse YNonConstOp, YConstOp;
     SCEV::NoWrapFlags XFlagsPresent;
@@ -11740,6 +11740,15 @@ bool ScalarEvolution::isKnownPredicateViaNoOverflow(CmpPredicate Pred,
       return false;
     }
 
+    // X >= Y is equivalent to X > Y - 1, and this holds trivially when Y - 1
+    // does not overflow. When it does overflow, it must be INT_MAX, and the
+    // comparison would evaluate to false anyway.
+    // This simplification is useful when computing backedge-taken-counts.
+    if (YConstOp->isZero() && ICmpInst::isGE(Pred)) {
+      Pred = ICmpInst::getStrictPredicate(Pred);
+      YConstOp = getMinusOne(Y->getType());
+    }
+
     OutC1 = cast<SCEVConstant>(XConstOp)->getAPInt();
     OutC2 = cast<SCEVConstant>(YConstOp)->getAPInt();
 
diff --git a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll
index 8f7d2007cdcc0..f9c7f041bff46 100644
--- a/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll
+++ b/llvm/test/Analysis/ScalarEvolution/max-backedge-taken-count-guard-info.ll
@@ -1751,7 +1751,7 @@ define void @range_check_idiom_through_zext_ugt(i32 %len) {
 ; CHECK-NEXT:    --> {1,+,1}<nuw><nsw><%loop> U: [1,-2147483648) S: [1,-2147483648) Exits: %len LoopDispositions: { %loop: Computable }
 ; CHECK-NEXT:  Determining loop execution counts for: @range_check_idiom_through_zext_ugt
 ; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + %len)
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 -1
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i32 -2
 ; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + %len)
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
@@ -1982,7 +1982,7 @@ define void @one_sided_guard_does_not_bound_difference(ptr %dst, i64 %n, i64 %st
 ; CHECK-NEXT:    --> {(1 + %start),+,1}<nuw><%loop> U: full-set S: full-set Exits: %n LoopDispositions: { %loop: Computable }
 ; CHECK-NEXT:  Determining loop execution counts for: @one_sided_guard_does_not_bound_difference
 ; CHECK-NEXT:  Loop %loop: backedge-taken count is (-1 + (-1 * %start) + %n)
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -1
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i64 -2
 ; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-1 + (-1 * %start) + %n)
 ; CHECK-NEXT:  Loop %loop: Trip multiple is 1
 ;
diff --git a/llvm/test/Analysis/ScalarEvolution/trip-count13.ll b/llvm/test/Analysis/ScalarEvolution/trip-count13.ll
index 720b2ae49c090..e36dd45d3ba03 100644
--- a/llvm/test/Analysis/ScalarEvolution/trip-count13.ll
+++ b/llvm/test/Analysis/ScalarEvolution/trip-count13.ll
@@ -6,10 +6,10 @@ define void @u_0(i8 %rhs) {
 ;
 ; CHECK-LABEL: 'u_0'
 ; CHECK-NEXT:  Determining loop execution counts for: @u_0
-; CHECK-NEXT:  Loop %loop: backedge-taken count is (-100 + (-1 * %rhs) + ((100 + %rhs) umax %rhs))
-; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i8 -100, actual taken count either this or zero.
-; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is (-100 + (-1 * %rhs) + ((100 + %rhs) umax %rhs)), actual taken count either this or zero.
-; CHECK-NEXT:  Loop %loop: Trip multiple is 1
+; CHECK-NEXT:  Loop %loop: backedge-taken count is i8 -100
+; CHECK-NEXT:  Loop %loop: constant max backedge-taken count is i8 -100
+; CHECK-NEXT:  Loop %loop: symbolic max backedge-taken count is i8 -100
+; CHECK-NEXT:  Loop %loop: Trip multiple is 157
 ;
 entry:
   %start = add i8 %rhs, 100
diff --git a/llvm/test/Transforms/IndVarSimplify/trivial-checks.ll b/llvm/test/Transforms/IndVarSimplify/trivial-checks.ll
index 05aba4be5d54f..ce69b5f4fbed5 100644
--- a/llvm/test/Transforms/IndVarSimplify/trivial-checks.ll
+++ b/llvm/test/Transforms/IndVarSimplify/trivial-checks.ll
@@ -2,6 +2,7 @@
 ; RUN: opt -passes=indvars -S < %s | FileCheck %s
 
 ; FIXME: In all cases, x is from [0; 1000) and we cannot prove that x + 1 > x.
+; We can prove it for the ugt case though.
 
 define void @test_sgt(i32 %x) {
 ; CHECK-LABEL: @test_sgt(
@@ -101,14 +102,9 @@ define void @test_ugt(i32 %x) {
 ; CHECK:       loop.preheader:
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
-; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], [[GUARDED:%.*]] ], [ [[X]], [[LOOP_PREHEADER]] ]
-; CHECK-NEXT:    [[TMP:%.*]] = add nsw i32 [[IV]], 1
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ugt i32 [[TMP]], [[IV]]
-; CHECK-NEXT:    br i1 [[GUARD]], label [[GUARDED]], label [[FAIL:%.*]]
+; CHECK-NEXT:    br i1 false, label [[GUARDED:%.*]], label [[FAIL:%.*]]
 ; CHECK:       guarded:
-; CHECK-NEXT:    [[IV_NEXT]] = add nsw i32 [[IV]], -1
-; CHECK-NEXT:    [[COND:%.*]] = icmp eq i32 [[IV]], 0
-; CHECK-NEXT:    br i1 [[COND]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
+; CHECK-NEXT:    br i1 true, label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
 ; CHECK:       exit.loopexit:
 ; CHECK-NEXT:    br label [[EXIT]]
 ; CHECK:       exit:



More information about the llvm-commits mailing list