[clang] [CIR] Support aggregate co_await / co_yield in AggExprEmitter (PR #225412)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Sep 22 07:40:14 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clangir
Author: Aman Maurya (amanmaurya92)
<details>
<summary>Changes</summary>
Implement support for evaluating `co_await` and `co_yield` expressions whose result is an aggregate type in `AggExprEmitter`.
### Summary of Changes:
1. **`AggExprEmitter`**:
- Implement `VisitCoawaitExpr` and `VisitCoyieldExpr` by delegating to `emitCoawaitExpr` and `emitCoyieldExpr` with the destination `AggValueSlot`.
- Implement `VisitUnaryCoawait` by visiting the subexpression, matching classic Clang (`CGExprAgg.cpp`).
2. **`CIRGenCoroutine.cpp`**:
- In `emitSuspendExpression`: Omit the scalar/complex `__coawait_resume_rval` temporary alloca for aggregate return values, as the aggregate is emitted directly into `aggSlot`.
- In `emitSuspendExpr`: Remove the `emitSuspendExpr Aggregate` NYI assertion left as a TODO in #<!-- -->194027, return `rval` directly, and defensively instantiate an aggregate temporary slot when `aggSlot.isIgnored()`.
3. **Tests**:
- Add `clang/test/CIR/CodeGenCoroutines/coro-agg.cpp` covering used and unused results for aggregate `co_await` and `co_yield`.
Fixes #<!-- -->225317
Resolves Row 11 in the August 2026 ClangIR Progress Report.
---
Full diff: https://github.com/llvm/llvm-project/pull/225412.diff
3 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp (+18-19)
- (modified) clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp (+3-5)
- (added) clang/test/CIR/CodeGenCoroutines/coro-agg.cpp (+72)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index 6f2c0e33ce1d9..2fa9bd19a62f3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -707,25 +707,20 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro,
awaitRes.rv =
cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
if (!awaitRes.rv.isIgnored()) {
- // Create the alloca in the block before the scope wrapping
- // cir.await.
- mlir::Value value;
RValue rv = awaitRes.rv;
- if (rv.isScalar()) {
- value = rv.getValue();
- } else if (rv.isComplex()) {
- value = rv.getComplexValue();
+ if (rv.isScalar() || rv.isComplex()) {
+ mlir::Value value =
+ rv.isScalar() ? rv.getValue() : rv.getComplexValue();
+ tmpResumeRValAddr = cgf.emitAlloca(
+ "__coawait_resume_rval", value.getType(), loc,
+ CharUnits::One(),
+ builder.getBestAllocaInsertPoint(scopeParentBlock));
+ // Store the rvalue so we can reload it before the promise call.
+ builder.CIRBaseBuilderTy::createStore(loc, value,
+ tmpResumeRValAddr);
} else {
- cgf.cgm.errorNYI("emitSuspendExpression: Aggregate value");
- return;
+ assert(rv.isAggregate() && "unexpected rvalue kind");
}
-
- tmpResumeRValAddr = cgf.emitAlloca(
- "__coawait_resume_rval", value.getType(), loc, CharUnits::One(),
- builder.getBestAllocaInsertPoint(scopeParentBlock));
- // Store the rvalue so we can reload it before the promise call.
- builder.CIRBaseBuilderTy::createStore(loc, value,
- tmpResumeRValAddr);
}
}
@@ -744,6 +739,12 @@ static RValue emitSuspendExpr(CIRGenFunction &cgf,
RValue rval;
mlir::Location scopeLoc = cgf.getLoc(e.getSourceRange());
+ if (!ignoreResult && aggSlot.isIgnored() &&
+ cgf.getEvaluationKind(e.getType()) == cir::TEK_Aggregate) {
+ aggSlot = cgf.createAggTemp(e.getType(), scopeLoc,
+ cgf.getCounterAggTmpAsString());
+ }
+
// Since we model suspend / resume as an inner region, we must store
// resume scalar results in a tmp alloca, and load it after we build the
// suspend expression. An alternative way to do this would be to make
@@ -768,9 +769,7 @@ static RValue emitSuspendExpr(CIRGenFunction &cgf,
rval.getValue().getType(),
tmpResumeRValAddr));
} else if (rval.isAggregate()) {
- // This is probably already handled via AggSlot, remove this assertion
- // once we have a testcase and prove all pieces work.
- cgf.cgm.errorNYI("emitSuspendExpr Aggregate");
+ return rval;
} else { // complex
rval = RValue::getComplex(cir::LoadOp::create(
cgf.getBuilder(), scopeLoc, rval.getComplexValue().getType(),
diff --git a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
index 2fb66232d806c..e6ce32390d2c6 100644
--- a/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp
@@ -392,14 +392,12 @@ class AggExprEmitter : public StmtVisitor<AggExprEmitter> {
Visit(ge->getResultExpr());
}
void VisitCoawaitExpr(CoawaitExpr *e) {
- cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCoawaitExpr");
+ cgf.emitCoawaitExpr(*e, dest, dest.isIgnored());
}
void VisitCoyieldExpr(CoyieldExpr *e) {
- cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitCoyieldExpr");
- }
- void VisitUnaryCoawait(UnaryOperator *e) {
- cgf.cgm.errorNYI(e->getSourceRange(), "AggExprEmitter: VisitUnaryCoawait");
+ cgf.emitCoyieldExpr(*e, dest, dest.isIgnored());
}
+ void VisitUnaryCoawait(UnaryOperator *e) { Visit(e->getSubExpr()); }
void VisitUnaryExtension(UnaryOperator *e) { Visit(e->getSubExpr()); }
void VisitSubstNonTypeTemplateParmExpr(SubstNonTypeTemplateParmExpr *e) {
Visit(e->getReplacement());
diff --git a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp
new file mode 100644
index 0000000000000..5b31f07b0aea3
--- /dev/null
+++ b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp
@@ -0,0 +1,72 @@
+// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir
+// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR
+
+#include "Inputs/coroutine.h"
+
+struct B {
+ int x;
+ int y;
+ bool await_ready() { return true; }
+ B await_resume() { return {}; }
+ template <typename F> void await_suspend(F) {}
+};
+
+struct coro_t {
+ struct promise_type {
+ coro_t get_return_object() { return {}; }
+ std::suspend_never initial_suspend() { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ static void unhandled_exception() {}
+ B yield_value(int) { return {}; }
+ };
+};
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z22aggregate_coawait_exprv
+coro_t aggregate_coawait_expr() {
+ // CIR: %[[VAL:.*]] = cir.alloca "val" align(4) init : !cir.ptr<!rec_B>
+ // CIR: cir.await(user, ready : {
+ // CIR: }, suspend : {
+ // CIR: }, resume : {
+ // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}})
+ // CIR: cir.store align(4) %{{.*}}, %[[VAL]] : !rec_B, !cir.ptr<!rec_B>
+ // CIR: cir.yield
+ // CIR: },)
+ B val = co_await B{};
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z29aggregate_coawait_expr_unusedv
+coro_t aggregate_coawait_expr_unused() {
+ // CIR: cir.await(user, ready : {
+ // CIR: }, suspend : {
+ // CIR: }, resume : {
+ // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}})
+ // CIR: cir.yield
+ // CIR: },)
+ co_await B{};
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z22aggregate_coyield_exprv
+coro_t aggregate_coyield_expr() {
+ // CIR: %[[VAL:.*]] = cir.alloca "val" align(4) init : !cir.ptr<!rec_B>
+ // CIR: cir.await(yield, ready : {
+ // CIR: }, suspend : {
+ // CIR: }, resume : {
+ // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}})
+ // CIR: cir.store align(4) %{{.*}}, %[[VAL]] : !rec_B, !cir.ptr<!rec_B>
+ // CIR: cir.yield
+ // CIR: },)
+ B val = co_yield 42;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z29aggregate_coyield_expr_unusedv
+coro_t aggregate_coyield_expr_unused() {
+ // CIR: cir.await(yield, ready : {
+ // CIR: }, suspend : {
+ // CIR: }, resume : {
+ // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}})
+ // CIR: cir.yield
+ // CIR: },)
+ co_yield 42;
+}
+
``````````
</details>
https://github.com/llvm/llvm-project/pull/225412
More information about the cfe-commits
mailing list