2016-10-03 22:44:48 +00:00
|
|
|
|
//===----- CGCoroutine.cpp - Emit LLVM Code for C++ coroutines ------------===//
|
|
|
|
|
//
|
2019-01-19 08:50:56 +00:00
|
|
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
|
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
|
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
2016-10-03 22:44:48 +00:00
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
//
|
|
|
|
|
// This contains code dealing with C++ code generation of coroutines.
|
|
|
|
|
//
|
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
2017-05-24 02:38:26 +00:00
|
|
|
|
#include "CGCleanup.h"
|
2016-10-03 22:44:48 +00:00
|
|
|
|
#include "CodeGenFunction.h"
|
2017-03-26 02:18:05 +00:00
|
|
|
|
#include "llvm/ADT/ScopeExit.h"
|
2016-10-27 16:28:31 +00:00
|
|
|
|
#include "clang/AST/StmtCXX.h"
|
2017-05-24 20:09:14 +00:00
|
|
|
|
#include "clang/AST/StmtVisitor.h"
|
2016-10-03 22:44:48 +00:00
|
|
|
|
|
|
|
|
|
using namespace clang;
|
|
|
|
|
using namespace CodeGen;
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
using llvm::Value;
|
|
|
|
|
using llvm::BasicBlock;
|
2016-10-03 22:44:48 +00:00
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
namespace {
|
|
|
|
|
enum class AwaitKind { Init, Normal, Yield, Final };
|
|
|
|
|
static constexpr llvm::StringLiteral AwaitKindStr[] = {"init", "await", "yield",
|
|
|
|
|
"final"};
|
|
|
|
|
}
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
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;
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// 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.
|
2017-03-06 21:12:54 +00:00
|
|
|
|
unsigned CoreturnCount = 0;
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// A branch to this block is emitted when coroutine needs to suspend.
|
|
|
|
|
llvm::BasicBlock *SuspendBB = nullptr;
|
|
|
|
|
|
2018-05-04 14:02:37 +00:00
|
|
|
|
// The promise type's 'unhandled_exception' handler, if it defines one.
|
|
|
|
|
Stmt *ExceptionHandler = nullptr;
|
|
|
|
|
|
|
|
|
|
// A temporary i1 alloca that stores whether 'await_resume' threw an
|
|
|
|
|
// exception. If it did, 'true' is stored in this variable, and the coroutine
|
|
|
|
|
// body must be skipped. If the promise type does not define an exception
|
|
|
|
|
// handler, this is null.
|
|
|
|
|
llvm::Value *ResumeEHVar = nullptr;
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
2016-10-03 22:44:48 +00:00
|
|
|
|
// 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;
|
2017-03-26 02:18:05 +00:00
|
|
|
|
|
2017-05-23 03:46:59 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
2017-05-23 04:21:27 +00:00
|
|
|
|
// 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;
|
|
|
|
|
|
2016-10-03 22:44:48 +00:00
|
|
|
|
// 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;
|
|
|
|
|
};
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// Defining these here allows to keep CGCoroData private to this file.
|
2016-10-03 22:44:48 +00:00
|
|
|
|
clang::CodeGen::CodeGenFunction::CGCoroInfo::CGCoroInfo() {}
|
|
|
|
|
CodeGenFunction::CGCoroInfo::~CGCoroInfo() {}
|
|
|
|
|
|
2016-10-27 16:28:31 +00:00
|
|
|
|
static void createCoroData(CodeGenFunction &CGF,
|
2016-10-03 22:44:48 +00:00
|
|
|
|
CodeGenFunction::CGCoroInfo &CurCoro,
|
2016-10-27 16:28:31 +00:00
|
|
|
|
llvm::CallInst *CoroId,
|
|
|
|
|
CallExpr const *CoroIdExpr = nullptr) {
|
2016-10-03 22:44:48 +00:00
|
|
|
|
if (CurCoro.Data) {
|
|
|
|
|
if (CurCoro.Data->CoroIdExpr)
|
2018-08-09 21:08:08 +00:00
|
|
|
|
CGF.CGM.Error(CoroIdExpr->getBeginLoc(),
|
2016-10-03 22:44:48 +00:00
|
|
|
|
"only one __builtin_coro_id can be used in a function");
|
|
|
|
|
else if (CoroIdExpr)
|
2018-08-09 21:08:08 +00:00
|
|
|
|
CGF.CGM.Error(CoroIdExpr->getBeginLoc(),
|
2016-10-03 22:44:48 +00:00
|
|
|
|
"__builtin_coro_id shall not be used in a C++ coroutine");
|
|
|
|
|
else
|
|
|
|
|
llvm_unreachable("EmitCoroutineBodyStatement called twice?");
|
|
|
|
|
|
2016-10-27 16:28:31 +00:00
|
|
|
|
return;
|
2016-10-03 22:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
CurCoro.Data = std::unique_ptr<CGCoroData>(new CGCoroData);
|
|
|
|
|
CurCoro.Data->CoroId = CoroId;
|
|
|
|
|
CurCoro.Data->CoroIdExpr = CoroIdExpr;
|
2016-10-27 16:28:31 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
|
|
|
|
|
2023-11-28 19:04:29 -08:00
|
|
|
|
// Check if function can throw based on prototype noexcept, also works for
|
|
|
|
|
// destructors which are implicitly noexcept but can be marked noexcept(false).
|
|
|
|
|
static bool FunctionCanThrow(const FunctionDecl *D) {
|
|
|
|
|
const auto *Proto = D->getType()->getAs<FunctionProtoType>();
|
|
|
|
|
if (!Proto) {
|
|
|
|
|
// Function proto is not found, we conservatively assume throwing.
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
return !isNoexceptExceptionSpec(Proto->getExceptionSpecType()) ||
|
|
|
|
|
Proto->canThrow() != CT_Cannot;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static bool ResumeStmtCanThrow(const Stmt *S) {
|
|
|
|
|
if (const auto *CE = dyn_cast<CallExpr>(S)) {
|
|
|
|
|
const auto *Callee = CE->getDirectCallee();
|
|
|
|
|
if (!Callee)
|
|
|
|
|
// We don't have direct callee. Conservatively assume throwing.
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
if (FunctionCanThrow(Callee))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Fall through to visit the children.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (const auto *TE = dyn_cast<CXXBindTemporaryExpr>(S)) {
|
|
|
|
|
// Special handling of CXXBindTemporaryExpr here as calling of Dtor of the
|
|
|
|
|
// temporary is not part of `children()` as covered in the fall through.
|
|
|
|
|
// We need to mark entire statement as throwing if the destructor of the
|
|
|
|
|
// temporary throws.
|
|
|
|
|
const auto *Dtor = TE->getTemporary()->getDestructor();
|
|
|
|
|
if (FunctionCanThrow(Dtor))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
// Fall through to visit the children.
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const auto *child : S->children())
|
|
|
|
|
if (ResumeStmtCanThrow(child))
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
return false;
|
2018-06-23 18:57:26 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// 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.
|
|
|
|
|
//
|
2017-06-15 19:43:36 +00:00
|
|
|
|
namespace {
|
|
|
|
|
struct LValueOrRValue {
|
|
|
|
|
LValue LV;
|
|
|
|
|
RValue RV;
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
static LValueOrRValue emitSuspendExpression(CodeGenFunction &CGF, CGCoroData &Coro,
|
2017-03-26 02:18:05 +00:00
|
|
|
|
CoroutineSuspendExpr const &S,
|
|
|
|
|
AwaitKind Kind, AggValueSlot aggSlot,
|
2017-06-15 19:43:36 +00:00
|
|
|
|
bool ignoreResult, bool forLValue) {
|
2017-03-26 02:18:05 +00:00
|
|
|
|
auto *E = S.getCommonExpr();
|
2017-05-23 05:25:31 +00:00
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
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});
|
|
|
|
|
|
2023-02-23 14:53:49 -08:00
|
|
|
|
CGF.CurCoro.InSuspendBlock = true;
|
2017-03-26 02:18:05 +00:00
|
|
|
|
auto *SuspendRet = CGF.EmitScalarExpr(S.getSuspendExpr());
|
2023-02-23 14:53:49 -08:00
|
|
|
|
CGF.CurCoro.InSuspendBlock = false;
|
2023-08-28 13:35:39 +08:00
|
|
|
|
|
2017-08-25 04:46:54 +00:00
|
|
|
|
if (SuspendRet != nullptr && SuspendRet->getType()->isIntegerTy(1)) {
|
2017-03-26 02:18:05 +00:00
|
|
|
|
// Veto suspension if requested by bool returning await_suspend.
|
|
|
|
|
BasicBlock *RealSuspendBlock =
|
|
|
|
|
CGF.createBasicBlock(Prefix + Twine(".suspend.bool"));
|
|
|
|
|
CGF.Builder.CreateCondBr(SuspendRet, RealSuspendBlock, ReadyBlock);
|
|
|
|
|
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);
|
2018-06-23 18:57:26 +00:00
|
|
|
|
|
|
|
|
|
// Exception handling requires additional IR. If the 'await_resume' function
|
|
|
|
|
// is marked as 'noexcept', we avoid generating this additional IR.
|
2018-05-04 14:02:37 +00:00
|
|
|
|
CXXTryStmt *TryStmt = nullptr;
|
2018-06-23 18:57:26 +00:00
|
|
|
|
if (Coro.ExceptionHandler && Kind == AwaitKind::Init &&
|
2023-11-28 19:04:29 -08:00
|
|
|
|
ResumeStmtCanThrow(S.getResumeExpr())) {
|
2018-05-04 14:02:37 +00:00
|
|
|
|
Coro.ResumeEHVar =
|
|
|
|
|
CGF.CreateTempAlloca(Builder.getInt1Ty(), Prefix + Twine("resume.eh"));
|
|
|
|
|
Builder.CreateFlagStore(true, Coro.ResumeEHVar);
|
|
|
|
|
|
|
|
|
|
auto Loc = S.getResumeExpr()->getExprLoc();
|
|
|
|
|
auto *Catch = new (CGF.getContext())
|
|
|
|
|
CXXCatchStmt(Loc, /*exDecl=*/nullptr, Coro.ExceptionHandler);
|
[FPEnv] Allow CompoundStmt to keep FP options
This is a recommit of b822efc7404bf09ccfdc1ab7657475026966c3b2,
reverted in dc34d8df4c48b3a8f474360970cae8a58e6c84f0. The commit caused
fails because the test ast-print-fp-pragmas.c did not specify particular
target, and it failed on targets which do not support constrained
intrinsics. The original commit message is below.
AST does not have special nodes for pragmas. Instead a pragma modifies
some state variables of Sema, which in turn results in modified
attributes of AST nodes. This technique applies to floating point
operations as well. Every AST node that can depend on FP options keeps
current set of them.
This technique works well for options like exception behavior or fast
math options. They represent instructions to the compiler how to modify
code generation for the affected nodes. However treatment of FP control
modes has problems with this technique. Modifying FP control mode
(like rounding direction) usually requires operations on hardware, like
writing to control registers. It must be done prior to the first
operation that depends on the control mode. In particular, such
operations are required for implementation of `pragma STDC FENV_ROUND`,
compiler should set up necessary rounding direction at the beginning of
compound statement where the pragma occurs. As there is no representation
for pragmas in AST, the code generation becomes a complicated task in
this case.
To solve this issue FP options are kept inside CompoundStmt. Unlike to FP
options in expressions, these does not affect any operation on FP values,
but only inform the codegen about the FP options that act in the body of
the statement. As all pragmas that modify FP environment may occurs only
at the start of compound statement or at global level, such solution
works for all relevant pragmas. The options are kept as a difference
from the options in the enclosing compound statement or default options,
it helps codegen to set only changed control modes.
Differential Revision: https://reviews.llvm.org/D123952
2022-07-01 18:32:26 +07:00
|
|
|
|
auto *TryBody = CompoundStmt::Create(CGF.getContext(), S.getResumeExpr(),
|
|
|
|
|
FPOptionsOverride(), Loc, Loc);
|
2018-05-04 14:02:37 +00:00
|
|
|
|
TryStmt = CXXTryStmt::Create(CGF.getContext(), Loc, TryBody, Catch);
|
|
|
|
|
CGF.EnterCXXTryStmt(*TryStmt);
|
2023-11-21 21:21:27 -08:00
|
|
|
|
CGF.EmitStmt(TryBody);
|
|
|
|
|
// We don't use EmitCXXTryStmt here. We need to store to ResumeEHVar that
|
|
|
|
|
// doesn't exist in the body.
|
|
|
|
|
Builder.CreateFlagStore(false, Coro.ResumeEHVar);
|
|
|
|
|
CGF.ExitCXXTryStmt(*TryStmt);
|
|
|
|
|
LValueOrRValue Res;
|
|
|
|
|
// We are not supposed to obtain the value from init suspend await_resume().
|
|
|
|
|
Res.RV = RValue::getIgnored();
|
|
|
|
|
return Res;
|
2018-05-04 14:02:37 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-06-15 19:43:36 +00:00
|
|
|
|
LValueOrRValue Res;
|
|
|
|
|
if (forLValue)
|
|
|
|
|
Res.LV = CGF.EmitLValue(S.getResumeExpr());
|
|
|
|
|
else
|
|
|
|
|
Res.RV = CGF.EmitAnyExpr(S.getResumeExpr(), aggSlot, ignoreResult);
|
2018-05-04 14:02:37 +00:00
|
|
|
|
|
2017-06-15 19:43:36 +00:00
|
|
|
|
return Res;
|
2017-03-26 02:18:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
RValue CodeGenFunction::EmitCoawaitExpr(const CoawaitExpr &E,
|
|
|
|
|
AggValueSlot aggSlot,
|
|
|
|
|
bool ignoreResult) {
|
|
|
|
|
return emitSuspendExpression(*this, *CurCoro.Data, E,
|
|
|
|
|
CurCoro.Data->CurrentAwaitKind, aggSlot,
|
2017-06-15 19:43:36 +00:00
|
|
|
|
ignoreResult, /*forLValue*/false).RV;
|
2017-03-26 02:18:05 +00:00
|
|
|
|
}
|
|
|
|
|
RValue CodeGenFunction::EmitCoyieldExpr(const CoyieldExpr &E,
|
|
|
|
|
AggValueSlot aggSlot,
|
|
|
|
|
bool ignoreResult) {
|
|
|
|
|
return emitSuspendExpression(*this, *CurCoro.Data, E, AwaitKind::Yield,
|
2017-06-15 19:43:36 +00:00
|
|
|
|
aggSlot, ignoreResult, /*forLValue*/false).RV;
|
2017-03-26 02:18:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-06 21:12:54 +00:00
|
|
|
|
void CodeGenFunction::EmitCoreturnStmt(CoreturnStmt const &S) {
|
|
|
|
|
++CurCoro.Data->CoreturnCount;
|
2017-07-31 07:48:13 +00:00
|
|
|
|
const Expr *RV = S.getOperand();
|
2020-03-13 14:08:34 +08:00
|
|
|
|
if (RV && RV->getType()->isVoidType() && !isa<InitListExpr>(RV)) {
|
|
|
|
|
// Make sure to evaluate the non initlist expression of a co_return
|
|
|
|
|
// with a void expression for side effects.
|
2017-07-31 07:48:13 +00:00
|
|
|
|
RunCleanupsScope cleanupScope(*this);
|
|
|
|
|
EmitIgnoredExpr(RV);
|
|
|
|
|
}
|
2017-03-06 21:12:54 +00:00
|
|
|
|
EmitStmt(S.getPromiseCall());
|
|
|
|
|
EmitBranchThroughCleanup(CurCoro.Data->FinalJD);
|
|
|
|
|
}
|
|
|
|
|
|
2017-06-15 19:43:36 +00:00
|
|
|
|
|
|
|
|
|
#ifndef NDEBUG
|
|
|
|
|
static QualType getCoroutineSuspendExprReturnType(const ASTContext &Ctx,
|
|
|
|
|
const CoroutineSuspendExpr *E) {
|
|
|
|
|
const auto *RE = E->getResumeExpr();
|
|
|
|
|
// Is it possible for RE to be a CXXBindTemporaryExpr wrapping
|
|
|
|
|
// a MemberCallExpr?
|
|
|
|
|
assert(isa<CallExpr>(RE) && "unexpected suspend expression type");
|
|
|
|
|
return cast<CallExpr>(RE)->getCallReturnType(Ctx);
|
|
|
|
|
}
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
LValue
|
|
|
|
|
CodeGenFunction::EmitCoawaitLValue(const CoawaitExpr *E) {
|
|
|
|
|
assert(getCoroutineSuspendExprReturnType(getContext(), E)->isReferenceType() &&
|
|
|
|
|
"Can't have a scalar return unless the return type is a "
|
|
|
|
|
"reference type!");
|
|
|
|
|
return emitSuspendExpression(*this, *CurCoro.Data, *E,
|
|
|
|
|
CurCoro.Data->CurrentAwaitKind, AggValueSlot::ignored(),
|
|
|
|
|
/*ignoreResult*/false, /*forLValue*/true).LV;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
LValue
|
|
|
|
|
CodeGenFunction::EmitCoyieldLValue(const CoyieldExpr *E) {
|
|
|
|
|
assert(getCoroutineSuspendExprReturnType(getContext(), E)->isReferenceType() &&
|
|
|
|
|
"Can't have a scalar return unless the return type is a "
|
|
|
|
|
"reference type!");
|
|
|
|
|
return emitSuspendExpression(*this, *CurCoro.Data, *E,
|
|
|
|
|
AwaitKind::Yield, AggValueSlot::ignored(),
|
|
|
|
|
/*ignoreResult*/false, /*forLValue*/true).LV;
|
|
|
|
|
}
|
|
|
|
|
|
2017-05-24 20:09:14 +00:00
|
|
|
|
// Hunts for the parameter reference in the parameter copy/move declaration.
|
|
|
|
|
namespace {
|
|
|
|
|
struct GetParamRef : public StmtVisitor<GetParamRef> {
|
|
|
|
|
public:
|
|
|
|
|
DeclRefExpr *Expr = nullptr;
|
|
|
|
|
GetParamRef() {}
|
|
|
|
|
void VisitDeclRefExpr(DeclRefExpr *E) {
|
|
|
|
|
assert(Expr == nullptr && "multilple declref in param move");
|
|
|
|
|
Expr = E;
|
|
|
|
|
}
|
|
|
|
|
void VisitStmt(Stmt *S) {
|
|
|
|
|
for (auto *C : S->children()) {
|
|
|
|
|
if (C)
|
|
|
|
|
Visit(C);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This class replaces references to parameters to their copies by changing
|
|
|
|
|
// the addresses in CGF.LocalDeclMap and restoring back the original values in
|
|
|
|
|
// its destructor.
|
|
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
struct ParamReferenceReplacerRAII {
|
|
|
|
|
CodeGenFunction::DeclMapTy SavedLocals;
|
|
|
|
|
CodeGenFunction::DeclMapTy& LocalDeclMap;
|
|
|
|
|
|
|
|
|
|
ParamReferenceReplacerRAII(CodeGenFunction::DeclMapTy &LocalDeclMap)
|
|
|
|
|
: LocalDeclMap(LocalDeclMap) {}
|
|
|
|
|
|
|
|
|
|
void addCopy(DeclStmt const *PM) {
|
|
|
|
|
// Figure out what param it refers to.
|
|
|
|
|
|
|
|
|
|
assert(PM->isSingleDecl());
|
|
|
|
|
VarDecl const*VD = static_cast<VarDecl const*>(PM->getSingleDecl());
|
|
|
|
|
Expr const *InitExpr = VD->getInit();
|
|
|
|
|
GetParamRef Visitor;
|
|
|
|
|
Visitor.Visit(const_cast<Expr*>(InitExpr));
|
|
|
|
|
assert(Visitor.Expr);
|
2018-03-01 05:43:23 +00:00
|
|
|
|
DeclRefExpr *DREOrig = Visitor.Expr;
|
2017-05-24 20:09:14 +00:00
|
|
|
|
auto *PD = DREOrig->getDecl();
|
|
|
|
|
|
|
|
|
|
auto it = LocalDeclMap.find(PD);
|
|
|
|
|
assert(it != LocalDeclMap.end() && "parameter is not found");
|
|
|
|
|
SavedLocals.insert({ PD, it->second });
|
|
|
|
|
|
|
|
|
|
auto copyIt = LocalDeclMap.find(VD);
|
|
|
|
|
assert(copyIt != LocalDeclMap.end() && "parameter copy is not found");
|
|
|
|
|
it->second = copyIt->getSecond();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
~ParamReferenceReplacerRAII() {
|
|
|
|
|
for (auto&& SavedLocal : SavedLocals) {
|
|
|
|
|
LocalDeclMap.insert({SavedLocal.first, SavedLocal.second});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// For WinEH exception representation backend needs to know what funclet coro.end
|
2017-04-05 04:55:03 +00:00
|
|
|
|
// 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);
|
2023-09-15 09:54:38 -07:00
|
|
|
|
auto *CoroEnd =
|
|
|
|
|
CGF.Builder.CreateCall(CoroEndFn,
|
|
|
|
|
{NullPtr, CGF.Builder.getTrue(),
|
|
|
|
|
llvm::ConstantTokenNone::get(CoroEndFn->getContext())},
|
|
|
|
|
Bundles);
|
2017-04-05 04:55:03 +00:00
|
|
|
|
if (Bundles.empty()) {
|
|
|
|
|
// Otherwise, (landingpad model), create a conditional branch that leads
|
|
|
|
|
// either to a cleanup block or a block with EH resume instruction.
|
2019-07-16 04:46:31 +00:00
|
|
|
|
auto *ResumeBB = CGF.getEHResumeBlock(/*isCleanup=*/true);
|
2017-04-05 04:55:03 +00:00
|
|
|
|
auto *CleanupContBB = CGF.createBasicBlock("cleanup.cont");
|
|
|
|
|
CGF.Builder.CreateCondBr(CoroEnd, ResumeBB, CleanupContBB);
|
|
|
|
|
CGF.EmitBlock(CleanupContBB);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-01 00:22:47 +00:00
|
|
|
|
namespace {
|
|
|
|
|
// Make sure to call coro.delete on scope exit.
|
|
|
|
|
struct CallCoroDelete final : public EHScopeStack::Cleanup {
|
|
|
|
|
Stmt *Deallocate;
|
|
|
|
|
|
2017-05-23 04:21:27 +00:00
|
|
|
|
// 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.
|
2017-04-01 00:22:47 +00:00
|
|
|
|
void Emit(CodeGenFunction &CGF, Flags) override {
|
2017-05-23 04:21:27 +00:00
|
|
|
|
// 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);
|
2017-04-01 00:22:47 +00:00
|
|
|
|
CGF.EmitStmt(Deallocate);
|
2017-05-23 04:21:27 +00:00
|
|
|
|
|
|
|
|
|
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) {
|
2018-08-09 21:08:08 +00:00
|
|
|
|
CGF.CGM.Error(Deallocate->getBeginLoc(),
|
2017-05-23 04:21:27 +00:00
|
|
|
|
"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);
|
2017-04-01 00:22:47 +00:00
|
|
|
|
}
|
|
|
|
|
explicit CallCoroDelete(Stmt *DeallocStmt) : Deallocate(DeallocStmt) {}
|
|
|
|
|
};
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 12:51:34 -08:00
|
|
|
|
namespace {
|
|
|
|
|
struct GetReturnObjectManager {
|
|
|
|
|
CodeGenFunction &CGF;
|
|
|
|
|
CGBuilderTy &Builder;
|
|
|
|
|
const CoroutineBodyStmt &S;
|
2023-03-08 16:00:02 -08:00
|
|
|
|
// When true, performs RVO for the return object.
|
|
|
|
|
bool DirectEmit = false;
|
2023-03-07 12:51:34 -08:00
|
|
|
|
|
|
|
|
|
Address GroActiveFlag;
|
|
|
|
|
CodeGenFunction::AutoVarEmission GroEmission;
|
|
|
|
|
|
|
|
|
|
GetReturnObjectManager(CodeGenFunction &CGF, const CoroutineBodyStmt &S)
|
|
|
|
|
: CGF(CGF), Builder(CGF.Builder), S(S), GroActiveFlag(Address::invalid()),
|
2023-03-08 16:00:02 -08:00
|
|
|
|
GroEmission(CodeGenFunction::AutoVarEmission::invalid()) {
|
|
|
|
|
// The call to get_return_object is sequenced before the call to
|
|
|
|
|
// initial_suspend and is invoked at most once, but there are caveats
|
|
|
|
|
// regarding on whether the prvalue result object may be initialized
|
|
|
|
|
// directly/eager or delayed, depending on the types involved.
|
|
|
|
|
//
|
|
|
|
|
// More info at https://github.com/cplusplus/papers/issues/1414
|
|
|
|
|
//
|
|
|
|
|
// The general cases:
|
|
|
|
|
// 1. Same type of get_return_object and coroutine return type (direct
|
|
|
|
|
// emission):
|
|
|
|
|
// - Constructed in the return slot.
|
|
|
|
|
// 2. Different types (delayed emission):
|
|
|
|
|
// - Constructed temporary object prior to initial suspend initialized with
|
|
|
|
|
// a call to get_return_object()
|
|
|
|
|
// - When coroutine needs to to return to the caller and needs to construct
|
|
|
|
|
// return value for the coroutine it is initialized with expiring value of
|
|
|
|
|
// the temporary obtained above.
|
|
|
|
|
//
|
|
|
|
|
// Direct emission for void returning coroutines or GROs.
|
|
|
|
|
DirectEmit = [&]() {
|
|
|
|
|
auto *RVI = S.getReturnValueInit();
|
|
|
|
|
assert(RVI && "expected RVI");
|
|
|
|
|
auto GroType = RVI->getType();
|
|
|
|
|
return CGF.getContext().hasSameType(GroType, CGF.FnRetTy);
|
|
|
|
|
}();
|
|
|
|
|
}
|
2023-03-07 12:51:34 -08:00
|
|
|
|
|
|
|
|
|
// 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() {
|
2023-03-08 16:00:02 -08:00
|
|
|
|
if (DirectEmit)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
auto *GroDeclStmt = dyn_cast_or_null<DeclStmt>(S.getResultDecl());
|
2023-03-07 12:51:34 -08:00
|
|
|
|
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);
|
2023-09-22 02:52:05 -03:00
|
|
|
|
auto *GroAlloca = dyn_cast_or_null<llvm::AllocaInst>(
|
|
|
|
|
GroEmission.getOriginalAllocatedAddress().getPointer());
|
|
|
|
|
assert(GroAlloca && "expected alloca to be emitted");
|
|
|
|
|
GroAlloca->setMetadata(llvm::LLVMContext::MD_coro_outside_frame,
|
|
|
|
|
llvm::MDNode::get(CGF.CGM.getLLVMContext(), {}));
|
2023-03-07 12:51:34 -08:00
|
|
|
|
|
|
|
|
|
// 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() {
|
2023-03-08 16:00:02 -08:00
|
|
|
|
if (DirectEmit) {
|
|
|
|
|
// ReturnValue should be valid as long as the coroutine's return type
|
|
|
|
|
// is not void. The assertion could help us to reduce the check later.
|
|
|
|
|
assert(CGF.ReturnValue.isValid() == (bool)S.getReturnStmt());
|
|
|
|
|
// Now we have the promise, initialize the GRO.
|
|
|
|
|
// We need to emit `get_return_object` first. According to:
|
|
|
|
|
// [dcl.fct.def.coroutine]p7
|
|
|
|
|
// The call to get_return_object is sequenced before the call to
|
|
|
|
|
// initial_suspend and is invoked at most once.
|
|
|
|
|
//
|
|
|
|
|
// So we couldn't emit return value when we emit return statment,
|
|
|
|
|
// otherwise the call to get_return_object wouldn't be in front
|
|
|
|
|
// of initial_suspend.
|
|
|
|
|
if (CGF.ReturnValue.isValid()) {
|
|
|
|
|
CGF.EmitAnyExprToMem(S.getReturnValue(), CGF.ReturnValue,
|
|
|
|
|
S.getReturnValue()->getType().getQualifiers(),
|
|
|
|
|
/*IsInit*/ true);
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-07 12:51:34 -08:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
} // namespace
|
|
|
|
|
|
2017-05-22 22:33:17 +00:00
|
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
|
2016-10-27 16:28:31 +00:00
|
|
|
|
void CodeGenFunction::EmitCoroutineBody(const CoroutineBodyStmt &S) {
|
2023-08-09 00:52:17 +02:00
|
|
|
|
auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getPtrTy());
|
2016-10-27 16:28:31 +00:00
|
|
|
|
auto &TI = CGM.getContext().getTargetInfo();
|
|
|
|
|
unsigned NewAlign = TI.getNewAlign() / TI.getCharWidth();
|
|
|
|
|
|
2017-05-23 01:13:17 +00:00
|
|
|
|
auto *EntryBB = Builder.GetInsertBlock();
|
|
|
|
|
auto *AllocBB = createBasicBlock("coro.alloc");
|
|
|
|
|
auto *InitBB = createBasicBlock("coro.init");
|
2017-03-06 21:12:54 +00:00
|
|
|
|
auto *FinalBB = createBasicBlock("coro.final");
|
2017-03-26 02:18:05 +00:00
|
|
|
|
auto *RetBB = createBasicBlock("coro.ret");
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2016-10-27 16:28:31 +00:00
|
|
|
|
auto *CoroId = Builder.CreateCall(
|
|
|
|
|
CGM.getIntrinsic(llvm::Intrinsic::coro_id),
|
|
|
|
|
{Builder.getInt32(NewAlign), NullPtr, NullPtr, NullPtr});
|
|
|
|
|
createCoroData(*this, CurCoro, CoroId);
|
2017-03-26 02:18:05 +00:00
|
|
|
|
CurCoro.Data->SuspendBB = RetBB;
|
[Coroutine][Clang] Force emit lifetime intrinsics for Coroutines
tl;dr Correct implementation of Corouintes requires having lifetime intrinsics available.
Coroutine functions are functions that can be suspended and resumed latter. To do so, data that need to stay alive after suspension must be put on the heap (i.e. the coroutine frame).
The optimizer is responsible for analyzing each AllocaInst and figure out whether it should be put on the stack or the frame.
In most cases, for data that we are unable to accurately analyze lifetime, we can just conservatively put them on the heap.
Unfortunately, there exists a few cases where certain data MUST be put on the stack, not on the heap. Without lifetime intrinsics, we are unable to correctly analyze those data's lifetime.
To dig into more details, there exists cases where at certain code points, the current coroutine frame may have already been destroyed. Hence no frame access would be allowed beyond that point.
The following is a common code pattern called "Symmetric Transfer" in coroutine:
```
auto tmp = await_suspend();
__builtin_coro_resume(tmp.address());
return;
```
In the above code example, `await_suspend()` returns a new coroutine handle, which we will obtain the address and then resume that coroutine. This essentially "transfered" from the current coroutine to a different coroutine.
During the call to `await_suspend()`, the current coroutine may be destroyed, which should be fine because we are not accessing any data afterwards.
However when LLVM is emitting IR for the above code, it needs to emit an AllocaInst for `tmp`. It will then call the `address` function on tmp. `address` function is a member function of coroutine, and there is no way for the LLVM optimizer to know that it does not capture the `tmp` pointer. So when the optimizer looks at it, it has to conservatively assume that `tmp` may escape and hence put it on the heap. Furthermore, in some cases `address` call would be inlined, which will generate a bunch of store/load instructions that move the `tmp` pointer around. Those stores will also make the compiler to think that `tmp` might escape.
To summarize, it's really difficult for the mid-end to figure out that the `tmp` data is short-lived.
I made some attempt in D98638, but it appears to be way too complex and is basically doing the same thing as inserting lifetime intrinsics in coroutines.
Also, for reference, we already force emitting lifetime intrinsics in O0 for AlwaysInliner: https://github.com/llvm/llvm-project/blob/main/llvm/lib/Passes/PassBuilder.cpp#L1893
Differential Revision: https://reviews.llvm.org/D99227
2021-03-25 13:46:20 -07:00
|
|
|
|
assert(ShouldEmitLifetimeMarkers &&
|
|
|
|
|
"Must emit lifetime intrinsics for coroutines");
|
2016-10-27 16:28:31 +00:00
|
|
|
|
|
2017-05-23 01:13:17 +00:00
|
|
|
|
// 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);
|
2017-03-27 23:36:59 +00:00
|
|
|
|
auto *AllocateCall = EmitScalarExpr(S.getAllocate());
|
2017-05-23 01:13:17 +00:00
|
|
|
|
auto *AllocOrInvokeContBB = Builder.GetInsertBlock();
|
2017-03-27 23:36:59 +00:00
|
|
|
|
|
|
|
|
|
// 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);
|
2023-06-26 14:27:05 +08:00
|
|
|
|
// Expect the allocation to be successful.
|
|
|
|
|
emitCondLikelihoodViaExpectIntrinsic(Cond, Stmt::LH_Likely);
|
2017-03-27 23:36:59 +00:00
|
|
|
|
Builder.CreateCondBr(Cond, InitBB, RetOnFailureBB);
|
|
|
|
|
|
|
|
|
|
// If not, return OnAllocFailure object.
|
|
|
|
|
EmitBlock(RetOnFailureBB);
|
|
|
|
|
EmitStmt(RetOnAllocFailure);
|
|
|
|
|
}
|
2017-05-23 01:13:17 +00:00
|
|
|
|
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);
|
2017-05-23 03:46:59 +00:00
|
|
|
|
auto *CoroBegin = Builder.CreateCall(
|
2017-05-23 01:13:17 +00:00
|
|
|
|
CGM.getIntrinsic(llvm::Intrinsic::coro_begin), {CoroId, Phi});
|
2017-05-23 03:46:59 +00:00
|
|
|
|
CurCoro.Data->CoroBegin = CoroBegin;
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2023-03-07 12:51:34 -08:00
|
|
|
|
GetReturnObjectManager GroManager(*this, S);
|
|
|
|
|
GroManager.EmitGroAlloca();
|
|
|
|
|
|
2017-04-01 00:22:47 +00:00
|
|
|
|
CurCoro.Data->CleanupJD = getJumpDestInCurrentScope(RetBB);
|
|
|
|
|
{
|
[Clang][Coroutine][DebugInfo] In c++ coroutine, clang will emit different debug info variables for parameters and move-parameters.
The first one is the real parameters of the coroutine function, the
other one just for copying parameters to the coroutine frame.
Considering the following c++ code:
```
struct coro {
...
};
coro foo(struct test & t) {
...
co_await suspend_always();
...
co_await suspend_always();
...
co_await suspend_always();
}
int main(int argc, char *argv[]) {
auto c = foo(...);
c.handle.resume();
...
}
```
Function foo is the standard coroutine function, and it has only
one parameter named t (ignoring this at first),
when we use the llvm code to compile this function, we can get the
following ir:
```
!2921 = distinct !DISubprogram(name: "foo", linkageName:
"_ZN6Object3fooE4test", scope: !2211, file: !45, li\
ne: 48, type: !2329, scopeLine: 48, flags: DIFlagPrototyped |
DIFlagAllCallsDescribed, spFlags: DISPFlagDefi\
nition | DISPFlagOptimized, unit: !44, declaration: !2328,
retainedNodes: !2922)
!2924 = !DILocalVariable(name: "t", arg: 2, scope: !2921, file: !45,
line: 48, type: !838)
...
!2926 = !DILocalVariable(name: "t", scope: !2921, type: !838, flags:
DIFlagArtificial)
```
We can find there are two `the same` DIVariable named t in the same
dwarf scope for foo.resume.
And when we try to use llvm-dwarfdump to dump the dwarf info of this
elf, we get the following output:
```
0x00006684: DW_TAG_subprogram
DW_AT_low_pc (0x00000000004013a0)
DW_AT_high_pc (0x00000000004013a8)
DW_AT_frame_base (DW_OP_reg7 RSP)
DW_AT_object_pointer (0x0000669c)
DW_AT_GNU_all_call_sites (true)
DW_AT_specification (0x00005b5c "_ZN6Object3fooE4test")
0x000066a5: DW_TAG_formal_parameter
DW_AT_name ("t")
DW_AT_decl_file ("/disk1/yifeng.dongyifeng/my_code/llvm/build/bin/coro-debug-1.cpp")
DW_AT_decl_line (48)
DW_AT_type (0x00004146 "test")
0x000066ba: DW_TAG_variable
DW_AT_name ("t")
DW_AT_type (0x00004146 "test")
DW_AT_artificial (true)
```
The elf also has two 't' in the same scope.
But unluckily, it might let the debugger
confused. And failed to print parameters for O0 or above.
This patch will make coroutine parameters and move
parameters use the same DIVar and try to fix the problems
that I mentioned before.
Test Plan: check-clang
Reviewed By: aprantl, jmorse
Differential Revision: https://reviews.llvm.org/D97533
2021-04-12 10:59:22 +08:00
|
|
|
|
CGDebugInfo *DI = getDebugInfo();
|
2017-05-24 20:09:14 +00:00
|
|
|
|
ParamReferenceReplacerRAII ParamReplacer(LocalDeclMap);
|
2017-04-01 00:22:47 +00:00
|
|
|
|
CodeGenFunction::RunCleanupsScope ResumeScope(*this);
|
|
|
|
|
EHStack.pushCleanup<CallCoroDelete>(NormalAndEHCleanup, S.getDeallocate());
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
[Clang][Coroutine][DebugInfo] In c++ coroutine, clang will emit different debug info variables for parameters and move-parameters.
The first one is the real parameters of the coroutine function, the
other one just for copying parameters to the coroutine frame.
Considering the following c++ code:
```
struct coro {
...
};
coro foo(struct test & t) {
...
co_await suspend_always();
...
co_await suspend_always();
...
co_await suspend_always();
}
int main(int argc, char *argv[]) {
auto c = foo(...);
c.handle.resume();
...
}
```
Function foo is the standard coroutine function, and it has only
one parameter named t (ignoring this at first),
when we use the llvm code to compile this function, we can get the
following ir:
```
!2921 = distinct !DISubprogram(name: "foo", linkageName:
"_ZN6Object3fooE4test", scope: !2211, file: !45, li\
ne: 48, type: !2329, scopeLine: 48, flags: DIFlagPrototyped |
DIFlagAllCallsDescribed, spFlags: DISPFlagDefi\
nition | DISPFlagOptimized, unit: !44, declaration: !2328,
retainedNodes: !2922)
!2924 = !DILocalVariable(name: "t", arg: 2, scope: !2921, file: !45,
line: 48, type: !838)
...
!2926 = !DILocalVariable(name: "t", scope: !2921, type: !838, flags:
DIFlagArtificial)
```
We can find there are two `the same` DIVariable named t in the same
dwarf scope for foo.resume.
And when we try to use llvm-dwarfdump to dump the dwarf info of this
elf, we get the following output:
```
0x00006684: DW_TAG_subprogram
DW_AT_low_pc (0x00000000004013a0)
DW_AT_high_pc (0x00000000004013a8)
DW_AT_frame_base (DW_OP_reg7 RSP)
DW_AT_object_pointer (0x0000669c)
DW_AT_GNU_all_call_sites (true)
DW_AT_specification (0x00005b5c "_ZN6Object3fooE4test")
0x000066a5: DW_TAG_formal_parameter
DW_AT_name ("t")
DW_AT_decl_file ("/disk1/yifeng.dongyifeng/my_code/llvm/build/bin/coro-debug-1.cpp")
DW_AT_decl_line (48)
DW_AT_type (0x00004146 "test")
0x000066ba: DW_TAG_variable
DW_AT_name ("t")
DW_AT_type (0x00004146 "test")
DW_AT_artificial (true)
```
The elf also has two 't' in the same scope.
But unluckily, it might let the debugger
confused. And failed to print parameters for O0 or above.
This patch will make coroutine parameters and move
parameters use the same DIVar and try to fix the problems
that I mentioned before.
Test Plan: check-clang
Reviewed By: aprantl, jmorse
Differential Revision: https://reviews.llvm.org/D97533
2021-04-12 10:59:22 +08:00
|
|
|
|
// Create mapping between parameters and copy-params for coroutine function.
|
2022-09-07 10:35:54 +08:00
|
|
|
|
llvm::ArrayRef<const Stmt *> ParamMoves = S.getParamMoves();
|
[Clang][Coroutine][DebugInfo] In c++ coroutine, clang will emit different debug info variables for parameters and move-parameters.
The first one is the real parameters of the coroutine function, the
other one just for copying parameters to the coroutine frame.
Considering the following c++ code:
```
struct coro {
...
};
coro foo(struct test & t) {
...
co_await suspend_always();
...
co_await suspend_always();
...
co_await suspend_always();
}
int main(int argc, char *argv[]) {
auto c = foo(...);
c.handle.resume();
...
}
```
Function foo is the standard coroutine function, and it has only
one parameter named t (ignoring this at first),
when we use the llvm code to compile this function, we can get the
following ir:
```
!2921 = distinct !DISubprogram(name: "foo", linkageName:
"_ZN6Object3fooE4test", scope: !2211, file: !45, li\
ne: 48, type: !2329, scopeLine: 48, flags: DIFlagPrototyped |
DIFlagAllCallsDescribed, spFlags: DISPFlagDefi\
nition | DISPFlagOptimized, unit: !44, declaration: !2328,
retainedNodes: !2922)
!2924 = !DILocalVariable(name: "t", arg: 2, scope: !2921, file: !45,
line: 48, type: !838)
...
!2926 = !DILocalVariable(name: "t", scope: !2921, type: !838, flags:
DIFlagArtificial)
```
We can find there are two `the same` DIVariable named t in the same
dwarf scope for foo.resume.
And when we try to use llvm-dwarfdump to dump the dwarf info of this
elf, we get the following output:
```
0x00006684: DW_TAG_subprogram
DW_AT_low_pc (0x00000000004013a0)
DW_AT_high_pc (0x00000000004013a8)
DW_AT_frame_base (DW_OP_reg7 RSP)
DW_AT_object_pointer (0x0000669c)
DW_AT_GNU_all_call_sites (true)
DW_AT_specification (0x00005b5c "_ZN6Object3fooE4test")
0x000066a5: DW_TAG_formal_parameter
DW_AT_name ("t")
DW_AT_decl_file ("/disk1/yifeng.dongyifeng/my_code/llvm/build/bin/coro-debug-1.cpp")
DW_AT_decl_line (48)
DW_AT_type (0x00004146 "test")
0x000066ba: DW_TAG_variable
DW_AT_name ("t")
DW_AT_type (0x00004146 "test")
DW_AT_artificial (true)
```
The elf also has two 't' in the same scope.
But unluckily, it might let the debugger
confused. And failed to print parameters for O0 or above.
This patch will make coroutine parameters and move
parameters use the same DIVar and try to fix the problems
that I mentioned before.
Test Plan: check-clang
Reviewed By: aprantl, jmorse
Differential Revision: https://reviews.llvm.org/D97533
2021-04-12 10:59:22 +08:00
|
|
|
|
assert(
|
|
|
|
|
(ParamMoves.size() == 0 || (ParamMoves.size() == FnArgs.size())) &&
|
|
|
|
|
"ParamMoves and FnArgs should be the same size for coroutine function");
|
|
|
|
|
if (ParamMoves.size() == FnArgs.size() && DI)
|
|
|
|
|
for (const auto Pair : llvm::zip(FnArgs, ParamMoves))
|
|
|
|
|
DI->getCoroutineParameterMappings().insert(
|
|
|
|
|
{std::get<0>(Pair), std::get<1>(Pair)});
|
|
|
|
|
|
2017-05-24 20:09:14 +00:00
|
|
|
|
// Create parameter copies. We do it before creating a promise, since an
|
|
|
|
|
// evolution of coroutine TS may allow promise constructor to observe
|
|
|
|
|
// parameter copies.
|
|
|
|
|
for (auto *PM : S.getParamMoves()) {
|
|
|
|
|
EmitStmt(PM);
|
|
|
|
|
ParamReplacer.addCopy(cast<DeclStmt>(PM));
|
|
|
|
|
// TODO: if(CoroParam(...)) need to surround ctor and dtor
|
|
|
|
|
// for the copy, so that llvm can elide it if the copy is
|
|
|
|
|
// not needed.
|
|
|
|
|
}
|
|
|
|
|
|
2017-04-01 00:22:47 +00:00
|
|
|
|
EmitStmt(S.getPromiseDeclStmt());
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-05-24 20:09:14 +00:00
|
|
|
|
Address PromiseAddr = GetAddrOfLocalVar(S.getPromiseDecl());
|
|
|
|
|
auto *PromiseAddrVoidPtr =
|
|
|
|
|
new llvm::BitCastInst(PromiseAddr.getPointer(), VoidPtrTy, "", CoroId);
|
|
|
|
|
// Update CoroId to refer to the promise. We could not do it earlier because
|
|
|
|
|
// promise local variable was not emitted yet.
|
|
|
|
|
CoroId->setArgOperand(1, PromiseAddrVoidPtr);
|
|
|
|
|
|
2023-03-07 12:51:34 -08:00
|
|
|
|
// Now we have the promise, initialize the GRO
|
|
|
|
|
GroManager.EmitGroInit();
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-05-24 20:09:14 +00:00
|
|
|
|
EHStack.pushCleanup<CallCoroEnd>(EHCleanup);
|
2017-05-23 05:04:01 +00:00
|
|
|
|
|
|
|
|
|
CurCoro.Data->CurrentAwaitKind = AwaitKind::Init;
|
2018-05-04 14:02:37 +00:00
|
|
|
|
CurCoro.Data->ExceptionHandler = S.getExceptionHandler();
|
2017-05-23 05:04:01 +00:00
|
|
|
|
EmitStmt(S.getInitSuspendStmt());
|
2017-05-24 20:09:14 +00:00
|
|
|
|
CurCoro.Data->FinalJD = getJumpDestInCurrentScope(FinalBB);
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-04-01 00:22:47 +00:00
|
|
|
|
CurCoro.Data->CurrentAwaitKind = AwaitKind::Normal;
|
2017-05-22 22:33:17 +00:00
|
|
|
|
|
2018-05-04 14:02:37 +00:00
|
|
|
|
if (CurCoro.Data->ExceptionHandler) {
|
2018-06-23 18:57:26 +00:00
|
|
|
|
// If we generated IR to record whether an exception was thrown from
|
|
|
|
|
// 'await_resume', then use that IR to determine whether the coroutine
|
|
|
|
|
// body should be skipped.
|
|
|
|
|
// If we didn't generate the IR (perhaps because 'await_resume' was marked
|
|
|
|
|
// as 'noexcept'), then we skip this check.
|
|
|
|
|
BasicBlock *ContBB = nullptr;
|
|
|
|
|
if (CurCoro.Data->ResumeEHVar) {
|
|
|
|
|
BasicBlock *BodyBB = createBasicBlock("coro.resumed.body");
|
|
|
|
|
ContBB = createBasicBlock("coro.resumed.cont");
|
|
|
|
|
Value *SkipBody = Builder.CreateFlagLoad(CurCoro.Data->ResumeEHVar,
|
|
|
|
|
"coro.resumed.eh");
|
|
|
|
|
Builder.CreateCondBr(SkipBody, ContBB, BodyBB);
|
|
|
|
|
EmitBlock(BodyBB);
|
|
|
|
|
}
|
2018-05-04 14:02:37 +00:00
|
|
|
|
|
2018-08-09 21:08:08 +00:00
|
|
|
|
auto Loc = S.getBeginLoc();
|
2018-05-04 14:02:37 +00:00
|
|
|
|
CXXCatchStmt Catch(Loc, /*exDecl=*/nullptr,
|
|
|
|
|
CurCoro.Data->ExceptionHandler);
|
2023-04-06 15:10:24 +08:00
|
|
|
|
auto *TryStmt =
|
|
|
|
|
CXXTryStmt::Create(getContext(), Loc, S.getBody(), &Catch);
|
2017-05-22 22:33:17 +00:00
|
|
|
|
|
|
|
|
|
EnterCXXTryStmt(*TryStmt);
|
|
|
|
|
emitBodyAndFallthrough(*this, S, TryStmt->getTryBlock());
|
|
|
|
|
ExitCXXTryStmt(*TryStmt);
|
2018-05-04 14:02:37 +00:00
|
|
|
|
|
2018-06-23 18:57:26 +00:00
|
|
|
|
if (ContBB)
|
|
|
|
|
EmitBlock(ContBB);
|
2017-05-22 22:33:17 +00:00
|
|
|
|
}
|
|
|
|
|
else {
|
|
|
|
|
emitBodyAndFallthrough(*this, S, S.getBody());
|
|
|
|
|
}
|
2017-03-06 21:12:54 +00:00
|
|
|
|
|
2017-04-01 00:22:47 +00:00
|
|
|
|
// 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);
|
2017-05-23 05:04:01 +00:00
|
|
|
|
CurCoro.Data->CurrentAwaitKind = AwaitKind::Final;
|
|
|
|
|
EmitStmt(S.getFinalSuspendStmt());
|
2017-05-29 21:15:31 +00:00
|
|
|
|
} else {
|
2017-05-24 01:54:37 +00:00
|
|
|
|
// We don't need FinalBB. Emit it to make sure the block is deleted.
|
|
|
|
|
EmitBlock(FinalBB, /*IsFinished=*/true);
|
|
|
|
|
}
|
2017-03-06 21:12:54 +00:00
|
|
|
|
}
|
|
|
|
|
|
2017-03-26 02:18:05 +00:00
|
|
|
|
EmitBlock(RetBB);
|
2017-05-22 20:22:23 +00:00
|
|
|
|
// Emit coro.end before getReturnStmt (and parameter destructors), since
|
|
|
|
|
// resume and destroy parts of the coroutine should not include them.
|
2017-04-05 04:55:03 +00:00
|
|
|
|
llvm::Function *CoroEnd = CGM.getIntrinsic(llvm::Intrinsic::coro_end);
|
2023-09-15 09:54:38 -07:00
|
|
|
|
Builder.CreateCall(CoroEnd,
|
|
|
|
|
{NullPtr, Builder.getFalse(),
|
|
|
|
|
llvm::ConstantTokenNone::get(CoroEnd->getContext())});
|
2017-03-26 02:18:05 +00:00
|
|
|
|
|
2023-03-08 16:00:02 -08:00
|
|
|
|
if (Stmt *Ret = S.getReturnStmt()) {
|
|
|
|
|
// Since we already emitted the return value above, so we shouldn't
|
|
|
|
|
// emit it again here.
|
|
|
|
|
if (GroManager.DirectEmit)
|
|
|
|
|
cast<ReturnStmt>(Ret)->setRetValue(nullptr);
|
2017-05-22 20:22:23 +00:00
|
|
|
|
EmitStmt(Ret);
|
2023-03-08 16:00:02 -08:00
|
|
|
|
}
|
2022-01-05 10:19:44 +08:00
|
|
|
|
|
2022-06-10 11:37:09 +08:00
|
|
|
|
// LLVM require the frontend to mark the coroutine.
|
|
|
|
|
CurFn->setPresplitCoroutine();
|
2023-11-09 14:42:07 +08:00
|
|
|
|
|
|
|
|
|
if (CXXRecordDecl *RD = FnRetTy->getAsCXXRecordDecl();
|
|
|
|
|
RD && RD->hasAttr<CoroOnlyDestroyWhenCompleteAttr>())
|
|
|
|
|
CurFn->setCoroDestroyOnlyWhenComplete();
|
2016-10-03 22:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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;
|
2017-05-23 03:46:59 +00:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2018-08-09 21:08:08 +00:00
|
|
|
|
CGM.Error(E->getBeginLoc(), "this builtin expect that __builtin_coro_begin "
|
|
|
|
|
"has been used earlier in this function");
|
2023-08-09 00:52:17 +02:00
|
|
|
|
auto *NullPtr = llvm::ConstantPointerNull::get(Builder.getPtrTy());
|
2017-05-23 03:46:59 +00:00
|
|
|
|
return RValue::get(NullPtr);
|
|
|
|
|
}
|
2022-09-01 16:30:43 +08:00
|
|
|
|
case llvm::Intrinsic::coro_size: {
|
|
|
|
|
auto &Context = getContext();
|
2022-09-07 10:35:54 +08:00
|
|
|
|
CanQualType SizeTy = Context.getSizeType();
|
|
|
|
|
llvm::IntegerType *T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
|
2022-09-01 16:30:43 +08:00
|
|
|
|
llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_size, T);
|
|
|
|
|
return RValue::get(Builder.CreateCall(F));
|
|
|
|
|
}
|
2022-09-22 10:59:39 +08:00
|
|
|
|
case llvm::Intrinsic::coro_align: {
|
|
|
|
|
auto &Context = getContext();
|
|
|
|
|
CanQualType SizeTy = Context.getSizeType();
|
|
|
|
|
llvm::IntegerType *T = Builder.getIntNTy(Context.getTypeSize(SizeTy));
|
|
|
|
|
llvm::Function *F = CGM.getIntrinsic(llvm::Intrinsic::coro_align, T);
|
|
|
|
|
return RValue::get(Builder.CreateCall(F));
|
|
|
|
|
}
|
2016-10-03 22:44:48 +00:00
|
|
|
|
// 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;
|
|
|
|
|
}
|
2018-08-09 21:08:08 +00:00
|
|
|
|
CGM.Error(E->getBeginLoc(), "this builtin expect that __builtin_coro_id has"
|
2016-10-03 22:44:48 +00:00
|
|
|
|
" been used earlier in this function");
|
|
|
|
|
// Fallthrough to the next case to add TokenNone as the first argument.
|
2022-08-08 09:12:46 -07:00
|
|
|
|
[[fallthrough]];
|
2016-10-03 22:44:48 +00:00
|
|
|
|
}
|
|
|
|
|
// @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;
|
|
|
|
|
}
|
2019-02-01 21:58:17 +00:00
|
|
|
|
for (const Expr *Arg : E->arguments())
|
2016-10-03 22:44:48 +00:00
|
|
|
|
Args.push_back(EmitScalarExpr(Arg));
|
2023-09-15 09:54:38 -07:00
|
|
|
|
// @llvm.coro.end takes a token parameter. Add token 'none' as the last
|
|
|
|
|
// argument.
|
|
|
|
|
if (IID == llvm::Intrinsic::coro_end)
|
|
|
|
|
Args.push_back(llvm::ConstantTokenNone::get(getLLVMContext()));
|
2023-09-15 17:26:10 -04:00
|
|
|
|
|
2019-02-03 21:53:49 +00:00
|
|
|
|
llvm::Function *F = CGM.getIntrinsic(IID);
|
2016-10-03 22:44:48 +00:00
|
|
|
|
llvm::CallInst *Call = Builder.CreateCall(F, Args);
|
|
|
|
|
|
2017-05-23 04:21:27 +00:00
|
|
|
|
// Note: The following code is to enable to emit coro.id and coro.begin by
|
2017-05-23 03:46:59 +00:00
|
|
|
|
// hand to experiment with coroutines in C.
|
2016-10-03 22:44:48 +00:00
|
|
|
|
// 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);
|
|
|
|
|
}
|
2017-05-23 03:46:59 +00:00
|
|
|
|
else if (IID == llvm::Intrinsic::coro_begin) {
|
|
|
|
|
if (CurCoro.Data)
|
|
|
|
|
CurCoro.Data->CoroBegin = Call;
|
|
|
|
|
}
|
2017-05-23 04:21:27 +00:00
|
|
|
|
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;
|
2017-05-24 20:09:14 +00:00
|
|
|
|
}
|
|
|
|
|
return RValue::get(Call);
|
2016-10-03 22:44:48 +00:00
|
|
|
|
}
|