[clang] [ClangIR] Support bool-returning await_suspend (PR #227527)
Aman Maurya via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 1 03:47:04 PDT 2026
https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/227527
>From fc06215c9fcdcd8ba36bf6c1e28919e0d370cd48 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <amanmaurya9209 at gmail.com>
Date: Wed, 30 Sep 2026 06:05:37 +0530
Subject: [PATCH] [ClangIR] Support bool-returning await_suspend
Support bool-returning await_suspend in cir.await and CIRGen.
Closes #227404
Assisted-by: Antigravity
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 2 +
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 23 +++---
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 40 +++++++++--
.../coro-await-suspend-bool.cpp | 44 ++++++++++++
clang/test/CIR/IR/await.cir | 72 ++++++++++++-------
clang/test/CIR/IR/invalid-await.cir | 60 ++++++++++------
6 files changed, 183 insertions(+), 58 deletions(-)
create mode 100644 clang/test/CIR/CodeGenCoroutines/coro-await-suspend-bool.cpp
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index de805d4c642a27c..596493d0c3fbb57 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1129,6 +1129,8 @@ def CIR_ConditionOp : CIR_Op<"condition", [
if true, or exits it if false.
- When in the `ready` region of a `cir.await`, it branches to the `resume`
region when true, and to the `suspend` region when false.
+ - When in the `suspend` region of a `cir.await`, it suspends (exits `cir.await`)
+ when true, or branches to the `resume` region when false (veto suspension).
Example:
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index b0e03ce10d5a61b..8d1bc4fa7a1f01f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -631,16 +631,21 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
// 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");
+ if (s.getSuspendReturnType() ==
+ CoroutineSuspendExpr::SuspendReturnType::SuspendBool) {
+ mlir::Value suspendRet = cgf.evaluateExprAsBool(s.getSuspendExpr());
+ // Veto suspension if requested by bool returning await_suspend.
+ builder.createCondition(suspendRet);
+ } else if (s.getSuspendReturnType() ==
+ CoroutineSuspendExpr::SuspendReturnType::SuspendVoid) {
+ cgf.emitScalarExpr(s.getSuspendExpr());
+ // Signals the parent that execution flows to next region.
+ cir::YieldOp::create(builder, loc);
+ } else {
+ cgf.cgm.errorNYI(s.getSourceRange(),
+ "await_suspend returning handle");
+ cir::YieldOp::create(builder, loc);
}
-
- // Signals the parent that execution flows to next region.
- cir::YieldOp::create(builder, loc);
},
/*resumeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index d5a587ff6d81ed4..084cdbc445cda19 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -701,10 +701,21 @@ void cir::ConditionOp::getSuccessorRegions(
return;
}
- // Parent is an await: condition may branch to resume or suspend regions.
+ // Parent is an await: condition in ready region branches to resume or
+ // suspend regions. Condition in suspend region branches to resume (veto) or
+ // exits to parent op (suspend).
auto await = cast<AwaitOp>(getOperation()->getParentOp());
- regions.emplace_back(&await.getResume());
- regions.emplace_back(&await.getSuspend());
+ mlir::Region *parentRegion = getOperation()->getBlock()->getParent();
+ if (parentRegion == &await.getReady()) {
+ regions.emplace_back(&await.getResume());
+ regions.emplace_back(&await.getSuspend());
+ return;
+ }
+ if (parentRegion == &await.getSuspend()) {
+ regions.emplace_back(getOperation());
+ regions.emplace_back(&await.getResume());
+ return;
+ }
}
MutableOperandRange
@@ -3568,13 +3579,34 @@ void cir::AwaitOp::getSuccessorRegions(
return;
}
+ // Branching from suspend: if terminated by cir.condition, it may branch to
+ // exit to parent op (suspend) or resume (veto).
+ if (&getSuspend() == parentRegion) {
+ if (isa<ConditionOp>(point.getTerminatorPredecessorOrNull())) {
+ regions.emplace_back(getOperation());
+ regions.emplace_back(&getResume());
+ return;
+ }
+ }
+
// Branching from suspend or resume: exit to the parent operation.
regions.emplace_back(getOperation());
}
LogicalResult cir::AwaitOp::verify() {
- if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
+ if (this->getReady().empty() ||
+ !isa<ConditionOp>(this->getReady().back().getTerminator()))
return emitOpError("ready region must end with cir.condition");
+
+ if (this->getSuspend().empty() ||
+ !isa<YieldOp, ConditionOp>(this->getSuspend().back().getTerminator()))
+ return emitOpError(
+ "suspend region must end with cir.yield or cir.condition");
+
+ if (this->getResume().empty() ||
+ !isa<YieldOp>(this->getResume().back().getTerminator()))
+ return emitOpError("resume region must end with cir.yield");
+
return success();
}
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-await-suspend-bool.cpp b/clang/test/CIR/CodeGenCoroutines/coro-await-suspend-bool.cpp
new file mode 100644
index 000000000000000..f88531206b0e982
--- /dev/null
+++ b/clang/test/CIR/CodeGenCoroutines/coro-await-suspend-bool.cpp
@@ -0,0 +1,44 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll
+// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG
+
+#include "Inputs/coroutine.h"
+
+struct Task {
+ struct promise_type {
+ Task get_return_object() { return {}; }
+ std::suspend_never initial_suspend() noexcept { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+struct BoolAwaiter {
+ bool await_ready() { return false; }
+ bool await_suspend(std::coroutine_handle<>) { return false; }
+ void await_resume() {}
+};
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z15await_bool_vetov
+// OGCG-LABEL: define dso_local void @_Z15await_bool_vetov
+Task await_bool_veto() {
+ // CIR: cir.await(user, ready : {
+ // CIR: %[[READY:.*]] = cir.call @_ZN11BoolAwaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_BoolAwaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[READY]])
+ // CIR: }, suspend : {
+ // CIR: %[[SUSPEND_RET:.*]] = cir.call @_ZN11BoolAwaiter13await_suspendESt16coroutine_handleIvE(%{{.*}}) : (!cir.ptr<!rec_BoolAwaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[SUSPEND_RET]])
+ // CIR: }, resume : {
+ // CIR: cir.call @_ZN11BoolAwaiter12await_resumeEv(%{{.*}}) : (!cir.ptr<!rec_BoolAwaiter>{{.*}}) -> ()
+ // CIR: cir.yield
+ // CIR: },)
+
+ // OGCG: %[[READY_RES:.*]] = call noundef zeroext i1 @_ZN11BoolAwaiter11await_readyEv(ptr {{.*}})
+ // OGCG: br i1 %[[READY_RES]], label %[[AWAIT_READY_DEST:.*]], label %[[AWAIT_SUSPEND:.*]]
+ // OGCG: [[AWAIT_SUSPEND]]:
+ // OGCG: %[[SUSP_RET:.*]] = call i1 @llvm.coro.await.suspend.bool(ptr {{.*}}, ptr {{.*}}, ptr {{.*}})
+ // OGCG: br i1 %[[SUSP_RET]], label %{{.*}}, label %[[AWAIT_READY_DEST]]
+ co_await BoolAwaiter{};
+}
diff --git a/clang/test/CIR/IR/await.cir b/clang/test/CIR/IR/await.cir
index 06b925bcc07b399..a07b3af6ff05a4d 100644
--- a/clang/test/CIR/IR/await.cir
+++ b/clang/test/CIR/IR/await.cir
@@ -1,24 +1,48 @@
-// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
-
-cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.yield
- }, resume : {
- cir.yield
- },)
- cir.yield
- }
- cir.return
-}
-
-// CHECK: cir.func coroutine @checkPrintParse
-// CHECK: cir.await(user, ready : {
-// CHECK: cir.condition(%arg0)
-// CHECK: }, suspend : {
-// CHECK: cir.yield
-// CHECK: }, resume : {
-// CHECK: cir.yield
-// CHECK: },)
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }
+ cir.return
+}
+
+// CHECK: cir.func coroutine @checkPrintParse
+// CHECK: cir.await(user, ready : {
+// CHECK: cir.condition(%arg0)
+// CHECK: }, suspend : {
+// CHECK: cir.yield
+// CHECK: }, resume : {
+// CHECK: cir.yield
+// CHECK: },)
+
+cir.func coroutine @checkPrintParseBoolSuspend(%arg0 : !cir.bool) {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.condition(%arg0)
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }
+ cir.return
+}
+
+// CHECK: cir.func coroutine @checkPrintParseBoolSuspend
+// CHECK: cir.await(user, ready : {
+// CHECK: cir.condition(%arg0)
+// CHECK: }, suspend : {
+// CHECK: cir.condition(%arg0)
+// CHECK: }, resume : {
+// CHECK: cir.yield
+// CHECK: },)
+
diff --git a/clang/test/CIR/IR/invalid-await.cir b/clang/test/CIR/IR/invalid-await.cir
index fe0fd76769e44e6..74941f0add7d9db 100644
--- a/clang/test/CIR/IR/invalid-await.cir
+++ b/clang/test/CIR/IR/invalid-await.cir
@@ -1,21 +1,39 @@
-// RUN: cir-opt %s -verify-diagnostics -split-input-file
-cir.func coroutine @bad_task() { // expected-error {{coroutine body must use at least one cir.await op}}
- cir.return
-}
-
-// -----
-
-cir.func coroutine @missing_condition() {
- cir.scope {
- cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
- cir.yield
- }, suspend : {
- cir.yield
- }, resume : {
- cir.yield
- },)
- }
- cir.coro.body {
- }
- cir.return
-}
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+cir.func coroutine @bad_task() { // expected-error {{coroutine body must use at least one cir.await op}}
+ cir.return
+}
+
+// -----
+
+cir.func coroutine @missing_condition() {
+ cir.scope {
+ cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
+ cir.yield
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.coro.body {
+ }
+ cir.return
+}
+
+// -----
+
+cir.func coroutine @invalid_suspend_terminator(%arg0 : !cir.bool) {
+ cir.scope {
+ cir.await(user, ready : { // expected-error {{suspend region must end with cir.yield or cir.condition}}
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.unreachable
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.coro.body {
+ }
+ cir.return
+}
+
More information about the cfe-commits
mailing list