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()); + + 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; + 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()) - if (CXXRecordDecl *ClassDecl = dyn_cast(RT->getDecl())) { - llvm::Value *Loc = DeclPtr; - if (isByRef) - Loc = Builder.CreateStructGEP(DeclPtr, getByRefValueLLVMField(&D), - D.getNameAsString()); - - if (!ClassDecl->hasTrivialDestructor()) { + if (CXXRecordDecl *ClassDecl = dyn_cast(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() && + !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; +}