[clang] [CIR] Emit ready and suspend branches for cir.await (PR #168814)
via cfe-commits
cfe-commits at lists.llvm.org
Wed Nov 19 19:57:30 PST 2025
https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/168814
>From 53a0c360454208a3b290ac460e3f2aabfc231a0b Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Wed, 19 Nov 2025 22:01:06 -0500
Subject: [PATCH 1/2] [CIR] Emit ready and suspend branches for cir.await
---
clang/include/clang/CIR/MissingFeatures.h | 1 -
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 4 +--
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 25 +++++++++++--
clang/lib/CIR/CodeGen/CIRGenFunction.h | 1 +
clang/test/CIR/CodeGen/coro-task.cpp | 43 +++++++++++++++++++++--
5 files changed, 65 insertions(+), 9 deletions(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 6b5c34d28ce2a..a6bdf11a01a4b 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -151,7 +151,6 @@ struct MissingFeatures {
// Coroutines
static bool coroEndBuiltinCall() { return false; }
- static bool coroutineFrame() { return false; }
static bool emitBodyAndFallthrough() { return false; }
static bool coroOutsideFrameMD() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index 77f19343653db..862f25cc4d3c8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -470,9 +470,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return getUndefRValue(e->getType());
case Builtin::BI__builtin_coro_frame: {
- cgm.errorNYI(e->getSourceRange(), "BI__builtin_coro_frame NYI");
- assert(!cir::MissingFeatures::coroutineFrame());
- return getUndefRValue(e->getType());
+ return emitCoroutineFrame();
}
case Builtin::BI__builtin_coro_free:
case Builtin::BI__builtin_coro_size: {
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index bb55991d9366a..d105d64ea5d31 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -97,6 +97,14 @@ struct ParamReferenceReplacerRAII {
}
};
} // namespace
+
+RValue CIRGenFunction::emitCoroutineFrame() {
+ if (curCoro.data && curCoro.data->coroBegin) {
+ return RValue::get(curCoro.data->coroBegin);
+ }
+ cgm.errorNYI("NYI");
+}
+
static void createCoroData(CIRGenFunction &cgf,
CIRGenFunction::CGCoroInfo &curCoro,
cir::CallOp coroId) {
@@ -302,11 +310,24 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
builder, cgf.getLoc(s.getSourceRange()), kind,
/*readyBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
- builder.createCondition(
- cgf.createDummyValue(loc, cgf.getContext().BoolTy));
+ Expr *condExpr = s.getReadyExpr()->IgnoreParens();
+ builder.createCondition(cgf.evaluateExprAsBool(condExpr));
},
/*suspendBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
+ // Note that differently from LLVM codegen we do not emit coro.save
+ // and coro.suspend here, that should be done as part of lowering this
+ // to LLVM dialect (or some other MLIR dialect)
+
+ // A invalid suspendRet indicates "void returning await_suspend"
+ mlir::Value suspendRet = cgf.emitScalarExpr(s.getSuspendExpr());
+
+ // Veto suspension if requested by bool returning await_suspend.
+ if (suspendRet) {
+ cgf.cgm.errorNYI("Veto await_suspend");
+ }
+
+ // Signals the parent that execution flows to next region.
cir::YieldOp::create(builder, loc);
},
/*resumeBuilder=*/
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index b426f3389ff1b..d09dfbd84975a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1415,6 +1415,7 @@ class CIRGenFunction : public CIRGenTypeCache {
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc);
cir::CallOp emitCoroBeginBuiltinCall(mlir::Location loc,
mlir::Value coroframeAddr);
+ RValue emitCoroutineFrame();
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index 01e0786fbda71..4843f2433fa64 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -111,6 +111,9 @@ co_invoke_fn co_invoke;
// CIR-DAG: ![[VoidPromisse:.*]] = !cir.record<struct "folly::coro::Task<void>::promise_type" padded {!u8i}>
// CIR-DAG: ![[IntPromisse:.*]] = !cir.record<struct "folly::coro::Task<int>::promise_type" padded {!u8i}>
// CIR-DAG: ![[StdString:.*]] = !cir.record<struct "std::string" padded {!u8i}>
+// CIR-DAG: ![[CoroHandleVoid:.*]] = !cir.record<struct "std::coroutine_handle<void>" padded {!u8i}>
+// CIR-DAG: ![[CoroHandlePromiseVoid:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<void>::promise_type>" padded {!u8i}>
+// CIR-DAG: ![[CoroHandlePromiseInt:rec_.*]] = !cir.record<struct "std::coroutine_handle<folly::coro::Task<int>::promise_type>" padded {!u8i}>
// CIR-DAG: ![[SuspendAlways:.*]] = !cir.record<struct "std::suspend_always" padded {!u8i}>
// CIR: module {{.*}} {
@@ -160,6 +163,8 @@ VoidTask silly_task() {
// CIR: cir.scope {
// CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64}
+// CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64}
+// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64}
// Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting
// the suspend_always struct to use for cir.await. Note that we return by-value since we defer ABI lowering
@@ -175,8 +180,28 @@ VoidTask silly_task() {
// First regions `ready` has a special cir.yield code to veto suspension.
// CIR: cir.await(init, ready : {
-// CIR: cir.condition({{.*}})
+// CIR: %[[ReadyVeto:.*]] = cir.scope {
+// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool
+// CIR: }
+// CIR: cir.condition(%[[ReadyVeto]])
+
+// Second region `suspend` contains the actual suspend logic.
+//
+// - Start by getting the coroutine handle using from_address().
+// - Implicit convert coroutine handle from task specific promisse
+// specialization to a void one.
+// - Call suspend_always::await_suspend() passing the handle.
+//
+// FIXME: add veto support for non-void await_suspends.
+
// CIR: }, suspend : {
+// CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr]])
+// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseVoid]]
+// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]])
+// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
// CIR: cir.yield
// CIR: }, resume : {
// CIR: cir.yield
@@ -203,11 +228,23 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]
// CIR: cir.scope {
// CIR: %[[SuspendAlwaysAddr:.*]] = cir.alloca ![[SuspendAlways]], {{.*}} ["ref.tmp0"] {alignment = 1 : i64}
+// CIR: %[[CoroHandleVoidAddr:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp0"] {alignment = 1 : i64}
+// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseInt]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64}
// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
// CIR: cir.await(init, ready : {
-// CIR: cir.condition({{.*}})
+// CIR: %[[ReadyVeto:.*]] = cir.scope {
+// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield %[[TmpCallRes:.*]] : !cir.bool
+// CIR: }
+// CIR: cir.condition(%[[ReadyVeto]])
// CIR: }, suspend : {
-// CIR: cir.yield
+// CIR: %[[FromAddrRes:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIiE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr:.*]])
+// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseInt]]
+// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]])
+// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
+// CIR: cir.yield
// CIR: }, resume : {
// CIR: cir.yield
// CIR: },)
>From d03614664d8612f42ddbcc151235797d89aab4a8 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Wed, 19 Nov 2025 22:57:01 -0500
Subject: [PATCH 2/2] Fix non-void function does not return
---
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 1 +
1 file changed, 1 insertion(+)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index d105d64ea5d31..f429b19a8d33a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -103,6 +103,7 @@ RValue CIRGenFunction::emitCoroutineFrame() {
return RValue::get(curCoro.data->coroBegin);
}
cgm.errorNYI("NYI");
+ return RValue();
}
static void createCoroData(CIRGenFunction &cgf,
More information about the cfe-commits
mailing list