[clang] ab40e2b - [clang][CodeGen] Never collect return value alloca into coroutine frame (#213580)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Aug 9 20:59:45 PDT 2026
Author: Weibo He
Date: 2026-08-10T11:59:40+08:00
New Revision: ab40e2bc72b39ec1009962100089be67a66d09f2
URL: https://github.com/llvm/llvm-project/commit/ab40e2bc72b39ec1009962100089be67a66d09f2
DIFF: https://github.com/llvm/llvm-project/commit/ab40e2bc72b39ec1009962100089be67a66d09f2.diff
LOG: [clang][CodeGen] Never collect return value alloca into coroutine frame (#213580)
The coroutine return value must not reside within the coroutine frame;
otherwise, a heap-use-after-free error will occur, as the frame is
destroyed before the return is completed.
In the front end, emit `coro_outside_frame` metadata for the
return-value alloca so that it does not accidentally enter the frame.
Close #49843
Added:
clang/test/CodeGenCoroutines/coro-gro5.cpp
Modified:
clang/lib/CodeGen/CGCoroutine.cpp
clang/test/CodeGenCoroutines/coro-gro.cpp
Removed:
################################################################################
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index bf896f1338ab4..2f162519139e0 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -880,6 +880,9 @@ struct GetReturnObjectManager {
Builder.CreateCondBr(InRamp, ConvBB, AfterConvBB);
CGF.EmitBlock(ConvBB);
+ if (auto *AI = dyn_cast<llvm::AllocaInst>(CGF.ReturnValue.getBasePointer()))
+ AI->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
+ llvm::MDNode::get(CGF.getLLVMContext(), {}));
CGF.EmitAnyExprToMem(S.getReturnValue(), CGF.ReturnValue,
S.getReturnValue()->getType().getQualifiers(),
/*IsInit*/ true);
diff --git a/clang/test/CodeGenCoroutines/coro-gro.cpp b/clang/test/CodeGenCoroutines/coro-gro.cpp
index e9f79e10db7ae..c4c93a9457010 100644
--- a/clang/test/CodeGenCoroutines/coro-gro.cpp
+++ b/clang/test/CodeGenCoroutines/coro-gro.cpp
@@ -28,10 +28,10 @@ void doSomething() noexcept;
// CHECK: define{{.*}} i32 @_Z1fv(
int f() {
- // CHECK: %[[RetVal:.+]] = alloca i32
+ // CHECK: %[[RetVal:.+]] = alloca i32, align 4, !coro.outside.frame ![[OutFrameMetadata:.+]]
// CHECK-NEXT: %[[GroActive:.+]] = alloca i1
// CHECK-NEXT: %[[Promise:.+]] = alloca %"struct.std::coroutine_traits<int>::promise_type", align 1
- // CHECK-NEXT: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata:.+]]
+ // CHECK-NEXT: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata]]
// CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
// CHECK-NEXT: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]])
diff --git a/clang/test/CodeGenCoroutines/coro-gro5.cpp b/clang/test/CodeGenCoroutines/coro-gro5.cpp
new file mode 100644
index 0000000000000..7c275b826cbc3
--- /dev/null
+++ b/clang/test/CodeGenCoroutines/coro-gro5.cpp
@@ -0,0 +1,35 @@
+// Test that return value alloca does not enter the coro frame
+// Regression test for GH49843
+// RUN: %clang_cc1 -std=c++20 -triple=x86_64-unknown-linux-gnu -fexceptions -fcxx-exceptions -emit-llvm -o - %s | FileCheck %s
+
+#include "Inputs/coroutine.h"
+
+struct tag { char data[8]; }; // `tag` can be any type. It could be empty, or an int, or anything.
+
+struct expected {
+ char data; // No issues if this member isn't here.
+
+ expected(tag) : data() {}
+
+ struct promise_type {
+ tag get_return_object() { return {}; } // No issues if we return an `expected` instead.
+ std::suspend_never initial_suspend() { return {}; }
+ std::suspend_never final_suspend() noexcept { return {}; }
+ tag return_value(tag) { return tag{}; }
+ void unhandled_exception() {}
+ };
+};
+
+// CHECK-LABEL: define {{.*}} i8 @_Z2f1v()
+expected f1() {
+ // CHECK: %[[Retval:.+]] = alloca %struct.expected, align 1, !coro.outside.frame
+
+ // %Retval are captured
+ // CHECK: gro.conv:
+ // CHECK: invoke void @_ZN8expectedC1E3tag(ptr {{.*}} %[[Retval]], i64 {{.*}})
+
+ // CHECK: %[[GEP:.+]] = getelementptr {{.*}} %struct.expected, ptr %[[Retval]], i32 0, i32 0
+ // CHECK-NEXT: %[[Val:.+]] = load i8, ptr %[[GEP]], align 1
+ // CHECK-NEXT: ret i8 %[[Val]]
+ co_return {};
+}
More information about the cfe-commits
mailing list