Skip to content

Commit 6312dd5

Browse files
committed
[Clang][LLVM][Coroutines] Prevent __coro_gro from outliving __promise
When dealing with short-circuiting coroutines (e.g. expected), the deferred calls that resolve the get_return_object are currently being emitted after we delete the coroutine frame. This was caught by ASAN when using optimizations -O1 and above: optimizations after inlining would place the __coro_gro in the heap, and subsequent delete of the coroframe followed by the conversion -> BOOM. This patch forbids the GRO to be placed in the coroutine frame, by adding a new metadata node that can be attached to `alloca` instructions. This fixes #49843.
1 parent 47669af commit 6312dd5

File tree

5 files changed

+31
-1
lines changed

5 files changed

+31
-1
lines changed

clang/lib/CodeGen/CGCoroutine.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -535,6 +535,11 @@ struct GetReturnObjectManager {
535535
Builder.CreateStore(Builder.getFalse(), GroActiveFlag);
536536

537537
GroEmission = CGF.EmitAutoVarAlloca(*GroVarDecl);
538+
auto *GroAlloca = dyn_cast_or_null<llvm::AllocaInst>(
539+
GroEmission.getOriginalAllocatedAddress().getPointer());
540+
assert(GroAlloca && "expected alloca to be emitted");
541+
GroAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
542+
llvm::MDNode::get(CGF.CGM.getLLVMContext(), {}));
538543

539544
// Remember the top of EHStack before emitting the cleanup.
540545
auto old_top = CGF.EHStack.stable_begin();

clang/test/CodeGenCoroutines/coro-gro.cpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,13 @@ void doSomething() noexcept;
3030
int f() {
3131
// CHECK: %[[RetVal:.+]] = alloca i32
3232
// CHECK: %[[GroActive:.+]] = alloca i1
33+
// CHECK: %[[CoroGro:.+]] = alloca %struct.GroType, {{.*}} !coro.outside.frame ![[OutFrameMetadata:.+]]
3334

3435
// CHECK: %[[Size:.+]] = call i64 @llvm.coro.size.i64()
3536
// CHECK: call noalias noundef nonnull ptr @_Znwm(i64 noundef %[[Size]])
3637
// CHECK: store i1 false, ptr %[[GroActive]]
3738
// CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_typeC1Ev(
38-
// CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_type17get_return_objectEv(
39+
// CHECK: call void @_ZNSt16coroutine_traitsIiJEE12promise_type17get_return_objectEv({{.*}} %[[CoroGro]]
3940
// CHECK: store i1 true, ptr %[[GroActive]]
4041

4142
Cleanup cleanup;
@@ -104,3 +105,5 @@ invoker g() {
104105
// CHECK: call void @_ZN7invoker15invoker_promise17get_return_objectEv({{.*}} %[[AggRes]]
105106
co_return;
106107
}
108+
109+
// CHECK: ![[OutFrameMetadata]] = !{}

llvm/docs/LangRef.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6691,6 +6691,22 @@ sections that the user does not want removed after linking.
66916691
...
66926692
!0 = !{}
66936693

6694+
'``coro.outside.frame``' Metadata
6695+
^^^^^^^^^^^^^^^^^^^^^^
6696+
6697+
``coro.outside.frame`` metadata may be attached to an alloca instruction to
6698+
to signify that it shouldn't be promoted to the coroutine frame, useful for
6699+
filtering allocas out by the frontend when emitting internal control mechanisms.
6700+
Additionally, this metadata is only used as a flag, so the associated
6701+
node must be empty.
6702+
6703+
.. code-block:: text
6704+
6705+
%__coro_gro = alloca %struct.GroType, align 1, !coro.outside.frame !0
6706+
6707+
...
6708+
!0 = !{}
6709+
66946710
'``unpredictable``' Metadata
66956711
^^^^^^^^^^^^^^^^^^^^^^^^^^^^
66966712

llvm/include/llvm/IR/FixedMetadataKinds.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,3 +50,4 @@ LLVM_FIXED_MD_KIND(MD_callsite, "callsite", 35)
5050
LLVM_FIXED_MD_KIND(MD_kcfi_type, "kcfi_type", 36)
5151
LLVM_FIXED_MD_KIND(MD_pcsections, "pcsections", 37)
5252
LLVM_FIXED_MD_KIND(MD_DIAssignID, "DIAssignID", 38)
53+
LLVM_FIXED_MD_KIND(MD_coro_outside_frame, "coro.outside.frame", 39)

llvm/lib/Transforms/Coroutines/CoroFrame.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2804,6 +2804,11 @@ static void collectFrameAlloca(AllocaInst *AI, coro::Shape &Shape,
28042804
if (AI == Shape.SwitchLowering.PromiseAlloca)
28052805
return;
28062806

2807+
// The __coro_gro alloca should outlive the promise, make sure we
2808+
// keep it outside the frame.
2809+
if (MDNode *MD = AI->getMetadata(LLVMContext::MD_coro_outside_frame))
2810+
return;
2811+
28072812
// The code that uses lifetime.start intrinsic does not work for functions
28082813
// with loops without exit. Disable it on ABIs we know to generate such
28092814
// code.

0 commit comments

Comments
 (0)