[llvm] [ConstraintElim] Bound IVs by compares of the phi in header/latch. (PR #226297)

Florian Hahn via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 14:04:43 PDT 2026


https://github.com/fhahn created https://github.com/llvm/llvm-project/pull/226297

Extend addInfoForInductions to add bounds for IVs when the phi is
compared in the header or latch:

In that case, every iteration taking the backedge checked
PN ContinuePred B, which guarantees PN != B for NE and LT predicates.
For an increment by one, PN != B together with StartValue <= B (added
precondition) imply PN <= B.

Alive2 Proofs:
 * icmp ult in header: https://alive2.llvm.org/ce/z/x37Uvm
 * icmp slt in header: https://alive2.llvm.org/ce/z/ibnn5k
 * icmp ne in header: https://alive2.llvm.org/ce/z/wLRfZe

Triggers in a number of additional cases on
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1431.

As-is, this comes with a slight compile-time increase:
* stage1-O3: +0.04%
* stage1-ReleaseThinLTO: +0.03%
* stage1-ReleaseLTO-g: +0.03%
* stage1-aarch64-O3: +0.02%
* stage2-O3: +0.03%

https://llvm-compile-time-tracker.com/compare.php?from=ea9f4c16ec59a210753b6e5dd23e6b0ee0a8542c&to=56c848df9c9a711d71987d4771c6914f582b83bf&stat=instructions:u

>From 7efb7037146716e82b0ad91ac03f7957a39681b2 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 24 Sep 2026 15:12:31 +0100
Subject: [PATCH 1/2] [ConstraintElim] Add tests for bounds from compares of
 induction phis (NFC).

Add tests for bounding an induction phi in the loop header by header or
latch compares of the phi itself (not its increment) against a
loop-invariant bound.
---
 ...on-condition-in-loop-exit-latch-counted.ll |   4 +-
 .../induction-phi-compare-header-bound.ll     | 923 ++++++++++++++++++
 2 files changed, 925 insertions(+), 2 deletions(-)
 create mode 100644 llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll

diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
index 31ae62c564dd6..506fa5c85f57e 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
@@ -59,8 +59,8 @@ exit.1:
 
 ; The latch compares the phi (%iv == %n), not the post-increment. The header
 ; check is not removable.
-define i1 @latch_counted_phi_not_removable(ptr %p, i64 %n, i64 %lim) {
-; CHECK-LABEL: define i1 @latch_counted_phi_not_removable(
+define i1 @latch_counted_phi(ptr %p, i64 %n, i64 %lim) {
+; CHECK-LABEL: define i1 @latch_counted_phi(
 ; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]], i64 [[LIM:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i64 [[N]], 0
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll b/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
new file mode 100644
index 0000000000000..6774e03c02c1c
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
@@ -0,0 +1,923 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=constraint-elimination -S %s | FileCheck %s
+
+declare void @use(i1)
+
+define void @header_tested_ult(i8 %n) {
+; CHECK-LABEL: define void @header_tested_ult(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[T_2:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %c.1 = icmp ult i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  %t.2 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.2)
+  ret void
+}
+
+define void @header_tested_ugt_commuted(i8 %n) {
+; CHECK-LABEL: define void @header_tested_ugt_commuted(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[N]], [[IV]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %cmp = icmp ugt i8 %n, %iv
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @latch_tested_ne_symbolic_start(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @latch_tested_ne_symbolic_start(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE_U:%.*]] = icmp ule i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE_U]])
+; CHECK-NEXT:    [[PRE_S:%.*]] = icmp sle i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE_S]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[T_2:%.*]] = icmp sle i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    br label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre.u = icmp ule i8 %start, %n
+  call void @llvm.assume(i1 %pre.u)
+  %pre.s = icmp sle i8 %start, %n
+  call void @llvm.assume(i1 %pre.s)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %t.2 = icmp sle i8 %iv, %n
+  call void @use(i1 %t.2)
+  %c.1 = icmp ult i8 %iv, %n
+  call void @use(i1 %c.1)
+  br label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  %ec = icmp eq i8 %iv, %n
+  br i1 %ec, label %exit, label %loop.header
+
+exit:
+  ret void
+}
+
+define void @header_latch_tested_slt(i8 %n) {
+; CHECK-LABEL: define void @header_latch_tested_slt(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sge i8 [[N]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp sle i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp sge i8 %n, 0
+  call void @llvm.assume(i1 %pre)
+  br label %loop
+
+loop:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop ]
+  %t.1 = icmp sle i8 %iv, %n
+  call void @use(i1 %t.1)
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp slt i8 %iv, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  ret void
+}
+
+define void @header_tested_ne_and(i8 %n, i1 %c) {
+; CHECK-LABEL: define void @header_tested_ne_and(
+; CHECK-SAME: i8 [[N:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
+; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP]], [[C]]
+; CHECK-NEXT:    br i1 [[AND]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %cmp = icmp ne i8 %iv, %n
+  %and = and i1 %cmp, %c
+  br i1 %and, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @header_tested_uge_or(i8 %n, i1 %c) {
+; CHECK-LABEL: define void @header_tested_uge_or(
+; CHECK-SAME: i8 [[N:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[IV]], [[N]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[C]]
+; CHECK-NEXT:    br i1 [[OR]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %cmp = icmp uge i8 %iv, %n
+  %or = or i1 %cmp, %c
+  br i1 %or, label %exit, label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @header_tested_ne_exit_use(i8 %n) {
+; CHECK-LABEL: define void @header_tested_ne_exit_use(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %cmp = icmp ne i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  %t.2 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.2)
+  ret void
+}
+
+define void @latch_tested_ult(i8 %n) {
+; CHECK-LABEL: define void @latch_tested_ult(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    br label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  br label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.header, label %exit
+
+exit:
+  ret void
+}
+
+define void @header_tested_ult_uadd_with_overflow(i8 %n) {
+; CHECK-LABEL: define void @header_tested_ult_uadd_with_overflow(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[OV:%.*]] = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 [[IV]], i8 1)
+; CHECK-NEXT:    [[IV_NEXT]] = extractvalue { i8, i1 } [[OV]], 0
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %ov = call { i8, i1 } @llvm.uadd.with.overflow.i8(i8 %iv, i8 1)
+  %iv.next = extractvalue { i8, i1 } %ov, 0
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @header_tested_ult_symbolic_start(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @header_tested_ult_symbolic_start(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE_U:%.*]] = icmp ule i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE_U]])
+; CHECK-NEXT:    [[PRE_S:%.*]] = icmp sle i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE_S]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[T_2:%.*]] = icmp sle i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre.u = icmp ule i8 %start, %n
+  call void @llvm.assume(i1 %pre.u)
+  %pre.s = icmp sle i8 %start, %n
+  call void @llvm.assume(i1 %pre.s)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %t.2 = icmp sle i8 %iv, %n
+  call void @use(i1 %t.2)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @latch_tested_slt_unsigned_bound(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @latch_tested_slt_unsigned_bound(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp ule i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    br label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp ule i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  br label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp slt i8 %iv, %n
+  br i1 %cmp, label %loop.header, label %exit
+
+exit:
+  ret void
+}
+
+define void @header_tested_postinc_ult(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @header_tested_postinc_ult(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp ult i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[T_2:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp ult i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ult i8 %iv, %n
+  call void @use(i1 %t.1)
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp ult i8 %iv.next, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  br label %loop.header
+
+exit:
+  %t.2 = icmp ult i8 %iv, %n
+  call void @use(i1 %t.2)
+  ret void
+}
+
+define void @header_tested_postinc_slt(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @header_tested_postinc_slt(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp slt i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    [[PRE_2:%.*]] = icmp sge i8 [[START]], 0
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE_2]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[T_2:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    [[T_3:%.*]] = icmp slt i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_3]])
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp slt i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  %pre.2 = icmp sge i8 %start, 0
+  call void @llvm.assume(i1 %pre.2)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp slt i8 %iv, %n
+  call void @use(i1 %t.1)
+  %t.2 = icmp ult i8 %iv, %n
+  call void @use(i1 %t.2)
+  %iv.next = add nsw i8 %iv, 1
+  %cmp = icmp slt i8 %iv.next, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  br label %loop.header
+
+exit:
+  %t.3 = icmp slt i8 %iv, %n
+  call void @use(i1 %t.3)
+  ret void
+}
+
+define void @neg_header_tested_ult_start_only_unsigned_below(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @neg_header_tested_ult_start_only_unsigned_below(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp ule i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp sle i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp ule i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %t.1)
+  %c.1 = icmp sle i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_latch_tested_slt_unsigned_bound_start_unknown(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @neg_latch_tested_slt_unsigned_bound_start_unknown(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sle i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    br label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_HEADER]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp sle i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  br label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp slt i8 %iv, %n
+  br i1 %cmp, label %loop.header, label %exit
+
+exit:
+  ret void
+}
+
+define void @neg_header_tested_postinc_ult_start_unknown(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @neg_header_tested_postinc_ult_start_unknown(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ult i8 %iv, %n
+  call void @use(i1 %c.1)
+  %iv.next = add i8 %iv, 1
+  %cmp = icmp ult i8 %iv.next, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_header_tested_ult_or_continue(i8 %n, i1 %c) {
+; CHECK-LABEL: define void @neg_header_tested_ult_or_continue(
+; CHECK-SAME: i8 [[N:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[C]]
+; CHECK-NEXT:    br i1 [[OR]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ult i8 %iv, %n
+  %or = or i1 %cmp, %c
+  br i1 %or, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+; The step is 2, so iv can step past %n.
+define void @neg_header_tested_ult_step_2(i8 %n) {
+; CHECK-LABEL: define void @neg_header_tested_ult_step_2(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 2
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 2
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_header_tested_ule(i8 %n) {
+; CHECK-LABEL: define void @neg_header_tested_ule(
+; CHECK-SAME: i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ule i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_latch_tested_ne_start_unknown(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @neg_latch_tested_ne_start_unknown(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    br label %[[LOOP_LATCH]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    [[EC:%.*]] = icmp eq i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[EC]], label %[[EXIT:.*]], label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  br label %loop.latch
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  %ec = icmp eq i8 %iv, %n
+  br i1 %ec, label %exit, label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_header_tested_ne_start_only_signed_below(i8 %start, i8 %n) {
+; CHECK-LABEL: define void @neg_header_tested_ne_start_only_signed_below(
+; CHECK-SAME: i8 [[START:%.*]], i8 [[N:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    [[PRE:%.*]] = icmp sle i8 [[START]], [[N]]
+; CHECK-NEXT:    call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[T_1:%.*]] = icmp sle i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  %pre = icmp sle i8 %start, %n
+  call void @llvm.assume(i1 %pre)
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ %start, %entry ], [ %iv.next, %loop.latch ]
+  %t.1 = icmp sle i8 %iv, %n
+  call void @use(i1 %t.1)
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ne i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_compare_in_middle_block(i8 %n, i1 %c) {
+; CHECK-LABEL: define void @neg_compare_in_middle_block(
+; CHECK-SAME: i8 [[N:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    br i1 [[C]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_BODY]]:
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT]]
+; CHECK:       [[LOOP_LATCH]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %loop.latch ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  br i1 %c, label %loop.body, label %exit
+
+loop.body:
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.latch, label %exit
+
+loop.latch:
+  %iv.next = add i8 %iv, 1
+  br label %loop.header
+
+exit:
+  ret void
+}
+
+define void @neg_two_latches(i8 %n, i1 %c) {
+; CHECK-LABEL: define void @neg_two_latches(
+; CHECK-SAME: i8 [[N:%.*]], i1 [[C:%.*]]) {
+; CHECK-NEXT:  [[ENTRY:.*]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
+; CHECK:       [[LOOP_HEADER]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LATCH_1:.*]] ], [ [[IV_NEXT]], %[[LATCH_2:.*]] ]
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
+; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_BODY:.*]], label %[[EXIT:.*]]
+; CHECK:       [[LOOP_BODY]]:
+; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
+; CHECK-NEXT:    br i1 [[C]], label %[[LATCH_1]], label %[[LATCH_2]]
+; CHECK:       [[LATCH_1]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[LATCH_2]]:
+; CHECK-NEXT:    br label %[[LOOP_HEADER]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    ret void
+;
+entry:
+  br label %loop.header
+
+loop.header:
+  %iv = phi i8 [ 0, %entry ], [ %iv.next, %latch.1 ], [ %iv.next, %latch.2 ]
+  %c.1 = icmp ule i8 %iv, %n
+  call void @use(i1 %c.1)
+  %cmp = icmp ult i8 %iv, %n
+  br i1 %cmp, label %loop.body, label %exit
+
+loop.body:
+  %iv.next = add i8 %iv, 1
+  br i1 %c, label %latch.1, label %latch.2
+
+latch.1:
+  br label %loop.header
+
+latch.2:
+  br label %loop.header
+
+exit:
+  ret void
+}

>From fe8c0e3402fff5d67d1766938640d14acf8c9891 Mon Sep 17 00:00:00 2001
From: Florian Hahn <flo at fhahn.com>
Date: Thu, 24 Sep 2026 15:16:06 +0100
Subject: [PATCH 2/2] [ConstraintElim] Bound IVs by compares of the phi in
 header/latch.

Extend addInfoForInductions to add bounds for IVs when the phi is
compared in the header or latch:

In that case, every iteration taking the backedge checked
PN ContinuePred B, which guarantees PN != B for NE and LT predicates.
For an increment by one, PN != B together with StartValue <= B (added
precondition) imply PN <= B.

Alive2 Proofs:
 * icmp ult in header: https://alive2.llvm.org/ce/z/x37Uvm
 * icmp slt in header: https://alive2.llvm.org/ce/z/ibnn5k
 * icmp ne in header: https://alive2.llvm.org/ce/z/wLRfZe
---
 .../Scalar/ConstraintElimination.cpp          | 48 ++++++++++----
 ...on-condition-in-loop-exit-latch-counted.ll |  5 +-
 .../induction-phi-compare-header-bound.ll     | 66 +++++++------------
 .../induction-symbolic-start-postinc.ll       |  3 +-
 .../loops-bottom-tested-base.ll               | 10 ++-
 .../loops-header-tested-base.ll               |  8 +--
 .../monotonic-int-phis.ll                     |  3 +-
 ...facts-to-unsigned-is-known-non-negative.ll |  3 +-
 .../PhaseOrdering/runtime-check-removal.ll    | 15 ++---
 9 files changed, 73 insertions(+), 88 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index 0d1dae4e667c0..a5341bd2a6fe9 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1241,15 +1241,6 @@ void State::addInfoForInductions(BasicBlock &BB) {
       !SE.isSCEVable(PN->getType()))
     return;
 
-  // For latch conditions, we need to inject the condition that holds for the
-  // next iteration into the header. We limit to post-inc conditions, for which
-  // an original PN + Step != B condition results in a PN < B constraint in the
-  // header, which also holds for the next loop iteration. This would no longer
-  // be correct if the post-inc handling would inject a more precise PN + Step <
-  // B constraint instead.
-  if (&BB == Latch && !IncStep)
-    return;
-
   bool ContinueOnTrue =
       Pred == CmpInst::ICMP_NE || ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred);
   CmpInst::Predicate ContinuePred =
@@ -1270,16 +1261,42 @@ void State::addInfoForInductions(BasicBlock &BB) {
 
   auto [StartValue, Backedge] = getStartAndBackedgeValue(*PN, LoopPred);
   DomTreeNode *DTN = DT.getNode(InLoopSucc);
+  DomTreeNode *HeaderDTN = DT.getNode(Header);
+
+  // BB is the header or the latch, so every iteration taking the backedge
+  // checked PN ContinuePred B, which guarantees PN != B for NE and LT
+  // predicates. For an increment by one, PN != B together with StartValue <= B
+  // (added precondition) imply PN <= B.
+  const APInt *Step;
+  bool HasHeaderBound =
+      !IncStep && match(Backedge, m_IncrementOf(m_Specific(PN), Step)) &&
+      Step->isOne() &&
+      (ContinuePred == CmpInst::ICMP_NE || ICmpInst::isLT(ContinuePred));
+  if (HasHeaderBound) {
+    for (CmpInst::Predicate BoundPred : {CmpInst::ICMP_ULE, CmpInst::ICMP_SLE})
+      WorkList.push_back(FactOrCheck::getConditionFact(
+          HeaderDTN, BoundPred, PN, B, ConditionTy(BoundPred, StartValue, B)));
+  }
+
+  // For latch conditions, we need to inject the condition that holds for the
+  // next iteration into the header. We limit to post-inc conditions, for which
+  // an original PN + Step != B condition results in a PN < B constraint in the
+  // header, which also holds for the next loop iteration. This would no longer
+  // be correct if the post-inc handling would inject a more precise PN + Step <
+  // B constraint instead.
+  if (&BB == Latch && !IncStep)
+    return;
 
   if (ICmpInst::isRelational(ContinuePred)) {
     if (A != Backedge)
       return;
 
-    // The latch condition ensures ContinuePred holds in the header on each
-    // iteration other than the first. Together with a precondition on the start
-    // value (StartValue ContinuePred B), we can add B as bound of PN.
+    // The condition ensures ContinuePred holds in the header on each iteration
+    // other than the first. Together with a precondition on the start value
+    // (StartValue ContinuePred B), we can add B as bound of PN.
     WorkList.push_back(FactOrCheck::getConditionFact(
-        DTN, ContinuePred, PN, B, ConditionTy(ContinuePred, StartValue, B)));
+        HeaderDTN, ContinuePred, PN, B,
+        ConditionTy(ContinuePred, StartValue, B)));
 
     // A signed bound can be translated to the unsigned system if PN is signed
     // non-decreasing (StartValue s<= PN s< B) and StartValue u< B holds.
@@ -1292,7 +1309,7 @@ void State::addInfoForInductions(BasicBlock &BB) {
       if (Info.Signed && !Info.Decreasing) {
         CmpInst::Predicate UPred = ICmpInst::getUnsignedPredicate(ContinuePred);
         WorkList.push_back(FactOrCheck::getConditionFact(
-            DTN, UPred, PN, B, ConditionTy(UPred, StartValue, B)));
+            HeaderDTN, UPred, PN, B, ConditionTy(UPred, StartValue, B)));
       }
     }
 
@@ -1398,6 +1415,9 @@ void State::addInfoForInductions(BasicBlock &BB) {
   // must be u<= B, as other exits may only exit earlier.
   assert(!StepOffset->isNegative() && "induction must be increasing");
   assert(ContinuePred == CmpInst::ICMP_NE && "unsupported predicate");
+  // The header bound already implies PN u<= B in the exits.
+  if (HasHeaderBound)
+    return;
   SmallVector<BasicBlock *> ExitBBs;
   L->getExitBlocks(ExitBBs);
   for (BasicBlock *EB : ExitBBs) {
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
index 506fa5c85f57e..f3d2b1e201e60 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll
@@ -58,7 +58,7 @@ exit.1:
 }
 
 ; The latch compares the phi (%iv == %n), not the post-increment. The header
-; check is not removable.
+; check is removable, as %iv u<= %n holds in the header.
 define i1 @latch_counted_phi(ptr %p, i64 %n, i64 %lim) {
 ; CHECK-LABEL: define i1 @latch_counted_phi(
 ; CHECK-SAME: ptr [[P:%.*]], i64 [[N:%.*]], i64 [[LIM:%.*]]) {
@@ -72,8 +72,7 @@ define i1 @latch_counted_phi(ptr %p, i64 %n, i64 %lim) {
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 0, %[[PH]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
 ; CHECK-NEXT:    [[OFF:%.*]] = shl nuw nsw i64 [[IV]], 2
-; CHECK-NEXT:    [[RC:%.*]] = icmp ult i64 [[OFF]], [[LIM]]
-; CHECK-NEXT:    br i1 [[RC]], label %[[LOOP_LATCH]], label %[[EXIT_1]]
+; CHECK-NEXT:    br i1 true, label %[[LOOP_LATCH]], label %[[EXIT_1]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    [[GEP:%.*]] = getelementptr i8, ptr [[P]], i64 [[OFF]]
 ; CHECK-NEXT:    store i8 0, ptr [[GEP]], align 1
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll b/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
index 6774e03c02c1c..b0efabc7d3520 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
@@ -10,8 +10,7 @@ define void @header_tested_ult(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i8 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
@@ -20,8 +19,7 @@ define void @header_tested_ult(i8 %n) {
 ; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
 ; CHECK-NEXT:    br label %[[LOOP_HEADER]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[T_2:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -53,8 +51,7 @@ define void @header_tested_ugt_commuted(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ugt i8 [[N]], [[IV]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
@@ -92,10 +89,8 @@ define void @latch_tested_ne_symbolic_start(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
-; CHECK-NEXT:    [[T_2:%.*]] = icmp sle i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i8 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    br label %[[LOOP_LATCH]]
@@ -141,10 +136,8 @@ define void @header_latch_tested_slt(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP:.*]]
 ; CHECK:       [[LOOP]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp sle i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
-; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[C_1]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP]], label %[[EXIT:.*]]
@@ -177,8 +170,7 @@ define void @header_tested_ne_and(i8 %n, i1 %c) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
 ; CHECK-NEXT:    [[AND:%.*]] = and i1 [[CMP]], [[C]]
 ; CHECK-NEXT:    br i1 [[AND]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
@@ -214,8 +206,7 @@ define void @header_tested_uge_or(i8 %n, i1 %c) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp uge i8 [[IV]], [[N]]
 ; CHECK-NEXT:    [[OR:%.*]] = or i1 [[CMP]], [[C]]
 ; CHECK-NEXT:    br i1 [[OR]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
@@ -251,8 +242,7 @@ define void @header_tested_ne_exit_use(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
@@ -289,8 +279,7 @@ define void @latch_tested_ult(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
@@ -324,8 +313,7 @@ define void @header_tested_ult_uadd_with_overflow(i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
@@ -365,10 +353,8 @@ define void @header_tested_ult_symbolic_start(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
-; CHECK-NEXT:    [[T_2:%.*]] = icmp sle i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
@@ -410,8 +396,7 @@ define void @latch_tested_slt_unsigned_bound(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    br label %[[LOOP_LATCH]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
@@ -449,16 +434,14 @@ define void @header_tested_postinc_ult(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ult i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[IV_NEXT]] = add i8 [[IV]], 1
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV_NEXT]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    br label %[[LOOP_HEADER]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[T_2:%.*]] = icmp ult i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -494,18 +477,15 @@ define void @header_tested_postinc_slt(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp slt i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
-; CHECK-NEXT:    [[T_2:%.*]] = icmp ult i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_2]])
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i8 [[IV_NEXT]], [[N]]
 ; CHECK-NEXT:    br i1 [[CMP]], label %[[LOOP_LATCH]], label %[[EXIT:.*]]
 ; CHECK:       [[LOOP_LATCH]]:
 ; CHECK-NEXT:    br label %[[LOOP_HEADER]]
 ; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    [[T_3:%.*]] = icmp slt i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_3]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    ret void
 ;
 entry:
@@ -543,8 +523,7 @@ define void @neg_header_tested_ult_start_only_unsigned_below(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp sle i8 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ult i8 [[IV]], [[N]]
@@ -803,8 +782,7 @@ define void @neg_header_tested_ne_start_only_signed_below(i8 %start, i8 %n) {
 ; CHECK-NEXT:    br label %[[LOOP_HEADER:.*]]
 ; CHECK:       [[LOOP_HEADER]]:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i8 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp sle i8 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ule i8 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    [[CMP:%.*]] = icmp ne i8 [[IV]], [[N]]
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-symbolic-start-postinc.ll b/llvm/test/Transforms/ConstraintElimination/induction-symbolic-start-postinc.ll
index 0f6bce64a7339..e2b642f5453c0 100644
--- a/llvm/test/Transforms/ConstraintElimination/induction-symbolic-start-postinc.ll
+++ b/llvm/test/Transforms/ConstraintElimination/induction-symbolic-start-postinc.ll
@@ -589,8 +589,7 @@ define void @phi_compare_symbolic_start_header_is_latch(i32 %start, i32 %n) {
 ; CHECK-NEXT:    call void @use(i1 [[C_0]])
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp slt i32 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
-; CHECK-NEXT:    [[C_2:%.*]] = icmp ule i32 [[IV]], [[N]]
-; CHECK-NEXT:    call void @use(i1 [[C_2]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_3:%.*]] = icmp sle i32 [[IV]], [[N]]
 ; CHECK-NEXT:    call void @use(i1 [[C_3]])
 ; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
diff --git a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll
index 6510fbb9d260f..a6031316ee4c3 100644
--- a/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll
+++ b/llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll
@@ -9,11 +9,10 @@ define void @loop_iv_cond_variable_bound(i32 %n) {
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i32 [[IV]], [[N:%.*]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ugt i32 [[IV]], 1
 ; CHECK-NEXT:    call void @use(i1 [[C_2]])
@@ -54,8 +53,7 @@ define void @loop_iv_cond_constant_bound() {
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i32 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], 2
@@ -101,7 +99,7 @@ define void @eq_exit_check_constant_int() {
 ; CHECK-NEXT:    br label [[LOOP:%.*]]
 ; CHECK:       loop:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i64 [ 1, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP]] ]
-; CHECK-NEXT:    [[IV_NEXT]] = add nuw i64 [[IV]], 1
+; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i64 [[IV]], 1
 ; CHECK-NEXT:    call void @clobber()
 ; CHECK-NEXT:    [[C:%.*]] = icmp eq i64 [[IV]], 2
 ; CHECK-NEXT:    br i1 [[C]], label [[EXIT:%.*]], label [[LOOP]]
diff --git a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll
index 5454d74a37d62..f74714d13ea0c 100644
--- a/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll
+++ b/llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll
@@ -292,11 +292,10 @@ define void @loop_iv_cond_variable_bound(i32 %n) {
 ; CHECK-NEXT:    br label [[LOOP_HEADER:%.*]]
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i32 [[IV]], [[N:%.*]]
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
-; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], [[N]]
+; CHECK-NEXT:    call void @use(i1 true)
+; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], [[N:%.*]]
 ; CHECK-NEXT:    call void @use(i1 [[C_1]])
 ; CHECK-NEXT:    [[C_2:%.*]] = icmp ugt i32 [[IV]], 1
 ; CHECK-NEXT:    call void @use(i1 [[C_2]])
@@ -355,8 +354,7 @@ define void @loop_iv_cond_constant_bound() {
 ; CHECK-NEXT:    br label [[LOOP_HEADER:%.*]]
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, [[ENTRY:%.*]] ], [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ]
-; CHECK-NEXT:    [[T_1:%.*]] = icmp ule i32 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[T_1]])
+; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    call void @use(i1 true)
 ; CHECK-NEXT:    [[C_1:%.*]] = icmp ult i32 [[IV]], 2
diff --git a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis.ll b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis.ll
index 8adc886f698df..d4219b04fbf97 100644
--- a/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis.ll
+++ b/llvm/test/Transforms/ConstraintElimination/monotonic-int-phis.ll
@@ -66,8 +66,7 @@ define void @test_remove_check_with_incrementing_integer_induction(i8 %len.n, i1
 ; CHECK:       loop.header:
 ; CHECK-NEXT:    [[IV:%.*]] = phi i16 [ [[IV_NEXT:%.*]], [[LOOP_LATCH:%.*]] ], [ 0, [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    [[C:%.*]] = icmp ne i16 [[IV]], [[LEN]]
-; CHECK-NEXT:    [[T_2:%.*]] = icmp ult i16 [[IV]], [[A]]
-; CHECK-NEXT:    [[OR_COND:%.*]] = and i1 [[C]], [[T_2]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = and i1 [[C]], true
 ; CHECK-NEXT:    br i1 [[OR_COND]], label [[LOOP_LATCH]], label [[EXIT]]
 ; CHECK:       loop.latch:
 ; CHECK-NEXT:    call void @use(i16 [[IV]])
diff --git a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
index d415845ee5a5d..1b34c5c0501ba 100644
--- a/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
+++ b/llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll
@@ -65,8 +65,7 @@ define void @iv_known_non_negative_constant_trip_count_no_nsw_flag(i8 %N) {
 ; CHECK-NEXT:    call void @use(i1 [[F_1]])
 ; CHECK-NEXT:    [[F_2:%.*]] = icmp sle i8 [[N]], [[IV]]
 ; CHECK-NEXT:    call void @use(i1 [[F_2]])
-; CHECK-NEXT:    [[C_0:%.*]] = icmp ugt i8 [[IV]], 2
-; CHECK-NEXT:    call void @use(i1 [[C_0]])
+; CHECK-NEXT:    call void @use(i1 false)
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i8 [[IV]], 1
 ; CHECK-NEXT:    br label [[LOOP_HEADER]]
 ; CHECK:       exit.1:
diff --git a/llvm/test/Transforms/PhaseOrdering/runtime-check-removal.ll b/llvm/test/Transforms/PhaseOrdering/runtime-check-removal.ll
index 971267cc74b65..020b974d55a60 100644
--- a/llvm/test/Transforms/PhaseOrdering/runtime-check-removal.ll
+++ b/llvm/test/Transforms/PhaseOrdering/runtime-check-removal.ll
@@ -12,19 +12,14 @@ define void @test_remove_check_with_incrementing_integer_induction(i16 %start, i
 ; CHECK-NEXT:    [[LEN:%.*]] = zext i8 [[LEN_N:%.*]] to i16
 ; CHECK-NEXT:    [[LEN_NEG_NOT:%.*]] = icmp ugt i16 [[A:%.*]], [[LEN]]
 ; CHECK-NEXT:    [[C1:%.*]] = icmp ne i8 [[LEN_N]], 0
-; CHECK-NEXT:    [[OR_COND3:%.*]] = and i1 [[LEN_NEG_NOT]], [[C1]]
-; CHECK-NEXT:    br i1 [[OR_COND3]], label [[LOOP_LATCH_PREHEADER:%.*]], label [[EXIT:%.*]]
-; CHECK:       loop.latch.preheader:
-; CHECK-NEXT:    [[TMP0:%.*]] = add i16 [[A]], -1
-; CHECK-NEXT:    [[TMP1:%.*]] = add nsw i16 [[LEN]], -1
-; CHECK-NEXT:    [[UMIN:%.*]] = tail call i16 @llvm.umin.i16(i16 [[TMP0]], i16 [[TMP1]])
-; CHECK-NEXT:    br label [[LOOP_LATCH:%.*]]
+; CHECK-NEXT:    [[OR_COND:%.*]] = and i1 [[LEN_NEG_NOT]], [[C1]]
+; CHECK-NEXT:    br i1 [[OR_COND]], label [[LOOP_LATCH:%.*]], label [[EXIT:%.*]]
 ; CHECK:       loop.latch:
-; CHECK-NEXT:    [[IV2:%.*]] = phi i16 [ [[IV_NEXT:%.*]], [[LOOP_LATCH]] ], [ 0, [[LOOP_LATCH_PREHEADER]] ]
+; CHECK-NEXT:    [[IV2:%.*]] = phi i16 [ [[IV_NEXT:%.*]], [[LOOP_LATCH]] ], [ 0, [[ENTRY:%.*]] ]
 ; CHECK-NEXT:    tail call void @use(i16 [[IV2]])
 ; CHECK-NEXT:    [[IV_NEXT]] = add nuw nsw i16 [[IV2]], 1
-; CHECK-NEXT:    [[EXITCOND_NOT:%.*]] = icmp eq i16 [[IV2]], [[UMIN]]
-; CHECK-NEXT:    br i1 [[EXITCOND_NOT]], label [[EXIT]], label [[LOOP_LATCH]]
+; CHECK-NEXT:    [[C_NOT:%.*]] = icmp eq i16 [[IV_NEXT]], [[LEN]]
+; CHECK-NEXT:    br i1 [[C_NOT]], label [[EXIT]], label [[LOOP_LATCH]]
 ; CHECK:       exit:
 ; CHECK-NEXT:    ret void
 ;



More information about the llvm-commits mailing list