Skip to content

[Coroutines] Respect noinline attributes when eliding heap allocation #115384

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Nov 8, 2024

Conversation

yuxuanchen1997
Copy link
Member

Because the latest coroutine heap elision no longer depend on inlining. We can occasionally elide coroutines that are marked __attribute__((noinline)). Considering that we are force inlining the ramp function as of #114004, this is kind of important.

@yuxuanchen1997 yuxuanchen1997 marked this pull request as ready for review November 7, 2024 22:42
@yuxuanchen1997 yuxuanchen1997 self-assigned this Nov 7, 2024
@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2024

@llvm/pr-subscribers-coroutines

Author: Yuxuan Chen (yuxuanchen1997)

Changes

Because the latest coroutine heap elision no longer depend on inlining. We can occasionally elide coroutines that are marked __attribute__((noinline)). Considering that we are force inlining the ramp function as of #114004, this is kind of important.


Full diff: https://github.com/llvm/llvm-project/pull/115384.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Coroutines/CoroSplit.cpp (+3-3)
  • (added) llvm/test/Transforms/Coroutines/coro-split-noinline.ll (+62)
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index efd622123bccde..25a962ddf1b0da 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2088,9 +2088,9 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
 
   bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
 
-  bool shouldCreateNoAllocVariant = !isNoSuspendCoroutine &&
-                                    Shape.ABI == coro::ABI::Switch &&
-                                    hasSafeElideCaller(F);
+  bool shouldCreateNoAllocVariant =
+      !isNoSuspendCoroutine && Shape.ABI == coro::ABI::Switch &&
+      hasSafeElideCaller(F) && !F.hasFnAttribute(llvm::Attribute::NoInline);
 
   // If there are no suspend points, no split required, just remove
   // the allocation and deallocation blocks, they are not needed.
diff --git a/llvm/test/Transforms/Coroutines/coro-split-noinline.ll b/llvm/test/Transforms/Coroutines/coro-split-noinline.ll
new file mode 100644
index 00000000000000..c53771570a0795
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-noinline.ll
@@ -0,0 +1,62 @@
+; Tests that coro-split does not generate noinline variant for noinline functions
+; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
+
+; CHECK-LABEL: @cannotinline()
+define ptr @cannotinline() presplitcoroutine noinline {
+entry:
+  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
+  %need.alloc = call i1 @llvm.coro.alloc(token %id)
+  br i1 %need.alloc, label %dyn.alloc, label %begin
+
+dyn.alloc:  
+  %size = call i32 @llvm.coro.size.i32()
+  %alloc = call ptr @malloc(i32 %size)
+  br label %begin
+
+begin:
+  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
+  call void @print(i32 0)
+  %0 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %0, label %suspend [i8 0, label %resume 
+                                i8 1, label %cleanup]
+resume:
+  call void @print(i32 1)
+  br label %cleanup
+
+cleanup:
+  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem)
+  br label %suspend
+suspend:
+  call i1 @llvm.coro.end(ptr %hdl, i1 0, token none)  
+  ret ptr %hdl
+}
+
+; CHECK-NOT-LABEL: @cannotinline.noalloc()
+
+
+; Make a safe_elide call to cannotinline
+define void @caller() presplitcoroutine {
+entry:
+  %ptr = call ptr @cannotinline() #1
+  ret void
+}
+
+
+declare ptr @llvm.coro.free(token, ptr)
+declare i32 @llvm.coro.size.i32()
+declare i8  @llvm.coro.suspend(token, i1)
+declare void @llvm.coro.resume(ptr)
+declare void @llvm.coro.destroy(ptr)
+
+declare token @llvm.coro.id(i32, ptr, ptr, ptr)
+declare i1 @llvm.coro.alloc(token)
+declare ptr @llvm.coro.begin(token, ptr)
+declare i1 @llvm.coro.end(ptr, i1, token) 
+
+declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") "alloc-family"="malloc"
+declare void @print(i32)
+declare void @free(ptr) willreturn allockind("free") "alloc-family"="malloc"
+
+attributes #1 = { coro_elide_safe }

@llvmbot
Copy link
Member

llvmbot commented Nov 7, 2024

@llvm/pr-subscribers-llvm-transforms

Author: Yuxuan Chen (yuxuanchen1997)

Changes

Because the latest coroutine heap elision no longer depend on inlining. We can occasionally elide coroutines that are marked __attribute__((noinline)). Considering that we are force inlining the ramp function as of #114004, this is kind of important.


Full diff: https://github.com/llvm/llvm-project/pull/115384.diff

2 Files Affected:

  • (modified) llvm/lib/Transforms/Coroutines/CoroSplit.cpp (+3-3)
  • (added) llvm/test/Transforms/Coroutines/coro-split-noinline.ll (+62)
diff --git a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
index efd622123bccde..25a962ddf1b0da 100644
--- a/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroSplit.cpp
@@ -2088,9 +2088,9 @@ static void doSplitCoroutine(Function &F, SmallVectorImpl<Function *> &Clones,
 
   bool isNoSuspendCoroutine = Shape.CoroSuspends.empty();
 
-  bool shouldCreateNoAllocVariant = !isNoSuspendCoroutine &&
-                                    Shape.ABI == coro::ABI::Switch &&
-                                    hasSafeElideCaller(F);
+  bool shouldCreateNoAllocVariant =
+      !isNoSuspendCoroutine && Shape.ABI == coro::ABI::Switch &&
+      hasSafeElideCaller(F) && !F.hasFnAttribute(llvm::Attribute::NoInline);
 
   // If there are no suspend points, no split required, just remove
   // the allocation and deallocation blocks, they are not needed.
diff --git a/llvm/test/Transforms/Coroutines/coro-split-noinline.ll b/llvm/test/Transforms/Coroutines/coro-split-noinline.ll
new file mode 100644
index 00000000000000..c53771570a0795
--- /dev/null
+++ b/llvm/test/Transforms/Coroutines/coro-split-noinline.ll
@@ -0,0 +1,62 @@
+; Tests that coro-split does not generate noinline variant for noinline functions
+; RUN: opt < %s -passes='cgscc(coro-split),simplifycfg,early-cse' -S | FileCheck %s
+
+; CHECK-LABEL: @cannotinline()
+define ptr @cannotinline() presplitcoroutine noinline {
+entry:
+  %id = call token @llvm.coro.id(i32 0, ptr null, ptr null, ptr null)
+  %need.alloc = call i1 @llvm.coro.alloc(token %id)
+  br i1 %need.alloc, label %dyn.alloc, label %begin
+
+dyn.alloc:  
+  %size = call i32 @llvm.coro.size.i32()
+  %alloc = call ptr @malloc(i32 %size)
+  br label %begin
+
+begin:
+  %phi = phi ptr [ null, %entry ], [ %alloc, %dyn.alloc ]
+  %hdl = call ptr @llvm.coro.begin(token %id, ptr %phi)
+  call void @print(i32 0)
+  %0 = call i8 @llvm.coro.suspend(token none, i1 false)
+  switch i8 %0, label %suspend [i8 0, label %resume 
+                                i8 1, label %cleanup]
+resume:
+  call void @print(i32 1)
+  br label %cleanup
+
+cleanup:
+  %mem = call ptr @llvm.coro.free(token %id, ptr %hdl)
+  call void @free(ptr %mem)
+  br label %suspend
+suspend:
+  call i1 @llvm.coro.end(ptr %hdl, i1 0, token none)  
+  ret ptr %hdl
+}
+
+; CHECK-NOT-LABEL: @cannotinline.noalloc()
+
+
+; Make a safe_elide call to cannotinline
+define void @caller() presplitcoroutine {
+entry:
+  %ptr = call ptr @cannotinline() #1
+  ret void
+}
+
+
+declare ptr @llvm.coro.free(token, ptr)
+declare i32 @llvm.coro.size.i32()
+declare i8  @llvm.coro.suspend(token, i1)
+declare void @llvm.coro.resume(ptr)
+declare void @llvm.coro.destroy(ptr)
+
+declare token @llvm.coro.id(i32, ptr, ptr, ptr)
+declare i1 @llvm.coro.alloc(token)
+declare ptr @llvm.coro.begin(token, ptr)
+declare i1 @llvm.coro.end(ptr, i1, token) 
+
+declare noalias ptr @malloc(i32) allockind("alloc,uninitialized") "alloc-family"="malloc"
+declare void @print(i32)
+declare void @free(ptr) willreturn allockind("free") "alloc-family"="malloc"
+
+attributes #1 = { coro_elide_safe }

@ChuanqiXu9 ChuanqiXu9 self-requested a review November 8, 2024 05:58
@yuxuanchen1997 yuxuanchen1997 merged commit d233fed into main Nov 8, 2024
12 checks passed
@yuxuanchen1997 yuxuanchen1997 deleted the users/yuxuanchen1997/coro-respect-noinline branch November 8, 2024 06:41
Groverkss pushed a commit to iree-org/llvm-project that referenced this pull request Nov 15, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants