[llvm] [LCSSA] Avoid rewriting lifetime markers through PHIs (PR #210811)
Karim Alweheshy via llvm-commits
llvm-commits at lists.llvm.org
Wed Jul 22 02:46:37 PDT 2026
https://github.com/karim-alweheshy updated https://github.com/llvm/llvm-project/pull/210811
>From 89ad2d4adcfa1425d013e87c4f4a6bf7e73d823a Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at reddit.com>
Date: Mon, 20 Jul 2026 23:22:22 +0200
Subject: [PATCH 1/3] [LCSSA] Avoid rewriting lifetime markers through PHIs
Lifetime intrinsics must reference an alloca directly. When a loop-local alloca has a lifetime marker outside the loop, LCSSA currently rewrites that marker to an exit PHI and creates invalid IR. Drop all lifetime markers for the alloca in that case, while preserving markers that remain within the loop.
---
llvm/lib/Transforms/Utils/LCSSA.cpp | 18 +++++++
.../test/Transforms/LCSSA/lifetime-markers.ll | 48 +++++++++++++++++++
2 files changed, 66 insertions(+)
create mode 100644 llvm/test/Transforms/LCSSA/lifetime-markers.ll
diff --git a/llvm/lib/Transforms/Utils/LCSSA.cpp b/llvm/lib/Transforms/Utils/LCSSA.cpp
index a9e08ada82ca0..5d572097f3ce1 100644
--- a/llvm/lib/Transforms/Utils/LCSSA.cpp
+++ b/llvm/lib/Transforms/Utils/LCSSA.cpp
@@ -106,10 +106,22 @@ formLCSSAForInstructionsImpl(SmallVectorImpl<Instruction *> &Worklist,
if (ExitBlocks.empty())
continue;
+ SmallVector<Instruction *, 4> LifetimeMarkers;
+ bool DropLifetimeMarkers = false;
for (Use &U : make_early_inc_range(I->uses())) {
Instruction *User = cast<Instruction>(U.getUser());
BasicBlock *UserBB = User->getParent();
+ // Lifetime markers must refer directly to an alloca. Rewriting their
+ // operands through LCSSA PHIs would produce invalid IR, so conservatively
+ // drop all lifetime markers when one crosses the loop boundary.
+ if (User->isLifetimeStartOrEnd()) {
+ LifetimeMarkers.push_back(User);
+ if (InstBB != UserBB && !L->contains(UserBB))
+ DropLifetimeMarkers = true;
+ continue;
+ }
+
// Skip uses in unreachable blocks.
if (!DT.isReachableFromEntry(UserBB)) {
U.set(PoisonValue::get(I->getType()));
@@ -126,6 +138,12 @@ formLCSSAForInstructionsImpl(SmallVectorImpl<Instruction *> &Worklist,
UsesToRewrite.push_back(&U);
}
+ if (DropLifetimeMarkers) {
+ for (Instruction *Marker : LifetimeMarkers)
+ Marker->eraseFromParent();
+ Changed = true;
+ }
+
// If there are no uses outside the loop, exit with no change.
if (UsesToRewrite.empty())
continue;
diff --git a/llvm/test/Transforms/LCSSA/lifetime-markers.ll b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
new file mode 100644
index 0000000000000..47cfb62c5bfb4
--- /dev/null
+++ b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
@@ -0,0 +1,48 @@
+; RUN: opt -passes='function(lcssa),verify' -S %s | FileCheck %s
+
+; Lifetime markers must point directly to allocas. If an alloca is defined in
+; a loop and a marker uses it outside the loop, LCSSA must drop all of the
+; alloca's lifetime markers instead of rewriting one through a PHI.
+
+declare void @llvm.lifetime.start.p0(ptr captures(none))
+declare void @llvm.lifetime.end.p0(ptr captures(none))
+
+define void @drop_lifetime_markers(i1 %repeat) {
+; CHECK-LABEL: @drop_lifetime_markers(
+; CHECK: loop:
+; CHECK-NEXT: %storage = alloca i8, align 1
+; CHECK-NOT: call void @llvm.lifetime
+; CHECK: exit:
+; CHECK-NEXT: ret void
+entry:
+ br label %loop
+loop:
+ %storage = alloca i8
+ call void @llvm.lifetime.start.p0(ptr %storage)
+ br i1 %repeat, label %loop, label %exit
+exit:
+ call void @llvm.lifetime.end.p0(ptr %storage)
+ ret void
+}
+
+; If all lifetime markers stay inside the loop, preserve them even when another
+; use of the alloca requires an LCSSA PHI.
+define ptr @preserve_lifetime_markers(i1 %repeat) {
+; CHECK-LABEL: @preserve_lifetime_markers(
+; CHECK: loop:
+; CHECK-NEXT: %storage = alloca i8, align 1
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %storage)
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %storage)
+; CHECK: exit:
+; CHECK-NEXT: %storage.lcssa = phi ptr [ %storage, %loop ]
+; CHECK-NEXT: ret ptr %storage.lcssa
+entry:
+ br label %loop
+loop:
+ %storage = alloca i8
+ call void @llvm.lifetime.start.p0(ptr %storage)
+ call void @llvm.lifetime.end.p0(ptr %storage)
+ br i1 %repeat, label %loop, label %exit
+exit:
+ ret ptr %storage
+}
>From 5ef657b954d689b39e1ad7eb788364e7a4ca75cc Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at reddit.com>
Date: Tue, 21 Jul 2026 09:34:14 +0200
Subject: [PATCH 2/3] [LCSSA] Address review feedback
---
llvm/lib/Transforms/Utils/LCSSA.cpp | 3 +-
.../test/Transforms/LCSSA/lifetime-markers.ll | 39 +++++++++++--------
2 files changed, 25 insertions(+), 17 deletions(-)
diff --git a/llvm/lib/Transforms/Utils/LCSSA.cpp b/llvm/lib/Transforms/Utils/LCSSA.cpp
index 5d572097f3ce1..9005630f1e14b 100644
--- a/llvm/lib/Transforms/Utils/LCSSA.cpp
+++ b/llvm/lib/Transforms/Utils/LCSSA.cpp
@@ -106,7 +106,7 @@ formLCSSAForInstructionsImpl(SmallVectorImpl<Instruction *> &Worklist,
if (ExitBlocks.empty())
continue;
- SmallVector<Instruction *, 4> LifetimeMarkers;
+ SmallVector<Instruction *> LifetimeMarkers;
bool DropLifetimeMarkers = false;
for (Use &U : make_early_inc_range(I->uses())) {
Instruction *User = cast<Instruction>(U.getUser());
@@ -139,6 +139,7 @@ formLCSSAForInstructionsImpl(SmallVectorImpl<Instruction *> &Worklist,
}
if (DropLifetimeMarkers) {
+ // Use-list order is arbitrary, so wait until all markers are collected.
for (Instruction *Marker : LifetimeMarkers)
Marker->eraseFromParent();
Changed = true;
diff --git a/llvm/test/Transforms/LCSSA/lifetime-markers.ll b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
index 47cfb62c5bfb4..4c86735d60f60 100644
--- a/llvm/test/Transforms/LCSSA/lifetime-markers.ll
+++ b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
@@ -1,19 +1,21 @@
+; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
; RUN: opt -passes='function(lcssa),verify' -S %s | FileCheck %s
; Lifetime markers must point directly to allocas. If an alloca is defined in
; a loop and a marker uses it outside the loop, LCSSA must drop all of the
; alloca's lifetime markers instead of rewriting one through a PHI.
-declare void @llvm.lifetime.start.p0(ptr captures(none))
-declare void @llvm.lifetime.end.p0(ptr captures(none))
-
define void @drop_lifetime_markers(i1 %repeat) {
-; CHECK-LABEL: @drop_lifetime_markers(
-; CHECK: loop:
-; CHECK-NEXT: %storage = alloca i8, align 1
-; CHECK-NOT: call void @llvm.lifetime
-; CHECK: exit:
+; CHECK-LABEL: define void @drop_lifetime_markers(
+; CHECK-SAME: i1 [[REPEAT:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[STORAGE:%.*]] = alloca i8, align 1
+; CHECK-NEXT: br i1 [[REPEAT]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
; CHECK-NEXT: ret void
+;
entry:
br label %loop
loop:
@@ -28,14 +30,19 @@ exit:
; If all lifetime markers stay inside the loop, preserve them even when another
; use of the alloca requires an LCSSA PHI.
define ptr @preserve_lifetime_markers(i1 %repeat) {
-; CHECK-LABEL: @preserve_lifetime_markers(
-; CHECK: loop:
-; CHECK-NEXT: %storage = alloca i8, align 1
-; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %storage)
-; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr %storage)
-; CHECK: exit:
-; CHECK-NEXT: %storage.lcssa = phi ptr [ %storage, %loop ]
-; CHECK-NEXT: ret ptr %storage.lcssa
+; CHECK-LABEL: define ptr @preserve_lifetime_markers(
+; CHECK-SAME: i1 [[REPEAT:%.*]]) {
+; CHECK-NEXT: [[ENTRY:.*:]]
+; CHECK-NEXT: br label %[[LOOP:.*]]
+; CHECK: [[LOOP]]:
+; CHECK-NEXT: [[STORAGE:%.*]] = alloca i8, align 1
+; CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr [[STORAGE]])
+; CHECK-NEXT: call void @llvm.lifetime.end.p0(ptr [[STORAGE]])
+; CHECK-NEXT: br i1 [[REPEAT]], label %[[LOOP]], label %[[EXIT:.*]]
+; CHECK: [[EXIT]]:
+; CHECK-NEXT: [[STORAGE_LCSSA:%.*]] = phi ptr [ [[STORAGE]], %[[LOOP]] ]
+; CHECK-NEXT: ret ptr [[STORAGE_LCSSA]]
+;
entry:
br label %loop
loop:
>From 0ede1188d00afa8e7261b0512ed97217ab5b0a0d Mon Sep 17 00:00:00 2001
From: Karim Alweheshy <karim.alweheshy at reddit.com>
Date: Wed, 22 Jul 2026 11:46:18 +0200
Subject: [PATCH 3/3] [LCSSA] Remove redundant verifier pass
---
llvm/test/Transforms/LCSSA/lifetime-markers.ll | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/llvm/test/Transforms/LCSSA/lifetime-markers.ll b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
index 4c86735d60f60..ee3ca14c6f92b 100644
--- a/llvm/test/Transforms/LCSSA/lifetime-markers.ll
+++ b/llvm/test/Transforms/LCSSA/lifetime-markers.ll
@@ -1,5 +1,5 @@
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py UTC_ARGS: --version 6
-; RUN: opt -passes='function(lcssa),verify' -S %s | FileCheck %s
+; RUN: opt -passes='function(lcssa)' -S %s | FileCheck %s
; Lifetime markers must point directly to allocas. If an alloca is defined in
; a loop and a marker uses it outside the loop, LCSSA must drop all of the
More information about the llvm-commits
mailing list