[clang] [CIR] Add coroutine cleanup handling and update co_return semantics (PR #189281)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Mar 29 12:56:34 PDT 2026
https://github.com/Andres-Salamanca created https://github.com/llvm/llvm-project/pull/189281
This PR adds cleanup handling for coroutine frame destruction. The cleanup is emitted as a conditional that checks the result of the `coro.free` builtin, which is used to determine whether the coroutine frame was heap-allocated, if the returned pointer is null, no destruction is performed. Additionally, this PR changes how co_return is represented: previously, it was lowered directly into a branch to the block containing the final suspend logic, but now a new `cir.coro.body` operation is introduced to represent the user-written coroutine body. Inside this region, `cir.co_return` operations mark exits from the coroutine body and represent structured control flow that transfers execution to the final suspend point. The lowering of this structured control flow into explicit branches is deferred to a future PR in the FlattenCFG pass.
>From d77d9f73174c4fb9bf167a1c1218613e9ce711a9 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Sun, 29 Mar 2026 14:35:23 -0500
Subject: [PATCH] [CIR] Add coroutine cleanup handling and update co_return
semantics
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 61 ++
clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp | 1 +
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 136 ++++-
clang/lib/CIR/CodeGen/CIRGenFunction.cpp | 4 -
clang/lib/CIR/CodeGen/CIRGenFunction.h | 2 +
clang/lib/CIR/CodeGen/CIRGenModule.h | 1 +
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 42 ++
clang/test/CIR/CodeGen/coro-task.cpp | 560 ++++++++++++-------
clang/test/CIR/IR/co-return.cir | 21 +
clang/test/CIR/IR/coro-body.cir | 19 +
clang/test/CIR/IR/invalid-co-return.cir | 5 +
clang/test/CIR/IR/invalid-coro-body.cir | 7 +
12 files changed, 636 insertions(+), 223 deletions(-)
create mode 100644 clang/test/CIR/IR/co-return.cir
create mode 100644 clang/test/CIR/IR/coro-body.cir
create mode 100644 clang/test/CIR/IR/invalid-co-return.cir
create mode 100644 clang/test/CIR/IR/invalid-coro-body.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index a5a5197cd3ea6..3e66e6c20a01e 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -3625,6 +3625,67 @@ def CIR_AwaitOp : CIR_Op<"await",[
let hasVerifier = 1;
}
+//===----------------------------------------------------------------------===//
+// CoroBody
+//===----------------------------------------------------------------------===//
+def CIR_CoroBodyOp : CIR_Op<"coro.body", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
+ RecursivelySpeculatable, AutomaticAllocationScope, NoRegionArguments,
+ RecursiveMemoryEffects
+]> {
+ let summary = "Region containing the user-authored coroutine body";
+ let description = [{
+ The `cir.coro.body` operation models the region where the user-authored
+ coroutine code is emitted.
+
+ This operation serves as a structural boundary separating the coroutine
+ setup and teardown logic (e.g. initial suspend, final suspend, and cleanup)
+ from the user-provided statements inside the coroutine.
+
+ The body region contains the code corresponding to the original function
+ body, including `co_await` and `co_return` expressions. In particular,
+ `cir.co_return` operations inside this region mark coroutine exit points
+ and introduce structured control flow that transfers execution to the
+ final suspend point of the coroutine.
+ }];
+
+ let regions = (region AnyRegion:$bodyRegion);
+
+ let skipDefaultBuilders = 1;
+
+ let builders = [
+ OpBuilder<(ins "BuilderCallbackRef":$bodyBuilder)>
+ ];
+
+ let assemblyFormat = [{
+ $bodyRegion attr-dict
+ }];
+
+ let hasLLVMLowering = false;
+ let hasVerifier = 1;
+}
+
+//===----------------------------------------------------------------------===//
+// CoReturnOp
+//===----------------------------------------------------------------------===//
+
+def CIR_CoReturnOp : CIR_Op<"co_return", [
+ NoMemoryEffect, Pure, Terminator
+]> {
+ let summary = "Coroutine return operation";
+ let description = [{
+ The `cir.co_return` operation models a coroutine return point inside a
+ `cir.coro.body` region.
+ This operation is expected to appear only within a `cir.coro.body` region.
+ }];
+
+ let assemblyFormat = "attr-dict ";
+
+ let hasVerifier = 1;
+
+ let hasLLVMLowering = false;
+}
+
//===----------------------------------------------------------------------===//
// CopyOp
diff --git a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
index a27e66e0989fa..cb059855e6d1b 100644
--- a/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenBuiltin.cpp
@@ -1215,6 +1215,7 @@ RValue CIRGenFunction::emitBuiltinExpr(const GlobalDecl &gd, unsigned builtinID,
return emitCoroutineFrame();
}
case Builtin::BI__builtin_coro_free:
+ return RValue::get(emitCoroFreeBuiltin(e).getResult());
case Builtin::BI__builtin_coro_size: {
GlobalDecl gd{fd};
mlir::Type ty = cgm.getTypes().getFunctionType(
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 036f1b1cfe637..fa1f34e052742 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -33,17 +33,16 @@ struct clang::CIRGen::CGCoroData {
// Stores the result of __builtin_coro_begin call.
mlir::Value coroBegin = nullptr;
- // Stores the insertion point for final suspend, this happens after the
- // promise call (return_xxx promise member) but before a cir.br to the return
- // block.
- mlir::Operation *finalSuspendInsPoint;
-
// How many co_return statements are in the coroutine. Used to decide whether
// we need to add co_return; equivalent at the end of the user authored body.
unsigned coreturnCount = 0;
// The promise type's 'unhandled_exception' handler, if it defines one.
Stmt *exceptionHandler = nullptr;
+
+ // Stores the last emitted coro.free for the deallocate expressions, we use it
+ // to wrap dealloc code with if(auto mem = coro.free) dealloc(mem).
+ cir::CallOp lastCoroFree = nullptr;
};
// Defining these here allows to keep CGCoroData private to this file.
@@ -110,6 +109,67 @@ struct ParamReferenceReplacerRAII {
};
} // namespace
+namespace {
+// Make sure to call coro.delete on scope exit.
+struct CallCoroDelete final : public EHScopeStack::Cleanup {
+ Stmt *deallocate;
+
+ // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
+
+ // Note: That deallocation will be emitted twice: once for a normal exit and
+ // once for exceptional exit. This usage is safe because Deallocate does not
+ // contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
+ // builds a single call to a deallocation function which is safe to emit
+ // multiple times.
+ void emit(CIRGenFunction &cgf, Flags) override {
+ // Remember the current point, as we are going to emit deallocation code
+ // first to get to coro.free instruction that is an argument to a delete
+ // call.
+
+ if (cgf.emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
+ cgf.cgm.error(deallocate->getBeginLoc(),
+ "failed to emit coroutine deallocation expression");
+ return;
+ }
+
+ CIRGenBuilderTy &builder = cgf.getBuilder();
+ mlir::Location loc = cgf.getLoc(deallocate->getSourceRange());
+ cir::CallOp coroFree = cgf.curCoro.data->lastCoroFree;
+
+ if (!coroFree) {
+ cgf.cgm.error(deallocate->getBeginLoc(),
+ "Deallocation expressoin does not refer to coro.free");
+ return;
+ }
+
+ builder.setInsertionPointAfter(coroFree);
+ cir::ConstantOp nullPtrCst = builder.getNullPtr(cgf.voidPtrTy, loc);
+ auto cmp = cir::CmpOp::create(builder, loc, cir::CmpOpKind::ne,
+ coroFree.getResult(), nullPtrCst);
+
+ llvm::SmallVector<mlir::Operation *> opsToMove;
+ mlir::Block *block = cmp->getBlock();
+ mlir::Block::iterator it(cmp);
+
+ for (++it; it != block->end(); ++it) {
+ opsToMove.push_back(&*it);
+ }
+
+ auto ifOp =
+ cir::IfOp::create(builder, cgf.getLoc(deallocate->getSourceRange()),
+ cmp.getResult(), /*withElseRegion*/ false,
+ [&](mlir::OpBuilder &builder, mlir::Location loc) {
+ cir::YieldOp::create(builder, loc);
+ });
+
+ mlir::Operation *yieldOp = ifOp.getThenRegion().back().getTerminator();
+ for (auto *op : opsToMove)
+ op->moveBefore(yieldOp);
+ }
+ explicit CallCoroDelete(Stmt *deallocStmt) : deallocate(deallocStmt) {}
+};
+} // namespace
+
RValue CIRGenFunction::emitCoroutineFrame() {
if (curCoro.data && curCoro.data->coroBegin) {
return RValue::get(curCoro.data->coroBegin);
@@ -235,6 +295,28 @@ cir::CallOp CIRGenFunction::emitCoroEndBuiltinCall(mlir::Location loc,
loc, fnOp, mlir::ValueRange{nullPtr, builder.getBool(false, loc)});
}
+cir::CallOp CIRGenFunction::emitCoroFreeBuiltin(const CallExpr *e) {
+ mlir::Operation *builtin = cgm.getGlobalValue(cgm.builtinCoroFree);
+ mlir::Location loc = getLoc(e->getBeginLoc());
+ cir::FuncOp fnOp;
+ if (!builtin) {
+ fnOp = cgm.createCIRBuiltinFunction(
+ loc, cgm.builtinCoroFree,
+ cir::FuncType::get({uInt32Ty, voidPtrTy}, voidPtrTy),
+ /*fd=*/nullptr);
+ assert(fnOp && "should always succeed");
+ } else {
+ fnOp = cast<cir::FuncOp>(builtin);
+ }
+ cir::CallOp coroFree =
+ builder.createCallOp(loc, fnOp,
+ mlir::ValueRange{curCoro.data->coroId.getResult(),
+ curCoro.data->coroBegin});
+
+ curCoro.data->lastCoroFree = coroFree;
+ return coroFree;
+}
+
mlir::LogicalResult
CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
mlir::Location openCurlyLoc = getLoc(s.getBeginLoc());
@@ -280,6 +362,8 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
{
assert(!cir::MissingFeatures::generateDebugInfo());
ParamReferenceReplacerRAII paramReplacer(localDeclMap);
+ RunCleanupsScope resumeScope(*this);
+ ehStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate());
// Create mapping between parameters and copy-params for coroutine
// function.
llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
@@ -326,11 +410,20 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
curCoro.data->currentAwaitKind = cir::AwaitKind::User;
- // FIXME(cir): wrap emitBodyAndFallthrough with try/catch bits.
- if (s.getExceptionHandler())
- assert(!cir::MissingFeatures::coroutineExceptions());
- if (emitBodyAndFallthrough(*this, s, s.getBody(), curLexScope).failed())
- return mlir::failure();
+ mlir::OpBuilder::InsertPoint userBody;
+ cir::CoroBodyOp::create(builder, openCurlyLoc, /*scopeBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ userBody = b.saveInsertionPoint();
+ });
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.restoreInsertionPoint(userBody);
+ // FIXME(cir): wrap emitBodyAndFallthrough with try/catch bits.
+ if (s.getExceptionHandler())
+ assert(!cir::MissingFeatures::coroutineExceptions());
+ if (emitBodyAndFallthrough(*this, s, s.getBody(), curLexScope).failed())
+ return mlir::failure();
+ }
// Note that LLVM checks CanFallthrough by looking into the availability
// of the insert block which is kinda brittle and unintuitive, seems to be
@@ -346,13 +439,26 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
{
mlir::OpBuilder::InsertionGuard guard(builder);
- builder.setInsertionPoint(curCoro.data->finalSuspendInsPoint);
if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
.failed())
return mlir::failure();
}
}
}
+
+ emitCoroEndBuiltinCall(
+ openCurlyLoc, builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc));
+ if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) {
+ // Since we already emitted the return value above, so we shouldn't
+ // emit it again here.
+ Expr *previousRetValue = ret->getRetValue();
+ ret->setRetValue(nullptr);
+ if (emitStmt(ret, /*useCurrentScope=*/true).failed())
+ return mlir::failure();
+ // Set the return value back. The code generator, as the AST **Consumer**,
+ // shouldn't change the AST.
+ ret->setRetValue(previousRetValue);
+ }
return mlir::success();
}
@@ -538,13 +644,7 @@ mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) {
// it. The actual return instruction is only inserted during current
// scope cleanup handling.
mlir::Location loc = getLoc(s.getSourceRange());
- mlir::Block *retBlock = curLexScope->getOrCreateRetBlock(*this, loc);
- curCoro.data->finalSuspendInsPoint =
- cir::BrOp::create(builder, loc, retBlock);
-
- // Insert the new block to continue codegen after branch to ret block,
- // this will likely be an empty block.
- builder.createBlock(builder.getBlock()->getParent());
+ cir::CoReturnOp::create(builder, loc);
return mlir::success();
}
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 8e956539da546..7643eafd2a207 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -362,10 +362,6 @@ cir::ReturnOp CIRGenFunction::LexicalScope::emitReturn(mlir::Location loc) {
auto fn = dyn_cast<cir::FuncOp>(cgf.curFn);
assert(fn && "emitReturn from non-function");
- // If we are on a coroutine, add the coro_end builtin call.
- if (fn.getCoroutine())
- cgf.emitCoroEndBuiltinCall(loc,
- builder.getNullPtr(builder.getVoidPtrTy(), loc));
if (!fn.getFunctionType().hasVoidReturn()) {
// Load the value from `__retval` and return it via the `cir.return` op.
auto value = cir::LoadOp::create(
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index ac09901f4a9f7..69f923b63a2d2 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -1468,6 +1468,8 @@ class CIRGenFunction : public CIRGenTypeCache {
cir::CallOp emitCoroAllocBuiltinCall(mlir::Location loc);
cir::CallOp emitCoroBeginBuiltinCall(mlir::Location loc,
mlir::Value coroframeAddr);
+
+ cir::CallOp emitCoroFreeBuiltin(const CallExpr *e);
RValue emitCoroutineFrame();
void emitDestroy(Address addr, QualType type, Destroyer *destroyer);
diff --git a/clang/lib/CIR/CodeGen/CIRGenModule.h b/clang/lib/CIR/CodeGen/CIRGenModule.h
index 52464a8bc30c4..14c2ab9f5833f 100644
--- a/clang/lib/CIR/CodeGen/CIRGenModule.h
+++ b/clang/lib/CIR/CodeGen/CIRGenModule.h
@@ -636,6 +636,7 @@ class CIRGenModule : public CIRGenTypeCache {
static constexpr const char *builtinCoroAlloc = "__builtin_coro_alloc";
static constexpr const char *builtinCoroBegin = "__builtin_coro_begin";
static constexpr const char *builtinCoroEnd = "__builtin_coro_end";
+ static constexpr const char *builtinCoroFree = "__builtin_coro_free";
/// Given a builtin id for a function like "__builtin_fabsf", return a
/// Function* for "fabsf".
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index d65aabf949f3f..15902a4840880 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -2819,6 +2819,48 @@ LogicalResult cir::AwaitOp::verify() {
return success();
}
+LogicalResult cir::CoReturnOp::verify() {
+ if (!getOperation()->getParentOfType<CoroBodyOp>())
+ return emitOpError("must be inside a cir.coro.body");
+ return success();
+}
+
+//===----------------------------------------------------------------------===//
+// CoroBody
+//===----------------------------------------------------------------------===//
+
+void cir::CoroBodyOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
+ if (!point.isParent()) {
+ regions.push_back(RegionSuccessor::parent());
+ return;
+ }
+
+ regions.push_back(RegionSuccessor(&getBodyRegion()));
+}
+
+mlir::ValueRange
+cir::CoroBodyOp::getSuccessorInputs(RegionSuccessor successor) {
+ return ValueRange();
+}
+
+LogicalResult cir::CoroBodyOp::verify() {
+ if (!getOperation()->getParentOfType<FuncOp>().getCoroutine())
+ return emitOpError("enclosing function must be a coroutine");
+ return success();
+}
+
+void cir::CoroBodyOp::build(OpBuilder &builder, OperationState &result,
+ BuilderCallbackRef bodyBuilder) {
+ assert(bodyBuilder &&
+ "the builder callback for 'CoroBodyOp' must be present");
+ OpBuilder::InsertionGuard guard(builder);
+
+ Region *bodyRegion = result.addRegion();
+ builder.createBlock(bodyRegion);
+ bodyBuilder(builder, result.location);
+}
+
//===----------------------------------------------------------------------===//
// CopyOp Definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGen/coro-task.cpp b/clang/test/CIR/CodeGen/coro-task.cpp
index 637b058443bc7..f900a0df0b114 100644
--- a/clang/test/CIR/CodeGen/coro-task.cpp
+++ b/clang/test/CIR/CodeGen/coro-task.cpp
@@ -196,8 +196,11 @@ VoidTask silly_task() {
// Call promise.get_return_object() to retrieve the task object.
-// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]]
-// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
+
+// CIR: cir.cleanup.scope {
+
+// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]]
+// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
// OGCG: call void @llvm.lifetime.start.p0(ptr %[[VoidPromisseAddr]])
// OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]])
@@ -210,8 +213,8 @@ VoidTask silly_task() {
// the suspend_always struct to use for cir.await. Note that we return by-value since we defer ABI lowering
// to later passes, same is done elsewhere.
-// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])
-// CIR: cir.store{{.*}} %[[Tmp0:.*]], %[[SuspendAlwaysAddr]]
+// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])
+// CIR: cir.store{{.*}} %[[Tmp0:.*]], %[[SuspendAlwaysAddr]]
// OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]])
@@ -221,9 +224,9 @@ VoidTask silly_task() {
// First regions `ready` has a special cir.yield code to veto suspension.
-// CIR: cir.await(init, ready : {
-// CIR: %[[ReadyVeto:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
-// CIR: cir.condition(%[[ReadyVeto]])
+// CIR: cir.await(init, ready : {
+// CIR: %[[ReadyVeto:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.condition(%[[ReadyVeto]])
// OGCG: %[[Tmp0:.*]] = call noundef zeroext i1 @_ZNSt14suspend_always11await_readyEv(ptr noundef nonnull align 1 dereferenceable(1) %[[SuspendAlwaysAddr]])
// OGCG: br i1 %[[Tmp0]], label %init.ready, label %init.suspend
@@ -237,14 +240,14 @@ VoidTask silly_task() {
//
// 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: }, 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
// OGCG: init.suspend:
// OGCG: %[[Save:.*]] = call token @llvm.coro.save(ptr null)
@@ -257,10 +260,10 @@ VoidTask silly_task() {
// Third region `resume` handles coroutine resuming logic.
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
-// CIR: cir.yield
-// CIR: },)
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield
+// CIR: },)
// OGCG: init.ready:
// OGCG: call void @_ZNSt14suspend_always12await_resumeEv(ptr noundef nonnull align 1 dereferenceable(1) %[[SuspendAlwaysAddr]]
@@ -272,31 +275,63 @@ VoidTask silly_task() {
// - The final suspend co_await
// - Return
+// CIR: cir.coro.body {
+
// The actual user written co_await
-// CIR: cir.await(user, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
// OGCG: cleanup.cont
// OGCG: await.suspend:
// OGCG: await.ready:
// The promise call
-// CHECK: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]])
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]])
+// CIR: cir.co_return
+// CIR: }
// OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]])
// The final suspend co_await
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
// OGCG: coro.final:
// OGCG: final.suspend:
// OGCG: final.ready:
+// Cleanup of coroutine frame.
+//
+// `__builtin_coro_free` returns the frame pointer or null.
+// If null, no dynamic allocation happened, so nothing to free.
+// The `if` ensures we only call delete on non-null.
+
+// CIR: } cleanup normal {
+// CIR: %[[FreeMem:.*]] = cir.call @__builtin_coro_free(%[[CoroId]], %[[CoroFrameAddr]])
+// CIR: %[[NullPtr2:.*]] = cir.const #cir.ptr<null>
+// CIR: %[[Cond:.*]] = cir.cmp(ne, %[[FreeMem]], %[[NullPtr2]])
+// CIR: cir.if %[[Cond]] {
+// CIR: %[[Size:.*]] = cir.call @__builtin_coro_size()
+// CIR: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]])
+// CIR: }
+// CIR: cir.yield
+// CIR: }
+
+// OGCG: %[[FreeMem:.*]] = call ptr @llvm.coro.free(token %[[CoroId]], ptr %[[CoroFrameAddr]])
+// OGCG: %[[Cond:.*]] = icmp ne ptr %[[FreeMem]], null
+// OGCG: br i1 %[[Cond]], label %coro.free, label %after.coro.free
+
+// OGCG: coro.free:
+// OGCG: %[[Size:.*]] = call i64 @llvm.coro.size.i64()
+// OGCG: call void @_ZdlPvm(ptr {{.*}} %[[FreeMem]], i64 {{.*}} %[[Size]])
+// OGCG: br label %after.coro.free
+// OGCG: after.coro.free:
+
// Call builtin coro end and return
// CIR: %[[CoroEndArg0:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
@@ -326,38 +361,46 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca ![[CoroHandlePromiseInt]], {{.*}} ["agg.tmp1"] {alignment = 1 : i64}
// CIR: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr<![[StdString]]>, {{.*}}
+// CIR: cir.cleanup.scope {
// Call promise.get_return_object() to retrieve the task object.
-// CIR: %[[LOAD:.*]] = cir.load %[[AllocaParam]] : !cir.ptr<!cir.ptr<![[StdString]]>>, !cir.ptr<![[StdString]]>
-// CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>
-// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ![[IntTask]]
-// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]
-// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
-// CIR: cir.store{{.*}} %[[Tmp0]], %[[SuspendAlwaysAddr]]
-// CIR: cir.await(init, ready : {
-// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
-// CIR: cir.condition(%[[TmpCallRes]])
-// CIR: }, suspend : {
-// 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.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
-// CIR: cir.yield
-// CIR: },)
-
-// can't fallthrough
+// CIR: %[[LOAD:.*]] = cir.load %[[AllocaParam]] : !cir.ptr<!cir.ptr<![[StdString]]>>, !cir.ptr<![[StdString]]>
+// CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>
+// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ![[IntTask]]
+// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]
+// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
+// CIR: cir.store{{.*}} %[[Tmp0]], %[[SuspendAlwaysAddr]]
+// CIR: cir.await(init, ready : {
+// CIR: %[[TmpCallRes:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.condition(%[[TmpCallRes]])
+// CIR: }, suspend : {
+// 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.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.coro.body {
+ // can't fallthrough
// CIR-NOT: cir.await(user
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[IntPromisseAddr]], %[[STRING_SIZE:.*]])
+//CIR: cir.co_return
+// CIR: }
+
// The final suspend co_await
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
+// CIR: }
folly::coro::Task<void> silly_coro() {
std::optional<folly::coro::Task<int>> task;
@@ -373,16 +416,22 @@ folly::coro::Task<void> silly_coro() {
// check there are not multiple co_returns emitted.
// CIR: cir.func coroutine {{.*}} @_Z10silly_corov() {{.*}} ![[VoidTask]]
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
-// CIR-NOT: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
+// CIR: cir.co_return
+// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
+// CIR: }
folly::coro::Task<void> yield();
folly::coro::Task<void> yield1() {
@@ -408,67 +457,74 @@ folly::coro::Task<void> yield1() {
// CIR-DAG: %[[CH_VOID2:.*]] = cir.alloca ![[CoroHandleVoid]], {{.*}} ["agg.tmp5"]
// CIR-DAG: %[[CH_PROM2:.*]] = cir.alloca ![[CoroHandlePromiseVoid]], {{.*}} ["agg.tmp6"]
+// CIR: cir.cleanup.scope {
// initial_suspend + await(init)
-// CIR: %[[INIT_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}}
-// CIR: cir.store{{.*}} %[[INIT_SUSP]], %[[SUSP0]]
-// CIR: cir.await(init, ready : {
-// CIR: %[[READY0:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP0]]){{.*}}
-// CIR: cir.condition(%[[READY0]])
-// CIR: }, suspend : {
-// CIR: %[[FROMADDR0:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: cir.store{{.*}} %[[FROMADDR0]], %[[CH_PROM0]]
-// CIR: %[[PROM_RELOAD0:.*]] = cir.load{{.*}} %[[CH_PROM0]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]], %[[PROM_RELOAD0]]){{.*}}
-// CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}}
-// CIR: cir.yield
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
+// CIR: %[[INIT_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: cir.store{{.*}} %[[INIT_SUSP]], %[[SUSP0]]
+// CIR: cir.await(init, ready : {
+// CIR: %[[READY0:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP0]]){{.*}}
+// CIR: cir.condition(%[[READY0]])
+// CIR: }, suspend : {
+// CIR: %[[FROMADDR0:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: cir.store{{.*}} %[[FROMADDR0]], %[[CH_PROM0]]
+// CIR: %[[PROM_RELOAD0:.*]] = cir.load{{.*}} %[[CH_PROM0]]
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]], %[[PROM_RELOAD0]]){{.*}}
+// CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]]
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}}
+// CIR: cir.yield
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
+// CIR: cir.yield
+// CIR: },)
// yield_value + await(yield)
-// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
-// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]]
-// CIR: cir.copy %[[T_ADDR]] to %[[AWAITER_COPY_ADDR]]
-// CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]]
-// CIR: %[[YIELD_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]], %[[AWAITER]]){{.*}}
-// CIR: cir.store{{.*}} %[[YIELD_SUSP]], %[[SUSP1]]
-// CIR: cir.await(yield, ready : {
-// CIR: %[[READY1:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP1]]){{.*}}
-// CIR: cir.condition(%[[READY1]])
-// CIR: }, suspend : {
-// CIR: %[[FROMADDR1:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: cir.store{{.*}} %[[FROMADDR1]], %[[CH_PROM1]]
-// CIR: %[[PROM_RELOAD1:.*]] = cir.load{{.*}} %[[CH_PROM1]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]], %[[PROM_RELOAD1]]){{.*}}
-// CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}}
-// CIR: cir.yield
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
+// CIR: cir.coro.body {
+// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
+// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]]
+// CIR: cir.copy %[[T_ADDR]] to %[[AWAITER_COPY_ADDR]]
+// CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]]
+// CIR: %[[YIELD_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]], %[[AWAITER]]){{.*}}
+// CIR: cir.store{{.*}} %[[YIELD_SUSP]], %[[SUSP1]]
+// CIR: cir.await(yield, ready : {
+// CIR: %[[READY1:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP1]]){{.*}}
+// CIR: cir.condition(%[[READY1]])
+// CIR: }, suspend : {
+// CIR: %[[FROMADDR1:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: cir.store{{.*}} %[[FROMADDR1]], %[[CH_PROM1]]
+// CIR: %[[PROM_RELOAD1:.*]] = cir.load{{.*}} %[[CH_PROM1]]
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]], %[[PROM_RELOAD1]]){{.*}}
+// CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]]
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}}
+// CIR: cir.yield
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}}
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[PROMISE]])
+// CIR: cir.co_return
+// CIR: }
// return_void + await(final)
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[PROMISE]]){{.*}}
-// CIR: %[[FINAL_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type13final_suspendEv(%[[PROMISE]]){{.*}}
-// CIR: cir.store{{.*}} %[[FINAL_SUSP]], %[[SUSP2]]
-// CIR: cir.await(final, ready : {
-// CIR: %[[READY2:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP2]]){{.*}}
-// CIR: cir.condition(%[[READY2]])
-// CIR: }, suspend : {
-// CIR: %[[FROMADDR2:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: cir.store{{.*}} %[[FROMADDR2]], %[[CH_PROM2]]
-// CIR: %[[PROM_RELOAD2:.*]] = cir.load{{.*}} %[[CH_PROM2]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]], %[[PROM_RELOAD2]]){{.*}}
-// CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}}
+// CIR: %[[FINAL_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type13final_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: cir.store{{.*}} %[[FINAL_SUSP]], %[[SUSP2]]
+// CIR: cir.await(final, ready : {
+// CIR: %[[READY2:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP2]]){{.*}}
+// CIR: cir.condition(%[[READY2]])
+// CIR: }, suspend : {
+// CIR: %[[FROMADDR2:.*]] = cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: cir.store{{.*}} %[[FROMADDR2]], %[[CH_PROM2]]
+// CIR: %[[PROM_RELOAD2:.*]] = cir.load{{.*}} %[[CH_PROM2]]
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]], %[[PROM_RELOAD2]]){{.*}}
+// CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]]
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}}
+// CIR: cir.yield
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
+// CIR: cir.yield
+// CIR: },)
// CIR: cir.yield
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
+// CIR: } cleanup normal {
+// CIR: }
// CIR: = cir.call @__builtin_coro_end(%{{.*}}, %{{.*}}){{.*}}
// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
// CIR: cir.return %[[RETLOAD]]
@@ -485,34 +541,41 @@ folly::coro::Task<int> go1() {
// CIR: cir.func coroutine {{.*}} @_Z3go1v() {{.*}} ![[IntTask]]
// CIR: %[[IntTaskAddr:.*]] = cir.alloca ![[IntTask]], !cir.ptr<![[IntTask]]>, ["task", init]
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
// The call to go(1) has its own scope due to full-expression rules.
-// CIR: cir.scope {
-// CIR: %[[OneAddr:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp1", init] {alignment = 4 : i64}
-// CIR: %[[One:.*]] = cir.const #cir.int<1> : !s32i
-// CIR: cir.store{{.*}} %[[One]], %[[OneAddr]] : !s32i, !cir.ptr<!s32i>
-// CIR: %[[IntTaskTmp:.*]] = cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>{{.*}}) -> ![[IntTask]]
-// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: cir.scope {
+// CIR: %[[OneAddr:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp1", init] {alignment = 4 : i64}
+// CIR: %[[One:.*]] = cir.const #cir.int<1> : !s32i
+// CIR: cir.store{{.*}} %[[One]], %[[OneAddr]] : !s32i, !cir.ptr<!s32i>
+// CIR: %[[IntTaskTmp:.*]] = cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>{{.*}}) -> ![[IntTask]]
+// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: }
+
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: %[[ResumeVal:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[IntTaskAddr]])
+// CIR: cir.store{{.*}} %[[ResumeVal]], %[[CoReturnValAddr:.*]] : !s32i, !cir.ptr<!s32i>
+// CIR: },)
+// CIR: %[[V:.*]] = cir.load{{.*}} %[[CoReturnValAddr]] : !cir.ptr<!s32i>, !s32i
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}}, %[[V]])
+// CIR: cir.co_return
+// CIR: }
+
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
// CIR: }
-// CIR: cir.await(user, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: %[[ResumeVal:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[IntTaskAddr]])
-// CIR: cir.store{{.*}} %[[ResumeVal]], %[[CoReturnValAddr:.*]] : !s32i, !cir.ptr<!s32i>
-// CIR: },)
-// CIR: %[[V:.*]] = cir.load{{.*}} %[[CoReturnValAddr]] : !cir.ptr<!s32i>, !s32i
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}}, %[[V]])
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-
folly::coro::Task<int> go1_lambda() {
auto task = []() -> folly::coro::Task<int> {
@@ -522,27 +585,45 @@ folly::coro::Task<int> go1_lambda() {
}
// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]]
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[ONE]])
+// CIR: cir.co_return
+// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: } cleanup normal {
+
// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() {{.*}} ![[IntTask]]
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.await(user, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
+// CIR: cir.call @_ZZ10go1_lambdavENK3$_0clEv
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: %[[RESUME_RES:.*]] = cir.call @_ZN5folly4coro4TaskIiE12await_resumeEv(%[[TASK:.*]])
+// CIR: cir.store %[[RESUME_RES]], %[[resume_rval:.*]] : !s32i, !cir.ptr<!s32i>
+// CIR: },)
+// CIR: %[[TMP1:.*]] = cir.load %[[resume_rval:.*]] : !cir.ptr<!s32i>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[TMP1]])
+// CIR: cir.co_return
+// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: } cleanup normal {
folly::coro::Task<int> go4() {
auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
@@ -551,50 +632,61 @@ folly::coro::Task<int> go4() {
}
// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[I:.*]])
+// CIR: cir.co_return
+// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: } cleanup normal {
// CIR: cir.func coroutine {{.*}} @_Z3go4v() {{.*}} ![[IntTask]]
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
// Get the lambda invoker ptr via `lambda operator folly::coro::Task<int> (*)(int const&)()`
-// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
-// CIR: %[[PLUS:.*]] = cir.unary(plus, %[[INVOKER]]) : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
-// CIR: cir.store{{.*}} %[[PLUS]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
-// CIR: cir.scope {
-// CIR: %[[ARG:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp2", init] {alignment = 4 : i64}
-// CIR: %[[FN:.*]] = cir.load{{.*}} %[[FN_ADDR]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
-// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
-// CIR: cir.store{{.*}} %[[THREE]], %[[ARG]] : !s32i, !cir.ptr<!s32i>
-
-// Call invoker, which calls operator() indirectly.
-// CIR: %[[CALLRES:.*]] = cir.call %[[FN]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!s32i> {{.*}}) -> ![[IntTask]]
-// CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
+// CIR: %[[PLUS:.*]] = cir.unary(plus, %[[INVOKER]]) : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: cir.store{{.*}} %[[PLUS]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
+// CIR: cir.scope {
+// CIR: %[[ARG:.*]] = cir.alloca !s32i, !cir.ptr<!s32i>, ["ref.tmp2", init] {alignment = 4 : i64}
+// CIR: %[[FN:.*]] = cir.load{{.*}} %[[FN_ADDR]] : !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>, !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>
+// CIR: %[[THREE:.*]] = cir.const #cir.int<3> : !s32i
+// CIR: cir.store{{.*}} %[[THREE]], %[[ARG]] : !s32i, !cir.ptr<!s32i>
+
+// Call invoker, which calls operator() indirectly.
+// CIR: %[[CALLRES:.*]] = cir.call %[[FN]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!s32i> {{.*}}) -> ![[IntTask]]
+// CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: }
+
+// CIR: cir.await(user, ready : {
+// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
+// CIR: cir.condition(
+// CIR: }, suspend : {
+// CIR: cir.yield
+// CIR: }, resume : {
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi
+// CIR: cir.co_return
// CIR: }
-// CIR: cir.await(user, ready : {
-// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
-// CIR: cir.condition(
-// CIR: }, suspend : {
-// CIR: cir.yield
-// CIR: }, resume : {
-// CIR: cir.yield
-// CIR: },)
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: } cleanup normal {
// OGCG: define {{.*}}__await_suspend_wrapper__init(ptr noundef nonnull %[[Awaiter:.*]], ptr noundef %[[Handle:.*]])
// OGCG: entry:
@@ -610,3 +702,69 @@ folly::coro::Task<int> go4() {
// OGCG: call void @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(ptr noundef nonnull align 1 dereferenceable(1) %[[AwaiterReload]])
// OGCG: ret void
// OGCG: }
+
+folly::coro::Task<int> co_returns(int x) {
+ if (x < 0)
+ co_return -1;
+ else if (x == 1)
+ co_return -2;
+
+ co_await std::suspend_always();
+
+ co_return x * 2;
+}
+
+// CIR: cir.func coroutine {{.*}} @_Z10co_returnsi
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.coro.body {
+// CIR: cir.scope {
+// CIR: cir.if {{.*}} {
+// CIR: %[[MINUS_ONE:.*]] = cir.const #cir.int<-1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[MINUS_ONE]])
+// CIR: cir.co_return
+// CIR: } else {
+// CIR: cir.if {{.*}} {
+// CIR: %[[MINUS_TWO:.*]] = cir.const #cir.int<-2>
+ // CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[MINUS_TWO]])
+ // CIR: cir.co_return
+// CIR: }
+// CIR: }
+// CIR: }
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: %[[X_LOAD:.*]] = cir.load {{.*}} %[[X:.*]]
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: %[[RES:.*]] = cir.binop(mul, %[[X_LOAD]], %[[TWO]])
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[RES]])
+// CIR: cir.co_return
+// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
+
+
+// OGCG: define {{.*}} @_Z10co_returnsi
+// OGCG: coro.init:
+// OGCG: init.suspend:
+// OGCG: init.ready:
+// OGCG: if.then:
+// OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise:.*]], {{.*}} -1)
+// OGCG: br label %coro.final
+// OGCG: if.else:
+// OGCG: if.then5:
+// OGCG: call void @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi({{.*}} %[[promise:.*]], {{.*}} -2)
+// OGCG: br label %coro.final
+// OGCG: await.suspend:
+// OGCG: await.ready:
+// OGCG: coro.final:
+// OGCG: final.suspend:
+// OGCG: final.ready:
diff --git a/clang/test/CIR/IR/co-return.cir b/clang/test/CIR/IR/co-return.cir
new file mode 100644
index 0000000000000..613399b142650
--- /dev/null
+++ b/clang/test/CIR/IR/co-return.cir
@@ -0,0 +1,21 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+cir.func coroutine @coro_co_return(%arg0 : !cir.bool) {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
+ cir.co_return
+ }
+ cir.return
+}
+
+// CHECK: cir.func coroutine @coro_co_return
+// CHECK: cir.coro.body {
+// CHECK: cir.co_return
+// CHECK: }
+
+
diff --git a/clang/test/CIR/IR/coro-body.cir b/clang/test/CIR/IR/coro-body.cir
new file mode 100644
index 0000000000000..1c0dae384691a
--- /dev/null
+++ b/clang/test/CIR/IR/coro-body.cir
@@ -0,0 +1,19 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+cir.func coroutine @coro_body(%arg0 : !cir.bool) {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
+ cir.co_return
+ }
+ cir.return
+}
+
+// CHECK: cir.func coroutine @coro_body
+// CHECK: cir.coro.body {
+// CHECK: }
diff --git a/clang/test/CIR/IR/invalid-co-return.cir b/clang/test/CIR/IR/invalid-co-return.cir
new file mode 100644
index 0000000000000..6085bcfe3a408
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-co-return.cir
@@ -0,0 +1,5 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+cir.func @must_be_inside_coro_body() {
+ cir.co_return // expected-error {{must be inside a cir.coro.body}}
+}
diff --git a/clang/test/CIR/IR/invalid-coro-body.cir b/clang/test/CIR/IR/invalid-coro-body.cir
new file mode 100644
index 0000000000000..c304030fd2040
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-coro-body.cir
@@ -0,0 +1,7 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+cir.func @must_be_inside_coroutine() {
+ cir.coro.body { // expected-error {{enclosing function must be a coroutine}}
+
+ }
+}
More information about the cfe-commits
mailing list