[llvm] Handle lifetime.end-only allocas (PR #210581)

via llvm-commits llvm-commits at lists.llvm.org
Sun Jul 19 01:39:10 PDT 2026


https://github.com/SomeFlyingThing created https://github.com/llvm/llvm-project/pull/210581

StackLifetime previously only tracked allocas with a lifetime.start marker, so an alloca with only lifetime.end markers incorrectly received a full-function live range.It now tracks every alloca with either llvm.lifetime.start or llvm.lifetime.end

>From 6a10d0c88e8e98d8033afbb12606068297450641 Mon Sep 17 00:00:00 2001
From: SomeFlyingThing <306498559+SomeFlyingThing at users.noreply.github.com>
Date: Sun, 19 Jul 2026 09:32:33 +0100
Subject: [PATCH] [StackLifetime] Handle lifetime.end-only allocas

---
 llvm/include/llvm/Analysis/StackLifetime.h    |  8 ++-
 llvm/lib/Analysis/StackLifetime.cpp           | 21 +++++---
 .../Analysis/StackSafetyAnalysis/lifetime.ll  | 54 +++++++++++++++++--
 3 files changed, 71 insertions(+), 12 deletions(-)

diff --git a/llvm/include/llvm/Analysis/StackLifetime.h b/llvm/include/llvm/Analysis/StackLifetime.h
index ccc5fae1bc0cc..6ff3fad3f368e 100644
--- a/llvm/include/llvm/Analysis/StackLifetime.h
+++ b/llvm/include/llvm/Analysis/StackLifetime.h
@@ -108,9 +108,13 @@ class StackLifetime {
   /// LiveRange for allocas.
   SmallVector<LiveRange, 8> LiveRanges;
 
-  /// The set of allocas that have at least one lifetime.start. All other
+  /// The set of allocas that have at least one lifetime marker. All other
   /// allocas get LiveRange that corresponds to the entire function.
-  BitVector InterestingAllocas;
+  BitVector MarkerAllocas;
+
+  /// The set of allocas that have at least one lifetime.start and are
+  /// therefore initially dead.
+  BitVector StartAllocas;
 
   struct Marker {
     unsigned AllocaNo;
diff --git a/llvm/lib/Analysis/StackLifetime.cpp b/llvm/lib/Analysis/StackLifetime.cpp
index 30e0316b882cc..726b743f60ae3 100644
--- a/llvm/lib/Analysis/StackLifetime.cpp
+++ b/llvm/lib/Analysis/StackLifetime.cpp
@@ -60,7 +60,8 @@ bool StackLifetime::isAliveAfter(const AllocaInst *AI,
 }
 
 void StackLifetime::collectMarkers() {
-  InterestingAllocas.resize(NumAllocas);
+  MarkerAllocas.resize(NumAllocas);
+  StartAllocas.resize(NumAllocas);
   DenseMap<const BasicBlock *, SmallDenseMap<const IntrinsicInst *, Marker>>
       BBMarkerSet;
 
@@ -78,8 +79,9 @@ void StackLifetime::collectMarkers() {
         continue;
       auto AllocaNo = It->second;
       bool IsStart = II->getIntrinsicID() == Intrinsic::lifetime_start;
+      MarkerAllocas.set(AllocaNo);
       if (IsStart)
-        InterestingAllocas.set(AllocaNo);
+        StartAllocas.set(AllocaNo);
       BBMarkerSet[BB][II] = {AllocaNo, IsStart};
     }
   }
@@ -168,9 +170,16 @@ void StackLifetime::calculateLocalLiveness() {
         BitsIn |= I->second.LiveOut;
       }
 
-      // Everything is "may be dead" for entry without predecessors.
-      if (Type == LivenessType::Must && BitsIn.empty())
-        BitsIn.resize(NumAllocas, true);
+      // Allocas with a lifetime.start are initially dead. Allocas with only
+      // lifetime.end markers are initially alive.
+      if (BB == &F.getEntryBlock()) {
+        if (Type == LivenessType::May) {
+          BitsIn = MarkerAllocas;
+          BitsIn.reset(StartAllocas);
+        } else {
+          BitsIn = StartAllocas;
+        }
+      }
 
       // Update block LiveIn set, noting whether it has changed.
       if (!BitsIn.subsetOf(BlockInfo.LiveIn)) {
@@ -303,7 +312,7 @@ StackLifetime::StackLifetime(const Function &F,
 void StackLifetime::run() {
   LiveRanges.resize(NumAllocas, LiveRange(Instructions.size()));
   for (unsigned I = 0; I < NumAllocas; ++I)
-    if (!InterestingAllocas.test(I))
+    if (!MarkerAllocas.test(I))
       LiveRanges[I] = getFullLiveRange();
 
   calculateLocalLiveness();
diff --git a/llvm/test/Analysis/StackSafetyAnalysis/lifetime.ll b/llvm/test/Analysis/StackSafetyAnalysis/lifetime.ll
index 51bfa154e957a..e67c96a3becbd 100644
--- a/llvm/test/Analysis/StackSafetyAnalysis/lifetime.ll
+++ b/llvm/test/Analysis/StackSafetyAnalysis/lifetime.ll
@@ -438,23 +438,23 @@ entry:
   call void @capture32(ptr %x)
   call void @llvm.lifetime.end.p0(ptr %x)
 ; CHECK: call void @llvm.lifetime.end.p0(ptr %x)
-; CHECK-NEXT: Alive: <x>
+; CHECK-NEXT: Alive: <>
 
   br i1 %d, label %bb2, label %bb3
 
 bb2:                                              ; preds = %entry
 ; CHECK: bb2:
-; CHECK-NEXT: Alive: <x>
+; CHECK-NEXT: Alive: <>
   call void @llvm.lifetime.start.p0(ptr %y)
 ; CHECK: call void @llvm.lifetime.start.p0(ptr %y)
-; CHECK-NEXT: Alive: <x y>
+; CHECK-NEXT: Alive: <y>
 
   call void @capture32(ptr %y)
   ret void
 
 bb3:                                              ; preds = %entry
 ; CHECK: bb3:
-; CHECK-NEXT: Alive: <x>
+; CHECK-NEXT: Alive: <>
   ret void
 }
 
@@ -949,6 +949,52 @@ if.end:
   ret void
 }
 
+define void @end_only() {
+; CHECK-LABEL: define void @end_only()
+entry:
+  %x = alloca i8
+; CHECK: %x = alloca i8
+; CHECK-NEXT: Alive: <x>
+
+  call void @llvm.lifetime.end.p0(ptr %x)
+; CHECK: call void @llvm.lifetime.end.p0(ptr %x)
+; CHECK-NEXT: Alive: <>
+
+  ret void
+}
+
+define void @end_only_propagates() {
+; CHECK-LABEL: define void @end_only_propagates()
+entry:
+  %x = alloca i8
+  call void @llvm.lifetime.end.p0(ptr %x)
+
+  br label %next
+; CHECK: br label %next
+; CHECK-NEXT: Alive: <>
+
+next:
+; CHECK: next:
+; CHECK-NEXT: Alive: <>
+  ret void
+}
+
+define void @start_propagates() {
+; CHECK-LABEL: define void @start_propagates()
+entry:
+  %x = alloca i8
+  call void @llvm.lifetime.start.p0(ptr %x)
+
+  br label %next
+; CHECK: br label %next
+; CHECK-NEXT: Alive: <x>
+
+next:
+; CHECK: next:
+; CHECK-NEXT: Alive: <x>
+  ret void
+}
+
 declare void @llvm.lifetime.start.p0(ptr captures(none))
 declare void @llvm.lifetime.end.p0(ptr captures(none))
 declare void @capture8(ptr)



More information about the llvm-commits mailing list