| // Sparc64 doesn't support musttail (yet), so it uses method cloning for | |
| // variadic thunks. Use it for testing. | |
| // RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -funwind-tables=2 -emit-llvm -o - \ | |
| // RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-NONOPT %s | |
| // RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -debug-info-kind=standalone -dwarf-version=5 -funwind-tables=2 -emit-llvm -o - \ | |
| // RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-NONOPT,CHECK-DBG %s | |
| // RUN: %clang_cc1 %s -triple=sparc64-pc-linux-gnu -funwind-tables=2 -emit-llvm -o - -O1 -disable-llvm-passes \ | |
| // RUN: | FileCheck --check-prefixes=CHECK,CHECK-CLONE,CHECK-OPT %s | |
| // Test x86_64, which uses musttail for variadic thunks. | |
| // RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -funwind-tables=2 -emit-llvm -o - -O1 -disable-llvm-passes \ | |
| // RUN: | FileCheck --check-prefixes=CHECK,CHECK-TAIL,CHECK-OPT %s | |
| // Finally, reuse these tests for the MS ABI. | |
| // RUN: %clang_cc1 %s -triple=x86_64-windows-msvc -funwind-tables=2 -emit-llvm -o - -O1 -disable-llvm-passes \ | |
| // RUN: | FileCheck --check-prefixes=WIN64 %s | |
| namespace Test1 { | |
| // Check that we emit a non-virtual thunk for C::f. | |
| struct A { | |
| virtual void f(); | |
| }; | |
| struct B { | |
| virtual void f(); | |
| }; | |
| struct C : A, B { | |
| virtual void c(); | |
| virtual void f(); | |
| }; | |
| // CHECK-LABEL: define{{.*}} void @_ZThn8_N5Test11C1fEv( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret void | |
| // | |
| // WIN64-LABEL: define dso_local void @"?f@C@Test1@@UEAAXXZ"( | |
| // WIN64-LABEL: define linkonce_odr dso_local void @"?f@C@Test1@@W7EAAXXZ"( | |
| // WIN64: getelementptr i8, ptr {{.*}}, i32 -8 | |
| // WIN64: ret void | |
| void C::f() { } | |
| } | |
| namespace Test2 { | |
| // Check that we emit a thunk for B::f since it's overriding a virtual base. | |
| struct A { | |
| virtual void f(); | |
| }; | |
| struct B : virtual A { | |
| virtual void b(); | |
| virtual void f(); | |
| }; | |
| // CHECK-LABEL: define{{.*}} void @_ZTv0_n24_N5Test21B1fEv( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret void | |
| void B::f() { } | |
| // No thunk is used for this case in the MS ABI. | |
| // WIN64-LABEL: define dso_local void @"?f@B@Test2@@UEAAXXZ"( | |
| // WIN64-NOT: define {{.*}} void @"?f@B@Test2 | |
| } | |
| namespace Test3 { | |
| // Check that we emit a covariant thunk for B::f. | |
| struct V1 { }; | |
| struct V2 : virtual V1 { }; | |
| struct A { | |
| virtual V1 *f(); | |
| }; | |
| struct B : A { | |
| virtual void b(); | |
| virtual V2 *f(); | |
| }; | |
| // CHECK: define{{.*}} ptr @_ZTch0_v0_n24_N5Test31B1fEv( | |
| // WIN64: define weak_odr dso_local noundef ptr @"?f@B@Test3@@QEAAPEAUV1@2@XZ"( | |
| V2 *B::f() { return 0; } | |
| } | |
| namespace Test4 { | |
| // Check that the thunk for 'C::f' has the same visibility as the function itself. | |
| struct A { | |
| virtual void f(); | |
| }; | |
| struct B { | |
| virtual void f(); | |
| }; | |
| struct __attribute__((visibility("protected"))) C : A, B { | |
| virtual void c(); | |
| virtual void f(); | |
| }; | |
| // CHECK-LABEL: define protected void @_ZThn8_N5Test41C1fEv( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret void | |
| void C::f() { } | |
| // Visibility doesn't matter on COFF, but whatever. We could add an ELF test | |
| // mode later. | |
| // WIN64-LABEL: define protected void @"?f@C@Test4@@UEAAXXZ"( | |
| // WIN64-LABEL: define linkonce_odr protected void @"?f@C@Test4@@W7EAAXXZ"( | |
| } | |
| // Check that the thunk gets internal linkage. | |
| namespace Test4B { | |
| struct A { | |
| virtual void f(); | |
| }; | |
| struct B { | |
| virtual void f(); | |
| }; | |
| namespace { | |
| struct C : A, B { | |
| virtual void c(); | |
| virtual void f(); | |
| }; | |
| } | |
| void C::c() {} | |
| void C::f() {} | |
| // Force C::f to be used. | |
| void f() { | |
| C c; | |
| c.f(); | |
| } | |
| } | |
| // Not sure why this isn't delayed like in Itanium. | |
| // WIN64-LABEL: define internal void @"?f@C@?A{{.*}}@Test4B@@UEAAXXZ"( | |
| namespace Test5 { | |
| // Check that the thunk for 'B::f' gets the same linkage as the function itself. | |
| struct A { | |
| virtual void f(); | |
| }; | |
| struct B : virtual A { | |
| virtual void f() { } | |
| }; | |
| void f(B b) { | |
| b.f(); | |
| } | |
| // No thunk in MS ABI in this case. | |
| } | |
| namespace Test6 { | |
| struct X { | |
| X(); | |
| X(const X&); | |
| X &operator=(const X&); | |
| ~X(); | |
| }; | |
| struct P { | |
| P(); | |
| P(const P&); | |
| ~P(); | |
| X first; | |
| X second; | |
| }; | |
| P getP(); | |
| struct Base1 { | |
| int i; | |
| virtual X f() { return X(); } | |
| }; | |
| struct Base2 { | |
| float real; | |
| virtual X f() { return X(); } | |
| }; | |
| struct Thunks : Base1, Base2 { | |
| long l; | |
| virtual X f(); | |
| }; | |
| // CHECK-LABEL: define{{.*}} void @_ZThn16_N5Test66Thunks1fEv | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK-NOT: memcpy | |
| // CHECK: {{call void @_ZN5Test66Thunks1fEv.*sret(.+) align 1}} | |
| // CHECK: ret void | |
| X Thunks::f() { return X(); } | |
| // WIN64-LABEL: define linkonce_odr dso_local void @"?f@Thunks@Test6@@WBA@EAA?AUX@2@XZ"({{.*}} sret({{.*}}) align 1 %{{.*}}) | |
| // WIN64-NOT: memcpy | |
| // WIN64: tail call void @"?f@Thunks@Test6@@UEAA?AUX@2@XZ"({{.*}} sret({{.*}}) align 1 %{{.*}}) | |
| } | |
| namespace Test7 { | |
| // PR7188 | |
| struct X { | |
| X(); | |
| X(const X&); | |
| X &operator=(const X&); | |
| ~X(); | |
| }; | |
| struct Small { short s; }; | |
| struct Large { | |
| char array[1024]; | |
| }; | |
| class A { | |
| protected: | |
| virtual void foo() = 0; | |
| }; | |
| class B : public A { | |
| protected: | |
| virtual void bar() = 0; | |
| }; | |
| class C : public A { | |
| protected: | |
| virtual void baz(X, X&, _Complex float, Small, Small&, Large) = 0; | |
| }; | |
| class D : public B, | |
| public C { | |
| void foo() {} | |
| void bar() {} | |
| void baz(X, X&, _Complex float, Small, Small&, Large); | |
| }; | |
| void D::baz(X, X&, _Complex float, Small, Small&, Large) { } | |
| // CHECK-LABEL: define{{.*}} void @_ZThn8_N5Test71D3bazENS_1XERS1_CfNS_5SmallERS4_NS_5LargeE( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK-NOT: memcpy | |
| // CHECK: ret void | |
| void testD() { D d; } | |
| // MS C++ ABI doesn't use a thunk, so this case isn't interesting. | |
| } | |
| namespace Test8 { | |
| struct NonPOD { ~NonPOD(); int x, y, z; }; | |
| struct A { virtual void foo(); }; | |
| struct B { virtual void bar(NonPOD); }; | |
| struct C : A, B { virtual void bar(NonPOD); static void helper(NonPOD); }; | |
| // CHECK: define{{.*}} void @_ZN5Test81C6helperENS_6NonPODE(ptr | |
| void C::helper(NonPOD var) {} | |
| // CHECK-LABEL: define{{.*}} void @_ZThn8_N5Test81C3barENS_6NonPODE( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK-NOT: load [[NONPODTYPE:%.*]], ptr | |
| // CHECK-NOT: memcpy | |
| // CHECK: ret void | |
| void C::bar(NonPOD var) {} | |
| // MS C++ ABI doesn't use a thunk, so this case isn't interesting. | |
| } | |
| // PR7241: Emitting thunks for a method shouldn't require the vtable for | |
| // that class to be emitted. | |
| namespace Test9 { | |
| struct A { virtual ~A() { } }; | |
| struct B : A { virtual void test() const {} }; | |
| struct C : B { C(); ~C(); }; | |
| struct D : C { D() {} }; | |
| void test() { | |
| D d; | |
| } | |
| } | |
| namespace Test10 { | |
| struct A { virtual void foo(); }; | |
| struct B { virtual void foo(); }; | |
| struct C : A, B { void foo() {} }; | |
| // Test later. | |
| void test() { | |
| C c; | |
| } | |
| } | |
| // PR7611 | |
| namespace Test11 { | |
| struct A { virtual A* f(); }; | |
| struct B : virtual A { virtual A* f(); }; | |
| struct C : B { virtual C* f(); }; | |
| C* C::f() { return 0; } | |
| // C::f itself. | |
| // CHECK: define {{.*}} @_ZN6Test111C1fEv( | |
| // The this-adjustment and return-adjustment thunk required when | |
| // C::f appears in a vtable where A is at a nonzero offset from C. | |
| // CHECK: define {{.*}} @_ZTcv0_n24_v0_n32_N6Test111C1fEv( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret | |
| // The return-adjustment thunk required when C::f appears in a vtable | |
| // where A is at a zero offset from C. | |
| // CHECK: define {{.*}} @_ZTch0_v0_n32_N6Test111C1fEv( | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret | |
| // WIN64-LABEL: define dso_local noundef ptr @"?f@C@Test11@@UEAAPEAU12@XZ"(ptr | |
| // WIN64-LABEL: define weak_odr dso_local noundef ptr @"?f@C@Test11@@QEAAPEAUA@2@XZ"(ptr | |
| // WIN64: call noundef ptr @"?f@C@Test11@@UEAAPEAU12@XZ"(ptr noundef %{{.*}}) | |
| // | |
| // Match the vbtable return adjustment. | |
| // WIN64: load ptr, ptr %{{[^,]*}}, align 8 | |
| // WIN64: getelementptr inbounds i32, ptr %{{[^,]*}}, i32 1 | |
| // WIN64: load i32, ptr %{{[^,]*}}, align 4 | |
| } | |
| // Varargs thunk test. | |
| namespace Test12 { | |
| struct A { | |
| virtual A* f(int x, ...); | |
| }; | |
| struct B { | |
| virtual B* f(int x, ...); | |
| }; | |
| struct C : A, B { | |
| virtual void c(); | |
| virtual C* f(int x, ...); | |
| }; | |
| C* makeC(); | |
| C* C::f(int x, ...) { return makeC(); } | |
| // C::f | |
| // CHECK: define {{.*}} @_ZN6Test121C1fEiz | |
| // Varargs thunk; check that both the this and covariant adjustments | |
| // are generated. | |
| // CHECK: define {{.*}} @_ZTchn8_h8_N6Test121C1fEiz | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 -8 | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 8 | |
| // The vtable layout goes: | |
| // C vtable in A: | |
| // - f impl, no adjustment | |
| // C vtable in B: | |
| // - f thunk 2, covariant, clone | |
| // - f thunk 2, musttail this adjust to impl | |
| // FIXME: The weak_odr linkage is probably not necessary and just an artifact | |
| // of Itanium ABI details. | |
| // WIN64-LABEL: define dso_local {{.*}} @"?f@C@Test12@@UEAAPEAU12@HZZ"( | |
| // WIN64: call noundef ptr @"?makeC@Test12@@YAPEAUC@1@XZ"() | |
| // | |
| // This thunk needs return adjustment, clone. | |
| // WIN64-LABEL: define weak_odr dso_local {{.*}} @"?f@C@Test12@@W7EAAPEAUB@2@HZZ"( | |
| // WIN64: call noundef ptr @"?makeC@Test12@@YAPEAUC@1@XZ"() | |
| // WIN64: getelementptr inbounds i8, ptr %{{.*}}, i32 8 | |
| // | |
| // Musttail call back to the A implementation after this adjustment from B to A. | |
| // WIN64-LABEL: define linkonce_odr dso_local noundef ptr @"?f@C@Test12@@W7EAAPEAU12@HZZ"( | |
| // WIN64: getelementptr i8, ptr %{{[^,]*}}, i32 -8 | |
| // WIN64: musttail call {{.*}} @"?f@C@Test12@@UEAAPEAU12@HZZ"( | |
| C c; | |
| } | |
| // PR13832 | |
| namespace Test13 { | |
| struct B1 { | |
| virtual B1 &foo1(); | |
| }; | |
| struct Pad1 { | |
| virtual ~Pad1(); | |
| }; | |
| struct Proxy1 : Pad1, B1 { | |
| virtual ~Proxy1(); | |
| }; | |
| struct D : virtual Proxy1 { | |
| virtual ~D(); | |
| virtual D &foo1(); | |
| }; | |
| D& D::foo1() { | |
| return *this; | |
| } | |
| // CHECK: define {{.*}} @_ZTcvn8_n32_v8_n24_N6Test131D4foo1Ev | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 -8 | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 -32 | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 -24 | |
| // CHECK: getelementptr inbounds i8, ptr {{.*}}, i64 8 | |
| // CHECK: ret ptr | |
| // WIN64-LABEL: define weak_odr dso_local noundef ptr @"?foo1@D@Test13@@$4PPPPPPPE@A@EAAAEAUB1@2@XZ"( | |
| // This adjustment. | |
| // WIN64: getelementptr inbounds i8, ptr {{.*}}, i64 -12 | |
| // Call implementation. | |
| // WIN64: call {{.*}} @"?foo1@D@Test13@@UEAAAEAU12@XZ"(ptr {{.*}}) | |
| // Virtual + nonvirtual return adjustment. | |
| // WIN64: load ptr, ptr %{{[^,]*}}, align 8 | |
| // WIN64: getelementptr inbounds i32, ptr %{{[^,]*}}, i32 1 | |
| // WIN64: load i32, ptr %{{[^,]*}}, align 4 | |
| // WIN64: getelementptr inbounds i8, ptr %{{[^,]*}}, i32 %{{[^,]*}} | |
| } | |
| namespace Test14 { | |
| class A { | |
| virtual void f(); | |
| }; | |
| class B { | |
| virtual void f(); | |
| }; | |
| class C : public A, public B { | |
| virtual void f(); | |
| }; | |
| void C::f() { | |
| } | |
| // CHECK: define{{.*}} void @_ZThn8_N6Test141C1fEv({{.*}}) unnamed_addr [[NUW:#[0-9]+]] | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret void | |
| } | |
| // Varargs non-covariant thunk test. | |
| // PR18098 | |
| namespace Test15 { | |
| struct A { | |
| virtual ~A(); | |
| }; | |
| struct B { | |
| virtual void f(int x, ...); | |
| }; | |
| struct C : A, B { | |
| virtual void c(); | |
| virtual void f(int x, ...); | |
| }; | |
| void C::c() {} | |
| // C::c | |
| // CHECK-CLONE: declare void @_ZN6Test151C1fEiz | |
| // non-virtual thunk to C::f | |
| // CHECK-CLONE: declare void @_ZThn8_N6Test151C1fEiz | |
| // If we have musttail, then we emit the thunk as available_externally. | |
| // CHECK-TAIL: declare void @_ZN6Test151C1fEiz | |
| // CHECK-TAIL: define available_externally void @_ZThn8_N6Test151C1fEiz({{.*}}) | |
| // CHECK-TAIL: musttail call void (ptr, i32, ...) @_ZN6Test151C1fEiz({{.*}}, ...) | |
| // MS C++ ABI doesn't use a thunk, so this case isn't interesting. | |
| } | |
| namespace Test16 { | |
| struct A { | |
| virtual ~A(); | |
| }; | |
| struct B { | |
| virtual void foo(); | |
| }; | |
| struct C : public A, public B { | |
| void foo() {} | |
| }; | |
| struct D : public C { | |
| ~D(); | |
| }; | |
| D::~D() {} | |
| // CHECK: define linkonce_odr void @_ZThn8_N6Test161C3fooEv({{.*}}) {{.*}} comdat | |
| // CHECK-DBG-NOT: dbg.declare | |
| // CHECK: ret void | |
| } | |
| namespace Test17 { | |
| class A { | |
| virtual void f(const char *, ...); | |
| }; | |
| class B { | |
| virtual void f(const char *, ...); | |
| }; | |
| class C : A, B { | |
| virtual void anchor(); | |
| void f(const char *, ...) override; | |
| }; | |
| // Key method and object anchor vtable for Itanium and MSVC. | |
| void C::anchor() {} | |
| C c; | |
| // CHECK-CLONE-LABEL: declare void @_ZThn8_N6Test171C1fEPKcz( | |
| // CHECK-TAIL-LABEL: define available_externally void @_ZThn8_N6Test171C1fEPKcz( | |
| // CHECK-TAIL: getelementptr inbounds i8, ptr %{{.*}}, i64 -8 | |
| // CHECK-TAIL: musttail call {{.*}} @_ZN6Test171C1fEPKcz({{.*}}, ...) | |
| // MSVC-LABEL: define linkonce_odr dso_local void @"?f@C@Test17@@G7EAAXPEBDZZ" | |
| // MSVC-SAME: (ptr %this, ptr %[[ARG:[^,]+]], ...) | |
| // MSVC: getelementptr i8, ptr %{{.*}}, i32 -8 | |
| // MSVC: musttail call void (ptr, ptr, ...) @"?f@C@Test17@@EEAAXPEBDZZ"(ptr %{{.*}}, ptr noundef %[[ARG]], ...) | |
| } | |
| /**** The following has to go at the end of the file ****/ | |
| // checking without opt | |
| // CHECK-NONOPT-LABEL: define internal void @_ZThn8_N6Test4B12_GLOBAL__N_11C1fEv( | |
| // CHECK-NONOPT-NOT: comdat | |
| // This is from Test5: | |
| // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test51B1fEv | |
| // This is from Test10: | |
| // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZN6Test101C3fooEv | |
| // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZThn8_N6Test101C3fooEv | |
| // Checking with opt | |
| // CHECK-OPT-LABEL: define internal void @_ZThn8_N6Test4B12_GLOBAL__N_11C1fEv(ptr noundef %this) unnamed_addr #1 align 2 | |
| // This is from Test5: | |
| // CHECK-OPT-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test51B1fEv | |
| // This is from Test10: | |
| // CHECK-OPT-LABEL: define linkonce_odr void @_ZN6Test101C3fooEv | |
| // CHECK-OPT-LABEL: define linkonce_odr void @_ZThn8_N6Test101C3fooEv | |
| // This is from Test10: | |
| // WIN64-LABEL: define linkonce_odr dso_local void @"?foo@C@Test10@@UEAAXXZ"( | |
| // WIN64-LABEL: define linkonce_odr dso_local void @"?foo@C@Test10@@W7EAAXXZ"( | |
| // CHECK-NONOPT: attributes [[NUW]] = { noinline nounwind optnone uwtable{{.*}} } | |
| // CHECK-OPT: attributes [[NUW]] = { nounwind uwtable{{.*}} } |