diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index bf896f1338ab4..61db0292efe0d 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -880,6 +880,10 @@ struct GetReturnObjectManager { Builder.CreateCondBr(InRamp, ConvBB, AfterConvBB); CGF.EmitBlock(ConvBB); + if (auto *AI = + dyn_cast(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::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 {}; +}