2014-07-18 09:48:10 +08:00
|
|
|
// RUN: %clang_cc1 -emit-llvm -fno-rtti %s -std=c++11 -o - -mconstructor-aliases -triple=i386-pc-win32 -fno-rtti > %t
|
2013-02-13 16:37:51 +08:00
|
|
|
// RUN: FileCheck %s < %t
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
// vftables are emitted very late, so do another pass to try to keep the checks
|
|
|
|
// in source order.
|
|
|
|
// RUN: FileCheck --check-prefix DTORS %s < %t
|
2014-02-19 06:51:52 +08:00
|
|
|
// RUN: FileCheck --check-prefix DTORS2 %s < %t
|
|
|
|
// RUN: FileCheck --check-prefix DTORS3 %s < %t
|
2013-08-26 18:32:04 +08:00
|
|
|
//
|
2014-01-15 03:35:09 +08:00
|
|
|
// RUN: %clang_cc1 -emit-llvm %s -o - -mconstructor-aliases -triple=x86_64-pc-win32 -fno-rtti | FileCheck --check-prefix DTORS-X64 %s
|
2012-04-20 16:05:00 +08:00
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
namespace basic {
|
|
|
|
|
2012-04-20 16:05:00 +08:00
|
|
|
class A {
|
|
|
|
public:
|
|
|
|
A() { }
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
~A();
|
2012-04-20 16:05:00 +08:00
|
|
|
};
|
|
|
|
|
2013-02-12 21:22:47 +08:00
|
|
|
void no_constructor_destructor_infinite_recursion() {
|
2012-04-20 16:05:00 +08:00
|
|
|
A a;
|
|
|
|
|
2015-01-16 23:37:11 +08:00
|
|
|
// CHECK: define linkonce_odr x86_thiscallcc %"class.basic::A"* @"\01??0A@basic@@QAE@XZ"(%"class.basic::A"* returned %this) {{.*}} comdat {{.*}} {
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: [[THIS_ADDR:%[.0-9A-Z_a-z]+]] = alloca %"class.basic::A"*, align 4
|
|
|
|
// CHECK-NEXT: store %"class.basic::A"* %this, %"class.basic::A"** [[THIS_ADDR]], align 4
|
|
|
|
// CHECK-NEXT: [[T1:%[.0-9A-Z_a-z]+]] = load %"class.basic::A"** [[THIS_ADDR]]
|
|
|
|
// CHECK-NEXT: ret %"class.basic::A"* [[T1]]
|
2012-09-25 16:00:39 +08:00
|
|
|
// CHECK-NEXT: }
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
}
|
2012-04-20 16:05:00 +08:00
|
|
|
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
A::~A() {
|
2012-04-20 16:05:00 +08:00
|
|
|
// Make sure that the destructor doesn't call itself:
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: define {{.*}} @"\01??1A@basic@@QAE@XZ"
|
|
|
|
// CHECK-NOT: call void @"\01??1A@basic@@QAE@XZ"
|
2012-04-20 16:05:00 +08:00
|
|
|
// CHECK: ret
|
|
|
|
}
|
2012-09-25 16:00:39 +08:00
|
|
|
|
2013-01-21 21:02:41 +08:00
|
|
|
struct B {
|
2013-02-27 21:46:31 +08:00
|
|
|
B();
|
|
|
|
};
|
|
|
|
|
|
|
|
// Tests that we can define constructors outside the class (PR12784).
|
|
|
|
B::B() {
|
2013-07-01 04:40:16 +08:00
|
|
|
// CHECK: define x86_thiscallcc %"struct.basic::B"* @"\01??0B@basic@@QAE@XZ"(%"struct.basic::B"* returned %this)
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
struct C {
|
|
|
|
virtual ~C() {
|
2015-01-16 23:37:11 +08:00
|
|
|
// DTORS: define linkonce_odr x86_thiscallcc i8* @"\01??_GC@basic@@UAEPAXI@Z"(%"struct.basic::C"* %this, i32 %should_call_delete) {{.*}} comdat {{.*}} {
|
2013-08-27 18:38:19 +08:00
|
|
|
// DTORS: store i32 %should_call_delete, i32* %[[SHOULD_DELETE_VAR:[0-9a-z._]+]], align 4
|
2014-11-01 04:09:12 +08:00
|
|
|
// DTORS: store i8* %{{.*}}, i8** %[[RETVAL:[0-9a-z._]+]]
|
2013-08-27 18:38:19 +08:00
|
|
|
// DTORS: %[[SHOULD_DELETE_VALUE:[0-9a-z._]+]] = load i32* %[[SHOULD_DELETE_VAR]]
|
2013-02-27 21:46:31 +08:00
|
|
|
// DTORS: call x86_thiscallcc void @"\01??1C@basic@@UAE@XZ"(%"struct.basic::C"* %[[THIS:[0-9a-z]+]])
|
2013-08-27 18:38:19 +08:00
|
|
|
// DTORS-NEXT: %[[CONDITION:[0-9]+]] = icmp eq i32 %[[SHOULD_DELETE_VALUE]], 0
|
2013-02-13 20:14:25 +08:00
|
|
|
// DTORS-NEXT: br i1 %[[CONDITION]], label %[[CONTINUE_LABEL:[0-9a-z._]+]], label %[[CALL_DELETE_LABEL:[0-9a-z._]+]]
|
|
|
|
//
|
|
|
|
// DTORS: [[CALL_DELETE_LABEL]]
|
2013-02-27 21:46:31 +08:00
|
|
|
// DTORS-NEXT: %[[THIS_AS_VOID:[0-9a-z]+]] = bitcast %"struct.basic::C"* %[[THIS]] to i8*
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
// DTORS-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[THIS_AS_VOID]])
|
2013-02-13 20:14:25 +08:00
|
|
|
// DTORS-NEXT: br label %[[CONTINUE_LABEL]]
|
|
|
|
//
|
|
|
|
// DTORS: [[CONTINUE_LABEL]]
|
2014-11-01 04:09:12 +08:00
|
|
|
// DTORS-NEXT: %[[RET:.*]] = load i8** %[[RETVAL]]
|
|
|
|
// DTORS-NEXT: ret i8* %[[RET]]
|
2013-08-26 18:32:04 +08:00
|
|
|
|
|
|
|
// Check that we do the mangling correctly on x64.
|
|
|
|
// DTORS-X64: @"\01??_GC@basic@@UEAAPEAXI@Z"
|
2013-02-13 16:37:51 +08:00
|
|
|
}
|
2013-01-21 21:02:41 +08:00
|
|
|
virtual void foo();
|
|
|
|
};
|
|
|
|
|
2013-02-13 16:37:51 +08:00
|
|
|
// Emits the vftable in the output.
|
2013-02-27 21:46:31 +08:00
|
|
|
void C::foo() {}
|
2013-02-13 16:37:51 +08:00
|
|
|
|
2013-01-21 21:02:41 +08:00
|
|
|
void check_vftable_offset() {
|
2013-02-27 21:46:31 +08:00
|
|
|
C c;
|
2013-01-21 21:02:41 +08:00
|
|
|
// The vftable pointer should point at the beginning of the vftable.
|
2014-12-04 05:00:21 +08:00
|
|
|
// CHECK: [[THIS_PTR:%[0-9]+]] = bitcast %"struct.basic::C"* {{.*}} to i32 (...)***
|
|
|
|
// CHECK: store i32 (...)** bitcast ([2 x i8*]* @"\01??_7C@basic@@6B@" to i32 (...)**), i32 (...)*** [[THIS_PTR]]
|
2013-01-21 21:02:41 +08:00
|
|
|
}
|
2013-02-13 16:37:51 +08:00
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
void call_complete_dtor(C *obj_ptr) {
|
|
|
|
// CHECK: define void @"\01?call_complete_dtor@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
|
|
|
|
obj_ptr->~C();
|
|
|
|
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
|
|
|
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)*, i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
|
|
|
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
|
2013-02-15 22:45:22 +08:00
|
|
|
// CHECK-NEXT: ret void
|
2013-02-13 16:37:51 +08:00
|
|
|
}
|
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
void call_deleting_dtor(C *obj_ptr) {
|
|
|
|
// CHECK: define void @"\01?call_deleting_dtor@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
|
2013-02-13 16:37:51 +08:00
|
|
|
delete obj_ptr;
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
|
|
|
|
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
|
|
|
|
|
|
|
|
// CHECK: [[DELETE_NOTNULL]]
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
|
|
|
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)*, i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
|
|
|
// CHECK-NEXT: call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 1)
|
|
|
|
// CHECK: ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
void call_deleting_dtor_and_global_delete(C *obj_ptr) {
|
|
|
|
// CHECK: define void @"\01?call_deleting_dtor_and_global_delete@basic@@YAXPAUC@1@@Z"(%"struct.basic::C"* %obj_ptr)
|
|
|
|
::delete obj_ptr;
|
|
|
|
// CHECK: %[[OBJ_PTR_VALUE:.*]] = load %"struct.basic::C"** %{{.*}}, align 4
|
|
|
|
// CHECK: br i1 {{.*}}, label %[[DELETE_NULL:.*]], label %[[DELETE_NOTNULL:.*]]
|
|
|
|
|
|
|
|
// CHECK: [[DELETE_NOTNULL]]
|
|
|
|
// CHECK-NEXT: %[[PVTABLE:.*]] = bitcast %"struct.basic::C"* %[[OBJ_PTR_VALUE]] to i8* (%"struct.basic::C"*, i32)***
|
|
|
|
// CHECK-NEXT: %[[VTABLE:.*]] = load i8* (%"struct.basic::C"*, i32)*** %[[PVTABLE]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[PVDTOR:.*]] = getelementptr inbounds i8* (%"struct.basic::C"*, i32)*, i8* (%"struct.basic::C"*, i32)** %[[VTABLE]], i64 0
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK-NEXT: %[[VDTOR:.*]] = load i8* (%"struct.basic::C"*, i32)** %[[PVDTOR]]
|
|
|
|
// CHECK-NEXT: %[[CALL:.*]] = call x86_thiscallcc i8* %[[VDTOR]](%"struct.basic::C"* %[[OBJ_PTR_VALUE]], i32 0)
|
|
|
|
// CHECK-NEXT: call void @"\01??3@YAXPAX@Z"(i8* %[[CALL]])
|
2013-02-15 22:45:22 +08:00
|
|
|
// CHECK: ret void
|
2013-02-13 16:37:51 +08:00
|
|
|
}
|
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
struct D {
|
2013-02-13 16:37:51 +08:00
|
|
|
static int foo();
|
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
D() {
|
2013-02-13 16:37:51 +08:00
|
|
|
static int ctor_static = foo();
|
|
|
|
// CHECK that the static in the ctor gets mangled correctly:
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: @"\01?ctor_static@?1???0D@basic@@QAE@XZ@4HA"
|
2013-02-13 16:37:51 +08:00
|
|
|
}
|
2013-02-27 21:46:31 +08:00
|
|
|
~D() {
|
2013-02-13 16:37:51 +08:00
|
|
|
static int dtor_static = foo();
|
|
|
|
// CHECK that the static in the dtor gets mangled correctly:
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: @"\01?dtor_static@?1???1D@basic@@QAE@XZ@4HA"
|
2013-02-13 16:37:51 +08:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
void use_D() { D c; }
|
2013-02-22 17:10:20 +08:00
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
} // end namespace basic
|
|
|
|
|
2014-02-19 06:51:52 +08:00
|
|
|
namespace dtor_in_second_nvbase {
|
|
|
|
|
|
|
|
struct A {
|
|
|
|
virtual void f(); // A needs vftable to be primary.
|
|
|
|
};
|
|
|
|
struct B {
|
|
|
|
virtual ~B();
|
|
|
|
};
|
|
|
|
struct C : A, B {
|
|
|
|
virtual ~C();
|
|
|
|
};
|
|
|
|
|
|
|
|
C::~C() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1C@dtor_in_second_nvbase@@UAE@XZ"
|
|
|
|
// CHECK: (%"struct.dtor_in_second_nvbase::C"* %this)
|
|
|
|
// No this adjustment!
|
|
|
|
// CHECK-NOT: getelementptr
|
|
|
|
// CHECK: load %"struct.dtor_in_second_nvbase::C"** %{{.*}}
|
|
|
|
// Now we this-adjust before calling ~B.
|
|
|
|
// CHECK: bitcast %"struct.dtor_in_second_nvbase::C"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: getelementptr inbounds i8, i8* %{{.*}}, i64 4
|
2014-02-19 06:51:52 +08:00
|
|
|
// CHECK: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::B"*
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1B@dtor_in_second_nvbase@@UAE@XZ"
|
|
|
|
// CHECK: (%"struct.dtor_in_second_nvbase::B"* %{{.*}})
|
|
|
|
// CHECK: ret void
|
|
|
|
}
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
C c;
|
|
|
|
}
|
2014-11-01 04:09:12 +08:00
|
|
|
// DTORS2-LABEL: define linkonce_odr x86_thiscallcc i8* @"\01??_EC@dtor_in_second_nvbase@@W3AEPAXI@Z"
|
2014-02-19 06:51:52 +08:00
|
|
|
// DTORS2: (%"struct.dtor_in_second_nvbase::C"* %this, i32 %should_call_delete)
|
|
|
|
// Do an adjustment from B* to C*.
|
2015-02-28 03:18:17 +08:00
|
|
|
// DTORS2: getelementptr i8, i8* %{{.*}}, i32 -4
|
2014-02-19 06:51:52 +08:00
|
|
|
// DTORS2: bitcast i8* %{{.*}} to %"struct.dtor_in_second_nvbase::C"*
|
2014-11-01 04:09:12 +08:00
|
|
|
// DTORS2: %[[CALL:.*]] = call x86_thiscallcc i8* @"\01??_GC@dtor_in_second_nvbase@@UAEPAXI@Z"
|
|
|
|
// DTORS2: ret i8* %[[CALL]]
|
2014-02-19 06:51:52 +08:00
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace test2 {
|
|
|
|
// Just like dtor_in_second_nvbase, except put that in a vbase of a diamond.
|
|
|
|
|
|
|
|
// C's dtor is in the non-primary base.
|
|
|
|
struct A { virtual void f(); };
|
|
|
|
struct B { virtual ~B(); };
|
|
|
|
struct C : A, B { virtual ~C(); int c; };
|
|
|
|
|
|
|
|
// Diamond hierarchy, with C as the shared vbase.
|
|
|
|
struct D : virtual C { int d; };
|
|
|
|
struct E : virtual C { int e; };
|
|
|
|
struct F : D, E { ~F(); int f; };
|
|
|
|
|
|
|
|
F::~F() {
|
|
|
|
// CHECK-LABEL: define x86_thiscallcc void @"\01??1F@test2@@UAE@XZ"(%"struct.test2::F"*)
|
|
|
|
// Do an adjustment from C vbase subobject to F as though F was the
|
|
|
|
// complete type.
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK: getelementptr inbounds i8, i8* %{{.*}}, i32 -20
|
2014-02-19 06:51:52 +08:00
|
|
|
// CHECK: bitcast i8* %{{.*}} to %"struct.test2::F"*
|
|
|
|
// CHECK: store %"struct.test2::F"*
|
|
|
|
}
|
|
|
|
|
|
|
|
void foo() {
|
|
|
|
F f;
|
|
|
|
}
|
2015-01-17 09:47:39 +08:00
|
|
|
// DTORS3-LABEL: define linkonce_odr x86_thiscallcc void @"\01??_DF@test2@@UAE@XZ"({{.*}} {{.*}} comdat
|
2014-02-19 06:51:52 +08:00
|
|
|
// Do an adjustment from C* to F*.
|
2015-02-28 03:18:17 +08:00
|
|
|
// DTORS3: getelementptr i8, i8* %{{.*}}, i32 20
|
2014-02-19 06:51:52 +08:00
|
|
|
// DTORS3: bitcast i8* %{{.*}} to %"struct.test2::F"*
|
|
|
|
// DTORS3: call x86_thiscallcc void @"\01??1F@test2@@UAE@XZ"
|
|
|
|
// DTORS3: ret void
|
|
|
|
|
|
|
|
}
|
2013-02-27 21:46:31 +08:00
|
|
|
|
|
|
|
namespace constructors {
|
|
|
|
|
|
|
|
struct A {
|
|
|
|
A() {}
|
|
|
|
};
|
|
|
|
|
|
|
|
struct B : A {
|
|
|
|
B();
|
|
|
|
~B();
|
|
|
|
};
|
|
|
|
|
|
|
|
B::B() {
|
2013-07-01 04:40:16 +08:00
|
|
|
// CHECK: define x86_thiscallcc %"struct.constructors::B"* @"\01??0B@constructors@@QAE@XZ"(%"struct.constructors::B"* returned %this)
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}})
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
struct C : virtual A {
|
|
|
|
C();
|
|
|
|
};
|
|
|
|
|
|
|
|
C::C() {
|
2013-07-01 04:40:16 +08:00
|
|
|
// CHECK: define x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* returned %this, i32 %is_most_derived)
|
2013-02-27 21:46:31 +08:00
|
|
|
// TODO: make sure this works in the Release build too;
|
|
|
|
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
|
|
|
|
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
|
|
|
|
// CHECK: %[[SHOULD_CALL_VBASE_CTORS:.*]] = icmp ne i32 %[[IS_MOST_DERIVED_VAL]], 0
|
|
|
|
// CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
|
|
|
//
|
|
|
|
// CHECK: [[INIT_VBASES]]
|
2013-06-19 23:20:38 +08:00
|
|
|
// CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::C"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr_off:.*]] = getelementptr inbounds i8, i8* %[[this_i8]], i64 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i32**
|
|
|
|
// CHECK-NEXT: store i32* getelementptr inbounds ([2 x i32]* @"\01??_8C@constructors@@7B@", i32 0, i32 0), i32** %[[vbptr]]
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast %"struct.constructors::C"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: getelementptr inbounds i8, i8* %{{.*}}, i64 4
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast i8* %{{.*}} to %"struct.constructors::A"*
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}})
|
|
|
|
// CHECK-NEXT: br label %[[SKIP_VBASES]]
|
|
|
|
//
|
|
|
|
// CHECK: [[SKIP_VBASES]]
|
2013-09-27 22:48:01 +08:00
|
|
|
// Class C does not define or override methods, so shouldn't change the vfptr.
|
|
|
|
// CHECK-NOT: @"\01??_7C@constructors@@6B@"
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
void create_C() {
|
|
|
|
C c;
|
|
|
|
// CHECK: define void @"\01?create_C@constructors@@YAXXZ"()
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* %c, i32 1)
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
struct D : C {
|
|
|
|
D();
|
|
|
|
};
|
|
|
|
|
|
|
|
D::D() {
|
2013-07-01 04:40:16 +08:00
|
|
|
// CHECK: define x86_thiscallcc %"struct.constructors::D"* @"\01??0D@constructors@@QAE@XZ"(%"struct.constructors::D"* returned %this, i32 %is_most_derived) unnamed_addr
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
|
|
|
|
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
|
|
|
|
// CHECK: %[[SHOULD_CALL_VBASE_CTORS:.*]] = icmp ne i32 %[[IS_MOST_DERIVED_VAL]], 0
|
|
|
|
// CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
|
|
|
//
|
|
|
|
// CHECK: [[INIT_VBASES]]
|
2013-06-19 23:20:38 +08:00
|
|
|
// CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::D"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr_off:.*]] = getelementptr inbounds i8, i8* %[[this_i8]], i64 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr:.*]] = bitcast i8* %[[vbptr_off]] to i32**
|
|
|
|
// CHECK-NEXT: store i32* getelementptr inbounds ([2 x i32]* @"\01??_8D@constructors@@7B@", i32 0, i32 0), i32** %[[vbptr]]
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast %"struct.constructors::D"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: getelementptr inbounds i8, i8* %{{.*}}, i64 4
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast i8* %{{.*}} to %"struct.constructors::A"*
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}})
|
|
|
|
// CHECK-NEXT: br label %[[SKIP_VBASES]]
|
|
|
|
//
|
|
|
|
// CHECK: [[SKIP_VBASES]]
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* %{{.*}}, i32 0)
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
struct E : virtual C {
|
|
|
|
E();
|
|
|
|
};
|
|
|
|
|
|
|
|
E::E() {
|
2013-07-01 04:40:16 +08:00
|
|
|
// CHECK: define x86_thiscallcc %"struct.constructors::E"* @"\01??0E@constructors@@QAE@XZ"(%"struct.constructors::E"* returned %this, i32 %is_most_derived) unnamed_addr
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK: store i32 %is_most_derived, i32* %[[IS_MOST_DERIVED_VAR:.*]], align 4
|
|
|
|
// CHECK: %[[IS_MOST_DERIVED_VAL:.*]] = load i32* %[[IS_MOST_DERIVED_VAR]]
|
|
|
|
// CHECK: %[[SHOULD_CALL_VBASE_CTORS:.*]] = icmp ne i32 %[[IS_MOST_DERIVED_VAL]], 0
|
|
|
|
// CHECK: br i1 %[[SHOULD_CALL_VBASE_CTORS]], label %[[INIT_VBASES:.*]], label %[[SKIP_VBASES:.*]]
|
|
|
|
//
|
|
|
|
// CHECK: [[INIT_VBASES]]
|
2013-06-19 23:20:38 +08:00
|
|
|
// CHECK-NEXT: %[[this_i8:.*]] = bitcast %"struct.constructors::E"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[offs:.*]] = getelementptr inbounds i8, i8* %[[this_i8]], i64 0
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr_E:.*]] = bitcast i8* %[[offs]] to i32**
|
|
|
|
// CHECK-NEXT: store i32* getelementptr inbounds ([3 x i32]* @"\01??_8E@constructors@@7B01@@", i32 0, i32 0), i32** %[[vbptr_E]]
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: %[[offs:.*]] = getelementptr inbounds i8, i8* %[[this_i8]], i64 4
|
2014-10-23 01:26:00 +08:00
|
|
|
// CHECK-NEXT: %[[vbptr_C:.*]] = bitcast i8* %[[offs]] to i32**
|
|
|
|
// CHECK-NEXT: store i32* getelementptr inbounds ([2 x i32]* @"\01??_8E@constructors@@7BC@1@@", i32 0, i32 0), i32** %[[vbptr_C]]
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast %"struct.constructors::E"* %{{.*}} to i8*
|
2015-02-28 03:18:17 +08:00
|
|
|
// CHECK-NEXT: getelementptr inbounds i8, i8* %{{.*}}, i64 4
|
2013-10-12 04:19:00 +08:00
|
|
|
// CHECK-NEXT: bitcast i8* %{{.*}} to %"struct.constructors::A"*
|
2013-02-27 21:46:31 +08:00
|
|
|
// CHECK-NEXT: call x86_thiscallcc %"struct.constructors::A"* @"\01??0A@constructors@@QAE@XZ"(%"struct.constructors::A"* %{{.*}})
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.constructors::C"* @"\01??0C@constructors@@QAE@XZ"(%"struct.constructors::C"* %{{.*}}, i32 0)
|
|
|
|
// CHECK-NEXT: br label %[[SKIP_VBASES]]
|
|
|
|
//
|
|
|
|
// CHECK: [[SKIP_VBASES]]
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
2013-08-05 01:30:04 +08:00
|
|
|
// PR16735 - even abstract classes should have a constructor emitted.
|
|
|
|
struct F {
|
|
|
|
F();
|
|
|
|
virtual void f() = 0;
|
|
|
|
};
|
|
|
|
|
|
|
|
F::F() {}
|
|
|
|
// CHECK: define x86_thiscallcc %"struct.constructors::F"* @"\01??0F@constructors@@QAE@XZ"
|
|
|
|
|
2013-02-27 21:46:31 +08:00
|
|
|
} // end namespace constructors
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
|
|
|
|
namespace dtors {
|
|
|
|
|
|
|
|
struct A {
|
|
|
|
~A();
|
|
|
|
};
|
|
|
|
|
|
|
|
void call_nv_complete(A *a) {
|
|
|
|
a->~A();
|
|
|
|
// CHECK: define void @"\01?call_nv_complete@dtors@@YAXPAUA@1@@Z"
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1A@dtors@@QAE@XZ"
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// CHECK: declare x86_thiscallcc void @"\01??1A@dtors@@QAE@XZ"
|
|
|
|
|
|
|
|
// Now try some virtual bases, where we need the complete dtor.
|
|
|
|
|
|
|
|
struct B : virtual A { ~B(); };
|
|
|
|
struct C : virtual A { ~C(); };
|
|
|
|
struct D : B, C { ~D(); };
|
|
|
|
|
|
|
|
void call_vbase_complete(D *d) {
|
|
|
|
d->~D();
|
|
|
|
// CHECK: define void @"\01?call_vbase_complete@dtors@@YAXPAUD@1@@Z"
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// The complete dtor should call the base dtors for D and the vbase A (once).
|
2015-01-17 09:47:39 +08:00
|
|
|
// CHECK: define linkonce_odr x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"({{.*}}) {{.*}} comdat
|
[ms-cxxabi] Emit linkonce complete dtors in TUs that need them
Based on Peter Collingbourne's destructor patches.
Prior to this change, clang was considering ?1 to be the complete
destructor and the base destructor, which was wrong. This lead to
crashes when clang tried to emit two LLVM functions with the same name.
In this ABI, TUs with non-inline dtors might not emit a complete
destructor. They are emitted as inline thunks in TUs that need them,
and they always delegate to the base dtors of the complete class and its
virtual bases. This change uses the DeferredDecls machinery to emit
complete dtors as needed.
Currently in clang try body destructors can catch exceptions thrown by
virtual base destructors. In the Microsoft C++ ABI, clang may not have
the destructor definition, in which case clang won't wrap the virtual
virtual base destructor calls in a try-catch. Diagnosing this in user
code is TODO.
Finally, for classes that don't use virtual inheritance, MSVC always
calls the base destructor (?1) directly. This is a useful code size
optimization that avoids emitting lots of extra thunks or aliases.
Implementing it also means our existing tests continue to pass, and is
consistent with MSVC's output.
We can do the same for Itanium by tweaking GetAddrOfCXXDestructor, but
it will require further testing.
Reviewers: rjmccall
CC: cfe-commits
Differential Revision: http://llvm-reviews.chandlerc.com/D1066
llvm-svn: 186828
2013-07-22 21:51:44 +08:00
|
|
|
// CHECK-NOT: call
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1D@dtors@@QAE@XZ"
|
|
|
|
// CHECK-NOT: call
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??1A@dtors@@QAE@XZ"
|
|
|
|
// CHECK-NOT: call
|
|
|
|
// CHECK: ret
|
|
|
|
|
|
|
|
void destroy_d_complete() {
|
|
|
|
D d;
|
|
|
|
// CHECK: define void @"\01?destroy_d_complete@dtors@@YAXXZ"
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: Clang manually inlines the deletion, so we don't get a call to the
|
|
|
|
// deleting dtor (_G). The only way to call deleting dtors currently is through
|
|
|
|
// a vftable.
|
|
|
|
void call_nv_deleting_dtor(D *d) {
|
|
|
|
delete d;
|
|
|
|
// CHECK: define void @"\01?call_nv_deleting_dtor@dtors@@YAXPAUD@1@@Z"
|
|
|
|
// CHECK: call x86_thiscallcc void @"\01??_DD@dtors@@QAE@XZ"(%"struct.dtors::D"* %{{[^,]+}})
|
|
|
|
// CHECK: call void @"\01??3@YAXPAX@Z"
|
|
|
|
// CHECK: ret
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
2013-12-12 03:21:27 +08:00
|
|
|
|
2013-12-18 03:46:40 +08:00
|
|
|
namespace test1 {
|
|
|
|
struct A { };
|
|
|
|
struct B : virtual A {
|
|
|
|
B(int *a);
|
|
|
|
B(const char *a, ...);
|
|
|
|
__cdecl B(short *a);
|
|
|
|
};
|
|
|
|
B::B(int *a) {}
|
|
|
|
B::B(const char *a, ...) {}
|
|
|
|
B::B(short *a) {}
|
|
|
|
// CHECK: define x86_thiscallcc %"struct.test1::B"* @"\01??0B@test1@@QAE@PAH@Z"
|
|
|
|
// CHECK: (%"struct.test1::B"* returned %this, i32* %a, i32 %is_most_derived)
|
|
|
|
// CHECK: define %"struct.test1::B"* @"\01??0B@test1@@QAA@PBDZZ"
|
|
|
|
// CHECK: (%"struct.test1::B"* returned %this, i32 %is_most_derived, i8* %a, ...)
|
|
|
|
|
|
|
|
// FIXME: This should be x86_thiscallcc. MSVC ignores explicit CCs on structors.
|
|
|
|
// CHECK: define %"struct.test1::B"* @"\01??0B@test1@@QAA@PAF@Z"
|
|
|
|
// CHECK: (%"struct.test1::B"* returned %this, i16* %a, i32 %is_most_derived)
|
|
|
|
|
|
|
|
void construct_b() {
|
|
|
|
int a;
|
|
|
|
B b1(&a);
|
|
|
|
B b2("%d %d", 1, 2);
|
|
|
|
}
|
|
|
|
// CHECK-LABEL: define void @"\01?construct_b@test1@@YAXXZ"()
|
|
|
|
// CHECK: call x86_thiscallcc %"struct.test1::B"* @"\01??0B@test1@@QAE@PAH@Z"
|
|
|
|
// CHECK: (%"struct.test1::B"* {{.*}}, i32* {{.*}}, i32 1)
|
|
|
|
// CHECK: call %"struct.test1::B"* (%"struct.test1::B"*, i32, i8*, ...)* @"\01??0B@test1@@QAA@PBDZZ"
|
|
|
|
// CHECK: (%"struct.test1::B"* {{.*}}, i32 1, i8* {{.*}}, i32 1, i32 2)
|
|
|
|
}
|
|
|
|
|
2014-07-18 09:48:10 +08:00
|
|
|
namespace implicit_copy_vtable {
|
|
|
|
// This was a crash that only reproduced in ABIs without key functions.
|
|
|
|
struct ImplicitCopy {
|
|
|
|
// implicit copy ctor
|
|
|
|
virtual ~ImplicitCopy();
|
|
|
|
};
|
|
|
|
void CreateCopy(ImplicitCopy *a) {
|
|
|
|
new ImplicitCopy(*a);
|
|
|
|
}
|
|
|
|
// CHECK: store {{.*}} @"\01??_7ImplicitCopy@implicit_copy_vtable@@6B@"
|
|
|
|
|
|
|
|
struct MoveOnly {
|
|
|
|
MoveOnly(MoveOnly &&o) = default;
|
|
|
|
virtual ~MoveOnly();
|
|
|
|
};
|
|
|
|
MoveOnly &&f();
|
|
|
|
void g() { new MoveOnly(f()); }
|
|
|
|
// CHECK: store {{.*}} @"\01??_7MoveOnly@implicit_copy_vtable@@6B@"
|
|
|
|
}
|
|
|
|
|
2013-12-12 03:21:27 +08:00
|
|
|
// Dtor thunks for classes in anonymous namespaces should be internal, not
|
|
|
|
// linkonce_odr.
|
|
|
|
namespace {
|
|
|
|
struct A {
|
|
|
|
virtual ~A() { }
|
|
|
|
};
|
|
|
|
}
|
|
|
|
void *getA() {
|
|
|
|
return (void*)new A();
|
|
|
|
}
|
2014-11-01 04:09:12 +08:00
|
|
|
// CHECK: define internal x86_thiscallcc i8* @"\01??_GA@?A@@UAEPAXI@Z"
|
2014-04-02 13:58:29 +08:00
|
|
|
// CHECK: (%"struct.(anonymous namespace)::A"* %this, i32 %should_call_delete)
|
2013-12-12 03:21:27 +08:00
|
|
|
// CHECK: define internal x86_thiscallcc void @"\01??1A@?A@@UAE@XZ"
|
2014-04-02 13:58:29 +08:00
|
|
|
// CHECK: (%"struct.(anonymous namespace)::A"* %this)
|