aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDouglas Gregor <dgregor@apple.com>2010-05-15 06:46:45 +0000
committerDouglas Gregor <dgregor@apple.com>2010-05-15 06:46:45 +0000
commitd86c477fb5d3fc34864afecbbb5443da9355e8fb (patch)
tree43c06bb0a10f4cce71fbd57c3b2d8d97e7acae20
parent5077c3876beeaed32280af88244e8050078619a8 (diff)
Implement a simple form of the C++ named return value optimization for
return statements. We perform NRVO only when all of the return statements in the function return the same variable. Fixes some link failures in Boost.Interprocess (which is relying on NRVO), and probably improves performance for some C++ applications. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@103867 91177308-0d34-0410-b5e6-96231b3b80d8
-rw-r--r--lib/CodeGen/CGDecl.cpp58
-rw-r--r--lib/CodeGen/CGStmt.cpp7
-rw-r--r--lib/Sema/SemaStmt.cpp3
-rw-r--r--test/CodeGenCXX/nrvo.cpp55
4 files changed, 99 insertions, 24 deletions
diff --git a/lib/CodeGen/CGDecl.cpp b/lib/CodeGen/CGDecl.cpp
index 48198ff576..c93d9ba33d 100644
--- a/lib/CodeGen/CGDecl.cpp
+++ b/lib/CodeGen/CGDecl.cpp
@@ -398,16 +398,19 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
CharUnits Align = CharUnits::Zero();
bool IsSimpleConstantInitializer = false;
+ bool NRVO = false;
llvm::Value *DeclPtr;
if (Ty->isConstantSizeType()) {
if (!Target.useGlobalsForAutomaticVariables()) {
-
+ NRVO = getContext().getLangOptions().ElideConstructors &&
+ D.isNRVOVariable();
// If this value is an array or struct, is POD, and if the initializer is
- // a staticly determinable constant, try to optimize it.
+ // a staticly determinable constant, try to optimize it (unless the NRVO
+ // is already optimizing this).
if (D.getInit() && !isByRef &&
(Ty->isArrayType() || Ty->isRecordType()) &&
Ty->isPODType() &&
- D.getInit()->isConstantInitializer(getContext())) {
+ D.getInit()->isConstantInitializer(getContext()) && !NRVO) {
// If this variable is marked 'const', emit the value as a global.
if (CGM.getCodeGenOpts().MergeAllConstants &&
Ty.isConstant(getContext())) {
@@ -418,19 +421,29 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
IsSimpleConstantInitializer = true;
}
- // A normal fixed sized variable becomes an alloca in the entry block.
+ // A normal fixed sized variable becomes an alloca in the entry block,
+ // unless it's an NRVO variable.
const llvm::Type *LTy = ConvertTypeForMem(Ty);
- if (isByRef)
- LTy = BuildByRefType(&D);
- llvm::AllocaInst *Alloc = CreateTempAlloca(LTy);
- Alloc->setName(D.getNameAsString());
-
- Align = getContext().getDeclAlign(&D);
- if (isByRef)
- Align = std::max(Align,
- CharUnits::fromQuantity(Target.getPointerAlign(0) / 8));
- Alloc->setAlignment(Align.getQuantity());
- DeclPtr = Alloc;
+
+ if (NRVO) {
+ // The named return value optimization: allocate this variable in the
+ // return slot, so that we can elide the copy when returning this
+ // variable (C++0x [class.copy]p34).
+ DeclPtr = ReturnValue;
+ } else {
+ if (isByRef)
+ LTy = BuildByRefType(&D);
+
+ llvm::AllocaInst *Alloc = CreateTempAlloca(LTy);
+ Alloc->setName(D.getNameAsString());
+
+ Align = getContext().getDeclAlign(&D);
+ if (isByRef)
+ Align = std::max(Align,
+ CharUnits::fromQuantity(Target.getPointerAlign(0) / 8));
+ Alloc->setAlignment(Align.getQuantity());
+ DeclPtr = Alloc;
+ }
} else {
// Targets that don't support recursion emit locals as globals.
const char *Class =
@@ -645,13 +658,14 @@ void CodeGenFunction::EmitLocalBlockVarDecl(const VarDecl &D) {
while (const ArrayType *Array = getContext().getAsArrayType(DtorTy))
DtorTy = getContext().getBaseElementType(Array);
if (const RecordType *RT = DtorTy->getAs<RecordType>())
- if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
- llvm::Value *Loc = DeclPtr;
- if (isByRef)
- Loc = Builder.CreateStructGEP(DeclPtr, getByRefValueLLVMField(&D),
- D.getNameAsString());
-
- if (!ClassDecl->hasTrivialDestructor()) {
+ if (CXXRecordDecl *ClassDecl = dyn_cast<CXXRecordDecl>(RT->getDecl())) {
+ if (!ClassDecl->hasTrivialDestructor() && !NRVO) {
+ // Note: We suppress the destructor call when this is an NRVO variable.
+ llvm::Value *Loc = DeclPtr;
+ if (isByRef)
+ Loc = Builder.CreateStructGEP(DeclPtr, getByRefValueLLVMField(&D),
+ D.getNameAsString());
+
const CXXDestructorDecl *D = ClassDecl->getDestructor(getContext());
assert(D && "EmitLocalBlockVarDecl - destructor is nul");
diff --git a/lib/CodeGen/CGStmt.cpp b/lib/CodeGen/CGStmt.cpp
index 772f640ed0..f51a899e7f 100644
--- a/lib/CodeGen/CGStmt.cpp
+++ b/lib/CodeGen/CGStmt.cpp
@@ -607,7 +607,12 @@ void CodeGenFunction::EmitReturnStmt(const ReturnStmt &S) {
// FIXME: Clean this up by using an LValue for ReturnTemp,
// EmitStoreThroughLValue, and EmitAnyExpr.
- if (!ReturnValue) {
+ if (S.getNRVOCandidate() && S.getNRVOCandidate()->isNRVOVariable() &&
+ !Target.useGlobalsForAutomaticVariables()) {
+ // Apply the named return value optimization for this return statement,
+ // which means doing nothing: the appropriate result has already been
+ // constructed into the NRVO variable.
+ } else if (!ReturnValue) {
// Make sure not to return anything, but evaluate the expression
// for side effects.
if (RV)
diff --git a/lib/Sema/SemaStmt.cpp b/lib/Sema/SemaStmt.cpp
index f8fe4fe21c..d904907e97 100644
--- a/lib/Sema/SemaStmt.cpp
+++ b/lib/Sema/SemaStmt.cpp
@@ -1090,7 +1090,8 @@ static const VarDecl *getNRVOCandidate(ASTContext &Ctx, QualType RetType,
return 0;
if (VD->getKind() == Decl::Var && VD->hasLocalStorage() &&
- !VD->getType()->isReferenceType() && !VD->getType().isVolatileQualified())
+ !VD->getType()->isReferenceType() && !VD->hasAttr<BlocksAttr>() &&
+ !VD->getType().isVolatileQualified())
return VD;
return 0;
diff --git a/test/CodeGenCXX/nrvo.cpp b/test/CodeGenCXX/nrvo.cpp
new file mode 100644
index 0000000000..7b4f20398d
--- /dev/null
+++ b/test/CodeGenCXX/nrvo.cpp
@@ -0,0 +1,55 @@
+// RUN: %clang_cc1 -emit-llvm -o - %s | FileCheck %s
+
+// Test code generation for the named return value optimization.
+class X {
+public:
+ X();
+ X(const X&);
+ ~X();
+};
+
+// CHECK: define void @_Z5test0v
+X test0() {
+ X x;
+ // CHECK-NOT: call void @_ZN1XD1Ev
+ // CHECK: ret void
+ return x;
+}
+
+// CHECK: define void @_Z5test1b(
+X test1(bool B) {
+ // CHECK: call void @_ZN1XC1Ev
+ X x;
+ // CHECK-NOT: call void @_ZN1XD1Ev
+ // CHECK: ret void
+ if (B)
+ return (x);
+ return x;
+}
+
+// CHECK: define void @_Z5test2b
+X test2(bool B) {
+ // No NRVO
+ // CHECK: call void @_ZN1XC1Ev
+ X x;
+ // CHECK: call void @_ZN1XC1Ev
+ X y;
+ // CHECK: call void @_ZN1XC1ERKS_
+ if (B)
+ return y;
+ // CHECK: call void @_ZN1XC1ERKS_
+ return x;
+ // CHECK: call void @_ZN1XD1Ev
+ // CHECK: call void @_ZN1XD1Ev
+ // CHECK: ret void
+}
+
+X test3(bool B) {
+ // FIXME: We don't manage to apply NRVO here, although we could.
+ {
+ X y;
+ return y;
+ }
+ X x;
+ return x;
+}