Skip to content

Commit 033f1b3

Browse files
committed
[Clang][Coroutines] Only change cleanup order when GRO does emits an alloca
1 parent f9d54b8 commit 033f1b3

File tree

2 files changed

+43
-37
lines changed

2 files changed

+43
-37
lines changed

clang/lib/CodeGen/CGCoroutine.cpp

Lines changed: 41 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -512,21 +512,28 @@ struct GetReturnObjectManager {
512512
}();
513513
}
514514

515+
bool shouldEmitAlloca() {
516+
if (DirectEmit)
517+
return false;
518+
519+
if (!isa_and_nonnull<DeclStmt>(S.getResultDecl())) {
520+
// If get_return_object returns void, no need to do an alloca.
521+
return false;
522+
}
523+
524+
return true;
525+
}
526+
515527
// The gro variable has to outlive coroutine frame and coroutine promise, but,
516528
// it can only be initialized after coroutine promise was created, thus, we
517529
// split its emission in two parts. EmitGroAlloca emits an alloca and sets up
518530
// cleanups. Later when coroutine promise is available we initialize the gro
519531
// and sets the flag that the cleanup is now active.
520532
void EmitGroAlloca() {
521-
if (DirectEmit)
522-
return;
523-
524-
auto *GroDeclStmt = dyn_cast_or_null<DeclStmt>(S.getResultDecl());
525-
if (!GroDeclStmt) {
526-
// If get_return_object returns void, no need to do an alloca.
533+
if (!shouldEmitAlloca())
527534
return;
528-
}
529535

536+
auto *GroDeclStmt = cast<DeclStmt>(S.getResultDecl());
530537
auto *GroVarDecl = cast<VarDecl>(GroDeclStmt->getSingleDecl());
531538

532539
// Set GRO flag that it is not initialized yet
@@ -658,6 +665,25 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
658665

659666
GetReturnObjectManager GroManager(*this, S);
660667
CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB);
668+
bool shouldGROEmitAlloca = GroManager.shouldEmitAlloca();
669+
auto emitRetBlock = [&]() {
670+
EmitBlock(RetBB);
671+
// Emit coro.end before getReturnStmt (and parameter destructors), since
672+
// resume and destroy parts of the coroutine should not include them.
673+
llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
674+
Builder.CreateCall(CoroEnd,
675+
{NullPtr, Builder.getFalse(),
676+
llvm::ConstantTokenNone::get(CoroEnd->getContext())});
677+
678+
if (Stmt *Ret = S.getReturnStmt()) {
679+
// Since we already emitted the return value above, so we shouldn't
680+
// emit it again here.
681+
if (GroManager.DirectEmit)
682+
cast<ReturnStmt>(Ret)->setRetValue(nullptr);
683+
EmitStmt(Ret);
684+
}
685+
};
686+
661687
{
662688
CGDebugInfo *DI = getDebugInfo();
663689
ParamReferenceReplacerRAII ParamReplacer(LocalDeclMap);
@@ -757,23 +783,16 @@ void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
757783
EmitBlock(FinalBB, /*IsFinished=*/true);
758784
}
759785

760-
EmitBlock(RetBB);
761-
// Emit coro.end before getReturnStmt (and parameter destructors), since
762-
// resume and destroy parts of the coroutine should not include them.
763-
llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
764-
Builder.CreateCall(CoroEnd,
765-
{NullPtr, Builder.getFalse(),
766-
llvm::ConstantTokenNone::get(CoroEnd->getContext())});
767-
768-
if (Stmt *Ret = S.getReturnStmt()) {
769-
// Since we already emitted the return value above, so we shouldn't
770-
// emit it again here.
771-
if (GroManager.DirectEmit)
772-
cast<ReturnStmt>(Ret)->setRetValue(nullptr);
773-
EmitStmt(Ret);
774-
}
786+
// If an alloca is emitted for GRO handling, make sure the ret block
787+
// is taken into account within the appropriate cleanup.
788+
if (shouldGROEmitAlloca)
789+
emitRetBlock();
775790
}
776791

792+
// No GRO alloca's, no need for to account for extra cleanups.
793+
if (!shouldGROEmitAlloca)
794+
emitRetBlock();
795+
777796
// LLVM require the frontend to mark the coroutine.
778797
CurFn->setPresplitCoroutine();
779798
}

clang/test/CodeGenCoroutines/coro-dest-slot.cpp

Lines changed: 2 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -33,22 +33,9 @@ extern "C" coro f(int) { co_return; }
3333

3434
// CHECK: call void @_ZNSt13suspend_never12await_resumeEv(
3535
// CHECK: %[[CLEANUP_DEST1:.+]] = phi i32 [ 0, %[[FINAL_READY]] ], [ 2, %[[FINAL_CLEANUP]] ]
36-
// CHECK: switch i32 %[[CLEANUP_DEST1]], label %[[CLEANUP_BB_2:.+]] [
37-
// CHECK: i32 0, label %[[CLEANUP_CONT:.+]]
38-
// CHECK: ]
39-
40-
// CHECK: [[CLEANUP_CONT]]:
41-
// CHECK: br label %[[CORO_RET:.+]]
42-
43-
// CHECK: [[CORO_RET]]:
44-
// CHECK: call i1 @llvm.coro.end
45-
// CHECK: br label %cleanup19
46-
47-
// CHECK: [[CLEANUP_BB_2]]:
48-
// CHECK: %[[CLEANUP_DEST2:.+]] = phi i32 [ %[[CLEANUP_DEST0]], %{{.+}} ], [ 1, %[[CORO_RET]] ], [ %[[CLEANUP_DEST1]], %{{.+}} ]
49-
// CHECK: call void @llvm.lifetime.end.p0(i64 1, ptr %[[PROMISE]])
36+
// CHECK: %[[CLEANUP_DEST2:.+]] = phi i32 [ %[[CLEANUP_DEST0]], %{{.+}} ], [ %[[CLEANUP_DEST1]], %{{.+}} ], [ 0, %{{.+}} ]
5037
// CHECK: call ptr @llvm.coro.free(
5138
// CHECK: switch i32 %[[CLEANUP_DEST2]], label %{{.+}} [
39+
// CHECK-NEXT: i32 0
5240
// CHECK-NEXT: i32 2
53-
// CHECK-NEXT: i32 1
5441
// CHECK-NEXT: ]

0 commit comments

Comments
 (0)