diff options
author | Brian Gesiak <modocache@gmail.com> | 2018-02-15 20:37:22 +0000 |
---|---|---|
committer | Brian Gesiak <modocache@gmail.com> | 2018-02-15 20:37:22 +0000 |
commit | 986062219fd2600835229f36496bb4ec7ee52b38 (patch) | |
tree | aa19f7cfd9fed1cfc291dd2c7bc78685ea2875ec /clang/lib/Sema/SemaCoroutine.cpp | |
parent | f3f35efe5c911e7dd51c58a92b073806a8909a3a (diff) | |
download | bcm5719-llvm-986062219fd2600835229f36496bb4ec7ee52b38.tar.gz bcm5719-llvm-986062219fd2600835229f36496bb4ec7ee52b38.zip |
[Coroutines] Use allocator overload when available
Summary:
Depends on https://reviews.llvm.org/D42605.
An implementation of the behavior described in `[dcl.fct.def.coroutine]/7`:
when a promise type overloads `operator new` using a "placement new"
that takes the same argument types as the coroutine function, that
overload is used when allocating the coroutine frame.
Simply passing references to the coroutine function parameters directly
to `operator new` results in invariant violations in LLVM's coroutine
splitting pass, so this implementation modifies Clang codegen to
produce allocator-specific alloc/store/loads for each parameter being
forwarded to the allocator.
Test Plan: `check-clang`
Reviewers: rsmith, GorNishanov, eric_niebler
Reviewed By: GorNishanov
Subscribers: lewissbaker, EricWF, cfe-commits
Differential Revision: https://reviews.llvm.org/D42606
llvm-svn: 325291
Diffstat (limited to 'clang/lib/Sema/SemaCoroutine.cpp')
-rw-r--r-- | clang/lib/Sema/SemaCoroutine.cpp | 125 |
1 files changed, 89 insertions, 36 deletions
diff --git a/clang/lib/Sema/SemaCoroutine.cpp b/clang/lib/Sema/SemaCoroutine.cpp index 67c8b9b1a78..54dcc9784f5 100644 --- a/clang/lib/Sema/SemaCoroutine.cpp +++ b/clang/lib/Sema/SemaCoroutine.cpp @@ -12,6 +12,7 @@ //===----------------------------------------------------------------------===// #include "CoroutineStmtBuilder.h" +#include "clang/AST/ASTLambda.h" #include "clang/AST/Decl.h" #include "clang/AST/ExprCXX.h" #include "clang/AST/StmtCXX.h" @@ -506,24 +507,15 @@ VarDecl *Sema::buildCoroutinePromise(SourceLocation Loc) { auto RefExpr = ExprEmpty(); auto Move = Moves.find(PD); - if (Move != Moves.end()) { - // If a reference to the function parameter exists in the coroutine - // frame, use that reference. - auto *MoveDecl = - cast<VarDecl>(cast<DeclStmt>(Move->second)->getSingleDecl()); - RefExpr = BuildDeclRefExpr(MoveDecl, MoveDecl->getType(), - ExprValueKind::VK_LValue, FD->getLocation()); - } else { - // If the function parameter doesn't exist in the coroutine frame, it - // must be a scalar value. Use it directly. - assert(!PD->getType()->getAsCXXRecordDecl() && - "Non-scalar types should have been moved and inserted into the " - "parameter moves map"); - RefExpr = - BuildDeclRefExpr(PD, PD->getOriginalType().getNonReferenceType(), - ExprValueKind::VK_LValue, FD->getLocation()); - } - + assert(Move != Moves.end() && + "Coroutine function parameter not inserted into move map"); + // If a reference to the function parameter exists in the coroutine + // frame, use that reference. + auto *MoveDecl = + cast<VarDecl>(cast<DeclStmt>(Move->second)->getSingleDecl()); + RefExpr = + BuildDeclRefExpr(MoveDecl, MoveDecl->getType().getNonReferenceType(), + ExprValueKind::VK_LValue, FD->getLocation()); if (RefExpr.isInvalid()) return nullptr; CtorArgExprs.push_back(RefExpr.get()); @@ -1050,7 +1042,12 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { const bool RequiresNoThrowAlloc = ReturnStmtOnAllocFailure != nullptr; - // FIXME: Add support for stateful allocators. + // [dcl.fct.def.coroutine]/7 + // Lookup allocation functions using a parameter list composed of the + // requested size of the coroutine state being allocated, followed by + // the coroutine function's arguments. If a matching allocation function + // exists, use it. Otherwise, use an allocation function that just takes + // the requested size. FunctionDecl *OperatorNew = nullptr; FunctionDecl *OperatorDelete = nullptr; @@ -1058,10 +1055,62 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { bool PassAlignment = false; SmallVector<Expr *, 1> PlacementArgs; + // [dcl.fct.def.coroutine]/7 + // "The allocation function’s name is looked up in the scope of P. + // [...] If the lookup finds an allocation function in the scope of P, + // overload resolution is performed on a function call created by assembling + // an argument list. The first argument is the amount of space requested, + // and has type std::size_t. The lvalues p1 ... pn are the succeeding + // arguments." + // + // ...where "p1 ... pn" are defined earlier as: + // + // [dcl.fct.def.coroutine]/3 + // "For a coroutine f that is a non-static member function, let P1 denote the + // type of the implicit object parameter (13.3.1) and P2 ... Pn be the types + // of the function parameters; otherwise let P1 ... Pn be the types of the + // function parameters. Let p1 ... pn be lvalues denoting those objects." + if (auto *MD = dyn_cast<CXXMethodDecl>(&FD)) { + if (MD->isInstance() && !isLambdaCallOperator(MD)) { + ExprResult ThisExpr = S.ActOnCXXThis(Loc); + if (ThisExpr.isInvalid()) + return false; + ThisExpr = S.CreateBuiltinUnaryOp(Loc, UO_Deref, ThisExpr.get()); + if (ThisExpr.isInvalid()) + return false; + PlacementArgs.push_back(ThisExpr.get()); + } + } + for (auto *PD : FD.parameters()) { + if (PD->getType()->isDependentType()) + continue; + + // Build a reference to the parameter. + auto PDLoc = PD->getLocation(); + ExprResult PDRefExpr = + S.BuildDeclRefExpr(PD, PD->getOriginalType().getNonReferenceType(), + ExprValueKind::VK_LValue, PDLoc); + if (PDRefExpr.isInvalid()) + return false; + + PlacementArgs.push_back(PDRefExpr.get()); + } S.FindAllocationFunctions(Loc, SourceRange(), /*UseGlobal*/ false, PromiseType, /*isArray*/ false, PassAlignment, PlacementArgs, - OperatorNew, UnusedResult); + OperatorNew, UnusedResult, /*Diagnose*/ false); + + // [dcl.fct.def.coroutine]/7 + // "If no matching function is found, overload resolution is performed again + // on a function call created by passing just the amount of space required as + // an argument of type std::size_t." + if (!OperatorNew && !PlacementArgs.empty()) { + PlacementArgs.clear(); + S.FindAllocationFunctions(Loc, SourceRange(), + /*UseGlobal*/ false, PromiseType, + /*isArray*/ false, PassAlignment, + PlacementArgs, OperatorNew, UnusedResult); + } bool IsGlobalOverload = OperatorNew && !isa<CXXRecordDecl>(OperatorNew->getDeclContext()); @@ -1080,7 +1129,8 @@ bool CoroutineStmtBuilder::makeNewAndDeleteExpr() { OperatorNew, UnusedResult); } - assert(OperatorNew && "expected definition of operator new to be found"); + if (!OperatorNew) + return false; if (RequiresNoThrowAlloc) { const auto *FT = OperatorNew->getType()->getAs<FunctionProtoType>(); @@ -1386,25 +1436,28 @@ bool Sema::buildCoroutineParameterMoves(SourceLocation Loc) { if (PD->getType()->isDependentType()) continue; - // No need to copy scalars, LLVM will take care of them. - if (PD->getType()->getAsCXXRecordDecl()) { - ExprResult PDRefExpr = BuildDeclRefExpr( - PD, PD->getType(), ExprValueKind::VK_LValue, Loc); // FIXME: scope? - if (PDRefExpr.isInvalid()) - return false; + ExprResult PDRefExpr = + BuildDeclRefExpr(PD, PD->getType().getNonReferenceType(), + ExprValueKind::VK_LValue, Loc); // FIXME: scope? + if (PDRefExpr.isInvalid()) + return false; - Expr *CExpr = castForMoving(*this, PDRefExpr.get()); + Expr *CExpr = nullptr; + if (PD->getType()->getAsCXXRecordDecl() || + PD->getType()->isRValueReferenceType()) + CExpr = castForMoving(*this, PDRefExpr.get()); + else + CExpr = PDRefExpr.get(); - auto D = buildVarDecl(*this, Loc, PD->getType(), PD->getIdentifier()); - AddInitializerToDecl(D, CExpr, /*DirectInit=*/true); + auto D = buildVarDecl(*this, Loc, PD->getType(), PD->getIdentifier()); + AddInitializerToDecl(D, CExpr, /*DirectInit=*/true); - // Convert decl to a statement. - StmtResult Stmt = ActOnDeclStmt(ConvertDeclToDeclGroup(D), Loc, Loc); - if (Stmt.isInvalid()) - return false; + // Convert decl to a statement. + StmtResult Stmt = ActOnDeclStmt(ConvertDeclToDeclGroup(D), Loc, Loc); + if (Stmt.isInvalid()) + return false; - ScopeInfo->CoroutineParameterMoves.insert(std::make_pair(PD, Stmt.get())); - } + ScopeInfo->CoroutineParameterMoves.insert(std::make_pair(PD, Stmt.get())); } return true; } |