https://github.com/amanmaurya92 created 
https://github.com/llvm/llvm-project/pull/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.

>From 8e7a8e834f92257428e49fd7abe000a344190194 Mon Sep 17 00:00:00 2001
From: amanmaurya92 <[email protected]>
Date: Thu, 15 Oct 2026 17:47:48 +0530
Subject: [PATCH] [CIR] Support CoyieldExpr in emitLValue (#232233)

Support evaluating CoyieldExpr as an lvalue when yield_value() returns an 
awaiter whose await_resume() returns an lvalue reference. The resume pointer is 
spilled and reloaded across cir.await to maintain MLIR SSA dominance in the 
parent scope.

Fixes #232233.
---
 clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp |  45 +++++++++-
 clang/lib/CIR/CodeGen/CIRGenFunction.cpp  |   3 +-
 clang/lib/CIR/CodeGen/CIRGenFunction.h    |   1 +
 clang/test/CIR/CodeGen/coro-yield.cpp     | 100 ++++++++++++++++++++++
 4 files changed, 145 insertions(+), 4 deletions(-)
 create mode 100644 clang/test/CIR/CodeGen/coro-yield.cpp

diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp 
b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index b0dbd11795439..6a3f41f4e55b5 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -796,9 +796,17 @@ 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 = kind == cir::AwaitKind::Yield
+                                        ? "__coyield_resume_lval"
+                                        : "__coawait_resume_lval";
+          tmpResumeRValAddr = cgf.emitAlloca(
+              tmpName, ptr.getType(), loc, CharUnits::One(),
+              builder.getBestAllocaInsertPoint(scopeParentBlock));
+          builder.CIRBaseBuilderTy::createStore(loc, ptr, tmpResumeRValAddr);
         } else {
           awaitRes.rv =
               cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
@@ -890,6 +898,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 14fc9cdff3ab6..0ea767263d29a 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1356,8 +1356,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 8be794a660224..6a00434f01659 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -2307,6 +2307,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/CodeGen/coro-yield.cpp 
b/clang/test/CIR/CodeGen/coro-yield.cpp
new file mode 100644
index 0000000000000..c2c8c6c7393c7
--- /dev/null
+++ b/clang/test/CIR/CodeGen/coro-yield.cpp
@@ -0,0 +1,100 @@
+// 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
+
+namespace std {
+template <class Ret, typename... T>
+struct coroutine_traits { using promise_type = typename Ret::promise_type; };
+
+template <class Promise = void> struct coroutine_handle {
+  coroutine_handle() = default;
+  static coroutine_handle from_address(void *) noexcept;
+  void *address() const noexcept;
+};
+
+template <> struct coroutine_handle<void> {
+  coroutine_handle() = default;
+  template <class PromiseType>
+  coroutine_handle(coroutine_handle<PromiseType>) noexcept;
+  static coroutine_handle from_address(void *) noexcept;
+  void *address() const noexcept;
+};
+
+struct suspend_always {
+  bool await_ready() noexcept { return false; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+
+struct suspend_never {
+  bool await_ready() noexcept { return true; }
+  void await_suspend(coroutine_handle<>) noexcept {}
+  void await_resume() noexcept {}
+};
+} // namespace std
+
+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

Reply via email to