llvm-project/clang/lib/CodeGen/CGCoroutine.cpp
Gor Nishanov 4c2f68fd7c [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
2017-05-24 02:38:26 +00:00

582 lines
22 KiB
C++

//===----- CGCoroutine.cpp - Emit LLVM Code for C++ coroutines ------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This contains code dealing with C++ code generation of coroutines.
//
//===----------------------------------------------------------------------===//
#include "CGCleanup.h"
#include "CodeGenFunction.h"
#include "llvm/ADT/ScopeExit.h"
#include "clang/AST/StmtCXX.h"
using namespace clang;
using namespace CodeGen;
using llvm::Value;
using llvm::BasicBlock;
namespace {
enum class AwaitKind { Init, Normal, Yield, Final };
static constexpr llvm::StringLiteral AwaitKindStr[] = {"init", "await", "yield",
"final"};
}
struct clang::CodeGen::CGCoroData {
// What is the current await expression kind and how many
// await/yield expressions were encountered so far.
// These are used to generate pretty labels for await expressions in LLVM IR.
AwaitKind CurrentAwaitKind = AwaitKind::Init;
unsigned AwaitNum = 0;
unsigned YieldNum = 0;
// How many co_return statements are in the coroutine. Used to decide whether
// we need to add co_return; equivalent at the end of the user authored body.
unsigned CoreturnCount = 0;
// A branch to this block is emitted when coroutine needs to suspend.
llvm::BasicBlock *SuspendBB = nullptr;
// Stores the jump destination just before the coroutine memory is freed.
// This is the destination that every suspend point jumps to for the cleanup
// branch.
CodeGenFunction::JumpDest CleanupJD;
// Stores the jump destination just before the final suspend. The co_return
// statements jumps to this point after calling return_xxx promise member.
CodeGenFunction::JumpDest FinalJD;
// Stores the llvm.coro.id emitted in the function so that we can supply it
// as the first argument to coro.begin, coro.alloc and coro.free intrinsics.
// Note: llvm.coro.id returns a token that cannot be directly expressed in a
// builtin.
llvm::CallInst *CoroId = nullptr;
// Stores the llvm.coro.begin emitted in the function so that we can replace
// all coro.frame intrinsics with direct SSA value of coro.begin that returns
// the address of the coroutine frame of the current coroutine.
llvm::CallInst *CoroBegin = nullptr;
// Stores the last emitted coro.free for the deallocate expressions, we use it
// to wrap dealloc code with if(auto mem = coro.free) dealloc(mem).
llvm::CallInst *LastCoroFree = nullptr;
// If coro.id came from the builtin, remember the expression to give better
// diagnostic. If CoroIdExpr is nullptr, the coro.id was created by
// EmitCoroutineBody.
CallExpr const *CoroIdExpr = nullptr;
};
// Defining these here allows to keep CGCoroData private to this file.
clang::CodeGen::CodeGenFunction::CGCoroInfo::CGCoroInfo() {}
CodeGenFunction::CGCoroInfo::~CGCoroInfo() {}
static void createCoroData(CodeGenFunction &CGF,
CodeGenFunction::CGCoroInfo &CurCoro,
llvm::CallInst *CoroId,
CallExpr const *CoroIdExpr = nullptr) {
if (CurCoro.Data) {
if (CurCoro.Data->CoroIdExpr)
CGF.CGM.Error(CoroIdExpr->getLocStart(),
"only one __builtin_coro_id can be used in a function");
else if (CoroIdExpr)
CGF.CGM.Error(CoroIdExpr->getLocStart(),
"__builtin_coro_id shall not be used in a C++ coroutine");
else
llvm_unreachable("EmitCoroutineBodyStatement called twice?");
return;
}
CurCoro.Data = std::unique_ptr<CGCoroData>(new CGCoroData);
CurCoro.Data->CoroId = CoroId;
CurCoro.Data->CoroIdExpr = CoroIdExpr;
}
// Synthesize a pretty name for a suspend point.
static SmallString<32> buildSuspendPrefixStr(CGCoroData &Coro, AwaitKind Kind) {
unsigned No = 0;
switch (Kind) {
case AwaitKind::Init:
case AwaitKind::Final:
break;
case AwaitKind::Normal:
No = ++Coro.AwaitNum;
break;
case AwaitKind::Yield:
No = ++Coro.YieldNum;
break;
}
SmallString<32> Prefix(AwaitKindStr[static_cast<unsigned>(Kind)]);
if (No > 1) {
Twine(No).toVector(Prefix);
}
return Prefix;
}
// Emit suspend expression which roughly looks like:
//
// auto && x = CommonExpr();
// if (!x.await_ready()) {
// llvm_coro_save();
// x.await_suspend(...); (*)
// llvm_coro_suspend(); (**)
// }
// x.await_resume();
//
// where the result of the entire expression is the result of x.await_resume()
//
// (*) If x.await_suspend return type is bool, it allows to veto a suspend:
// if (x.await_suspend(...))
// llvm_coro_suspend();
//
// (**) llvm_coro_suspend() encodes three possible continuations as
// a switch instruction:
//
// %where-to = call i8 @llvm.coro.suspend(...)
// switch i8 %where-to, label %coro.ret [ ; jump to epilogue to suspend
// i8 0, label %yield.ready ; go here when resumed
// i8 1, label %yield.cleanup ; go here when destroyed
// ]
//
// See llvm's docs/Coroutines.rst for more details.
//
static RValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Coro,
CoroutineSuspendExpr const &S,
AwaitKind Kind, AggValueSlot aggSlot,
bool ignoreResult) {
auto *E = S.getCommonExpr();
// FIXME: rsmith 5/22/2017. Does it still make sense for us to have a
// UO_Coawait at all? As I recall, the only purpose it ever had was to
// represent a dependent co_await expression that couldn't yet be resolved to
// a CoawaitExpr. But now we have (and need!) a separate DependentCoawaitExpr
// node to store unqualified lookup results, it seems that the UnaryOperator
// portion of the representation serves no purpose (and as seen in this patch,
// it's getting in the way). Can we remove it?
// Skip passthrough operator co_await (present when awaiting on an LValue).
if (auto *UO = dyn_cast<UnaryOperator>(E))
if (UO->getOpcode() == UO_Coawait)
E = UO->getSubExpr();
auto Binder =
CodeGenFunction::OpaqueValueMappingData::bind(CGF, S.getOpaqueValue(), E);
auto UnbindOnExit = llvm::make_scope_exit([&] { Binder.unbind(CGF); });
auto Prefix = buildSuspendPrefixStr(Coro, Kind);
BasicBlock *ReadyBlock = CGF.createBasicBlock(Prefix + Twine(".ready"));
BasicBlock *SuspendBlock = CGF.createBasicBlock(Prefix + Twine(".suspend"));
BasicBlock *CleanupBlock = CGF.createBasicBlock(Prefix + Twine(".cleanup"));
// If expression is ready, no need to suspend.
CGF.EmitBranchOnBoolExpr(S.getReadyExpr(), ReadyBlock, SuspendBlock, 0);
// Otherwise, emit suspend logic.
CGF.EmitBlock(SuspendBlock);
auto &Builder = CGF.Builder;
llvm::Function *CoroSave = CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_save);
auto *NullPtr = llvm::ConstantPointerNull::get(CGF.CGM.Int8PtrTy);
auto *SaveCall = Builder.CreateCall(CoroSave, {NullPtr});
auto *SuspendRet = CGF.EmitScalarExpr(S.getSuspendExpr());
if (SuspendRet != nullptr) {
// Veto suspension if requested by bool returning await_suspend.
assert(SuspendRet->getType()->isIntegerTy(1) &&
"Sema should have already checked that it is void or bool");
BasicBlock *RealSuspendBlock =
CGF.createBasicBlock(Prefix + Twine(".suspend.bool"));
CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock);
SuspendBlock = RealSuspendBlock;
CGF.EmitBlock(RealSuspendBlock);
}
// Emit the suspend point.
const bool IsFinalSuspend = (Kind == AwaitKind::Final);
llvm::Function *CoroSuspend =
CGF.CGM.getIntrinsic(llvm::Intrinsic::coro_suspend);
auto *SuspendResult = Builder.CreateCall(
CoroSuspend, {SaveCall, Builder.getInt1(IsFinalSuspend)});
// Create a switch capturing three possible continuations.
auto *Switch = Builder.CreateSwitch(SuspendResult, Coro.SuspendBB, 2);
Switch->addCase(Builder.getInt8(0), ReadyBlock);
Switch->addCase(Builder.getInt8(1), CleanupBlock);
// Emit cleanup for this suspend point.
CGF.EmitBlock(CleanupBlock);
CGF.EmitBranchThroughCleanup(Coro.CleanupJD);
// Emit await_resume expression.
CGF.EmitBlock(ReadyBlock);
return CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
}
RValue CodeGenFunction::EmitCoawaitExpr(const CoawaitExpr &E,
AggValueSlot aggSlot,
bool ignoreResult) {
return emitSuspendExpression(*this, *CurCoro.Data, E,
CurCoro.Data->CurrentAwaitKind, aggSlot,
ignoreResult);
}
RValue CodeGenFunction::EmitCoyieldExpr(const CoyieldExpr &E,
AggValueSlot aggSlot,
bool ignoreResult) {
return emitSuspendExpression(*this, *CurCoro.Data, E, AwaitKind::Yield,
aggSlot, ignoreResult);
}
void CodeGenFunction::EmitCoreturnStmt(CoreturnStmt const &S) {
++CurCoro.Data->CoreturnCount;
EmitStmt(S.getPromiseCall());
EmitBranchThroughCleanup(CurCoro.Data->FinalJD);
}
// For WinEH exception representation backend need to know what funclet coro.end
// belongs to. That information is passed in a funclet bundle.
static SmallVector<llvm::OperandBundleDef, 1>
getBundlesForCoroEnd(CodeGenFunction &CGF) {
SmallVector<llvm::OperandBundleDef, 1> BundleList;
if (llvm::Instruction *EHPad = CGF.CurrentFuncletPad)
BundleList.emplace_back("funclet", EHPad);
return BundleList;
}
namespace {
// We will insert coro.end to cut any of the destructors for objects that
// do not need to be destroyed once the coroutine is resumed.
// See llvm/docs/Coroutines.rst for more details about coro.end.
struct CallCoroEnd final : public EHScopeStack::Cleanup {
void Emit(CodeGenFunction &CGF, Flags flags) override {
auto &CGM = CGF.CGM;
auto *NullPtr = llvm::ConstantPointerNull::get(CGF.Int8PtrTy);
llvm::Function *CoroEndFn = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
// See if we have a funclet bundle to associate coro.end with. (WinEH)
auto Bundles = getBundlesForCoroEnd(CGF);
auto *CoroEnd = CGF.Builder.CreateCall(
CoroEndFn, {NullPtr, CGF.Builder.getTrue()}, Bundles);
if (Bundles.empty()) {
// Otherwise, (landingpad model), create a conditional branch that leads
// either to a cleanup block or a block with EH resume instruction.
auto *ResumeBB = CGF.getEHResumeBlock(/*cleanup=*/true);
auto *CleanupContBB = CGF.createBasicBlock("cleanup.cont");
CGF.Builder.CreateCondBr(CoroEnd, ResumeBB, CleanupContBB);
CGF.EmitBlock(CleanupContBB);
}
}
};
}
namespace {
// Make sure to call coro.delete on scope exit.
struct CallCoroDelete final : public EHScopeStack::Cleanup {
Stmt *Deallocate;
// Emit "if (coro.free(CoroId, CoroBegin)) Deallocate;"
// Note: That deallocation will be emitted twice: once for a normal exit and
// once for exceptional exit. This usage is safe because Deallocate does not
// contain any declarations. The SubStmtBuilder::makeNewAndDeleteExpr()
// builds a single call to a deallocation function which is safe to emit
// multiple times.
void Emit(CodeGenFunction &CGF, Flags) override {
// Remember the current point, as we are going to emit deallocation code
// first to get to coro.free instruction that is an argument to a delete
// call.
BasicBlock *SaveInsertBlock = CGF.Builder.GetInsertBlock();
auto *FreeBB = CGF.createBasicBlock("coro.free");
CGF.EmitBlock(FreeBB);
CGF.EmitStmt(Deallocate);
auto *AfterFreeBB = CGF.createBasicBlock("after.coro.free");
CGF.EmitBlock(AfterFreeBB);
// We should have captured coro.free from the emission of deallocate.
auto *CoroFree = CGF.CurCoro.Data->LastCoroFree;
if (!CoroFree) {
CGF.CGM.Error(Deallocate->getLocStart(),
"Deallocation expressoin does not refer to coro.free");
return;
}
// Get back to the block we were originally and move coro.free there.
auto *InsertPt = SaveInsertBlock->getTerminator();
CoroFree->moveBefore(InsertPt);
CGF.Builder.SetInsertPoint(InsertPt);
// Add if (auto *mem = coro.free) Deallocate;
auto *NullPtr = llvm::ConstantPointerNull::get(CGF.Int8PtrTy);
auto *Cond = CGF.Builder.CreateICmpNE(CoroFree, NullPtr);
CGF.Builder.CreateCondBr(Cond, FreeBB, AfterFreeBB);
// No longer need old terminator.
InsertPt->eraseFromParent();
CGF.Builder.SetInsertPoint(AfterFreeBB);
}
explicit CallCoroDelete(Stmt *DeallocStmt) : Deallocate(DeallocStmt) {}
};
}
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);
const bool CanFallthrough = CGF.Builder.GetInsertBlock();
if (CanFallthrough)
if (Stmt *OnFallthrough = S.getFallthroughHandler())
CGF.EmitStmt(OnFallthrough);
}
void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy());
auto &TI = CGM.getContext().getTargetInfo();
unsigned NewAlign = TI.getNewAlign() / TI.getCharWidth();
auto *EntryBB = Builder.GetInsertBlock();
auto *AllocBB = createBasicBlock("coro.alloc");
auto *InitBB = createBasicBlock("coro.init");
auto *FinalBB = createBasicBlock("coro.final");
auto *RetBB = createBasicBlock("coro.ret");
auto *CoroId = Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::coro_id),
{Builder.getInt32(NewAlign), NullPtr, NullPtr, NullPtr});
createCoroData(*this, CurCoro, CoroId);
CurCoro.Data->SuspendBB = RetBB;
// Backend is allowed to elide memory allocations, to help it, emit
// auto mem = coro.alloc() ? 0 : ... allocation code ...;
auto *CoroAlloc = Builder.CreateCall(
CGM.getIntrinsic(llvm::Intrinsic::coro_alloc), {CoroId});
Builder.CreateCondBr(CoroAlloc, AllocBB, InitBB);
EmitBlock(AllocBB);
auto *AllocateCall = EmitScalarExpr(S.getAllocate());
auto *AllocOrInvokeContBB = Builder.GetInsertBlock();
// Handle allocation failure if 'ReturnStmtOnAllocFailure' was provided.
if (auto *RetOnAllocFailure = S.getReturnStmtOnAllocFailure()) {
auto *RetOnFailureBB = createBasicBlock("coro.ret.on.failure");
// See if allocation was successful.
auto *NullPtr = llvm::ConstantPointerNull::get(Int8PtrTy);
auto *Cond = Builder.CreateICmpNE(AllocateCall, NullPtr);
Builder.CreateCondBr(Cond, InitBB, RetOnFailureBB);
// If not, return OnAllocFailure object.
EmitBlock(RetOnFailureBB);
EmitStmt(RetOnAllocFailure);
}
else {
Builder.CreateBr(InitBB);
}
EmitBlock(InitBB);
// Pass the result of the allocation to coro.begin.
auto *Phi = Builder.CreatePHI(VoidPtrTy, 2);
Phi->addIncoming(NullPtr, EntryBB);
Phi->addIncoming(AllocateCall, AllocOrInvokeContBB);
auto *CoroBegin = Builder.CreateCall(
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());
// Now we have the promise, initialize the GRO
GroManager.EmitGroInit();
EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
// FIXME: Emit param moves.
CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
EmitStmt(S.getInitSuspendStmt());
CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
if (auto *OnException = S.getExceptionHandler()) {
auto Loc = S.getLocStart();
CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr, OnException);
auto *TryStmt = CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
EnterCXXTryStmt(*TryStmt);
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
ExitCXXTryStmt(*TryStmt);
}
else {
emitBodyAndFallthrough(*this, S, S.getBody());
}
// See if we need to generate final suspend.
const bool CanFallthrough = Builder.GetInsertBlock();
const bool HasCoreturns = CurCoro.Data->CoreturnCount > 0;
if (CanFallthrough || HasCoreturns) {
EmitBlock(FinalBB);
CurCoro.Data->CurrentAwaitKind = AwaitKind::Final;
EmitStmt(S.getFinalSuspendStmt());
}
else {
// We don't need FinalBB. Emit it to make sure the block is deleted.
EmitBlock(FinalBB, /*IsFinished=*/true);
}
}
EmitBlock(RetBB);
// Emit coro.end before getReturnStmt (and parameter destructors), since
// resume and destroy parts of the coroutine should not include them.
llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
Builder.CreateCall(CoroEnd, {NullPtr, Builder.getFalse()});
if (Stmt *Ret = S.getReturnStmt())
EmitStmt(Ret);
}
// Emit coroutine intrinsic and patch up arguments of the token type.
RValue CodeGenFunction::EmitCoroutineIntrinsic(const CallExpr *E,
unsigned int IID) {
SmallVector<llvm::Value *, 8> Args;
switch (IID) {
default:
break;
// The coro.frame builtin is replaced with an SSA value of the coro.begin
// intrinsic.
case llvm::Intrinsic::coro_frame: {
if (CurCoro.Data && CurCoro.Data->CoroBegin) {
return RValue::get(CurCoro.Data->CoroBegin);
}
CGM.Error(E->getLocStart(), "this builtin expect that __builtin_coro_begin "
"has been used earlier in this function");
auto NullPtr = llvm::ConstantPointerNull::get(Builder.getInt8PtrTy());
return RValue::get(NullPtr);
}
// The following three intrinsics take a token parameter referring to a token
// returned by earlier call to @llvm.coro.id. Since we cannot represent it in
// builtins, we patch it up here.
case llvm::Intrinsic::coro_alloc:
case llvm::Intrinsic::coro_begin:
case llvm::Intrinsic::coro_free: {
if (CurCoro.Data && CurCoro.Data->CoroId) {
Args.push_back(CurCoro.Data->CoroId);
break;
}
CGM.Error(E->getLocStart(), "this builtin expect that __builtin_coro_id has"
" been used earlier in this function");
// Fallthrough to the next case to add TokenNone as the first argument.
}
// @llvm.coro.suspend takes a token parameter. Add token 'none' as the first
// argument.
case llvm::Intrinsic::coro_suspend:
Args.push_back(llvm::ConstantTokenNone::get(getLLVMContext()));
break;
}
for (auto &Arg : E->arguments())
Args.push_back(EmitScalarExpr(Arg));
llvm::Value *F = CGM.getIntrinsic(IID);
llvm::CallInst *Call = Builder.CreateCall(F, Args);
// Note: The following code is to enable to emit coro.id and coro.begin by
// hand to experiment with coroutines in C.
// If we see @llvm.coro.id remember it in the CoroData. We will update
// coro.alloc, coro.begin and coro.free intrinsics to refer to it.
if (IID == llvm::Intrinsic::coro_id) {
createCoroData(*this, CurCoro, Call, E);
}
else if (IID == llvm::Intrinsic::coro_begin) {
if (CurCoro.Data)
CurCoro.Data->CoroBegin = Call;
}
else if (IID == llvm::Intrinsic::coro_free) {
// Remember the last coro_free as we need it to build the conditional
// deletion of the coroutine frame.
if (CurCoro.Data)
CurCoro.Data->LastCoroFree = Call;
} return RValue::get(Call);
}