21namespace ranges = std::ranges;
35using namespace asthelpers;
38QualType CoroutinesCodeGenerator::GetFramePointerType()
const
40 return Ptr(GetFrameType());
75FieldDecl* CoroutinesCodeGenerator::AddField(std::string_view name, QualType type)
77 return ::clang::insights::AddField(mASTData, name, type);
115 size_t& mSuspendsCount;
116 llvm::DenseMap<VarDecl*, MemberExpr*> mVarNamePrefix{};
120 size_t& suspendsCounter,
122 llvm::DenseMap<VarDecl*, MemberExpr*> varNamePrefix,
123 Stmt* prev =
nullptr)
125 , mASTData{coroutineASTData}
126 , mSuspendsCount{suspendsCounter}
127 , mVarNamePrefix{varNamePrefix}
129 if(
nullptr == mPrevStmt) {
145 for(
auto* child : stmt->body()) {
150 mBodyStmts.
Add(child);
165 Visit(stmt->getCond());
172 Visit(stmt->getCond());
179 Visit(stmt->getCond());
186 Visit(stmt->getCond());
196 Visit(stmt->getInit());
201 auto* oldInit = stmt->getInit();
202 auto* newInit = mBodyStmts.
mStmts.back();
203 mBodyStmts.
mStmts.pop_back();
210 Visit(stmt->getCond());
212 Visit(stmt->getInc());
221 Visit(stmt->getRangeStmt());
230 if(
auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl())) {
231 RETURN_IF(not vd->isLocalVarDeclOrParm() or vd->isStaticLocal() or not
Contains(mVarNamePrefix, vd));
233 auto* memberExpr = mVarNamePrefix[vd];
241 for(
auto* decl : stmt->decls()) {
242 if(
auto* varDecl = dyn_cast_or_null<VarDecl>(decl)) {
243 if(varDecl->isStaticLocal()) {
249 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
251 auto* assign =
Assign(me, field, varDecl->getInit());
253 mVarNamePrefix.insert(std::make_pair(varDecl, me));
255 Visit(varDecl->getInit());
258 mBodyStmts.
Add(assign);
260 }
else if(
const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(decl)) {
261 mASTData.
mFrameType->addDecl(
const_cast<CXXRecordDecl*
>(recordDecl));
271 ReplaceNode(mPrevStmt, stmt, indirectThisMemberExpr);
280 auto* tmp = mPrevStmt;
283 for(
auto* arg : stmt->arguments()) {
292 auto* tmp = mPrevStmt;
293 mPrevStmt = stmt->getCallee();
295 Visit(stmt->getCallee());
304 Visit(stmt->getOperand());
305 Visit(stmt->getPromiseCall());
312 if(isa<ExprWithCleanups>(mStaged)) {
313 mBodyStmts.
Add(stmt);
317 Visit(stmt->getOperand());
324 if(
const bool returnsVoid{stmt->getResumeExpr()->getType()->isVoidType()}; returnsVoid) {
325 Visit(stmt->getOperand());
331 mBodyStmts.
Add(stmt);
336 auto* resultVar =
Variable(name, stmt->getType());
341 Visit(stmt->getCommonExpr());
342 Visit(stmt->getOperand());
343 Visit(stmt->getSuspendExpr());
344 Visit(stmt->getReadyExpr());
345 Visit(stmt->getResumeExpr());
350 auto* varDecl = stmt->getPromiseDecl();
355 mVarNamePrefix.insert(std::make_pair(varDecl, me));
368 for(
auto* param : stmt->getParamMoves()) {
369 if(
auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
370 if(
auto* varDecl2 = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
372 if(
auto* declRef =
FindDeclRef(varDecl2->getAnyInitializer())) {
373 auto* varDecl = dyn_cast<ParmVarDecl>(declRef->getDecl());
375 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
378 mVarNamePrefix.insert(std::make_pair(
const_cast<ParmVarDecl*
>(varDecl), me));
384 Visit(stmt->getBody());
386 Visit(stmt->getReturnStmt());
387 Visit(stmt->getReturnValue());
388 Visit(stmt->getReturnValueInit());
389 Visit(stmt->getExceptionHandler());
390 Visit(stmt->getReturnStmtOnAllocFailure());
391 Visit(stmt->getFallthroughHandler());
392 Visit(stmt->getInitSuspendStmt());
393 Visit(stmt->getFinalSuspendStmt());
398 auto* tmp = mPrevStmt;
401 for(
auto* child : stmt->children()) {
422 if(
const auto* args = fd.getTemplateSpecializationArgs()) {
425 for(
OnceFalse needsUnderscore{};
const auto& arg : args->asArray()) {
426 if(needsUnderscore) {
434 auto str = std::move(ofm.GetString());
441 if(fd.isOverloadedOperator()) {
464 auto resumeFnType =
Ptr(resumeFnFd->getType());
468 auto destroyFnType =
Ptr(destroyFnFd->getType());
476 auto* reicast =
ReinterpretCast(GetFramePointerType(), stmt->getAllocate());
478 coroFrameVar->setInit(reicast);
484 if(stmt->getReturnStmtOnAllocFailure()) {
485 auto* nptr =
new(ctx) CXXNullPtrLiteralExpr({});
496 mASTData, mSuspendsCounter,
const_cast<CoroutineBodyStmt*
>(stmt), llvm::DenseMap<VarDecl*, MemberExpr*>{}};
500 InsertArgWithNull(setSuspendIndexToZero);
503 auto* initializeInitialAwaitResume =
505 InsertArgWithNull(initializeInitialAwaitResume);
508 for(
auto* param : stmt->getParamMoves()) {
509 if(
const auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
510 if(
const auto* varDecl = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
511 const auto varName =
GetName(*varDecl);
528 ArrayRef<ParmVarDecl*> funParams = fd.parameters();
529 SmallVector<ParmVarDecl*, 16> funParamStorage{};
530 QualType cxxMethodType{};
532 if(
const auto* cxxMethodDecl = dyn_cast_or_null<CXXMethodDecl>(&fd)) {
533 funParamStorage.reserve(funParams.size() + 1);
535 cxxMethodType = cxxMethodDecl->getFunctionObjectParameterType();
541 ranges::copy(funParams, std::back_inserter(funParamStorage));
543 funParams = funParamStorage;
546 auto getNonRefType = [&](
auto* var) -> QualType {
547 if(
const auto* et = var->getType().getNonReferenceType()->template getAs<ElaboratedType>()) {
548 return et->getNamedType();
550 return QualType(var->getType().getNonReferenceType().getTypePtrOrNull(), 0);
554 SmallVector<Expr*, 16> exprs{};
556 for(
auto* promiseTypeRecordDecl = mASTData.
mPromiseField->getType()->getAsCXXRecordDecl();
557 auto* ctor : promiseTypeRecordDecl->ctors()) {
559 if(not ranges::equal(
560 ctor->parameters(), funParams, [&](
auto& a,
auto& b) { return getNonRefType(a) == getNonRefType(b); })) {
568 if(not ctor->param_empty() and
569 (getNonRefType(ctor->getParamDecl(0)) == QualType(cxxMethodType.getTypePtrOrNull(), 0))) {
571 mASTData.
mThisExprs.push_back(CXXThisExpr::Create(ctx, {},
Ptr(cxxMethodType),
false));
574 (void)
static_cast<bool>(derefFirstParam);
577 for(
const auto& fparam : funParams) {
578 if(derefFirstParam) {
586 if(funParams.size()) {
603 auto* ctorArgs =
new(ctx) InitListExpr{ctx, {}, exprs, {}};
607 InsertArgWithNull(newFrame);
629 InsertArgWithNull(assignResumeFn);
632 InsertArgWithNull(assignDestroyFn);
636 R
"A(Call the made up function with the coroutine body for initial suspend.
637 This function will be called subsequently by coroutine_handle<>::resume()
638 which calls __builtin_coro_resume(__handle_))A"sv);
641 InsertArgWithNull(callCoroFSM);
667 if(
auto* dtor = mASTData.
mPromiseField->getType()->getAsCXXRecordDecl()->getDestructor()) {
668 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine promise type"sv));
671 auto* deallocPromise =
AccessMember(promiseAccess, dtor,
false);
672 auto* dtorCall =
CallMemberFun(deallocPromise, dtor->getType());
673 deallocFuncBodyStmts.Add(dtorCall);
676 deallocFuncBodyStmts.Add(
677 Comment(
"promise_type is trivially destructible, no dtor required."sv));
692 auto* dtorCall =
CallMemberFun(deallocPromise, GetFrameType());
693 deallocFuncBodyStmts.Add(dtorCall);
695 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine frame"sv));
696 deallocFuncBodyStmts.Add(
697 Comment(
"Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter"sv));
699 deallocFuncBodyStmts.Add(stmt->getDeallocate());
712 auto* initialSuspendCase =
Case(0,
Break());
716 switchBodyStmts.
Add(
Case(i + 1,
Goto(BuildResumeLabelName(i + 1))));
720 sstmt->setBody(switchBody);
723 Comment(
"Create a switch to get to the correct resume point"sv), sstmt, stmt->getInitSuspendStmt()};
726 mState = eState::InitialSuspend;
732 mInsertVarDecl =
false;
733 mSupressRecordDecls =
true;
735 for(
const auto* c : stmt->getBody()->children()) {
736 funcBodyStmts.Add(c);
739 if(
const auto* coReturnVoid = dyn_cast_or_null<CoreturnStmt>(stmt->getFallthroughHandler())) {
740 funcBodyStmts.Add(coReturnVoid);
744 funcBodyStmts.Add(gotoFinalSuspend);
746 auto* body = [&]() ->
Stmt* {
750 if(
const auto* exceptionHandler = stmt->getExceptionHandler()) {
757 return Try(tryBody,
Catch(catchBodyStmts));
769 mState = eState::FinalSuspend;
773 mInsertVarDecl =
true;
779 if(not mSupressRecordDecls) {
800 if(
const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreCasts())) {
805 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_free"sv) {
809 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_size"sv) {
819static std::optional<std::string>
820FindValue(llvm::DenseMap<
const Expr*, std::pair<const DeclRefExpr*, std::string>>& map,
const Expr* key)
822 if(
const auto& s = map.find(key); s != map.end()) {
823 return s->second.second;
832 const auto* sourceExpr = stmt->getSourceExpr();
834 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
850 if(
auto [thisDeref, v] = value; (thisDeref == dref) and (v == lookupName)) {
857 mOpaqueValues.insert(std::make_pair(sourceExpr, std::make_pair(dref, accessName)));
862 auto* promiseField = AddField(name, stmt->getType());
866 codeGenerator.InsertArg(assignPromiseSuspend);
867 ofm.AppendSemiNewLine();
877std::string CoroutinesCodeGenerator::BuildResumeLabelName(
int index)
const
887 if(isa<CoawaitExpr>(stmt)) {
888 return kwCoAwaitSpace;
890 return kwCoYieldSpace;
894 mPosBeforeSuspendExpr = mOutputFormatHelper.CurrentPos();
913 mSupressCasts =
true;
915 auto* il =
Int32(++mSuspendsCount);
916 auto* bop =
Assign(mASTData.mSuspendIndexAccess, mASTData.mSuspendIndexField, il);
921 const bool returnsVoid{stmt->getSuspendExpr()->getType()->isVoidType()};
926 StmtsContainer bodyStmts{};
927 Expr* initializeInitialAwaitResume =
nullptr;
929 auto addInitialAwaitSuspendCalled = [&] {
932 if(eState::InitialSuspend == mState) {
933 mState = eState::Body;
935 initializeInitialAwaitResume =
936 Assign(mASTData.mFrameAccessDeclRef, mASTData.mInitialAwaitResumeCalledField,
Bool(
true));
937 bodyStmts.Add(initializeInitialAwaitResume);
942 bodyStmts.Add(stmt->getSuspendExpr());
943 addInitialAwaitSuspendCalled();
946 InsertArg(
If(
Not(stmt->getReadyExpr()), bodyStmts));
949 addInitialAwaitSuspendCalled();
952 auto* ifSuspend =
If(stmt->getSuspendExpr(), bodyStmts);
954 InsertArg(
If(
Not(stmt->getReadyExpr()), ifSuspend));
957 if(not returnsVoid and initializeInitialAwaitResume) {
959 InsertArgWithNull(initializeInitialAwaitResume);
960 mOutputFormatHelper.AppendNewLine();
963 auto* suspendLabel =
Label(BuildResumeLabelName(mSuspendsCount));
964 InsertArg(suspendLabel);
966 if(eState::FinalSuspend == mState) {
967 auto* memExpr =
AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mDestroyFnField,
true);
968 auto* callCoroFSM =
Call(memExpr, {mASTData.mFrameAccessDeclRef});
969 InsertArg(callCoroFSM);
973 const auto* resumeExpr = stmt->getResumeExpr();
975 if(not resumeExpr->getType()->isVoidType()) {
976 const auto* sourceExpr = stmt->getOpaqueValue()->getSourceExpr();
978 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
982 AddField(fieldName, resumeExpr->getType());
986 InsertArg(resumeExpr);
990void CoroutinesCodeGenerator::InsertArg(
const CoreturnStmt* stmt)
994 if(stmt->getPromiseCall()) {
995 InsertArg(stmt->getPromiseCall());
997 if(stmt->isImplicit()) {
998 mOutputFormatHelper.AppendComment(
"implicit"sv);
1004void CoroutinesCodeGenerator::InsertArgWithNull(
const Stmt* stmt)
const ASTContext & GetGlobalAST()
Get access to the ASTContext.
constexpr std::string_view hlpDestroyFn
constexpr std::string_view kwCoReturnSpace
constexpr std::string_view kwInternalThis
constexpr std::string_view hlpResumeFn
constexpr std::string_view hlpAssing
#define RETURN_IF(cond)
! A helper inspired by https://github.com/Microsoft/wil/wiki/Error-handling-helpers
virtual void InsertArg(const Decl *stmt)
void InsertInstantiationPoint(const SourceManager &sm, const SourceLocation &instLoc, std::string_view text={})
Inserts the instantiation point of a template.
void InsertTemplateArg(const TemplateArgument &arg)
OutputFormatHelper & mOutputFormatHelper
A special container which creates either a CodeGenerator or a CfrontCodeGenerator depending on the co...
A special generator for coroutines. It is only activated, if -show-coroutines-transformation is given...
void InsertArg(const ImplicitCastExpr *stmt) override
void InsertCoroutine(const FunctionDecl &fd, const CoroutineBodyStmt *body)
~CoroutinesCodeGenerator() override
BinaryOperator * Equal(Expr *var, Expr *assignExpr)
UnaryOperator * AddrOf(const Expr *stmt)
CXXReinterpretCastExpr * ReinterpretCast(QualType toType, const Expr *toExpr, bool makePointer)
CXXRecordDecl * Struct(std::string_view name)
FieldDecl * mkFieldDecl(DeclContext *dc, std::string_view name, QualType type)
DeclRefExpr * mkDeclRefExpr(const ValueDecl *vd)
CXXNewExpr * New(ArrayRef< Expr * > placementArgs, const Expr *expr, QualType t)
std::vector< std::pair< std::string_view, QualType > > params_vector
CallExpr * Call(const FunctionDecl *fd, ArrayRef< Expr * > params)
UnaryOperator * Ref(const Expr *e)
VarDecl * Variable(std::string_view name, QualType type, DeclContext *dc)
MemberExpr * AccessMember(const Expr *expr, const ValueDecl *vd, bool isArrow)
DeclRefExpr * mkVarDeclRefExpr(std::string_view name, QualType type)
ReturnStmt * Return(Expr *stmt)
CaseStmt * Case(int value, Stmt *stmt)
Stmt * Comment(std::string_view comment)
IfStmt * If(const Expr *condition, ArrayRef< Stmt * > bodyStmts)
GotoStmt * Goto(std::string_view labelName)
CXXCatchStmt * Catch(ArrayRef< Stmt * > body)
CXXThrowExpr * Throw(const Expr *expr)
void ReplaceNode(Stmt *parent, Stmt *oldNode, Stmt *newNode)
UnaryExprOrTypeTraitExpr * Sizeof(QualType toType)
LabelStmt * Label(std::string_view name)
UnaryOperator * Not(const Expr *stmt)
CXXBoolLiteralExpr * Bool(bool b)
FunctionDecl * Function(std::string_view name, QualType returnType, const params_vector ¶meters)
CompoundStmt * mkCompoundStmt(ArrayRef< Stmt * > bodyStmts, SourceLocation beginLoc, SourceLocation endLoc)
SwitchStmt * Switch(Expr *stmt)
IntegerLiteral * Int32(uint64_t value)
ParmVarDecl * Parameter(const FunctionDecl *fd, std::string_view name, QualType type)
UnaryOperator * Dref(const Expr *stmt)
QualType Ptr(QualType srcType)
CXXStaticCastExpr * StaticCast(QualType toType, const Expr *toExpr, bool makePointer)
CXXMemberCallExpr * CallMemberFun(Expr *memExpr, QualType retType)
BinaryOperator * Assign(const VarDecl *var, Expr *assignExpr)
CXXTryStmt * Try(const Stmt *tryBody, CXXCatchStmt *catchAllBody)
const std::string SUSPEND_INDEX_NAME
const std::string RESUME_LABEL_PREFIX
bool Contains(const std::string_view source, const std::string_view search)
const DeclRefExpr * FindDeclRef(const Stmt *stmt)
Go deep in a Stmt if necessary and look to all childs for a DeclRefExpr.
std::string GetPlainName(const DeclRefExpr &DRE)
static auto * CreateCoroFunctionDecl(std::string funcName, QualType type)
static void SetFunctionBody(FunctionDecl *fd, StmtsContainer &bodyStmts)
constexpr std::string_view CORO_FRAME_NAME
std::string MakeLineColumnName(const SourceManager &sm, const SourceLocation &loc, const std::string_view &prefix)
std::string GetName(const NamedDecl &nd, const QualifiedName qualifiedName)
void ReplaceAll(std::string &str, std::string_view from, std::string_view to)
std::string BuildInternalVarName(const std::string_view &varName)
const std::string CORO_FRAME_ACCESS_THIS
std::string BuildTemplateParamObjectName(std::string name)
const std::string FINAL_SUSPEND_NAME
const std::string INITIAL_AWAIT_SUSPEND_CALLED_NAME
static std::string BuildSuspendVarName(const OpaqueValueExpr *stmt)
static std::optional< std::string > FindValue(llvm::DenseMap< const Expr *, std::pair< const DeclRefExpr *, std::string > > &map, const Expr *key)
void EnableGlobalInsert(GlobalInserts idx)
const std::string CORO_FRAME_ACCESS
static FieldDecl * AddField(CoroutineASTData &astData, std::string_view name, QualType type)
std::string StrCat(const auto &... args)
FieldDecl * mInitialAwaitResumeCalledField
std::vector< const CXXThisExpr * > mThisExprs
FieldDecl * mPromiseField
FieldDecl * mResumeFnField
FieldDecl * mSuspendIndexField
MemberExpr * mSuspendIndexAccess
DeclRefExpr * mFrameAccessDeclRef
CXXRecordDecl * mFrameType
FieldDecl * mDestroyFnField
MemberExpr * mInitialAwaitResumeCalledAccess
! A helper type to have a container for ArrayRef
void Add(const Stmt *stmt)
SmallVector< Stmt *, 64 > mStmts