Index: cfe/trunk/lib/CodeGen/CGCXXABI.h =================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.h +++ cfe/trunk/lib/CodeGen/CGCXXABI.h @@ -112,6 +112,10 @@ RAA_Indirect }; + /// Returns true if C++ allows us to copy the memory of an object of type RD + /// when it is passed as an argument. + bool canCopyArgument(const CXXRecordDecl *RD) const; + /// Returns how an argument of the given record type should be passed. virtual RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const = 0; Index: cfe/trunk/lib/CodeGen/CGCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/CGCXXABI.cpp +++ cfe/trunk/lib/CodeGen/CGCXXABI.cpp @@ -28,6 +28,37 @@ << S; } +bool CGCXXABI::canCopyArgument(const CXXRecordDecl *RD) const { + // If RD has a non-trivial move or copy constructor, we cannot copy the + // argument. + if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialMoveConstructor()) + return false; + + // If RD has a non-trivial destructor, we cannot copy the argument. + if (RD->hasNonTrivialDestructor()) + return false; + + // We can only copy the argument if there exists at least one trivial, + // non-deleted copy or move constructor. + // FIXME: This assumes that all lazily declared copy and move constructors are + // not deleted. This assumption might not be true in some corner cases. + bool CopyOrMoveDeleted = false; + for (const CXXConstructorDecl *CD : RD->ctors()) { + if (CD->isCopyConstructor() || CD->isMoveConstructor()) { + assert(CD->isTrivial()); + // We had at least one undeleted trivial copy or move ctor. Return + // directly. + if (!CD->isDeleted()) + return true; + CopyOrMoveDeleted = true; + } + } + + // If all trivial copy and move constructors are deleted, we cannot copy the + // argument. + return !CopyOrMoveDeleted; +} + llvm::Constant *CGCXXABI::GetBogusMemberPointer(QualType T) { return llvm::Constant::getNullValue(CGM.getTypes().ConvertType(T)); } Index: cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp +++ cfe/trunk/lib/CodeGen/ItaniumCXXABI.cpp @@ -55,9 +55,8 @@ bool classifyReturnType(CGFunctionInfo &FI) const override; RecordArgABI getRecordArgABI(const CXXRecordDecl *RD) const override { - // Structures with either a non-trivial destructor or a non-trivial - // copy constructor are always indirect. - if (!RD->hasTrivialDestructor() || RD->hasNonTrivialCopyConstructor()) + // If C++ prohibits us from making a copy, pass by address. + if (!canCopyArgument(RD)) return RAA_Indirect; return RAA_Default; } @@ -762,13 +761,11 @@ if (!RD) return false; - // Return indirectly if we have a non-trivial copy ctor or non-trivial dtor. - if (RD->hasNonTrivialDestructor() || RD->hasNonTrivialCopyConstructor()) { + // If C++ prohibits us from making a copy, return by address. + if (!canCopyArgument(RD)) { FI.getReturnInfo() = ABIArgInfo::getIndirect(0, /*ByVal=*/false); return true; } - - // Otherwise, use the normal C ABI rules. return false; } Index: cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp =================================================================== --- cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp +++ cfe/trunk/lib/CodeGen/MicrosoftCXXABI.cpp @@ -404,19 +404,46 @@ return RAA_Default; case llvm::Triple::x86: - // 32-bit x86 constructs non-trivial objects directly in outgoing argument - // slots. LLVM uses the inalloca attribute to implement this. - if (RD->hasNonTrivialCopyConstructor() || RD->hasNonTrivialDestructor()) + // All record arguments are passed in memory on x86. Decide whether to + // construct the object directly in argument memory, or to construct the + // argument elsewhere and copy the bytes during the call. + + // If C++ prohibits us from making a copy, construct the arguments directly + // into argument memory. + if (!canCopyArgument(RD)) return RAA_DirectInMemory; + + // Otherwise, construct the argument into a temporary and copy the bytes + // into the outgoing argument memory. return RAA_Default; case llvm::Triple::x86_64: // Win64 passes objects with non-trivial copy ctors indirectly. if (RD->hasNonTrivialCopyConstructor()) return RAA_Indirect; + // Win64 passes objects larger than 8 bytes indirectly. if (getContext().getTypeSize(RD->getTypeForDecl()) > 64) return RAA_Indirect; + + // We have a trivial copy constructor or no copy constructors, but we have + // to make sure it isn't deleted. + bool CopyDeleted = false; + for (const CXXConstructorDecl *CD : RD->ctors()) { + if (CD->isCopyConstructor()) { + assert(CD->isTrivial()); + // We had at least one undeleted trivial copy ctor. Return directly. + if (!CD->isDeleted()) + return RAA_Default; + CopyDeleted = true; + } + } + + // The trivial copy constructor was deleted. Return indirectly. + if (CopyDeleted) + return RAA_Indirect; + + // There were no copy ctors. Return in RAX. return RAA_Default; } Index: cfe/trunk/test/CodeGenCXX/uncopyable-args.cpp =================================================================== --- cfe/trunk/test/CodeGenCXX/uncopyable-args.cpp +++ cfe/trunk/test/CodeGenCXX/uncopyable-args.cpp @@ -0,0 +1,200 @@ +// RUN: %clang_cc1 -std=c++11 -triple x86_64-unknown-unknown -emit-llvm -o - %s | FileCheck %s +// RUN: %clang_cc1 -std=c++11 -triple x86_64-windows-msvc -emit-llvm -o - %s | FileCheck %s -check-prefix=WIN64 + +namespace trivial { +// Trivial structs should be passed directly. +struct A { + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN7trivial3barEv() +// CHECK: alloca %"struct.trivial::A" +// CHECK: load i8** +// CHECK: call void @_ZN7trivial3fooENS_1AE(i8* %{{.*}}) +// CHECK-LABEL: declare void @_ZN7trivial3fooENS_1AE(i8*) + +// WIN64-LABEL: declare void @"\01?foo@trivial@@YAXUA@1@@Z"(i64) +} + +namespace default_ctor { +struct A { + A(); + void *p; +}; +void foo(A); +void bar() { + // Core issue 1590. We can pass this type in registers, even though C++ + // normally doesn't permit copies when using braced initialization. + foo({}); +} +// CHECK-LABEL: define void @_ZN12default_ctor3barEv() +// CHECK: alloca %"struct.default_ctor::A" +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK: load i8** +// CHECK: call void @_ZN12default_ctor3fooENS_1AE(i8* %{{.*}}) +// CHECK-LABEL: declare void @_ZN12default_ctor3fooENS_1AE(i8*) + +// WIN64-LABEL: declare void @"\01?foo@default_ctor@@YAXUA@1@@Z"(i64) +} + +namespace move_ctor { +// The presence of a move constructor implicitly deletes the trivial copy ctor +// and means that we have to pass this struct by address. +struct A { + A(); + A(A &&o); + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN9move_ctor3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK-NOT: call +// CHECK: call void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"* %{{.*}}) +// CHECK-LABEL: declare void @_ZN9move_ctor3fooENS_1AE(%"struct.move_ctor::A"*) + +// WIN64-LABEL: declare void @"\01?foo@move_ctor@@YAXUA@1@@Z"(%"struct.move_ctor::A"*) +} + +namespace all_deleted { +struct A { + A(); + A(const A &o) = delete; + A(A &&o) = delete; + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN11all_deleted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK-NOT call +// CHECK: call void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"* %{{.*}}) +// CHECK-LABEL: declare void @_ZN11all_deleted3fooENS_1AE(%"struct.all_deleted::A"*) + +// WIN64-LABEL: declare void @"\01?foo@all_deleted@@YAXUA@1@@Z"(%"struct.all_deleted::A"*) +} + +namespace implicitly_deleted { +struct A { + A(); + A &operator=(A &&o); + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN18implicitly_deleted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK-NOT call +// CHECK: call void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"* %{{.*}}) +// CHECK-LABEL: declare void @_ZN18implicitly_deleted3fooENS_1AE(%"struct.implicitly_deleted::A"*) + +// WIN64-LABEL: declare void @"\01?foo@implicitly_deleted@@YAXUA@1@@Z"(%"struct.implicitly_deleted::A"*) +} + +namespace one_deleted { +struct A { + A(); + A(A &&o) = delete; + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN11one_deleted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK-NOT call +// CHECK: call void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"* %{{.*}}) +// CHECK-LABEL: declare void @_ZN11one_deleted3fooENS_1AE(%"struct.one_deleted::A"*) + +// WIN64-LABEL: declare void @"\01?foo@one_deleted@@YAXUA@1@@Z"(%"struct.one_deleted::A"*) +} + +namespace copy_defaulted { +struct A { + A(); + A(const A &o) = default; + A(A &&o) = delete; + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN14copy_defaulted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK: load i8** +// CHECK: call void @_ZN14copy_defaulted3fooENS_1AE(i8* %{{.*}}) +// CHECK-LABEL: declare void @_ZN14copy_defaulted3fooENS_1AE(i8*) + +// WIN64-LABEL: declare void @"\01?foo@copy_defaulted@@YAXUA@1@@Z"(i64) +} + +namespace move_defaulted { +struct A { + A(); + A(const A &o) = delete; + A(A &&o) = default; + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN14move_defaulted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK: load i8** +// CHECK: call void @_ZN14move_defaulted3fooENS_1AE(i8* %{{.*}}) +// CHECK-LABEL: declare void @_ZN14move_defaulted3fooENS_1AE(i8*) + +// WIN64-LABEL: declare void @"\01?foo@move_defaulted@@YAXUA@1@@Z"(%"struct.move_defaulted::A"*) +} + +namespace trivial_defaulted { +struct A { + A(); + A(const A &o) = default; + void *p; +}; +void foo(A); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN17trivial_defaulted3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK: load i8** +// CHECK: call void @_ZN17trivial_defaulted3fooENS_1AE(i8* %{{.*}}) +// CHECK-LABEL: declare void @_ZN17trivial_defaulted3fooENS_1AE(i8*) + +// WIN64-LABEL: declare void @"\01?foo@trivial_defaulted@@YAXUA@1@@Z"(i64) +} + +namespace two_copy_ctors { +struct A { + A(); + A(const A &) = default; + A(const A &, int = 0); + void *p; +}; +struct B : A {}; + +void foo(B); +void bar() { + foo({}); +} +// CHECK-LABEL: define void @_ZN14two_copy_ctors3barEv() +// CHECK: call void @_Z{{.*}}C1Ev( +// CHECK: call void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"* %{{.*}}) +// CHECK-LABEL: declare void @_ZN14two_copy_ctors3fooENS_1BE(%"struct.two_copy_ctors::B"*) + +// WIN64-LABEL: declare void @"\01?foo@two_copy_ctors@@YAXUB@1@@Z"(%"struct.two_copy_ctors::B"*) +}