Index: lib/CodeGen/CGDecl.cpp =================================================================== --- lib/CodeGen/CGDecl.cpp +++ lib/CodeGen/CGDecl.cpp @@ -467,13 +467,11 @@ } }; - struct DestroyNRVOVariable final : EHScopeStack::Cleanup { - DestroyNRVOVariable(Address addr, - const CXXDestructorDecl *Dtor, - llvm::Value *NRVOFlag) - : Dtor(Dtor), NRVOFlag(NRVOFlag), Loc(addr) {} + template + struct DestroyNRVOVariable : EHScopeStack::Cleanup { + DestroyNRVOVariable(Address addr, llvm::Value *NRVOFlag) + : NRVOFlag(NRVOFlag), Loc(addr) {} - const CXXDestructorDecl *Dtor; llvm::Value *NRVOFlag; Address Loc; @@ -492,12 +490,39 @@ CGF.EmitBlock(RunDtorBB); } + static_cast(this)->emitDestructorCall(CGF); + + if (NRVO) CGF.EmitBlock(SkipDtorBB); + } + + virtual ~DestroyNRVOVariable() = default; + }; + + struct DestroyNRVOVariableCXX final + : DestroyNRVOVariable { + DestroyNRVOVariableCXX(Address addr, const CXXDestructorDecl *Dtor, + llvm::Value *NRVOFlag) + : DestroyNRVOVariable(addr, NRVOFlag), + Dtor(Dtor) {} + + const CXXDestructorDecl *Dtor; + + void emitDestructorCall(CodeGenFunction &CGF) { CGF.EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false, - /*Delegating=*/false, - Loc); + /*Delegating=*/false, Loc); + } + }; - if (NRVO) CGF.EmitBlock(SkipDtorBB); + struct DestroyNRVOVariableC final + : DestroyNRVOVariable { + DestroyNRVOVariableC(Address addr, llvm::Value *NRVOFlag, QualType Ty) + : DestroyNRVOVariable(addr, NRVOFlag), Ty(Ty) {} + + QualType Ty; + + void emitDestructorCall(CodeGenFunction &CGF) { + CGF.destroyNonTrivialCStruct(CGF, Loc, Ty); } }; @@ -1088,7 +1113,10 @@ address = ReturnValue; if (const RecordType *RecordTy = Ty->getAs()) { - if (!cast(RecordTy->getDecl())->hasTrivialDestructor()) { + const auto *RD = cast(RecordTy->getDecl()); + const auto *CXXRD = dyn_cast(RD); + if ((CXXRD && !CXXRD->hasTrivialDestructor()) || + RD->isNonTrivialToPrimitiveCopy()) { // Create a flag that is used to indicate when the NRVO was applied // to this variable. Set it to zero to indicate that NRVO was not // applied. @@ -1461,8 +1489,8 @@ if (emission.NRVOFlag) { assert(!type->isArrayType()); CXXDestructorDecl *dtor = type->getAsCXXRecordDecl()->getDestructor(); - EHStack.pushCleanup(cleanupKind, addr, - dtor, emission.NRVOFlag); + EHStack.pushCleanup(cleanupKind, addr, dtor, + emission.NRVOFlag); return; } break; @@ -1484,6 +1512,12 @@ case QualType::DK_nontrivial_c_struct: destroyer = CodeGenFunction::destroyNonTrivialCStruct; + if (emission.NRVOFlag) { + assert(!type->isArrayType()); + EHStack.pushCleanup(cleanupKind, addr, + emission.NRVOFlag, type); + return; + } break; } Index: lib/Sema/SemaDecl.cpp =================================================================== --- lib/Sema/SemaDecl.cpp +++ lib/Sema/SemaDecl.cpp @@ -12712,8 +12712,10 @@ // Try to apply the named return value optimization. We have to check // if we can do this here because lambdas keep return statements around // to deduce an implicit return type. - if (getLangOpts().CPlusPlus && FD->getReturnType()->isRecordType() && - !FD->isDependentContext()) + QualType RetTy = FD->getReturnType(); + if (RetTy->isRecordType() && + ((getLangOpts().CPlusPlus && !FD->isDependentContext()) || + RetTy.isNonTrivialToPrimitiveCopy())) computeNRVO(Body, getCurFunction()); } Index: lib/Sema/SemaStmt.cpp =================================================================== --- lib/Sema/SemaStmt.cpp +++ lib/Sema/SemaStmt.cpp @@ -2872,7 +2872,7 @@ /// NRVO, or NULL if there is no such candidate. VarDecl *Sema::getCopyElisionCandidate(QualType ReturnType, Expr *E, CopyElisionSemanticsKind CESK) { - if (!getLangOpts().CPlusPlus) + if (!getLangOpts().CPlusPlus && !ReturnType.isNonTrivialToPrimitiveCopy()) return nullptr; // - in a return statement in a function [where] ... Index: test/CodeGenObjC/objc-non-trivial-struct-nrvo.m =================================================================== --- /dev/null +++ test/CodeGenObjC/objc-non-trivial-struct-nrvo.m @@ -0,0 +1,111 @@ +// RUN: %clang_cc1 -triple x86_64-apple-darwin10 -emit-llvm -fobjc-arc -fobjc-runtime-has-weak -o - %s | FileCheck %s + +// CHECK: %[[STRUCT_TRIVIAL:.*]] = type { i32 } +// CHECK: %[[STRUCT_STRONG:.*]] = type { i8* } +// CHECK: %[[STRUCT_WEAK:.*]] = type { i8* } + +typedef struct { + int x; +} Trivial; + +typedef struct { + id x; +} Strong; + +typedef struct { + __weak id x; +} Weak; + +// CHECK: define i32 @testTrivial() +// CHECK-NOT: br +// CHECK: ret i32 %{{.*}} + +Trivial testTrivial() { + Trivial a; + return a; +} + +// CHECK: define i8* @testStrong() +// CHECK: %[[RETVAL:.*]] = alloca %[[STRUCT_STRONG]], align 8 +// CHECK: %[[NRVO:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8** +// CHECK: call void @__default_constructor_8_s0(i8** %[[V0]]) +// CHECK: store i1 true, i1* %[[NRVO]], align 1 +// CHECK: %[[NRVO_VAL:.*]] = load i1, i1* %[[NRVO]], align 1 +// CHECK: br i1 %[[NRVO_VAL]], + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_STRONG]]* %[[RETVAL]] to i8** +// CHECK: call void @__destructor_8_s0(i8** %[[V1]]) +// CHECK: br + +// CHECK: %[[COERCE_DIVE:.*]] = getelementptr inbounds %[[STRUCT_STRONG]], %[[STRUCT_STRONG]]* %[[RETVAL]], i32 0, i32 0 +// CHECK: %[[V2:.*]] = load i8*, i8** %[[COERCE_DIVE]], align 8 +// CHECK: ret i8* %[[V2]] + +Strong testStrong() { + Strong a; + return a; +} + +// CHECK: define void @testWeak(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]]) +// CHECK: %[[NRVO:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// CHECK: call void @__default_constructor_8_w0(i8** %[[V0]]) +// CHECK: store i1 true, i1* %[[NRVO]], align 1 +// CHECK: %[[NRVO_VAL:.*]] = load i1, i1* %[[NRVO]], align 1 +// CHECK: br i1 %[[NRVO_VAL]], + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// CHECK: call void @__destructor_8_w0(i8** %[[V1]]) #3 +// CHECK: br + +// CHECK-NOT: call +// CHECK: ret void + +Weak testWeak() { + Weak a; + return a; +} + +// CHECK: define void @testWeak2( +// CHECK: call void @__default_constructor_8_w0( +// CHECK: call void @__default_constructor_8_w0( +// CHECK: call void @__copy_constructor_8_8_w0( +// CHECK: call void @__copy_constructor_8_8_w0( +// CHECK: call void @__destructor_8_w0( +// CHECK: call void @__destructor_8_w0( + +Weak testWeak2(int c) { + Weak a, b; + if (c) + return a; + else + return b; +} + +// CHECK: define internal void @"\01-[C1 foo1]"(%[[STRUCT_WEAK]]* noalias sret %[[AGG_RESULT:.*]], %{{.*}}* %{{.*}}, i8* %{{.*}}) +// CHECK: %[[NRVO:.*]] = alloca i1, align 1 +// CHECK: %[[V0:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// CHECK: call void @__default_constructor_8_w0(i8** %[[V0]]) +// CHECK: store i1 true, i1* %[[NRVO]], align 1 +// CHECK: %[[NRVO_VAL:.*]] = load i1, i1* %[[NRVO]], align 1 +// CHECK: br i1 %[[NRVO_VAL]], + +// CHECK: %[[V1:.*]] = bitcast %[[STRUCT_WEAK]]* %[[AGG_RESULT]] to i8** +// CHECK: call void @__destructor_8_w0(i8** %[[V1]]) +// CHECK: br + +// CHECK-NOT: call +// CHECK: ret void + +__attribute__((objc_root_class)) +@interface C1 +- (Weak)foo1; +@end + +@implementation C1 +- (Weak)foo1 { + Weak a; + return a; +} +@end