https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/227527
>From fc06215c9fcdcd8ba36bf6c1e28919e0d370cd48 Mon Sep 17 00:00:00 2001 From: amanmaurya92 <[email protected]> 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 de805d4c642a2..596493d0c3fbb 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 b0e03ce10d5a6..8d1bc4fa7a1f0 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 d5a587ff6d81e..084cdbc445cda 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 0000000000000..f88531206b0e9 --- /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 06b925bcc07b3..a07b3af6ff05a 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 fe0fd76769e44..74941f0add7d9 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 +} + _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
