aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/clang/AST/Decl.h19
-rw-r--r--include/clang/AST/Stmt.h18
-rw-r--r--lib/CodeGen/CGObjC.cpp3
-rw-r--r--lib/Frontend/PCHReaderDecl.cpp1
-rw-r--r--lib/Frontend/PCHReaderStmt.cpp1
-rw-r--r--lib/Frontend/PCHWriterDecl.cpp2
-rw-r--r--lib/Frontend/PCHWriterStmt.cpp1
-rw-r--r--lib/Sema/Sema.cpp1
-rw-r--r--lib/Sema/Sema.h5
-rw-r--r--lib/Sema/SemaDecl.cpp35
-rw-r--r--lib/Sema/SemaStmt.cpp181
11 files changed, 187 insertions, 80 deletions
diff --git a/include/clang/AST/Decl.h b/include/clang/AST/Decl.h
index 7b2300bc4e..a7c947afe6 100644
--- a/include/clang/AST/Decl.h
+++ b/include/clang/AST/Decl.h
@@ -519,6 +519,10 @@ private:
/// or an Objective-C @catch statement.
bool ExceptionVar : 1;
+ /// \brief Whether this local variable could be allocated in the return
+ /// slot of its function, enabling the named return value optimization (NRVO).
+ bool NRVOVariable : 1;
+
friend class StmtIteratorBase;
protected:
VarDecl(Kind DK, DeclContext *DC, SourceLocation L, IdentifierInfo *Id,
@@ -526,7 +530,7 @@ protected:
StorageClass SCAsWritten)
: DeclaratorDecl(DK, DC, L, Id, T, TInfo), Init(),
ThreadSpecified(false), HasCXXDirectInit(false),
- DeclaredInCondition(false), ExceptionVar(false) {
+ DeclaredInCondition(false), ExceptionVar(false), NRVOVariable(false) {
SClass = SC;
SClassAsWritten = SCAsWritten;
}
@@ -869,6 +873,19 @@ public:
}
void setExceptionVariable(bool EV) { ExceptionVar = EV; }
+ /// \brief Determine whether this local variable can be used with the named
+ /// return value optimization (NRVO).
+ ///
+ /// The named return value optimization (NRVO) works by marking certain
+ /// non-volatile local variables of class type as NRVO objects. These
+ /// locals can be allocated within the return slot of their containing
+ /// function, in which case there is no need to copy the object to the
+ /// return slot when returning from the function. Within the function body,
+ /// each return that returns the NRVO object will have this variable as its
+ /// NRVO candidate.
+ bool isNRVOVariable() const { return NRVOVariable; }
+ void setNRVOVariable(bool NRVO) { NRVOVariable = NRVO; }
+
/// \brief If this variable is an instantiated static data member of a
/// class template specialization, returns the templated static data member
/// from which it was instantiated.
diff --git a/include/clang/AST/Stmt.h b/include/clang/AST/Stmt.h
index bae6da00f5..f95c35a126 100644
--- a/include/clang/AST/Stmt.h
+++ b/include/clang/AST/Stmt.h
@@ -1096,9 +1096,15 @@ public:
class ReturnStmt : public Stmt {
Stmt *RetExpr;
SourceLocation RetLoc;
+ const VarDecl *NRVOCandidate;
+
public:
- ReturnStmt(SourceLocation RL, Expr *E = 0) : Stmt(ReturnStmtClass),
- RetExpr((Stmt*) E), RetLoc(RL) {}
+ ReturnStmt(SourceLocation RL)
+ : Stmt(ReturnStmtClass), RetExpr(0), NRVOCandidate(0) { }
+
+ ReturnStmt(SourceLocation RL, Expr *E, const VarDecl *NRVOCandidate)
+ : Stmt(ReturnStmtClass), RetExpr((Stmt*) E), RetLoc(RL),
+ NRVOCandidate(NRVOCandidate) {}
/// \brief Build an empty return expression.
explicit ReturnStmt(EmptyShell Empty) : Stmt(ReturnStmtClass, Empty) { }
@@ -1110,6 +1116,14 @@ public:
SourceLocation getReturnLoc() const { return RetLoc; }
void setReturnLoc(SourceLocation L) { RetLoc = L; }
+ /// \brief Retrieve the variable that might be used for the named return
+ /// value optimization.
+ ///
+ /// The optimization itself can only be performed if the variable is
+ /// also marked as an NRVO object.
+ const VarDecl *getNRVOCandidate() const { return NRVOCandidate; }
+ void setNRVOCandidate(const VarDecl *Var) { NRVOCandidate = Var; }
+
virtual SourceRange getSourceRange() const;
static bool classof(const Stmt *T) {
diff --git a/lib/CodeGen/CGObjC.cpp b/lib/CodeGen/CGObjC.cpp
index b3e9dc5251..df0263ee12 100644
--- a/lib/CodeGen/CGObjC.cpp
+++ b/lib/CodeGen/CGObjC.cpp
@@ -255,7 +255,8 @@ void CodeGenFunction::GenerateObjCGetter(ObjCImplementationDecl *IMP,
if (PID->getGetterCXXConstructor()) {
ReturnStmt *Stmt =
new (getContext()) ReturnStmt(SourceLocation(),
- PID->getGetterCXXConstructor());
+ PID->getGetterCXXConstructor(),
+ 0);
EmitReturnStmt(*Stmt);
}
else {
diff --git a/lib/Frontend/PCHReaderDecl.cpp b/lib/Frontend/PCHReaderDecl.cpp
index 3f91d0cafb..49010bf0d6 100644
--- a/lib/Frontend/PCHReaderDecl.cpp
+++ b/lib/Frontend/PCHReaderDecl.cpp
@@ -435,6 +435,7 @@ void PCHDeclReader::VisitVarDecl(VarDecl *VD) {
VD->setCXXDirectInitializer(Record[Idx++]);
VD->setDeclaredInCondition(Record[Idx++]);
VD->setExceptionVariable(Record[Idx++]);
+ VD->setNRVOVariable(Record[Idx++]);
VD->setPreviousDeclaration(
cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
if (Record[Idx++])
diff --git a/lib/Frontend/PCHReaderStmt.cpp b/lib/Frontend/PCHReaderStmt.cpp
index 7595d03a15..3931adbe8f 100644
--- a/lib/Frontend/PCHReaderStmt.cpp
+++ b/lib/Frontend/PCHReaderStmt.cpp
@@ -291,6 +291,7 @@ unsigned PCHStmtReader::VisitReturnStmt(ReturnStmt *S) {
VisitStmt(S);
S->setRetValue(cast_or_null<Expr>(StmtStack.back()));
S->setReturnLoc(SourceLocation::getFromRawEncoding(Record[Idx++]));
+ S->setNRVOCandidate(cast_or_null<VarDecl>(Reader.GetDecl(Record[Idx++])));
return 1;
}
diff --git a/lib/Frontend/PCHWriterDecl.cpp b/lib/Frontend/PCHWriterDecl.cpp
index 28a10ad6b0..cc58e8ee46 100644
--- a/lib/Frontend/PCHWriterDecl.cpp
+++ b/lib/Frontend/PCHWriterDecl.cpp
@@ -415,6 +415,7 @@ void PCHDeclWriter::VisitVarDecl(VarDecl *D) {
Record.push_back(D->hasCXXDirectInitializer());
Record.push_back(D->isDeclaredInCondition());
Record.push_back(D->isExceptionVariable());
+ Record.push_back(D->isNRVOVariable());
Writer.AddDeclRef(D->getPreviousDeclaration(), Record);
Record.push_back(D->getInit() ? 1 : 0);
if (D->getInit())
@@ -683,6 +684,7 @@ void PCHWriter::WriteDeclsBlockAbbrevs() {
Abv->Add(BitCodeAbbrevOp(0)); // hasCXXDirectInitializer
Abv->Add(BitCodeAbbrevOp(0)); // isDeclaredInCondition
Abv->Add(BitCodeAbbrevOp(0)); // isExceptionVariable
+ Abv->Add(BitCodeAbbrevOp(0)); // isNRVOVariable
Abv->Add(BitCodeAbbrevOp(0)); // PrevDecl
Abv->Add(BitCodeAbbrevOp(0)); // HasInit
// ParmVarDecl
diff --git a/lib/Frontend/PCHWriterStmt.cpp b/lib/Frontend/PCHWriterStmt.cpp
index a7f73a17fa..a9ee43527c 100644
--- a/lib/Frontend/PCHWriterStmt.cpp
+++ b/lib/Frontend/PCHWriterStmt.cpp
@@ -275,6 +275,7 @@ void PCHStmtWriter::VisitReturnStmt(ReturnStmt *S) {
VisitStmt(S);
Writer.WriteSubStmt(S->getRetValue());
Writer.AddSourceLocation(S->getReturnLoc(), Record);
+ Writer.AddDeclRef(S->getNRVOCandidate(), Record);
Code = pch::STMT_RETURN;
}
diff --git a/lib/Sema/Sema.cpp b/lib/Sema/Sema.cpp
index 569de3203a..b9aa61144a 100644
--- a/lib/Sema/Sema.cpp
+++ b/lib/Sema/Sema.cpp
@@ -33,6 +33,7 @@ void FunctionScopeInfo::Clear(unsigned NumErrors) {
NeedsScopeChecking = false;
LabelMap.clear();
SwitchStack.clear();
+ Returns.clear();
NumErrorsAtStartOfFunction = NumErrors;
}
diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h
index d97dc67682..3e143bb3b0 100644
--- a/lib/Sema/Sema.h
+++ b/lib/Sema/Sema.h
@@ -134,6 +134,11 @@ struct FunctionScopeInfo {
/// block.
llvm::SmallVector<SwitchStmt*, 8> SwitchStack;
+ /// \brief The list of return statements that occur within the function or
+ /// block, if there is any chance of applying the named return value
+ /// optimization.
+ llvm::SmallVector<ReturnStmt *, 4> Returns;
+
FunctionScopeInfo(unsigned NumErrors)
: IsBlockInfo(false), NeedsScopeChecking(false),
NumErrorsAtStartOfFunction(NumErrors) { }
diff --git a/lib/Sema/SemaDecl.cpp b/lib/Sema/SemaDecl.cpp
index 5bb0b524d1..fb3100353b 100644
--- a/lib/Sema/SemaDecl.cpp
+++ b/lib/Sema/SemaDecl.cpp
@@ -4548,6 +4548,38 @@ Sema::DeclPtrTy Sema::ActOnStartOfFunctionDef(Scope *FnBodyScope, DeclPtrTy D) {
return DeclPtrTy::make(FD);
}
+/// \brief Given the set of return statements within a function body,
+/// compute the variables that are subject to the named return value
+/// optimization.
+///
+/// Each of the variables that is subject to the named return value
+/// optimization will be marked as NRVO variables in the AST, and any
+/// return statement that has a marked NRVO variable as its NRVO candidate can
+/// use the named return value optimization.
+///
+/// This function applies a very simplistic algorithm for NRVO: if every return
+/// statement in the function has the same NRVO candidate, that candidate is
+/// the NRVO variable.
+///
+/// FIXME: Employ a smarter algorithm that accounts for multiple return
+/// statements and the lifetimes of the NRVO candidates. We should be able to
+/// find a maximal set of NRVO variables.
+static void ComputeNRVO(Stmt *Body, ReturnStmt **Returns, unsigned NumReturns) {
+ const VarDecl *NRVOCandidate = 0;
+ for (unsigned I = 0; I != NumReturns; ++I) {
+ if (!Returns[I]->getNRVOCandidate())
+ return;
+
+ if (!NRVOCandidate)
+ NRVOCandidate = Returns[I]->getNRVOCandidate();
+ else if (NRVOCandidate != Returns[I]->getNRVOCandidate())
+ return;
+ }
+
+ if (NRVOCandidate)
+ const_cast<VarDecl*>(NRVOCandidate)->setNRVOVariable(true);
+}
+
Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg) {
return ActOnFinishFunctionBody(D, move(BodyArg), false);
}
@@ -4581,6 +4613,9 @@ Sema::DeclPtrTy Sema::ActOnFinishFunctionBody(DeclPtrTy D, StmtArg BodyArg,
// If this is a constructor, we need a vtable.
if (CXXConstructorDecl *Constructor = dyn_cast<CXXConstructorDecl>(FD))
MarkVTableUsed(FD->getLocation(), Constructor->getParent());
+
+ ComputeNRVO(Body, FunctionScopes.back()->Returns.data(),
+ FunctionScopes.back()->Returns.size());
}
assert(FD == getCurFunctionDecl() && "Function parsing confused");
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index d9210f6a1b..f8fe4fe21c 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -1058,29 +1058,42 @@ 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) {
+/// \brief Determine whether a return statement is a candidate for the named
+/// return value optimization (C++0x 12.8p34, bullet 1).
+///
+/// \param Ctx The context in which the return expression and type occur.
+///
+/// \param RetType The return type of the function or block.
+///
+/// \param RetExpr The expression being returned from the function or block.
+///
+/// \returns The NRVO candidate variable, if the return statement may use the
+/// NRVO, or NULL if there is no such candidate.
+static const VarDecl *getNRVOCandidate(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;
+ return 0;
// ... the same cv-unqualified type as the function return type ...
if (!Ctx.hasSameUnqualifiedType(RetType, ExprType))
- return false;
+ return 0;
// ... 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;
+ return 0;
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();
+ return 0;
+
+ if (VD->getKind() == Decl::Var && VD->hasLocalStorage() &&
+ !VD->getType()->isReferenceType() && !VD->getType().isVolatileQualified())
+ return VD;
+
+ return 0;
}
/// ActOnBlockReturnStmt - Utility routine to figure out block's return type.
@@ -1117,46 +1130,58 @@ Sema::ActOnBlockReturnStmt(SourceLocation ReturnLoc, Expr *RetValExp) {
// Otherwise, verify that this result type matches the previous one. We are
// pickier with blocks than for normal functions because we don't have GCC
// compatibility to worry about here.
+ ReturnStmt *Result = 0;
if (CurBlock->ReturnType->isVoidType()) {
if (RetValExp) {
Diag(ReturnLoc, diag::err_return_block_has_expr);
RetValExp->Destroy(Context);
RetValExp = 0;
}
- return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
- }
-
- if (!RetValExp)
+ Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0);
+ } else if (!RetValExp) {
return StmtError(Diag(ReturnLoc, diag::err_block_return_missing_expr));
+ } else {
+ const VarDecl *NRVOCandidate = 0;
+
+ if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
+ // we have a non-void block with an expression, continue checking
+
+ // C99 6.8.6.4p3(136): The return statement is not an assignment. The
+ // overlap restriction of subclause 6.5.16.1 does not apply to the case of
+ // function return.
+
+ // In C++ the return statement is handled via a copy initialization.
+ // the C version of which boils down to CheckSingleAssignmentConstraints.
+ NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
+ OwningExprResult Res = PerformCopyInitialization(
+ InitializedEntity::InitializeResult(ReturnLoc,
+ FnRetType,
+ NRVOCandidate != 0),
+ SourceLocation(),
+ Owned(RetValExp));
+ if (Res.isInvalid()) {
+ // FIXME: Cleanup temporaries here, anyway?
+ return StmtError();
+ }
+
+ if (RetValExp)
+ RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
- if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
- // we have a non-void block with an expression, continue checking
-
- // C99 6.8.6.4p3(136): The return statement is not an assignment. The
- // overlap restriction of subclause 6.5.16.1 does not apply to the case of
- // function return.
-
- // 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,
- IsReturnCopyElidable(Context,
- FnRetType,
- RetValExp)),
- SourceLocation(),
- Owned(RetValExp));
- if (Res.isInvalid()) {
- // FIXME: Cleanup temporaries here, anyway?
- return StmtError();
+ RetValExp = Res.takeAs<Expr>();
+ if (RetValExp)
+ CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
}
- RetValExp = Res.takeAs<Expr>();
- if (RetValExp)
- CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
+ Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, NRVOCandidate);
}
- return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
+ // If we need to check for the named return value optimization, save the
+ // return statement in our scope for later processing.
+ if (getLangOptions().CPlusPlus && FnRetType->isRecordType() &&
+ !CurContext->isDependentContext())
+ FunctionScopes.back()->Returns.push_back(Result);
+
+ return Owned(Result);
}
Action::OwningStmtResult
@@ -1177,6 +1202,7 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
else // If we don't have a function/method context, bail.
return StmtError();
+ ReturnStmt *Result = 0;
if (FnRetType->isVoidType()) {
if (RetValExp && !RetValExp->isTypeDependent()) {
// C99 6.8.6.4p1 (ext_ since GCC warns)
@@ -1195,10 +1221,9 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
}
- return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
- }
-
- if (!RetValExp && !FnRetType->isDependentType()) {
+
+ Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, 0);
+ } else if (!RetValExp && !FnRetType->isDependentType()) {
unsigned DiagID = diag::warn_return_missing_expr; // C90 6.6.6.4p4
// C99 6.8.6.4p1 (ext_ since GCC warns)
if (getLangOptions().C99) DiagID = diag::ext_return_missing_expr;
@@ -1207,43 +1232,47 @@ Sema::ActOnReturnStmt(SourceLocation ReturnLoc, ExprArg rex) {
Diag(ReturnLoc, DiagID) << FD->getIdentifier() << 0/*fn*/;
else
Diag(ReturnLoc, DiagID) << getCurMethodDecl()->getDeclName() << 1/*meth*/;
- return Owned(new (Context) ReturnStmt(ReturnLoc, (Expr*)0));
- }
-
- if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
- // we have a non-void function with an expression, continue checking
-
- // C99 6.8.6.4p3(136): The return statement is not an assignment. The
- // overlap restriction of subclause 6.5.16.1 does not apply to the case of
- // function return.
+ Result = new (Context) ReturnStmt(ReturnLoc);
+ } else {
+ const VarDecl *NRVOCandidate = 0;
+ if (!FnRetType->isDependentType() && !RetValExp->isTypeDependent()) {
+ // we have a non-void function with an expression, continue checking
+
+ // C99 6.8.6.4p3(136): The return statement is not an assignment. The
+ // overlap restriction of subclause 6.5.16.1 does not apply to the case of
+ // function return.
+
+ // In C++ the return statement is handled via a copy initialization.
+ // the C version of which boils down to CheckSingleAssignmentConstraints.
+ NRVOCandidate = getNRVOCandidate(Context, FnRetType, RetValExp);
+ OwningExprResult Res = PerformCopyInitialization(
+ InitializedEntity::InitializeResult(ReturnLoc,
+ FnRetType,
+ NRVOCandidate != 0),
+ SourceLocation(),
+ Owned(RetValExp));
+ if (Res.isInvalid()) {
+ // FIXME: Cleanup temporaries here, anyway?
+ return StmtError();
+ }
- // 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,
- IsReturnCopyElidable(Context,
- FnRetType,
- RetValExp)),
- SourceLocation(),
- Owned(RetValExp));
- if (Res.isInvalid()) {
- // FIXME: Cleanup temporaries here, anyway?
- return StmtError();
+ RetValExp = Res.takeAs<Expr>();
+ if (RetValExp)
+ CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
}
-
- RetValExp = Res.takeAs<Expr>();
- if (RetValExp)
- CheckReturnStackAddr(RetValExp, FnRetType, ReturnLoc);
+
+ if (RetValExp)
+ RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
+ Result = new (Context) ReturnStmt(ReturnLoc, RetValExp, NRVOCandidate);
}
-
- if (RetValExp)
- RetValExp = MaybeCreateCXXExprWithTemporaries(RetValExp);
- return Owned(new (Context) ReturnStmt(ReturnLoc, RetValExp));
+
+ // If we need to check for the named return value optimization, save the
+ // return statement in our scope for later processing.
+ if (getLangOptions().CPlusPlus && FnRetType->isRecordType() &&
+ !CurContext->isDependentContext())
+ FunctionScopes.back()->Returns.push_back(Result);
+
+ return Owned(Result);
}
/// CheckAsmLValue - GNU C has an extremely ugly extension whereby they silently