[llvm-branch-commits] [llvm] [SCEV] Prove monotonic predicates from loop entry in isKnownViaInduction (PR #231697)
via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Wed Oct 14 05:14:42 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-analysis
Author: Dmitry Makogon (d-makogon)
<details>
<summary>Changes</summary>
Prove `IV pred Inv` on every iteration of a loop when `Inv` is
loop-invariant, the predicate holds on loop entry, and it can only change
from false to true as the loop iterates.
Before, the predicate also had to be proven on the backedge, which
usually fails when the loop exit only bounds the IV from above.
E.g. `i >=s 0` for an nsw IV starting at `s` was not proven even when `s >=s 0` was
known on entry. As a result, in a loop guarded by `start <u len`,
IndVars could not compute `a[i - 32]` in i64.
Together with https://github.com/llvm/llvm-project/pull/231631 this makes it possible to prove that `i >=s 0` on every iteration of:
```java
guard(start <u a.length);
for (int i = start; i < 10000; i++) a[i] = a[i - 32] + 1;
```
The first patch derives `start >=s 0` from the guard.
The second uses that fact to prove `{%start,+,1}<nuw><nsw> >=s 0`.
---
Patch is 42.40 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/231697.diff
9 Files Affected:
- (modified) llvm/include/llvm/Analysis/ScalarEvolution.h (+24)
- (modified) llvm/lib/Analysis/ScalarEvolution.cpp (+63-33)
- (modified) llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll (+56-9)
- (modified) llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll (+70-2)
- (added) llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll (+78)
- (modified) llvm/test/Transforms/IndVarSimplify/trivial-guard.ll (+4-2)
- (added) llvm/test/Transforms/IndVarSimplify/widen-zext-nneg-add-nsw-runtime-start.ll (+233)
- (added) llvm/test/Transforms/PhaseOrdering/indvars-vectorize-runtime-start.ll (+109)
- (modified) llvm/unittests/Analysis/ScalarEvolutionTest.cpp (+85)
``````````diff
diff --git a/llvm/include/llvm/Analysis/ScalarEvolution.h b/llvm/include/llvm/Analysis/ScalarEvolution.h
index 17bfec22d65ca..c9ed3948b6fd3 100644
--- a/llvm/include/llvm/Analysis/ScalarEvolution.h
+++ b/llvm/include/llvm/Analysis/ScalarEvolution.h
@@ -2456,6 +2456,30 @@ class ScalarEvolution {
getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS,
ICmpInst::Predicate Pred);
+ /// Test if the condition described by Pred, LHS, RHS is known to be true on
+ /// every iteration of \p L.
+ bool isKnownOnEveryIteration(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L);
+
+ /// "AR Pred RHS", with AR a recurrence of a loop and RHS invariant in it,
+ /// and how the predicate changes as the loop iterates.
+ struct MonotonicPredicate {
+ CmpPredicate Pred;
+ const SCEVAddRecExpr *AR;
+ const SCEV *RHS;
+ MonotonicPredicateType Type;
+ /// Whether the operands were swapped to put the recurrence on the left.
+ bool Swapped;
+ };
+
+ /// If one of LHS and RHS is a recurrence of \p L and the other is invariant
+ /// in \p L, and "LHS Pred RHS" is monotonic in \p L, return it with the
+ /// recurrence on the left. Otherwise, return std::nullopt.
+ std::optional<MonotonicPredicate> getMonotonicPredicate(CmpPredicate Pred,
+ const SCEV *LHS,
+ const SCEV *RHS,
+ const Loop *L);
+
/// Return SCEV no-wrap flags that can be proven based on reasoning about
/// how poison produced from no-wrap flags on this value (e.g. a nuw add)
/// would trigger undefined behavior on overflow.
diff --git a/llvm/lib/Analysis/ScalarEvolution.cpp b/llvm/lib/Analysis/ScalarEvolution.cpp
index fef9529ee0f9e..76cbb3217d48b 100644
--- a/llvm/lib/Analysis/ScalarEvolution.cpp
+++ b/llvm/lib/Analysis/ScalarEvolution.cpp
@@ -11387,30 +11387,7 @@ bool ScalarEvolution::isKnownViaInduction(CmpPredicate Pred, SCEVUse LHS,
return DT.properlyDominates(L1->getHeader(), L2->getHeader());
});
- // Get init and post increment value for LHS.
- auto SplitLHS = SplitIntoInitAndPostInc(MDL, LHS);
- // if LHS contains unknown non-invariant SCEV then bail out.
- if (SplitLHS.first == getCouldNotCompute())
- return false;
- assert (SplitLHS.second != getCouldNotCompute() && "Unexpected CNC");
- // Get init and post increment value for RHS.
- auto SplitRHS = SplitIntoInitAndPostInc(MDL, RHS);
- // if RHS contains unknown non-invariant SCEV then bail out.
- if (SplitRHS.first == getCouldNotCompute())
- return false;
- assert (SplitRHS.second != getCouldNotCompute() && "Unexpected CNC");
- // It is possible that init SCEV contains an invariant load but it does
- // not dominate MDL and is not available at MDL loop entry, so we should
- // check it here.
- if (!isAvailableAtLoopEntry(SplitLHS.first, MDL) ||
- !isAvailableAtLoopEntry(SplitRHS.first, MDL))
- return false;
-
- // It seems backedge guard check is faster than entry one so in some cases
- // it can speed up whole estimation by short circuit
- return isLoopBackedgeGuardedByCond(MDL, Pred, SplitLHS.second,
- SplitRHS.second) &&
- isLoopEntryGuardedByCond(MDL, Pred, SplitLHS.first, SplitRHS.first);
+ return isKnownOnEveryIteration(Pred, LHS, RHS, MDL);
}
bool ScalarEvolution::isKnownPredicate(CmpPredicate Pred, SCEVUse LHS,
@@ -11464,6 +11441,44 @@ bool ScalarEvolution::isKnownOnEveryIteration(CmpPredicate Pred,
isLoopBackedgeGuardedByCond(L, Pred, LHS->getPostIncExpr(*this), RHS);
}
+bool ScalarEvolution::isKnownOnEveryIteration(CmpPredicate Pred,
+ const SCEV *LHS, const SCEV *RHS,
+ const Loop *L) {
+ // Get init and post increment value for LHS.
+ auto SplitLHS = SplitIntoInitAndPostInc(L, LHS);
+ // if LHS contains unknown non-invariant SCEV then bail out.
+ if (SplitLHS.first == getCouldNotCompute())
+ return false;
+ assert(SplitLHS.second != getCouldNotCompute() && "Unexpected CNC");
+ // Get init and post increment value for RHS.
+ auto SplitRHS = SplitIntoInitAndPostInc(L, RHS);
+ // if RHS contains unknown non-invariant SCEV then bail out.
+ if (SplitRHS.first == getCouldNotCompute())
+ return false;
+ assert(SplitRHS.second != getCouldNotCompute() && "Unexpected CNC");
+ // It is possible that init SCEV contains an invariant load but it does
+ // not dominate L and is not available at L loop entry, so we should
+ // check it here.
+ if (!isAvailableAtLoopEntry(SplitLHS.first, L) ||
+ !isAvailableAtLoopEntry(SplitRHS.first, L))
+ return false;
+
+ // It seems backedge guard check is faster than entry one so in some cases
+ // it can speed up whole estimation by short circuit
+ if (isLoopBackedgeGuardedByCond(L, Pred, SplitLHS.second, SplitRHS.second))
+ return isLoopEntryGuardedByCond(L, Pred, SplitLHS.first, SplitRHS.first);
+
+ // If one side is a recurrence of L and the other side is invariant in L,
+ // and the predicate can only change from false to true as the loop
+ // iterates, it holds on every iteration once it holds on entry.
+ auto MP = getMonotonicPredicate(Pred.dropSameSign(), LHS, RHS, L);
+ if (!MP || MP->Type != MonotonicallyIncreasing)
+ return false;
+ if (MP->Swapped)
+ std::swap(SplitLHS, SplitRHS);
+ return isLoopEntryGuardedByCond(L, MP->Pred, SplitLHS.first, SplitRHS.first);
+}
+
std::optional<ScalarEvolution::MonotonicPredicateType>
ScalarEvolution::getMonotonicPredicateType(const SCEVAddRecExpr *LHS,
ICmpInst::Predicate Pred) {
@@ -11527,26 +11542,41 @@ ScalarEvolution::getMonotonicPredicateTypeImpl(const SCEVAddRecExpr *LHS,
return std::nullopt;
}
-std::optional<ScalarEvolution::LoopInvariantPredicate>
-ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
- const SCEV *RHS, const Loop *L,
- const Instruction *CtxI) {
+std::optional<ScalarEvolution::MonotonicPredicate>
+ScalarEvolution::getMonotonicPredicate(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L) {
// If there is a loop-invariant, force it into the RHS, otherwise bail out.
+ bool Swapped = false;
if (!isLoopInvariant(RHS, L)) {
if (!isLoopInvariant(LHS, L))
return std::nullopt;
std::swap(LHS, RHS);
Pred = ICmpInst::getSwappedCmpPredicate(Pred);
+ Swapped = true;
}
- const SCEVAddRecExpr *ArLHS = dyn_cast<SCEVAddRecExpr>(LHS);
- if (!ArLHS || ArLHS->getLoop() != L)
+ const auto *AR = dyn_cast<SCEVAddRecExpr>(LHS);
+ if (!AR || AR->getLoop() != L)
return std::nullopt;
- auto MonotonicType = getMonotonicPredicateType(ArLHS, Pred);
- if (!MonotonicType)
+ auto Type = getMonotonicPredicateType(AR, Pred);
+ if (!Type)
+ return std::nullopt;
+ return MonotonicPredicate{Pred, AR, RHS, *Type, Swapped};
+}
+
+std::optional<ScalarEvolution::LoopInvariantPredicate>
+ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
+ const SCEV *RHS, const Loop *L,
+ const Instruction *CtxI) {
+ auto MP = getMonotonicPredicate(Pred, LHS, RHS, L);
+ if (!MP)
return std::nullopt;
+ Pred = MP->Pred;
+ const SCEVAddRecExpr *ArLHS = MP->AR;
+ LHS = ArLHS;
+ RHS = MP->RHS;
// If the predicate "ArLHS `Pred` RHS" monotonically increases from false to
// true as the loop iterates, and the backedge is control dependent on
// "ArLHS `Pred` RHS" == true then we can reason as follows:
@@ -11564,7 +11594,7 @@ ScalarEvolution::getLoopInvariantPredicate(CmpPredicate Pred, const SCEV *LHS,
//
// A similar reasoning applies for a monotonically decreasing predicate, by
// replacing true with false and false with true in the above two bullets.
- bool Increasing = *MonotonicType == ScalarEvolution::MonotonicallyIncreasing;
+ bool Increasing = MP->Type == ScalarEvolution::MonotonicallyIncreasing;
auto P = Increasing ? Pred : ICmpInst::getInverseCmpPredicate(Pred);
if (isLoopBackedgeGuardedByCond(L, P, LHS, RHS))
diff --git a/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll b/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
index 08f9856ac603d..853de678655ad 100644
--- a/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
+++ b/llvm/test/Transforms/IndVarSimplify/eliminate-comparison.ll
@@ -569,6 +569,8 @@ leave:
ret void
}
+; The entry condition implies %len is negative, so the loop exits on its first
+; iteration.
define void @func_17(ptr %len.ptr) {
; CHECK-LABEL: @func_17(
; CHECK-NEXT: entry:
@@ -579,18 +581,13 @@ define void @func_17(ptr %len.ptr) {
; CHECK-NEXT: [[ENTRY_COND:%.*]] = and i1 [[ENTRY_COND_0]], [[ENTRY_COND_1]]
; CHECK-NEXT: br i1 [[ENTRY_COND]], label [[LOOP_PREHEADER:%.*]], label [[LEAVE:%.*]]
; CHECK: loop.preheader:
-; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[LEN]], i32 0)
-; CHECK-NEXT: [[TMP0:%.*]] = add nsw i32 [[SMAX]], -5
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_INC:%.*]], [[BE:%.*]] ], [ -6, [[LOOP_PREHEADER]] ]
; CHECK-NEXT: call void @side_effect()
-; CHECK-NEXT: [[IV_INC]] = add nsw i32 [[IV]], 1
-; CHECK-NEXT: br i1 true, label [[BE]], label [[LEAVE_LOOPEXIT:%.*]]
+; CHECK-NEXT: br i1 true, label [[BE:%.*]], label [[LEAVE_LOOPEXIT:%.*]]
; CHECK: be:
; CHECK-NEXT: call void @side_effect()
-; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_INC]], [[TMP0]]
-; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LEAVE_LOOPEXIT]]
+; CHECK-NEXT: br i1 false, label [[LOOP]], label [[LEAVE_LOOPEXIT]]
; CHECK: leave.loopexit:
; CHECK-NEXT: br label [[LEAVE]]
; CHECK: leave:
@@ -611,9 +608,59 @@ loop:
%iv.inc = add i32 %iv, 1
%iv.2.inc = add i32 %iv.2, 1
%iv.cmp = icmp slt i32 %iv, %len.add.5
+ br i1 %iv.cmp, label %be, label %leave
+
+be:
+ call void @side_effect()
+ %be.cond = icmp slt i32 %iv.2, %len
+ br i1 %be.cond, label %loop, label %leave
+
+leave:
+ ret void
+}
+
+; %iv + 1 s< %len.add.5 follows from %iv.2 s< %len, since %len s< INT_MIN - (-5)
+; from the entry condition. Unlike @func_17, the loop can iterate.
+define void @func_17_iterating(ptr %len.ptr) {
+; CHECK-LABEL: @func_17_iterating(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[LEN:%.*]] = load i32, ptr [[LEN_PTR:%.*]], align 4
+; CHECK-NEXT: [[LEN_ADD_5:%.*]] = add i32 [[LEN]], -5
+; CHECK-NEXT: [[ENTRY_COND_0:%.*]] = icmp slt i32 [[LEN]], -2147483643
+; CHECK-NEXT: [[ENTRY_COND_1:%.*]] = icmp slt i32 2147483642, [[LEN_ADD_5]]
+; CHECK-NEXT: [[ENTRY_COND:%.*]] = and i1 [[ENTRY_COND_0]], [[ENTRY_COND_1]]
+; CHECK-NEXT: br i1 [[ENTRY_COND]], label [[LOOP_PREHEADER:%.*]], label [[LEAVE:%.*]]
+; CHECK: loop.preheader:
+; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: loop:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_INC:%.*]], [[BE:%.*]] ], [ 2147483642, [[LOOP_PREHEADER]] ]
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: [[IV_INC]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: br i1 true, label [[BE]], label [[LEAVE_LOOPEXIT:%.*]]
+; CHECK: be:
+; CHECK-NEXT: call void @side_effect()
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i32 [[IV_INC]], [[LEN_ADD_5]]
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[LOOP]], label [[LEAVE_LOOPEXIT]]
+; CHECK: leave.loopexit:
+; CHECK-NEXT: br label [[LEAVE]]
+; CHECK: leave:
+; CHECK-NEXT: ret void
+;
+entry:
+ %len = load i32, ptr %len.ptr
+ %len.add.5 = add i32 %len, -5
+ %entry.cond.0 = icmp slt i32 %len, 2147483653 ;; 2147483653 == INT_MIN - (-5)
+ %entry.cond.1 = icmp slt i32 2147483642, %len.add.5
+ %entry.cond = and i1 %entry.cond.0, %entry.cond.1
+ br i1 %entry.cond, label %loop, label %leave
-; Deduces {-5,+,1} s< (-5 + %len) from {0,+,1} < %len
-; since %len s< INT_MIN - (-5) from the entry condition
+loop:
+ %iv.2 = phi i32 [ -2147483648, %entry ], [ %iv.2.inc, %be ]
+ %iv = phi i32 [ 2147483642, %entry ], [ %iv.inc, %be ]
+ call void @side_effect()
+ %iv.inc = add i32 %iv, 1
+ %iv.2.inc = add i32 %iv.2, 1
+ %iv.cmp = icmp slt i32 %iv, %len.add.5
br i1 %iv.cmp, label %be, label %leave
be:
diff --git a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
index a83e9ce74b12a..4ce57753da673 100644
--- a/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
+++ b/llvm/test/Transforms/IndVarSimplify/iv-widen-elim-ext.ll
@@ -206,7 +206,7 @@ for.end: ; preds = %for.cond.for.end_cr
declare i32 @dummy(ptr, ptr)
-; A case where zext should not be eliminated when its operands could only be extended by sext.
+; The guard 0 <s %M makes %add3 non-negative, so its zext is eliminated.
define i32 @foo3(i32 %M) {
; CHECK-LABEL: @foo3(
; CHECK-NEXT: entry:
@@ -224,6 +224,74 @@ define i32 @foo3(i32 %M) {
; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]]
; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[INDVARS_IV]], [[TMP0]]
+; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[TMP3]]
+; CHECK-NEXT: store i32 [[ADD]], ptr [[ARRAYIDX5]], align 4
+; CHECK-NEXT: br label [[FOR_INC]]
+; CHECK: for.inc:
+; CHECK-NEXT: [[INDVARS_IV_NEXT]] = add nuw nsw i64 [[INDVARS_IV]], 1
+; CHECK-NEXT: [[EXITCOND:%.*]] = icmp ne i64 [[INDVARS_IV_NEXT]], [[WIDE_TRIP_COUNT]]
+; CHECK-NEXT: br i1 [[EXITCOND]], label [[FOR_BODY]], label [[FOR_COND_FOR_END_CRIT_EDGE:%.*]]
+; CHECK: for.cond.for.end_crit_edge:
+; CHECK-NEXT: br label [[FOR_END]]
+; CHECK: for.end:
+; CHECK-NEXT: [[CALL:%.*]] = call i32 @dummy(ptr @a, ptr @b)
+; CHECK-NEXT: ret i32 0
+;
+entry:
+ %cmp1 = icmp slt i32 0, %M
+ br i1 %cmp1, label %for.body.lr.ph, label %for.end
+
+for.body.lr.ph: ; preds = %entry
+ br label %for.body
+
+for.body: ; preds = %for.body.lr.ph, %for.inc
+ %i.02 = phi i32 [ 0, %for.body.lr.ph ], [ %inc, %for.inc ]
+ %idxprom = sext i32 %i.02 to i64
+ %arrayidx = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 %idxprom
+ %0 = load i32, ptr %arrayidx, align 4
+ %idxprom1 = sext i32 %i.02 to i64
+ %arrayidx2 = getelementptr inbounds [100 x i32], ptr @b, i64 0, i64 %idxprom1
+ %1 = load i32, ptr %arrayidx2, align 4
+ %add = add nsw i32 %0, %1
+ %add3 = add nsw i32 %i.02, %M
+ %idxprom4 = zext i32 %add3 to i64
+ %arrayidx5 = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 %idxprom4
+ store i32 %add, ptr %arrayidx5, align 4
+ br label %for.inc
+
+for.inc: ; preds = %for.body
+ %inc = add nsw i32 %i.02, 1
+ %cmp = icmp slt i32 %inc, %M
+ br i1 %cmp, label %for.body, label %for.cond.for.end_crit_edge
+
+for.cond.for.end_crit_edge: ; preds = %for.inc
+ br label %for.end
+
+for.end: ; preds = %for.cond.for.end_crit_edge, %entry
+ %call = call i32 @dummy(ptr @a, ptr @b)
+ ret i32 0
+}
+
+; Same as @foo3, but the guard is on %N, so %add3 may be negative and its zext
+; must stay.
+define i32 @foo3_neg(i32 %M, i32 %N) {
+; CHECK-LABEL: @foo3_neg(
+; CHECK-NEXT: entry:
+; CHECK-NEXT: [[CMP1:%.*]] = icmp slt i32 0, [[N:%.*]]
+; CHECK-NEXT: br i1 [[CMP1]], label [[FOR_BODY_LR_PH:%.*]], label [[FOR_END:%.*]]
+; CHECK: for.body.lr.ph:
+; CHECK-NEXT: [[TMP0:%.*]] = sext i32 [[M:%.*]] to i64
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[M]], i32 1)
+; CHECK-NEXT: [[WIDE_TRIP_COUNT:%.*]] = zext i32 [[SMAX]] to i64
+; CHECK-NEXT: br label [[FOR_BODY:%.*]]
+; CHECK: for.body:
+; CHECK-NEXT: [[INDVARS_IV:%.*]] = phi i64 [ [[INDVARS_IV_NEXT:%.*]], [[FOR_INC:%.*]] ], [ 0, [[FOR_BODY_LR_PH]] ]
+; CHECK-NEXT: [[ARRAYIDX:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[TMP1:%.*]] = load i32, ptr [[ARRAYIDX]], align 4
+; CHECK-NEXT: [[ARRAYIDX2:%.*]] = getelementptr inbounds [100 x i32], ptr @b, i64 0, i64 [[INDVARS_IV]]
+; CHECK-NEXT: [[TMP2:%.*]] = load i32, ptr [[ARRAYIDX2]], align 4
+; CHECK-NEXT: [[ADD:%.*]] = add nsw i32 [[TMP1]], [[TMP2]]
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i64 [[INDVARS_IV]], [[TMP0]]
; CHECK-NEXT: [[TMP4:%.*]] = trunc nsw i64 [[TMP3]] to i32
; CHECK-NEXT: [[IDXPROM4:%.*]] = zext i32 [[TMP4]] to i64
; CHECK-NEXT: [[ARRAYIDX5:%.*]] = getelementptr inbounds [100 x i32], ptr @a, i64 0, i64 [[IDXPROM4]]
@@ -240,7 +308,7 @@ define i32 @foo3(i32 %M) {
; CHECK-NEXT: ret i32 0
;
entry:
- %cmp1 = icmp slt i32 0, %M
+ %cmp1 = icmp slt i32 0, %N
br i1 %cmp1, label %for.body.lr.ph, label %for.end
for.body.lr.ph: ; preds = %entry
diff --git a/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll b/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll
new file mode 100644
index 0000000000000..67c3d8a87f6da
--- /dev/null
+++ b/llvm/test/Transforms/IndVarSimplify/monotonic-predicate-samesign.ll
@@ -0,0 +1,78 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=indvars -S < %s | FileCheck %s
+
+; s >s inv does not make %c true on every iteration: with s = 3 and inv = -5,
+; %c is false on the last iteration, where %iv = INT_MIN.
+define i32 @samesign_ugt_signed_guard(i32 %s, i32 %inv) {
+; CHECK-LABEL: define i32 @samesign_ugt_signed_guard(
+; CHECK-SAME: i32 [[S:%.*]], i32 [[INV:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[G:%.*]] = icmp sgt i32 [[S]], [[INV]]
+; CHECK-NEXT: br i1 [[G]], label %[[LOOP_PREHEADER:.*]], label %[[BAIL:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[S]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp samesign ugt i32 [[IV]], [[INV]]
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV]], -2147483648
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[C_LCSSA:%.*]] = phi i1 [ [[C]], %[[LOOP]] ]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[C_LCSSA]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+; CHECK: [[BAIL]]:
+; CHECK-NEXT: ret i32 7
+;
+entry:
+ %g = icmp sgt i32 %s, %inv
+ br i1 %g, label %loop, label %bail
+loop:
+ %iv = phi i32 [ %s, %entry ], [ %iv.next, %loop ]
+ %c = icmp samesign ugt i32 %iv, %inv
+ %iv.next = add nuw i32 %iv, 1
+ %ec = icmp eq i32 %iv, u0x80000000
+ br i1 %ec, label %exit, label %loop
+exit:
+ %r = zext i1 %c to i32
+ ret i32 %r
+bail:
+ ret i32 7
+}
+
+; s >u inv is known on entry and the IV is nuw, so %c folds to true.
+define i32 @samesign_ugt_unsigned_guard(i32 %s, i32 %inv) {
+; CHECK-LABEL: define i32 @samesign_ugt_unsigned_guard(
+; CHECK-SAME: i32 [[S:%.*]], i32 [[INV:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: [[G:%.*]] = icmp ugt i32 [[S]], [[INV]]
+; CHECK-NEXT: br i1 [[G]], label %[[LOOP_PREHEADER:.*]], label %[[BAIL:.*]]
+; CHECK: [[LOOP_PREHEADER]]:
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[LOOP]] ], [ [[S]], %[[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i32 [[IV]], 1
+; CHECK-NEXT: [[EC:%.*]] = icmp eq i32 [[IV]], -2147483648
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[C_LCSSA:%.*]] = phi i1 [ true, %[[LOOP]] ]
+; CHECK-NEXT: [[R:%.*]] = zext i1 [[C_LCSSA]] to i32
+; CHECK-NEXT: ret i32 [[R]]
+; CHECK: [[BAIL]]:
+; CHECK-NEXT: ret i32 7
+;
+entry:
+ %g = icmp ugt i32 %s, %inv
+ br i1 %g, label %loop, label %bail
+loop:
+ %iv = phi i32 [ ...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/231697
More information about the llvm-branch-commits
mailing list