Author: Aman Maurya Date: 2026-10-21T01:34:45+05:30 New Revision: 9823e8670322ed3f081597beaaf9a87eae020bba
URL: https://github.com/llvm/llvm-project/commit/9823e8670322ed3f081597beaaf9a87eae020bba DIFF: https://github.com/llvm/llvm-project/commit/9823e8670322ed3f081597beaaf9a87eae020bba.diff LOG: [CIR] Support CoyieldExpr in emitLValue (#232298) In `CIRGenFunction::emitLValue`, `Expr::CoyieldExprClass` previously called `errorNYI`. When a coroutine yields an expression whose `yield_value()` returns an awaiter whose `await_resume()` returns an lvalue reference (for example, `int &r = co_yield 0;` or `(co_yield 0) = 42;`), Clang evaluates the `CoyieldExpr` as an lvalue. The resume pointer is spilled and reloaded across `cir.await` to maintain MLIR SSA dominance in the parent scope. Fixes #232233. Added: clang/test/CIR/CodeGenCoroutines/coro-yield.cpp Modified: clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp clang/lib/CIR/CodeGen/CIRGenFunction.cpp clang/lib/CIR/CodeGen/CIRGenFunction.h Removed: ################################################################################ diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp index 9d06f29b3ae5e..3ca0a29db565c 100644 --- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp @@ -818,9 +818,15 @@ emitSuspendExpression(CIRGenFunction &cgf, CGCoroData &coro, // await_resume(). awaitRes.rv = RValue::getIgnored(); } else if (forLValue) { - // FIXME(cir): the alloca for the resume expr should be placed in the - // enclosing cir.scope instead. awaitRes.lv = cgf.emitLValue(s.getResumeExpr()); + assert(awaitRes.lv.isSimple() && + "expected simple lvalue for coroutine resume"); + mlir::Value ptr = awaitRes.lv.getPointer(); + llvm::StringRef tmpName = "__coyield_resume_lval"; + tmpResumeRValAddr = cgf.emitAlloca( + tmpName, ptr.getType(), loc, cgf.getPointerAlign(), + builder.getBestAllocaInsertPoint(scopeParentBlock)); + builder.CIRBaseBuilderTy::createStore(loc, ptr, tmpResumeRValAddr); } else { awaitRes.rv = cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult); @@ -912,6 +918,39 @@ RValue CIRGenFunction::emitCoyieldExpr(const CoyieldExpr &e, ignoreResult); } +#ifndef NDEBUG +static QualType +getCoroutineSuspendExprReturnType(const ASTContext &ctx, + const CoroutineSuspendExpr *e) { + const auto *re = e->getResumeExpr(); + assert(isa<CallExpr>(re) && "unexpected suspend expression type"); + return cast<CallExpr>(re)->getCallReturnType(ctx); +} +#endif + +LValue CIRGenFunction::emitCoyieldLValue(const CoyieldExpr *e) { + assert( + getCoroutineSuspendExprReturnType(getContext(), e)->isReferenceType() && + "Can't have a scalar return unless the return type is a " + "reference type!"); + mlir::Location scopeLoc = getLoc(e->getSourceRange()); + mlir::Block *currEntryBlock = curLexScope->getEntryBlock(); + mlir::Value tmpResumeLValAddr; + LValue lv = + emitSuspendExpression(*this, *curCoro.data, *e, cir::AwaitKind::Yield, + AggValueSlot::ignored(), + /*ignoreResult=*/false, currEntryBlock, + tmpResumeLValAddr, /*forLValue=*/true) + .lv; + if (tmpResumeLValAddr) { + Address addr = lv.getAddress(); + mlir::Value loadedPtr = cir::LoadOp::create( + getBuilder(), scopeLoc, addr.getPointer().getType(), tmpResumeLValAddr); + lv.setAddress(addr.withPointer(loadedPtr)); + } + return lv; +} + mlir::LogicalResult CIRGenFunction::emitCoreturnStmt(CoreturnStmt const &s) { ++curCoro.data->coreturnCount; diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp index 1fa57c4087da2..e5f20f7dec01d 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp @@ -1376,8 +1376,7 @@ LValue CIRGenFunction::emitLValue(const Expr *e) { getCIRGenModule().errorNYI(e->getSourceRange(), "emitLValue: CoawaitExpr"); return LValue(); case Expr::CoyieldExprClass: - getCIRGenModule().errorNYI(e->getSourceRange(), "emitLValue: CoyieldExpr"); - return LValue(); + return emitCoyieldLValue(cast<CoyieldExpr>(e)); case Expr::PackIndexingExprClass: return emitLValue(cast<PackIndexingExpr>(e)->getSelectedExpr()); case Expr::HLSLOutArgExprClass: diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h index 4395e8a6a1fcd..a143483d49391 100644 --- a/clang/lib/CIR/CodeGen/CIRGenFunction.h +++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h @@ -2320,6 +2320,7 @@ class CIRGenFunction : public CIRGenTypeCache { RValue emitCoyieldExpr(const CoyieldExpr &e, AggValueSlot aggSlot = AggValueSlot::ignored(), bool ignoreResult = false); + LValue emitCoyieldLValue(const CoyieldExpr *e); /// Emit the computation of the specified expression of complex type, /// returning the result. mlir::Value emitComplexExpr(const Expr *e); diff --git a/clang/test/CIR/CodeGenCoroutines/coro-yield.cpp b/clang/test/CIR/CodeGenCoroutines/coro-yield.cpp new file mode 100644 index 0000000000000..ef14f998f2240 --- /dev/null +++ b/clang/test/CIR/CodeGenCoroutines/coro-yield.cpp @@ -0,0 +1,71 @@ +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -fclangir -Wno-coroutine-missing-unhandled-exception -emit-cir %s -o %t.cir +// RUN: FileCheck --input-file=%t.cir %s -check-prefix=CIR +// RUN: %clang_cc1 -std=c++20 -triple x86_64-unknown-linux-gnu -emit-llvm -disable-llvm-passes -Wno-coroutine-missing-unhandled-exception %s -o %t.ll +// RUN: FileCheck --input-file=%t.ll %s -check-prefix=OGCG + +#include "Inputs/coroutine.h" + +struct YieldAwaiter { + bool await_ready() { return false; } + void await_suspend(std::coroutine_handle<>) {} + int &await_resume() { return value; } + int value; +}; + +struct Task { + struct promise_type { + Task get_return_object() { return {}; } + std::suspend_never initial_suspend() noexcept { return {}; } + std::suspend_never final_suspend() noexcept { return {}; } + YieldAwaiter yield_value(int) { return {}; } + void return_void() {} + void unhandled_exception() {} + }; +}; + +// CIR-LABEL: cir.func coroutine {{.*}} @_Z17test_yield_assignv +// OGCG-LABEL: define dso_local void @_Z17test_yield_assignv +Task test_yield_assign() { + // CIR: %[[VAL:.*]] = cir.const #cir.int<42> : !s32i + // CIR: cir.await(yield, ready : { + // CIR: %[[READY:.*]] = cir.call @_ZN12YieldAwaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_YieldAwaiter>{{.*}}) -> (!cir.bool{{.*}}) + // CIR: cir.condition(%[[READY]]) + // CIR: }, suspend : { + // CIR: cir.call @_ZN12YieldAwaiter13await_suspendESt16coroutine_handleIvE(%{{.*}}) + // CIR: cir.coro.suspend_point + // CIR: }, resume : { + // CIR: %[[RES:.*]] = cir.call @_ZN12YieldAwaiter12await_resumeEv(%{{.*}}) : (!cir.ptr<!rec_YieldAwaiter>{{.*}}) -> (!cir.ptr<!s32i>{{.*}}) + // CIR: cir.store %[[RES]], %[[TMP_ADDR:.*]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + // CIR: cir.yield + // CIR: },) + // CIR: %[[LOADED_PTR:.*]] = cir.load %[[TMP_ADDR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> + // CIR: cir.store align(4) %[[VAL]], %[[LOADED_PTR]] : !s32i, !cir.ptr<!s32i> + + // OGCG: %[[RES_OGCG:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN12YieldAwaiter12await_resumeEv(ptr {{.*}}) + // OGCG: store i32 42, ptr %[[RES_OGCG]], align 4 + (co_yield 0) = 42; +} + +// CIR-LABEL: cir.func coroutine {{.*}} @_Z14test_yield_refv +// OGCG-LABEL: define dso_local void @_Z14test_yield_refv +Task test_yield_ref() { + // CIR: %[[R_ADDR:.*]] = cir.alloca "r" align(8) init const : !cir.ptr<!cir.ptr<!s32i>> + // CIR: cir.await(yield, ready : { + // CIR: %[[READY:.*]] = cir.call @_ZN12YieldAwaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_YieldAwaiter>{{.*}}) -> (!cir.bool{{.*}}) + // CIR: cir.condition(%[[READY]]) + // CIR: }, suspend : { + // CIR: cir.call @_ZN12YieldAwaiter13await_suspendESt16coroutine_handleIvE(%{{.*}}) + // CIR: cir.coro.suspend_point + // CIR: }, resume : { + // CIR: %[[RES:.*]] = cir.call @_ZN12YieldAwaiter12await_resumeEv(%{{.*}}) : (!cir.ptr<!rec_YieldAwaiter>{{.*}}) -> (!cir.ptr<!s32i>{{.*}}) + // CIR: cir.store %[[RES]], %[[TMP_ADDR:.*]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + // CIR: cir.yield + // CIR: },) + // CIR: %[[LOADED_PTR:.*]] = cir.load %[[TMP_ADDR]] : !cir.ptr<!cir.ptr<!s32i>>, !cir.ptr<!s32i> + // CIR: cir.store align(8) %[[LOADED_PTR]], %[[R_ADDR]] : !cir.ptr<!s32i>, !cir.ptr<!cir.ptr<!s32i>> + + // OGCG: %[[R_ADDR_OGCG:.*]] = alloca ptr, align 8 + // OGCG: call noundef nonnull align 4 dereferenceable(4) ptr @_ZN12YieldAwaiter12await_resumeEv(ptr {{.*}}) + // OGCG: store ptr %{{.*}}, ptr %[[R_ADDR_OGCG]], align 8 + int &r = co_yield 0; +} _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
