https://github.com/amanmaurya92 updated https://github.com/llvm/llvm-project/pull/225412
>From 1089bc4d9f013586a97f5f357974c9ed86b2a04a Mon Sep 17 00:00:00 2001 From: amanmaurya92 <[email protected]> Date: Tue, 22 Sep 2026 20:04:50 +0530 Subject: [PATCH 1/2] [CIR] Support aggregate co_await / co_yield in AggExprEmitter Implement support for evaluating co_await and co_yield expressions whose result is an aggregate type in AggExprEmitter. Assisted by Antigravity. Reviewed by amanmaurya92. Fixes #225317 --- clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 37 +++++----- clang/lib/CIR/CodeGen/CIRGenExprAggregate.cpp | 8 +-- clang/test/CIR/CodeGenCoroutines/coro-agg.cpp | 72 +++++++++++++++++++ 3 files changed, 93 insertions(+), 24 deletions(-) create mode 100644 clang/test/CIR/CodeGenCoroutines/coro-agg.cpp diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index 6f2c0e33ce1d96..2fa9bd19a62f3c 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 2fb66232d806cd..e6ce32390d2c63 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 00000000000000..5b31f07b0aea3f --- /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; +} + >From 6e4df09f8fe0dea4a3a10be8dadd398ad9ffb0eb Mon Sep 17 00:00:00 2001 From: amanmaurya92 <[email protected]> Date: Wed, 23 Sep 2026 12:54:24 +0530 Subject: [PATCH 2/2] [CIR] Address review comments on aggregate co_await / co_yield - Remove redundant aggregate temporary allocation in emitSuspendExpr, as emitAnyExpr already provisions one when dest is ignored and AggExprEmitter passes dest.isIgnored() as ignoreResult. - Add OGCG test coverage comparing classic Clang CodeGen side-by-side with CIR in coro-agg.cpp. --- clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp | 6 ------ clang/test/CIR/CodeGenCoroutines/coro-agg.cpp | 21 ++++++++++++++++++- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index 2fa9bd19a62f3c..b0e03ce10d5a61 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -739,12 +739,6 @@ 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 diff --git a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp index 5b31f07b0aea3f..7f3d7775729044 100644 --- a/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp +++ b/clang/test/CIR/CodeGenCoroutines/coro-agg.cpp @@ -1,5 +1,7 @@ // RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir // RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG #include "Inputs/coroutine.h" @@ -23,6 +25,7 @@ struct coro_t { }; // CIR-LABEL: cir.func coroutine {{.*}} @_Z22aggregate_coawait_exprv +// OGCG-LABEL: define dso_local void @_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 : { @@ -32,10 +35,15 @@ coro_t aggregate_coawait_expr() { // CIR: cir.store align(4) %{{.*}}, %[[VAL]] : !rec_B, !cir.ptr<!rec_B> // CIR: cir.yield // CIR: },) + // OGCG: %[[VAL:.*]] = alloca %struct.B, align 4 + // OGCG: await.ready: + // OGCG: %[[RES:.*]] = call i64 @_ZN1B12await_resumeEv(ptr {{.*}}) + // OGCG: store i64 %[[RES]], ptr %[[VAL]], align 4 B val = co_await B{}; } // CIR-LABEL: cir.func coroutine {{.*}} @_Z29aggregate_coawait_expr_unusedv +// OGCG-LABEL: define dso_local void @_Z29aggregate_coawait_expr_unusedv coro_t aggregate_coawait_expr_unused() { // CIR: cir.await(user, ready : { // CIR: }, suspend : { @@ -43,10 +51,14 @@ coro_t aggregate_coawait_expr_unused() { // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}}) // CIR: cir.yield // CIR: },) + // OGCG: await.ready: + // OGCG: %[[RES:.*]] = call i64 @_ZN1B12await_resumeEv(ptr {{.*}}) + // OGCG: store i64 %[[RES]], ptr %{{.*}}, align 4 co_await B{}; } // CIR-LABEL: cir.func coroutine {{.*}} @_Z22aggregate_coyield_exprv +// OGCG-LABEL: define dso_local void @_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 : { @@ -56,10 +68,15 @@ coro_t aggregate_coyield_expr() { // CIR: cir.store align(4) %{{.*}}, %[[VAL]] : !rec_B, !cir.ptr<!rec_B> // CIR: cir.yield // CIR: },) + // OGCG: %[[VAL:.*]] = alloca %struct.B, align 4 + // OGCG: yield.ready: + // OGCG: %[[RES:.*]] = call i64 @_ZN1B12await_resumeEv(ptr {{.*}}) + // OGCG: store i64 %[[RES]], ptr %[[VAL]], align 4 B val = co_yield 42; } // CIR-LABEL: cir.func coroutine {{.*}} @_Z29aggregate_coyield_expr_unusedv +// OGCG-LABEL: define dso_local void @_Z29aggregate_coyield_expr_unusedv coro_t aggregate_coyield_expr_unused() { // CIR: cir.await(yield, ready : { // CIR: }, suspend : { @@ -67,6 +84,8 @@ coro_t aggregate_coyield_expr_unused() { // CIR: cir.call @_ZN1B12await_resumeEv(%{{.*}}) // CIR: cir.yield // CIR: },) + // OGCG: yield.ready: + // OGCG: %[[RES:.*]] = call i64 @_ZN1B12await_resumeEv(ptr {{.*}}) + // OGCG: store i64 %[[RES]], ptr %{{.*}}, align 4 co_yield 42; } - _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
