https://github.com/NewSigma updated https://github.com/llvm/llvm-project/pull/213580
>From 9e17c33fee5324d4b01f1b3bcd62ea0ef105ed8e Mon Sep 17 00:00:00 2001 From: NewSigma <[email protected]> Date: Thu, 30 Jul 2026 16:22:44 +0800 Subject: [PATCH 1/2] [Coroutines] Never collect return value alloca into coroutine frame --- clang/lib/CodeGen/CGCoroutine.cpp | 4 +++ clang/test/CodeGenCoroutines/coro-gro.cpp | 1 + clang/test/CodeGenCoroutines/coro-gro5.cpp | 35 +++++++++++++++++++ llvm/lib/Transforms/Coroutines/SpillUtils.cpp | 5 --- .../Coroutines/coro-lifetime-end.ll | 4 +-- 5 files changed, 42 insertions(+), 7 deletions(-) create mode 100644 clang/test/CodeGenCoroutines/coro-gro5.cpp diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index bf896f1338ab4..e64778b36590c 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); + // Emit lifetime.start after all suspending points, ensuring the return + // alloca does not go into the coroutine frame. + if (auto *AI = dyn_cast<llvm::AllocaInst>(CGF.ReturnValue.getBasePointer())) + CGF.EmitLifetimeStart(AI); 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..17065bb5ae729 100644 --- a/clang/test/CodeGenCoroutines/coro-gro.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro.cpp @@ -61,6 +61,7 @@ int f() { // CHECK-NEXT: br i1 %InRamp, label %[[GroConv:.+]], label %[[AfterGroConv:.+]] // CHECK: [[GroConv]]: + // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[RetVal]]) // CHECK-NEXT: %[[Conv:.+]] = call noundef i32 @_ZN7GroTypecviEv( // CHECK-NEXT: store i32 %[[Conv]], ptr %[[RetVal]] // CHECK-NEXT: %[[IsActive:.+]] = load i1, ptr %[[GroActive]] diff --git a/clang/test/CodeGenCoroutines/coro-gro5.cpp b/clang/test/CodeGenCoroutines/coro-gro5.cpp new file mode 100644 index 0000000000000..a546d1e5aeade --- /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 + + // CHECK: gro.conv: + // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[Retval]]) + // 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 {}; +} diff --git a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp index 05abccf0f9a97..8396430f5b4f0 100644 --- a/llvm/lib/Transforms/Coroutines/SpillUtils.cpp +++ b/llvm/lib/Transforms/Coroutines/SpillUtils.cpp @@ -340,11 +340,6 @@ struct AllocaUseVisitor : PtrUseVisitor<AllocaUseVisitor> { // every basic block that uses the pointer to see if they cross suspension // points. The uses cover both direct uses as well as indirect uses. if (ShouldUseLifetimeStartInfo && !LifetimeStarts.empty()) { - // If there is no explicit lifetime.end, then assume the address can - // cross suspension points. - if (LifetimeEndBBs.empty()) - return true; - // If there is a path from a lifetime.start to a suspend without a // corresponding lifetime.end, then the alloca's lifetime persists // beyond that suspension point and the alloca must go on the frame. diff --git a/llvm/test/Transforms/Coroutines/coro-lifetime-end.ll b/llvm/test/Transforms/Coroutines/coro-lifetime-end.ll index ea044daa75533..9a9b40cc05a77 100644 --- a/llvm/test/Transforms/Coroutines/coro-lifetime-end.ll +++ b/llvm/test/Transforms/Coroutines/coro-lifetime-end.ll @@ -8,8 +8,8 @@ declare void @consume.i8.array(ptr) @testbool = external local_unnamed_addr global i8, align 1 -; testval does not contain an explicit lifetime end. We must assume that it may -; live across suspension. +; testval does not contain an explicit lifetime.end between lifetime.start and coro.suspend +; We must assume that it may live across suspension. define void @HasNoLifetimeEnd() presplitcoroutine { ; CHECK-LABEL: define void @HasNoLifetimeEnd() { ; CHECK-NEXT: entry: >From c37792c6989b31d43f3d6b56cee56280d2797b27 Mon Sep 17 00:00:00 2001 From: NewSigma <[email protected]> Date: Tue, 4 Aug 2026 11:33:16 +0800 Subject: [PATCH 2/2] Add coro.outside.frame --- clang/lib/CodeGen/CGCoroutine.cpp | 10 +++++++--- clang/test/CodeGenCoroutines/coro-gro.cpp | 4 ++-- clang/test/CodeGenCoroutines/coro-gro5.cpp | 2 +- 3 files changed, 10 insertions(+), 6 deletions(-) diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp index e64778b36590c..6e3249af1ca2f 100644 --- a/clang/lib/CodeGen/CGCoroutine.cpp +++ b/clang/lib/CodeGen/CGCoroutine.cpp @@ -880,10 +880,14 @@ struct GetReturnObjectManager { Builder.CreateCondBr(InRamp, ConvBB, AfterConvBB); CGF.EmitBlock(ConvBB); - // Emit lifetime.start after all suspending points, ensuring the return - // alloca does not go into the coroutine frame. - if (auto *AI = dyn_cast<llvm::AllocaInst>(CGF.ReturnValue.getBasePointer())) + if (auto *AI = + dyn_cast<llvm::AllocaInst>(CGF.ReturnValue.getBasePointer())) { + AI->setMetadata(llvm::LLVMContext::MD_coro_outside_frame, + llvm::MDNode::get(CGF.getLLVMContext(), {})); + // Emit lifetime.start after all suspend points to doubly ensure the + // return alloca stays out of the coroutine frame. CGF.EmitLifetimeStart(AI); + } 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 17065bb5ae729..f171cece98e11 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 index a546d1e5aeade..b48f2747ff726 100644 --- a/clang/test/CodeGenCoroutines/coro-gro5.cpp +++ b/clang/test/CodeGenCoroutines/coro-gro5.cpp @@ -22,7 +22,7 @@ struct expected { // CHECK-LABEL: define {{.*}} i8 @_Z2f1v() expected f1() { - // CHECK: %[[Retval:.+]] = alloca %struct.expected, align 1 + // CHECK: %[[Retval:.+]] = alloca %struct.expected, align 1, !coro.outside.frame // CHECK: gro.conv: // CHECK-NEXT: call void @llvm.lifetime.start.p0(ptr %[[Retval]]) _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
