[clang] [CIR] Support CoawaitExpr in emitLValue (PR #232280)
via cfe-commits
cfe-commits at lists.llvm.org
Thu Oct 15 04:22:34 PDT 2026
llvmorg-github-actions[bot] wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-clang
Author: Aman Maurya (amanmaurya92)
<details>
<summary>Changes</summary>
In `CIRGenFunction::emitLValue`, `Expr::CoawaitExprClass` previously called `errorNYI`. When a coroutine awaits an expression whose `await_resume()` returns an lvalue reference (for example, `int &x = co_await awaiter;` or `co_await awaiter = 42;`), Clang evaluates the `CoawaitExpr` as an lvalue.
This patch:
- Adds `emitCoawaitLValue` in `CIRGenFunction` and routes `Expr::CoawaitExprClass` to it in `emitLValue`.
- In `emitSuspendExpression`, when `forLValue` is true, allocates a temporary alloca in the parent lexical scope to store the resume lvalue pointer before yielding from the `resume` region.
- In `emitCoawaitLValue`, reloads the pointer outside `cir.await` to preserve MLIR SSA dominance in the parent block.
- Adds FileCheck tests under `clang/test/CIR/CodeGen/coro-await.cpp`.
Fixes #<!-- -->232232.
---
Full diff: https://github.com/llvm/llvm-project/pull/232280.diff
4 Files Affected:
- (modified) clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp (+40-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.cpp (+1-2)
- (modified) clang/lib/CIR/CodeGen/CIRGenFunction.h (+1)
- (added) clang/test/CIR/CodeGen/coro-await.cpp (+101)
``````````diff
diff --git a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
index b0dbd11795439..a6ad3812a9916 100644
--- a/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenCoroutine.cpp
@@ -796,9 +796,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();
+ tmpResumeRValAddr = cgf.emitAlloca(
+ "__coawait_resume_lval", ptr.getType(), loc,
+ CharUnits::One(),
+ builder.getBestAllocaInsertPoint(scopeParentBlock));
+ builder.CIRBaseBuilderTy::createStore(loc, ptr, tmpResumeRValAddr);
} else {
awaitRes.rv =
cgf.emitAnyExpr(s.getResumeExpr(), aggSlot, ignoreResult);
@@ -883,6 +889,38 @@ RValue CIRGenFunction::emitCoawaitExpr(const CoawaitExpr &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::emitCoawaitLValue(const CoawaitExpr *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,
+ curCoro.data->currentAwaitKind,
+ 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;
+}
+
RValue CIRGenFunction::emitCoyieldExpr(const CoyieldExpr &e,
AggValueSlot aggSlot,
bool ignoreResult) {
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
index 14fc9cdff3ab6..1b67ea3981ca3 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.cpp
@@ -1353,8 +1353,7 @@ LValue CIRGenFunction::emitLValue(const Expr *e) {
case Expr::MaterializeTemporaryExprClass:
return emitMaterializeTemporaryExpr(cast<MaterializeTemporaryExpr>(e));
case Expr::CoawaitExprClass:
- getCIRGenModule().errorNYI(e->getSourceRange(), "emitLValue: CoawaitExpr");
- return LValue();
+ return emitCoawaitLValue(cast<CoawaitExpr>(e));
case Expr::CoyieldExprClass:
getCIRGenModule().errorNYI(e->getSourceRange(), "emitLValue: CoyieldExpr");
return LValue();
diff --git a/clang/lib/CIR/CodeGen/CIRGenFunction.h b/clang/lib/CIR/CodeGen/CIRGenFunction.h
index 8be794a660224..6293375e2e154 100644
--- a/clang/lib/CIR/CodeGen/CIRGenFunction.h
+++ b/clang/lib/CIR/CodeGen/CIRGenFunction.h
@@ -2303,6 +2303,7 @@ class CIRGenFunction : public CIRGenTypeCache {
RValue emitCoawaitExpr(const CoawaitExpr &e,
AggValueSlot aggSlot = AggValueSlot::ignored(),
bool ignoreResult = false);
+ LValue emitCoawaitLValue(const CoawaitExpr *e);
RValue emitCoyieldExpr(const CoyieldExpr &e,
AggValueSlot aggSlot = AggValueSlot::ignored(),
diff --git a/clang/test/CIR/CodeGen/coro-await.cpp b/clang/test/CIR/CodeGen/coro-await.cpp
new file mode 100644
index 0000000000000..029ea991e2735
--- /dev/null
+++ b/clang/test/CIR/CodeGen/coro-await.cpp
@@ -0,0 +1,101 @@
+// 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 Task {
+ struct promise_type {
+ Task get_return_object() { return {}; }
+ std::suspend_never initial_suspend() noexcept { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ void return_void() {}
+ void unhandled_exception() {}
+ };
+};
+
+struct Awaiter {
+ bool await_ready() { return false; }
+ void await_suspend(std::coroutine_handle<>) {}
+ int &await_resume() { return value; }
+ int value;
+};
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z14test_assign_lvv
+// OGCG-LABEL: define dso_local void @_Z14test_assign_lvv
+Task test_assign_lv() {
+ // CIR: %[[VAL:.*]] = cir.const #cir.int<42> : !s32i
+ // CIR: cir.await(user, ready : {
+ // CIR: %[[READY:.*]] = cir.call @_ZN7Awaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[READY]])
+ // CIR: }, suspend : {
+ // CIR: cir.call @_ZN7Awaiter13await_suspendESt16coroutine_handleIvE(%{{.*}})
+ // CIR: cir.coro.suspend_point
+ // CIR: }, resume : {
+ // CIR: %[[RES:.*]] = cir.call @_ZN7Awaiter12await_resumeEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!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 @_ZN7Awaiter12await_resumeEv(ptr {{.*}})
+ // OGCG: store i32 42, ptr %[[RES_OGCG]], align 4
+ Awaiter a;
+ co_await a = 42;
+}
+
+// CIR-LABEL: cir.func coroutine {{.*}} @_Z11test_ref_lvv
+// OGCG-LABEL: define dso_local void @_Z11test_ref_lvv
+Task test_ref_lv() {
+ // CIR: %[[R_ADDR:.*]] = cir.alloca "r" align(8) init const : !cir.ptr<!cir.ptr<!s32i>>
+ // CIR: cir.await(user, ready : {
+ // CIR: %[[READY:.*]] = cir.call @_ZN7Awaiter11await_readyEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!cir.bool{{.*}})
+ // CIR: cir.condition(%[[READY]])
+ // CIR: }, suspend : {
+ // CIR: cir.call @_ZN7Awaiter13await_suspendESt16coroutine_handleIvE(%{{.*}})
+ // CIR: cir.coro.suspend_point
+ // CIR: }, resume : {
+ // CIR: %[[RES:.*]] = cir.call @_ZN7Awaiter12await_resumeEv(%{{.*}}) : (!cir.ptr<!rec_Awaiter>{{.*}}) -> (!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: %[[RES_OGCG:.*]] = call noundef nonnull align 4 dereferenceable(4) ptr @_ZN7Awaiter12await_resumeEv(ptr {{.*}})
+ // OGCG: store ptr %[[RES_OGCG]], ptr %[[R_ADDR_OGCG]], align 8
+ Awaiter a;
+ int &r = co_await a;
+}
``````````
</details>
https://github.com/llvm/llvm-project/pull/232280
More information about the cfe-commits
mailing list