[llvm] [IRCE] Prove predicates with and without loop-guarded SCEVs (PR #174843)
Aleksandr Popov via llvm-commits
llvm-commits at lists.llvm.org
Wed Jan 7 11:15:37 PST 2026
https://github.com/aleks-tmb created https://github.com/llvm/llvm-project/pull/174843
IRCE may fail to prove predicates when loop bounds are rewritten by
LoopConstrainer::applyLoopGuards(). In such cases, simple predicates
(e.g. %start < %limit) become obscured by strengthened SCEV expressions,
even though they are trivially implied by the guard in the loop preheader.
See: https://github.com/llvm/llvm-project/issues/167827
This change makes IRCE attempt to prove predicates both on the original
SCEVs and on the guarded SCEVs, preventing incorrect rejection of loops
with safe bounds.
>From 41176ef342c6d933e83b9bb52569c0f0138d6ead Mon Sep 17 00:00:00 2001
From: Aleksandr Popov <apopov at azul.com>
Date: Wed, 7 Jan 2026 18:04:47 +0000
Subject: [PATCH 1/2] Revert "[LoopConstrainer] Apply loop gurads to check that
loop bounds are safe (#71531)"
This reverts commit cd20600767409b183a6d213d56f85f8041a21487.
---
llvm/lib/Transforms/Utils/LoopConstrainer.cpp | 22 ++---
.../Transforms/IRCE/compound-loop-bound.ll | 85 ++-----------------
2 files changed, 17 insertions(+), 90 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/LoopConstrainer.cpp b/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
index efc307e31a8b9..b6f36561c60df 100644
--- a/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
+++ b/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
@@ -42,11 +42,8 @@ static bool isSafeDecreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
ICmpInst::Predicate BoundPred =
IsSigned ? CmpInst::ICMP_SGT : CmpInst::ICMP_UGT;
- auto StartLG = SE.applyLoopGuards(Start, L);
- auto BoundLG = SE.applyLoopGuards(BoundSCEV, L);
-
if (LatchBrExitIdx == 1)
- return SE.isLoopEntryGuardedByCond(L, BoundPred, StartLG, BoundLG);
+ return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, BoundSCEV);
assert(LatchBrExitIdx == 0 && "LatchBrExitIdx should be either 0 or 1");
@@ -57,10 +54,10 @@ static bool isSafeDecreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
const SCEV *Limit = SE.getMinusSCEV(SE.getConstant(Min), StepPlusOne);
const SCEV *MinusOne =
- SE.getMinusSCEV(BoundLG, SE.getOne(BoundLG->getType()));
+ SE.getMinusSCEV(BoundSCEV, SE.getOne(BoundSCEV->getType()));
- return SE.isLoopEntryGuardedByCond(L, BoundPred, StartLG, MinusOne) &&
- SE.isLoopEntryGuardedByCond(L, BoundPred, BoundLG, Limit);
+ return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, MinusOne) &&
+ SE.isLoopEntryGuardedByCond(L, BoundPred, BoundSCEV, Limit);
}
/// Given a loop with an increasing induction variable, is it possible to
@@ -89,11 +86,8 @@ static bool isSafeIncreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
ICmpInst::Predicate BoundPred =
IsSigned ? CmpInst::ICMP_SLT : CmpInst::ICMP_ULT;
- auto StartLG = SE.applyLoopGuards(Start, L);
- auto BoundLG = SE.applyLoopGuards(BoundSCEV, L);
-
if (LatchBrExitIdx == 1)
- return SE.isLoopEntryGuardedByCond(L, BoundPred, StartLG, BoundLG);
+ return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, BoundSCEV);
assert(LatchBrExitIdx == 0 && "LatchBrExitIdx should be 0 or 1");
@@ -103,9 +97,9 @@ static bool isSafeIncreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
: APInt::getMaxValue(BitWidth);
const SCEV *Limit = SE.getMinusSCEV(SE.getConstant(Max), StepMinusOne);
- return (SE.isLoopEntryGuardedByCond(L, BoundPred, StartLG,
- SE.getAddExpr(BoundLG, Step)) &&
- SE.isLoopEntryGuardedByCond(L, BoundPred, BoundLG, Limit));
+ return (SE.isLoopEntryGuardedByCond(L, BoundPred, Start,
+ SE.getAddExpr(BoundSCEV, Step)) &&
+ SE.isLoopEntryGuardedByCond(L, BoundPred, BoundSCEV, Limit));
}
/// Returns estimate for max latch taken count of the loop of the narrowest
diff --git a/llvm/test/Transforms/IRCE/compound-loop-bound.ll b/llvm/test/Transforms/IRCE/compound-loop-bound.ll
index e50d8c6127f40..0930d19e22154 100644
--- a/llvm/test/Transforms/IRCE/compound-loop-bound.ll
+++ b/llvm/test/Transforms/IRCE/compound-loop-bound.ll
@@ -16,56 +16,23 @@ define void @incrementing_loop(ptr %arr, ptr %len_ptr, i32 %K, i32 %M) {
; CHECK-NEXT: br i1 [[AND]], label [[PREHEADER:%.*]], label [[EXIT:%.*]]
; CHECK: preheader:
; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[K]], i32 [[M]])
-; CHECK-NEXT: [[SMIN1:%.*]] = call i32 @llvm.smin.i32(i32 [[LEN]], i32 [[M]])
-; CHECK-NEXT: [[SMIN2:%.*]] = call i32 @llvm.smin.i32(i32 [[SMIN1]], i32 [[K]])
-; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 [[SMIN2]], i32 0)
-; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]]
-; CHECK-NEXT: br i1 [[TMP0]], label [[LOOP_PREHEADER:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]]
-; CHECK: loop.preheader:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER]] ]
-; CHECK-NEXT: [[IDX_NEXT]] = add nsw i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ]
+; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1
; CHECK-NEXT: [[GUARD:%.*]] = icmp slt i32 [[IDX]], [[LEN]]
-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]]
+; CHECK-NEXT: br i1 [[GUARD]], label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS:%.*]]
; CHECK: in.bounds:
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX]]
; CHECK-NEXT: store i32 0, ptr [[ADDR]], align 4
; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[SMIN]]
-; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]]
-; CHECK-NEXT: br i1 [[TMP1]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]]
-; CHECK: main.exit.selector:
-; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ]
-; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[SMIN]]
-; CHECK-NEXT: br i1 [[TMP2]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]]
-; CHECK: main.pseudo.exit:
-; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ]
-; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ]
-; CHECK-NEXT: br label [[POSTLOOP:%.*]]
-; CHECK: out.of.bounds.loopexit:
-; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]]
-; CHECK: out.of.bounds.loopexit3:
-; CHECK-NEXT: br label [[OUT_OF_BOUNDS]]
+; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
; CHECK: out.of.bounds:
; CHECK-NEXT: ret void
-; CHECK: exit.loopexit.loopexit:
-; CHECK-NEXT: br label [[EXIT_LOOPEXIT]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
-; CHECK: postloop:
-; CHECK-NEXT: br label [[LOOP_POSTLOOP:%.*]]
-; CHECK: loop.postloop:
-; CHECK-NEXT: [[IDX_POSTLOOP:%.*]] = phi i32 [ [[IDX_COPY]], [[POSTLOOP]] ], [ [[IDX_NEXT_POSTLOOP:%.*]], [[IN_BOUNDS_POSTLOOP:%.*]] ]
-; CHECK-NEXT: [[IDX_NEXT_POSTLOOP]] = add i32 [[IDX_POSTLOOP]], 1
-; CHECK-NEXT: [[GUARD_POSTLOOP:%.*]] = icmp slt i32 [[IDX_POSTLOOP]], [[LEN]]
-; CHECK-NEXT: br i1 [[GUARD_POSTLOOP]], label [[IN_BOUNDS_POSTLOOP]], label [[OUT_OF_BOUNDS_LOOPEXIT:%.*]]
-; CHECK: in.bounds.postloop:
-; CHECK-NEXT: [[ADDR_POSTLOOP:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX_POSTLOOP]]
-; CHECK-NEXT: store i32 0, ptr [[ADDR_POSTLOOP]], align 4
-; CHECK-NEXT: [[NEXT_POSTLOOP:%.*]] = icmp slt i32 [[IDX_NEXT_POSTLOOP]], [[SMIN]]
-; CHECK-NEXT: br i1 [[NEXT_POSTLOOP]], label [[LOOP_POSTLOOP]], label [[EXIT_LOOPEXIT_LOOPEXIT:%.*]], !llvm.loop [[LOOP1:![0-9]+]], !loop_constrainer.loop.clone !6
;
entry:
%len = load i32, ptr %len_ptr, !range !0
@@ -111,58 +78,24 @@ define void @decrementing_loop(ptr %arr, ptr %len_ptr, i32 %K, i32 %M) {
; CHECK-NEXT: [[AND:%.*]] = and i1 [[CHECK0]], [[CHECK1]]
; CHECK-NEXT: br i1 [[AND]], label [[PREHEADER:%.*]], label [[EXIT:%.*]]
; CHECK: preheader:
-; CHECK-NEXT: [[INDVAR_START:%.*]] = call i32 @llvm.smin.i32(i32 [[K]], i32 [[M]])
-; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[INDVAR_START]], 1
-; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[LEN]], i32 [[TMP0]])
-; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[SMIN]], i32 0)
-; CHECK-NEXT: [[EXIT_PRELOOP_AT:%.*]] = add nsw i32 [[SMAX]], -1
-; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[INDVAR_START]], [[EXIT_PRELOOP_AT]]
-; CHECK-NEXT: br i1 [[TMP1]], label [[LOOP_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
-; CHECK: loop.preloop.preheader:
-; CHECK-NEXT: br label [[LOOP_PRELOOP:%.*]]
-; CHECK: mainloop:
+; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[K]], i32 [[M]])
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_PRELOOP_COPY:%.*]], [[MAINLOOP:%.*]] ], [ [[IDX_DEC:%.*]], [[IN_BOUNDS:%.*]] ]
-; CHECK-NEXT: [[IDX_DEC]] = sub nsw i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[SMIN]], [[PREHEADER]] ], [ [[IDX_DEC:%.*]], [[IN_BOUNDS:%.*]] ]
+; CHECK-NEXT: [[IDX_DEC]] = sub i32 [[IDX]], 1
; CHECK-NEXT: [[GUARD:%.*]] = icmp slt i32 [[IDX]], [[LEN]]
-; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT1:%.*]]
+; CHECK-NEXT: br i1 [[GUARD]], label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS:%.*]]
; CHECK: in.bounds:
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX]]
; CHECK-NEXT: store i32 0, ptr [[ADDR]], align 4
; CHECK-NEXT: [[NEXT:%.*]] = icmp sgt i32 [[IDX_DEC]], -1
-; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP]], label [[EXIT_LOOPEXIT_LOOPEXIT:%.*]]
-; CHECK: out.of.bounds.loopexit:
-; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]]
-; CHECK: out.of.bounds.loopexit1:
-; CHECK-NEXT: br label [[OUT_OF_BOUNDS]]
+; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
; CHECK: out.of.bounds:
; CHECK-NEXT: ret void
-; CHECK: exit.loopexit.loopexit:
-; CHECK-NEXT: br label [[EXIT_LOOPEXIT:%.*]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
-; CHECK: loop.preloop:
-; CHECK-NEXT: [[IDX_PRELOOP:%.*]] = phi i32 [ [[IDX_DEC_PRELOOP:%.*]], [[IN_BOUNDS_PRELOOP:%.*]] ], [ [[INDVAR_START]], [[LOOP_PRELOOP_PREHEADER]] ]
-; CHECK-NEXT: [[IDX_DEC_PRELOOP]] = sub i32 [[IDX_PRELOOP]], 1
-; CHECK-NEXT: [[GUARD_PRELOOP:%.*]] = icmp slt i32 [[IDX_PRELOOP]], [[LEN]]
-; CHECK-NEXT: br i1 [[GUARD_PRELOOP]], label [[IN_BOUNDS_PRELOOP]], label [[OUT_OF_BOUNDS_LOOPEXIT:%.*]]
-; CHECK: in.bounds.preloop:
-; CHECK-NEXT: [[ADDR_PRELOOP:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX_PRELOOP]]
-; CHECK-NEXT: store i32 0, ptr [[ADDR_PRELOOP]], align 4
-; CHECK-NEXT: [[NEXT_PRELOOP:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP]], -1
-; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP]], [[EXIT_PRELOOP_AT]]
-; CHECK-NEXT: br i1 [[TMP2]], label [[LOOP_PRELOOP]], label [[PRELOOP_EXIT_SELECTOR:%.*]], !llvm.loop [[LOOP7:![0-9]+]], !loop_constrainer.loop.clone !6
-; CHECK: preloop.exit.selector:
-; CHECK-NEXT: [[IDX_DEC_PRELOOP_LCSSA:%.*]] = phi i32 [ [[IDX_DEC_PRELOOP]], [[IN_BOUNDS_PRELOOP]] ]
-; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP_LCSSA]], -1
-; CHECK-NEXT: br i1 [[TMP3]], label [[PRELOOP_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT]]
-; CHECK: preloop.pseudo.exit:
-; CHECK-NEXT: [[IDX_PRELOOP_COPY]] = phi i32 [ [[INDVAR_START]], [[PREHEADER]] ], [ [[IDX_DEC_PRELOOP_LCSSA]], [[PRELOOP_EXIT_SELECTOR]] ]
-; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ [[INDVAR_START]], [[PREHEADER]] ], [ [[IDX_DEC_PRELOOP_LCSSA]], [[PRELOOP_EXIT_SELECTOR]] ]
-; CHECK-NEXT: br label [[MAINLOOP]]
;
entry:
%len = load i32, ptr %len_ptr, !range !0
>From c388f0bd38ffe1148ac09a8966623c63cbba084f Mon Sep 17 00:00:00 2001
From: Aleksandr Popov <apopov at apopov-zvm-34867.cloud1sc1.openstack.internal>
Date: Wed, 7 Jan 2026 19:06:33 +0000
Subject: [PATCH 2/2] [IRCE] Prove predicates with and without loop-guarded
SCEVs
IRCE may fail to prove predicates when loop bounds are rewritten by
LoopConstrainer::applyLoopGuards(). In such cases, simple predicates
(e.g. %start < %limit) become obscured by strengthened SCEV expressions,
even though they are trivially implied by the guard in the loop
preheader.
See: https://github.com/llvm/llvm-project/issues/167827
This change makes IRCE attempt to prove predicates both on the original
SCEVs and on the guarded SCEVs, preventing incorrect rejection of loops
with safe bounds.
---
llvm/lib/Transforms/Utils/LoopConstrainer.cpp | 23 ++-
.../Transforms/IRCE/compound-loop-bound.ll | 83 +++++++++--
.../Transforms/IRCE/loop-guarded-bounds.ll | 136 ++++++++++++++++++
3 files changed, 228 insertions(+), 14 deletions(-)
create mode 100644 llvm/test/Transforms/IRCE/loop-guarded-bounds.ll
diff --git a/llvm/lib/Transforms/Utils/LoopConstrainer.cpp b/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
index b6f36561c60df..7e2ca3118406c 100644
--- a/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
+++ b/llvm/lib/Transforms/Utils/LoopConstrainer.cpp
@@ -14,6 +14,17 @@ static const char *ClonedLoopTag = "loop_constrainer.loop.clone";
#define DEBUG_TYPE "loop-constrainer"
+static bool isLoopEntryGuardedByCond(ScalarEvolution &SE, Loop *L, ICmpInst::Predicate Pred,
+ const SCEV *Start, const SCEV *Bound) {
+ // First, try to prove the predicate without applying loop guards.
+ if (SE.isLoopEntryGuardedByCond(L, Pred, Start, Bound))
+ return true;
+ // Otherwise, try again with loop guards applied to the SCEVs.
+ auto StartLG = SE.applyLoopGuards(Start, L);
+ auto BoundLG = SE.applyLoopGuards(Bound, L);
+ return SE.isLoopEntryGuardedByCond(L, Pred, StartLG, BoundLG);
+}
+
/// Given a loop with an deccreasing induction variable, is it possible to
/// safely calculate the bounds of a new loop using the given Predicate.
static bool isSafeDecreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
@@ -43,7 +54,7 @@ static bool isSafeDecreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
IsSigned ? CmpInst::ICMP_SGT : CmpInst::ICMP_UGT;
if (LatchBrExitIdx == 1)
- return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, BoundSCEV);
+ return isLoopEntryGuardedByCond(SE, L, BoundPred, Start, BoundSCEV);
assert(LatchBrExitIdx == 0 && "LatchBrExitIdx should be either 0 or 1");
@@ -56,8 +67,8 @@ static bool isSafeDecreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
const SCEV *MinusOne =
SE.getMinusSCEV(BoundSCEV, SE.getOne(BoundSCEV->getType()));
- return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, MinusOne) &&
- SE.isLoopEntryGuardedByCond(L, BoundPred, BoundSCEV, Limit);
+ return isLoopEntryGuardedByCond(SE, L, BoundPred, Start, MinusOne) &&
+ isLoopEntryGuardedByCond(SE, L, BoundPred, BoundSCEV, Limit);
}
/// Given a loop with an increasing induction variable, is it possible to
@@ -87,7 +98,7 @@ static bool isSafeIncreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
IsSigned ? CmpInst::ICMP_SLT : CmpInst::ICMP_ULT;
if (LatchBrExitIdx == 1)
- return SE.isLoopEntryGuardedByCond(L, BoundPred, Start, BoundSCEV);
+ return isLoopEntryGuardedByCond(SE, L, BoundPred, Start, BoundSCEV);
assert(LatchBrExitIdx == 0 && "LatchBrExitIdx should be 0 or 1");
@@ -97,9 +108,9 @@ static bool isSafeIncreasingBound(const SCEV *Start, const SCEV *BoundSCEV,
: APInt::getMaxValue(BitWidth);
const SCEV *Limit = SE.getMinusSCEV(SE.getConstant(Max), StepMinusOne);
- return (SE.isLoopEntryGuardedByCond(L, BoundPred, Start,
+ return (isLoopEntryGuardedByCond(SE, L, BoundPred, Start,
SE.getAddExpr(BoundSCEV, Step)) &&
- SE.isLoopEntryGuardedByCond(L, BoundPred, BoundSCEV, Limit));
+ isLoopEntryGuardedByCond(SE, L, BoundPred, BoundSCEV, Limit));
}
/// Returns estimate for max latch taken count of the loop of the narrowest
diff --git a/llvm/test/Transforms/IRCE/compound-loop-bound.ll b/llvm/test/Transforms/IRCE/compound-loop-bound.ll
index 0930d19e22154..c40d8343f840a 100644
--- a/llvm/test/Transforms/IRCE/compound-loop-bound.ll
+++ b/llvm/test/Transforms/IRCE/compound-loop-bound.ll
@@ -16,23 +16,56 @@ define void @incrementing_loop(ptr %arr, ptr %len_ptr, i32 %K, i32 %M) {
; CHECK-NEXT: br i1 [[AND]], label [[PREHEADER:%.*]], label [[EXIT:%.*]]
; CHECK: preheader:
; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[K]], i32 [[M]])
+; CHECK-NEXT: [[SMIN1:%.*]] = call i32 @llvm.smin.i32(i32 [[LEN]], i32 [[M]])
+; CHECK-NEXT: [[SMIN2:%.*]] = call i32 @llvm.smin.i32(i32 [[SMIN1]], i32 [[K]])
+; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 [[SMIN2]], i32 0)
+; CHECK-NEXT: [[TMP0:%.*]] = icmp slt i32 0, [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP0]], label [[LOOP_PREHEADER:%.*]], label [[MAIN_PSEUDO_EXIT:%.*]]
+; CHECK: loop.preheader:
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ]
-; CHECK-NEXT: [[IDX_NEXT]] = add i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_NEXT:%.*]], [[IN_BOUNDS:%.*]] ], [ 0, [[LOOP_PREHEADER]] ]
+; CHECK-NEXT: [[IDX_NEXT]] = add nsw i32 [[IDX]], 1
; CHECK-NEXT: [[GUARD:%.*]] = icmp slt i32 [[IDX]], [[LEN]]
-; CHECK-NEXT: br i1 [[GUARD]], label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS:%.*]]
+; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT3:%.*]]
; CHECK: in.bounds:
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX]]
; CHECK-NEXT: store i32 0, ptr [[ADDR]], align 4
; CHECK-NEXT: [[NEXT:%.*]] = icmp slt i32 [[IDX_NEXT]], [[SMIN]]
-; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
+; CHECK-NEXT: [[TMP1:%.*]] = icmp slt i32 [[IDX_NEXT]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[LOOP]], label [[MAIN_EXIT_SELECTOR:%.*]]
+; CHECK: main.exit.selector:
+; CHECK-NEXT: [[IDX_NEXT_LCSSA:%.*]] = phi i32 [ [[IDX_NEXT]], [[IN_BOUNDS]] ]
+; CHECK-NEXT: [[TMP2:%.*]] = icmp slt i32 [[IDX_NEXT_LCSSA]], [[SMIN]]
+; CHECK-NEXT: br i1 [[TMP2]], label [[MAIN_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT:%.*]]
+; CHECK: main.pseudo.exit:
+; CHECK-NEXT: [[IDX_COPY:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ]
+; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ 0, [[PREHEADER]] ], [ [[IDX_NEXT_LCSSA]], [[MAIN_EXIT_SELECTOR]] ]
+; CHECK-NEXT: br label [[POSTLOOP:%.*]]
+; CHECK: out.of.bounds.loopexit:
+; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]]
+; CHECK: out.of.bounds.loopexit3:
+; CHECK-NEXT: br label [[OUT_OF_BOUNDS]]
; CHECK: out.of.bounds:
; CHECK-NEXT: ret void
+; CHECK: exit.loopexit.loopexit:
+; CHECK-NEXT: br label [[EXIT_LOOPEXIT]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
+; CHECK: postloop:
+; CHECK-NEXT: br label [[LOOP_POSTLOOP:%.*]]
+; CHECK: loop.postloop:
+; CHECK-NEXT: [[IDX_POSTLOOP:%.*]] = phi i32 [ [[IDX_COPY]], [[POSTLOOP]] ], [ [[IDX_NEXT_POSTLOOP:%.*]], [[IN_BOUNDS_POSTLOOP:%.*]] ]
+; CHECK-NEXT: [[IDX_NEXT_POSTLOOP]] = add i32 [[IDX_POSTLOOP]], 1
+; CHECK-NEXT: [[GUARD_POSTLOOP:%.*]] = icmp slt i32 [[IDX_POSTLOOP]], [[LEN]]
+; CHECK-NEXT: br i1 [[GUARD_POSTLOOP]], label [[IN_BOUNDS_POSTLOOP]], label [[OUT_OF_BOUNDS_LOOPEXIT:%.*]]
+; CHECK: in.bounds.postloop:
+; CHECK-NEXT: [[ADDR_POSTLOOP:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX_POSTLOOP]]
+; CHECK-NEXT: store i32 0, ptr [[ADDR_POSTLOOP]], align 4
+; CHECK-NEXT: [[NEXT_POSTLOOP:%.*]] = icmp slt i32 [[IDX_NEXT_POSTLOOP]], [[SMIN]]
+; CHECK-NEXT: br i1 [[NEXT_POSTLOOP]], label [[LOOP_POSTLOOP]], label [[EXIT_LOOPEXIT_LOOPEXIT:%.*]], !llvm.loop [[LOOP1:![0-9]+]], !loop_constrainer.loop.clone [[META6:![0-9]+]]
;
entry:
%len = load i32, ptr %len_ptr, !range !0
@@ -79,23 +112,57 @@ define void @decrementing_loop(ptr %arr, ptr %len_ptr, i32 %K, i32 %M) {
; CHECK-NEXT: br i1 [[AND]], label [[PREHEADER:%.*]], label [[EXIT:%.*]]
; CHECK: preheader:
; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[K]], i32 [[M]])
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[SMIN]], 1
+; CHECK-NEXT: [[SMIN1:%.*]] = call i32 @llvm.smin.i32(i32 [[LEN]], i32 [[TMP0]])
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[SMIN1]], i32 0)
+; CHECK-NEXT: [[EXIT_PRELOOP_AT:%.*]] = add nsw i32 [[SMAX]], -1
+; CHECK-NEXT: [[TMP1:%.*]] = icmp sgt i32 [[SMIN]], [[EXIT_PRELOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP1]], label [[LOOP_PRELOOP_PREHEADER:%.*]], label [[PRELOOP_PSEUDO_EXIT:%.*]]
+; CHECK: loop.preloop.preheader:
; CHECK-NEXT: br label [[LOOP:%.*]]
+; CHECK: mainloop:
+; CHECK-NEXT: br label [[LOOP2:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[SMIN]], [[PREHEADER]] ], [ [[IDX_DEC:%.*]], [[IN_BOUNDS:%.*]] ]
-; CHECK-NEXT: [[IDX_DEC]] = sub i32 [[IDX]], 1
+; CHECK-NEXT: [[IDX:%.*]] = phi i32 [ [[IDX_PRELOOP_COPY:%.*]], [[MAINLOOP:%.*]] ], [ [[IDX_DEC:%.*]], [[IN_BOUNDS:%.*]] ]
+; CHECK-NEXT: [[IDX_DEC]] = sub nsw i32 [[IDX]], 1
; CHECK-NEXT: [[GUARD:%.*]] = icmp slt i32 [[IDX]], [[LEN]]
-; CHECK-NEXT: br i1 [[GUARD]], label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS:%.*]]
+; CHECK-NEXT: br i1 true, label [[IN_BOUNDS]], label [[OUT_OF_BOUNDS_LOOPEXIT1:%.*]]
; CHECK: in.bounds:
; CHECK-NEXT: [[ADDR:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX]]
; CHECK-NEXT: store i32 0, ptr [[ADDR]], align 4
; CHECK-NEXT: [[NEXT:%.*]] = icmp sgt i32 [[IDX_DEC]], -1
-; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP]], label [[EXIT_LOOPEXIT:%.*]]
+; CHECK-NEXT: br i1 [[NEXT]], label [[LOOP2]], label [[EXIT_LOOPEXIT_LOOPEXIT:%.*]]
+; CHECK: out.of.bounds.loopexit:
+; CHECK-NEXT: br label [[OUT_OF_BOUNDS:%.*]]
+; CHECK: out.of.bounds.loopexit1:
+; CHECK-NEXT: br label [[OUT_OF_BOUNDS]]
; CHECK: out.of.bounds:
; CHECK-NEXT: ret void
+; CHECK: exit.loopexit.loopexit:
+; CHECK-NEXT: br label [[EXIT_LOOPEXIT:%.*]]
; CHECK: exit.loopexit:
; CHECK-NEXT: br label [[EXIT]]
; CHECK: exit:
; CHECK-NEXT: ret void
+; CHECK: loop.preloop:
+; CHECK-NEXT: [[IDX_PRELOOP:%.*]] = phi i32 [ [[IDX_DEC_PRELOOP:%.*]], [[IN_BOUNDS_PRELOOP:%.*]] ], [ [[SMIN]], [[LOOP_PRELOOP_PREHEADER]] ]
+; CHECK-NEXT: [[IDX_DEC_PRELOOP]] = sub i32 [[IDX_PRELOOP]], 1
+; CHECK-NEXT: [[GUARD_PRELOOP:%.*]] = icmp slt i32 [[IDX_PRELOOP]], [[LEN]]
+; CHECK-NEXT: br i1 [[GUARD_PRELOOP]], label [[IN_BOUNDS_PRELOOP]], label [[OUT_OF_BOUNDS_LOOPEXIT:%.*]]
+; CHECK: in.bounds.preloop:
+; CHECK-NEXT: [[ADDR_PRELOOP:%.*]] = getelementptr i32, ptr [[ARR]], i32 [[IDX_PRELOOP]]
+; CHECK-NEXT: store i32 0, ptr [[ADDR_PRELOOP]], align 4
+; CHECK-NEXT: [[NEXT_PRELOOP:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP]], -1
+; CHECK-NEXT: [[TMP2:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP]], [[EXIT_PRELOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP2]], label [[LOOP]], label [[PRELOOP_EXIT_SELECTOR:%.*]], !llvm.loop [[LOOP7:![0-9]+]], !loop_constrainer.loop.clone [[META6]]
+; CHECK: preloop.exit.selector:
+; CHECK-NEXT: [[IDX_DEC_PRELOOP_LCSSA:%.*]] = phi i32 [ [[IDX_DEC_PRELOOP]], [[IN_BOUNDS_PRELOOP]] ]
+; CHECK-NEXT: [[TMP3:%.*]] = icmp sgt i32 [[IDX_DEC_PRELOOP_LCSSA]], -1
+; CHECK-NEXT: br i1 [[TMP3]], label [[PRELOOP_PSEUDO_EXIT]], label [[EXIT_LOOPEXIT]]
+; CHECK: preloop.pseudo.exit:
+; CHECK-NEXT: [[IDX_PRELOOP_COPY]] = phi i32 [ [[SMIN]], [[PREHEADER]] ], [ [[IDX_DEC_PRELOOP_LCSSA]], [[PRELOOP_EXIT_SELECTOR]] ]
+; CHECK-NEXT: [[INDVAR_END:%.*]] = phi i32 [ [[SMIN]], [[PREHEADER]] ], [ [[IDX_DEC_PRELOOP_LCSSA]], [[PRELOOP_EXIT_SELECTOR]] ]
+; CHECK-NEXT: br label [[MAINLOOP]]
;
entry:
%len = load i32, ptr %len_ptr, !range !0
diff --git a/llvm/test/Transforms/IRCE/loop-guarded-bounds.ll b/llvm/test/Transforms/IRCE/loop-guarded-bounds.ll
new file mode 100644
index 0000000000000..ea169987cf2fc
--- /dev/null
+++ b/llvm/test/Transforms/IRCE/loop-guarded-bounds.ll
@@ -0,0 +1,136 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=irce < %s -S | FileCheck %s
+
+declare void @deopt()
+
+; This test verifies that IRCE and LoopConstrainer can prove loop entry
+; predicates even when loop bounds are rewritten by applyLoopGuards().
+define i32 @main(i32 %limit, i32 %start, i32 %length) {
+; CHECK-LABEL: define i32 @main(
+; CHECK-SAME: i32 [[LIMIT:%.*]], i32 [[INDVAR_START:%.*]], i32 [[LENGTH:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[CHECK0:%.*]] = icmp eq i32 [[LIMIT]], 0
+; CHECK-NEXT: br i1 [[CHECK0]], label %[[EXIT:.*]], label %[[PREHEADER:.*]]
+; CHECK: [[PREHEADER]]:
+; CHECK-NEXT: [[PRE_CHECK:%.*]] = icmp slt i32 [[INDVAR_START]], [[LIMIT]]
+; CHECK-NEXT: br i1 [[PRE_CHECK]], label %[[LOOP__PREHEADER:.*]], label %[[DEOPT:.*]]
+; CHECK: [[LOOP__PREHEADER]]:
+; CHECK-NEXT: [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[LENGTH]], i32 0)
+; CHECK-NEXT: [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[SMIN]], i32 -1)
+; CHECK-NEXT: [[TMP0:%.*]] = sub i32 -1, [[SMAX]]
+; CHECK-NEXT: [[SMIN1:%.*]] = call i32 @llvm.smin.i32(i32 [[LIMIT]], i32 [[TMP0]])
+; CHECK-NEXT: [[EXIT_PRELOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 [[INDVAR_START]], i32 [[SMIN1]])
+; CHECK-NEXT: [[TMP1:%.*]] = add i32 [[LENGTH]], -2147483647
+; CHECK-NEXT: [[SMAX3:%.*]] = call i32 @llvm.smax.i32(i32 [[TMP1]], i32 1)
+; CHECK-NEXT: [[TMP2:%.*]] = sub i32 [[LENGTH]], [[SMAX3]]
+; CHECK-NEXT: [[TMP3:%.*]] = add nsw i32 [[SMAX]], 1
+; CHECK-NEXT: [[TMP4:%.*]] = mul i32 [[TMP2]], [[TMP3]]
+; CHECK-NEXT: [[SMIN4:%.*]] = call i32 @llvm.smin.i32(i32 [[LIMIT]], i32 [[TMP4]])
+; CHECK-NEXT: [[EXIT_MAINLOOP_AT:%.*]] = call i32 @llvm.smax.i32(i32 [[INDVAR_START]], i32 [[SMIN4]])
+; CHECK-NEXT: [[TMP5:%.*]] = icmp slt i32 [[INDVAR_START]], [[EXIT_PRELOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP5]], label %[[LOOP__PRELOOP_PREHEADER:.*]], label %[[PRELOOP_PSEUDO_EXIT:.*]]
+; CHECK: [[LOOP__PRELOOP_PREHEADER]]:
+; CHECK-NEXT: br label %[[LOOP__PRELOOP:.*]]
+; CHECK: [[MAINLOOP:.*]]:
+; CHECK-NEXT: [[TMP6:%.*]] = icmp slt i32 [[INDVAR_END:%.*]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP6]], label %[[LOOP__PREHEADER8:.*]], label %[[MAIN_PSEUDO_EXIT:.*]]
+; CHECK: [[LOOP__PREHEADER8]]:
+; CHECK-NEXT: br label %[[LOOP_:.*]]
+; CHECK: [[LOOP_]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[IV_NEXT:%.*]], %[[GUARDED:.*]] ], [ [[IV_PRELOOP_COPY:%.*]], %[[LOOP__PREHEADER8]] ]
+; CHECK-NEXT: [[IV_NEXT]] = add nuw nsw i32 [[IV]], 1
+; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_NEXT]], [[LENGTH]]
+; CHECK-NEXT: br i1 true, label %[[GUARDED]], label %[[DEOPT_LOOPEXIT_LOOPEXIT9:.*]]
+; CHECK: [[GUARDED]]:
+; CHECK-NEXT: [[EXIT_CHECK:%.*]] = icmp slt i32 [[IV_NEXT]], [[LIMIT]]
+; CHECK-NEXT: [[TMP7:%.*]] = icmp slt i32 [[IV_NEXT]], [[EXIT_MAINLOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP7]], label %[[LOOP_]], label %[[MAIN_EXIT_SELECTOR:.*]]
+; CHECK: [[MAIN_EXIT_SELECTOR]]:
+; CHECK-NEXT: [[IV_NEXT_LCSSA:%.*]] = phi i32 [ [[IV_NEXT]], %[[GUARDED]] ]
+; CHECK-NEXT: [[TMP8:%.*]] = icmp slt i32 [[IV_NEXT_LCSSA]], [[LIMIT]]
+; CHECK-NEXT: br i1 [[TMP8]], label %[[MAIN_PSEUDO_EXIT]], label %[[EXIT_LOOPEXIT:.*]]
+; CHECK: [[MAIN_PSEUDO_EXIT]]:
+; CHECK-NEXT: [[IV_COPY:%.*]] = phi i32 [ [[IV_PRELOOP_COPY]], %[[MAINLOOP]] ], [ [[IV_NEXT_LCSSA]], %[[MAIN_EXIT_SELECTOR]] ]
+; CHECK-NEXT: [[INDVAR_END6:%.*]] = phi i32 [ [[INDVAR_END]], %[[MAINLOOP]] ], [ [[IV_NEXT_LCSSA]], %[[MAIN_EXIT_SELECTOR]] ]
+; CHECK-NEXT: br label %[[POSTLOOP:.*]]
+; CHECK: [[DEOPT_LOOPEXIT_LOOPEXIT:.*]]:
+; CHECK-NEXT: br label %[[DEOPT_LOOPEXIT:.*]]
+; CHECK: [[DEOPT_LOOPEXIT_LOOPEXIT7:.*]]:
+; CHECK-NEXT: br label %[[DEOPT_LOOPEXIT]]
+; CHECK: [[DEOPT_LOOPEXIT_LOOPEXIT9]]:
+; CHECK-NEXT: br label %[[DEOPT_LOOPEXIT]]
+; CHECK: [[DEOPT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[DEOPT]]
+; CHECK: [[DEOPT]]:
+; CHECK-NEXT: call void @deopt()
+; CHECK-NEXT: unreachable
+; CHECK: [[EXIT_LOOPEXIT_LOOPEXIT:.*]]:
+; CHECK-NEXT: br label %[[EXIT_LOOPEXIT]]
+; CHECK: [[EXIT_LOOPEXIT]]:
+; CHECK-NEXT: br label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[RET_OP:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ 1, %[[EXIT_LOOPEXIT]] ]
+; CHECK-NEXT: ret i32 [[RET_OP]]
+; CHECK: [[LOOP__PRELOOP]]:
+; CHECK-NEXT: [[IV_PRELOOP:%.*]] = phi i32 [ [[IV_NEXT_PRELOOP:%.*]], %[[GUARDED_PRELOOP:.*]] ], [ [[INDVAR_START]], %[[LOOP__PRELOOP_PREHEADER]] ]
+; CHECK-NEXT: [[IV_NEXT_PRELOOP]] = add nuw nsw i32 [[IV_PRELOOP]], 1
+; CHECK-NEXT: [[RANGE_CHECK_PRELOOP:%.*]] = icmp ult i32 [[IV_NEXT_PRELOOP]], [[LENGTH]]
+; CHECK-NEXT: br i1 [[RANGE_CHECK_PRELOOP]], label %[[GUARDED_PRELOOP]], label %[[DEOPT_LOOPEXIT_LOOPEXIT]]
+; CHECK: [[GUARDED_PRELOOP]]:
+; CHECK-NEXT: [[EXIT_CHECK_PRELOOP:%.*]] = icmp slt i32 [[IV_NEXT_PRELOOP]], [[LIMIT]]
+; CHECK-NEXT: [[TMP9:%.*]] = icmp slt i32 [[IV_NEXT_PRELOOP]], [[EXIT_PRELOOP_AT]]
+; CHECK-NEXT: br i1 [[TMP9]], label %[[LOOP__PRELOOP]], label %[[PRELOOP_EXIT_SELECTOR:.*]], !llvm.loop [[LOOP0:![0-9]+]], !loop_constrainer.loop.clone [[META5:![0-9]+]]
+; CHECK: [[PRELOOP_EXIT_SELECTOR]]:
+; CHECK-NEXT: [[IV_NEXT_PRELOOP_LCSSA:%.*]] = phi i32 [ [[IV_NEXT_PRELOOP]], %[[GUARDED_PRELOOP]] ]
+; CHECK-NEXT: [[TMP10:%.*]] = icmp slt i32 [[IV_NEXT_PRELOOP_LCSSA]], [[LIMIT]]
+; CHECK-NEXT: br i1 [[TMP10]], label %[[PRELOOP_PSEUDO_EXIT]], label %[[EXIT_LOOPEXIT]]
+; CHECK: [[PRELOOP_PSEUDO_EXIT]]:
+; CHECK-NEXT: [[IV_PRELOOP_COPY]] = phi i32 [ [[INDVAR_START]], %[[LOOP__PREHEADER]] ], [ [[IV_NEXT_PRELOOP_LCSSA]], %[[PRELOOP_EXIT_SELECTOR]] ]
+; CHECK-NEXT: [[INDVAR_END]] = phi i32 [ [[INDVAR_START]], %[[LOOP__PREHEADER]] ], [ [[IV_NEXT_PRELOOP_LCSSA]], %[[PRELOOP_EXIT_SELECTOR]] ]
+; CHECK-NEXT: br label %[[MAINLOOP]]
+; CHECK: [[POSTLOOP]]:
+; CHECK-NEXT: br label %[[LOOP__POSTLOOP:.*]]
+; CHECK: [[LOOP__POSTLOOP]]:
+; CHECK-NEXT: [[IV_POSTLOOP:%.*]] = phi i32 [ [[IV_NEXT_POSTLOOP:%.*]], %[[GUARDED_POSTLOOP:.*]] ], [ [[IV_COPY]], %[[POSTLOOP]] ]
+; CHECK-NEXT: [[IV_NEXT_POSTLOOP]] = add nuw nsw i32 [[IV_POSTLOOP]], 1
+; CHECK-NEXT: [[RANGE_CHECK_POSTLOOP:%.*]] = icmp ult i32 [[IV_NEXT_POSTLOOP]], [[LENGTH]]
+; CHECK-NEXT: br i1 [[RANGE_CHECK_POSTLOOP]], label %[[GUARDED_POSTLOOP]], label %[[DEOPT_LOOPEXIT_LOOPEXIT7]]
+; CHECK: [[GUARDED_POSTLOOP]]:
+; CHECK-NEXT: [[EXIT_CHECK_POSTLOOP:%.*]] = icmp slt i32 [[IV_NEXT_POSTLOOP]], [[LIMIT]]
+; CHECK-NEXT: br i1 [[EXIT_CHECK_POSTLOOP]], label %[[LOOP__POSTLOOP]], label %[[EXIT_LOOPEXIT_LOOPEXIT]], !llvm.loop [[LOOP6:![0-9]+]], !loop_constrainer.loop.clone [[META5]]
+;
+entry:
+ %check0 = icmp eq i32 %limit, 0
+ br i1 %check0, label %exit, label %preheader
+
+preheader: ; preds = %entry
+ %pre_check = icmp slt i32 %start, %limit
+ br i1 %pre_check, label %loop_, label %deopt
+
+loop_: ; preds = %guarded, %preheader
+ %iv = phi i32 [ %iv.next, %guarded ], [ %start, %preheader ]
+ %iv.next = add nuw nsw i32 %iv, 1
+ %range_check = icmp ult i32 %iv.next, %length
+ br i1 %range_check, label %guarded, label %deopt
+
+guarded: ; preds = %loop_
+ %exit_check = icmp slt i32 %iv.next, %limit
+ br i1 %exit_check, label %loop_, label %exit
+
+deopt:
+ call void @deopt()
+ unreachable
+
+exit: ; preds = %guarded, %loop_, %preheader, %entry
+ %ret.op = phi i32 [ 0, %entry ], [ 1, %guarded ]
+ ret i32 %ret.op
+}
+;.
+; CHECK: [[LOOP0]] = distinct !{[[LOOP0]], [[META1:![0-9]+]], [[META2:![0-9]+]], [[META3:![0-9]+]], [[META4:![0-9]+]]}
+; CHECK: [[META1]] = !{!"llvm.loop.unroll.disable"}
+; CHECK: [[META2]] = !{!"llvm.loop.vectorize.enable", i1 false}
+; CHECK: [[META3]] = !{!"llvm.loop.licm_versioning.disable"}
+; CHECK: [[META4]] = !{!"llvm.loop.distribute.enable", i1 false}
+; CHECK: [[META5]] = !{}
+; CHECK: [[LOOP6]] = distinct !{[[LOOP6]], [[META1]], [[META2]], [[META3]], [[META4]]}
+;.
More information about the llvm-commits
mailing list