diff options
author | Reid Kleckner <reid@kleckner.net> | 2014-05-14 16:02:09 +0000 |
---|---|---|
committer | Reid Kleckner <reid@kleckner.net> | 2014-05-14 16:02:09 +0000 |
commit | 13ec00d6d7dbed25cfdc202ca482de2ecb41c61b (patch) | |
tree | 0a3996a3fd9ef1d617d3aa5fbb61ba4655123789 /test/CodeGenCXX/uncopyable-args.cpp | |
parent | b51314cc3e1eb7bd67a27fb1a8410b01cf4c6895 (diff) |
Don't copy objects with trivial, deleted copy ctors
This affects both the Itanium and Microsoft C++ ABIs.
This is in anticipation of a change to the Itanium C++ ABI, and should
match GCC's current behavior. The new text will likely be:
"""
Pass an object of class type by value if every copy constructor and
move constructor is deleted or trivial and at least one of them is not
deleted, and the destructor is trivial.
"""
http://sourcerytools.com/pipermail/cxx-abi-dev/2014-May/002728.html
On x86 Windows, we can mostly use the same logic, where we use inalloca
instead of passing by address. However, on Win64, there are register
parameters, and we have to do what MSVC does. MSVC ignores the presence
of non-trivial move constructors and only considers the presence of
non-trivial or deleted copy constructors. If a non-trivial or deleted
copy ctor is present, it passes the argument indirectly.
This change fixes bugs and makes us more ABI compatible with both GCC
and MSVC.
Fixes PR19668.
Reviewers: rsmith
Differential Revision: http://reviews.llvm.org/D3660
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@208786 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'test/CodeGenCXX/uncopyable-args.cpp')
-rw-r--r-- | test/CodeGenCXX/uncopyable-args.cpp | 200 |
1 files changed, 200 insertions, 0 deletions
diff --git a/test/CodeGenCXX/uncopyable-args.cpp b/test/CodeGenCXX/uncopyable-args.cpp new file mode 100644 index 0000000000..e2acc79d82 --- /dev/null +++ b/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"*) +} |