https://github.com/NewSigma updated 
https://github.com/llvm/llvm-project/pull/143333

>From 771ff1c9cc7d4b7f95d78d4219288d5c7ca0b83e Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Thu, 5 Jun 2025 21:51:06 +0800
Subject: [PATCH 1/2] Rise lifetime.end to get smaller frame size

---
 clang/test/CodeGenCoroutines/pr56919.cpp      |  6 +-
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp  | 94 ++++++++++++++++++-
 .../Transforms/Coroutines/coro-alloca-06.ll   |  2 +
 .../Transforms/Coroutines/coro-alloca-07.ll   |  2 +
 .../Coroutines/coro-split-rise-lifetime-01.ll | 39 ++++++++
 .../Coroutines/coro-split-rise-lifetime-02.ll | 61 ++++++++++++
 .../Coroutines/coro-split-rise-lifetime-03.ll | 62 ++++++++++++
 7 files changed, 259 insertions(+), 7 deletions(-)
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
 create mode 100644 
llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll

diff --git a/clang/test/CodeGenCoroutines/pr56919.cpp 
b/clang/test/CodeGenCoroutines/pr56919.cpp
index baa8c27ce6649..e709cecf6d93a 100644
--- a/clang/test/CodeGenCoroutines/pr56919.cpp
+++ b/clang/test/CodeGenCoroutines/pr56919.cpp
@@ -111,15 +111,15 @@ Task<void> Bar() { co_await Baz(); }
 
 // CHECK: _Z3Quxv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $40, %esi
+// CHECK-NEXT: movl    $32, %esi
 // CHECK-NEXT: jmp     _ZdlPvm@PLT
 
 // CHECK: _Z3Bazv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $80, %esi
+// CHECK-NEXT: movl    $64, %esi
 // CHECK-NEXT: jmp     _ZdlPvm
 
 // CHECK: _Z3Barv.destroy:{{.*}}
 // CHECK-NEXT: #
-// CHECK-NEXT: movl    $120, %esi
+// CHECK-NEXT: movl    $96, %esi
 // CHECK-NEXT: jmp     _ZdlPvm
diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index 53d78edda2e9f..fb56832dec9e3 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -18,6 +18,7 @@
 #include "CoroInternal.h"
 #include "llvm/ADT/ScopeExit.h"
 #include "llvm/ADT/SmallString.h"
+#include "llvm/Analysis/PostDominators.h"
 #include "llvm/Analysis/StackLifetime.h"
 #include "llvm/IR/DIBuilder.h"
 #include "llvm/IR/DebugInfo.h"
@@ -1767,6 +1768,18 @@ static void eliminateSwiftError(Function &F, coro::Shape 
&Shape) {
   }
 }
 
+static bool isLifetimeStart(Instruction *I) {
+  if (auto *II = dyn_cast<IntrinsicInst>(I))
+    return II->getIntrinsicID() == Intrinsic::lifetime_start;
+  return false;
+}
+
+static bool isLifetimeEnd(Instruction *I) {
+  if (auto *II = dyn_cast<IntrinsicInst>(I))
+    return II->getIntrinsicID() == Intrinsic::lifetime_end;
+  return false;
+}
+
 /// For each local variable that all of its user are only used inside one of
 /// suspended region, we sink their lifetime.start markers to the place where
 /// after the suspend block. Doing so minimizes the lifetime of each variable,
@@ -1819,7 +1832,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
 
       for (User *U : AI->users()) {
         Instruction *UI = cast<Instruction>(U);
-        // For all users except lifetime.start markers, if they are all
+        // For all users except lifetime markers, if they are all
         // dominated by one of the basic blocks and do not cross
         // suspend points as well, then there is no need to spill the
         // instruction.
@@ -1827,7 +1840,7 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
             Checker.isDefinitionAcrossSuspend(DomBB, UI)) {
           // Skip lifetime.start, GEP and bitcast used by lifetime.start
           // markers.
-          if (collectLifetimeStart(UI, AI))
+          if (collectLifetimeStart(UI, AI) || isLifetimeEnd(UI))
             continue;
           Valid = false;
           break;
@@ -1850,6 +1863,78 @@ static void sinkLifetimeStartMarkers(Function &F, 
coro::Shape &Shape,
   }
 }
 
+static bool mayEscape(Value *V, User *U) {
+  if (V == U->stripInBoundsOffsets() || isa<PHINode>(U))
+    return true;
+
+  if (auto *SI = dyn_cast<StoreInst>(U))
+    return SI->getValueOperand() == V;
+
+  if (auto *CB = dyn_cast<CallBase>(U)) {
+    unsigned OpCount = CB->arg_size();
+    for (unsigned Op = 0; Op < OpCount; ++Op)
+      if (V == CB->getArgOperand(Op) && !CB->doesNotCapture(Op))
+        return true;
+  }
+  return false;
+}
+
+// Find the suspend point that dominate all uses of alloca,
+// we will rise lifetime.end markers to the end of corresponding save block.
+static void riseLifetimeEndMarkers(Function &F, const coro::Shape &Shape) {
+  const PostDominatorTree PDT(F);
+  for (Instruction &I : instructions(F)) {
+    AllocaInst *AI = dyn_cast<AllocaInst>(&I);
+    if (!AI)
+      continue;
+
+    SmallVector<Instruction *, 2> LifetimeEnds;
+    SmallPtrSet<BasicBlock *, 2> UserBBs{};
+    bool Escape = false;
+    for (User *U : AI->users()) {
+      auto *I = cast<Instruction>(U);
+      // lifetime markers are not actual uses
+      if (isLifetimeStart(I))
+        continue;
+
+      if (isLifetimeEnd(I))
+        LifetimeEnds.push_back(I);
+      else if (mayEscape(AI, U)) {
+        Escape = true;
+        break;
+      }
+      else
+        UserBBs.insert(I->getParent());
+    }
+
+    // Lifetime is unbounded if no lifetime.end
+    if (LifetimeEnds.empty() || Escape)
+      continue;
+
+    BasicBlock *DomBB = nullptr;
+    for (auto *Suspend : Shape.CoroSuspends) {
+      bool DomAll = llvm::all_of(UserBBs, [&](BasicBlock *UserBB) {
+        return PDT.dominates(Suspend->getParent(), UserBB);
+      });
+
+      if (DomAll) {
+        DomBB = Suspend->getParent();
+        break;
+      }
+    }
+
+    if (DomBB != nullptr) {
+      assert(coro::isSuspendBlock(DomBB));
+      auto *SaveBB = DomBB->getSinglePredecessor();
+      auto *NewEnd = LifetimeEnds[0]->clone();
+      NewEnd->insertBefore(SaveBB->getTerminator()->getIterator());
+
+      for (auto *I : LifetimeEnds)
+        I->eraseFromParent();
+    }
+  }
+}
+
 static std::optional<std::pair<Value &, DIExpression &>>
 salvageDebugInfoImpl(SmallDenseMap<Argument *, AllocaInst *, 4> 
&ArgToAllocaMap,
                      bool UseEntryValue, Function *F, Value *Storage,
@@ -2070,9 +2155,10 @@ void coro::BaseABI::buildCoroutineFrame(bool 
OptimizeFrame) {
   doRematerializations(F, Checker, IsMaterializable);
 
   const DominatorTree DT(F);
-  if (Shape.ABI != coro::ABI::Async && Shape.ABI != coro::ABI::Retcon &&
-      Shape.ABI != coro::ABI::RetconOnce)
+  if (!F.hasOptNone() && Shape.ABI == coro::ABI::Switch) {
     sinkLifetimeStartMarkers(F, Shape, Checker, DT);
+    riseLifetimeEndMarkers(F, Shape);
+  }
 
   // All values (that are not allocas) that needs to be spilled to the frame.
   coro::SpillInfo Spills;
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-06.ll 
b/llvm/test/Transforms/Coroutines/coro-alloca-06.ll
index 89149ceba4c14..ecb42118ca3e9 100644
--- a/llvm/test/Transforms/Coroutines/coro-alloca-06.ll
+++ b/llvm/test/Transforms/Coroutines/coro-alloca-06.ll
@@ -51,6 +51,8 @@ suspend:
 ; CHECK-NEXT:    store ptr [[TMP2]], ptr [[TMP0]], align 8
 ; CHECK-NEXT:    call void @llvm.lifetime.start.p0(i64 8, ptr [[TMP1]])
 ; CHECK-NEXT:    store ptr [[TMP0]], ptr [[TMP1]], align 8
+; CHECK-NEXT:    %index.addr1 = getelementptr inbounds nuw %f.Frame, ptr %hdl, 
i32 0, i32 2 
+; CHECK-NEXT:    store i1 false, ptr %index.addr1, align 1 
 ; CHECK-NEXT:    call void @llvm.lifetime.end.p0(i64 8, ptr [[TMP1]])
 ;
 
diff --git a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll 
b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll
index 3b0acdd794af4..def6cde84cb35 100644
--- a/llvm/test/Transforms/Coroutines/coro-alloca-07.ll
+++ b/llvm/test/Transforms/Coroutines/coro-alloca-07.ll
@@ -31,6 +31,8 @@ resume:
   br label %cleanup
 
 cleanup:
+  call void @llvm.lifetime.end.p0(i64 8, ptr %x)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %y)
   %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
   call void @free(ptr %mem)
   br label %suspend
diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll 
b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
new file mode 100644
index 0000000000000..577d822a6c6d1
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-01.ll
@@ -0,0 +1,39 @@
+; Test allocas that do not cross suspension point will not go to frame
+; RUN: opt < %s -passes='cgscc(coro-split),early-cse' -S | FileCheck %s
+
+; CHECK: %large.alloca = alloca [500 x i8], align 16
+; CHECK-NOT: %large.alloca.reload.addr
+
+define void @f() presplitcoroutine {
+entry:
+  %large.alloca = alloca [500 x i8], align 16
+  %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null)
+  %size = call i32 @llvm.coro.size.i32()
+  %mem = call ptr @malloc(i32 %size)
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem)
+  call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca)
+  %value = load i8, ptr %large.alloca, align 1
+  call void @consume(i8 %value)
+  %0 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %0, label %exit [
+    i8 0, label %suspend
+    i8 1, label %cleanup
+  ]
+
+suspend:
+  br label %cleanup
+
+cleanup:
+  call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca)
+  %1 = call ptr @llvm.coro.free(token %id, ptr %mem)
+  call void @free(ptr %mem)
+  br label %exit
+
+exit:
+  %2 = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+  ret void
+}
+
+declare void @consume(i8)
+declare ptr @malloc(i32)
+declare void @free(ptr)
diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll 
b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
new file mode 100644
index 0000000000000..b4d3cb4f07d08
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-02.ll
@@ -0,0 +1,61 @@
+; Test that we rise lifetime markers to the correct place if there are many 
suspend points.
+; RUN: opt < %s -passes='cgscc(coro-split),early-cse' -S | FileCheck %s
+
+; CHECK: %large.alloca = alloca [500 x i8], align 16
+; CHECK-NOT: %large.alloca.reload.addr
+
+define void @many_suspend() presplitcoroutine {
+entry:
+  %large.alloca = alloca [500 x i8], align 16
+  %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null)
+  %size = call i64 @llvm.coro.size.i64()
+  %call = call noalias ptr @malloc(i64 %size)
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %call)
+  call void @llvm.lifetime.start.p0(i64 500, ptr %large.alloca)
+  %save1 = call token @llvm.coro.save(ptr null)
+  %sp1 = call i8 @llvm.coro.suspend(token %save1, i1 false)
+  switch i8 %sp1, label %coro.ret [
+    i8 0, label %await.ready
+    i8 1, label %cleanup
+  ]
+
+await.ready:
+  %save2 = call token @llvm.coro.save(ptr null)
+  %sp2 = call i8 @llvm.coro.suspend(token %save2, i1 false)
+  switch i8 %sp2, label %coro.ret [
+    i8 0, label %await2.ready
+    i8 1, label %cleanup
+  ]
+
+await2.ready:
+  %value = load i8, ptr %large.alloca, align 1
+  call void @consume(i8 %value)
+  %save3 = call token @llvm.coro.save(ptr null)
+  %sp3 = call i8 @llvm.coro.suspend(token %save3, i1 false)
+  switch i8 %sp3, label %coro.ret [
+    i8 0, label %await3.ready
+    i8 1, label %cleanup
+  ]
+
+await3.ready:
+  %save4 = call token @llvm.coro.save(ptr null)
+  %sp4 = call i8 @llvm.coro.suspend(token %save4, i1 false)
+  switch i8 %sp4, label %coro.ret [
+    i8 0, label %cleanup
+    i8 1, label %cleanup
+  ]
+
+cleanup:
+  call void @llvm.lifetime.end.p0(i64 500, ptr %large.alloca)
+  %mem1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem1)
+  br label %coro.ret
+
+coro.ret:
+  %InResumePart = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+  ret void
+}
+
+declare void @consume(i8)
+declare ptr @malloc(i64)
+declare void @free(ptr)
diff --git a/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll 
b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll
new file mode 100644
index 0000000000000..5ea3c47bf9989
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-rise-lifetime-03.ll
@@ -0,0 +1,62 @@
+; Test we do not rise lifetime.end for allocas that may escape
+; RUN: opt < %s -passes='cgscc(coro-split),early-cse' -S | FileCheck %s
+
+; CHECK-NOT: %escape.gep = alloca [500 x i8], align 16
+; CHECK: %escape.gep.reload.addr
+
+; CHECK-NOT: %escape.store = alloca [500 x i8], align 16
+; CHECK: %escape.store.reload.addr
+
+; CHECK-NOT: %escape.call = alloca [500 x i8], align 16
+; CHECK: %escape.call.reload.addr
+
+define void @fn() presplitcoroutine {
+entry:
+  %id = call token @llvm.coro.id(i32 16, ptr null, ptr null, ptr null)
+  %size = call i64 @llvm.coro.size.i64()
+  %mem = call ptr @malloc(i64 %size)
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %mem)
+
+  %escape.gep = alloca [500 x i8], align 16
+  call void @llvm.lifetime.start.p0(i64 500, ptr %escape.gep)
+  %gep.ptr = getelementptr inbounds nuw i8, ptr %escape.gep, i64 8
+
+  %escape.store = alloca [500 x i8], align 16
+  %store.ptr = alloca ptr, align 8
+  call void @llvm.lifetime.start.p0(i64 500, ptr %escape.store)
+  call void @llvm.lifetime.start.p0(i64 8, ptr %store.ptr)
+  store ptr %escape.store, ptr %store.ptr, align 8
+
+  %escape.call = alloca [500 x i8], align 16
+  call void @llvm.lifetime.start.p0(i64 500, ptr %escape.call)
+  call void @consume(ptr %escape.call)
+
+  %save = call token @llvm.coro.save(ptr null)
+  %suspend = call i8 @llvm.coro.suspend(token %save, i1 false)
+  switch i8 %suspend, label %coro.ret [
+    i8 0, label %await.ready
+    i8 1, label %cleanup
+  ]
+
+await.ready:
+  call void @consume(ptr %gep.ptr)
+  call void @consume(ptr %store.ptr)
+  br label %cleanup
+
+cleanup:
+  call void @llvm.lifetime.end.p0(i64 500, ptr %escape.gep)
+  call void @llvm.lifetime.end.p0(i64 500, ptr %escape.store)
+  call void @llvm.lifetime.end.p0(i64 500, ptr %escape.call)
+  call void @llvm.lifetime.end.p0(i64 8, ptr %store.ptr)
+  %mem1 = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem1)
+  br label %coro.ret
+
+coro.ret:
+  %InResumePart = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+  ret void
+}
+
+declare void @consume(ptr)
+declare ptr @malloc(i64)
+declare void @free(ptr)

>From 367269db9ee353ff1badda35bce06c80e1711e9b Mon Sep 17 00:00:00 2001
From: NewSigma <newsi...@163.com>
Date: Mon, 9 Jun 2025 11:06:02 +0800
Subject: [PATCH 2/2] Update

---
 llvm/lib/Transforms/Coroutines/CoroFrame.cpp | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp 
b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
index fb56832dec9e3..72b5e801e9d51 100644
--- a/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroFrame.cpp
@@ -1787,9 +1787,6 @@ static bool isLifetimeEnd(Instruction *I) {
 static void sinkLifetimeStartMarkers(Function &F, coro::Shape &Shape,
                                      SuspendCrossingInfo &Checker,
                                      const DominatorTree &DT) {
-  if (F.hasOptNone())
-    return;
-
   // Collect all possible basic blocks which may dominate all uses of allocas.
   SmallPtrSet<BasicBlock *, 4> DomSet;
   DomSet.insert(&F.getEntryBlock());
@@ -1902,8 +1899,7 @@ static void riseLifetimeEndMarkers(Function &F, const 
coro::Shape &Shape) {
       else if (mayEscape(AI, U)) {
         Escape = true;
         break;
-      }
-      else
+      } else
         UserBBs.insert(I->getParent());
     }
 

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to