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

via llvm-commits llvm-commits at lists.llvm.org
Thu Sep 24 14:05:29 PDT 2026


llvmorg-github-actions[bot] wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-llvm-transforms

Author: Florian Hahn (fhahn)

<details>
<summary>Changes</summary>

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

---

Patch is 43.26 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/226297.diff


9 Files Affected:

- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+34-14) 
- (modified) llvm/test/Transforms/ConstraintElimination/induction-condition-in-loop-exit-latch-counted.ll (+4-5) 
- (added) llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll (+901) 
- (modified) llvm/test/Transforms/ConstraintElimination/induction-symbolic-start-postinc.ll (+1-2) 
- (modified) llvm/test/Transforms/ConstraintElimination/loops-bottom-tested-base.ll (+4-6) 
- (modified) llvm/test/Transforms/ConstraintElimination/loops-header-tested-base.ll (+3-5) 
- (modified) llvm/test/Transforms/ConstraintElimination/monotonic-int-phis.ll (+1-2) 
- (modified) llvm/test/Transforms/ConstraintElimination/transfer-signed-facts-to-unsigned-is-known-non-negative.ll (+1-2) 
- (modified) llvm/test/Transforms/PhaseOrdering/runtime-check-removal.ll (+5-10) 


``````````diff
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 31ae62c564dd6..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,9 +58,9 @@ 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(
+; 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:%.*]]) {
 ; CHECK-NEXT:  [[ENTRY:.*:]]
 ; CHECK-NEXT:    [[POS:%.*]] = icmp sgt i64 [[N]], 0
@@ -72,8 +72,7 @@ define i1 @latch_counted_phi_not_removable(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
new file mode 100644
index 0000000000000..b0efabc7d3520
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/induction-phi-compare-header-bound.ll
@@ -0,0 +1,901 @@
+; 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:    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]]
+; 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)
+  %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:    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]]:
+; 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:    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]]
+; 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:    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:.*]]
+; 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:    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:.*]]
+; 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:    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]]
+; 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:    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]]:
+; 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:    call void @use(i1 true)
+; 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:    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]]:
+; 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:    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]]:
+; 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:    call void @use(i1 true)
+; 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 %[[LO...
[truncated]

``````````

</details>


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


More information about the llvm-commits mailing list