[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