[llvm] [DSE] Defer alloca store elimination for CoroSplit (PR #133918)

via llvm-commits llvm-commits at lists.llvm.org
Wed Apr 2 19:37:42 PDT 2025


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

>From 71baee432642817b69c9e88e4bfc7c3053fc0240 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Tue, 1 Apr 2025 22:36:15 +0800
Subject: [PATCH 1/2] [DSE] Defer alloca store elimination for CoroSplit

Allocas are destroyed when returning from functions. However, this is not the case for pre-split coroutines. Any premature elimination will lead to side effects.

Fix 123347
---
 .../Scalar/DeadStoreElimination.cpp           |  9 ++--
 .../DeadStoreElimination/coro-alloca.ll       | 41 +++++++++++++++++++
 2 files changed, 45 insertions(+), 5 deletions(-)
 create mode 100644 llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index 935f21fd484f3..c452f04b29d72 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1194,14 +1194,13 @@ struct DSEState {
 
   bool isInvisibleToCallerAfterRet(const Value *V) {
     if (isa<AllocaInst>(V))
-      return true;
+      // Defer alloca store elimination, wait for CoroSplit
+      return !F.hasFnAttribute(Attribute::PresplitCoroutine);
+
     auto I = InvisibleToCallerAfterRet.insert({V, false});
     if (I.second) {
-      if (!isInvisibleToCallerOnUnwind(V)) {
-        I.first->second = false;
-      } else if (isNoAliasCall(V)) {
+      if (isInvisibleToCallerOnUnwind(V) && isNoAliasCall(V))
         I.first->second = !PointerMayBeCaptured(V, /*ReturnCaptures=*/true);
-      }
     }
     return I.first->second;
   }
diff --git a/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll b/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll
new file mode 100644
index 0000000000000..97883177f89e2
--- /dev/null
+++ b/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll
@@ -0,0 +1,41 @@
+; Test that store-load operation that crosses suspension point will not be eliminated by DSE before CoroSplit
+; RUN: opt < %s -passes='dse,verify' -S | FileCheck %s
+
+define void @fn(ptr align 8 %0) presplitcoroutine {
+  %2 = alloca ptr, align 8
+  %3 = alloca i8, align 1
+  %4 = call token @llvm.coro.id(i32 16, ptr %2, ptr @fn, ptr null)
+  %5 = call ptr @llvm.coro.begin(token %4, ptr null)
+  %6 = call ptr @malloc(i64 1)
+  call void @llvm.lifetime.start.p0(i64 8, ptr %2)
+  store ptr %6, ptr %2, align 8
+  %7 = call token @llvm.coro.save(ptr null)
+  call void @llvm.coro.await.suspend.void(ptr %3, ptr %5, ptr @await_suspend_wrapper_void)
+  %8 = call i8 @llvm.coro.suspend(token %7, i1 false)
+  %9 = icmp ule i8 %8, 1
+  br i1 %9, label %10, label %11
+
+10:
+  call void @llvm.lifetime.end.p0(i64 8, ptr %2)
+  br label %11
+
+11:
+  %12 = call i1 @llvm.coro.end(ptr null, i1 false, token none)
+  %13 = load ptr, ptr %2, align 8
+  store ptr %13, ptr %0, align 8
+; store when suspend, load when resume
+; CHECK: store ptr null, ptr %2, align 8
+  store ptr null, ptr %2, align 8
+  ret void
+}
+
+declare ptr @malloc(i64)
+declare token @llvm.coro.id(i32, ptr, ptr, ptr)
+declare ptr @llvm.coro.begin(token, ptr)
+declare void @llvm.lifetime.start.p0(i64, ptr)
+declare token @llvm.coro.save(ptr)
+declare void @llvm.lifetime.end.p0(i64, ptr)
+declare void @llvm.coro.await.suspend.void(ptr, ptr, ptr)
+declare i8 @llvm.coro.suspend(token, i1)
+declare i1 @llvm.coro.end(ptr, i1, token)
+declare void @await_suspend_wrapper_void(ptr, ptr)

>From 5362a65a08a41fa5e6e8eae5bcd7f0adfd2e9a82 Mon Sep 17 00:00:00 2001
From: NewSigma <NewSigma at 163.com>
Date: Wed, 2 Apr 2025 22:54:11 +0800
Subject: [PATCH 2/2] Revision 1

---
 .../Scalar/DeadStoreElimination.cpp           |  7 ++-
 .../DeadStoreElimination/coro-alloca.ll       | 54 ++++++++-----------
 2 files changed, 28 insertions(+), 33 deletions(-)

diff --git a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
index c452f04b29d72..780b64e70136f 100644
--- a/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
+++ b/llvm/lib/Transforms/Scalar/DeadStoreElimination.cpp
@@ -1195,12 +1195,15 @@ struct DSEState {
   bool isInvisibleToCallerAfterRet(const Value *V) {
     if (isa<AllocaInst>(V))
       // Defer alloca store elimination, wait for CoroSplit
-      return !F.hasFnAttribute(Attribute::PresplitCoroutine);
+      return !F.isPresplitCoroutine();
 
     auto I = InvisibleToCallerAfterRet.insert({V, false});
     if (I.second) {
-      if (isInvisibleToCallerOnUnwind(V) && isNoAliasCall(V))
+      if (!isInvisibleToCallerOnUnwind(V)) {
+        I.first->second = false;
+      } else if (isNoAliasCall(V)) {
         I.first->second = !PointerMayBeCaptured(V, /*ReturnCaptures=*/true);
+      }
     }
     return I.first->second;
   }
diff --git a/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll b/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll
index 97883177f89e2..ec9dc84f2c4ae 100644
--- a/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll
+++ b/llvm/test/Transforms/DeadStoreElimination/coro-alloca.ll
@@ -1,41 +1,33 @@
 ; Test that store-load operation that crosses suspension point will not be eliminated by DSE before CoroSplit
-; RUN: opt < %s -passes='dse,verify' -S | FileCheck %s
+; RUN: opt < %s -passes='dse' -S | FileCheck %s
 
-define void @fn(ptr align 8 %0) presplitcoroutine {
-  %2 = alloca ptr, align 8
-  %3 = alloca i8, align 1
-  %4 = call token @llvm.coro.id(i32 16, ptr %2, ptr @fn, ptr null)
-  %5 = call ptr @llvm.coro.begin(token %4, ptr null)
-  %6 = call ptr @malloc(i64 1)
-  call void @llvm.lifetime.start.p0(i64 8, ptr %2)
-  store ptr %6, ptr %2, align 8
-  %7 = call token @llvm.coro.save(ptr null)
-  call void @llvm.coro.await.suspend.void(ptr %3, ptr %5, ptr @await_suspend_wrapper_void)
-  %8 = call i8 @llvm.coro.suspend(token %7, i1 false)
-  %9 = icmp ule i8 %8, 1
-  br i1 %9, label %10, label %11
+define void @fn(ptr align 8 %arg) presplitcoroutine {
+  %promise = alloca ptr, align 8
+  %awaiter = alloca i8, align 1
+  %id = call token @llvm.coro.id(i32 16, ptr %promise, ptr @fn, ptr null)
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr null)
+  %mem = call ptr @malloc(i64 1)
+  call void @llvm.lifetime.start.p0(i64 8, ptr %promise)
+  store ptr %mem, ptr %promise, align 8
+  %save = call token @llvm.coro.save(ptr null)
+  call void @llvm.coro.await.suspend.void(ptr %awaiter, ptr %hdl, ptr @await_suspend_wrapper_void)
+  %sp = call i8 @llvm.coro.suspend(token %save, i1 false)
+  %flag = icmp ule i8 %sp, 1
+  br i1 %flag, label %resume, label %suspend
 
-10:
-  call void @llvm.lifetime.end.p0(i64 8, ptr %2)
-  br label %11
+resume:
+  call void @llvm.lifetime.end.p0(i64 8, ptr %promise)
+  br label %suspend
 
-11:
-  %12 = call i1 @llvm.coro.end(ptr null, i1 false, token none)
-  %13 = load ptr, ptr %2, align 8
-  store ptr %13, ptr %0, align 8
+suspend:
+  call i1 @llvm.coro.end(ptr null, i1 false, token none)
+  %temp = load ptr, ptr %promise, align 8
+  store ptr %temp, ptr %arg, align 8
 ; store when suspend, load when resume
-; CHECK: store ptr null, ptr %2, align 8
-  store ptr null, ptr %2, align 8
+; CHECK: store ptr null, ptr %promise, align 8
+  store ptr null, ptr %promise, align 8
   ret void
 }
 
 declare ptr @malloc(i64)
-declare token @llvm.coro.id(i32, ptr, ptr, ptr)
-declare ptr @llvm.coro.begin(token, ptr)
-declare void @llvm.lifetime.start.p0(i64, ptr)
-declare token @llvm.coro.save(ptr)
-declare void @llvm.lifetime.end.p0(i64, ptr)
-declare void @llvm.coro.await.suspend.void(ptr, ptr, ptr)
-declare i8 @llvm.coro.suspend(token, i1)
-declare i1 @llvm.coro.end(ptr, i1, token)
 declare void @await_suspend_wrapper_void(ptr, ptr)



More information about the llvm-commits mailing list