[llvm] [ConstraintElim] Derive IV bound from an relation predicate in latch. (PR #220107)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Aug 31 15:01:07 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-llvm-transforms
Author: Florian Hahn (fhahn)
<details>
<summary>Changes</summary>
A loop whose latch has a relational predicate of the IV post-increment
as exit condition steps *past* the bound rather than landing on it, so the
existing EQ/NE step reasoning does not apply.
But the condition (IV+Step Pred N) holds for the induction phi each iteration
after the first (when the header is entered via the guarded latch).
In the first iteration, the induction phi's value is its start value.
Together with a precondition (Start Pred N), (IV Pred N) hold in each
iteration.
Add this as fact enables removing a few redundant conditions in practice:
https://github.com/dtcxzyw/llvm-opt-benchmark-nightly/pull/1132.
Compile-time impact is in the noise:
https://llvm-compile-time-tracker.com/compare.php?from=955f2beedea61b1a5494d0b0df684f0efde12bbe&to=b5eb118e0426552dfddf1fa65e002842326b0a8a&stat=instructions:u
Alive2 Proofs for ULT/SLT: https://alive2.llvm.org/ce/z/AwHN2F
---
Patch is 27.99 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/220107.diff
2 Files Affected:
- (modified) llvm/lib/Transforms/Scalar/ConstraintElimination.cpp (+24-11)
- (added) llvm/test/Transforms/ConstraintElimination/induction-relational-predicate-latch.ll (+715)
``````````diff
diff --git a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
index ef324c40e44a6..293fc25269815 100644
--- a/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/ConstraintElimination.cpp
@@ -1109,13 +1109,12 @@ void State::addInfoForInductions(BasicBlock &BB) {
if (&BB == Latch && !IncStep)
return;
- BasicBlock *InLoopSucc = nullptr;
- if (Pred == CmpInst::ICMP_NE)
- InLoopSucc = cast<CondBrInst>(BB.getTerminator())->getSuccessor(0);
- else if (Pred == CmpInst::ICMP_EQ)
- InLoopSucc = cast<CondBrInst>(BB.getTerminator())->getSuccessor(1);
- else
- return;
+ bool ContinueOnTrue =
+ Pred == CmpInst::ICMP_NE || ICmpInst::isLT(Pred) || ICmpInst::isLE(Pred);
+ CmpInst::Predicate ContinuePred =
+ ContinueOnTrue ? Pred.dropSameSign() : CmpInst::getInversePredicate(Pred);
+ BasicBlock *InLoopSucc = cast<CondBrInst>(BB.getTerminator())
+ ->getSuccessor(ContinueOnTrue ? 0 : 1);
if (!L->contains(InLoopSucc) || !L->isLoopExiting(&BB) || InLoopSucc == &BB)
return;
@@ -1125,6 +1124,23 @@ void State::addInfoForInductions(BasicBlock &BB) {
return;
auto [StartValue, Backedge] = getStartAndBackedgeValue(*PN, LoopPred);
+ DomTreeNode *DTN = DT.getNode(InLoopSucc);
+
+ 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 we 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)));
+
+ // A relational latch steps past B rather than landing on it, so none of the
+ // reasoning below applies.
+ return;
+ }
+
const APInt *StepOffset = nullptr;
const SCEV *StartSCEV = nullptr;
if (match(Backedge, m_c_Add(m_Specific(PN), m_APInt(StepOffset)))) {
@@ -1138,8 +1154,6 @@ void State::addInfoForInductions(BasicBlock &BB) {
return;
}
- DomTreeNode *DTN = DT.getNode(InLoopSucc);
-
// If we looked through `PN + C`, only derive facts when that add is
// really the induction's post-increment or post-decrement.
if (IncStep && *IncStep != *StepOffset)
@@ -1235,8 +1249,7 @@ void State::addInfoForInductions(BasicBlock &BB) {
// blocks. When exiting either with EQ or NE, we know that the induction value
// must be u<= B, as other exits may only exit earlier.
assert(!StepOffset->isNegative() && "induction must be increasing");
- assert((Pred == CmpInst::ICMP_EQ || Pred == CmpInst::ICMP_NE) &&
- "unsupported predicate");
+ assert(ContinuePred == CmpInst::ICMP_NE && "unsupported predicate");
SmallVector<BasicBlock *> ExitBBs;
L->getExitBlocks(ExitBBs);
for (BasicBlock *EB : ExitBBs) {
diff --git a/llvm/test/Transforms/ConstraintElimination/induction-relational-predicate-latch.ll b/llvm/test/Transforms/ConstraintElimination/induction-relational-predicate-latch.ll
new file mode 100644
index 0000000000000..abb3648b34ed6
--- /dev/null
+++ b/llvm/test/Transforms/ConstraintElimination/induction-relational-predicate-latch.ll
@@ -0,0 +1,715 @@
+; 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 @symbolic_start_and_stride_slt_latch(i64 %start, i64 %step, i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @symbolic_start_and_stride_slt_latch(
+; CHECK-SAME: i64 [[START:%.*]], i64 [[STEP:%.*]], i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp slt i64 [[START]], [[N]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], [[STEP]]
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp slt i64 %start, %n
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add i64 %iv, %step
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @symbolic_start_and_stride_ult_latch(i64 %start, i64 %step, i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @symbolic_start_and_stride_ult_latch(
+; CHECK-SAME: i64 [[START:%.*]], i64 [[STEP:%.*]], i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp ult i64 [[START]], [[N]]
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ [[START]], %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add i64 [[IV]], [[STEP]]
+; CHECK-NEXT: [[EC:%.*]] = icmp ult i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp ult i64 %start, %n
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ %start, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add i64 %iv, %step
+ %ec = icmp ult i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @stride2_slt_latch(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_slt_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @stride2_slt_header_is_latch(i64 %n) {
+; CHECK-LABEL: define void @stride2_slt_header_is_latch(
+; CHECK-SAME: i64 [[N:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop
+
+loop:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %loop, label %exit
+
+exit:
+ ret void
+}
+
+define void @stride2_sge_latch(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_sge_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sge i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+; The latch tests iv + 2, but the induction advances by 4.
+define void @neg_compared_value_not_backedge(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_compared_value_not_backedge(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[PROBE:%.*]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 4
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[PROBE]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %probe = add nsw i64 %iv, 2
+ %iv.next = add nsw i64 %iv, 4
+ %ec = icmp slt i64 %probe, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+; Test where bound is not loop-invariant.
+define void @neg_variant_bound(ptr %p, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_variant_bound(
+; CHECK-SAME: ptr [[P:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[N:%.*]] = load i64, ptr [[P]], align 4
+; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %n = load i64, ptr %p
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @negative_start_unsigned_precondition_blocks(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @negative_start_unsigned_precondition_blocks(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ -4, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: [[C_U:%.*]] = icmp ult i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C_U]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sgt i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ -4, %entry ], [ %iv.next, %loop.latch ]
+ %c.s = icmp slt i64 %iv, %n
+ call void @use(i1 %c.s)
+ %c.u = icmp ult i64 %iv, %n
+ call void @use(i1 %c.u)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+; Loop continues while iv + 2 s>= n.
+define void @neg_wrong_polarity(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @neg_wrong_polarity(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: [[C:%.*]] = icmp slt i64 [[IV]], [[N]]
+; CHECK-NEXT: call void @use(i1 [[C]])
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp slt i64 [[IV_NEXT]], [[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 i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp slt i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp slt i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @stride2_ult_latch(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_ult_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp ult i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp ne i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nuw i64 %iv, 2
+ %ec = icmp ult i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @stride2_uge_latch(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_uge_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp ne i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp uge i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[EXIT]], label %[[LOOP_HEADER]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp ne i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp ult i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nuw i64 %iv, 2
+ %ec = icmp uge i64 %iv.next, %n
+ br i1 %ec, label %exit, label %loop.header
+
+exit:
+ ret void
+}
+
+define void @stride2_sle_latch(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @stride2_sle_latch(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sge i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nsw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icmp sle i64 [[IV_NEXT]], [[N]]
+; CHECK-NEXT: br i1 [[EC]], label %[[LOOP_HEADER]], label %[[EXIT]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: ret void
+;
+entry:
+ %pre = icmp sge i64 %n, 0
+ call void @llvm.assume(i1 %pre)
+ br label %loop.header
+
+loop.header:
+ %iv = phi i64 [ 0, %entry ], [ %iv.next, %loop.latch ]
+ %c = icmp sle i64 %iv, %n
+ call void @use(i1 %c)
+ br i1 %header.ec, label %exit, label %loop.latch
+
+loop.latch:
+ %iv.next = add nsw i64 %iv, 2
+ %ec = icmp sle i64 %iv.next, %n
+ br i1 %ec, label %loop.header, label %exit
+
+exit:
+ ret void
+}
+
+define void @unsigned_latch_signed_fact(i64 %n, i1 %header.ec) {
+; CHECK-LABEL: define void @unsigned_latch_signed_fact(
+; CHECK-SAME: i64 [[N:%.*]], i1 [[HEADER_EC:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*]]:
+; CHECK-NEXT: [[PRE:%.*]] = icmp sgt i64 [[N]], 0
+; CHECK-NEXT: call void @llvm.assume(i1 [[PRE]])
+; CHECK-NEXT: br label %[[LOOP_HEADER:.*]]
+; CHECK: [[LOOP_HEADER]]:
+; CHECK-NEXT: [[IV:%.*]] = phi i64 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP_LATCH:.*]] ]
+; CHECK-NEXT: call void @use(i1 true)
+; CHECK-NEXT: br i1 [[HEADER_EC]], label %[[EXIT:.*]], label %[[LOOP_LATCH]]
+; CHECK: [[LOOP_LATCH]]:
+; CHECK-NEXT: [[IV_NEXT]] = add nuw i64 [[IV]], 2
+; CHECK-NEXT: [[EC:%.*]] = icm...
[truncated]
``````````
</details>
https://github.com/llvm/llvm-project/pull/220107
More information about the llvm-commits
mailing list