[llvm] [SCEV] Simplify `udiv` equality comparisons against 0 (PR #218084)

Justin Fargnoli via llvm-commits llvm-commits at lists.llvm.org
Fri Aug 28 09:02:37 PDT 2026


https://github.com/justinfargnoli updated https://github.com/llvm/llvm-project/pull/218084

>From c0ea2e1d194afed4e85a8ed12d6adc087bbedd2f Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Tue, 25 Aug 2026 20:44:33 +0000
Subject: [PATCH 1/5] pre-commit tests

---
 .../Transforms/IndVarSimplify/udiv-icmp.ll    | 247 ++++++++++++++++++
 .../runtime-unroll-scev-udiv-entry-guard.ll   | 118 +++++++++
 2 files changed, 365 insertions(+)
 create mode 100644 llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
 create mode 100644 llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll

diff --git a/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll b/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
new file mode 100644
index 0000000000000..d03a4d9edfe82
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
@@ -0,0 +1,247 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt < %s -S -passes='loop(indvars)' | FileCheck %s
+
+define i1 @quotient_eq_zero(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_eq_zero(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp eq i64 [[Q]], 0
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp eq i64 %q, 0
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp ult i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @quotient_ule_zero(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_ule_zero(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp ule i64 [[Q]], 0
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp ule i64 %q, 0
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp ult i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @quotient_ult_one(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_ult_one(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp ult i64 [[Q]], 1
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp ult i64 %q, 1
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp ult i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @quotient_ne_zero(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_ne_zero(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp ne i64 [[Q]], 0
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp ne i64 %q, 0
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp uge i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @quotient_ugt_zero(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_ugt_zero(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp ugt i64 [[Q]], 0
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp ugt i64 %q, 0
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp uge i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @quotient_uge_one(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @quotient_uge_one(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp uge i64 [[Q]], 1
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp uge i64 %q, 1
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp uge i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
+
+define i1 @constant_on_left(i64 %a, i64 %b) {
+; CHECK-LABEL: define i1 @constant_on_left(
+; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
+; CHECK-NEXT:    [[GUARD:%.*]] = icmp ult i64 0, [[Q]]
+; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_PREHEADER]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
+; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK:       [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    br label %[[EXIT]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT:    ret i1 [[RESULT]]
+;
+entry:
+  %q = udiv i64 %a, %b
+  %guard = icmp ult i64 0, %q
+  br i1 %guard, label %loop, label %exit
+
+loop:
+  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
+  %cmp = icmp uge i64 %same, %b
+  %same.next = add i64 %same, 1
+  br i1 false, label %loop, label %exit
+
+exit:
+  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
+  ret i1 %result
+}
diff --git a/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll b/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
new file mode 100644
index 0000000000000..01beac717a256
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
@@ -0,0 +1,118 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -S -passes='loop-unroll<O3;runtime>' < %s | FileCheck %s
+
+define void @openmp_loop(
+; CHECK-LABEL: define void @openmp_loop(
+; CHECK-SAME: ptr [[LOWER_BOUND_PTR:%.*]], ptr [[DENOMINATOR_PTR:%.*]], i64 [[OUTER_LIMIT:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[OUTER:.*]]
+; CHECK:       [[OUTER]]:
+; CHECK-NEXT:    [[OUTER_INDEX:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[NEXT_OUTER_INDEX:%.*]], %[[OUTER_LATCH:.*]] ]
+; CHECK-NEXT:    [[LOWER_BOUND_I32:%.*]] = load i32, ptr [[LOWER_BOUND_PTR]], align 4
+; CHECK-NEXT:    [[DENOMINATOR_I32:%.*]] = load i32, ptr [[DENOMINATOR_PTR]], align 4
+; CHECK-NEXT:    [[LOWER_BOUND:%.*]] = zext i32 [[LOWER_BOUND_I32]] to i64
+; CHECK-NEXT:    [[DENOMINATOR:%.*]] = zext i32 [[DENOMINATOR_I32]] to i64
+; CHECK-NEXT:    [[LOWER_BOUND_TOO_LARGE:%.*]] = icmp ugt i32 [[LOWER_BOUND_I32]], 35
+; CHECK-NEXT:    br i1 [[LOWER_BOUND_TOO_LARGE]], label %[[OUTER_LATCH]], label %[[COMPUTE_TRIP_COUNT:.*]]
+; CHECK:       [[COMPUTE_TRIP_COUNT]]:
+; CHECK-NEXT:    [[DIFFERENCE:%.*]] = sub nsw i64 [[DENOMINATOR]], [[LOWER_BOUND]]
+; CHECK-NEXT:    [[NUMERATOR:%.*]] = add nsw i64 [[DIFFERENCE]], 35
+; CHECK-NEXT:    [[QUOTIENT:%.*]] = udiv i64 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[SKIP_INNER:%.*]] = icmp ult i64 [[NUMERATOR]], [[DENOMINATOR]]
+; CHECK-NEXT:    br i1 [[SKIP_INNER]], label %[[OUTER_LATCH]], label %[[INNER_EPIL_PREHEADER:.*]]
+; CHECK:       [[INNER_EPIL_PREHEADER]]:
+; CHECK-NEXT:    br label %[[INNER_EPIL:.*]]
+; CHECK:       [[INNER_EPIL]]:
+; CHECK-NEXT:    [[COUNT_EPIL:%.*]] = phi i64 [ [[NEXT_EPIL:%.*]], %[[INNER_EPIL]] ], [ [[QUOTIENT]], %[[INNER_EPIL_PREHEADER]] ]
+; CHECK-NEXT:    [[OFFSET_EPIL:%.*]] = phi i64 [ [[NEXT_OFFSET_EPIL:%.*]], %[[INNER_EPIL]] ], [ 0, %[[INNER_EPIL_PREHEADER]] ]
+; CHECK-NEXT:    [[NEXT_EPIL]] = add i64 [[COUNT_EPIL]], -1
+; CHECK-NEXT:    [[NEXT_OFFSET_EPIL]] = add i64 [[OFFSET_EPIL]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[CONTINUE_EPIL:%.*]] = icmp ugt i64 [[COUNT_EPIL]], 1
+; CHECK-NEXT:    br i1 [[CONTINUE_EPIL]], label %[[INNER_EPIL]], label %[[OUTER_LATCH_LOOPEXIT:.*]]
+; CHECK:       [[OUTER_LATCH_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[OUTER_LATCH]]
+; CHECK:       [[OUTER_LATCH]]:
+; CHECK-NEXT:    call void @barrier()
+; CHECK-NEXT:    [[NEXT_OUTER_INDEX]] = add i64 [[OUTER_INDEX]], 1
+; CHECK-NEXT:    [[CONTINUE_OUTER:%.*]] = icmp ult i64 [[NEXT_OUTER_INDEX]], [[OUTER_LIMIT]]
+; CHECK-NEXT:    br i1 [[CONTINUE_OUTER]], label %[[OUTER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+  ptr %lower_bound_ptr, ptr %denominator_ptr, i64 %outer_limit) {
+entry:
+  br label %outer
+
+outer:
+  %outer_index = phi i64 [ 0, %entry ], [ %next_outer_index, %outer_latch ]
+  %lower_bound_i32 = load i32, ptr %lower_bound_ptr
+  %denominator_i32 = load i32, ptr %denominator_ptr
+  %lower_bound = zext i32 %lower_bound_i32 to i64
+  %denominator = zext i32 %denominator_i32 to i64
+  %lower_bound_too_large = icmp ugt i32 %lower_bound_i32, 35
+  br i1 %lower_bound_too_large, label %outer_latch, label %compute_trip_count
+
+compute_trip_count:
+  %difference = sub nsw i64 %denominator, %lower_bound
+  %numerator = add nsw i64 %difference, 35
+  %quotient = udiv i64 %numerator, %denominator
+  %skip_inner = icmp ult i64 %numerator, %denominator
+  br i1 %skip_inner, label %outer_latch, label %inner
+
+inner:
+  %count = phi i64 [ %next, %inner ], [ %quotient, %compute_trip_count ]
+  %offset = phi i64 [ %next_offset, %inner ], [ 0, %compute_trip_count ]
+  %next = add i64 %count, -1
+  %next_offset = add i64 %offset, %denominator
+  %continue = icmp ugt i64 %count, 1
+  br i1 %continue, label %inner, label %outer_latch
+
+outer_latch:
+  call void @barrier()
+  %next_outer_index = add i64 %outer_index, 1
+  %continue_outer = icmp ult i64 %next_outer_index, %outer_limit
+  br i1 %continue_outer, label %outer, label %exit
+
+exit:
+  ret void
+}
+
+declare void @barrier() convergent noduplicate
+
+define void @udiv_entry_guard_reduced(ptr %denominator_ptr) {
+; CHECK-LABEL: define void @udiv_entry_guard_reduced(
+; CHECK-SAME: ptr [[DENOMINATOR_PTR:%.*]]) {
+; CHECK-NEXT:  [[OUTER_LOOPEXIT_UNR_LCSSA:.*:]]
+; CHECK-NEXT:    br label %[[OUTER_LOOPEXIT:.*]]
+; CHECK:       [[OUTER_LOOPEXIT1:.*]]:
+; CHECK-NEXT:    br label %[[OUTER_BACKEDGE:.*]]
+; CHECK:       [[OUTER_LOOPEXIT]]:
+; CHECK-NEXT:    [[DENOMINATOR:%.*]] = load i64, ptr [[DENOMINATOR_PTR]], align 4
+; CHECK-NEXT:    [[QUOTIENT:%.*]] = udiv i64 35, [[DENOMINATOR]]
+; CHECK-NEXT:    [[SKIP_INNER:%.*]] = icmp ult i64 35, [[DENOMINATOR]]
+; CHECK-NEXT:    br i1 [[SKIP_INNER]], label %[[OUTER_BACKEDGE]], label %[[INNER_PREHEADER:.*]]
+; CHECK:       [[OUTER_BACKEDGE]]:
+; CHECK-NEXT:    br label %[[OUTER_LOOPEXIT]]
+; CHECK:       [[INNER_PREHEADER]]:
+; CHECK-NEXT:    br label %[[INNER_PROL:.*]]
+; CHECK:       [[INNER_PROL]]:
+; CHECK-NEXT:    [[COUNT_PROL:%.*]] = phi i64 [ [[NEXT_PROL:%.*]], %[[INNER_PROL]] ], [ [[QUOTIENT]], %[[INNER_PREHEADER]] ]
+; CHECK-NEXT:    [[NEXT_PROL]] = add i64 [[COUNT_PROL]], -1
+; CHECK-NEXT:    [[CONTINUE_PROL:%.*]] = icmp ugt i64 [[COUNT_PROL]], 1
+; CHECK-NEXT:    br i1 [[CONTINUE_PROL]], label %[[INNER_PROL]], label %[[OUTER_LOOPEXIT1]]
+;
+entry:
+  br label %outer
+
+outer:                                            ; preds = %inner, %outer, %entry
+  %denominator = load i64, ptr %denominator_ptr
+  %quotient = udiv i64 35, %denominator
+  %skip_inner = icmp ult i64 35, %denominator
+  br i1 %skip_inner, label %outer, label %inner
+
+inner:                                            ; preds = %inner, %outer
+  %count = phi i64 [ %next, %inner ], [ %quotient, %outer ]
+  %next = add i64 %count, -1
+  %continue = icmp ugt i64 %count, 1
+  br i1 %continue, label %inner, label %outer
+}

>From 566ba48a10d542f373eee670cd1d55315a20afa3 Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Tue, 25 Aug 2026 20:45:42 +0000
Subject: [PATCH 2/5] [SCEV] Simplify udiv equality comparisons against 0

---
 llvm/lib/Analysis/ScalarEvolution.cpp         | 11 +++
 .../Thumb2/LowOverheadLoops/while-loops.ll    | 12 +--
 .../IndVarSimplify/AArch64/loop-guards.ll     |  9 +-
 .../Transforms/IndVarSimplify/udiv-icmp.ll    | 21 ++---
 .../runtime-unroll-scev-udiv-entry-guard.ll   | 82 +++++++++++++++++--
 5 files changed, 103 insertions(+), 32 deletions(-)

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index ba15210a3b456..a994c4d579966 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11113,6 +11113,17 @@ bool ScalarEvolution::SimplifyICmpOperands(CmpPredicate &Pred, SCEVUse &LHS,
     }
   }
 
+  // For a defined unsigned division, the quotient is zero exactly when the
+  // numerator is less than the denominator. The constant-range canonicalization
+  // above reduces equivalent zero/nonzero comparisons to EQ/NE against zero.
+  if (const auto *Div = dyn_cast<SCEVUDivExpr>(LHS);
+      Div && RHS->isZero() && ICmpInst::isEquality(Pred)) {
+    LHS = Div->getLHS();
+    RHS = Div->getRHS();
+    Pred = Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
+    Changed = true;
+  }
+
   // Check for obvious equality.
   if (HasSameValue(LHS, RHS)) {
     if (ICmpInst::isTrueWhenEqual(Pred))
diff --git a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/while-loops.ll b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/while-loops.ll
index a637fe0ca446b..68cceb324cb3a 100644
--- a/llvm/test/CodeGen/Thumb2/LowOverheadLoops/while-loops.ll
+++ b/llvm/test/CodeGen/Thumb2/LowOverheadLoops/while-loops.ll
@@ -10,7 +10,7 @@ define void @simple(ptr nocapture readonly %x, ptr nocapture readnone %y, ptr no
 ; CHECK-NEXT:    ldr r1, [sp, #8]
 ; CHECK-NEXT:    mov r12, r3
 ; CHECK-NEXT:    adds r3, r1, #3
-; CHECK-NEXT:    lsrs r3, r3, #2
+; CHECK-NEXT:    lsrs.w lr, r3, #2
 ; CHECK-NEXT:    beq .LBB0_3
 ; CHECK-NEXT:  @ %bb.1: @ %do.body.preheader
 ; CHECK-NEXT:    dlstp.32 lr, r1
@@ -73,14 +73,14 @@ define void @nested(ptr nocapture readonly %x, ptr nocapture readnone %y, ptr no
 ; CHECK-NEXT:  .LBB1_4: @ %for.body
 ; CHECK-NEXT:    @ =>This Loop Header: Depth=1
 ; CHECK-NEXT:    @ Child Loop BB1_6 Depth 2
-; CHECK-NEXT:    add.w r6, r12, #3
-; CHECK-NEXT:    lsrs r7, r6, #2
+; CHECK-NEXT:    add.w r7, r12, #3
+; CHECK-NEXT:    lsrs r5, r7, #2
 ; CHECK-NEXT:    beq .LBB1_2
 ; CHECK-NEXT:  @ %bb.5: @ %do.body.preheader
 ; CHECK-NEXT:    @ in Loop: Header=BB1_4 Depth=1
-; CHECK-NEXT:    bic r5, r6, #3
+; CHECK-NEXT:    bic r7, r7, #3
+; CHECK-NEXT:    add.w r8, r0, r7, lsl #2
 ; CHECK-NEXT:    mov r4, r3
-; CHECK-NEXT:    add.w r8, r0, r5, lsl #2
 ; CHECK-NEXT:    dlstp.32 lr, r12
 ; CHECK-NEXT:  .LBB1_6: @ %do.body
 ; CHECK-NEXT:    @ Parent Loop BB1_4 Depth=1
@@ -90,7 +90,7 @@ define void @nested(ptr nocapture readonly %x, ptr nocapture readnone %y, ptr no
 ; CHECK-NEXT:    letp lr, .LBB1_6
 ; CHECK-NEXT:  @ %bb.7: @ %if.end.loopexit
 ; CHECK-NEXT:    @ in Loop: Header=BB1_4 Depth=1
-; CHECK-NEXT:    sub.w r12, r12, r5
+; CHECK-NEXT:    sub.w r12, r12, r7
 ; CHECK-NEXT:    mov r0, r8
 ; CHECK-NEXT:    b .LBB1_3
 ; CHECK-NEXT:  .LBB1_8:
diff --git a/llvm/test/Transforms/IndVarSimplify/AArch64/loop-guards.ll b/llvm/test/Transforms/IndVarSimplify/AArch64/loop-guards.ll
index 409622c255ea0..541875e6b0527 100644
--- a/llvm/test/Transforms/IndVarSimplify/AArch64/loop-guards.ll
+++ b/llvm/test/Transforms/IndVarSimplify/AArch64/loop-guards.ll
@@ -10,8 +10,7 @@ define i32 @guards_applied_to_add_rec(ptr %dst) {
 ; CHECK-NEXT:  [[ENTRY:.*]]:
 ; CHECK-NEXT:    br label %[[OUTER_HEADER:.*]]
 ; CHECK:       [[OUTER_HEADER]]:
-; CHECK-NEXT:    [[OUTER_IV_0:%.*]] = phi i32 [ 2, %[[ENTRY]] ], [ [[OUTER_IV_0_NEXT:%.*]], %[[OUTER_LATCH:.*]] ]
-; CHECK-NEXT:    [[OUTER_IV_1:%.*]] = phi i32 [ 1, %[[ENTRY]] ], [ [[OUTER_IV_0]], %[[OUTER_LATCH]] ]
+; CHECK-NEXT:    [[OUTER_IV_1:%.*]] = phi i32 [ [[INDVARS_IV_NEXT2:%.*]], %[[OUTER_LATCH:.*]] ], [ 1, %[[ENTRY]] ]
 ; CHECK-NEXT:    [[SHR28:%.*]] = lshr i32 [[OUTER_IV_1]], 1
 ; CHECK-NEXT:    [[PRE:%.*]] = icmp samesign ult i32 [[OUTER_IV_1]], 2
 ; CHECK-NEXT:    br i1 [[PRE]], label %[[OUTER_LATCH]], label %[[INNER_PREHEADER:.*]]
@@ -24,13 +23,13 @@ define i32 @guards_applied_to_add_rec(ptr %dst) {
 ; CHECK-NEXT:    [[TMP1:%.*]] = trunc nuw nsw i64 [[INDVARS_IV]] to i32
 ; CHECK-NEXT:    store i32 [[TMP1]], ptr [[GEP_DST]], align 4
 ; CHECK-NEXT:    [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
-; CHECK-NEXT:    [[CMP29:%.*]] = icmp samesign ult i64 [[INDVARS_IV_NEXT]], [[TMP0]]
+; CHECK-NEXT:    [[CMP29:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[TMP0]]
 ; CHECK-NEXT:    br i1 [[CMP29]], label %[[INNER]], label %[[OUTER_LATCH_LOOPEXIT:.*]]
 ; CHECK:       [[OUTER_LATCH_LOOPEXIT]]:
 ; CHECK-NEXT:    br label %[[OUTER_LATCH]]
 ; CHECK:       [[OUTER_LATCH]]:
-; CHECK-NEXT:    [[OUTER_IV_0_NEXT]] = add nuw i32 [[OUTER_IV_0]], 1
-; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[OUTER_IV_0_NEXT]], -2147483647
+; CHECK-NEXT:    [[INDVARS_IV_NEXT2]] = add nuw i32 [[OUTER_IV_1]], 1
+; CHECK-NEXT:    [[EXITCOND:%.*]] = icmp ne i32 [[INDVARS_IV_NEXT2]], -2147483648
 ; CHECK-NEXT:    br i1 [[EXITCOND]], label %[[OUTER_HEADER]], label %[[EXIT:.*]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    ret i32 0
diff --git a/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll b/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
index d03a4d9edfe82..d1c008f3fd3ca 100644
--- a/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
+++ b/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
@@ -11,10 +11,9 @@ define i1 @quotient_eq_zero(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -46,10 +45,9 @@ define i1 @quotient_ule_zero(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -81,10 +79,9 @@ define i1 @quotient_ult_one(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -116,10 +113,9 @@ define i1 @quotient_ne_zero(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -151,10 +147,9 @@ define i1 @quotient_ugt_zero(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -186,10 +181,9 @@ define i1 @quotient_uge_one(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
@@ -221,10 +215,9 @@ define i1 @constant_on_left(i64 %a, i64 %b) {
 ; CHECK:       [[LOOP_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i64 [[A]], [[B]]
 ; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
 ; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ [[CMP]], %[[LOOP]] ]
+; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
 ; CHECK-NEXT:    br label %[[EXIT]]
 ; CHECK:       [[EXIT]]:
 ; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
diff --git a/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll b/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
index 01beac717a256..e1cbb302d724e 100644
--- a/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
+++ b/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
@@ -21,15 +21,53 @@ define void @openmp_loop(
 ; CHECK-NEXT:    [[SKIP_INNER:%.*]] = icmp ult i64 [[NUMERATOR]], [[DENOMINATOR]]
 ; CHECK-NEXT:    br i1 [[SKIP_INNER]], label %[[OUTER_LATCH]], label %[[INNER_EPIL_PREHEADER:.*]]
 ; CHECK:       [[INNER_EPIL_PREHEADER]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = add i64 [[QUOTIENT]], -1
+; CHECK-NEXT:    [[XTRAITER:%.*]] = and i64 [[QUOTIENT]], 7
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 7
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[INNER_EPIL_PREHEADER1:.*]], label %[[INNER_PREHEADER_NEW:.*]]
+; CHECK:       [[INNER_PREHEADER_NEW]]:
+; CHECK-NEXT:    [[UNROLL_ITER:%.*]] = sub i64 [[QUOTIENT]], [[XTRAITER]]
 ; CHECK-NEXT:    br label %[[INNER_EPIL:.*]]
 ; CHECK:       [[INNER_EPIL]]:
-; CHECK-NEXT:    [[COUNT_EPIL:%.*]] = phi i64 [ [[NEXT_EPIL:%.*]], %[[INNER_EPIL]] ], [ [[QUOTIENT]], %[[INNER_EPIL_PREHEADER]] ]
-; CHECK-NEXT:    [[OFFSET_EPIL:%.*]] = phi i64 [ [[NEXT_OFFSET_EPIL:%.*]], %[[INNER_EPIL]] ], [ 0, %[[INNER_EPIL_PREHEADER]] ]
+; CHECK-NEXT:    [[COUNT:%.*]] = phi i64 [ [[QUOTIENT]], %[[INNER_PREHEADER_NEW]] ], [ [[NEXT_7:%.*]], %[[INNER_EPIL]] ]
+; CHECK-NEXT:    [[OFFSET:%.*]] = phi i64 [ 0, %[[INNER_PREHEADER_NEW]] ], [ [[NEXT_OFFSET_7:%.*]], %[[INNER_EPIL]] ]
+; CHECK-NEXT:    [[NITER:%.*]] = phi i64 [ 0, %[[INNER_PREHEADER_NEW]] ], [ [[NITER_NEXT_7:%.*]], %[[INNER_EPIL]] ]
+; CHECK-NEXT:    [[NEXT_OFFSET:%.*]] = add i64 [[OFFSET]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_1:%.*]] = add i64 [[NEXT_OFFSET]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_2:%.*]] = add i64 [[NEXT_OFFSET_1]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_3:%.*]] = add i64 [[NEXT_OFFSET_2]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_4:%.*]] = add i64 [[NEXT_OFFSET_3]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_5:%.*]] = add i64 [[NEXT_OFFSET_4]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_OFFSET_6:%.*]] = add i64 [[NEXT_OFFSET_5]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NEXT_7]] = add i64 [[COUNT]], -8
+; CHECK-NEXT:    [[NEXT_OFFSET_7]] = add i64 [[NEXT_OFFSET_6]], [[DENOMINATOR]]
+; CHECK-NEXT:    [[NITER_NEXT_7]] = add i64 [[NITER]], 8
+; CHECK-NEXT:    [[NITER_NCMP_7:%.*]] = icmp ne i64 [[NITER_NEXT_7]], [[UNROLL_ITER]]
+; CHECK-NEXT:    br i1 [[NITER_NCMP_7]], label %[[INNER_EPIL]], label %[[OUTER_LATCH_LOOPEXIT_UNR_LCSSA:.*]]
+; CHECK:       [[OUTER_LATCH_LOOPEXIT_UNR_LCSSA]]:
+; CHECK-NEXT:    [[COUNT_UNR:%.*]] = phi i64 [ [[NEXT_7]], %[[INNER_EPIL]] ]
+; CHECK-NEXT:    [[OFFSET_UNR:%.*]] = phi i64 [ [[NEXT_OFFSET_7]], %[[INNER_EPIL]] ]
+; CHECK-NEXT:    [[LCMP_MOD:%.*]] = icmp ne i64 [[XTRAITER]], 0
+; CHECK-NEXT:    br i1 [[LCMP_MOD]], label %[[INNER_EPIL_PREHEADER1]], label %[[OUTER_LATCH_LOOPEXIT1:.*]]
+; CHECK:       [[INNER_EPIL_PREHEADER1]]:
+; CHECK-NEXT:    [[COUNT_EPIL_INIT:%.*]] = phi i64 [ [[QUOTIENT]], %[[INNER_EPIL_PREHEADER]] ], [ [[COUNT_UNR]], %[[OUTER_LATCH_LOOPEXIT_UNR_LCSSA]] ]
+; CHECK-NEXT:    [[OFFSET_EPIL_INIT:%.*]] = phi i64 [ 0, %[[INNER_EPIL_PREHEADER]] ], [ [[OFFSET_UNR]], %[[OUTER_LATCH_LOOPEXIT_UNR_LCSSA]] ]
+; CHECK-NEXT:    [[LCMP_MOD1:%.*]] = icmp ne i64 [[XTRAITER]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[LCMP_MOD1]])
+; CHECK-NEXT:    br label %[[INNER_EPIL1:.*]]
+; CHECK:       [[INNER_EPIL1]]:
+; CHECK-NEXT:    [[COUNT_EPIL:%.*]] = phi i64 [ [[NEXT_EPIL:%.*]], %[[INNER_EPIL1]] ], [ [[COUNT_EPIL_INIT]], %[[INNER_EPIL_PREHEADER1]] ]
+; CHECK-NEXT:    [[OFFSET_EPIL:%.*]] = phi i64 [ [[NEXT_OFFSET_EPIL:%.*]], %[[INNER_EPIL1]] ], [ [[OFFSET_EPIL_INIT]], %[[INNER_EPIL_PREHEADER1]] ]
+; CHECK-NEXT:    [[EPIL_ITER:%.*]] = phi i64 [ 0, %[[INNER_EPIL_PREHEADER1]] ], [ [[EPIL_ITER_NEXT:%.*]], %[[INNER_EPIL1]] ]
 ; CHECK-NEXT:    [[NEXT_EPIL]] = add i64 [[COUNT_EPIL]], -1
 ; CHECK-NEXT:    [[NEXT_OFFSET_EPIL]] = add i64 [[OFFSET_EPIL]], [[DENOMINATOR]]
 ; CHECK-NEXT:    [[CONTINUE_EPIL:%.*]] = icmp ugt i64 [[COUNT_EPIL]], 1
-; CHECK-NEXT:    br i1 [[CONTINUE_EPIL]], label %[[INNER_EPIL]], label %[[OUTER_LATCH_LOOPEXIT:.*]]
+; CHECK-NEXT:    [[EPIL_ITER_NEXT]] = add i64 [[EPIL_ITER]], 1
+; CHECK-NEXT:    [[EPIL_ITER_CMP:%.*]] = icmp ne i64 [[EPIL_ITER_NEXT]], [[XTRAITER]]
+; CHECK-NEXT:    br i1 [[EPIL_ITER_CMP]], label %[[INNER_EPIL1]], label %[[OUTER_LATCH_LOOPEXIT:.*]], !llvm.loop [[LOOP0:![0-9]+]]
 ; CHECK:       [[OUTER_LATCH_LOOPEXIT]]:
+; CHECK-NEXT:    br label %[[OUTER_LATCH_LOOPEXIT1]]
+; CHECK:       [[OUTER_LATCH_LOOPEXIT1]]:
 ; CHECK-NEXT:    br label %[[OUTER_LATCH]]
 ; CHECK:       [[OUTER_LATCH]]:
 ; CHECK-NEXT:    call void @barrier()
@@ -86,20 +124,45 @@ define void @udiv_entry_guard_reduced(ptr %denominator_ptr) {
 ; CHECK-NEXT:    br label %[[OUTER_LOOPEXIT:.*]]
 ; CHECK:       [[OUTER_LOOPEXIT1:.*]]:
 ; CHECK-NEXT:    br label %[[OUTER_BACKEDGE:.*]]
+; CHECK:       [[OUTER_BACKEDGE]]:
+; CHECK-NEXT:    br label %[[OUTER_BACKEDGE1:.*]]
 ; CHECK:       [[OUTER_LOOPEXIT]]:
 ; CHECK-NEXT:    [[DENOMINATOR:%.*]] = load i64, ptr [[DENOMINATOR_PTR]], align 4
 ; CHECK-NEXT:    [[QUOTIENT:%.*]] = udiv i64 35, [[DENOMINATOR]]
 ; CHECK-NEXT:    [[SKIP_INNER:%.*]] = icmp ult i64 35, [[DENOMINATOR]]
-; CHECK-NEXT:    br i1 [[SKIP_INNER]], label %[[OUTER_BACKEDGE]], label %[[INNER_PREHEADER:.*]]
-; CHECK:       [[OUTER_BACKEDGE]]:
+; CHECK-NEXT:    br i1 [[SKIP_INNER]], label %[[OUTER_BACKEDGE1]], label %[[INNER_PREHEADER:.*]]
+; CHECK:       [[OUTER_BACKEDGE1]]:
 ; CHECK-NEXT:    br label %[[OUTER_LOOPEXIT]]
 ; CHECK:       [[INNER_PREHEADER]]:
+; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i64 [[QUOTIENT]], -1
+; CHECK-NEXT:    [[XTRAITER:%.*]] = and i64 [[QUOTIENT]], 7
+; CHECK-NEXT:    [[LCMP_MOD:%.*]] = icmp ne i64 [[XTRAITER]], 0
+; CHECK-NEXT:    br i1 [[LCMP_MOD]], label %[[INNER_PROL_PREHEADER:.*]], label %[[INNER_PROL_LOOPEXIT:.*]]
+; CHECK:       [[INNER_PROL_PREHEADER]]:
 ; CHECK-NEXT:    br label %[[INNER_PROL:.*]]
 ; CHECK:       [[INNER_PROL]]:
-; CHECK-NEXT:    [[COUNT_PROL:%.*]] = phi i64 [ [[NEXT_PROL:%.*]], %[[INNER_PROL]] ], [ [[QUOTIENT]], %[[INNER_PREHEADER]] ]
+; CHECK-NEXT:    [[COUNT_PROL:%.*]] = phi i64 [ [[NEXT_PROL:%.*]], %[[INNER_PROL]] ], [ [[QUOTIENT]], %[[INNER_PROL_PREHEADER]] ]
+; CHECK-NEXT:    [[PROL_ITER:%.*]] = phi i64 [ 0, %[[INNER_PROL_PREHEADER]] ], [ [[PROL_ITER_NEXT:%.*]], %[[INNER_PROL]] ]
 ; CHECK-NEXT:    [[NEXT_PROL]] = add i64 [[COUNT_PROL]], -1
 ; CHECK-NEXT:    [[CONTINUE_PROL:%.*]] = icmp ugt i64 [[COUNT_PROL]], 1
-; CHECK-NEXT:    br i1 [[CONTINUE_PROL]], label %[[INNER_PROL]], label %[[OUTER_LOOPEXIT1]]
+; CHECK-NEXT:    [[PROL_ITER_NEXT]] = add i64 [[PROL_ITER]], 1
+; CHECK-NEXT:    [[PROL_ITER_CMP:%.*]] = icmp ne i64 [[PROL_ITER_NEXT]], [[XTRAITER]]
+; CHECK-NEXT:    br i1 [[PROL_ITER_CMP]], label %[[INNER_PROL]], label %[[INNER_PROL_LOOPEXIT_UNR_LCSSA:.*]], !llvm.loop [[LOOP2:![0-9]+]]
+; CHECK:       [[INNER_PROL_LOOPEXIT_UNR_LCSSA]]:
+; CHECK-NEXT:    [[COUNT_UNR_PH:%.*]] = phi i64 [ [[NEXT_PROL]], %[[INNER_PROL]] ]
+; CHECK-NEXT:    br label %[[INNER_PROL_LOOPEXIT]]
+; CHECK:       [[INNER_PROL_LOOPEXIT]]:
+; CHECK-NEXT:    [[COUNT_UNR:%.*]] = phi i64 [ [[QUOTIENT]], %[[INNER_PREHEADER]] ], [ [[COUNT_UNR_PH]], %[[INNER_PROL_LOOPEXIT_UNR_LCSSA]] ]
+; CHECK-NEXT:    [[TMP1:%.*]] = icmp ult i64 [[TMP0]], 7
+; CHECK-NEXT:    br i1 [[TMP1]], label %[[OUTER_BACKEDGE]], label %[[INNER_PREHEADER_NEW:.*]]
+; CHECK:       [[INNER_PREHEADER_NEW]]:
+; CHECK-NEXT:    br label %[[INNER:.*]]
+; CHECK:       [[INNER]]:
+; CHECK-NEXT:    [[COUNT:%.*]] = phi i64 [ [[COUNT_UNR]], %[[INNER_PREHEADER_NEW]] ], [ [[NEXT_7:%.*]], %[[INNER]] ]
+; CHECK-NEXT:    [[NEXT_6:%.*]] = add i64 [[COUNT]], -7
+; CHECK-NEXT:    [[NEXT_7]] = add i64 [[COUNT]], -8
+; CHECK-NEXT:    [[CONTINUE_7:%.*]] = icmp ugt i64 [[NEXT_6]], 1
+; CHECK-NEXT:    br i1 [[CONTINUE_7]], label %[[INNER]], label %[[OUTER_LOOPEXIT1]]
 ;
 entry:
   br label %outer
@@ -116,3 +179,8 @@ inner:                                            ; preds = %inner, %outer
   %continue = icmp ugt i64 %count, 1
   br i1 %continue, label %inner, label %outer
 }
+;.
+; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
+; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[META1]]}
+;.

>From 345df03e430fec4a5420350832fd327d7f1c694f Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Tue, 25 Aug 2026 22:02:52 +0000
Subject: [PATCH 3/5] Switch to unittest

---
 .../Transforms/IndVarSimplify/udiv-icmp.ll    | 240 ------------------
 .../Analysis/ScalarEvolutionTest.cpp          |  22 ++
 2 files changed, 22 insertions(+), 240 deletions(-)
 delete mode 100644 llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll

diff --git a/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll b/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
deleted file mode 100644
index d1c008f3fd3ca..0000000000000
--- a/llvm/test/Transforms/IndVarSimplify/udiv-icmp.ll
+++ /dev/null
@@ -1,240 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt < %s -S -passes='loop(indvars)' | FileCheck %s
-
-define i1 @quotient_eq_zero(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_eq_zero(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp eq i64 [[Q]], 0
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp eq i64 %q, 0
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp ult i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @quotient_ule_zero(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_ule_zero(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ule i64 [[Q]], 0
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp ule i64 %q, 0
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp ult i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @quotient_ult_one(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_ult_one(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ult i64 [[Q]], 1
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp ult i64 %q, 1
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp ult i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @quotient_ne_zero(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_ne_zero(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ne i64 [[Q]], 0
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp ne i64 %q, 0
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp uge i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @quotient_ugt_zero(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_ugt_zero(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ugt i64 [[Q]], 0
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp ugt i64 %q, 0
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp uge i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @quotient_uge_one(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @quotient_uge_one(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp uge i64 [[Q]], 1
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp uge i64 %q, 1
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp uge i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
-
-define i1 @constant_on_left(i64 %a, i64 %b) {
-; CHECK-LABEL: define i1 @constant_on_left(
-; CHECK-SAME: i64 [[A:%.*]], i64 [[B:%.*]]) {
-; CHECK-NEXT:  [[ENTRY:.*]]:
-; CHECK-NEXT:    [[Q:%.*]] = udiv i64 [[A]], [[B]]
-; CHECK-NEXT:    [[GUARD:%.*]] = icmp ult i64 0, [[Q]]
-; CHECK-NEXT:    br i1 [[GUARD]], label %[[LOOP_PREHEADER:.*]], label %[[EXIT:.*]]
-; CHECK:       [[LOOP_PREHEADER]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    br i1 false, label %[[LOOP]], label %[[EXIT_LOOPEXIT:.*]]
-; CHECK:       [[EXIT_LOOPEXIT]]:
-; CHECK-NEXT:    [[CMP_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
-; CHECK-NEXT:    br label %[[EXIT]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[RESULT:%.*]] = phi i1 [ false, %[[ENTRY]] ], [ [[CMP_LCSSA]], %[[EXIT_LOOPEXIT]] ]
-; CHECK-NEXT:    ret i1 [[RESULT]]
-;
-entry:
-  %q = udiv i64 %a, %b
-  %guard = icmp ult i64 0, %q
-  br i1 %guard, label %loop, label %exit
-
-loop:
-  %same = phi i64 [ %a, %entry ], [ %same.next, %loop ]
-  %cmp = icmp uge i64 %same, %b
-  %same.next = add i64 %same, 1
-  br i1 false, label %loop, label %exit
-
-exit:
-  %result = phi i1 [ false, %entry ], [ %cmp, %loop ]
-  ret i1 %result
-}
diff --git a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
index 2cbac61bac13f..41d792bd171b3 100644
--- a/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
+++ b/llvm/unittests/Analysis/ScalarEvolutionTest.cpp
@@ -2124,6 +2124,28 @@ TEST_F(ScalarEvolutionsTest, SimplifyICmpOperands) {
       EXPECT_EQ(NewRHS, B);
     }
   });
+
+  runWithSE(*M, "foo", [](Function &F, LoopInfo &LI, ScalarEvolution &SE) {
+    const SCEV *Numerator = SE.getSCEV(getArgByName(F, "a"));
+    const SCEV *Denominator = SE.getSCEV(getArgByName(F, "b"));
+    const SCEV *Quotient = SE.getUDivExpr(Numerator, Denominator);
+    const SCEV *Zero = SE.getZero(Numerator->getType());
+
+    auto CheckSimplification = [&](CmpPredicate Pred,
+                                   ICmpInst::Predicate ExpectedPred) {
+      SCEVUse LHS = Quotient;
+      SCEVUse RHS = Zero;
+      EXPECT_TRUE(SE.SimplifyICmpOperands(Pred, LHS, RHS));
+      EXPECT_EQ(Pred, ExpectedPred);
+      EXPECT_EQ(LHS, Numerator);
+      EXPECT_EQ(RHS, Denominator);
+    };
+
+    // a / b == 0  =>  a < b
+    CheckSimplification(ICmpInst::ICMP_EQ, ICmpInst::ICMP_ULT);
+    // a / b != 0  =>  a >= b
+    CheckSimplification(ICmpInst::ICMP_NE, ICmpInst::ICMP_UGE);
+  });
 }
 
 // An operand of a SCEV expression is a SCEVUse and may carry use-specific

>From 513edad9cb9fde4145f09ba288f85491887a4dcc Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Thu, 27 Aug 2026 18:09:14 +0000
Subject: [PATCH 4/5] Address review comments

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

diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index a994c4d579966..e2c2bcd5f583e 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11113,13 +11113,13 @@ bool ScalarEvolution::SimplifyICmpOperands(CmpPredicate &Pred, SCEVUse &LHS,
     }
   }
 
-  // For a defined unsigned division, the quotient is zero exactly when the
-  // numerator is less than the denominator. The constant-range canonicalization
-  // above reduces equivalent zero/nonzero comparisons to EQ/NE against zero.
-  if (const auto *Div = dyn_cast<SCEVUDivExpr>(LHS);
-      Div && RHS->isZero() && ICmpInst::isEquality(Pred)) {
-    LHS = Div->getLHS();
-    RHS = Div->getRHS();
+  // a /u b == 0 => a < b
+  // a /u b != 0 => a >= b
+  SCEVUse UDivLHS, UDivRHS;
+  if (ICmpInst::isEquality(Pred) && RHS->isZero() &&
+      match(LHS, m_scev_UDiv(m_SCEV(UDivLHS), m_SCEV(UDivRHS)))) {
+    LHS = UDivLHS;
+    RHS = UDivRHS;
     Pred = Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
     Changed = true;
   }

>From 4a7baf02f52f6bd8681547eca499010d0577bbf5 Mon Sep 17 00:00:00 2001
From: Justin Fargnoli <jfargnoli at nvidia.com>
Date: Thu, 27 Aug 2026 13:29:24 -0700
Subject: [PATCH 5/5] Address review comments pt 2

Co-authored-by: Florian Hahn <flo at fhahn.com>
---
 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 e2c2bcd5f583e..2c5758194a12e 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11115,11 +11115,8 @@ bool ScalarEvolution::SimplifyICmpOperands(CmpPredicate &Pred, SCEVUse &LHS,
 
   // a /u b == 0 => a < b
   // a /u b != 0 => a >= b
-  SCEVUse UDivLHS, UDivRHS;
   if (ICmpInst::isEquality(Pred) && RHS->isZero() &&
-      match(LHS, m_scev_UDiv(m_SCEV(UDivLHS), m_SCEV(UDivRHS)))) {
-    LHS = UDivLHS;
-    RHS = UDivRHS;
+      match(LHS, m_scev_UDiv(m_SCEV(LHS), m_SCEV(RHS)))) {
     Pred = Pred == ICmpInst::ICMP_EQ ? ICmpInst::ICMP_ULT : ICmpInst::ICMP_UGE;
     Changed = true;
   }



More information about the llvm-commits mailing list