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:
Douglas Gregor 2010-05-15 00:13:29 +00:00
Родитель 67d438d39a
Коммит 3c9034cb7f
8 изменённых файлов: 117 добавлений и 74 удалений

Просмотреть файл

@ -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()) {