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);
152 if(
const auto* coret = dyn_cast_or_null<CoreturnStmt>(child);
153 coret and (coret->getOperand() ==
nullptr)) {
170 Visit(stmt->getCond());
177 Visit(stmt->getCond());
184 Visit(stmt->getCond());
191 Visit(stmt->getCond());
201 Visit(stmt->getInit());
206 auto* oldInit = stmt->getInit();
207 auto* newInit = mBodyStmts.
mStmts.back();
208 mBodyStmts.
mStmts.pop_back();
215 Visit(stmt->getCond());
217 Visit(stmt->getInc());
226 Visit(stmt->getRangeStmt());
235 if(
auto* vd = dyn_cast_or_null<VarDecl>(stmt->getDecl())) {
236 RETURN_IF(not vd->isLocalVarDeclOrParm() or vd->isStaticLocal() or not
Contains(mVarNamePrefix, vd));
238 auto* memberExpr = mVarNamePrefix[vd];
246 for(
auto* decl : stmt->decls()) {
247 if(
auto* varDecl = dyn_cast_or_null<VarDecl>(decl)) {
248 if(varDecl->isStaticLocal()) {
254 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
256 auto* assign =
Assign(me, field, varDecl->getInit());
258 mVarNamePrefix.insert(std::make_pair(varDecl, me));
260 Visit(varDecl->getInit());
263 mBodyStmts.
Add(assign);
265 }
else if(
const auto* recordDecl = dyn_cast_or_null<CXXRecordDecl>(decl)) {
266 mASTData.
mFrameType->addDecl(
const_cast<CXXRecordDecl*
>(recordDecl));
276 ReplaceNode(mPrevStmt, stmt, indirectThisMemberExpr);
285 auto* tmp = mPrevStmt;
288 for(
auto* arg : stmt->arguments()) {
297 auto* tmp = mPrevStmt;
298 mPrevStmt = stmt->getCallee();
300 Visit(stmt->getCallee());
309 Visit(stmt->getOperand());
310 Visit(stmt->getPromiseCall());
317 if(isa<ExprWithCleanups>(mStaged)) {
318 mBodyStmts.
Add(stmt);
322 Visit(stmt->getOperand());
329 if(
const bool returnsVoid{stmt->getResumeExpr()->getType()->isVoidType()}; returnsVoid) {
330 Visit(stmt->getOperand());
336 mBodyStmts.
Add(stmt);
341 auto* resultVar =
Variable(name, stmt->getType());
346 Visit(stmt->getCommonExpr());
347 Visit(stmt->getOperand());
348 Visit(stmt->getSuspendExpr());
349 Visit(stmt->getReadyExpr());
350 Visit(stmt->getResumeExpr());
355 auto* varDecl = stmt->getPromiseDecl();
360 mVarNamePrefix.insert(std::make_pair(varDecl, me));
373 for(
auto* param : stmt->getParamMoves()) {
374 if(
auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
375 if(
auto* varDecl2 = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
377 if(
auto* declRef =
FindDeclRef(varDecl2->getAnyInitializer())) {
378 auto* varDecl = dyn_cast<ParmVarDecl>(declRef->getDecl());
380 auto* field =
AddField(mASTData,
GetName(*varDecl), varDecl->getType());
383 mVarNamePrefix.insert(std::make_pair(
const_cast<ParmVarDecl*
>(varDecl), me));
389 Visit(stmt->getBody());
391 Visit(stmt->getReturnStmt());
392 Visit(stmt->getReturnValue());
393 Visit(stmt->getReturnValueInit());
394 Visit(stmt->getExceptionHandler());
395 Visit(stmt->getReturnStmtOnAllocFailure());
396 Visit(stmt->getFallthroughHandler());
397 Visit(stmt->getInitSuspendStmt());
398 Visit(stmt->getFinalSuspendStmt());
403 auto* tmp = mPrevStmt;
406 for(
auto* child : stmt->children()) {
427 if(
const auto* args = fd.getTemplateSpecializationArgs()) {
430 for(
OnceFalse needsUnderscore{};
const auto& arg : args->asArray()) {
431 if(needsUnderscore) {
439 auto str = std::move(ofm.GetString());
446 if(fd.isOverloadedOperator()) {
469 auto resumeFnType =
Ptr(resumeFnFd->getType());
473 auto destroyFnType =
Ptr(destroyFnFd->getType());
481 auto* reicast =
ReinterpretCast(GetFramePointerType(), stmt->getAllocate());
483 coroFrameVar->setInit(reicast);
489 if(stmt->getReturnStmtOnAllocFailure()) {
490 auto* nptr =
new(ctx) CXXNullPtrLiteralExpr({});
501 mASTData, mSuspendsCounter,
const_cast<CoroutineBodyStmt*
>(stmt), llvm::DenseMap<VarDecl*, MemberExpr*>{}};
505 InsertArgWithNull(setSuspendIndexToZero);
508 auto* initializeInitialAwaitResume =
510 InsertArgWithNull(initializeInitialAwaitResume);
513 for(
auto* param : stmt->getParamMoves()) {
514 if(
const auto* declStmt = dyn_cast_or_null<DeclStmt>(param)) {
515 if(
const auto* varDecl = dyn_cast_or_null<VarDecl>(declStmt->getSingleDecl())) {
516 const auto varName =
GetName(*varDecl);
533 ArrayRef<ParmVarDecl*> funParams = fd.parameters();
534 SmallVector<ParmVarDecl*, 16> funParamStorage{};
535 QualType cxxMethodType{};
537 if(
const auto* cxxMethodDecl = dyn_cast_or_null<CXXMethodDecl>(&fd)) {
538 funParamStorage.reserve(funParams.size() + 1);
540 cxxMethodType = cxxMethodDecl->getFunctionObjectParameterType();
546 ranges::copy(funParams, std::back_inserter(funParamStorage));
548 funParams = funParamStorage;
551 auto getNonRefType = [&](
auto* var) -> QualType {
552#if IS_CLANG_NEWER_THAN(22)
553 return QualType(var->getType().getNonReferenceType().getCanonicalType().getTypePtrOrNull(), 0);
555 if(
const auto* et = var->getType().getNonReferenceType()->template getAs<ElaboratedType>()) {
556 return et->getNamedType();
558 return QualType(var->getType().getNonReferenceType().getTypePtrOrNull(), 0);
563 SmallVector<Expr*, 16> exprs{};
565 for(
auto* promiseTypeRecordDecl = mASTData.
mPromiseField->getType()->getAsCXXRecordDecl();
566 auto* ctor : promiseTypeRecordDecl->ctors()) {
568 if(not ranges::equal(
569 ctor->parameters(), funParams, [&](
auto& a,
auto& b) { return getNonRefType(a) == getNonRefType(b); })) {
577 if(not ctor->param_empty() and (not cxxMethodType.isNull()) and
578 (getNonRefType(ctor->getParamDecl(0)) == QualType(cxxMethodType.getCanonicalType().getTypePtrOrNull(), 0))) {
580 mASTData.
mThisExprs.push_back(CXXThisExpr::Create(ctx, {},
Ptr(cxxMethodType),
false));
583 (void)
static_cast<bool>(derefFirstParam);
586 for(
const auto& fparam : funParams) {
587 if(derefFirstParam) {
595 if(funParams.size()) {
612 auto* ctorArgs =
new(ctx) InitListExpr{ctx,
616#if IS_CLANG_NEWER_THAN(22)
624 InsertArgWithNull(newFrame);
646 InsertArgWithNull(assignResumeFn);
649 InsertArgWithNull(assignDestroyFn);
653 R
"A(Call the made up function with the coroutine body for initial suspend.
654 This function will be called subsequently by coroutine_handle<>::resume()
655 which calls __builtin_coro_resume(__handle_))A"sv);
658 InsertArgWithNull(callCoroFSM);
685 if(
auto* dtor = mASTData.
mPromiseField->getType()->getAsCXXRecordDecl()->getDestructor()) {
686 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine promise type"sv));
689 auto* deallocPromise =
AccessMember(promiseAccess, dtor,
false);
690 auto* dtorCall =
CallMemberFun(deallocPromise, dtor->getType());
691 deallocFuncBodyStmts.Add(dtorCall);
694 deallocFuncBodyStmts.Add(
695 Comment(
"promise_type is trivially destructible, no dtor required."sv));
710 auto* dtorCall =
CallMemberFun(deallocPromise, GetFrameType());
711 deallocFuncBodyStmts.Add(dtorCall);
713 deallocFuncBodyStmts.Add(
Comment(
"Deallocating the coroutine frame"sv));
714 deallocFuncBodyStmts.Add(
715 Comment(
"Note: The actual argument to delete is __builtin_coro_frame with the promise as parameter"sv));
717 deallocFuncBodyStmts.Add(stmt->getDeallocate());
730 auto* initialSuspendCase =
Case(0,
Break());
734 switchBodyStmts.
Add(
Case(i + 1,
Goto(BuildResumeLabelName(i + 1))));
738 sstmt->setBody(switchBody);
741 Comment(
"Create a switch to get to the correct resume point"sv), sstmt, stmt->getInitSuspendStmt()};
744 mState = eState::InitialSuspend;
750 mInsertVarDecl =
false;
751 mSupressRecordDecls =
true;
753 for(
const auto* c : stmt->getBody()->children()) {
754 funcBodyStmts.Add(c);
757 if(
const auto* coReturnVoid = dyn_cast_or_null<CoreturnStmt>(stmt->getFallthroughHandler())) {
758 funcBodyStmts.Add(coReturnVoid);
762 funcBodyStmts.Add(gotoFinalSuspend);
764 auto* body = [&]() ->
Stmt* {
768 if(
const auto* exceptionHandler = stmt->getExceptionHandler()) {
775 return Try(tryBody,
Catch(catchBodyStmts));
787 mState = eState::FinalSuspend;
791 mInsertVarDecl =
true;
797 if(not mSupressRecordDecls) {
818 if(
const auto* callee = dyn_cast_or_null<DeclRefExpr>(stmt->getCallee()->IgnoreCasts())) {
823 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_free"sv) {
827 }
else if(
GetPlainName(*callee) ==
"__builtin_coro_size"sv) {
837static std::optional<std::string>
838FindValue(llvm::DenseMap<
const Expr*, std::pair<const DeclRefExpr*, std::string>>& map,
const Expr* key)
840 if(
const auto& s = map.find(key); s != map.end()) {
841 return s->second.second;
850 const auto* sourceExpr = stmt->getSourceExpr();
852 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
868 if(
auto [thisDeref, v] = value; (thisDeref == dref) and (v == lookupName)) {
875 mOpaqueValues.insert(std::make_pair(sourceExpr, std::make_pair(dref, accessName)));
880 auto* promiseField = AddField(name, stmt->getType());
884 codeGenerator.InsertArg(assignPromiseSuspend);
885 ofm.AppendSemiNewLine();
895std::string CoroutinesCodeGenerator::BuildResumeLabelName(
int index)
const
905 if(isa<CoawaitExpr>(stmt)) {
906 return kwCoAwaitSpace;
908 return kwCoYieldSpace;
912 mPosBeforeSuspendExpr = mOutputFormatHelper.CurrentPos();
931 mSupressCasts =
true;
933 auto* il =
Int32(++mSuspendsCount);
934 auto* bop =
Assign(mASTData.mSuspendIndexAccess, mASTData.mSuspendIndexField, il);
939 const bool returnsVoid{stmt->getSuspendExpr()->getType()->isVoidType()};
944 StmtsContainer bodyStmts{};
945 Expr* initializeInitialAwaitResume =
nullptr;
947 const bool canThrow{[&] {
948 if(
const auto* e = dyn_cast_or_null<ExprWithCleanups>(stmt->getSuspendExpr())) {
949 if(
const auto* ce = dyn_cast_or_null<CallExpr>(e->getSubExpr())) {
950 if(
const FunctionDecl* fd = ce->getDirectCallee()) {
951 if(
const FunctionProtoType* fpt = fd->getType()->getAs<FunctionProtoType>()) {
952 return not fpt->isNothrow(
false);
961 auto addInitialAwaitSuspendCalled = [&] {
962 if(eState::InitialSuspend == mState) {
963 mState = eState::Body;
965 initializeInitialAwaitResume =
966 Assign(mASTData.mFrameAccessDeclRef, mASTData.mInitialAwaitResumeCalledField,
Bool(
true));
967 bodyStmts.Add(initializeInitialAwaitResume);
971 auto insertTryCatchIfNecessary = [&](StmtsContainer& cont) {
975 StmtsContainer catchBodyStmts{
976 Assign(mASTData.mSuspendIndexAccess, mASTData.mSuspendIndexField,
Int32(mSuspendsCount - 1)),
Throw()};
979 cont.Add(
Try(tryBody,
Catch(catchBodyStmts)));
985 bodyStmts.Add(stmt->getSuspendExpr());
987 insertTryCatchIfNecessary(bodyStmts);
989 addInitialAwaitSuspendCalled();
992 InsertArg(
If(
Not(stmt->getReadyExpr()), bodyStmts));
995 addInitialAwaitSuspendCalled();
998 auto* ifSuspend =
If(stmt->getSuspendExpr(), bodyStmts);
1000 StmtsContainer innerBodyStmts{bop, ifSuspend};
1001 insertTryCatchIfNecessary(innerBodyStmts);
1003 InsertArg(
If(
Not(stmt->getReadyExpr()), innerBodyStmts));
1006 if(not returnsVoid and initializeInitialAwaitResume) {
1008 InsertArgWithNull(initializeInitialAwaitResume);
1009 mOutputFormatHelper.AppendNewLine();
1012 auto* suspendLabel =
Label(BuildResumeLabelName(mSuspendsCount));
1013 InsertArg(suspendLabel);
1015 if(eState::FinalSuspend == mState) {
1016 auto* memExpr =
AccessMember(mASTData.mFrameAccessDeclRef, mASTData.mDestroyFnField,
true);
1017 auto* callCoroFSM =
Call(memExpr, {mASTData.mFrameAccessDeclRef});
1018 InsertArg(callCoroFSM);
1022 const auto* resumeExpr = stmt->getResumeExpr();
1024 if(not resumeExpr->getType()->isVoidType()) {
1025 const auto* sourceExpr = stmt->getOpaqueValue()->getSourceExpr();
1027 if(
const auto& s =
FindValue(mOpaqueValues, sourceExpr)) {
1031 AddField(fieldName, resumeExpr->getType());
1035 InsertArg(resumeExpr);
1039void CoroutinesCodeGenerator::InsertArg(
const CoreturnStmt* stmt)
1043 if(stmt->getPromiseCall()) {
1044 InsertArg(stmt->getPromiseCall());
1046 if(stmt->isImplicit()) {
1047 mOutputFormatHelper.AppendComment(
"implicit"sv);
1053void 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