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

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


Author: Justin Fargnoli
Date: 2026-08-28T16:45:56Z
New Revision: fa3c2e9f49471a15ff9314503f9922e287a63908

URL: https://github.com/llvm/llvm-project/commit/fa3c2e9f49471a15ff9314503f9922e287a63908
DIFF: https://github.com/llvm/llvm-project/commit/fa3c2e9f49471a15ff9314503f9922e287a63908.diff

LOG: [SCEV] Simplify `udiv` equality comparisons against 0 (#218084)

OpenMP sometimes generates loops with the following structure
(significantly oversimplified):
```
unsigned A = ...
unsigned B = ...
unsigned RuntimeTripCount = A / B
if (A >= B) {
  do {
    <body>
  } while (RuntimeTripCount-- > 1)
}
```

However, the runtime unroller will not unroll the do-while loop. SCEV
cannot prove that `RuntimeTripCount` is nonzero at the beginning of the
loop and thus creates a conservative runtime trip count - `1 +
RuntimeTripCount - umin(1, RuntimeTripCount)` - that [exceeds the
default
SCEVExpansionBudget](https://github.com/llvm/llvm-project/blob/80dfaec659031e988cf2859b4787ade601afaef2/llvm/lib/Transforms/Utils/LoopUnrollRuntime.cpp#L775-L780).

This patch teaches SCEV that if `A >= B`, then `RuntimeTripCount` cannot
be 0. Thus SCEV can simplify its runtime trip count expression to
`RuntimeTripCount`, which is below the `SCEVExpansionBudget`.

Alive 2 proofs: [eq](https://alive2.llvm.org/ce/z/V7hN-b)
[ne](https://alive2.llvm.org/ce/z/EsJRAb).

---------

Co-authored-by: Florian Hahn <flo at fhahn.com>

Added: 
    llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll

Modified: 
    llvm/lib/Analysis/ScalarEvolution.cpp
    llvm/test/CodeGen/Thumb2/LowOverheadLoops/while-loops.ll
    llvm/test/Transforms/IndVarSimplify/AArch64/loop-guards.ll
    llvm/unittests/Analysis/ScalarEvolutionTest.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index 7ad84855d6cb6..b9d8fef022a26 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11103,6 +11103,14 @@ bool ScalarEvolution::SimplifyICmpOperands(CmpPredicate &Pred, SCEVUse &LHS,
     }
   }
 
+  // a /u b == 0 => a < b
+  // a /u b != 0 => a >= b
+  if (ICmpInst::isEquality(Pred) && RHS->isZero() &&
+      match(LHS, m_scev_UDiv(m_SCEV(LHS), m_SCEV(RHS)))) {
+    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/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..e1cbb302d724e
--- /dev/null
+++ b/llvm/test/Transforms/LoopUnroll/runtime-unroll-scev-udiv-entry-guard.ll
@@ -0,0 +1,186 @@
+; 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:    [[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:%.*]] = 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:    [[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()
+; 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:
+  %
diff erence = sub nsw i64 %denominator, %lower_bound
+  %numerator = add nsw i64 %
diff erence, 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_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_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_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:    [[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
+
+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
+}
+;.
+; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]]}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
+; CHECK: [[LOOP2]] = distinct !{[[LOOP2]], [[META1]]}
+;.

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


        


More information about the llvm-commits mailing list