Skip to content

Conversation

@NewSigma
Copy link
Contributor

This patch proposes adding presplitcoroutine attribute instead of asserting it. It doesn't seem good to suppress the requirement in release mode.

Close #156652

@llvmbot
Copy link
Member

llvmbot commented Nov 28, 2025

@llvm/pr-subscribers-clang-codegen
@llvm/pr-subscribers-llvm-transforms

@llvm/pr-subscribers-coroutines

Author: Weibo He (NewSigma)

Changes

This patch proposes adding presplitcoroutine attribute instead of asserting it. It doesn't seem good to suppress the requirement in release mode.

Close #156652


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

1 Files Affected:

  • (modified) llvm/lib/Transforms/Coroutines/CoroEarly.cpp (+59-57)
diff --git a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp
index cdb58523d1e0e..a84fdad071303 100644
--- a/llvm/lib/Transforms/Coroutines/CoroEarly.cpp
+++ b/llvm/lib/Transforms/Coroutines/CoroEarly.cpp
@@ -204,64 +204,66 @@ void Lowerer::lowerEarlyIntrinsics(Function &F) {
       continue;
 
     switch (CB->getIntrinsicID()) {
-      default:
-        continue;
-      case Intrinsic::coro_begin:
-      case Intrinsic::coro_begin_custom_abi:
-        if (CoroBegin)
-          report_fatal_error(
-              "coroutine should have exactly one defining @llvm.coro.begin");
-        CoroBegin = cast<CoroBeginInst>(&I);
-        break;
-      case Intrinsic::coro_free:
-        CoroFrees.push_back(cast<CoroFreeInst>(&I));
-        break;
-      case Intrinsic::coro_suspend:
-        // Make sure that final suspend point is not duplicated as CoroSplit
-        // pass expects that there is at most one final suspend point.
-        if (cast<CoroSuspendInst>(&I)->isFinal())
-          CB->setCannotDuplicate();
-        HasCoroSuspend = true;
-        break;
-      case Intrinsic::coro_end_async:
-      case Intrinsic::coro_end:
-        // Make sure that fallthrough coro.end is not duplicated as CoroSplit
-        // pass expects that there is at most one fallthrough coro.end.
-        if (cast<AnyCoroEndInst>(&I)->isFallthrough())
-          CB->setCannotDuplicate();
-        break;
-      case Intrinsic::coro_noop:
-        lowerCoroNoop(cast<IntrinsicInst>(&I));
-        break;
-      case Intrinsic::coro_id:
-        if (auto *CII = cast<CoroIdInst>(&I)) {
-          if (CII->getInfo().isPreSplit()) {
-            assert(F.isPresplitCoroutine() &&
-                   "The frontend uses Switch-Resumed ABI should emit "
-                   "\"presplitcoroutine\" attribute for the coroutine.");
-            setCannotDuplicate(CII);
-            CII->setCoroutineSelf();
-            CoroId = cast<CoroIdInst>(&I);
-          }
+    default:
+      continue;
+    case Intrinsic::coro_begin:
+    case Intrinsic::coro_begin_custom_abi:
+      if (CoroBegin)
+        report_fatal_error(
+            "coroutine should have exactly one defining @llvm.coro.begin");
+      CoroBegin = cast<CoroBeginInst>(&I);
+      break;
+    case Intrinsic::coro_free:
+      CoroFrees.push_back(cast<CoroFreeInst>(&I));
+      break;
+    case Intrinsic::coro_suspend:
+      // Make sure that final suspend point is not duplicated as CoroSplit
+      // pass expects that there is at most one final suspend point.
+      if (cast<CoroSuspendInst>(&I)->isFinal())
+        CB->setCannotDuplicate();
+      HasCoroSuspend = true;
+      break;
+    case Intrinsic::coro_end_async:
+    case Intrinsic::coro_end:
+      // Make sure that fallthrough coro.end is not duplicated as CoroSplit
+      // pass expects that there is at most one fallthrough coro.end.
+      if (cast<AnyCoroEndInst>(&I)->isFallthrough())
+        CB->setCannotDuplicate();
+      break;
+    case Intrinsic::coro_noop:
+      lowerCoroNoop(cast<IntrinsicInst>(&I));
+      break;
+    case Intrinsic::coro_id:
+      if (auto *CII = cast<CoroIdInst>(&I)) {
+        if (CII->getInfo().isPreSplit()) {
+          // The frontend uses Switch-Resumed ABI should emit
+          // `presplitcoroutine` attribute for the coroutine.
+          if (!F.isPresplitCoroutine())
+            F.setPresplitCoroutine();
+
+          setCannotDuplicate(CII);
+          CII->setCoroutineSelf();
+          CoroId = cast<CoroIdInst>(&I);
         }
-        break;
-      case Intrinsic::coro_id_retcon:
-      case Intrinsic::coro_id_retcon_once:
-      case Intrinsic::coro_id_async:
-        F.setPresplitCoroutine();
-        break;
-      case Intrinsic::coro_resume:
-        lowerResumeOrDestroy(*CB, CoroSubFnInst::ResumeIndex);
-        break;
-      case Intrinsic::coro_destroy:
-        lowerResumeOrDestroy(*CB, CoroSubFnInst::DestroyIndex);
-        break;
-      case Intrinsic::coro_promise:
-        lowerCoroPromise(cast<CoroPromiseInst>(&I));
-        break;
-      case Intrinsic::coro_done:
-        lowerCoroDone(cast<IntrinsicInst>(&I));
-        break;
+      }
+      break;
+    case Intrinsic::coro_id_retcon:
+    case Intrinsic::coro_id_retcon_once:
+    case Intrinsic::coro_id_async:
+      F.setPresplitCoroutine();
+      break;
+    case Intrinsic::coro_resume:
+      lowerResumeOrDestroy(*CB, CoroSubFnInst::ResumeIndex);
+      break;
+    case Intrinsic::coro_destroy:
+      lowerResumeOrDestroy(*CB, CoroSubFnInst::DestroyIndex);
+      break;
+    case Intrinsic::coro_promise:
+      lowerCoroPromise(cast<CoroPromiseInst>(&I));
+      break;
+    case Intrinsic::coro_done:
+      lowerCoroDone(cast<IntrinsicInst>(&I));
+      break;
     }
   }
 

@zwuis
Copy link
Contributor

zwuis commented Nov 28, 2025

@NewSigma
Copy link
Contributor Author

NewSigma commented Dec 1, 2025

Done. Thanks for the reminder.

@NewSigma NewSigma changed the title [CoroEarly] Add presplitcoroutine when frontend misses it [CoroEarly] Infer presplitcoroutine attribute for Switch-Resumed ABI Dec 1, 2025
@llvmbot llvmbot added the clang:codegen IR generation bugs: mangling, exceptions, etc. label Dec 1, 2025
@NewSigma NewSigma marked this pull request as draft December 1, 2025 02:58
@github-actions
Copy link

github-actions bot commented Dec 1, 2025

🐧 Linux x64 Test Results

  • 166452 tests passed
  • 2876 tests skipped
  • 2 tests failed

Failed Tests

(click on a test name to see its output)

Clang

Clang.CodeGenCoroutines/coro-always-inline.cpp
Exit Code: 2

Command Output (stdout):
--
# RUN: at line 1
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include -nostdsysteminc -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20    -O0 /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp -o - | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include -nostdsysteminc -triple x86_64-unknown-linux-gnu -emit-llvm -std=c++20 -O0 /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp -o -
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp:44:41: warning: this coroutine may be split into pieces; not every piece is guaranteed to be inlined [-Walways-inline-coroutine]
# |    44 | __attribute__((__always_inline__)) void bar() {
# |       |                                         ^
# | fatal error: error in backend: coroutine should have exactly one defining @llvm.coro.begin
# `-----------------------------
# error: command failed with exit status: 70
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp
# .---command stderr------------
# | FileCheck error: '<stdin>' is empty.
# | FileCheck command line:  /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-always-inline.cpp
# `-----------------------------
# error: command failed with exit status: 2

--

Clang.CodeGenCoroutines/coro-attributes.cpp
Exit Code: 1

Command Output (stdout):
--
# RUN: at line 1
/home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include -nostdsysteminc -triple x86_64-unknown-linux-gnu -std=c++20 -disable-llvm-passes -emit-llvm /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp -o - | /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/clang -cc1 -internal-isystem /home/gha/actions-runner/_work/llvm-project/llvm-project/build/lib/clang/22/include -nostdsysteminc -triple x86_64-unknown-linux-gnu -std=c++20 -disable-llvm-passes -emit-llvm /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp -o -
# note: command had no output on stdout or stderr
# executed command: /home/gha/actions-runner/_work/llvm-project/llvm-project/build/bin/FileCheck /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp
# .---command stderr------------
# | /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp:18:11: error: CHECK: expected string not found in input
# | // CHECK: attributes #[[FOO_ATTR_NUM]] = { {{.*}} presplitcoroutine
# |           ^
# | <stdin>:211:38: note: scanning from here
# | declare token @llvm.coro.save(ptr) #7
# |                                      ^
# | <stdin>:211:38: note: with "FOO_ATTR_NUM" equal to "0"
# | declare token @llvm.coro.save(ptr) #7
# |                                      ^
# | <stdin>:225:28: note: possible intended match here
# |  %coerce.dive = getelementptr inbounds nuw %"struct.std::coroutine_handle.0", ptr %ref.tmp, i32 0, i32 0
# |                            ^
# | 
# | Input file: <stdin>
# | Check file: /home/gha/actions-runner/_work/llvm-project/llvm-project/clang/test/CodeGenCoroutines/coro-attributes.cpp
# | 
# | -dump-input=help explains the following input dump.
# | 
# | Input was:
# | <<<<<<
# |             .
# |             .
# |             .
# |           206:  %this1 = load ptr, ptr %this.addr, align 8 
# |           207:  ret i1 true 
# |           208: } 
# |           209:  
# |           210: ; Function Attrs: nomerge nounwind 
# |           211: declare token @llvm.coro.save(ptr) #7 
# | check:18'0                                          X error: no match found
# | check:18'1                                            with "FOO_ATTR_NUM" equal to "0"
# |           212:  
# | check:18'0     ~
# |           213: ; Function Attrs: alwaysinline mustprogress 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           214: define internal void @_Z3foov.__await_suspend_wrapper__init(ptr noundef nonnull %0, ptr noundef %1) #8 { 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           215: entry: 
# | check:18'0     ~~~~~~~
# |           216:  %.addr = alloca ptr, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |             .
# |             .
# |             .
# |           220:  store ptr %0, ptr %.addr, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           221:  store ptr %1, ptr %.addr1, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           222:  %2 = load ptr, ptr %.addr, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           223:  %3 = load ptr, ptr %.addr1, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           224:  %call = call ptr @_ZNSt16coroutine_handleIN4coro12promise_typeEE12from_addressEPv(ptr noundef %3) #2 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           225:  %coerce.dive = getelementptr inbounds nuw %"struct.std::coroutine_handle.0", ptr %ref.tmp, i32 0, i32 0 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# | check:18'2                                ?                                                                              possible intended match
# |           226:  %coerce.dive2 = getelementptr inbounds nuw %"struct.std::coroutine_handle", ptr %coerce.dive, i32 0, i32 0 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           227:  store ptr %call, ptr %coerce.dive2, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           228:  call void @llvm.memcpy.p0.p0.i64(ptr align 8 %agg.tmp, ptr align 8 %ref.tmp, i64 8, i1 false) 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           229:  %coerce.dive3 = getelementptr inbounds nuw %"struct.std::coroutine_handle", ptr %agg.tmp, i32 0, i32 0 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |           230:  %4 = load ptr, ptr %coerce.dive3, align 8 
# | check:18'0     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
# |             .
# |             .
# |             .
# | >>>>>>
# `-----------------------------
# error: command failed with exit status: 1

--

If these failures are unrelated to your changes (for example tests are broken or flaky at HEAD), please open an issue at https://github.com/llvm/llvm-project/issues and add the infrastructure label.

@efriedma-quic
Copy link
Collaborator

If you just want to fix the crash, you can add a verifier check. (The IR will still fail to parse, but it won't present as a crash.)

That said, if it's not useful to have the frontend add the marking, we can just drop the requirement.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

clang:codegen IR generation bugs: mangling, exceptions, etc. coroutines C++20 coroutines llvm:transforms

Projects

None yet

Development

Successfully merging this pull request may close these issues.

crashes with assertion failure when running opt -passes=coro-early

5 participants