[clang] [CIR] Upstream Emit the resume branch for cir.await op (PR #169864)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Nov 27 18:44:18 PST 2025
https://github.com/Andres-Salamanca created https://github.com/llvm/llvm-project/pull/169864
This PR upstreams the emission of the `cir.await` resume branch. Handling the case where the return value of `co_await` is not ignored is deferred to a future PR, which will be added once `co_return` is upstreamed. Additionally, the `forLValue` variable is always `false` in the current implementation. When support for emitting `coro_yield` is added, this variable will be set to `true`, so that work is also deferred to a future PR.
>From fc7e7bf1866462da004c664791987c24c23efc99 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 27 Nov 2025 21:25:18 -0500
Subject: [PATCH] [CIR] Upstream Emit the resume branch for cir.await op
---
clang/include/clang/CIR/MissingFeatures.h | 2 ++
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 39 +++++++++++++++++++++++
clang/test/CIR/CodeGen/coro-task.cpp | 18 +++++++++--
3 files changed, 56 insertions(+), 3 deletions(-)
diff --git a/clang/include/clang/CIR/MissingFeatures.h b/clang/include/clang/CIR/MissingFeatures.h
index 1427c677d0f34..3d3251adae279 100644
--- a/clang/include/clang/CIR/MissingFeatures.h
+++ b/clang/include/clang/CIR/MissingFeatures.h
@@ -153,6 +153,8 @@ struct MissingFeatures {
static bool coroEndBuiltinCall() { return false; }
static bool emitBodyAndFallthrough() { return false; }
static bool coroOutsideFrameMD() { return false; }
+ static bool coroCoReturn() { return false; }
+ static bool coroCoYield() { return false; }
// Various handling of deferred processing in CIRGenModule.
static bool cgmRelease() { return false; }
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index f7df811a67c26..35a6354b19705 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -32,6 +32,9 @@ struct clang::CIRGen::CGCoroData {
// Stores the result of __builtin_coro_begin call.
mlir::Value coroBegin = nullptr;
+
+ // The promise type's 'unhandled_exception' handler, if it defines one.
+ Stmt *exceptionHandler = nullptr;
};
// Defining these here allows to keep CGCoroData private to this file.
@@ -272,6 +275,17 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
}
return mlir::success();
}
+
+static bool memberCallExpressionCanThrow(const Expr *e) {
+ if (const auto *ce = dyn_cast<CXXMemberCallExpr>(e))
+ if (const FunctionProtoType *proto =
+ ce->getMethodDecl()->getType()->getAs<FunctionProtoType>())
+ if (isNoexceptExceptionSpec(proto->getExceptionSpecType()) &&
+ proto->canThrow() == CT_Cannot)
+ return false;
+ return true;
+}
+
// Given a suspend expression which roughly looks like:
//
// auto && x = CommonExpr();
@@ -333,6 +347,31 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
},
/*resumeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
+ // Exception handling requires additional IR. If the 'await_resume'
+ // function is marked as 'noexcept', we avoid generating this additional
+ // IR.
+ CXXTryStmt *tryStmt = nullptr;
+ if (coro.exceptionHandler && kind == cir::AwaitKind::Init &&
+ memberCallExpressionCanThrow(s.getResumeExpr()))
+ cgf.cgm.errorNYI("Coro resume Exception");
+
+ // FIXME(cir): the alloca for the resume expr should be placed in the
+ // enclosing cir.scope instead.
+ if (forLValue) {
+ assert(!cir::MissingFeatures::coroCoYield());
+ } else {
+ awaitRes.rv =
+ cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
+ if (!awaitRes.rv.isIgnored())
+ // Create the alloca in the block before the scope wrapping
+ // cir.await.
+ assert(!cir::MissingFeatures::coroCoReturn());
+ }
+
+ if (tryStmt)
+ cgf.cgm.errorNYI("Coro tryStmt");
+
+ // Returns control back to parent.
cir::YieldOp::create(builder, loc);
});
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index 4843f2433fa64..6cafe3fd0c419 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -203,11 +203,21 @@ VoidTask silly_task() {
// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
// CIR: cir.yield
+
+// Third region `resume` handles coroutine resuming logic.
+
// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
// CIR: cir.yield
// CIR: },)
// CIR: }
+// Since we already tested cir.await guts above, the remaining checks for:
+// - The actual user written co_await
+// - The promise call
+// - The final suspend co_await
+// - Return
+
folly::coro::Task<int> byRef(const std::string& s) {
co_return s.size();
}
@@ -245,6 +255,8 @@ folly::coro::Task<int> byRef(const std::string& s) {
// 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: },)
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield
+// CIR: },)
+// CIR: }
More information about the cfe-commits
mailing list