зеркало из https://github.com/microsoft/clang-1.git
Recognize when the named return value optimization applies in a
"return" statement and mark the corresponding CXXConstructExpr as elidable. Teach CodeGen that eliding a temporary is different from eliding an object construction. This is just a baby step toward NRVO. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@103849 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
Родитель
67d438d39a
Коммит
3c9034cb7f
|
@ -321,11 +321,13 @@ CodeGenFunction::EmitCXXConstructExpr(llvm::Value *Dest,
|
|||
return;
|
||||
}
|
||||
// Code gen optimization to eliminate copy constructor and return
|
||||
// its first argument instead.
|
||||
// its first argument instead, if in fact that argument is a temporary
|
||||
// object.
|
||||
if (getContext().getLangOptions().ElideConstructors && E->isElidable()) {
|
||||
const Expr *Arg = E->getArg(0)->getTemporaryObject();
|
||||
EmitAggExpr(Arg, Dest, false);
|
||||
return;
|
||||
if (const Expr *Arg = E->getArg(0)->getTemporaryObject()) {
|
||||
EmitAggExpr(Arg, Dest, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (Array) {
|
||||
QualType BaseElementTy = getContext().getBaseElementType(Array);
|
||||
|
|
|
@ -956,6 +956,7 @@ unsigned PCHStmtReader::VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|||
E->setRequiresZeroInitialization(Record[Idx++]);
|
||||
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
|
||||
E->setArg(I, cast<Expr>(StmtStack[StmtStack.size() - N + I]));
|
||||
E->setConstructionKind((CXXConstructExpr::ConstructionKind)Record[Idx++]);
|
||||
return E->getNumArgs();
|
||||
}
|
||||
|
||||
|
|
|
@ -873,6 +873,7 @@ void PCHStmtWriter::VisitCXXConstructExpr(CXXConstructExpr *E) {
|
|||
Record.push_back(E->getNumArgs());
|
||||
for (unsigned I = 0, N = E->getNumArgs(); I != N; ++I)
|
||||
Writer.WriteSubStmt(E->getArg(I));
|
||||
Record.push_back(E->getConstructionKind()); // FIXME: stable encoding
|
||||
Code = pch::EXPR_CXX_CONSTRUCT;
|
||||
}
|
||||
|
||||
|
|
|
@ -441,8 +441,10 @@ bool Sema::CheckCXXThrowOperand(SourceLocation ThrowLoc, Expr *&E) {
|
|||
|
||||
// Initialize the exception result. This implicitly weeds out
|
||||
// abstract types or types with inaccessible copy constructors.
|
||||
// FIXME: Determine whether we can elide this copy per C++0x [class.copy]p34.
|
||||
InitializedEntity Entity =
|
||||
InitializedEntity::InitializeException(ThrowLoc, E->getType());
|
||||
InitializedEntity::InitializeException(ThrowLoc, E->getType(),
|
||||
/*NRVO=*/false);
|
||||
OwningExprResult Res = PerformCopyInitialization(Entity,
|
||||
SourceLocation(),
|
||||
Owned(E));
|
||||
|
|
|
@ -1984,6 +1984,26 @@ DeclaratorDecl *InitializedEntity::getDecl() const {
|
|||
return 0;
|
||||
}
|
||||
|
||||
bool InitializedEntity::allowsNRVO() const {
|
||||
switch (getKind()) {
|
||||
case EK_Result:
|
||||
case EK_Exception:
|
||||
return LocAndNRVO.NRVO;
|
||||
|
||||
case EK_Variable:
|
||||
case EK_Parameter:
|
||||
case EK_Member:
|
||||
case EK_New:
|
||||
case EK_Temporary:
|
||||
case EK_Base:
|
||||
case EK_ArrayElement:
|
||||
case EK_VectorElement:
|
||||
break;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
//===----------------------------------------------------------------------===//
|
||||
// Initialization sequence
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
@ -3242,9 +3262,9 @@ static Sema::OwningExprResult CopyObject(Sema &S,
|
|||
// directly into the target of the omitted copy/move
|
||||
//
|
||||
// Note that the other three bullets are handled elsewhere. Copy
|
||||
// elision for return statements and throw expressions are (FIXME:
|
||||
// not yet) handled as part of constructor initialization, while
|
||||
// copy elision for exception handlers is handled by the run-time.
|
||||
// elision for return statements and throw expressions are handled as part
|
||||
// of constructor initialization, while copy elision for exception handlers
|
||||
// is handled by the run-time.
|
||||
bool Elidable = CurInitExpr->isTemporaryObject() &&
|
||||
S.Context.hasSameUnqualifiedType(T, CurInitExpr->getType());
|
||||
SourceLocation Loc;
|
||||
|
@ -3737,7 +3757,7 @@ InitializationSequence::Perform(Sema &S,
|
|||
unsigned NumArgs = Args.size();
|
||||
CXXConstructorDecl *Constructor
|
||||
= cast<CXXConstructorDecl>(Step->Function.Function);
|
||||
|
||||
|
||||
// Build a call to the selected constructor.
|
||||
ASTOwningVector<&ActionBase::DeleteExpr> ConstructorArgs(S);
|
||||
SourceLocation Loc = Kind.getLocation();
|
||||
|
@ -3774,11 +3794,21 @@ InitializationSequence::Perform(Sema &S,
|
|||
CXXConstructExpr::CK_VirtualBase :
|
||||
CXXConstructExpr::CK_NonVirtualBase;
|
||||
}
|
||||
CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(),
|
||||
Constructor,
|
||||
move_arg(ConstructorArgs),
|
||||
ConstructorInitRequiresZeroInit,
|
||||
ConstructKind);
|
||||
|
||||
// If the entity allows NRVO, mark the construction as elidable
|
||||
// unconditionally.
|
||||
if (Entity.allowsNRVO())
|
||||
CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(),
|
||||
Constructor, /*Elidable=*/true,
|
||||
move_arg(ConstructorArgs),
|
||||
ConstructorInitRequiresZeroInit,
|
||||
ConstructKind);
|
||||
else
|
||||
CurInit = S.BuildCXXConstructExpr(Loc, Entity.getType(),
|
||||
Constructor,
|
||||
move_arg(ConstructorArgs),
|
||||
ConstructorInitRequiresZeroInit,
|
||||
ConstructKind);
|
||||
}
|
||||
if (CurInit.isInvalid())
|
||||
return S.ExprError();
|
||||
|
|
|
@ -85,11 +85,16 @@ private:
|
|||
/// the VarDecl, ParmVarDecl, or FieldDecl, respectively.
|
||||
DeclaratorDecl *VariableOrMember;
|
||||
|
||||
/// \brief When Kind == EK_Result, EK_Exception, or EK_New, the
|
||||
/// location of the 'return', 'throw', or 'new' keyword,
|
||||
/// respectively. When Kind == EK_Temporary, the location where
|
||||
/// the temporary is being created.
|
||||
unsigned Location;
|
||||
struct {
|
||||
/// \brief When Kind == EK_Result, EK_Exception, or EK_New, the
|
||||
/// location of the 'return', 'throw', or 'new' keyword,
|
||||
/// respectively. When Kind == EK_Temporary, the location where
|
||||
/// the temporary is being created.
|
||||
unsigned Location;
|
||||
|
||||
/// \brief Whether the
|
||||
bool NRVO;
|
||||
} LocAndNRVO;
|
||||
|
||||
/// \brief When Kind == EK_Base, the base specifier that provides the
|
||||
/// base class. The lower bit specifies whether the base is an inherited
|
||||
|
@ -116,8 +121,13 @@ private:
|
|||
/// \brief Create the initialization entity for the result of a
|
||||
/// function, throwing an object, performing an explicit cast, or
|
||||
/// initializing a parameter for which there is no declaration.
|
||||
InitializedEntity(EntityKind Kind, SourceLocation Loc, QualType Type)
|
||||
: Kind(Kind), Parent(0), Type(Type), Location(Loc.getRawEncoding()) { }
|
||||
InitializedEntity(EntityKind Kind, SourceLocation Loc, QualType Type,
|
||||
bool NRVO = false)
|
||||
: Kind(Kind), Parent(0), Type(Type)
|
||||
{
|
||||
LocAndNRVO.Location = Loc.getRawEncoding();
|
||||
LocAndNRVO.NRVO = NRVO;
|
||||
}
|
||||
|
||||
/// \brief Create the initialization entity for a member subobject.
|
||||
InitializedEntity(FieldDecl *Member, const InitializedEntity *Parent)
|
||||
|
@ -152,14 +162,14 @@ public:
|
|||
|
||||
/// \brief Create the initialization entity for the result of a function.
|
||||
static InitializedEntity InitializeResult(SourceLocation ReturnLoc,
|
||||
QualType Type) {
|
||||
return InitializedEntity(EK_Result, ReturnLoc, Type);
|
||||
QualType Type, bool NRVO) {
|
||||
return InitializedEntity(EK_Result, ReturnLoc, Type, NRVO);
|
||||
}
|
||||
|
||||
/// \brief Create the initialization entity for an exception object.
|
||||
static InitializedEntity InitializeException(SourceLocation ThrowLoc,
|
||||
QualType Type) {
|
||||
return InitializedEntity(EK_Exception, ThrowLoc, Type);
|
||||
QualType Type, bool NRVO) {
|
||||
return InitializedEntity(EK_Exception, ThrowLoc, Type, NRVO);
|
||||
}
|
||||
|
||||
/// \brief Create the initialization entity for an object allocated via new.
|
||||
|
@ -208,6 +218,10 @@ public:
|
|||
/// initialized.
|
||||
DeclaratorDecl *getDecl() const;
|
||||
|
||||
/// \brief Determine whether this initialization allows the named return
|
||||
/// value optimization, which also applies to thrown objects.
|
||||
bool allowsNRVO() const;
|
||||
|
||||
/// \brief Retrieve the base specifier.
|
||||
CXXBaseSpecifier *getBaseSpecifier() const {
|
||||
assert(getKind() == EK_Base && "Not a base specifier");
|
||||
|
@ -224,14 +238,14 @@ public:
|
|||
/// the result of a function call.
|
||||
SourceLocation getReturnLoc() const {
|
||||
assert(getKind() == EK_Result && "No 'return' location!");
|
||||
return SourceLocation::getFromRawEncoding(Location);
|
||||
return SourceLocation::getFromRawEncoding(LocAndNRVO.Location);
|
||||
}
|
||||
|
||||
/// \brief Determine the location of the 'throw' keyword when initializing
|
||||
/// an exception object.
|
||||
SourceLocation getThrowLoc() const {
|
||||
assert(getKind() == EK_Exception && "No 'throw' location!");
|
||||
return SourceLocation::getFromRawEncoding(Location);
|
||||
return SourceLocation::getFromRawEncoding(LocAndNRVO.Location);
|
||||
}
|
||||
|
||||
/// \brief If this is already the initializer for an array or vector
|
||||
|
|
|
@ -445,8 +445,9 @@ Sema::DeclPtrTy Sema::ActOnPropertyImplDecl(Scope *S,
|
|||
SelfExpr, true, true);
|
||||
OwningExprResult Res =
|
||||
PerformCopyInitialization(InitializedEntity::InitializeResult(
|
||||
SourceLocation(),
|
||||
getterMethod->getResultType()),
|
||||
SourceLocation(),
|
||||
getterMethod->getResultType(),
|
||||
/*NRVO=*/false),
|
||||
SourceLocation(),
|
||||
Owned(IvarRefExpr));
|
||||
if (!Res.isInvalid()) {
|
||||
|
|
|
@ -1058,6 +1058,31 @@ Sema::ActOnBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
|
|||
return Owned(new (Context) BreakStmt(BreakLoc));
|
||||
}
|
||||
|
||||
/// IsReturnCopyElidable - Whether returning @p RetExpr from a function that
|
||||
/// returns a @p RetType fulfills the criteria for copy elision (C++0x 12.8p34).
|
||||
static bool IsReturnCopyElidable(ASTContext &Ctx, QualType RetType,
|
||||
Expr *RetExpr) {
|
||||
QualType ExprType = RetExpr->getType();
|
||||
// - in a return statement in a function with ...
|
||||
// ... a class return type ...
|
||||
if (!RetType->isRecordType())
|
||||
return false;
|
||||
// ... the same cv-unqualified type as the function return type ...
|
||||
if (!Ctx.hasSameUnqualifiedType(RetType, ExprType))
|
||||
return false;
|
||||
// ... the expression is the name of a non-volatile automatic object ...
|
||||
// We ignore parentheses here.
|
||||
// FIXME: Is this compliant? (Everyone else does it)
|
||||
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetExpr->IgnoreParens());
|
||||
if (!DR)
|
||||
return false;
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
|
||||
if (!VD)
|
||||
return false;
|
||||
return VD->getKind() == Decl::Var && VD->hasLocalStorage() &&
|
||||
!VD->getType()->isReferenceType() && !VD->getType().isVolatileQualified();
|
||||
}
|
||||
|
||||
/// ActOnBlockReturnStmt - Utility routine to figure out block's return type.
|
||||
///
|
||||
Action::OwningStmtResult
|
||||
|
@ -1115,7 +1140,10 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
|||
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
||||
OwningExprResult Res = PerformCopyInitialization(
|
||||
InitializedEntity::InitializeResult(ReturnLoc,
|
||||
FnRetType),
|
||||
FnRetType,
|
||||
IsReturnCopyElidable(Context,
|
||||
FnRetType,
|
||||
RetValExp)),
|
||||
SourceLocation(),
|
||||
Owned(RetValExp));
|
||||
if (Res.isInvalid()) {
|
||||
|
@ -1131,31 +1159,6 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
|
|||
return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
|
||||
}
|
||||
|
||||
/// IsReturnCopyElidable - Whether returning @p RetExpr from a function that
|
||||
/// returns a @p RetType fulfills the criteria for copy elision (C++0x 12.8p15).
|
||||
static bool IsReturnCopyElidable(ASTContext &Ctx, QualType RetType,
|
||||
Expr *RetExpr) {
|
||||
QualType ExprType = RetExpr->getType();
|
||||
// - in a return statement in a function with ...
|
||||
// ... a class return type ...
|
||||
if (!RetType->isRecordType())
|
||||
return false;
|
||||
// ... the same cv-unqualified type as the function return type ...
|
||||
if (!Ctx.hasSameUnqualifiedType(RetType, ExprType))
|
||||
return false;
|
||||
// ... the expression is the name of a non-volatile automatic object ...
|
||||
// We ignore parentheses here.
|
||||
// FIXME: Is this compliant?
|
||||
const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(RetExpr->IgnoreParens());
|
||||
if (!DR)
|
||||
return false;
|
||||
const VarDecl *VD = dyn_cast<VarDecl>(DR->getDecl());
|
||||
if (!VD)
|
||||
return false;
|
||||
return VD->hasLocalStorage() && !VD->getType()->isReferenceType()
|
||||
&& !VD->getType().isVolatileQualified();
|
||||
}
|
||||
|
||||
Action::OwningStmtResult
|
||||
Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
|
||||
Expr *RetValExp = rex.takeAs<Expr>();
|
||||
|
@ -1214,29 +1217,18 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
|
|||
// overlap restriction of subclause 6.5.16.1 does not apply to the case of
|
||||
// function return.
|
||||
|
||||
// C++0x 12.8p15: When certain criteria are met, an implementation is
|
||||
// allowed to omit the copy construction of a class object, [...]
|
||||
// - in a return statement in a function with a class return type, when
|
||||
// the expression is the name of a non-volatile automatic object with
|
||||
// the same cv-unqualified type as the function return type, the copy
|
||||
// operation can be omitted [...]
|
||||
// C++0x 12.8p16: When the criteria for elision of a copy operation are met
|
||||
// and the object to be copied is designated by an lvalue, overload
|
||||
// resolution to select the constructor for the copy is first performed
|
||||
// as if the object were designated by an rvalue.
|
||||
// Note that we only compute Elidable if we're in C++0x, since we don't
|
||||
// care otherwise.
|
||||
bool Elidable = getLangOptions().CPlusPlus0x ?
|
||||
IsReturnCopyElidable(Context, FnRetType, RetValExp) :
|
||||
false;
|
||||
// FIXME: Elidable
|
||||
(void)Elidable;
|
||||
|
||||
// C++0x [class.copy]p34:
|
||||
//
|
||||
|
||||
|
||||
// In C++ the return statement is handled via a copy initialization.
|
||||
// the C version of which boils down to CheckSingleAssignmentConstraints.
|
||||
OwningExprResult Res = PerformCopyInitialization(
|
||||
InitializedEntity::InitializeResult(ReturnLoc,
|
||||
FnRetType),
|
||||
FnRetType,
|
||||
IsReturnCopyElidable(Context,
|
||||
FnRetType,
|
||||
RetValExp)),
|
||||
SourceLocation(),
|
||||
Owned(RetValExp));
|
||||
if (Res.isInvalid()) {
|
||||
|
|
Загрузка…
Ссылка в новой задаче