[llvm] [LoopSplitUtils] Prevent erasure of non-dead latch compares (PR #214243)

Sean Clarke via llvm-commits llvm-commits at lists.llvm.org
Wed Aug 5 07:54:44 PDT 2026


https://github.com/xarkenz created https://github.com/llvm/llvm-project/pull/214243

In the case where a loop latch gets rewritten after cloning but the original latch compare only has uses outside the loop, the instruction seemingly has no uses since SSA has not yet been reconstructed, and thus is erased. Since it is captured as an escaping value during the initial phase, a use-after-free then occurs in attempting to reconstruct SSA, usually resulting in a crash. Instead, defer erasing the latch compares until after SSA has been reconstructed.

>From b4e832393774e2562480f71613c6d77dc76664f3 Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Tue, 4 Aug 2026 14:06:58 -0500
Subject: [PATCH 1/3] Add baseline tests

---
 .../LoopSplit/liveout-latchcmp-crash.ll       | 20 ++++++
 llvm/test/Transforms/LoopSplit/liveout.ll     | 64 +++++++++++++++++++
 2 files changed, 84 insertions(+)
 create mode 100644 llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
 create mode 100644 llvm/test/Transforms/LoopSplit/liveout.ll

diff --git a/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll b/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
new file mode 100644
index 0000000000000..0a5d0a8a18089
--- /dev/null
+++ b/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
@@ -0,0 +1,20 @@
+; REQUIRES: asserts
+; RUN: not --crash opt -passes=loop-split-utils -loop-split-points=50 -disable-output < %s
+
+declare void @use(i32)
+
+define i1 @latchcmp_liveout(i32 %n) {
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+  call void @use(i32 %iv)
+  %iv.next = add i32 %iv, 1
+  %cmp = icmp slt i32 %iv.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  %cmp.lcssa = phi i1 [ %cmp, %loop ]
+  ret i1 %cmp.lcssa
+}
diff --git a/llvm/test/Transforms/LoopSplit/liveout.ll b/llvm/test/Transforms/LoopSplit/liveout.ll
new file mode 100644
index 0000000000000..e75ca260cd2c9
--- /dev/null
+++ b/llvm/test/Transforms/LoopSplit/liveout.ll
@@ -0,0 +1,64 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
+
+declare void @use(i32)
+
+define i1 @latchcmp_liveout_extra_use(i32 %n) {
+; CHECK-LABEL: define i1 @latchcmp_liveout_extra_use(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[LS_GUARD0:.*]]:
+; CHECK-NEXT:    [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[N]], i32 1)
+; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i32 [[SMAX]], -1
+; CHECK-NEXT:    [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 49)
+; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i32 0, [[SMIN]]
+; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
+; CHECK:       [[ENTRY]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i32 [[IV_NEXT]], [[SMIN]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    [[CMP_EXT:%.*]] = zext i1 [[CMP]] to i32
+; CHECK-NEXT:    call void @use(i32 [[CMP_EXT]])
+; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    br label %[[LS_GUARD1]]
+; CHECK:       [[LS_GUARD1]]:
+; CHECK-NEXT:    [[CMP5:%.*]] = phi i1 [ [[CMP]], %[[EXIT]] ], [ poison, %[[LS_GUARD0]] ]
+; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i32 50, [[TMP0]]
+; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
+; CHECK:       [[ENTRY_LS1]]:
+; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
+; CHECK:       [[LOOP_LS1]]:
+; CHECK-NEXT:    [[IV_LS1:%.*]] = phi i32 [ 50, %[[ENTRY_LS1]] ], [ [[IV_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV_LS1]])
+; CHECK-NEXT:    [[IV_NEXT_LS1]] = add i32 [[IV_LS1]], 1
+; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i32 [[IV_NEXT_LS1]], [[TMP0]]
+; CHECK-NEXT:    [[CMP_LS1:%.*]] = icmp slt i32 [[IV_NEXT_LS1]], [[N]]
+; CHECK-NEXT:    [[CMP_EXT_LS1:%.*]] = zext i1 [[CMP_LS1]] to i32
+; CHECK-NEXT:    call void @use(i32 [[CMP_EXT_LS1]])
+; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
+; CHECK:       [[LS_EXIT1]]:
+; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
+; CHECK:       [[LS_FINAL_EXIT]]:
+; CHECK-NEXT:    [[CMP4:%.*]] = phi i1 [ [[CMP_LS1]], %[[LS_EXIT1]] ], [ [[CMP5]], %[[LS_GUARD1]] ]
+; CHECK-NEXT:    ret i1 [[CMP4]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+  call void @use(i32 %iv)
+  %iv.next = add i32 %iv, 1
+  %cmp = icmp slt i32 %iv.next, %n
+  %cmp.ext = zext i1 %cmp to i32
+  call void @use(i32 %cmp.ext)
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  %cmp.lcssa = phi i1 [ %cmp, %loop ]
+  ret i1 %cmp.lcssa
+}

>From 01ecef856b9193980b578e285a0d36e27c80dfca Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Wed, 5 Aug 2026 09:38:30 -0500
Subject: [PATCH 2/3] Erase dead latch compares after reconstructing SSA

---
 llvm/lib/Transforms/Utils/LoopSplitUtils.cpp  | 26 ++++++++----
 .../LoopSplit/liveout-latchcmp-crash.ll       | 41 ++++++++++++++++++-
 2 files changed, 59 insertions(+), 8 deletions(-)

diff --git a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
index db07e3f571f66..65474db56e829 100644
--- a/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
+++ b/llvm/lib/Transforms/Utils/LoopSplitUtils.cpp
@@ -113,6 +113,11 @@ struct LoopSplitUtils::SplitState {
   /// Values that must survive across partitions (carried and/or live-out).
   SmallVector<EscapingValue, 8> Escaping;
 
+  /// Latch compares displaced by rewriteLatch() that had no remaining use at
+  /// the time. Once SSA is reconstructed, we can know for certain which ones
+  /// are dead and erase them at that point.
+  SmallVector<ICmpInst *, 4> DanglingLatchCmps;
+
   EscapingValue &addEscaping(Value *Def) { return Escaping.emplace_back(Def); }
 };
 
@@ -319,6 +324,10 @@ bool LoopSplitUtils::split() {
   clonePartitions(S);
   chainPartitions(S);
   reconstructSSA(S);
+  // Strip out any remaining dead latch compares.
+  for (ICmpInst *Cmp : S.DanglingLatchCmps)
+    if (Cmp->use_empty())
+      Cmp->eraseFromParent();
   ExpanderCleaner.markResultUsed();
   return true;
 }
@@ -493,9 +502,11 @@ void LoopSplitUtils::clonePartitions(SplitState &S) {
 }
 
 // Replace a partition's latch test so it iterates only within [start, SelEnd].
-static void rewriteLatch(Loop *PL, Value *IndOp, Value *SelEnd,
-                         BasicBlock *Exit, bool Signed, bool Descending,
-                         bool LatchComparesPHI) {
+// Returns the old latch compare if it is now (seemingly) unused, for the caller
+// to erase if it is still dead after SSA has been rebuilt.
+static ICmpInst *rewriteLatch(Loop *PL, Value *IndOp, Value *SelEnd,
+                              BasicBlock *Exit, bool Signed, bool Descending,
+                              bool LatchComparesPHI) {
   auto *Term = cast<CondBrInst>(PL->getLoopLatch()->getTerminator());
   auto *Cmp = cast<ICmpInst>(Term->getCondition());
   IRBuilder<> B(Cmp);
@@ -518,8 +529,7 @@ static void rewriteLatch(Loop *PL, Value *IndOp, Value *SelEnd,
         /*IsExpected=*/false);
   }
   Term->eraseFromParent();
-  if (Cmp->use_empty())
-    Cmp->eraseFromParent();
+  return Cmp->use_empty() ? Cmp : nullptr;
 }
 
 // Pass 2: emit each partition's guard branch, clamp its latch, wire the
@@ -568,8 +578,10 @@ void LoopSplitUtils::chainPartitions(SplitState &S) {
     }
     GuardTerm->eraseFromParent();
 
-    rewriteLatch(P.SubLoop, P.LatchIndOp, P.SelEnd, P.Exit, InductionIsSigned,
-                 S.Descending, S.LatchComparesPHI);
+    if (ICmpInst *DanglingCmp =
+            rewriteLatch(P.SubLoop, P.LatchIndOp, P.SelEnd, P.Exit,
+                         InductionIsSigned, S.Descending, S.LatchComparesPHI))
+      S.DanglingLatchCmps.push_back(DanglingCmp);
     P.Exit->getTerminator()->setSuccessor(0, MergeAfter);
   }
 
diff --git a/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll b/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
index 0a5d0a8a18089..680c2f43da49a 100644
--- a/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
+++ b/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
@@ -1,9 +1,48 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
 ; REQUIRES: asserts
-; RUN: not --crash opt -passes=loop-split-utils -loop-split-points=50 -disable-output < %s
+; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
 
 declare void @use(i32)
 
 define i1 @latchcmp_liveout(i32 %n) {
+; CHECK-LABEL: define i1 @latchcmp_liveout(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[LS_GUARD0:.*]]:
+; CHECK-NEXT:    [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[N]], i32 1)
+; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i32 [[SMAX]], -1
+; CHECK-NEXT:    [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 49)
+; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i32 0, [[SMIN]]
+; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
+; CHECK:       [[ENTRY]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i32 [[IV_NEXT]], [[SMIN]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    br label %[[LS_GUARD1]]
+; CHECK:       [[LS_GUARD1]]:
+; CHECK-NEXT:    [[CMP5:%.*]] = phi i1 [ [[CMP]], %[[EXIT]] ], [ poison, %[[LS_GUARD0]] ]
+; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i32 50, [[TMP0]]
+; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
+; CHECK:       [[ENTRY_LS1]]:
+; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
+; CHECK:       [[LOOP_LS1]]:
+; CHECK-NEXT:    [[IV_LS1:%.*]] = phi i32 [ 50, %[[ENTRY_LS1]] ], [ [[IV_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV_LS1]])
+; CHECK-NEXT:    [[IV_NEXT_LS1]] = add i32 [[IV_LS1]], 1
+; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i32 [[IV_NEXT_LS1]], [[TMP0]]
+; CHECK-NEXT:    [[CMP_LS1:%.*]] = icmp slt i32 [[IV_NEXT_LS1]], [[N]]
+; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
+; CHECK:       [[LS_EXIT1]]:
+; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
+; CHECK:       [[LS_FINAL_EXIT]]:
+; CHECK-NEXT:    [[CMP4:%.*]] = phi i1 [ [[CMP_LS1]], %[[LS_EXIT1]] ], [ [[CMP5]], %[[LS_GUARD1]] ]
+; CHECK-NEXT:    ret i1 [[CMP4]]
+;
 entry:
   br label %loop
 

>From 3aa8ac7ad87716fbaf0500f3cef9d1832f3539cd Mon Sep 17 00:00:00 2001
From: Sean Clarke <sclarke at tenstorrent.com>
Date: Wed, 5 Aug 2026 09:40:11 -0500
Subject: [PATCH 3/3] Merge test files now that crash is resolved

---
 .../LoopSplit/liveout-latchcmp-crash.ll       | 59 -------------------
 llvm/test/Transforms/LoopSplit/liveout.ll     | 55 +++++++++++++++++
 2 files changed, 55 insertions(+), 59 deletions(-)
 delete mode 100644 llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll

diff --git a/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll b/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
deleted file mode 100644
index 680c2f43da49a..0000000000000
--- a/llvm/test/Transforms/LoopSplit/liveout-latchcmp-crash.ll
+++ /dev/null
@@ -1,59 +0,0 @@
-; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; REQUIRES: asserts
-; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
-
-declare void @use(i32)
-
-define i1 @latchcmp_liveout(i32 %n) {
-; CHECK-LABEL: define i1 @latchcmp_liveout(
-; CHECK-SAME: i32 [[N:%.*]]) {
-; CHECK-NEXT:  [[LS_GUARD0:.*]]:
-; CHECK-NEXT:    [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[N]], i32 1)
-; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i32 [[SMAX]], -1
-; CHECK-NEXT:    [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 49)
-; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i32 0, [[SMIN]]
-; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
-; CHECK:       [[ENTRY]]:
-; CHECK-NEXT:    br label %[[LOOP:.*]]
-; CHECK:       [[LOOP]]:
-; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
-; CHECK-NEXT:    call void @use(i32 [[IV]])
-; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
-; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i32 [[IV_NEXT]], [[SMIN]]
-; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]]
-; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
-; CHECK:       [[EXIT]]:
-; CHECK-NEXT:    br label %[[LS_GUARD1]]
-; CHECK:       [[LS_GUARD1]]:
-; CHECK-NEXT:    [[CMP5:%.*]] = phi i1 [ [[CMP]], %[[EXIT]] ], [ poison, %[[LS_GUARD0]] ]
-; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i32 50, [[TMP0]]
-; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
-; CHECK:       [[ENTRY_LS1]]:
-; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
-; CHECK:       [[LOOP_LS1]]:
-; CHECK-NEXT:    [[IV_LS1:%.*]] = phi i32 [ 50, %[[ENTRY_LS1]] ], [ [[IV_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
-; CHECK-NEXT:    call void @use(i32 [[IV_LS1]])
-; CHECK-NEXT:    [[IV_NEXT_LS1]] = add i32 [[IV_LS1]], 1
-; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i32 [[IV_NEXT_LS1]], [[TMP0]]
-; CHECK-NEXT:    [[CMP_LS1:%.*]] = icmp slt i32 [[IV_NEXT_LS1]], [[N]]
-; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
-; CHECK:       [[LS_EXIT1]]:
-; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
-; CHECK:       [[LS_FINAL_EXIT]]:
-; CHECK-NEXT:    [[CMP4:%.*]] = phi i1 [ [[CMP_LS1]], %[[LS_EXIT1]] ], [ [[CMP5]], %[[LS_GUARD1]] ]
-; CHECK-NEXT:    ret i1 [[CMP4]]
-;
-entry:
-  br label %loop
-
-loop:
-  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
-  call void @use(i32 %iv)
-  %iv.next = add i32 %iv, 1
-  %cmp = icmp slt i32 %iv.next, %n
-  br i1 %cmp, label %loop, label %exit
-
-exit:
-  %cmp.lcssa = phi i1 [ %cmp, %loop ]
-  ret i1 %cmp.lcssa
-}
diff --git a/llvm/test/Transforms/LoopSplit/liveout.ll b/llvm/test/Transforms/LoopSplit/liveout.ll
index e75ca260cd2c9..d77ee38a31867 100644
--- a/llvm/test/Transforms/LoopSplit/liveout.ll
+++ b/llvm/test/Transforms/LoopSplit/liveout.ll
@@ -1,8 +1,63 @@
 ; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
+; REQUIRES: asserts
 ; RUN: opt -passes=loop-split-utils -loop-split-points=50 -S < %s | FileCheck %s
 
 declare void @use(i32)
 
+define i1 @latchcmp_liveout(i32 %n) {
+; CHECK-LABEL: define i1 @latchcmp_liveout(
+; CHECK-SAME: i32 [[N:%.*]]) {
+; CHECK-NEXT:  [[LS_GUARD0:.*]]:
+; CHECK-NEXT:    [[SMAX:%.*]] = call i32 @llvm.smax.i32(i32 [[N]], i32 1)
+; CHECK-NEXT:    [[TMP0:%.*]] = add nsw i32 [[SMAX]], -1
+; CHECK-NEXT:    [[SMIN:%.*]] = call i32 @llvm.smin.i32(i32 [[TMP0]], i32 49)
+; CHECK-NEXT:    [[ITR_CHK:%.*]] = icmp sle i32 0, [[SMIN]]
+; CHECK-NEXT:    br i1 [[ITR_CHK]], label %[[ENTRY:.*]], label %[[LS_GUARD1:.*]]
+; CHECK:       [[ENTRY]]:
+; CHECK-NEXT:    br label %[[LOOP:.*]]
+; CHECK:       [[LOOP]]:
+; CHECK-NEXT:    [[IV:%.*]] = phi i32 [ 0, %[[ENTRY]] ], [ [[IV_NEXT:%.*]], %[[LOOP]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV]])
+; CHECK-NEXT:    [[IV_NEXT]] = add i32 [[IV]], 1
+; CHECK-NEXT:    [[ITR_CHK1:%.*]] = icmp sle i32 [[IV_NEXT]], [[SMIN]]
+; CHECK-NEXT:    [[CMP:%.*]] = icmp slt i32 [[IV_NEXT]], [[N]]
+; CHECK-NEXT:    br i1 [[ITR_CHK1]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK:       [[EXIT]]:
+; CHECK-NEXT:    br label %[[LS_GUARD1]]
+; CHECK:       [[LS_GUARD1]]:
+; CHECK-NEXT:    [[CMP5:%.*]] = phi i1 [ [[CMP]], %[[EXIT]] ], [ poison, %[[LS_GUARD0]] ]
+; CHECK-NEXT:    [[ITR_CHK2:%.*]] = icmp sle i32 50, [[TMP0]]
+; CHECK-NEXT:    br i1 [[ITR_CHK2]], label %[[ENTRY_LS1:.*]], label %[[LS_FINAL_EXIT:.*]]
+; CHECK:       [[ENTRY_LS1]]:
+; CHECK-NEXT:    br label %[[LOOP_LS1:.*]]
+; CHECK:       [[LOOP_LS1]]:
+; CHECK-NEXT:    [[IV_LS1:%.*]] = phi i32 [ 50, %[[ENTRY_LS1]] ], [ [[IV_NEXT_LS1:%.*]], %[[LOOP_LS1]] ]
+; CHECK-NEXT:    call void @use(i32 [[IV_LS1]])
+; CHECK-NEXT:    [[IV_NEXT_LS1]] = add i32 [[IV_LS1]], 1
+; CHECK-NEXT:    [[ITR_CHK3:%.*]] = icmp sle i32 [[IV_NEXT_LS1]], [[TMP0]]
+; CHECK-NEXT:    [[CMP_LS1:%.*]] = icmp slt i32 [[IV_NEXT_LS1]], [[N]]
+; CHECK-NEXT:    br i1 [[ITR_CHK3]], label %[[LOOP_LS1]], label %[[LS_EXIT1:.*]]
+; CHECK:       [[LS_EXIT1]]:
+; CHECK-NEXT:    br label %[[LS_FINAL_EXIT]]
+; CHECK:       [[LS_FINAL_EXIT]]:
+; CHECK-NEXT:    [[CMP4:%.*]] = phi i1 [ [[CMP_LS1]], %[[LS_EXIT1]] ], [ [[CMP5]], %[[LS_GUARD1]] ]
+; CHECK-NEXT:    ret i1 [[CMP4]]
+;
+entry:
+  br label %loop
+
+loop:
+  %iv = phi i32 [ 0, %entry ], [ %iv.next, %loop ]
+  call void @use(i32 %iv)
+  %iv.next = add i32 %iv, 1
+  %cmp = icmp slt i32 %iv.next, %n
+  br i1 %cmp, label %loop, label %exit
+
+exit:
+  %cmp.lcssa = phi i1 [ %cmp, %loop ]
+  ret i1 %cmp.lcssa
+}
+
 define i1 @latchcmp_liveout_extra_use(i32 %n) {
 ; CHECK-LABEL: define i1 @latchcmp_liveout_extra_use(
 ; CHECK-SAME: i32 [[N:%.*]]) {



More information about the llvm-commits mailing list