[coroutines] Implement correct GRO lifetime
Summary:
Sema creates a declaration for gro variable as:
auto $gro = $promise.get_return_object();
However, gro variable has to outlive coroutine frame and coroutine promise, but,
it can only be initialized after the coroutine promise was created, thus, we
split its emission in two parts: EmitGroAlloca emits an alloca and sets up
the cleanups. Later when the coroutine promise is available we initialize
the gro and set the flag that the cleanup is now active.
Duplicate of: https://reviews.llvm.org/D31670 (which arc patch refuses to apply for some reason)
Reviewers: GorNishanov, rsmith
Reviewed By: GorNishanov
Subscribers: EricWF, cfe-commits
Differential Revision: https://reviews.llvm.org/D33477
llvm-svn: 303716
diff --git a/clang/lib/CodeGen/CGCoroutine.cpp b/clang/lib/CodeGen/CGCoroutine.cpp
index 1316c76..9eb47b7 100644
--- a/clang/lib/CodeGen/CGCoroutine.cpp
+++ b/clang/lib/CodeGen/CGCoroutine.cpp
@@ -11,6 +11,7 @@
//
//===----------------------------------------------------------------------===//
+#include "CGCleanup.h"
#include "CodeGenFunction.h"
#include "llvm/ADT/ScopeExit.h"
#include "clang/AST/StmtCXX.h"
@@ -326,6 +327,72 @@
};
}
+namespace {
+struct GetReturnObjectManager {
+ CodeGenFunction &CGF;
+ CGBuilderTy &Builder;
+ const CoroutineBodyStmt &S;
+
+ Address GroActiveFlag;
+ CodeGenFunction::AutoVarEmission GroEmission;
+
+ GetReturnObjectManager(CodeGenFunction &CGF, const CoroutineBodyStmt &S)
+ : CGF(CGF), Builder(CGF.Builder), S(S), GroActiveFlag(Address::invalid()),
+ GroEmission(CodeGenFunction::AutoVarEmission::invalid()) {}
+
+ // The gro variable has to outlive coroutine frame and coroutine promise, but,
+ // it can only be initialized after coroutine promise was created, thus, we
+ // split its emission in two parts. EmitGroAlloca emits an alloca and sets up
+ // cleanups. Later when coroutine promise is available we initialize the gro
+ // and sets the flag that the cleanup is now active.
+
+ void EmitGroAlloca() {
+ auto *GroDeclStmt = dyn_cast<DeclStmt>(S.getResultDecl());
+ if (!GroDeclStmt) {
+ // If get_return_object returns void, no need to do an alloca.
+ return;
+ }
+
+ auto *GroVarDecl = cast<VarDecl>(GroDeclStmt->getSingleDecl());
+
+ // Set GRO flag that it is not initialized yet
+ GroActiveFlag =
+ CGF.CreateTempAlloca(Builder.getInt1Ty(), CharUnits::One(), "gro.active");
+ Builder.CreateStore(Builder.getFalse(), GroActiveFlag);
+
+ GroEmission = CGF.EmitAutoVarAlloca(*GroVarDecl);
+
+ // Remember the top of EHStack before emitting the cleanup.
+ auto old_top = CGF.EHStack.stable_begin();
+ CGF.EmitAutoVarCleanups(GroEmission);
+ auto top = CGF.EHStack.stable_begin();
+
+ // Make the cleanup conditional on gro.active
+ for (auto b = CGF.EHStack.find(top), e = CGF.EHStack.find(old_top);
+ b != e; b++) {
+ if (auto *Cleanup = dyn_cast<EHCleanupScope>(&*b)) {
+ assert(!Cleanup->hasActiveFlag() && "cleanup already has active flag?");
+ Cleanup->setActiveFlag(GroActiveFlag);
+ Cleanup->setTestFlagInEHCleanup();
+ Cleanup->setTestFlagInNormalCleanup();
+ }
+ }
+ }
+
+ void EmitGroInit() {
+ if (!GroActiveFlag.isValid()) {
+ // No Gro variable was allocated. Simply emit the call to
+ // get_return_object.
+ CGF.EmitStmt(S.getResultDecl());
+ return;
+ }
+
+ CGF.EmitAutoVarInit(GroEmission);
+ Builder.CreateStore(Builder.getTrue(), GroActiveFlag);
+ }
+};
+}
+
static void emitBodyAndFallthrough(CodeGenFunction &CGF,
const CoroutineBodyStmt &S, Stmt *Body) {
CGF.EmitStmt(Body);
@@ -390,14 +457,18 @@
CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi});
CurCoro.Data->CoroBegin = CoroBegin;
+ GetReturnObjectManager GroManager(*this, S);
+ GroManager.EmitGroAlloca();
+
CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB);
{
CodeGenFunction::RunCleanupsScope ResumeScope(*this);
EHStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, S.getDeallocate());
EmitStmt(S.getPromiseDeclStmt());
- EmitStmt(S.getResultDecl()); // FIXME: Gro lifetime is wrong.
+ // Now we have the promise, initialize the GRO
+ GroManager.EmitGroInit();
EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);