}
static std::optional<Value *>
-createReplacement(Value *V, const Loop *L, BasicBlock *ExitingBB,
- const SCEV *MaxIter, bool SkipLastIter, ScalarEvolution *SE,
- SCEVExpander &Rewriter) {
- ICmpInst::Predicate Pred;
- Value *LHS, *RHS;
- if (!match(V, m_ICmp(Pred, m_Value(LHS), m_Value(RHS))))
- return std::nullopt;
+createReplacement(ICmpInst *ICmp, const Loop *L, BasicBlock *ExitingBB,
+ const SCEV *MaxIter, bool Inverted, bool SkipLastIter,
+ ScalarEvolution *SE, SCEVExpander &Rewriter) {
+ ICmpInst::Predicate Pred = ICmp->getPredicate();
+ Value *LHS = ICmp->getOperand(0);
+ Value *RHS = ICmp->getOperand(1);
// 'LHS pred RHS' should now mean that we stay in loop.
auto *BI = cast<BranchInst>(ExitingBB->getTerminator());
- BasicBlock *FalseSucc = BI->getSuccessor(1);
- if (L->contains(FalseSucc))
+ if (Inverted)
Pred = CmpInst::getInversePredicate(Pred);
const SCEV *LHSS = SE->getSCEVAtScope(LHS, L);
assert(
(L->contains(BI->getSuccessor(0)) != L->contains(BI->getSuccessor(1))) &&
"Not a loop exit!");
- auto NewCond = createReplacement(BI->getCondition(), L, ExitingBB, MaxIter,
- SkipLastIter, SE, Rewriter);
- if (!NewCond)
- return false;
- replaceExitCond(BI, *NewCond, DeadInsts);
- return true;
+
+ // For branch that stays in loop by TRUE condition, go through AND. For branch
+ // that stays in loop by FALSE condition, go through OR. Both gives the
+ // similar logic: "stay in loop iff all conditions are true(false)".
+ bool Inverted = L->contains(BI->getSuccessor(1));
+ SmallVector<ICmpInst *, 4> LeafConditions;
+ SmallVector<Value *, 4> Worklist;
+ SmallPtrSet<Value *, 4> Visited;
+ Value *OldCond = BI->getCondition();
+ Visited.insert(OldCond);
+ Worklist.push_back(OldCond);
+
+ auto GoThrough = [&](Value *V) {
+ Value *LHS = nullptr, *RHS = nullptr;
+ if (Inverted) {
+ if (!match(V, m_LogicalOr(m_Value(LHS), m_Value(RHS))))
+ return false;
+ } else {
+ if (!match(V, m_LogicalAnd(m_Value(LHS), m_Value(RHS))))
+ return false;
+ }
+ if (Visited.insert(LHS).second)
+ Worklist.push_back(LHS);
+ if (Visited.insert(RHS).second)
+ Worklist.push_back(RHS);
+ return true;
+ };
+
+ do {
+ Value *Curr = Worklist.pop_back_val();
+ // Go through AND/OR conditions. Collect leaf ICMPs. We only care about
+ // those with one use, to avoid instruction duplication.
+ if (Curr->hasOneUse())
+ if (!GoThrough(Curr))
+ if (auto *ICmp = dyn_cast<ICmpInst>(Curr))
+ LeafConditions.push_back(ICmp);
+ } while (!Worklist.empty());
+
+ bool Changed = false;
+ for (auto *OldCond : LeafConditions)
+ if (auto Replaced =
+ createReplacement(OldCond, L, ExitingBB, MaxIter, Inverted,
+ SkipLastIter, SE, Rewriter)) {
+ Changed = true;
+ auto *NewCond = *Replaced;
+ if (auto *NCI = dyn_cast<Instruction>(NewCond)) {
+ NCI->setName(OldCond->getName() + ".first_iter");
+ NCI->moveBefore(cast<Instruction>(OldCond));
+ }
+ LLVM_DEBUG(dbgs() << "Unknown exit count: Replacing " << *OldCond
+ << " with " << *NewCond << "\n");
+ assert(OldCond->hasOneUse() && "Must be!");
+ OldCond->replaceAllUsesWith(NewCond);
+ DeadInsts.push_back(OldCond);
+ }
+ return Changed;
}
bool IndVarSimplify::canonicalizeExitCondition(Loop *L) {
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_CHECK]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[RANGE_CHECK3:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
-; CHECK-NEXT: br i1 [[RANGE_CHECK3]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
+; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
; CHECK-NEXT: [[LOOP_COND:%.*]] = call i1 @cond()
ret i32 -2
}
-; TODO: This example is equivalent to @test_simple_case, with only difference that
-; both checks are littered with extra irrelevant conditions. We should be able
-; to replace it with invariant despite this fact.
-; https://alive2.llvm.org/ce/z/G4iW8c
+; This example is equivalent to @test_simple_case, with only difference that
+; both checks are littered with extra irrelevant conditions. We should be able
+; to replace it with invariant despite this fact.
+; https://alive2.llvm.org/ce/z/G4iW8c
define i32 @test_litter_conditions(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK]], [[FAKE_2]]
+; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[AND_2]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
ret i32 -2
}
-; TODO: Same as test_litter_conditions, but swapped exit block branches
-; and exit condition expressed by OR. Still optimizable.
+; Same as test_litter_conditions, but swapped exit block branches
+; and exit condition expressed by OR. Still optimizable.
define i32 @test_litter_conditions_inverse(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_inverse(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK_FAILED:%.*]] = icmp uge i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FAILED_FIRST_ITER:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED]], [[FAKE_2]]
+; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[OR_2]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[RANGE_CHECK3:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
-; CHECK-NEXT: br i1 [[RANGE_CHECK3]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
+; CHECK-NEXT: br i1 [[RANGE_CHECK_FIRST_ITER]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
; CHECK-NEXT: [[LOOP_COND:%.*]] = call i1 @cond()
ret i32 -2
}
-; TODO: Same as test_litter_conditions_01, but swapped exit block branches
-; and condition expressed by OR. Still optimizable.
+; Same as test_litter_conditions_01, but swapped exit block branches
+; and condition expressed by OR. Still optimizable.
define i32 @test_litter_conditions_01_inverse(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_01_inverse(
; CHECK-NEXT: entry:
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[RANGE_CHECK_FAILED3:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
-; CHECK-NEXT: br i1 [[RANGE_CHECK_FAILED3]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
+; CHECK-NEXT: [[RANGE_CHECK_FAILED_FIRST_ITER:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
+; CHECK-NEXT: br i1 [[RANGE_CHECK_FAILED_FIRST_ITER]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
; CHECK-NEXT: [[LOOP_COND:%.*]] = call i1 @cond()
define i32 @test_litter_conditions_02(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_02(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_CHECK]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK]], [[FAKE_2]]
+; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[AND_2]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
ret i32 -2
}
-; TODO: Same as test_litter_conditions_02, but swapped exit block branches,
-; and condition is expressed as OR. Still optimizable.
+; Same as test_litter_conditions_02, but swapped exit block branches,
+; and condition is expressed as OR. Still optimizable.
define i32 @test_litter_conditions_02_inverse(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_02_inverse(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: br i1 [[ZERO_CHECK]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK_FAILED:%.*]] = icmp uge i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FAILED_FIRST_ITER:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED]], [[FAKE_2]]
+; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[OR_2]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
ret i32 -2
}
-; TODO: Same as @test_litter_conditions, but all conditions are computed in
-; header block. Make sure we infer fact from the right context.
-; https://alive2.llvm.org/ce/z/JiD-Pw
+; Same as @test_litter_conditions, but all conditions are computed in
+; header block. Make sure we infer fact from the right context.
+; https://alive2.llvm.org/ce/z/JiD-Pw
define i32 @test_litter_conditions_bad_context(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_bad_context(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK]], [[FAKE_2]]
+; CHECK-NEXT: [[AND_2:%.*]] = and i1 [[RANGE_CHECK_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
; CHECK-NEXT: br i1 [[AND_2]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
ret i32 -2
}
-; TODO: Same as @test_litter_conditions_bad_context, but swapped exit block branches,
-; and conditions expressed as OR. Still optimizable.
+; Same as @test_litter_conditions_bad_context, but swapped exit block branches,
+; and conditions expressed as OR. Still optimizable.
define i32 @test_litter_conditions_bad_context_inverse(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_bad_context_inverse(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK_FAILED:%.*]] = icmp uge i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FAILED_FIRST_ITER:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED]], [[FAKE_2]]
+; CHECK-NEXT: [[OR_2:%.*]] = or i1 [[RANGE_CHECK_FAILED_FIRST_ITER]], [[FAKE_2]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
; CHECK-NEXT: br i1 [[OR_2]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
ret i32 -3
}
-; TODO: Same as test_litter_conditions, but with logical AND.
+; Same as test_litter_conditions, but with logical AND.
define i32 @test_litter_conditions_logical_and(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_logical_and(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK:%.*]] = icmp ult i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FIRST_ITER:%.*]] = icmp ult i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[AND_2:%.*]] = select i1 [[RANGE_CHECK]], i1 [[FAKE_2]], i1 false
+; CHECK-NEXT: [[AND_2:%.*]] = select i1 [[RANGE_CHECK_FIRST_ITER]], i1 [[FAKE_2]], i1 false
; CHECK-NEXT: br i1 [[AND_2]], label [[BACKEDGE]], label [[FAILED_2:%.*]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1
ret i32 -2
}
-; TODO: Same as test_litter_conditions_inverse, but with logical OR.
+; Same as test_litter_conditions_inverse, but with logical OR.
define i32 @test_litter_conditions_inverse_logical_or(i32 %start, i32 %len) {
; CHECK-LABEL: @test_litter_conditions_inverse_logical_or(
; CHECK-NEXT: entry:
+; CHECK-NEXT: [[TMP0:%.*]] = add i32 [[START:%.*]], -1
; CHECK-NEXT: br label [[LOOP:%.*]]
; CHECK: loop:
-; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START:%.*]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
+; CHECK-NEXT: [[IV:%.*]] = phi i32 [ [[START]], [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[BACKEDGE:%.*]] ]
; CHECK-NEXT: [[ZERO_CHECK:%.*]] = icmp ne i32 [[IV]], 0
; CHECK-NEXT: [[FAKE_1:%.*]] = call i1 @cond()
; CHECK-NEXT: [[AND_1:%.*]] = and i1 [[ZERO_CHECK]], [[FAKE_1]]
; CHECK-NEXT: br i1 [[AND_1]], label [[RANGE_CHECK_BLOCK:%.*]], label [[FAILED_1:%.*]]
; CHECK: range_check_block:
-; CHECK-NEXT: [[IV_MINUS_1:%.*]] = add i32 [[IV]], -1
-; CHECK-NEXT: [[RANGE_CHECK_FAILED:%.*]] = icmp uge i32 [[IV_MINUS_1]], [[LEN:%.*]]
+; CHECK-NEXT: [[RANGE_CHECK_FAILED_FIRST_ITER:%.*]] = icmp uge i32 [[TMP0]], [[LEN:%.*]]
; CHECK-NEXT: [[FAKE_2:%.*]] = call i1 @cond()
-; CHECK-NEXT: [[OR_2:%.*]] = select i1 [[RANGE_CHECK_FAILED]], i1 true, i1 [[FAKE_2]]
+; CHECK-NEXT: [[OR_2:%.*]] = select i1 [[RANGE_CHECK_FAILED_FIRST_ITER]], i1 true, i1 [[FAKE_2]]
; CHECK-NEXT: br i1 [[OR_2]], label [[FAILED_2:%.*]], label [[BACKEDGE]]
; CHECK: backedge:
; CHECK-NEXT: [[IV_NEXT]] = add i32 [[IV]], -1