[clang] [CIR] Add structured control flow for coroutine suspend points (PR #213191)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Sep 24 18:51:48 PDT 2026
https://github.com/Andres-Salamanca updated https://github.com/llvm/llvm-project/pull/213191
>From 35d092ef1f807b63a13c222dd8d925a19c8a221c Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Mon, 13 Jul 2026 14:34:45 -0500
Subject: [PATCH 01/12] initial coro_suspend
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 36 +++++++++++++++++++
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 4 ++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 12 +++++++
.../test/CIR/CodeGenCoroutines/coro-task.cpp | 22 ++++++------
clang/test/CIR/IR/await.cir | 6 ++--
clang/test/CIR/IR/co-return.cir | 3 +-
clang/test/CIR/IR/coro-body.cir | 3 +-
clang/test/CIR/IR/func.cir | 3 +-
clang/test/CIR/IR/invalid-await.cir | 18 +++++++++-
clang/test/CIR/IR/invalid-coro-body.cir | 3 +-
clang/test/CIR/IR/invalid-coro-suspend.cir | 17 +++++++++
11 files changed, 108 insertions(+), 19 deletions(-)
create mode 100644 clang/test/CIR/IR/invalid-coro-suspend.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index f6b524176a6559..ad4b09501fc388 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5122,6 +5122,42 @@ def CIR_CoroDoneOp : CIR_CoroIntrinsicOp<"done", (ins CIR_VoidPtrType:$handle),
let llvmOp = "CoroDoneOp";
}
+//===----------------------------------------------------------------------===//
+// CoroSuspendPoint
+//===----------------------------------------------------------------------===//
+
+def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend.point", [
+ Pure, Terminator, HasParent<"AwaitOp">
+]> {
+ let summary = "";
+ let description = [{
+ }];
+
+ let assemblyFormat = [{
+ attr-dict
+ }];
+
+ let hasLLVMLowering = false;
+}
+
+//===----------------------------------------------------------------------===//
+// CoroSuspendPointDest
+//===----------------------------------------------------------------------===//
+
+def CIR_CoroSuspendPointDest : CIR_Op<"coro.suspend.point.dest", [
+ Pure
+]> {
+ let summary = "";
+ let description = [{
+ }];
+
+ let assemblyFormat = [{
+ attr-dict
+ }];
+
+ let hasLLVMLowering = false;
+}
+
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 6f2c0e33ce1d96..b28881f0cd0f19 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -559,6 +559,8 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind, tkNone);
+ cir::CoroSuspendPointDest::create(builder, 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.
@@ -640,7 +642,7 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
}
// Signals the parent that execution flows to next region.
- cir::YieldOp::create(builder, loc);
+ cir::CoroSuspendPoint::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 6bbf338fcbda12..bba9dca93861a6 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -32,6 +32,7 @@
#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallSet.h"
#include "llvm/ADT/TypeSwitch.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/LogicalResult.h"
using namespace mlir;
@@ -3134,6 +3135,7 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (!isDeclaration() && getCoroutine()) {
bool foundAwait = false;
int coroBodyCount = 0;
+ int coroSuspendPointDestCount = 0;
this->walk([&](Operation *op) {
if (auto await = dyn_cast<AwaitOp>(op)) {
foundAwait = true;
@@ -3142,6 +3144,11 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount > 1) {
return mlir::WalkResult::interrupt();
}
+ } else if (isa<CoroSuspendPointDest>(op)) {
+ coroSuspendPointDestCount++;
+ if (coroSuspendPointDestCount > 1) {
+ return mlir::WalkResult::interrupt();
+ }
}
return mlir::WalkResult::advance();
});
@@ -3151,6 +3158,9 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount != 1)
return emitOpError()
<< "coroutine function must have exactly one cir.body op";
+ if (coroSuspendPointDestCount != 1)
+ return emitOpError()
+ << "coroutine function must have exactly one cir.coro.suspend.point.dest";
}
llvm::SmallSet<llvm::StringRef, 16> labels;
@@ -3548,6 +3558,8 @@ void cir::AwaitOp::getSuccessorRegions(
LogicalResult cir::AwaitOp::verify() {
if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
return emitOpError("ready region must end with cir.condition");
+ if (!isa<CoroSuspendPoint>(this->getSuspend().back().getTerminator()))
+ return emitOpError("ready region must end with cir.coro.suspend.point");
return success();
}
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index 56360d852b0779..3ea3b903d153a7 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -131,8 +131,8 @@ VoidTask silly_task() {
// CIR-NEXT: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
// CIR-NEXT: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
// CIR-NEXT: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
-// CIR-NEXT: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]])
-// CIR-NEXT: cir.yield
+// CIR-NEXT: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
+// CIR-NEXT: cir.coro.suspend.point
// OGCG: init.suspend:
// OGCG: %[[Save:.*]] = call token @llvm.coro.save(ptr null)
@@ -268,8 +268,8 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]])
-// CIR: cir.yield
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
+// CIR: cir.coro.suspend.point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
// CIR: cir.yield
@@ -361,8 +361,8 @@ folly::coro::Task<void> yield1() {
// CIR: %[[PROM_RELOAD0:.*]] = cir.load{{.*}} %[[CH_PROM0]]
// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]]){{.*}}
// CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]]){{.*}}
-// CIR: cir.yield
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}}
+// CIR: cir.coro.suspend.point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
// CIR: cir.yield
@@ -387,8 +387,8 @@ folly::coro::Task<void> yield1() {
// CIR: %[[PROM_RELOAD1:.*]] = cir.load{{.*}} %[[CH_PROM1]]
// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]]){{.*}}
// CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]]){{.*}}
-// CIR: cir.yield
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}}
+// CIR: cir.coro.suspend.point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}}
// CIR: cir.yield
@@ -411,8 +411,8 @@ folly::coro::Task<void> yield1() {
// CIR: %[[PROM_RELOAD2:.*]] = cir.load{{.*}} %[[CH_PROM2]]
// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]]){{.*}}
// CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]]){{.*}}
-// CIR: cir.yield
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}}
+// CIR: cir.coro.suspend.point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
// CIR: cir.yield
@@ -550,7 +550,7 @@ folly::coro::Task<int> go4() {
// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
// CIR: cir.condition(
// CIR: }, suspend : {
-// CIR: cir.yield
+// CIR: cir.coro.suspend.point
// CIR: }, resume : {
// CIR: cir.yield
// CIR: },)
diff --git a/clang/test/CIR/IR/await.cir b/clang/test/CIR/IR/await.cir
index 06b925bcc07b39..1305c4bb31e0f9 100644
--- a/clang/test/CIR/IR/await.cir
+++ b/clang/test/CIR/IR/await.cir
@@ -5,12 +5,13 @@ cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
cir.await(user, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
cir.yield
}
+ cir.coro.suspend.point.dest
cir.return
}
@@ -18,7 +19,8 @@ cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
// CHECK: cir.await(user, ready : {
// CHECK: cir.condition(%arg0)
// CHECK: }, suspend : {
-// CHECK: cir.yield
+// CHECK: cir.coro.suspend.point
// CHECK: }, resume : {
// CHECK: cir.yield
// CHECK: },)
+// CHECK: cir.coro.suspend.point.dest
diff --git a/clang/test/CIR/IR/co-return.cir b/clang/test/CIR/IR/co-return.cir
index 613399b1426505..d6c3be2da80ada 100644
--- a/clang/test/CIR/IR/co-return.cir
+++ b/clang/test/CIR/IR/co-return.cir
@@ -4,12 +4,13 @@ cir.func coroutine @coro_co_return(%arg0 : !cir.bool) {
cir.await(user, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
cir.co_return
}
+ cir.coro.suspend.point.dest
cir.return
}
diff --git a/clang/test/CIR/IR/coro-body.cir b/clang/test/CIR/IR/coro-body.cir
index 1c0dae384691a3..b1560485303b1c 100644
--- a/clang/test/CIR/IR/coro-body.cir
+++ b/clang/test/CIR/IR/coro-body.cir
@@ -5,12 +5,13 @@ cir.func coroutine @coro_body(%arg0 : !cir.bool) {
cir.await(user, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
cir.co_return
}
+ cir.coro.suspend.point.dest
cir.return
}
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index 591c397e24e8c8..8b9cc2c8947865 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -115,12 +115,13 @@ cir.func coroutine @coro() {
%1 = cir.load align(1) %0 : !cir.ptr<!cir.bool>, !cir.bool
cir.condition(%1)
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
cir.yield
}
+ cir.coro.suspend.point.dest
cir.return
}
// CHECK: cir.func{{.*}} coroutine @coro()
diff --git a/clang/test/CIR/IR/invalid-await.cir b/clang/test/CIR/IR/invalid-await.cir
index fe0fd76769e44e..cc6d011718ad00 100644
--- a/clang/test/CIR/IR/invalid-await.cir
+++ b/clang/test/CIR/IR/invalid-await.cir
@@ -10,12 +10,28 @@ cir.func coroutine @missing_condition() {
cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
cir.yield
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
}
cir.coro.body {
}
+ cir.coro.suspend.point.dest
+ cir.return
+}
+
+cir.func coroutine @missing_suspend(%arg0 : !cir.bool) {
+ cir.coro.body {
+ cir.await(user, ready : { // expected-error {{ready region must end with cir.coro.suspend.point}}
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }
+ cir.coro.suspend.point.dest
cir.return
}
diff --git a/clang/test/CIR/IR/invalid-coro-body.cir b/clang/test/CIR/IR/invalid-coro-body.cir
index e08c96192e5b58..f247220c7d73d2 100644
--- a/clang/test/CIR/IR/invalid-coro-body.cir
+++ b/clang/test/CIR/IR/invalid-coro-body.cir
@@ -11,11 +11,12 @@ cir.func coroutine @must_have_one_coro_body(%arg0 : !cir.bool) { // expected-er
cir.await(user, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend.point
}, resume : {
cir.yield
},)
}
cir.coro.body {
}
+ cir.coro.suspend.point.dest
}
diff --git a/clang/test/CIR/IR/invalid-coro-suspend.cir b/clang/test/CIR/IR/invalid-coro-suspend.cir
new file mode 100644
index 00000000000000..3a45e22433a589
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-coro-suspend.cir
@@ -0,0 +1,17 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+
+cir.func coroutine @must_have_one_coro_suspend_dest(%arg0 : !cir.bool) { // expected-error {{coroutine function must have exactly one cir.coro.suspend.point.dest}}
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend.point
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.coro.suspend.point.dest
+ cir.coro.suspend.point.dest
+}
+
>From b7cfbe6542c80ad772d246e84a5ec9c7a12ac458 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 30 Jul 2026 21:15:54 -0500
Subject: [PATCH 02/12] [CIR] Add structured control flow for coroutine suspend
points
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 78 +-
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 31 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 82 +-
.../test/CIR/CodeGenCoroutines/coro-task.cpp | 702 +++++++++---------
clang/test/CIR/IR/await.cir | 25 +-
clang/test/CIR/IR/co-return.cir | 26 +-
clang/test/CIR/IR/coro-body.cir | 26 +-
clang/test/CIR/IR/coro-ret-point.cir | 23 +
clang/test/CIR/IR/func.cir | 26 +-
clang/test/CIR/IR/invalid-await.cir | 36 +-
clang/test/CIR/IR/invalid-coro-body.cir | 3 +-
clang/test/CIR/IR/invalid-coro-ret-point.cir | 55 ++
clang/test/CIR/IR/invalid-coro-suspend.cir | 30 +-
13 files changed, 710 insertions(+), 433 deletions(-)
create mode 100644 clang/test/CIR/IR/coro-ret-point.cir
create mode 100644 clang/test/CIR/IR/invalid-coro-ret-point.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index ad4b09501fc388..9916eb373afe74 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1014,7 +1014,7 @@ def CIR_StoreOp : CIR_Op<"store", [
defvar CIR_ReturnableScopes = [
"FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp", "CleanupScopeOp",
- "DoWhileOp", "WhileOp", "ForOp", "TryOp"
+ "DoWhileOp", "WhileOp", "ForOp", "TryOp", "CoroRetPointOp"
];
def CIR_ReturnOp : CIR_Op<"return", [
@@ -1164,7 +1164,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
defvar CIR_YieldableScopes = [
"ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "CoroBodyOp",
"DoWhileOp", "ForOp", "GlobalOp", "IfOp", "LocalInitOp", "ScopeOp", "SwitchOp",
- "TernaryOp", "TryOp", "WhileOp"
+ "TernaryOp", "TryOp", "WhileOp", "CoroRetPointOp"
];
def CIR_YieldOp : CIR_Op<"yield", [
@@ -5126,11 +5126,36 @@ def CIR_CoroDoneOp : CIR_CoroIntrinsicOp<"done", (ins CIR_VoidPtrType:$handle),
// CoroSuspendPoint
//===----------------------------------------------------------------------===//
-def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend.point", [
- Pure, Terminator, HasParent<"AwaitOp">
+def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend_point", [
+ Pure, Terminator, HasParent<"AwaitOp">, HasAncestor<"CoroRetPointOp">
]> {
- let summary = "";
+ let summary = "Marks the point where a coroutine actually suspends";
let description = [{
+ Terminates the `suspend` region of a `cir.await` op, marking the exact
+ point where control returns to the caller/resumer if the coroutine
+ decides to suspend.
+
+ This op must appear inside the `suspend` region of a `cir.await`, and
+ that `cir.await` must in turn be nested within a `cir.coro.ret_point`.
+ During lowering to FlattenCFG, `cir.coro.suspend_point` becomes the
+ branch target that routes control to one of three destinations
+ depending on how the coroutine resumes: the resume block (normal
+ continuation), the cleanup/destroy block, or the ret/exit block that
+ hands control back to the caller.
+
+ Example:
+ ```mlir
+ cir.await(user, ready : {
+ ...
+ cir.condition(%ready)
+ }, suspend : {
+ ...
+ cir.coro.suspend_point
+ }, resume : {
+ ...
+ cir.yield
+ },)
+ ```
}];
let assemblyFormat = [{
@@ -5141,21 +5166,54 @@ def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend.point", [
}
//===----------------------------------------------------------------------===//
-// CoroSuspendPointDest
+// CoroRetPoint
//===----------------------------------------------------------------------===//
-def CIR_CoroSuspendPointDest : CIR_Op<"coro.suspend.point.dest", [
- Pure
+def CIR_CoroRetPointOp : CIR_Op<"coro.ret_point", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
+ NoRegionArguments
]> {
- let summary = "";
+
+ let summary = "Marks the coroutine's structured suspend/exit region";
let description = [{
+ Pairs a coroutine's body its normal execution, including any
+ `cir.await`s and cleanups with a single, shared exit destination
+ (`retRegion`) that every suspend point in the body jumps to.
+
+ Every `cir.coro.suspend_point` inside `bodyRegion` is guaranteed to have
+ `cir.coro.ret_point` as an ancestor, so all suspend paths converge on
+ the same `retRegion` regardless of where in the body they occur. This
+ gives the coroutine a single, well-defined place to run final teardown
+ (e.g. `__builtin_coro_end`) and return control to the caller.
+
+ Example:
+ ```mlir
+ cir.coro.ret_point {
+ // body: awaits, cleanups, etc.
+ }, ret : {
+ // shared jump target for any suspend point in the body
+ cir.coro.intrinsic.end(...)
+ cir.return
+ }
+ ```
}];
+ let regions = (region AnyRegion:$bodyRegion,
+ AnyRegion:$retRegion);
+ let skipDefaultBuilders = 1;
+
+ let builders = [
+ OpBuilder<(ins "BuilderCallbackRef":$bodyBuilder,
+ "BuilderCallbackRef":$retBuilder)>
+ ];
+
let assemblyFormat = [{
+ $bodyRegion `,`
+ `ret` `:` $retRegion
attr-dict
}];
-
let hasLLVMLowering = false;
+ let hasVerifier = 1;
}
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index b28881f0cd0f19..f9ee288e1f29ef 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -15,8 +15,6 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/TargetInfo.h"
-#include "clang/CIR/Dialect/IR/CIRDialect.h"
-#include "clang/CIR/Dialect/IR/CIRTypes.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
@@ -424,10 +422,25 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
curCoro.data->coroId.getResult(),
cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)});
+ cir::CoroRetPointOp coroRet = nullptr;
+ mlir::OpBuilder::InsertPoint coroRetRegion;
{
assert(!cir::MissingFeatures::generateDebugInfo());
ParamReferenceReplacerRAII paramReplacer(localDeclMap);
RunCleanupsScope resumeScope(*this);
+ mlir::OpBuilder::InsertPoint coroRetBody;
+ coroRet = cir::CoroRetPointOp::create(
+ builder, openCurlyLoc,
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location) {
+ coroRetBody = b.saveInsertionPoint();
+ },
+ /*retBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location) {
+ coroRetRegion = b.saveInsertionPoint();
+ });
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.restoreInsertionPoint(coroRetBody);
ehStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate());
// Create mapping between parameters and copy-params for coroutine
// function.
@@ -559,7 +572,15 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind, tkNone);
- cir::CoroSuspendPointDest::create(builder, openCurlyLoc);
+ mlir::Block &coroRetBodyBlock = coroRet.getBodyRegion().back();
+ {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.setInsertionPointToEnd(&coroRetBodyBlock);
+ cir::YieldOp::create(builder, openCurlyLoc);
+ }
+
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.restoreInsertionPoint(coroRetRegion);
if (auto *ret = cast_or_null<ReturnStmt>(s.getReturnStmt())) {
// Since we already emitted the return value above, so we shouldn't
@@ -568,6 +589,10 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
ret->setRetValue(nullptr);
if (emitStmt(ret, /*useCurrentScope=*/true).failed())
return mlir::failure();
+ mlir::Block *block = builder.getInsertionBlock();
+ // emitReturnStmt() always creates a new insertion block after emitting the
+ // return. That block is unreachable in this case, so erase it.
+ block->erase();
// Set the return value back. The code generator, as the AST **Consumer**,
// shouldn't change the AST.
ret->setRetValue(previousRetValue);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index bba9dca93861a6..7ddc9636eab1bd 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3135,7 +3135,7 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (!isDeclaration() && getCoroutine()) {
bool foundAwait = false;
int coroBodyCount = 0;
- int coroSuspendPointDestCount = 0;
+ int coroRetPointCount = 0;
this->walk([&](Operation *op) {
if (auto await = dyn_cast<AwaitOp>(op)) {
foundAwait = true;
@@ -3144,9 +3144,9 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount > 1) {
return mlir::WalkResult::interrupt();
}
- } else if (isa<CoroSuspendPointDest>(op)) {
- coroSuspendPointDestCount++;
- if (coroSuspendPointDestCount > 1) {
+ } else if (isa<CoroRetPointOp>(op)) {
+ coroRetPointCount++;
+ if (coroRetPointCount > 1) {
return mlir::WalkResult::interrupt();
}
}
@@ -3158,9 +3158,9 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount != 1)
return emitOpError()
<< "coroutine function must have exactly one cir.body op";
- if (coroSuspendPointDestCount != 1)
- return emitOpError()
- << "coroutine function must have exactly one cir.coro.suspend.point.dest";
+ if (coroRetPointCount != 1)
+ return emitOpError() << "coroutine function must have exactly one "
+ "cir.coro.ret_point op";
}
llvm::SmallSet<llvm::StringRef, 16> labels;
@@ -3559,7 +3559,7 @@ LogicalResult cir::AwaitOp::verify() {
if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
return emitOpError("ready region must end with cir.condition");
if (!isa<CoroSuspendPoint>(this->getSuspend().back().getTerminator()))
- return emitOpError("ready region must end with cir.coro.suspend.point");
+ return emitOpError("suspend region must end with cir.coro.suspend_point");
return success();
}
@@ -3594,6 +3594,72 @@ void cir::CoroBodyOp::build(OpBuilder &builder, OperationState &result,
bodyBuilder(builder, result.location);
}
+//===----------------------------------------------------------------------===//
+// CoroRetPoint
+//===----------------------------------------------------------------------===//
+
+void cir::CoroRetPointOp::build(OpBuilder &builder, OperationState &result,
+ BuilderCallbackRef bodyBuilder,
+ BuilderCallbackRef retBuilder) {
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *bodyRegion = result.addRegion();
+ builder.createBlock(bodyRegion);
+ bodyBuilder(builder, result.location);
+ }
+
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *retRegion = result.addRegion();
+ builder.createBlock(retRegion);
+ retBuilder(builder, result.location);
+ }
+}
+
+void cir::CoroRetPointOp::getSuccessorRegions(
+ mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
+ if (!point.isParent()) {
+ regions.emplace_back(getOperation());
+ return;
+ }
+
+ regions.push_back(RegionSuccessor(&getBodyRegion()));
+ regions.push_back(RegionSuccessor(&getRetRegion()));
+}
+
+mlir::ValueRange
+cir::CoroRetPointOp::getSuccessorInputs(RegionSuccessor successor) {
+ return ValueRange();
+}
+
+LogicalResult cir::CoroRetPointOp::verify() {
+
+ // TODO: Should we verify that the return region contains a
+ // `cir.coro.intrinsic.end`? Coroutine semantics require `coro.end` to mark
+ // the end of access to the coroutine frame before the coroutine completes.
+ bool hasReturn = false;
+ this->getBodyRegion().walk<mlir::WalkOrder::PreOrder>(
+ [&](mlir::Operation *op) {
+ if (mlir::isa<cir::ReturnOp>(op)) {
+ hasReturn = true;
+ return WalkResult::interrupt();
+ }
+ return WalkResult::advance();
+ });
+
+ if (hasReturn)
+ return emitError()
+ << "body region must not contain 'cir.return' operations";
+
+ if (!mlir::isa<cir::YieldOp>(this->getBodyRegion().back().back()))
+ return emitError() << "body region must terminate with 'cir.yield'";
+
+ if (!mlir::isa<cir::ReturnOp>(this->getRetRegion().back().back()))
+ return emitError() << "return region must terminate with 'cir.return'";
+
+ return mlir::success();
+}
+
//===----------------------------------------------------------------------===//
// CopyOp Definitions
//===----------------------------------------------------------------------===//
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index 3ea3b903d153a7..9853eb5c9fd4be 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -74,15 +74,11 @@ VoidTask silly_task() {
// Call promise.get_return_object() to retrieve the task object.
+// CIR: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
-// CIR: cir.cleanup.scope {
-
-// Empty records are ignored, so calls returning one return nothing and the
-// slot takes a poison placeholder.
-
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ()
-// CIR: %[[RetObj:.*]] = cir.const #cir.poison : ![[VoidTask]]
-// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
+// 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]])
@@ -94,9 +90,8 @@ VoidTask silly_task() {
// Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting
// the suspend_always struct to use for cir.await.
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])
-// CIR: %[[Tmp0:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
-// 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]])
@@ -160,22 +155,22 @@ VoidTask silly_task() {
// - The final suspend co_await
// - Return
-// CIR: cir.coro.body {
+// 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
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]])
-// CIR: cir.co_return
-// CIR: }
+// 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]])
@@ -184,7 +179,7 @@ VoidTask silly_task() {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: },)
-// CIR-NEXT: cir.yield
+// CIR-NEXT: cir.yield
// OGCG: coro.final:
// OGCG: final.suspend:
@@ -196,16 +191,17 @@ VoidTask silly_task() {
// If null, no dynamic allocation happened, so nothing to free.
// The `if` ensures we only call delete on non-null.
-// CIR-NEXT: } cleanup normal {
-// CIR-NEXT: %[[FreeMem:.*]] = cir.coro.intrinsic.free(%[[CoroId]], %[[CoroFrameAddr]]) : (token, !cir.ptr<!void>) -> !cir.ptr<!void>
-// CIR-NEXT: %[[NullPtr2:.*]] = cir.const #cir.ptr<null>
-// CIR-NEXT: %[[Cond:.*]] = cir.cmp ne %[[FreeMem]], %[[NullPtr2]]
-// CIR-NEXT: cir.if %[[Cond]] {
-// CIR-NEXT: %[[Size:.*]] = cir.coro.intrinsic.size()
-// CIR-NEXT: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]]){{.*}}
-// CIR-NEXT: }
-// CIR-NEXT: cir.yield
-// CIR-NEXT: }
+// CIR: } cleanup normal {
+// CIR: %[[FreeMem:.*]] = cir.coro.intrinsic.free(%[[CoroId]], %[[CoroFrameAddr]]) : (token, !cir.ptr<!void>) -> !cir.ptr<!void>
+// 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: }
+// CIR: cir.yield
// OGCG: %[[FreeMem:.*]] = call ptr @llvm.coro.free(token %[[CoroId]], ptr %[[CoroFrameAddr]])
// OGCG: %[[Cond:.*]] = icmp ne ptr %[[FreeMem]], null
@@ -219,14 +215,17 @@ VoidTask silly_task() {
// Call builtin coro end and return
+// CIR: }, ret : {
// CIR-NEXT: %[[CoroEndArg0:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR-NEXT: %[[CoroEndArg1:.*]] = cir.const #false
// CIR-NEXT: %[[TK_NONE:.*]] = cir.token.none
// CIR-NEXT: cir.coro.intrinsic.end(%[[CoroEndArg0]], %[[CoroEndArg1]], %[[TK_NONE]]) : (!cir.ptr<!void>, !cir.bool, token){{.*}}
-
-// CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]]
-// CIR: cir.return
-// CIR: }
+// CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]]
+// CIR: cir.return %[[Tmp1]]
+// CIR: }
+// CIRL }
+// CIR: cir.trap
+// CIR:}
// OGCG: coro.ret:
// OGCG: call void @llvm.coro.end(ptr null, i1 false, token none)
@@ -247,49 +246,49 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca "agg.tmp1" {{.*}} : !cir.ptr<![[CoroHandlePromiseInt]]>
// CIR: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr<![[StdString]]>, {{.*}}
-// CIR: cir.cleanup.scope {
+// CIR: cir.coro.ret_point {
+// 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: cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ()
-// CIR: %[[RetObj:.*]] = cir.const #cir.poison : ![[IntTask]]
-// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
-// CIR: %[[Tmp0:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
-// 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: cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIiE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr:.*]])
-// CIR: %[[FromAddrRes:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseInt]]
-// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseInt]]
-// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
-// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
-// CIR: cir.coro.suspend.point
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
-// CIR: cir.yield
-// CIR: },)
-// CIR: cir.coro.body {
+// 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.coro.suspend_point
+// 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: }
+// 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: }
// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
+// CIR: }, ret : {
folly::coro::Task<void> silly_coro() {
std::optional<folly::coro::Task<int>> task;
@@ -304,23 +303,25 @@ folly::coro::Task<void> silly_coro() {
// Make sure we properly handle OnFallthrough coro body sub stmt and
// check there are not multiple co_returns emitted.
-// CIR: cir.func coroutine {{.*}} @_Z10silly_corov() attributes
-// 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.func coroutine {{.*}} @_Z10silly_corov() {{.*}} ![[VoidTask]]
+// CIR: cir.coro.ret_point {
+// 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: }
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
+// CIR: }, ret : {
folly::coro::Task<void> yield();
folly::coro::Task<void> yield1() {
@@ -346,85 +347,81 @@ folly::coro::Task<void> yield1() {
// CIR-DAG: %[[CH_VOID2:.*]] = cir.alloca "agg.tmp5" {{.*}} : !cir.ptr<![[CoroHandleVoid]]>
// CIR-DAG: %[[CH_PROM2:.*]] = cir.alloca "agg.tmp6" {{.*}} : !cir.ptr<![[CoroHandlePromiseVoid]]>
-// CIR: cir.cleanup.scope {
+// CIR: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
// initial_suspend + await(init)
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}}
-// CIR: %[[INIT_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
-// 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: cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: %[[FROMADDR0:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
-// CIR: cir.store{{.*}} %[[FROMADDR0]], %[[CH_PROM0]]
-// CIR: %[[PROM_RELOAD0:.*]] = cir.load{{.*}} %[[CH_PROM0]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]]){{.*}}
-// CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}}
-// CIR: cir.coro.suspend.point
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
-
-// yield_value + await(yield)
-// CIR: cir.coro.body {
-// CIR: cir.call @_Z5yieldv(){{.*}}
-// CIR: %[[YIELD_TASK:.*]] = cir.const #cir.poison : ![[VoidTask]]
-// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]]
-// CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]]
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]]){{.*}}
-// CIR: %[[YIELD_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
-// CIR: cir.store{{.*}} %[[YIELD_SUSP]], %[[SUSP1]]
-// CIR: cir.await(yield, ready : {
-// CIR: %[[READY1:.*]] = cir.call @_ZNSt14suspend_always11await_readyEv(%[[SUSP1]]){{.*}}
-// CIR: cir.condition(%[[READY1]])
+// 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: cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: %[[FROMADDR1:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
-// CIR: cir.store{{.*}} %[[FROMADDR1]], %[[CH_PROM1]]
-// CIR: %[[PROM_RELOAD1:.*]] = cir.load{{.*}} %[[CH_PROM1]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]]){{.*}}
-// CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}}
-// CIR: cir.coro.suspend.point
+// 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.coro.suspend_point
// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}}
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
// CIR: cir.yield
// CIR: },)
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[PROMISE]])
-// CIR: cir.co_return
-// CIR: }
+
+// yield_value + await(yield)
+// CIR: cir.coro.body {
+// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
+// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_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.coro.suspend_point
+// 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_type13final_suspendEv(%[[PROMISE]]){{.*}}
-// CIR: %[[FINAL_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
-// 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: cir.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
-// CIR: %[[FROMADDR2:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
-// CIR: cir.store{{.*}} %[[FROMADDR2]], %[[CH_PROM2]]
-// CIR: %[[PROM_RELOAD2:.*]] = cir.load{{.*}} %[[CH_PROM2]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]]){{.*}}
-// CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}}
-// CIR: cir.coro.suspend.point
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
-// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
-// CIR: cir.coro.intrinsic.end(%{{.*}}, %{{.*}}, %{{.*}}){{.*}}
-// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
-// CIR: cir.return
-// CIR: }
+// 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.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
+// CIR: }
+// CIR: }, ret : {
+// CIR: = cir.coro.intrinsic.end(%{{.*}}, %{{.*}})
+// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
+// CIR: cir.return %[[RETLOAD]]
+// CIR: }
+// CIR: cir.trap
// CHECK: }
folly::coro::Task<int> go(int const& val);
@@ -437,37 +434,39 @@ folly::coro::Task<int> go1() {
// CIR: %[[IntTaskAddr:.*]] = cir.alloca "task" {{.*}} init : !cir.ptr<![[IntTask]]>
// CIR: %[[OneAddr:.*]] = cir.alloca "ref.tmp1" align(4) init : !cir.ptr<!s32i>
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coro.ret_point {
+// 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> : !s32i
-// CIR: cir.store{{.*}} %[[One]], %[[OneAddr]] : !s32i, !cir.ptr<!s32i>
-// CIR: cir.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>{{.*}}) -> ()
-// CIR: %[[IntTaskTmp:.*]] = cir.const #cir.poison : ![[IntTask]]
-// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
+// CIR: cir.coro.body {
+// 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.await(user, ready : {
+// 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: %[[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.yield
+// CIR: } cleanup normal {
// CIR: }
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
+// CIR: cir.yield
+// CIR: }, ret : {
folly::coro::Task<int> go1_lambda() {
auto task = []() -> folly::coro::Task<int> {
@@ -476,46 +475,74 @@ folly::coro::Task<int> go1_lambda() {
co_return co_await task;
}
-// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() attributes
-// 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: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]]
+// CIR: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, 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 {
+// 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: }, ret : {
-// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}}) attributes
-// 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.coro.ret_point {
+// 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 {
+// CIR: }, ret : {
+
+folly::coro::Task<int> go4() {
+ auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
+ auto task = fn(3);
+ co_return co_await std::move(task);
+}
+
+// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
+// CIR: cir.coro.ret_point {
+// 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: }, ret : {
folly::coro::Task<int> go4() {
auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
@@ -525,44 +552,42 @@ folly::coro::Task<int> go4() {
// CIR: cir.func coroutine {{.*}} @_Z3go4v() attributes
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coro.ret_point {
+// 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: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
-// 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. The callee is bitcast to
-// the lowered signature.
-
-// CIR: %[[FN_LOWERED:.*]] = cir.cast bitcast %[[FN]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> -> !cir.ptr<!cir.func<(!cir.ptr<!s32i>)>>
-// CIR: cir.call %[[FN_LOWERED]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>)>>, !cir.ptr<!s32i> {{.*}}) -> ()
-// CIR: %[[CALLRES:.*]] = cir.const #cir.poison : ![[IntTask]]
-// CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
-
-// CIR: cir.await(user, ready : {
-// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
-// CIR: cir.condition(
-// CIR: }, suspend : {
-// CIR: cir.coro.suspend.point
-// CIR: }, resume : {
-// CIR: cir.yield
-// CIR: },)
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi
-// CIR: cir.co_return
-// CIR: }
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: } cleanup normal {
+// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
+// CIR: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
+// 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.await(user, ready : {
+// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
+// CIR: cir.condition(
+// CIR: }, suspend : {
+// CIR: cir.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi
+// CIR: cir.co_return
+// CIR: }
+
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: } cleanup normal {
+// CIR: }, ret : {
// OGCG: define {{.*}}__await_suspend_wrapper__init(ptr noundef nonnull %[[Awaiter:.*]], ptr noundef %[[Handle:.*]])
// OGCG: entry:
@@ -591,41 +616,43 @@ folly::coro::Task<int> co_returns(int x) {
}
// 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: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: },)
-// CIR: %[[X_LOAD:.*]] = cir.load {{.*}} %[[X:.*]]
-// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
-// CIR: %[[RES:.*]] = cir.mul nsw %[[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 {
+// 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.mul nsw %[[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 {
+// CIR: }, ret : {
// OGCG: define {{.*}} @_Z10co_returnsi
@@ -675,40 +702,42 @@ folly::coro::Task<int> co_return_with_dtor(int flag) {
// CIR: cir.func coroutine {{.*}} @_Z19co_return_with_dtori
// CIR: %[[LOCAL:.*]] = cir.alloca "local" {{.*}} : !cir.ptr<!rec_HasDtor>
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
-// CIR: cir.coro.body {
-// CIR: cir.cleanup.scope {
-// CIR: cir.scope {
-// CIR: %[[CAST_FLAG:.*]] = cir.cast int_to_bool %[[FLAG:.*]]
-// CIR: cir.if %[[CAST_FLAG]] {
-// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise:.*]], %[[ONE]])
-// CIR: cir.co_return
+// CIR: cir.coro.body {
+// CIR: cir.cleanup.scope {
+// CIR: cir.scope {
+// CIR: %[[CAST_FLAG:.*]] = cir.cast int_to_bool %[[FLAG:.*]]
+// CIR: cir.if %[[CAST_FLAG]] {
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise:.*]], %[[ONE]])
+// CIR: cir.co_return
+// CIR: }
// CIR: }
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise]], %[[TWO]])
+// CIR: cir.co_return
+// CIR: } cleanup normal {
+// CIR: cir.call @_ZN7HasDtorD1Ev(%[[LOCAL]])
+// CIR: cir.yield
// CIR: }
-// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise]], %[[TWO]])
-// CIR: cir.co_return
-// CIR: } cleanup normal {
-// CIR: cir.call @_ZN7HasDtorD1Ev(%[[LOCAL]])
// CIR: cir.yield
// CIR: }
+
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: } cleanup normal {
// CIR: cir.yield
// CIR: }
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: cir.yield
-// CIR: }
+// CIR: }, ret : {
// OGCG: define {{.*}} void @_Z19co_return_with_dtori
// OGCG: %[[LOCAL:.*]] = alloca %struct.HasDtor
@@ -740,35 +769,30 @@ folly::coro::Task<int __complex__> complex_co_await() noexcept {
// CIR: %[[COMPLEX_ADDR:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
// CIR: %[[RESUME_VAL_ADDR:.*]] = cir.alloca "__coawait_resume_rval" {{.*}} : !cir.ptr<!cir.complex<!s32i>>
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coro.ret_point {
+// CIR: cir.cleanup.scope {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
-// CIR: cir.coro.body {
-// CIR: cir.call @_Z9fetchDatav() nothrow : () -> ()
-// CIR: %[[CALL:.*]] = cir.const #cir.poison : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
-// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
+// CIR: cir.coro.body {
+// CIR: %[[CALL:.*]] = cir.call @_Z9fetchDatav() nothrow : () -> !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
+// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
+
+// CIR: cir.await(user, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!cir.complex<!s32i> {llvm.noundef})
+// CIR: cir.store %[[RESUME_VAL]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
+// CIR: },)
+// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: cir.yield
+// CIR: }
-// CIR: cir.await(user, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// _Complex int is one eightbyte in class INTEGER, so it returns as !u64i and
-// is reloaded through the coerce slot.
-
-// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!u64i {llvm.noundef})
-// CIR: cir.store %[[RESUME_VAL]], %[[COERCE_ADDR]] : !u64i, !cir.ptr<!u64i>
-// CIR: %[[COERCE_CAST:.*]] = cir.cast bitcast %[[COERCE_ADDR]] : !cir.ptr<!u64i> -> !cir.ptr<!cir.complex<!s32i>>
-// CIR: %[[RESUME_COMPLEX:.*]] = cir.load %[[COERCE_CAST]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
-// CIR: cir.store %[[RESUME_COMPLEX]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
-// CIR: },)
-// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
-// CIR: cir.yield
+// CIR: } cleanup normal {
// CIR: }
-
-// CIR: } cleanup normal {
-// CIR: }
+// CIR: }, ret : {
// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
// CIR: cir.cleanup.scope {
diff --git a/clang/test/CIR/IR/await.cir b/clang/test/CIR/IR/await.cir
index 1305c4bb31e0f9..9e6a71351c42e8 100644
--- a/clang/test/CIR/IR/await.cir
+++ b/clang/test/CIR/IR/await.cir
@@ -1,26 +1,29 @@
// 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.coro.suspend.point
- }, resume : {
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
cir.yield
- },)
+ }
cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
// CHECK: cir.func coroutine @checkPrintParse
// CHECK: cir.await(user, ready : {
// CHECK: cir.condition(%arg0)
// CHECK: }, suspend : {
-// CHECK: cir.coro.suspend.point
+// CHECK: cir.coro.suspend_point
// CHECK: }, resume : {
// CHECK: cir.yield
// CHECK: },)
-// CHECK: cir.coro.suspend.point.dest
diff --git a/clang/test/CIR/IR/co-return.cir b/clang/test/CIR/IR/co-return.cir
index d6c3be2da80ada..833b6a27d80162 100644
--- a/clang/test/CIR/IR/co-return.cir
+++ b/clang/test/CIR/IR/co-return.cir
@@ -1,17 +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.coro.suspend.point
- }, resume : {
- cir.yield
- },)
- cir.co_return
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.co_return
+ }
+ cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
// CHECK: cir.func coroutine @coro_co_return
diff --git a/clang/test/CIR/IR/coro-body.cir b/clang/test/CIR/IR/coro-body.cir
index b1560485303b1c..30af9fc0e5d3cb 100644
--- a/clang/test/CIR/IR/coro-body.cir
+++ b/clang/test/CIR/IR/coro-body.cir
@@ -1,18 +1,22 @@
// 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.coro.suspend.point
- }, resume : {
- cir.yield
- },)
- cir.co_return
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.co_return
+ }
+ cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
// CHECK: cir.func coroutine @coro_body
diff --git a/clang/test/CIR/IR/coro-ret-point.cir b/clang/test/CIR/IR/coro-ret-point.cir
new file mode 100644
index 00000000000000..44afdc6407931b
--- /dev/null
+++ b/clang/test/CIR/IR/coro-ret-point.cir
@@ -0,0 +1,23 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+cir.func coroutine @coro_ret_point(%arg0 : !cir.bool) {
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }
+ cir.yield
+ }, ret : {
+ cir.return
+ }
+ cir.trap
+}
+
+// CHECK: cir.coro.ret_point {
+// CHECK: }, ret : {
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index 8b9cc2c8947865..e7420d56022dbe 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -109,20 +109,24 @@ cir.func @ullfunc() -> !u64i {
// CHECK: }
cir.func coroutine @coro() {
- cir.coro.body {
- cir.await(init, ready : {
- %0 = cir.alloca "" align(1) : !cir.ptr<!cir.bool>
- %1 = cir.load align(1) %0 : !cir.ptr<!cir.bool>, !cir.bool
- cir.condition(%1)
- }, suspend : {
- cir.coro.suspend.point
- }, resume : {
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(init, ready : {
+ %0 = cir.alloca "" align(1) : !cir.ptr<!cir.bool>
+ %1 = cir.load align(1) %0 : !cir.ptr<!cir.bool>, !cir.bool
+ cir.condition(%1)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
cir.yield
- },)
+ }
cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
// CHECK: cir.func{{.*}} coroutine @coro()
diff --git a/clang/test/CIR/IR/invalid-await.cir b/clang/test/CIR/IR/invalid-await.cir
index cc6d011718ad00..8e40e0886428fa 100644
--- a/clang/test/CIR/IR/invalid-await.cir
+++ b/clang/test/CIR/IR/invalid-await.cir
@@ -6,32 +6,38 @@ cir.func coroutine @bad_task() { // expected-error {{coroutine body must use at
// -----
cir.func coroutine @missing_condition() {
- cir.scope {
+ cir.coro.ret_point {
cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
cir.yield
}, suspend : {
- cir.coro.suspend.point
+ cir.coro.suspend_point
}, resume : {
cir.yield
},)
+ cir.coro.body {
+ }
+ cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.body {
- }
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
cir.func coroutine @missing_suspend(%arg0 : !cir.bool) {
- cir.coro.body {
- cir.await(user, ready : { // expected-error {{ready region must end with cir.coro.suspend.point}}
- cir.condition(%arg0)
- }, suspend : {
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : { // expected-error {{suspend region must end with cir.coro.suspend_point}}
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.yield
+ }, resume : {
+ cir.yield
+ },)
cir.yield
- }, resume : {
- cir.yield
- },)
+ }
cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.return
+ cir.trap
}
diff --git a/clang/test/CIR/IR/invalid-coro-body.cir b/clang/test/CIR/IR/invalid-coro-body.cir
index f247220c7d73d2..c72ec6e4450fbb 100644
--- a/clang/test/CIR/IR/invalid-coro-body.cir
+++ b/clang/test/CIR/IR/invalid-coro-body.cir
@@ -11,12 +11,11 @@ cir.func coroutine @must_have_one_coro_body(%arg0 : !cir.bool) { // expected-er
cir.await(user, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.coro.suspend.point
+ cir.coro.suspend_point
}, resume : {
cir.yield
},)
}
cir.coro.body {
}
- cir.coro.suspend.point.dest
}
diff --git a/clang/test/CIR/IR/invalid-coro-ret-point.cir b/clang/test/CIR/IR/invalid-coro-ret-point.cir
new file mode 100644
index 00000000000000..3e65f094ccb63b
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-coro-ret-point.cir
@@ -0,0 +1,55 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+cir.func coroutine @coro_ret_point_body_must_end_in_yield(%arg0 : !cir.bool) {
+ cir.coro.ret_point { // expected-error {{body region must terminate with 'cir.yield'}}
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.trap
+ }, ret : {
+ cir.return
+ }
+ cir.trap
+}
+
+cir.func coroutine @coro_ret_point_body_must_not_contain_return(%arg0 : !cir.bool) {
+ cir.coro.ret_point { // expected-error {{body region must not contain 'cir.return' operations}}
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.return
+ }, ret : {
+ cir.return
+ }
+ cir.trap
+}
+
+cir.func coroutine @coro_ret_point_return_region_must_end_in_return(%arg0 : !cir.bool) {
+ cir.coro.ret_point { // expected-error {{return region must terminate with 'cir.return'}}
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.yield
+ }, ret : {
+ cir.trap
+ }
+ cir.trap
+}
diff --git a/clang/test/CIR/IR/invalid-coro-suspend.cir b/clang/test/CIR/IR/invalid-coro-suspend.cir
index 3a45e22433a589..d03769ed663060 100644
--- a/clang/test/CIR/IR/invalid-coro-suspend.cir
+++ b/clang/test/CIR/IR/invalid-coro-suspend.cir
@@ -1,17 +1,23 @@
// RUN: cir-opt %s -verify-diagnostics -split-input-file
-cir.func coroutine @must_have_one_coro_suspend_dest(%arg0 : !cir.bool) { // expected-error {{coroutine function must have exactly one cir.coro.suspend.point.dest}}
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend.point
- }, resume : {
- cir.yield
- },)
+cir.func coroutine @must_have_one_coro_ret_point(%arg0 : !cir.bool) { // expected-error {{coroutine function must have exactly one cir.coro.ret_point op}}
+ cir.coro.ret_point {
+ cir.coro.body {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ }
+ cir.yield
+ }, ret : {
+ cir.return
}
- cir.coro.suspend.point.dest
- cir.coro.suspend.point.dest
+ cir.coro.ret_point {
+ }, ret : {
+ }
+ cir.trap
}
-
>From 0ebe3080dfce680c7ac69ca2a79cf70ffa1fe3e4 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 30 Jul 2026 21:35:53 -0500
Subject: [PATCH 03/12] Fix formatting
---
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index f9ee288e1f29ef..11890c77d549e5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -667,7 +667,7 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
}
// Signals the parent that execution flows to next region.
- cir::CoroSuspendPoint::create(builder,loc);
+ cir::CoroSuspendPoint::create(builder, loc);
},
/*resumeBuilder=*/
[&](mlir::OpBuilder &b, mlir::Location loc) {
>From 417c67f142c27d37e0f9808a1a6cba28365e65f8 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Tue, 4 Aug 2026 20:52:07 -0500
Subject: [PATCH 04/12] Fix after rebase.
---
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 5 +++++
clang/test/CIR/CodeGenCoroutines/coro-task.cpp | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 11890c77d549e5..7ea854b2c1afea 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -582,6 +582,11 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
mlir::OpBuilder::InsertionGuard guard(builder);
builder.restoreInsertionPoint(coroRetRegion);
+ cir::CoroEndOp::create(
+ cgm.getBuilder(), openCurlyLoc,
+ mlir::ValueRange{builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc),
+ builder.getBool(false, 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.
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index 9853eb5c9fd4be..7879ea6655e633 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -196,7 +196,7 @@ VoidTask silly_task() {
// 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: %[[Size:.*]] = cir.coro.intrinsic.size()
// CIR: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]])
// CIR: }
// CIR: cir.yield
>From a4a17e831947ffb9757c6c3846f12a300991f2e0 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Tue, 11 Aug 2026 20:07:37 -0500
Subject: [PATCH 05/12] delete ret point
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 58 +++++++-------
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 34 --------
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 82 +++++++-------------
3 files changed, 53 insertions(+), 121 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 9916eb373afe74..eb91f539f772bb 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1014,7 +1014,7 @@ def CIR_StoreOp : CIR_Op<"store", [
defvar CIR_ReturnableScopes = [
"FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp", "CleanupScopeOp",
- "DoWhileOp", "WhileOp", "ForOp", "TryOp", "CoroRetPointOp"
+ "DoWhileOp", "WhileOp", "ForOp", "TryOp"
];
def CIR_ReturnOp : CIR_Op<"return", [
@@ -1164,7 +1164,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
defvar CIR_YieldableScopes = [
"ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "CoroBodyOp",
"DoWhileOp", "ForOp", "GlobalOp", "IfOp", "LocalInitOp", "ScopeOp", "SwitchOp",
- "TernaryOp", "TryOp", "WhileOp", "CoroRetPointOp"
+ "TernaryOp", "TryOp", "WhileOp"
];
def CIR_YieldOp : CIR_Op<"yield", [
@@ -5127,7 +5127,7 @@ def CIR_CoroDoneOp : CIR_CoroIntrinsicOp<"done", (ins CIR_VoidPtrType:$handle),
//===----------------------------------------------------------------------===//
def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend_point", [
- Pure, Terminator, HasParent<"AwaitOp">, HasAncestor<"CoroRetPointOp">
+ Pure, Terminator, HasParent<"AwaitOp">
]> {
let summary = "Marks the point where a coroutine actually suspends";
let description = [{
@@ -5166,56 +5166,50 @@ def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend_point", [
}
//===----------------------------------------------------------------------===//
-// CoroRetPoint
+// CoroutineOp
//===----------------------------------------------------------------------===//
-def CIR_CoroRetPointOp : CIR_Op<"coro.ret_point", [
+def CIR_CoroutineOp : CIR_Op<"coroutine", [
DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
NoRegionArguments
]> {
- let summary = "Marks the coroutine's structured suspend/exit region";
+ let summary = "";
let description = [{
- Pairs a coroutine's body its normal execution, including any
- `cir.await`s and cleanups with a single, shared exit destination
- (`retRegion`) that every suspend point in the body jumps to.
- Every `cir.coro.suspend_point` inside `bodyRegion` is guaranteed to have
- `cir.coro.ret_point` as an ancestor, so all suspend paths converge on
- the same `retRegion` regardless of where in the body they occur. This
- gives the coroutine a single, well-defined place to run final teardown
- (e.g. `__builtin_coro_end`) and return control to the caller.
-
- Example:
- ```mlir
- cir.coro.ret_point {
- // body: awaits, cleanups, etc.
- }, ret : {
- // shared jump target for any suspend point in the body
- cir.coro.intrinsic.end(...)
- cir.return
- }
- ```
}];
- let regions = (region AnyRegion:$bodyRegion,
- AnyRegion:$retRegion);
- let skipDefaultBuilders = 1;
+// TODO: ver como funciona con cleanup
+ let regions = (region AnyRegion:$initialSuspend,
+ AnyRegion:$body,
+ AnyRegion:$finalSuspend,
+ AnyRegion:$destroy,
+ AnyRegion:$exit);
+
+ let skipDefaultBuilders = 1;
let builders = [
- OpBuilder<(ins "BuilderCallbackRef":$bodyBuilder,
- "BuilderCallbackRef":$retBuilder)>
+ OpBuilder<(ins "BuilderCallbackRef":$initialSuspendBuilder,
+ "BuilderCallbackRef":$bodyBuilder,
+ "BuilderCallbackRef":$finalSuspendBuilder,
+ "BuilderCallbackRef":$destroyBuilder,
+ "BuilderCallbackRef":$exitBuilder)>
];
let assemblyFormat = [{
- $bodyRegion `,`
- `ret` `:` $retRegion
+ `initialSuspend` `:` $initialSuspend `,`
+ `body` `:` $body `,`
+ `finalSuspend` `:` $finalSuspend `,`
+ `destroy` `:` $destroy `,`
+ `exit` `:` $exit
attr-dict
}];
+
let hasLLVMLowering = false;
let hasVerifier = 1;
}
+
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 7ea854b2c1afea..976e3712ace6f8 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -422,25 +422,10 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
curCoro.data->coroId.getResult(),
cir::LoadOp::create(builder, openCurlyLoc, allocaTy, storeAddr)});
- cir::CoroRetPointOp coroRet = nullptr;
- mlir::OpBuilder::InsertPoint coroRetRegion;
{
assert(!cir::MissingFeatures::generateDebugInfo());
ParamReferenceReplacerRAII paramReplacer(localDeclMap);
RunCleanupsScope resumeScope(*this);
- mlir::OpBuilder::InsertPoint coroRetBody;
- coroRet = cir::CoroRetPointOp::create(
- builder, openCurlyLoc,
- /*bodyBuilder=*/
- [&](mlir::OpBuilder &b, mlir::Location) {
- coroRetBody = b.saveInsertionPoint();
- },
- /*retBuilder=*/
- [&](mlir::OpBuilder &b, mlir::Location) {
- coroRetRegion = b.saveInsertionPoint();
- });
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.restoreInsertionPoint(coroRetBody);
ehStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, s.getDeallocate());
// Create mapping between parameters and copy-params for coroutine
// function.
@@ -572,21 +557,6 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind, tkNone);
- mlir::Block &coroRetBodyBlock = coroRet.getBodyRegion().back();
- {
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.setInsertionPointToEnd(&coroRetBodyBlock);
- cir::YieldOp::create(builder, openCurlyLoc);
- }
-
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.restoreInsertionPoint(coroRetRegion);
-
- cir::CoroEndOp::create(
- cgm.getBuilder(), openCurlyLoc,
- mlir::ValueRange{builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc),
- builder.getBool(false, 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.
@@ -594,10 +564,6 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
ret->setRetValue(nullptr);
if (emitStmt(ret, /*useCurrentScope=*/true).failed())
return mlir::failure();
- mlir::Block *block = builder.getInsertionBlock();
- // emitReturnStmt() always creates a new insertion block after emitting the
- // return. That block is unreachable in this case, so erase it.
- block->erase();
// Set the return value back. The code generator, as the AST **Consumer**,
// shouldn't change the AST.
ret->setRetValue(previousRetValue);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 7ddc9636eab1bd..33c32a626e47c8 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3135,7 +3135,6 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (!isDeclaration() && getCoroutine()) {
bool foundAwait = false;
int coroBodyCount = 0;
- int coroRetPointCount = 0;
this->walk([&](Operation *op) {
if (auto await = dyn_cast<AwaitOp>(op)) {
foundAwait = true;
@@ -3144,11 +3143,6 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount > 1) {
return mlir::WalkResult::interrupt();
}
- } else if (isa<CoroRetPointOp>(op)) {
- coroRetPointCount++;
- if (coroRetPointCount > 1) {
- return mlir::WalkResult::interrupt();
- }
}
return mlir::WalkResult::advance();
});
@@ -3158,9 +3152,6 @@ mlir::LogicalResult cir::FuncOp::verify() {
if (coroBodyCount != 1)
return emitOpError()
<< "coroutine function must have exactly one cir.body op";
- if (coroRetPointCount != 1)
- return emitOpError() << "coroutine function must have exactly one "
- "cir.coro.ret_point op";
}
llvm::SmallSet<llvm::StringRef, 16> labels;
@@ -3594,68 +3585,49 @@ void cir::CoroBodyOp::build(OpBuilder &builder, OperationState &result,
bodyBuilder(builder, result.location);
}
+
//===----------------------------------------------------------------------===//
-// CoroRetPoint
+// CoroutineOp
//===----------------------------------------------------------------------===//
-void cir::CoroRetPointOp::build(OpBuilder &builder, OperationState &result,
+void cir::CoroutineOp::build(OpBuilder &builder, OperationState &result,
+ BuilderCallbackRef initialSuspendBuilder,
BuilderCallbackRef bodyBuilder,
- BuilderCallbackRef retBuilder) {
- {
- OpBuilder::InsertionGuard guard(builder);
- Region *bodyRegion = result.addRegion();
- builder.createBlock(bodyRegion);
- bodyBuilder(builder, result.location);
- }
-
- {
- OpBuilder::InsertionGuard guard(builder);
- Region *retRegion = result.addRegion();
- builder.createBlock(retRegion);
- retBuilder(builder, result.location);
- }
-}
-
-void cir::CoroRetPointOp::getSuccessorRegions(
+ BuilderCallbackRef finalSuspendBuilder,
+ BuilderCallbackRef destroyBuilder,
+ BuilderCallbackRef exitBuilder) {
+ //{
+ // OpBuilder::InsertionGuard guard(builder);
+ // Region *bodyRegion = result.addRegion();
+ // builder.createBlock(bodyRegion);
+ // bodyBuilder(builder, result.location);
+ //}
+
+ //{
+ // OpBuilder::InsertionGuard guard(builder);
+ // Region *retRegion = result.addRegion();
+ // builder.createBlock(retRegion);
+ // retBuilder(builder, result.location);
+ //}
+}
+
+void cir::CoroutineOp::getSuccessorRegions(
mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
if (!point.isParent()) {
regions.emplace_back(getOperation());
return;
}
- regions.push_back(RegionSuccessor(&getBodyRegion()));
- regions.push_back(RegionSuccessor(&getRetRegion()));
+ //regions.push_back(RegionSuccessor(&getBodyRegion()));
+ //regions.push_back(RegionSuccessor(&getRetRegion()));
}
mlir::ValueRange
-cir::CoroRetPointOp::getSuccessorInputs(RegionSuccessor successor) {
+cir::CoroutineOp::getSuccessorInputs(RegionSuccessor successor) {
return ValueRange();
}
-LogicalResult cir::CoroRetPointOp::verify() {
-
- // TODO: Should we verify that the return region contains a
- // `cir.coro.intrinsic.end`? Coroutine semantics require `coro.end` to mark
- // the end of access to the coroutine frame before the coroutine completes.
- bool hasReturn = false;
- this->getBodyRegion().walk<mlir::WalkOrder::PreOrder>(
- [&](mlir::Operation *op) {
- if (mlir::isa<cir::ReturnOp>(op)) {
- hasReturn = true;
- return WalkResult::interrupt();
- }
- return WalkResult::advance();
- });
-
- if (hasReturn)
- return emitError()
- << "body region must not contain 'cir.return' operations";
-
- if (!mlir::isa<cir::YieldOp>(this->getBodyRegion().back().back()))
- return emitError() << "body region must terminate with 'cir.yield'";
-
- if (!mlir::isa<cir::ReturnOp>(this->getRetRegion().back().back()))
- return emitError() << "return region must terminate with 'cir.return'";
+LogicalResult cir::CoroutineOp::verify() {
return mlir::success();
}
>From a9971e23e244b66f9f29f0fb4e4650071be6c5b1 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 20 Aug 2026 21:54:51 -0500
Subject: [PATCH 06/12] [CIR] Add cir.coroutine and remove cir.coroBody
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 169 +++--
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 303 ++++----
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 242 ++++--
.../CIR/CodeGenCoroutines/coro-exceptions.cpp | 10 +-
.../test/CIR/CodeGenCoroutines/coro-task.cpp | 711 +++++++++---------
clang/test/CIR/IR/await.cir | 49 +-
clang/test/CIR/IR/co-return.cir | 36 +-
clang/test/CIR/IR/coro-body.cir | 24 -
clang/test/CIR/IR/coro-ret-point.cir | 23 -
clang/test/CIR/IR/coroutine.cir | 36 +
clang/test/CIR/IR/func.cir | 31 +-
clang/test/CIR/IR/invalid-await.cir | 49 +-
clang/test/CIR/IR/invalid-co-return.cir | 28 +-
clang/test/CIR/IR/invalid-coro-body.cir | 21 -
clang/test/CIR/IR/invalid-coro-ret-point.cir | 55 --
clang/test/CIR/IR/invalid-coro-suspend.cir | 23 -
clang/test/CIR/IR/invalid-coroutine.cir | 289 +++++++
17 files changed, 1244 insertions(+), 855 deletions(-)
delete mode 100644 clang/test/CIR/IR/coro-body.cir
delete mode 100644 clang/test/CIR/IR/coro-ret-point.cir
create mode 100644 clang/test/CIR/IR/coroutine.cir
delete mode 100644 clang/test/CIR/IR/invalid-coro-body.cir
delete mode 100644 clang/test/CIR/IR/invalid-coro-ret-point.cir
delete mode 100644 clang/test/CIR/IR/invalid-coro-suspend.cir
create mode 100644 clang/test/CIR/IR/invalid-coroutine.cir
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index eb91f539f772bb..fccca08ee06ef0 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -1014,7 +1014,7 @@ def CIR_StoreOp : CIR_Op<"store", [
defvar CIR_ReturnableScopes = [
"FuncOp", "ScopeOp", "IfOp", "SwitchOp", "CaseOp", "CleanupScopeOp",
- "DoWhileOp", "WhileOp", "ForOp", "TryOp"
+ "CoroutineOp", "DoWhileOp", "WhileOp", "ForOp", "TryOp"
];
def CIR_ReturnOp : CIR_Op<"return", [
@@ -1162,7 +1162,7 @@ def CIR_ConditionOp : CIR_Op<"condition", [
//===----------------------------------------------------------------------===//
defvar CIR_YieldableScopes = [
- "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "CoroBodyOp",
+ "ArrayCtor", "ArrayDtor", "AwaitOp", "CaseOp", "CleanupScopeOp", "CoroutineOp",
"DoWhileOp", "ForOp", "GlobalOp", "IfOp", "LocalInitOp", "ScopeOp", "SwitchOp",
"TernaryOp", "TryOp", "WhileOp"
];
@@ -4779,6 +4779,85 @@ def CIR_TryCallOp : CIR_CallOpBase<"try_call",[
];
}
+//===----------------------------------------------------------------------===//
+// CoroutineOp
+//===----------------------------------------------------------------------===//
+
+def CIR_CoroutineOp : CIR_Op<"coroutine", [
+ DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
+ NoRegionArguments, RecursivelySpeculatable, AutomaticAllocationScope
+]> {
+
+ let summary = "Groups a coroutine's structured suspend/resume/destroy control flow";
+ let description = [{
+ `cir.coroutine` is an op consisting of seven regions: `initial_suspend`,
+ `body`, `final_suspend`, grp (TODO),`unwind` (TODO), `destroy`, and`exit`.
+
+ - `initial_suspend`: the coroutine ramp/setup region. Its `cir.await` marked
+ `init` is the ramp boundary: the coroutine frame and its associated state
+ are established before this point, and crossing the await transfers
+ execution to `body`.
+
+ - `body`: the user-written coroutine body, arbitrary CFG, zero or more
+ `cir.await`s at any nesting depth, terminated by `cir.co_return` or a
+ plain `cir.yield` if control simply falls off the end. This region is
+ a structural boundary separating the coroutine's setup and teardown
+ logic (initial suspend, final suspend, destroy) from the
+ user-provided statements of 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 `final_suspend`.
+ - `final_suspend`: exactly one `cir.await`, marked `final`. Nothing
+ else. Corresponds to `co_await promise.final_suspend()`. Resuming
+ past this point is undefined behavior in valid programs, so its
+ `resume` edge is expected to be dead in practice, but is kept for
+ structural symmetry with the other two suspend regions.
+ - `gro`: TODO.
+ - `unwind`: TODO.
+ - `destroy`: destroys the coroutine frame (`coro.free` + `delete`).
+ Reached by an explicit `destroy()` call on a suspended handle, by
+ normal completion through `final_suspend`, or by an exception
+ escaping the coroutine. Falls through to `exit` except when reached
+ due to an escaping exception, where it continues propagating that
+ exception instead.
+ - `exit`: the shared return-to-caller path. Reached directly from any
+ suspend point that isn't being destroyed (the coroutine is just
+ pausing), and from `destroy` when it was entered via ordinary
+ dispatch. Holds `coro.end` + the actual `cir.return`.
+
+ }];
+
+ // TODO(cir): Add GRO and EH
+ let regions = (region AnyRegion:$initialSuspend,
+ AnyRegion:$body,
+ AnyRegion:$finalSuspend,
+ AnyRegion:$destroy,
+ AnyRegion:$exit);
+
+ let skipDefaultBuilders = 1;
+
+ let builders = [
+ OpBuilder<(ins "BuilderCallbackRef":$initialSuspendBuilder,
+ "BuilderCallbackRef":$bodyBuilder,
+ "BuilderCallbackRef":$finalSuspendBuilder,
+ "BuilderCallbackRef":$destroyBuilder,
+ "BuilderCallbackRef":$exitBuilder)>
+ ];
+
+ let assemblyFormat = [{
+ `initialSuspend` `:` $initialSuspend `,`
+ `body` `:` $body `,`
+ `finalSuspend` `:` $finalSuspend `,`
+ `destroy` `:` $destroy `,`
+ `exit` `:` $exit
+ attr-dict
+ }];
+
+ let hasLLVMLowering = false;
+ let hasVerifier = 1;
+}
+
//===----------------------------------------------------------------------===//
// AwaitOp
//===----------------------------------------------------------------------===//
@@ -4793,7 +4872,7 @@ def CIR_AwaitKind : CIR_I32Enum<"AwaitKind", "await kind", [
def CIR_AwaitKindAttr : CIR_EnumAttr<CIR_AwaitKind, "await">;
def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [
- RecursivelySpeculatable, NoRegionArguments
+ RecursivelySpeculatable, NoRegionArguments, HasAncestor<"CoroutineOp">
]> {
let summary = "Wraps C++ co_await implicit logic";
let description = [{
@@ -4874,6 +4953,7 @@ def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [
let hasVerifier = 1;
let hasLLVMLowering = false;
}
+<<<<<<< HEAD
//===----------------------------------------------------------------------===//
// CoroBody
//===----------------------------------------------------------------------===//
@@ -4912,13 +4992,15 @@ def CIR_CoroBodyOp : CIR_RegionBranchOpBase<"coro.body", [
let hasLLVMLowering = false;
let hasVerifier = 1;
}
+=======
+>>>>>>> 56098cff2da3 ([CIR] Add cir.coroutine and remove cir.coroBody)
//===----------------------------------------------------------------------===//
// CoReturnOp
//===----------------------------------------------------------------------===//
def CIR_CoReturnOp : CIR_Op<"co_return", [
- ReturnLike, Pure, Terminator, HasAncestor<"CoroBodyOp">
+ ReturnLike, Pure, Terminator, HasAncestor<"CoroutineOp">
]> {
let summary = "Coroutine return operation";
let description = [{
@@ -4932,6 +5014,7 @@ def CIR_CoReturnOp : CIR_Op<"co_return", [
attr-dict
}];
+ let hasVerifier = 1;
let hasLLVMLowering = false;
}
@@ -5131,85 +5214,29 @@ def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend_point", [
]> {
let summary = "Marks the point where a coroutine actually suspends";
let description = [{
- Terminates the `suspend` region of a `cir.await` op, marking the exact
- point where control returns to the caller/resumer if the coroutine
- decides to suspend.
+ Terminates the `suspend` region of a `cir.await`. It has no explicit
+ successors; the destination is determined by `CoroutineOp`
+ according to how the suspended coroutine is subsequently handled:
- This op must appear inside the `suspend` region of a `cir.await`, and
- that `cir.await` must in turn be nested within a `cir.coro.ret_point`.
- During lowering to FlattenCFG, `cir.coro.suspend_point` becomes the
- branch target that routes control to one of three destinations
- depending on how the coroutine resumes: the resume block (normal
- continuation), the cleanup/destroy block, or the ret/exit block that
- hands control back to the caller.
+ - resume: continues execution through the `cir.await`'s `resume` region
+ when the coroutine handle is resumed.
- Example:
- ```mlir
- cir.await(user, ready : {
- ...
- cir.condition(%ready)
- }, suspend : {
- ...
- cir.coro.suspend_point
- }, resume : {
- ...
- cir.yield
- },)
- ```
- }];
+ - exit: transfers directly to the enclosing `cir.coroutine`'s `exit`
+ region when the coroutine remains suspended. The coroutine frame remains
+ alive and no cleanups are performed.
- let assemblyFormat = [{
- attr-dict
+ - destroy: transfers through the enclosing `cir.cleanup.scope`s to the
+ `cir.coroutine`'s `destroy` region when the coroutine handle is
+ destroyed.
}];
- let hasLLVMLowering = false;
-}
-
-//===----------------------------------------------------------------------===//
-// CoroutineOp
-//===----------------------------------------------------------------------===//
-
-def CIR_CoroutineOp : CIR_Op<"coroutine", [
- DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getSuccessorInputs"]>,
- NoRegionArguments
-]> {
-
- let summary = "";
- let description = [{
-
- }];
-
-// TODO: ver como funciona con cleanup
- let regions = (region AnyRegion:$initialSuspend,
- AnyRegion:$body,
- AnyRegion:$finalSuspend,
- AnyRegion:$destroy,
- AnyRegion:$exit);
-
- let skipDefaultBuilders = 1;
-
- let builders = [
- OpBuilder<(ins "BuilderCallbackRef":$initialSuspendBuilder,
- "BuilderCallbackRef":$bodyBuilder,
- "BuilderCallbackRef":$finalSuspendBuilder,
- "BuilderCallbackRef":$destroyBuilder,
- "BuilderCallbackRef":$exitBuilder)>
- ];
-
let assemblyFormat = [{
- `initialSuspend` `:` $initialSuspend `,`
- `body` `:` $body `,`
- `finalSuspend` `:` $finalSuspend `,`
- `destroy` `:` $destroy `,`
- `exit` `:` $exit
attr-dict
}];
let hasLLVMLowering = false;
- let hasVerifier = 1;
}
-
//===----------------------------------------------------------------------===//
// CopyOp
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 976e3712ace6f8..af6ee325706249 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/StmtCXX.h"
#include "clang/AST/StmtVisitor.h"
#include "clang/Basic/TargetInfo.h"
+#include "clang/CIR/Dialect/IR/CIRDialect.h"
#include "clang/CIR/MissingFeatures.h"
using namespace clang;
@@ -119,63 +120,6 @@ 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();
- cir::CoroFreeOp coroFree = cgf.curCoro.data->lastCoroFree;
-
- if (!coroFree) {
- cgf.cgm.error(deallocate->getBeginLoc(),
- "Deallocation expression does not refer to coro.free");
- return;
- }
-
- builder.setInsertionPointAfter(coroFree);
- mlir::Value isPtrNotNull = builder.createPtrIsNotNull(coroFree.getResult());
-
- llvm::SmallVector<mlir::Operation *> opsToMove;
- mlir::Block *block = builder.getInsertionBlock();
- mlir::Block::iterator it(isPtrNotNull.getDefiningOp());
-
- for (++it; it != block->end(); ++it)
- opsToMove.push_back(&*it);
-
- auto ifOp =
- cir::IfOp::create(builder, cgf.getLoc(deallocate->getSourceRange()),
- isPtrNotNull, /*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);
@@ -425,10 +369,9 @@ 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.
+
+ // Create mapping between parameters and copy-params for coroutine
+ // function.
llvm::ArrayRef<const Stmt *> paramMoves = s.getParamMoves();
assert((paramMoves.size() == 0 || (paramMoves.size() == fnArgs.size())) &&
"ParamMoves and FnArgs should be the same size for coroutine "
@@ -446,44 +389,42 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
paramReplacer.addCopy(cast<DeclStmt>(pm));
}
- if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
- return mlir::failure();
- // returnValue should be valid as long as the coroutine's return type
- // is not void. The assertion could help us to reduce the check later.
- assert(returnValue.isValid() == (bool)s.getReturnStmt());
- // Now we have the promise, initialize the GRO.
- // We need to emit `get_return_object` first. According to:
- // [dcl.fct.def.coroutine]p7
- // The call to get_return_Âobject is sequenced before the call to
- // initial_suspend and is invoked at most once.
- //
- // So we couldn't emit return value when we emit return statment,
- // otherwise the call to get_return_object wouldn't be in front
- // of initial_suspend.
- if (returnValue.isValid())
- emitAnyExprToMem(s.getReturnValue(), returnValue,
- s.getReturnValue()->getType().getQualifiers(),
- /*isInit*/ true);
-
- assert(!cir::MissingFeatures::ehCleanupScope());
-
- curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
- curCoro.data->exceptionHandler = s.getExceptionHandler();
-
- if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
- return mlir::failure();
-
- curCoro.data->currentAwaitKind = cir::AwaitKind::User;
-
- mlir::OpBuilder::InsertPoint userBody;
- auto coroBodyOp =
- cir::CoroBodyOp::create(builder, openCurlyLoc, /*scopeBuilder=*/
- [&](mlir::OpBuilder &b, mlir::Location loc) {
- userBody = b.saveInsertionPoint();
- });
- {
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.restoreInsertionPoint(userBody);
+ // Builds `initial_suspend`.
+ auto initialSuspendBuilder = [&]() -> mlir::LogicalResult {
+ if (emitStmt(s.getPromiseDeclStmt(), /*useCurrentScope=*/true).failed())
+ return mlir::failure();
+ // returnValue should be valid as long as the coroutine's return type
+ // is not void. The assertion could help us to reduce the check later.
+ assert(returnValue.isValid() == (bool)s.getReturnStmt());
+ // Now we have the promise, initialize the GRO.
+ // We need to emit `get_return_object` first. According to:
+ // [dcl.fct.def.coroutine]p7
+ // The call to get_return_Âobject is sequenced before the call to
+ // initial_suspend and is invoked at most once.
+ //
+ // So we couldn't emit return value when we emit return statment,
+ // otherwise the call to get_return_object wouldn't be in front
+ // of initial_suspend.
+ if (returnValue.isValid())
+ emitAnyExprToMem(s.getReturnValue(), returnValue,
+ s.getReturnValue()->getType().getQualifiers(),
+ /*isInit*/ true);
+
+ curCoro.data->currentAwaitKind = cir::AwaitKind::Init;
+ curCoro.data->exceptionHandler = s.getExceptionHandler();
+
+ if (emitStmt(s.getInitSuspendStmt(), /*useCurrentScope=*/true).failed())
+ return mlir::failure();
+
+ cir::YieldOp::create(builder, openCurlyLoc);
+ return mlir::success();
+ };
+
+ // Builds `body`: the user-written coroutine code, including its
+ // implicit `try { ... } catch (...) { unhandled_exception(); }`.
+ auto bodyBuilder = [&]() -> mlir::LogicalResult {
+ curCoro.data->currentAwaitKind = cir::AwaitKind::User;
+
if (curCoro.data->exceptionHandler) {
// This bit of code is supposed to do:
//
@@ -521,54 +462,142 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
.failed()) {
return mlir::failure();
}
- }
- mlir::Block &coroBodyBlock = coroBodyOp.getBody().back();
- if (!coroBodyBlock.mightHaveTerminator()) {
- mlir::OpBuilder::InsertionGuard guard(builder);
- builder.setInsertionPointToEnd(&coroBodyBlock);
+ return mlir::success();
+ };
+
+ // Builds `final_suspend`: only emitted at all if the body can actually
+ // reach it (an explicit co_return, or falling off the end).
+ auto finalSuspendBuilder = [&]() -> mlir::LogicalResult {
+ // Note that LLVM checks CanFallthrough by looking into the availability
+ // of the insert block which is kinda brittle and unintuitive, seems to be
+ // related with how landing pads are handled.
+ //
+ // CIRGen handles this by checking pre-existing co_returns in the current
+ // scope instead.
+ //
+ // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
+ const bool canFallthrough = curLexScope->hasCoreturn();
+ const bool hasCoreturns = curCoro.data->coreturnCount > 0;
+ if (canFallthrough || hasCoreturns) {
+ curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
+ if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
+ .failed())
+ return mlir::failure();
+ }
cir::YieldOp::create(builder, openCurlyLoc);
- }
+ return mlir::success();
+ };
+
+ // Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
+ auto destroyBuilder = [&]() -> mlir::LogicalResult {
+ Stmt *deallocate = s.getDeallocate();
+ if (emitStmt(deallocate, /*useCurrentScope=*/true).failed()) {
+ cgm.error(deallocate->getBeginLoc(),
+ "failed to emit coroutine deallocation expression");
+ return mlir::failure();
+ }
+
+ cir::CoroFreeOp coroFree = curCoro.data->lastCoroFree;
- // Note that LLVM checks CanFallthrough by looking into the availability
- // of the insert block which is kinda brittle and unintuitive, seems to be
- // related with how landing pads are handled.
- //
- // CIRGen handles this by checking pre-existing co_returns in the current
- // scope instead.
- //
- // From LLVM IR Gen: const bool CanFallthrough = Builder.GetInsertBlock();
- const bool canFallthrough = curLexScope->hasCoreturn();
- const bool hasCoreturns = curCoro.data->coreturnCount > 0;
- if (canFallthrough || hasCoreturns) {
- curCoro.data->currentAwaitKind = cir::AwaitKind::Final;
+ if (!coroFree) {
+ cgm.error(deallocate->getBeginLoc(),
+ "Deallocation expression does not refer to coro.free");
+ return mlir::failure();
+ }
{
mlir::OpBuilder::InsertionGuard guard(builder);
- if (emitStmt(s.getFinalSuspendStmt(), /*useCurrentScope=*/true)
- .failed())
+ builder.setInsertionPointAfter(coroFree);
+ mlir::Value isPtrNotNull =
+ builder.createPtrIsNotNull(coroFree.getResult());
+
+ llvm::SmallVector<mlir::Operation *> opsToMove;
+ mlir::Block *block = builder.getInsertionBlock();
+ mlir::Block::iterator it(isPtrNotNull.getDefiningOp());
+
+ for (++it; it != block->end(); ++it)
+ opsToMove.push_back(&*it);
+
+ auto ifOp = cir::IfOp::create(
+ builder, getLoc(deallocate->getSourceRange()), isPtrNotNull,
+ /*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);
+ }
+
+ cir::YieldOp::create(builder, openCurlyLoc);
+ return mlir::success();
+ };
+
+ // Builds `exit`: coro.end(/*unwind*/ false) followed by the actual return
+ // to the caller.
+ auto exitBuilder = [&]() {
+ cir::ConstantOp nullHandler =
+ builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc);
+ cir::ConstantOp noUnwind = builder.getBool(false, openCurlyLoc);
+ auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
+ cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind,
+ tkNone);
+
+ 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();
+ mlir::Block *block = builder.getInsertionBlock();
+ // emitReturnStmt() always creates a new insertion block after emitting
+ // the return. That block is unreachable in this case, so erase it.
+ block->erase();
+ // Set the return value back. The code generator, as the AST
+ // **Consumer**, shouldn't change the AST.
+ ret->setRetValue(previousRetValue);
}
+
+ return mlir::success();
+ };
+
+ cir::CoroutineOp coro = cir::CoroutineOp::create(
+ builder, openCurlyLoc,
+ /*initialSuspendBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ res = initialSuspendBuilder();
+ },
+ /*bodyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ if (res.succeeded())
+ res = bodyBuilder();
+ },
+ /*finalSuspendBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ if (res.succeeded())
+ res = finalSuspendBuilder();
+ },
+ /*destroyBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ if (res.succeeded())
+ res = destroyBuilder();
+ },
+ /*exitBuilder=*/
+ [&](mlir::OpBuilder &b, mlir::Location loc) {
+ if (res.succeeded())
+ res = exitBuilder();
+ });
+
+ if (coro && !coro.getBody().back().mightHaveTerminator()) {
+ mlir::OpBuilder::InsertionGuard guard(builder);
+ builder.setInsertionPointAfter(&coro.getBody().back().back());
+ cir::YieldOp::create(builder, openCurlyLoc);
}
}
- cir::ConstantOp nullHandler =
- builder.getNullPtr(builder.getVoidPtrTy(), openCurlyLoc);
- cir::ConstantOp noUnwind = builder.getBool(false, openCurlyLoc);
- auto tkNone = cir::TokenNoneOp::create(builder, openCurlyLoc);
- cir::CoroEndOp::create(builder, openCurlyLoc, nullHandler, noUnwind, tkNone);
-
- 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();
+ return res;
}
static bool memberCallExpressionCanThrow(const Expr *e) {
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 33c32a626e47c8..8a24c0464d21d2 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3133,25 +3133,19 @@ void cir::FuncOp::print(OpAsmPrinter &p) {
mlir::LogicalResult cir::FuncOp::verify() {
if (!isDeclaration() && getCoroutine()) {
- bool foundAwait = false;
- int coroBodyCount = 0;
+ int coroutineOpCount = 0;
this->walk([&](Operation *op) {
- if (auto await = dyn_cast<AwaitOp>(op)) {
- foundAwait = true;
- } else if (isa<CoroBodyOp>(op)) {
- coroBodyCount++;
- if (coroBodyCount > 1) {
+ if (isa<CoroutineOp>(op)) {
+ coroutineOpCount++;
+ if (coroutineOpCount > 1) {
return mlir::WalkResult::interrupt();
}
}
return mlir::WalkResult::advance();
});
- if (!foundAwait)
+ if (coroutineOpCount != 1)
return emitOpError()
- << "coroutine body must use at least one cir.await op";
- if (coroBodyCount != 1)
- return emitOpError()
- << "coroutine function must have exactly one cir.body op";
+ << "coroutine function must have exactly one cir.coroutine op";
}
llvm::SmallSet<llvm::StringRef, 16> labels;
@@ -3555,71 +3549,124 @@ LogicalResult cir::AwaitOp::verify() {
}
//===----------------------------------------------------------------------===//
-// CoroBody
+// CoReturnOp
//===----------------------------------------------------------------------===//
-void cir::CoroBodyOp::getSuccessorRegions(
- mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
- if (!point.isParent()) {
- regions.emplace_back(getOperation());
- return;
+LogicalResult cir::CoReturnOp::verify() {
+ mlir::Operation *coRet = getOperation();
+ auto coroutine = coRet->getParentOfType<CoroutineOp>();
+ mlir::Region &coroBody = coroutine.getBody();
+ for (Operation *current = coRet; current; current = current->getParentOp()) {
+ if (current->getParentRegion() == &coroBody)
+ return success();
}
- regions.push_back(RegionSuccessor(&getBody()));
+ return emitOpError("must be inside the cir.coroutine body region");
}
-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);
-}
-
-
//===----------------------------------------------------------------------===//
// CoroutineOp
//===----------------------------------------------------------------------===//
void cir::CoroutineOp::build(OpBuilder &builder, OperationState &result,
- BuilderCallbackRef initialSuspendBuilder,
- BuilderCallbackRef bodyBuilder,
- BuilderCallbackRef finalSuspendBuilder,
- BuilderCallbackRef destroyBuilder,
- BuilderCallbackRef exitBuilder) {
- //{
- // OpBuilder::InsertionGuard guard(builder);
- // Region *bodyRegion = result.addRegion();
- // builder.createBlock(bodyRegion);
- // bodyBuilder(builder, result.location);
- //}
-
- //{
- // OpBuilder::InsertionGuard guard(builder);
- // Region *retRegion = result.addRegion();
- // builder.createBlock(retRegion);
- // retBuilder(builder, result.location);
- //}
+ BuilderCallbackRef initialSuspendBuilder,
+ BuilderCallbackRef bodyBuilder,
+ BuilderCallbackRef finalSuspendBuilder,
+ BuilderCallbackRef destroyBuilder,
+ BuilderCallbackRef exitBuilder) {
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *initialRegion = result.addRegion();
+ builder.createBlock(initialRegion);
+ initialSuspendBuilder(builder, result.location);
+ }
+
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *bodyRegion = result.addRegion();
+ builder.createBlock(bodyRegion);
+ bodyBuilder(builder, result.location);
+ }
+
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *finalRegion = result.addRegion();
+ builder.createBlock(finalRegion);
+ finalSuspendBuilder(builder, result.location);
+ }
+
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *destroyRegion = result.addRegion();
+ builder.createBlock(destroyRegion);
+ destroyBuilder(builder, result.location);
+ }
+
+ {
+ OpBuilder::InsertionGuard guard(builder);
+ Region *exitRegion = result.addRegion();
+ builder.createBlock(exitRegion);
+ exitBuilder(builder, result.location);
+ }
}
void cir::CoroutineOp::getSuccessorRegions(
mlir::RegionBranchPoint point, SmallVectorImpl<RegionSuccessor> ®ions) {
- if (!point.isParent()) {
- regions.emplace_back(getOperation());
+ // The parent op always enters through initial_suspend get_return_object()
+ // (if present) and the init await always run first.
+ if (point.isParent()) {
+ regions.emplace_back(&getInitialSuspend());
+ return;
+ }
+
+ mlir::Region *parent =
+ point.getTerminatorPredecessorOrNull()->getParentRegion();
+
+ // initial_suspend either falls into body (resumed, or never actually
+ // suspended because await_ready() was true) or exits directly, a plain
+ // suspend here means nobody has resumed yet, so we just return to caller.
+ if (parent == &getInitialSuspend()) {
+ regions.emplace_back(&getBody());
+ regions.emplace_back(&getExit());
return;
}
- //regions.push_back(RegionSuccessor(&getBodyRegion()));
- //regions.push_back(RegionSuccessor(&getRetRegion()));
+ // body can fall through into final_suspend (co_return), exit directly
+ // from any suspend_point inside it that just suspends, or reach destroy,
+ // either from a suspend_point being destroyed, or from an exception
+ // escaping body's own catch-all.
+ if (parent == &getBody()) {
+ regions.emplace_back(&getFinalSuspend());
+ regions.emplace_back(&getExit()); // any suspend_point inside body
+ regions.emplace_back(&getDestroy()); // destroy() on any suspend inside body
+ return;
+ }
+
+ // final_suspend's only live edge in valid programs is destroy resuming
+ // past the final suspend is UB. The ready-immediately edge to exit is
+ // kept for structural symmetry with the other two suspend regions even
+ // though it's effectively dead.
+ if (parent == &getFinalSuspend()) {
+ regions.emplace_back(
+ &getDestroy()); // the only real exit from final_suspend
+ regions.emplace_back(&getExit()); // ready==true edge, rarely taken
+ return;
+ }
+
+ // destroy has two possible outcomes depending on why it was entered:
+ // ordinary destroy dispatch falls through to exit (normal return); an
+ // exception that reached destroy needs to keep propagating instead, i.e.
+ // leave the whole op rather than go through exit's cir.return.
+ if (parent == &getDestroy()) {
+ regions.emplace_back(&getExit());
+ regions.push_back(RegionSuccessor(getOperation())); // unwind out of the op
+ return;
+ }
+ // exit always terminates the op there's no region it loops back into.
+ if (parent == &getExit()) {
+ regions.emplace_back(getOperation());
+ return;
+ }
}
mlir::ValueRange
@@ -3628,6 +3675,83 @@ cir::CoroutineOp::getSuccessorInputs(RegionSuccessor successor) {
}
LogicalResult cir::CoroutineOp::verify() {
+ mlir::Region &initialSuspend = getInitialSuspend();
+ mlir::Region &body = getBody();
+ mlir::Region &finalSuspend = getFinalSuspend();
+ mlir::Region &destroy = getDestroy();
+ mlir::Region &exit = getExit();
+
+ // initial_suspend must contain exactly one 'init'-kind cir.await an
+ // optional prelude of ordinary statements (get_return_object() and
+ // similar) is allowed before it, but no *other* await may appear.
+ int initAwaitCount = 0;
+ bool hasInvalidAwait = false;
+
+ initialSuspend.walk([&](Operation *op) {
+ auto awaitOp = mlir::dyn_cast<AwaitOp>(op);
+ if (!awaitOp)
+ return mlir::WalkResult::advance();
+
+ if (awaitOp.getKind() == cir::AwaitKind::Init)
+ ++initAwaitCount;
+ else
+ hasInvalidAwait = true;
+
+ if (initAwaitCount > 1 || hasInvalidAwait)
+ return mlir::WalkResult::interrupt();
+
+ return mlir::WalkResult::advance();
+ });
+
+ if (hasInvalidAwait)
+ return emitOpError("'initial_suspend' must not contain any cir.await other "
+ "than the 'init' one");
+ if (initAwaitCount != 1)
+ return emitOpError(
+ "must have exactly one 'init' cir.await in 'initial_suspend'");
+
+ // final_suspend must contain only one or zero 'final'-kind cir.await
+ int finalAwaitCount = 0;
+ hasInvalidAwait = false;
+
+ finalSuspend.walk([&](Operation *op) {
+ auto awaitOp = mlir::dyn_cast<AwaitOp>(op);
+ if (!awaitOp)
+ return mlir::WalkResult::advance();
+
+ if (awaitOp.getKind() == cir::AwaitKind::Final)
+ ++finalAwaitCount;
+ else
+ hasInvalidAwait = true;
+
+ if (finalAwaitCount > 1 || hasInvalidAwait)
+ return mlir::WalkResult::interrupt();
+
+ return mlir::WalkResult::advance();
+ });
+
+ if (hasInvalidAwait)
+ return emitOpError("'final_suspend' must not contain any cir.await other "
+ "than the 'final' one");
+ if (finalAwaitCount > 1)
+ return emitOpError(
+ "must have one or zero 'final' cir.await in 'final_suspend'");
+
+ // Each region must end with the terminator its role requires:
+ // initial_suspend/final_suspend/destroy yield back into cir.coroutine's
+ // own control flow, body ends in a cir.co_return (or a plain yield if
+ // some path never reaches one), and exit actually returns from the
+ // function.
+ if (!isa<YieldOp>(initialSuspend.back().back()))
+ return emitOpError("'initial_suspend' must end with cir.yield");
+ if (!isa<YieldOp, CoReturnOp>(body.back().back()))
+ return emitOpError("'body' must end with cir.yield or cir.co_return");
+ if (!isa<YieldOp>(finalSuspend.back().back()))
+ return emitOpError("'final_suspend' must end with cir.yield");
+ if (!isa<YieldOp>(destroy.back().back()))
+ return emitOpError("'destroy' must end with cir.yield");
+ if (!isa<ReturnOp>(exit.back().back()))
+ return emitOpError("'exit' must end with cir.return");
return mlir::success();
}
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-exceptions.cpp b/clang/test/CIR/CodeGenCoroutines/coro-exceptions.cpp
index 5edae916f8f9fb..b1b35ae058a506 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-exceptions.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-exceptions.cpp
@@ -11,13 +11,14 @@ TaskWithEH simple_eh_body() {
}
// CIR-LABEL: cir.func{{.*}} @_Z14simple_eh_bodyv
+// CIR: cir.coroutine initialSuspend : {
// CIR: }, resume : {
// Note no 'try'/'catch' here.
// CIR-NEXT: cir.call @_ZNSt15suspend_nothrow12await_resumeEv
// CIR-NEXT: cir.yield
// CIR-NEXT: }
-// CIR: cir.coro.body {
+// CIR: }, body : {
// CIR-NOT: cir.if
// CIR: cir.scope {
// CIR: cir.try {
@@ -35,8 +36,8 @@ TaskWithEH simple_eh_body() {
// CIR: }
// CIR: }
// CIR: cir.yield
-// CIR: }
// Make sure that 'final' is outside of the above try/catch/etc.
+// CIR: }, finalSuspend : {
// CIR: cir.call @_ZN10TaskWithEH12promise_type13final_suspendEv
TaskThrowingInit throwing_init_suspend() {
@@ -45,6 +46,7 @@ TaskThrowingInit throwing_init_suspend() {
// CIR-LABEL: cir.func{{.*}} @_Z21throwing_init_suspendv
// CIR: %[[RESUME_FLAG:.*]] = cir.alloca "resume.eh" align(1) : !cir.ptr<!cir.bool>
+// CIR: cir.coroutine initialSuspend : {
// CIR: }, resume : {
// CIR: %[[FALSE:.*]] = cir.const #false
// CIR: cir.store %[[FALSE]], %[[RESUME_FLAG]] : !cir.bool, !cir.ptr<!cir.bool>
@@ -69,7 +71,7 @@ TaskThrowingInit throwing_init_suspend() {
// CIR: cir.yield
// CIR: }
-// CIR: cir.coro.body {
+// CIR: }, body : {
// CIR: %[[LOAD_RESUME_FLAG:.*]] = cir.load align(1) %[[RESUME_FLAG]] : !cir.ptr<!cir.bool>, !cir.bool
// CIR: cir.if %[[LOAD_RESUME_FLAG]] {
// CIR: cir.scope {
@@ -89,5 +91,5 @@ TaskThrowingInit throwing_init_suspend() {
// CIR: }
// CIR: }
// CIR: cir.yield
-// CIR: }
+// CIR: }, finalSuspend : {
// CIR: cir.call @_ZN16TaskThrowingInit12promise_type13final_suspendEv
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index 7879ea6655e633..b4ee46b94287ac 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -74,11 +74,11 @@ VoidTask silly_task() {
// Call promise.get_return_object() to retrieve the task object.
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
-// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]]
-// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
+// CIR: cir.coroutine initialSuspend : {
+
+// 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]])
@@ -90,8 +90,8 @@ VoidTask silly_task() {
// Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting
// the suspend_always struct to use for cir.await.
-// 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]])
@@ -127,7 +127,7 @@ VoidTask silly_task() {
// CIR-NEXT: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
// CIR-NEXT: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
// CIR-NEXT: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
-// CIR-NEXT: cir.coro.suspend.point
+// CIR-NEXT: cir.coro.suspend_point
// OGCG: init.suspend:
// OGCG: %[[Save:.*]] = call token @llvm.coro.save(ptr null)
@@ -144,6 +144,7 @@ VoidTask silly_task() {
// CIR-NEXT: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
// CIR-NEXT: cir.yield
// CIR-NEXT: },)
+// CIR-NEXTY cir.yield loc(#loc34)
// OGCG: init.ready:
// OGCG: call void @_ZNSt14suspend_always12await_resumeEv(ptr noundef nonnull align 1 dereferenceable(1) %[[SuspendAlwaysAddr]]
@@ -154,23 +155,23 @@ VoidTask silly_task() {
// - The promise call
// - The final suspend co_await
// - Return
-
-// CIR: cir.coro.body {
+
+// CIR: }, 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
-// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]])
-// CIR: cir.co_return
-// CIR: }
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(%[[VoidPromisseAddr]])
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
// OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]])
@@ -179,7 +180,7 @@ VoidTask silly_task() {
// CIR: }, suspend : {
// CIR: }, resume : {
// CIR: },)
-// CIR-NEXT: cir.yield
+// CIR-NEXT: cir.yield
// OGCG: coro.final:
// OGCG: final.suspend:
@@ -191,17 +192,17 @@ VoidTask silly_task() {
// 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.coro.intrinsic.free(%[[CoroId]], %[[CoroFrameAddr]]) : (token, !cir.ptr<!void>) -> !cir.ptr<!void>
-// CIR: %[[NullPtr2:.*]] = cir.const #cir.ptr<null>
-// CIR: %[[Cond:.*]] = cir.cmp ne %[[FreeMem]], %[[NullPtr2]]
-// CIR: cir.if %[[Cond]] {
-// CIR: %[[Size:.*]] = cir.coro.intrinsic.size()
-// CIR: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]])
-// CIR: }
-// CIR: cir.yield
-// CIR: }
-// CIR: cir.yield
+// CIR-NEXT: }, destroy : {
+
+// CIR-NEXT: %[[FreeMem:.*]] = cir.coro.intrinsic.free(%[[CoroId]], %[[CoroFrameAddr]]) : (token, !cir.ptr<!void>) -> !cir.ptr<!void>
+// CIR-NEXT: %[[NullPtr2:.*]] = cir.const #cir.ptr<null>
+// CIR-NEXT: %[[Cond:.*]] = cir.cmp ne %[[FreeMem]], %[[NullPtr2]]
+// CIR-NEXT: cir.if %[[Cond]] {
+// CIR-NEXT: %[[Size:.*]] = cir.coro.intrinsic.size()
+// CIR-NEXT: cir.call @_ZdlPvm(%[[FreeMem]], %[[Size]]){{.*}}
+// CIR-NEXT: }
+// CIR-NEXT: cir.yield
+// CIR-NEXT: }, exit : {
// OGCG: %[[FreeMem:.*]] = call ptr @llvm.coro.free(token %[[CoroId]], ptr %[[CoroFrameAddr]])
// OGCG: %[[Cond:.*]] = icmp ne ptr %[[FreeMem]], null
@@ -215,17 +216,14 @@ VoidTask silly_task() {
// Call builtin coro end and return
-// CIR: }, ret : {
// CIR-NEXT: %[[CoroEndArg0:.*]] = cir.const #cir.ptr<null> : !cir.ptr<!void>
// CIR-NEXT: %[[CoroEndArg1:.*]] = cir.const #false
// CIR-NEXT: %[[TK_NONE:.*]] = cir.token.none
// CIR-NEXT: cir.coro.intrinsic.end(%[[CoroEndArg0]], %[[CoroEndArg1]], %[[TK_NONE]]) : (!cir.ptr<!void>, !cir.bool, token){{.*}}
-// CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]]
-// CIR: cir.return %[[Tmp1]]
-// CIR: }
-// CIRL }
-// CIR: cir.trap
-// CIR:}
+
+// CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]]
+// CIR: cir.return %[[Tmp1]]
+// CIR: }
// OGCG: coro.ret:
// OGCG: call void @llvm.coro.end(ptr null, i1 false, token none)
@@ -246,49 +244,47 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: %[[CoroHandlePromiseAddr:.*]] = cir.alloca "agg.tmp1" {{.*}} : !cir.ptr<![[CoroHandlePromiseInt]]>
// CIR: cir.store %[[ARG]], %[[AllocaParam]] : !cir.ptr<![[StdString]]>, {{.*}}
-// CIR: cir.coro.ret_point {
-// 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.coro.suspend_point
-// 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: }
+// 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: cir.coroutine initialSuspend : {
+// 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.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, body : {
+ // can't fallthrough
+// CIR-NOT: cir.await(user
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[IntPromisseAddr]], %[[STRING_SIZE:.*]])
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
// The final suspend co_await
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
// CIR: cir.yield
-// CIR: }, ret : {
+// CIR: }, destroy : {
+// CIR: }, exit : {
folly::coro::Task<void> silly_coro() {
std::optional<folly::coro::Task<int>> task;
@@ -304,24 +300,22 @@ folly::coro::Task<void> silly_coro() {
// check there are not multiple co_returns emitted.
// CIR: cir.func coroutine {{.*}} @_Z10silly_corov() {{.*}} ![[VoidTask]]
-// CIR: cir.coro.ret_point {
-// 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: }
-// CIR: }, ret : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }, body : {
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11return_voidEv
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, destroy : {
+// CIR: }, exit : {
folly::coro::Task<void> yield();
folly::coro::Task<void> yield1() {
@@ -347,81 +341,80 @@ folly::coro::Task<void> yield1() {
// CIR-DAG: %[[CH_VOID2:.*]] = cir.alloca "agg.tmp5" {{.*}} : !cir.ptr<![[CoroHandleVoid]]>
// CIR-DAG: %[[CH_PROM2:.*]] = cir.alloca "agg.tmp6" {{.*}} : !cir.ptr<![[CoroHandlePromiseVoid]]>
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
+// CIR: cir.coroutine initialSuspend : {
// 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.coro.suspend_point
-// 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.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.yield
// yield_value + await(yield)
-// CIR: cir.coro.body {
-// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
-// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_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.coro.suspend_point
-// 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: }
+// CIR: }, body : {
+// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
+// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_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.coro.suspend_point
+// 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: }, finalSuspend : {
// return_void + await(final)
-// 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.coro.suspend_point
-// CIR: }, resume : {
-// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
-// CIR: cir.yield
-// CIR: },)
+// 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.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
// CIR: cir.yield
-// CIR: } cleanup normal {
-// CIR: }
-// CIR: }, ret : {
-// CIR: = cir.coro.intrinsic.end(%{{.*}}, %{{.*}})
-// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
-// CIR: cir.return %[[RETLOAD]]
-// CIR: }
-// CIR: cir.trap
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, destroy : {
+// CIR: }, exit : {
+// CIR: cir.coro.intrinsic.end(%{{.*}}, %{{.*}}, %{{.*}}){{.*}}
+// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
+// CIR: cir.return %[[RETLOAD]]
+// CIR: }
+
// CHECK: }
folly::coro::Task<int> go(int const& val);
@@ -434,39 +427,37 @@ folly::coro::Task<int> go1() {
// CIR: %[[IntTaskAddr:.*]] = cir.alloca "task" {{.*}} init : !cir.ptr<![[IntTask]]>
// CIR: %[[OneAddr:.*]] = cir.alloca "ref.tmp1" align(4) init : !cir.ptr<!s32i>
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
-// CIR: cir.coro.body {
-// 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.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: }, body : {
+// 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.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.co_return
+
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
// CIR: cir.yield
-// CIR: }, ret : {
+// CIR: }, destroy : {
+// CIR: }, exit : {
folly::coro::Task<int> go1_lambda() {
auto task = []() -> folly::coro::Task<int> {
@@ -476,49 +467,48 @@ folly::coro::Task<int> go1_lambda() {
}
// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]]
-// CIR: cir.coro.ret_point {
-// 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: }, ret : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, body : {
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[ONE]])
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }, destroy : {
// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() {{.*}} ![[IntTask]]
-// CIR: cir.coro.ret_point {
-// 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 {
-// CIR: }, ret : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, 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: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, destroy : {
folly::coro::Task<int> go4() {
auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
@@ -527,22 +517,21 @@ folly::coro::Task<int> go4() {
}
// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
-// CIR: cir.coro.ret_point {
-// 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: }, ret : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, body : {
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[I:.*]])
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }, destroy : {
folly::coro::Task<int> go4() {
auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
@@ -552,42 +541,40 @@ folly::coro::Task<int> go4() {
// CIR: cir.func coroutine {{.*}} @_Z3go4v() attributes
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coroutine initialSuspend : {
+// 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: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
-// 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>
+// CIR: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
+// CIR: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
+// 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.await(user, ready : {
-// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
-// CIR: cir.condition(
-// CIR: }, suspend : {
-// CIR: cir.coro.suspend_point
-// CIR: }, resume : {
-// CIR: cir.yield
-// CIR: },)
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi
-// CIR: cir.co_return
-// CIR: }
+// 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.await(user, ready : {
+// CIR: = cir.call @_ZN5folly4coro4TaskIiE11await_readyEv(%[[TASK_ADDR]])
+// CIR: cir.condition(
+// CIR: }, suspend : {
+// CIR: cir.coro.suspend_point
+// CIR: }, resume : {
+// CIR: cir.yield
+// CIR: },)
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: } cleanup normal {
-// CIR: }, ret : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: }, destroy : {
// OGCG: define {{.*}}__await_suspend_wrapper__init(ptr noundef nonnull %[[Awaiter:.*]], ptr noundef %[[Handle:.*]])
// OGCG: entry:
@@ -616,43 +603,42 @@ folly::coro::Task<int> co_returns(int x) {
}
// CIR: cir.func coroutine {{.*}} @_Z10co_returnsi
-// CIR: cir.coro.ret_point {
-// 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.mul nsw %[[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 {
-// CIR: }, ret : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, 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.mul nsw %[[X_LOAD]], %[[TWO]]
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[RES]])
+// CIR: cir.co_return
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, destroy : {
// OGCG: define {{.*}} @_Z10co_returnsi
@@ -679,14 +665,14 @@ folly::coro::Task<int> implicit_yield_terminator(int x) {
co_return 2;
}
// CIR: cir.func coroutine {{.*}} @_Z25implicit_yield_terminatori
-// CIR: cir.coro.body {
+// CIR: }, body : {
// CIR: cir.if %{{.*}} {
// CIR: cir.co_return
// CIR: } else {
// CIR: cir.co_return
// CIR: }
// CIR: cir.yield
-// CIR: }
+// CIR: }, finalSuspend : {
struct HasDtor {
~ HasDtor();
@@ -702,42 +688,41 @@ folly::coro::Task<int> co_return_with_dtor(int flag) {
// CIR: cir.func coroutine {{.*}} @_Z19co_return_with_dtori
// CIR: %[[LOCAL:.*]] = cir.alloca "local" {{.*}} : !cir.ptr<!rec_HasDtor>
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
-// CIR: cir.coro.body {
-// CIR: cir.cleanup.scope {
-// CIR: cir.scope {
-// CIR: %[[CAST_FLAG:.*]] = cir.cast int_to_bool %[[FLAG:.*]]
-// CIR: cir.if %[[CAST_FLAG]] {
-// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise:.*]], %[[ONE]])
-// CIR: cir.co_return
-// CIR: }
-// CIR: }
-// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise]], %[[TWO]])
+// CIR: }, body : {
+// CIR: cir.cleanup.scope {
+// CIR: cir.scope {
+// CIR: %[[CAST_FLAG:.*]] = cir.cast int_to_bool %[[FLAG:.*]]
+// CIR: cir.if %[[CAST_FLAG]] {
+// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise:.*]], %[[ONE]])
// CIR: cir.co_return
-// CIR: } cleanup normal {
-// CIR: cir.call @_ZN7HasDtorD1Ev(%[[LOCAL]])
-// CIR: cir.yield
// CIR: }
-// CIR: cir.yield
// CIR: }
-
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: cir.yield
+// CIR: %[[TWO:.*]] = cir.const #cir.int<2>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[promise]], %[[TWO]])
+// CIR: cir.co_return
// CIR: } cleanup normal {
+// CIR: cir.call @_ZN7HasDtorD1Ev(%[[LOCAL]])
// CIR: cir.yield
// CIR: }
-// CIR: }, ret : {
+// CIR: cir.yield
+
+// CIR: }, finalSuspend : {
+// CIR: cir.await(final, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+// CIR: cir.yield
+// CIR: }, destroy : {
+// CIR: cir.yield
+// CIR: }
// OGCG: define {{.*}} void @_Z19co_return_with_dtori
// OGCG: %[[LOCAL:.*]] = alloca %struct.HasDtor
@@ -769,30 +754,28 @@ folly::coro::Task<int __complex__> complex_co_await() noexcept {
// CIR: %[[COMPLEX_ADDR:.*]] = cir.alloca "ref.tmp1" {{.*}} : !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
// CIR: %[[RESUME_VAL_ADDR:.*]] = cir.alloca "__coawait_resume_rval" {{.*}} : !cir.ptr<!cir.complex<!s32i>>
-// CIR: cir.coro.ret_point {
-// CIR: cir.cleanup.scope {
-// CIR: cir.await(init, ready : {
+// CIR: cir.coroutine initialSuspend : {
+// CIR: cir.await(init, ready : {
+// CIR: }, suspend : {
+// CIR: }, resume : {
+// CIR: },)
+
+// CIR: }, body : {
+// CIR: %[[CALL:.*]] = cir.call @_Z9fetchDatav() nothrow : () -> !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
+// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
+
+// CIR: cir.await(user, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
+// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!cir.complex<!s32i> {llvm.noundef})
+// CIR: cir.store %[[RESUME_VAL]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
// CIR: },)
+// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: cir.yield
+// CIR: }, finalSuspend : {
-// CIR: cir.coro.body {
-// CIR: %[[CALL:.*]] = cir.call @_Z9fetchDatav() nothrow : () -> !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
-// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
-
-// CIR: cir.await(user, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!cir.complex<!s32i> {llvm.noundef})
-// CIR: cir.store %[[RESUME_VAL]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
-// CIR: },)
-// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
-// CIR: cir.yield
-// CIR: }
-
-// CIR: } cleanup normal {
-// CIR: }
-// CIR: }, ret : {
+// CIR: }, destroy : {
+// CIR: }, exit : {
// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
// CIR: cir.cleanup.scope {
diff --git a/clang/test/CIR/IR/await.cir b/clang/test/CIR/IR/await.cir
index 9e6a71351c42e8..b5d3df6175b9c2 100644
--- a/clang/test/CIR/IR/await.cir
+++ b/clang/test/CIR/IR/await.cir
@@ -1,29 +1,38 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
cir.func coroutine @checkPrintParse(%arg0 : !cir.bool) {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
cir.yield
- }
+ },)
cir.yield
- }, ret : {
- cir.return
+ }, body : {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
}
cir.trap
}
-// CHECK: cir.func coroutine @checkPrintParse
-// CHECK: cir.await(user, ready : {
-// CHECK: cir.condition(%arg0)
-// CHECK: }, suspend : {
-// CHECK: cir.coro.suspend_point
-// CHECK: }, resume : {
-// CHECK: cir.yield
-// CHECK: },)
+// CHECK: cir.await(user, ready : {
+// CHECK: cir.condition(%arg0)
+// CHECK: }, suspend : {
+// CHECK: cir.coro.suspend_point
+// CHECK: }, resume : {
+// CHECK: cir.yield
+// CHECK: },)
diff --git a/clang/test/CIR/IR/co-return.cir b/clang/test/CIR/IR/co-return.cir
index 833b6a27d80162..c7d16d4699c610 100644
--- a/clang/test/CIR/IR/co-return.cir
+++ b/clang/test/CIR/IR/co-return.cir
@@ -1,26 +1,26 @@
// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
cir.func coroutine @coro_co_return(%arg0 : !cir.bool) {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- cir.co_return
- }
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.co_return
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
cir.yield
- }, ret : {
- cir.return
+ }, exit : {
+ cir.return
}
cir.trap
}
// CHECK: cir.func coroutine @coro_co_return
-// CHECK: cir.coro.body {
-// CHECK: cir.co_return
-// CHECK: }
-
-
+// CHECK: cir.co_return
diff --git a/clang/test/CIR/IR/coro-body.cir b/clang/test/CIR/IR/coro-body.cir
deleted file mode 100644
index 30af9fc0e5d3cb..00000000000000
--- a/clang/test/CIR/IR/coro-body.cir
+++ /dev/null
@@ -1,24 +0,0 @@
-// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
-
-cir.func coroutine @coro_body(%arg0 : !cir.bool) {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- cir.co_return
- }
- cir.yield
- }, ret : {
- cir.return
- }
- cir.trap
-}
-
-// CHECK: cir.func coroutine @coro_body
-// CHECK: cir.coro.body {
-// CHECK: }
diff --git a/clang/test/CIR/IR/coro-ret-point.cir b/clang/test/CIR/IR/coro-ret-point.cir
deleted file mode 100644
index 44afdc6407931b..00000000000000
--- a/clang/test/CIR/IR/coro-ret-point.cir
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
-
-cir.func coroutine @coro_ret_point(%arg0 : !cir.bool) {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- cir.yield
- }
- cir.yield
- }, ret : {
- cir.return
- }
- cir.trap
-}
-
-// CHECK: cir.coro.ret_point {
-// CHECK: }, ret : {
diff --git a/clang/test/CIR/IR/coroutine.cir b/clang/test/CIR/IR/coroutine.cir
new file mode 100644
index 00000000000000..d9ef98936376a6
--- /dev/null
+++ b/clang/test/CIR/IR/coroutine.cir
@@ -0,0 +1,36 @@
+// RUN: cir-opt %s --verify-roundtrip | FileCheck %s
+
+cir.func coroutine @valid_coroutine(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// CHECK: cir.func coroutine @valid_coroutine
+// CHECK: cir.coroutine initialSuspend : {
+// CHECK: cir.await(init
+// CHECK: }, body : {
+// CHECK: cir.yield
+// CHECK: }, finalSuspend : {
+// CHECK: cir.yield
+// CHECK: }, destroy : {
+// CHECK: cir.yield
+// CHECK: }, exit : {
+// CHECK: cir.return
+// CHECK: }
diff --git a/clang/test/CIR/IR/func.cir b/clang/test/CIR/IR/func.cir
index e7420d56022dbe..2070df469d142b 100644
--- a/clang/test/CIR/IR/func.cir
+++ b/clang/test/CIR/IR/func.cir
@@ -109,22 +109,25 @@ cir.func @ullfunc() -> !u64i {
// CHECK: }
cir.func coroutine @coro() {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(init, ready : {
- %0 = cir.alloca "" align(1) : !cir.ptr<!cir.bool>
- %1 = cir.load align(1) %0 : !cir.ptr<!cir.bool>, !cir.bool
- cir.condition(%1)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
+ %0 = cir.alloca "" align(1) : !cir.ptr<!cir.bool>
+ %1 = cir.load align(1) %0 : !cir.ptr<!cir.bool>, !cir.bool
+ cir.condition(%1)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
cir.yield
- }
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
cir.yield
- }, ret : {
- cir.return
+ }, exit : {
+ cir.return
}
cir.trap
}
diff --git a/clang/test/CIR/IR/invalid-await.cir b/clang/test/CIR/IR/invalid-await.cir
index 8e40e0886428fa..813f10b65b519b 100644
--- a/clang/test/CIR/IR/invalid-await.cir
+++ b/clang/test/CIR/IR/invalid-await.cir
@@ -1,43 +1,52 @@
// 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.func coroutine @bad_task() { // expected-error {{coroutine function must have exactly one cir.coroutine op}}
cir.return
}
// -----
cir.func coroutine @missing_condition() {
- cir.coro.ret_point {
- cir.await(user, ready : { // expected-error {{ready region must end with cir.condition}}
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : { // expected-error {{ready region must end with cir.condition}}
cir.yield
}, suspend : {
cir.coro.suspend_point
}, resume : {
cir.yield
},)
- cir.coro.body {
- }
cir.yield
- }, ret : {
- cir.return
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
}
cir.trap
}
-cir.func coroutine @missing_suspend(%arg0 : !cir.bool) {
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : { // expected-error {{suspend region must end with cir.coro.suspend_point}}
- cir.condition(%arg0)
- }, suspend : {
- cir.yield
- }, resume : {
- cir.yield
- },)
+// -----
+
+cir.func coroutine @missing_suspend_point(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : { // expected-error {{suspend region must end with cir.coro.suspend_point}}
+ cir.condition(%arg0)
+ }, suspend : {
cir.yield
- }
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
cir.yield
- }, ret : {
- cir.return
+ }, exit : {
+ cir.return
}
cir.trap
}
diff --git a/clang/test/CIR/IR/invalid-co-return.cir b/clang/test/CIR/IR/invalid-co-return.cir
index 115bfbac26d427..3f3cbac044a4e6 100644
--- a/clang/test/CIR/IR/invalid-co-return.cir
+++ b/clang/test/CIR/IR/invalid-co-return.cir
@@ -1,5 +1,29 @@
// RUN: cir-opt %s -verify-diagnostics -split-input-file
-cir.func @must_be_inside_coro_body() {
- cir.co_return // expected-error {{expects ancestor op 'cir.coro.body'}}
+cir.func @must_be_inside_coroutine() {
+ cir.co_return // expected-error {{expects ancestor op 'cir.coroutine'}}
+}
+
+// -----
+
+cir.func coroutine @must_be_in_body(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.co_return // expected-error {{must be inside the cir.coroutine body region}}
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
}
diff --git a/clang/test/CIR/IR/invalid-coro-body.cir b/clang/test/CIR/IR/invalid-coro-body.cir
deleted file mode 100644
index c72ec6e4450fbb..00000000000000
--- a/clang/test/CIR/IR/invalid-coro-body.cir
+++ /dev/null
@@ -1,21 +0,0 @@
-// 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}}
-
- }
-}
-
-cir.func coroutine @must_have_one_coro_body(%arg0 : !cir.bool) { // expected-error {{coroutine function must have exactly one cir.body op}}
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- }
- cir.coro.body {
- }
-}
diff --git a/clang/test/CIR/IR/invalid-coro-ret-point.cir b/clang/test/CIR/IR/invalid-coro-ret-point.cir
deleted file mode 100644
index 3e65f094ccb63b..00000000000000
--- a/clang/test/CIR/IR/invalid-coro-ret-point.cir
+++ /dev/null
@@ -1,55 +0,0 @@
-// RUN: cir-opt %s -verify-diagnostics -split-input-file
-
-cir.func coroutine @coro_ret_point_body_must_end_in_yield(%arg0 : !cir.bool) {
- cir.coro.ret_point { // expected-error {{body region must terminate with 'cir.yield'}}
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- }
- cir.trap
- }, ret : {
- cir.return
- }
- cir.trap
-}
-
-cir.func coroutine @coro_ret_point_body_must_not_contain_return(%arg0 : !cir.bool) {
- cir.coro.ret_point { // expected-error {{body region must not contain 'cir.return' operations}}
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- }
- cir.return
- }, ret : {
- cir.return
- }
- cir.trap
-}
-
-cir.func coroutine @coro_ret_point_return_region_must_end_in_return(%arg0 : !cir.bool) {
- cir.coro.ret_point { // expected-error {{return region must terminate with 'cir.return'}}
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- }
- cir.yield
- }, ret : {
- cir.trap
- }
- cir.trap
-}
diff --git a/clang/test/CIR/IR/invalid-coro-suspend.cir b/clang/test/CIR/IR/invalid-coro-suspend.cir
deleted file mode 100644
index d03769ed663060..00000000000000
--- a/clang/test/CIR/IR/invalid-coro-suspend.cir
+++ /dev/null
@@ -1,23 +0,0 @@
-// RUN: cir-opt %s -verify-diagnostics -split-input-file
-
-
-cir.func coroutine @must_have_one_coro_ret_point(%arg0 : !cir.bool) { // expected-error {{coroutine function must have exactly one cir.coro.ret_point op}}
- cir.coro.ret_point {
- cir.coro.body {
- cir.await(user, ready : {
- cir.condition(%arg0)
- }, suspend : {
- cir.coro.suspend_point
- }, resume : {
- cir.yield
- },)
- }
- cir.yield
- }, ret : {
- cir.return
- }
- cir.coro.ret_point {
- }, ret : {
- }
- cir.trap
-}
diff --git a/clang/test/CIR/IR/invalid-coroutine.cir b/clang/test/CIR/IR/invalid-coroutine.cir
new file mode 100644
index 00000000000000..0ca25aed13e105
--- /dev/null
+++ b/clang/test/CIR/IR/invalid-coroutine.cir
@@ -0,0 +1,289 @@
+// RUN: cir-opt %s -verify-diagnostics -split-input-file
+
+cir.func coroutine @missing_initial_await() {
+ cir.coroutine initialSuspend : { // expected-error {{must have exactly one 'init' cir.await in 'initial_suspend'}}
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+}
+
+// -----
+
+cir.func coroutine @multiple_initial_awaits(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{must have exactly one 'init' cir.await in 'initial_suspend'}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+}
+
+// -----
+
+cir.func coroutine @wrong_initial_await_kind(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'initial_suspend' must not contain any cir.await other than the 'init' one}}
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @initial_suspend_wrong_terminator(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'initial_suspend' must end with cir.yield}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.return
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @body_wrong_terminator(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'body' must end with cir.yield or cir.co_return}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.return
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @wrong_final_await_kind(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'final_suspend' must not contain any cir.await other than the 'final' one}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @final_suspend_wrong_terminator(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'final_suspend' must end with cir.yield}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.return
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @destroy_wrong_terminator(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'destroy' must end with cir.yield}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.return
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @exit_wrong_terminator(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'exit' must end with cir.return}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.yield
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @final_suspend_multiple_awaits(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{must have one or zero 'final' cir.await in 'final_suspend'}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.await(final, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.await(final, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
+
+// -----
+
+cir.func coroutine @final_suspend_invalid_await(%arg0 : !cir.bool) {
+ cir.coroutine initialSuspend : { // expected-error {{'final_suspend' must not contain any cir.await other than the 'final' one}}
+ cir.await(init, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.await(user, ready : {
+ cir.condition(%arg0)
+ }, suspend : {
+ cir.coro.suspend_point
+ }, resume : {
+ cir.yield
+ },)
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
+ }
+ cir.trap
+}
>From 6e3e958a883f2baa6a92a3de6307dc74ffa9b455 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 3 Sep 2026 21:23:16 -0500
Subject: [PATCH 07/12] Fix after rebase
---
.../test/CIR/CodeGenCoroutines/coro-task.cpp | 184 ++++++++----------
1 file changed, 85 insertions(+), 99 deletions(-)
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
index b4ee46b94287ac..cbdd177d037076 100644
--- a/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
+++ b/clang/test/CIR/CodeGenCoroutines/coro-task.cpp
@@ -74,10 +74,13 @@ VoidTask silly_task() {
// Call promise.get_return_object() to retrieve the task object.
+// Empty records are ignored, so calls returning one return nothing and the
+// slot takes a poison placeholder.
// CIR: cir.coroutine initialSuspend : {
-// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ![[VoidTask]]
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type17get_return_objectEv(%[[VoidPromisseAddr]]) nothrow : {{.*}} -> ()
+// CIR: %[[RetObj:.*]] = cir.const #cir.poison : ![[VoidTask]]
// CIR: cir.store{{.*}} %[[RetObj]], %[[VoidTaskAddr]] : ![[VoidTask]]
// OGCG: call void @llvm.lifetime.start.p0(ptr %[[VoidPromisseAddr]])
@@ -90,7 +93,8 @@ VoidTask silly_task() {
// Effectively execute `coawait promise_type::initial_suspend()` by calling initial_suspend() and getting
// the suspend_always struct to use for cir.await.
-// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[VoidPromisseAddr]])
+// CIR: %[[Tmp0:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
// CIR: cir.store{{.*}} %[[Tmp0]], %[[SuspendAlwaysAddr]]
// OGCG: call void @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(ptr noundef nonnull align 1 dereferenceable(1) %[[VoidPromisseAddr]])
@@ -126,7 +130,7 @@ VoidTask silly_task() {
// CIR-NEXT: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
// CIR-NEXT: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
// CIR-NEXT: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
-// CIR-NEXT: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
+// CIR-NEXT: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]])
// CIR-NEXT: cir.coro.suspend_point
// OGCG: init.suspend:
@@ -155,7 +159,7 @@ VoidTask silly_task() {
// - The promise call
// - The final suspend co_await
// - Return
-
+
// CIR: }, body : {
// The actual user written co_await
@@ -222,7 +226,7 @@ VoidTask silly_task() {
// CIR-NEXT: cir.coro.intrinsic.end(%[[CoroEndArg0]], %[[CoroEndArg1]], %[[TK_NONE]]) : (!cir.ptr<!void>, !cir.bool, token){{.*}}
// CIR: %[[Tmp1:.*]] = cir.load{{.*}} %[[VoidTaskAddr]]
-// CIR: cir.return %[[Tmp1]]
+// CIR: cir.return
// CIR: }
// OGCG: coro.ret:
@@ -250,20 +254,23 @@ folly::coro::Task<int> byRef(const std::string& s) {
// CIR: cir.store {{.*}} %[[LOAD]], %[[AllocaFnUse]] : !cir.ptr<![[StdString]]>, !cir.ptr<!cir.ptr<![[StdString]]>>
// CIR: cir.coroutine initialSuspend : {
-// CIR: %[[RetObj:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ![[IntTask]]
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type17get_return_objectEv(%[[IntPromisseAddr]]) nothrow : {{.*}} -> ()
+// CIR: %[[RetObj:.*]] = cir.const #cir.poison : ![[IntTask]]
// CIR: cir.store {{.*}} %[[RetObj]], %[[IntTaskAddr]] : ![[IntTask]]
-// CIR: %[[Tmp0:.*]] = cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type15initial_suspendEv(%[[IntPromisseAddr]])
+// CIR: %[[Tmp0:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
// 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.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIiE12promise_typeEE12from_addressEPv(%[[CoroFrameAddr:.*]])
+// CIR: %[[FromAddrRes:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseInt]]
// CIR: cir.store{{.*}} %[[FromAddrRes]], %[[CoroHandlePromiseAddr]] : ![[CoroHandlePromiseInt]]
// CIR: %[[CoroHandlePromiseReload:.*]] = cir.load{{.*}} %[[CoroHandlePromiseAddr]]
-// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]], %[[CoroHandlePromiseReload]])
+// CIR: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIiE12promise_typeEEES_IT_E(%[[CoroHandleVoidAddr]])
// CIR: %[[CoroHandleVoidReload:.*]] = cir.load{{.*}} %[[CoroHandleVoidAddr]] : !cir.ptr<![[CoroHandleVoid]]>, ![[CoroHandleVoid]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]], %[[CoroHandleVoidReload]])
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SuspendAlwaysAddr]])
// CIR: cir.coro.suspend_point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SuspendAlwaysAddr]])
@@ -299,7 +306,7 @@ folly::coro::Task<void> silly_coro() {
// Make sure we properly handle OnFallthrough coro body sub stmt and
// check there are not multiple co_returns emitted.
-// CIR: cir.func coroutine {{.*}} @_Z10silly_corov() {{.*}} ![[VoidTask]]
+// CIR: cir.func coroutine {{.*}} @_Z10silly_corov() attributes
// CIR: cir.coroutine initialSuspend : {
// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
@@ -343,18 +350,20 @@ folly::coro::Task<void> yield1() {
// CIR: cir.coroutine initialSuspend : {
// initial_suspend + await(init)
-// CIR: %[[INIT_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type15initial_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: %[[INIT_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
// 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.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: %[[FROMADDR0:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
// 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: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID0]]){{.*}}
// CIR: %[[VOID_RELOAD0:.*]] = cir.load{{.*}} %[[CH_VOID0]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]], %[[VOID_RELOAD0]]){{.*}}
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP0]]){{.*}}
// CIR: cir.coro.suspend_point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP0]]){{.*}}
@@ -364,21 +373,24 @@ folly::coro::Task<void> yield1() {
// yield_value + await(yield)
// CIR: }, body : {
-// CIR: %[[YIELD_TASK:.*]] = cir.call @_Z5yieldv(){{.*}}
+// CIR: cir.call @_Z5yieldv(){{.*}}
+// CIR: %[[YIELD_TASK:.*]] = cir.const #cir.poison : ![[VoidTask]]
// CIR: cir.store{{.*}} %[[YIELD_TASK]], %[[T_ADDR]]
// CIR: %[[AWAITER:.*]] = cir.load{{.*}} %[[AWAITER_COPY_ADDR]]
-// CIR: %[[YIELD_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]], %[[AWAITER]]){{.*}}
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type11yield_valueES2_(%[[PROMISE]]){{.*}}
+// CIR: %[[YIELD_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
// 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.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: %[[FROMADDR1:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
// 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: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID1]]){{.*}}
// CIR: %[[VOID_RELOAD1:.*]] = cir.load{{.*}} %[[CH_VOID1]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]], %[[VOID_RELOAD1]]){{.*}}
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP1]]){{.*}}
// CIR: cir.coro.suspend_point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP1]]){{.*}}
@@ -389,19 +401,20 @@ folly::coro::Task<void> yield1() {
// CIR: }, finalSuspend : {
// return_void + await(final)
-
-// CIR: %[[FINAL_SUSP:.*]] = cir.call @_ZN5folly4coro4TaskIvE12promise_type13final_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: cir.call @_ZN5folly4coro4TaskIvE12promise_type13final_suspendEv(%[[PROMISE]]){{.*}}
+// CIR: %[[FINAL_SUSP:.*]] = cir.const #cir.poison : ![[SuspendAlways]]
// 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.call @_ZNSt16coroutine_handleIN5folly4coro4TaskIvE12promise_typeEE12from_addressEPv(%{{.*}}){{.*}}
+// CIR: %[[FROMADDR2:.*]] = cir.const #cir.poison : ![[CoroHandlePromiseVoid]]
// 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: cir.call @_ZNSt16coroutine_handleIvEC1IN5folly4coro4TaskIvE12promise_typeEEES_IT_E(%[[CH_VOID2]]){{.*}}
// CIR: %[[VOID_RELOAD2:.*]] = cir.load{{.*}} %[[CH_VOID2]]
-// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]], %[[VOID_RELOAD2]]){{.*}}
+// CIR: cir.call @_ZNSt14suspend_always13await_suspendESt16coroutine_handleIvE(%[[SUSP2]]){{.*}}
// CIR: cir.coro.suspend_point
// CIR: }, resume : {
// CIR: cir.call @_ZNSt14suspend_always12await_resumeEv(%[[SUSP2]]){{.*}}
@@ -412,7 +425,7 @@ folly::coro::Task<void> yield1() {
// CIR: }, exit : {
// CIR: cir.coro.intrinsic.end(%{{.*}}, %{{.*}}, %{{.*}}){{.*}}
// CIR: %[[RETLOAD:.*]] = cir.load{{.*}} %[[RETVAL]]
-// CIR: cir.return %[[RETLOAD]]
+// CIR: cir.return
// CIR: }
// CHECK: }
@@ -437,7 +450,8 @@ folly::coro::Task<int> go1() {
// CIR: }, body : {
// 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.call @_Z2goRKi(%[[OneAddr]]) : (!cir.ptr<!s32i>{{.*}}) -> ()
+// CIR: %[[IntTaskTmp:.*]] = cir.const #cir.poison : ![[IntTask]]
// CIR: cir.store{{.*}} %[[IntTaskTmp]], %[[IntTaskAddr]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
// CIR: cir.await(user, ready : {
@@ -466,7 +480,7 @@ folly::coro::Task<int> go1_lambda() {
co_return co_await task;
}
-// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}} ![[IntTask]]
+// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() attributes
// CIR: cir.coroutine initialSuspend : {
// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
@@ -474,9 +488,16 @@ folly::coro::Task<int> go1_lambda() {
// CIR: },)
// CIR: cir.yield
// CIR: }, body : {
-// CIR: %[[ONE:.*]] = cir.const #cir.int<1>
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[ONE]])
-// CIR: cir.co_return
+// 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: }, finalSuspend : {
// CIR: cir.await(final, ready : {
// CIR: }, suspend : {
@@ -484,7 +505,7 @@ folly::coro::Task<int> go1_lambda() {
// CIR: },)
// CIR: }, destroy : {
-// CIR: cir.func coroutine {{.*}} @_Z10go1_lambdav() {{.*}} ![[IntTask]]
+// CIR: cir.func coroutine {{.*}} @_ZZ10go1_lambdavENK3$_0clEv{{.*}}) attributes
// CIR: cir.coroutine initialSuspend : {
// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
@@ -492,16 +513,9 @@ folly::coro::Task<int> go1_lambda() {
// CIR: },)
// CIR: cir.yield
// CIR: }, 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: %[[ONE:.*]] = cir.const #cir.int<1>
+// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[ONE]])
+// CIR: cir.co_return
// CIR: }, finalSuspend : {
// CIR: cir.await(final, ready : {
// CIR: }, suspend : {
@@ -516,7 +530,7 @@ folly::coro::Task<int> go4() {
co_return co_await std::move(task);
}
-// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
+// CIR: cir.func coroutine {{.*}} @_Z3go4v() attributes
// CIR: cir.coroutine initialSuspend : {
// CIR: cir.await(init, ready : {
// CIR: }, suspend : {
@@ -524,38 +538,20 @@ folly::coro::Task<int> go4() {
// CIR: },)
// CIR: cir.yield
// CIR: }, body : {
-// CIR: cir.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE:.*]], %[[I:.*]])
-// CIR: cir.co_return
-// CIR: }, finalSuspend : {
-// CIR: cir.await(final, ready : {
-// CIR: }, suspend : {
-// CIR: }, resume : {
-// CIR: },)
-// CIR: }, destroy : {
-
-folly::coro::Task<int> go4() {
- auto* fn = +[](int const& i) -> folly::coro::Task<int> { co_return i; };
- auto task = fn(3);
- co_return co_await std::move(task);
-}
-
-// CIR: cir.func coroutine {{.*}} @_Z3go4v() attributes
-
-// CIR: cir.coroutine initialSuspend : {
-// 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: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
-// 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: %[[INVOKER:.*]] = cir.call @_ZZ3go4vENK3$_0cvPFN5folly4coro4TaskIiEERKiEEv(%{{.*}}) nothrow : {{.*}} -> (!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> {llvm.noundef})
+// CIR: cir.store{{.*}} %[[INVOKER]], %[[FN_ADDR:.*]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>, !cir.ptr<!cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>>>
+// 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. The callee is bitcast to
+// the lowered signature.
+
+// CIR: %[[FN_LOWERED:.*]] = cir.cast bitcast %[[FN]] : !cir.ptr<!cir.func<(!cir.ptr<!s32i>) -> ![[IntTask]]>> -> !cir.ptr<!cir.func<(!cir.ptr<!s32i>)>>
+// CIR: cir.call %[[FN_LOWERED]](%[[ARG]]) : (!cir.ptr<!cir.func<(!cir.ptr<!s32i>)>>, !cir.ptr<!s32i> {{.*}}) -> ()
+// CIR: %[[CALLRES:.*]] = cir.const #cir.poison : ![[IntTask]]
// CIR: cir.store{{.*}} %[[CALLRES]], %[[TASK_ADDR:.*]] : ![[IntTask]], !cir.ptr<![[IntTask]]>
// CIR: cir.await(user, ready : {
@@ -618,8 +614,8 @@ folly::coro::Task<int> co_returns(int x) {
// 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.call @_ZN5folly4coro4TaskIiE12promise_type12return_valueEi(%[[PROMISE]], %[[MINUS_TWO]])
+// CIR cir.co_return
// CIR: }
// CIR: }
// CIR: }
@@ -761,38 +757,28 @@ folly::coro::Task<int __complex__> complex_co_await() noexcept {
// CIR: },)
// CIR: }, body : {
-// CIR: %[[CALL:.*]] = cir.call @_Z9fetchDatav() nothrow : () -> !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
-// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
+// CIR: cir.call @_Z9fetchDatav() nothrow : () -> ()
+// CIR: %[[CALL:.*]] = cir.const #cir.poison : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E
+// CIR: cir.store {{.*}} %[[CALL]], %[[COMPLEX_ADDR]] : !rec_folly3A3Acoro3A3ATask3C_Complex_int3E, !cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E>
// CIR: cir.await(user, ready : {
// CIR: }, suspend : {
// CIR: }, resume : {
-// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!cir.complex<!s32i> {llvm.noundef})
-// CIR: cir.store %[[RESUME_VAL]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
-// CIR: },)
-// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
-// CIR: cir.yield
+// _Complex int is one eightbyte in class INTEGER, so it returns as !u64i and
+// is reloaded through the coerce slot.
+
+// CIR: %[[RESUME_VAL:.*]] = cir.call @_ZN5folly4coro4TaskICiE12await_resumeEv(%[[COMPLEX_ADDR]]) : (!cir.ptr<!rec_folly3A3Acoro3A3ATask3C_Complex_int3E> {llvm.align = 1 : i64, llvm.dereferenceable = 1 : i64, llvm.nonnull, llvm.noundef}) -> (!u64i {llvm.noundef})
+// CIR: cir.store %[[RESUME_VAL]], %[[COERCE_ADDR]] : !u64i, !cir.ptr<!u64i>
+// CIR: %[[COERCE_CAST:.*]] = cir.cast bitcast %[[COERCE_ADDR]] : !cir.ptr<!u64i> -> !cir.ptr<!cir.complex<!s32i>>
+// CIR: %[[RESUME_COMPLEX:.*]] = cir.load %[[COERCE_CAST]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: cir.store %[[RESUME_COMPLEX]], %[[RESUME_VAL_ADDR]] : !cir.complex<!s32i>, !cir.ptr<!cir.complex<!s32i>>
+// CIR: },)
+// CIR: %[[V:.*]] = cir.load %[[RESUME_VAL_ADDR]] : !cir.ptr<!cir.complex<!s32i>>, !cir.complex<!s32i>
+// CIR: cir.yield
// CIR: }, finalSuspend : {
-
// CIR: }, destroy : {
// CIR: }, exit : {
-// CIR: cir.func coroutine{{.*}} @_ZZ3go4vENK3$_0clERKi(
-// 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 {
-
// OGCG: define dso_local void @_Z16complex_co_awaitv()
// OGCG: %[[RESUME_VAL_ADDR:.*]] = alloca { i32, i32 }, align 4
>From af118251d5b55b4a76c1c14709a0fd73d28d8f7d Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Tue, 8 Sep 2026 17:20:21 -0500
Subject: [PATCH 08/12] Fix After Rebase
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 41 --------------------
1 file changed, 41 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index fccca08ee06ef0..c1d95c61345905 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4953,47 +4953,6 @@ def CIR_AwaitOp : CIR_RegionBranchOpBase<"await", [
let hasVerifier = 1;
let hasLLVMLowering = false;
}
-<<<<<<< HEAD
-//===----------------------------------------------------------------------===//
-// CoroBody
-//===----------------------------------------------------------------------===//
-def CIR_CoroBodyOp : CIR_RegionBranchOpBase<"coro.body", [
- 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:$body);
-
- let skipDefaultBuilders = 1;
-
- let builders = [
- OpBuilder<(ins "BuilderCallbackRef":$bodyBuilder)>
- ];
-
- let assemblyFormat = [{
- $body attr-dict
- }];
-
- let hasLLVMLowering = false;
- let hasVerifier = 1;
-}
-=======
->>>>>>> 56098cff2da3 ([CIR] Add cir.coroutine and remove cir.coroBody)
//===----------------------------------------------------------------------===//
// CoReturnOp
>From 531c83239fd6471983ef1c92733b991c6e412936 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Mon, 21 Sep 2026 20:23:46 -0500
Subject: [PATCH 09/12] Update ControlFlowTest after rebase
---
clang/unittests/CIR/ControlFlowTest.cpp | 95 ++++++++++++++++++++-----
1 file changed, 79 insertions(+), 16 deletions(-)
diff --git a/clang/unittests/CIR/ControlFlowTest.cpp b/clang/unittests/CIR/ControlFlowTest.cpp
index d6b9fda5235e6d..5376d27c84608d 100644
--- a/clang/unittests/CIR/ControlFlowTest.cpp
+++ b/clang/unittests/CIR/ControlFlowTest.cpp
@@ -613,50 +613,113 @@ TEST_F(CIRControlFlowTest, TryOpWithCatchAll) {
// have block arguments, so verifyControlFlowInterfaceConsistency fails.
}
-TEST_F(CIRControlFlowTest, CoroBodyOp) {
- // A coroutine body must contain at least one cir.await.
+TEST_F(CIRControlFlowTest, CoroutineOp) {
OwningOpRef<ModuleOp> module = parse(R"CIR(
cir.func coroutine @f(%arg0 : !cir.bool) {
- cir.coro.body {
- cir.await(user, ready : {
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend_point
}, resume : {
cir.yield
},)
cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
}
- cir.return
+ cir.trap
}
)CIR");
- auto coroBodyOp = findFirstOp<cir::CoroBodyOp>(*module);
+ auto coroOp = findFirstOp<cir::CoroutineOp>(*module);
- expectSuccessors(coroBodyOp, RegionBranchPoint::parent(),
- {&coroBodyOp.getBody()});
- expectTerminatorSuccessors(coroBodyOp.getBody(), {nullptr});
+ // The parent only ever enters through initial_suspend.
+ expectSuccessors(coroOp, RegionBranchPoint::parent(),
+ {&coroOp.getInitialSuspend()});
- RegionBranchOpInterface coroBranch = asRegionBranch(coroBodyOp);
+ // initial_suspend: falls into body once resumed, exits directly on a
+ // plain suspend, or reaches destroy on an explicit destroy() call.
+ RegionBranchTerminatorOpInterface initTerm =
+ getTerminator(coroOp.getInitialSuspend());
+ ASSERT_TRUE(initTerm);
+ expectSuccessors(
+ coroOp, RegionBranchPoint(initTerm),
+ {&coroOp.getBody(), &coroOp.getExit(), &coroOp.getDestroy()});
+ expectTerminatorSuccessors(
+ coroOp.getInitialSuspend(),
+ {&coroOp.getBody(), &coroOp.getExit(), &coroOp.getDestroy()});
+
+ // body: falls through to final_suspend, exits directly on a plain
+ // suspend, or reaches destroy
+ RegionBranchTerminatorOpInterface bodyTerm = getTerminator(coroOp.getBody());
+ ASSERT_TRUE(bodyTerm);
+ expectSuccessors(
+ coroOp, RegionBranchPoint(bodyTerm),
+ {&coroOp.getFinalSuspend(), &coroOp.getExit(), &coroOp.getDestroy()});
+ expectTerminatorSuccessors(
+ coroOp.getBody(),
+ {&coroOp.getFinalSuspend(), &coroOp.getExit(), &coroOp.getDestroy()});
+
+ // final_suspend: exits or destroy.
+ // Should always be destroy in practice, a real suspend after
+ // final_suspend is UB, so the exit edge is never actually taken by a
+ // valid program. Keeping exit here anyway for now.
+ RegionBranchTerminatorOpInterface finalTerm =
+ getTerminator(coroOp.getFinalSuspend());
+ ASSERT_TRUE(finalTerm);
+ expectSuccessors(coroOp, RegionBranchPoint(finalTerm),
+ {&coroOp.getExit(), &coroOp.getDestroy()});
+ expectTerminatorSuccessors(coroOp.getFinalSuspend(),
+ {&coroOp.getExit(), &coroOp.getDestroy()});
+
+ // destroy: ordinary dispatch falls through to exit;
+ RegionBranchTerminatorOpInterface destroyTerm =
+ getTerminator(coroOp.getDestroy());
+ ASSERT_TRUE(destroyTerm);
+ expectSuccessors(coroOp, RegionBranchPoint(destroyTerm), {&coroOp.getExit()});
+ expectTerminatorSuccessors(coroOp.getDestroy(), {&coroOp.getExit()});
+
+ // exit always terminates the op.
+ RegionBranchTerminatorOpInterface exitTerm = getTerminator(coroOp.getExit());
+ ASSERT_TRUE(exitTerm);
+ expectSuccessors(coroOp, RegionBranchPoint(exitTerm), {nullptr});
+ expectTerminatorSuccessors(coroOp.getExit(), {nullptr});
+
+ RegionBranchOpInterface coroBranch = asRegionBranch(coroOp);
EXPECT_FALSE(coroBranch.isRepetitiveRegion(0));
EXPECT_FALSE(coroBranch.hasLoop());
- verifyControlFlowInterfaceConsistency(coroBodyOp);
+ verifyControlFlowInterfaceConsistency(coroOp);
}
TEST_F(CIRControlFlowTest, AwaitOp) {
OwningOpRef<ModuleOp> module = parse(R"CIR(
cir.func coroutine @f(%arg0 : !cir.bool) {
- cir.coro.body {
- cir.await(user, ready : {
+ cir.coroutine initialSuspend : {
+ cir.await(init, ready : {
cir.condition(%arg0)
}, suspend : {
- cir.yield
+ cir.coro.suspend_point
}, resume : {
cir.yield
},)
cir.yield
+ }, body : {
+ cir.yield
+ }, finalSuspend : {
+ cir.yield
+ }, destroy : {
+ cir.yield
+ }, exit : {
+ cir.return
}
- cir.return
+ cir.trap
}
)CIR");
auto awaitOp = findFirstOp<cir::AwaitOp>(*module);
>From 2f0104ad8c934c3d0799d867e6c05a568796cd6d Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Mon, 21 Sep 2026 21:37:08 -0500
Subject: [PATCH 10/12] Address review comments
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 5 ++++
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 2 +-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 27 +++++++++++++++-----
3 files changed, 26 insertions(+), 8 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index c1d95c61345905..85a8dc5d61a1da 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -4808,19 +4808,24 @@ def CIR_CoroutineOp : CIR_Op<"coroutine", [
`cir.co_return` operations inside this region mark coroutine exit
points and introduce structured control flow that transfers
execution to `final_suspend`.
+
- `final_suspend`: exactly one `cir.await`, marked `final`. Nothing
else. Corresponds to `co_await promise.final_suspend()`. Resuming
past this point is undefined behavior in valid programs, so its
`resume` edge is expected to be dead in practice, but is kept for
structural symmetry with the other two suspend regions.
+
- `gro`: TODO.
+
- `unwind`: TODO.
+
- `destroy`: destroys the coroutine frame (`coro.free` + `delete`).
Reached by an explicit `destroy()` call on a suspended handle, by
normal completion through `final_suspend`, or by an exception
escaping the coroutine. Falls through to `exit` except when reached
due to an escaping exception, where it continues propagating that
exception instead.
+
- `exit`: the shared return-to-caller path. Reached directly from any
suspend point that isn't being destroyed (the coroutine is just
pausing), and from `destroy` when it was entered via ordinary
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index af6ee325706249..deb7f1d2bafcf9 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -590,7 +590,7 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
res = exitBuilder();
});
- if (coro && !coro.getBody().back().mightHaveTerminator()) {
+ if (coro && !coro.getBody().empty() && !coro.getBody().back().mightHaveTerminator()) {
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointAfter(&coro.getBody().back().back());
cir::YieldOp::create(builder, openCurlyLoc);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index 8a24c0464d21d2..a64977599019d6 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3543,6 +3543,8 @@ void cir::AwaitOp::getSuccessorRegions(
LogicalResult cir::AwaitOp::verify() {
if (!isa<ConditionOp>(this->getReady().back().getTerminator()))
return emitOpError("ready region must end with cir.condition");
+ if (this->getSuspend().empty())
+ return emitOpError("suspend region must not be empty");
if (!isa<CoroSuspendPoint>(this->getSuspend().back().getTerminator()))
return emitOpError("suspend region must end with cir.coro.suspend_point");
return success();
@@ -3555,13 +3557,9 @@ LogicalResult cir::AwaitOp::verify() {
LogicalResult cir::CoReturnOp::verify() {
mlir::Operation *coRet = getOperation();
auto coroutine = coRet->getParentOfType<CoroutineOp>();
- mlir::Region &coroBody = coroutine.getBody();
- for (Operation *current = coRet; current; current = current->getParentOp()) {
- if (current->getParentRegion() == &coroBody)
- return success();
- }
-
- return emitOpError("must be inside the cir.coroutine body region");
+ if (!coroutine.getBody().isAncestor(getOperation()->getParentRegion()))
+ return emitOpError("must be inside the cir.coroutine body region");
+ return success();
}
//===----------------------------------------------------------------------===//
@@ -3742,14 +3740,29 @@ LogicalResult cir::CoroutineOp::verify() {
// own control flow, body ends in a cir.co_return (or a plain yield if
// some path never reaches one), and exit actually returns from the
// function.
+ if (initialSuspend.empty() || initialSuspend.back().empty())
+ return emitOpError("initial_suspend region must not be empty");
if (!isa<YieldOp>(initialSuspend.back().back()))
return emitOpError("'initial_suspend' must end with cir.yield");
+
+ if (body.empty() || body.back().empty())
+ return emitOpError("'body' region must not be empty");
if (!isa<YieldOp, CoReturnOp>(body.back().back()))
return emitOpError("'body' must end with cir.yield or cir.co_return");
+
+ if (finalSuspend.empty() || finalSuspend.back().empty())
+ return emitOpError("'final_suspend' region must not be empty");
if (!isa<YieldOp>(finalSuspend.back().back()))
return emitOpError("'final_suspend' must end with cir.yield");
+
+ if (destroy.empty() || destroy.back().empty())
+ return emitOpError("'destroy' region must not be empty");
if (!isa<YieldOp>(destroy.back().back()))
return emitOpError("'destroy' must end with cir.yield");
+
+
+ if (exit.empty() || exit.back().empty())
+ return emitOpError("'exit' region must not be empty");
if (!isa<ReturnOp>(exit.back().back()))
return emitOpError("'exit' must end with cir.return");
>From 768af970a58d7ee555caadb6604d1bbf161f6137 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 24 Sep 2026 18:30:29 -0500
Subject: [PATCH 11/12] Fix formatting
---
clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 3 ++-
clang/lib/CIR/Dialect/IR/CIRDialect.cpp | 1 -
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index deb7f1d2bafcf9..dcff972ddd7d9c 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -590,7 +590,8 @@ CIRGenFunction::emitCoroutineBody(const CoroutineBodyStmt &s) {
res = exitBuilder();
});
- if (coro && !coro.getBody().empty() && !coro.getBody().back().mightHaveTerminator()) {
+ if (coro && !coro.getBody().empty() &&
+ !coro.getBody().back().mightHaveTerminator()) {
mlir::OpBuilder::InsertionGuard guard(builder);
builder.setInsertionPointAfter(&coro.getBody().back().back());
cir::YieldOp::create(builder, openCurlyLoc);
diff --git a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
index a64977599019d6..6653bbfc245b92 100644
--- a/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
+++ b/clang/lib/CIR/Dialect/IR/CIRDialect.cpp
@@ -3760,7 +3760,6 @@ LogicalResult cir::CoroutineOp::verify() {
if (!isa<YieldOp>(destroy.back().back()))
return emitOpError("'destroy' must end with cir.yield");
-
if (exit.empty() || exit.back().empty())
return emitOpError("'exit' region must not be empty");
if (!isa<ReturnOp>(exit.back().back()))
>From 234ec75939d11810b355612740492eafc50dc6b4 Mon Sep 17 00:00:00 2001
From: Andres Salamanca <andrealebarbaritos at gmail.com>
Date: Thu, 24 Sep 2026 20:50:54 -0500
Subject: [PATCH 12/12] Fix unittests
---
clang/include/clang/CIR/Dialect/IR/CIROps.td | 3 +-
clang/unittests/CIR/ControlFlowTest.cpp | 35 +++++++++++---------
2 files changed, 22 insertions(+), 16 deletions(-)
diff --git a/clang/include/clang/CIR/Dialect/IR/CIROps.td b/clang/include/clang/CIR/Dialect/IR/CIROps.td
index 85a8dc5d61a1da..11cd7a75687f78 100644
--- a/clang/include/clang/CIR/Dialect/IR/CIROps.td
+++ b/clang/include/clang/CIR/Dialect/IR/CIROps.td
@@ -5174,7 +5174,8 @@ def CIR_CoroDoneOp : CIR_CoroIntrinsicOp<"done", (ins CIR_VoidPtrType:$handle),
//===----------------------------------------------------------------------===//
def CIR_CoroSuspendPoint : CIR_Op<"coro.suspend_point", [
- Pure, Terminator, HasParent<"AwaitOp">
+ Pure, Terminator, HasParent<"AwaitOp">,
+ DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>
]> {
let summary = "Marks the point where a coroutine actually suspends";
let description = [{
diff --git a/clang/unittests/CIR/ControlFlowTest.cpp b/clang/unittests/CIR/ControlFlowTest.cpp
index 5376d27c84608d..47312d5e8843a1 100644
--- a/clang/unittests/CIR/ControlFlowTest.cpp
+++ b/clang/unittests/CIR/ControlFlowTest.cpp
@@ -648,12 +648,10 @@ TEST_F(CIRControlFlowTest, CoroutineOp) {
RegionBranchTerminatorOpInterface initTerm =
getTerminator(coroOp.getInitialSuspend());
ASSERT_TRUE(initTerm);
- expectSuccessors(
- coroOp, RegionBranchPoint(initTerm),
- {&coroOp.getBody(), &coroOp.getExit(), &coroOp.getDestroy()});
- expectTerminatorSuccessors(
- coroOp.getInitialSuspend(),
- {&coroOp.getBody(), &coroOp.getExit(), &coroOp.getDestroy()});
+ expectSuccessors(coroOp, RegionBranchPoint(initTerm),
+ {&coroOp.getBody(), &coroOp.getExit()});
+ expectTerminatorSuccessors(coroOp.getInitialSuspend(),
+ {&coroOp.getBody(), &coroOp.getExit()});
// body: falls through to final_suspend, exits directly on a plain
// suspend, or reaches destroy
@@ -682,20 +680,27 @@ TEST_F(CIRControlFlowTest, CoroutineOp) {
RegionBranchTerminatorOpInterface destroyTerm =
getTerminator(coroOp.getDestroy());
ASSERT_TRUE(destroyTerm);
- expectSuccessors(coroOp, RegionBranchPoint(destroyTerm), {&coroOp.getExit()});
- expectTerminatorSuccessors(coroOp.getDestroy(), {&coroOp.getExit()});
+ expectSuccessors(coroOp, RegionBranchPoint(destroyTerm),
+ {&coroOp.getExit(), nullptr});
+ expectTerminatorSuccessors(coroOp.getDestroy(), {&coroOp.getExit(), nullptr});
+ // TODO: cir.return doesn't implement RegionBranchTerminatorOpInterface
// exit always terminates the op.
- RegionBranchTerminatorOpInterface exitTerm = getTerminator(coroOp.getExit());
- ASSERT_TRUE(exitTerm);
- expectSuccessors(coroOp, RegionBranchPoint(exitTerm), {nullptr});
- expectTerminatorSuccessors(coroOp.getExit(), {nullptr});
+ // RegionBranchTerminatorOpInterface exitTerm =
+ // getTerminator(coroOp.getExit()); ASSERT_TRUE(exitTerm);
+ // expectSuccessors(coroOp, RegionBranchPoint(exitTerm), {nullptr});
+ // expectTerminatorSuccessors(coroOp.getExit(), {nullptr});
RegionBranchOpInterface coroBranch = asRegionBranch(coroOp);
EXPECT_FALSE(coroBranch.isRepetitiveRegion(0));
- EXPECT_FALSE(coroBranch.hasLoop());
-
- verifyControlFlowInterfaceConsistency(coroOp);
+ EXPECT_FALSE(coroBranch.isRepetitiveRegion(1));
+ EXPECT_FALSE(coroBranch.isRepetitiveRegion(2));
+ EXPECT_FALSE(coroBranch.isRepetitiveRegion(3));
+ EXPECT_FALSE(coroBranch.isRepetitiveRegion(4));
+ EXPECT_TRUE(coroBranch.hasLoop());
+
+ // TODO: cir.return doesn't implement RegionBranchTerminatorOpInterface
+ // verifyControlFlowInterfaceConsistency(coroOp);
}
TEST_F(CIRControlFlowTest, AwaitOp) {
More information about the cfe-commits
mailing list