[cfe-commits] r103867 - in /cfe/trunk: lib/CodeGen/CGDecl.cpp lib/CodeGen/CGStmt.cpp lib/Sema/SemaStmt.cpp test/CodeGenCXX/nrvo.cpp

Douglas Gregor dgregor at apple.com
Sat May 15 08:50:12 PDT 2010


On May 14, 2010, at 11:46 PM, Douglas Gregor wrote:

> Author: dgregor
> Date: Sat May 15 01:46:45 2010
> New Revision: 103867
> 
> URL: http://llvm.org/viewvc/llvm-project?rev=103867&view=rev
> Log:
> 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.
> 
> Added:
>    cfe/trunk/test/CodeGenCXX/nrvo.cpp   (with props)
> Modified:
>    cfe/trunk/lib/CodeGen/CGDecl.cpp
>    cfe/trunk/lib/CodeGen/CGStmt.cpp
>    cfe/trunk/lib/Sema/SemaStmt.cpp
> 
> Modified: cfe/trunk/lib/CodeGen/CGDecl.cpp
> URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/CodeGen/CGDecl.cpp?rev=103867&r1=103866&r2=103867&view=diff
> ==============================================================================
> --- cfe/trunk/lib/CodeGen/CGDecl.cpp (original)
> +++ cfe/trunk/lib/CodeGen/CGDecl.cpp Sat May 15 01:46:45 2010
> @@ -398,16 +398,19 @@
>   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 @@
>         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 @@
>   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");

And... this is broken. We need to destroy the local variable if an exception is thrown, but not if we return. Investigating a fix.

	- Doug



More information about the cfe-commits mailing list