Files
clang-p2996/clang/test/CodeGenCXX/microsoft-abi-vtables-return-thunks.cpp
Reid Kleckner dd6fc83cb4 [ms] Fix vbtable index for covariant overrides of vbase methods
Overriding a method from a virtual base with a covariant return type
consumes a slot from the vftable in the virtual base. This can make it
impossible to implement certain diamond inheritance hierarchies, but we
have to follow along for compatibility in the simple cases.

This patch only affects our vtable dumper and member pointer function
mangling, since all other callers of getMethodVFTableLocation seem to
recompute VBTableIndex instead of using the one in the method location.

Patch by David Majnemer

llvm-svn: 312017
2017-08-29 17:40:04 +00:00

219 lines
7.9 KiB
C++

// RUN: %clang_cc1 -fno-rtti %s -emit-llvm -o %t -triple=i386-pc-win32 -fdump-vtable-layouts 2>&1 | FileCheck --check-prefix=VFTABLES %s
// RUN: FileCheck --check-prefix=GLOBALS %s < %t
// RUN: FileCheck --check-prefix=CODEGEN %s < %t
namespace test1 {
// Some covariant types.
struct A { int a; };
struct B { int b; };
struct C : A, B { int c; };
struct D : C { int d; };
struct E : D { int e; };
// One base class and two overrides, all with covariant return types.
struct H { virtual B *foo(); };
struct I : H { virtual C *foo(); };
struct J : I { virtual D *foo(); J(); };
struct K : J { virtual E *foo(); K(); };
J::J() {}
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' (3 entries).
// VFTABLES-NEXT: 0 | test1::D *test1::J::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
// VFTABLES-NEXT: 1 | test1::D *test1::J::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
// VFTABLES-NEXT: 2 | test1::D *test1::J::foo()
// GLOBALS-LABEL: @"\01??_7J@test1@@6B@" = linkonce_odr unnamed_addr constant { [3 x i8*] }
// GLOBALS: @"\01?foo@J@test1@@QAEPAUB@2@XZ"
// GLOBALS: @"\01?foo@J@test1@@QAEPAUC@2@XZ"
// GLOBALS: @"\01?foo@J@test1@@UAEPAUD@2@XZ"
K::K() {}
// VFTABLES-LABEL: VFTable for 'test1::H' in 'test1::I' in 'test1::J' in 'test1::K' (4 entries).
// VFTABLES-NEXT: 0 | test1::E *test1::K::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual]
// VFTABLES-NEXT: 1 | test1::E *test1::K::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::C *'): 0 non-virtual]
// VFTABLES-NEXT: 2 | test1::E *test1::K::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test1::D *'): 0 non-virtual]
// VFTABLES-NEXT: 3 | test1::E *test1::K::foo()
// Only B to C requires adjustment, but we get 3 thunks in K's vftable, two of
// which are trivial.
// GLOBALS-LABEL: @"\01??_7K@test1@@6B@" = linkonce_odr unnamed_addr constant { [4 x i8*] }
// GLOBALS: @"\01?foo@K@test1@@QAEPAUB@2@XZ"
// GLOBALS: @"\01?foo@K@test1@@QAEPAUC@2@XZ"
// GLOBALS: @"\01?foo@K@test1@@QAEPAUD@2@XZ"
// GLOBALS: @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// This thunk has a return adjustment.
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUB@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN: icmp {{.*}}, null
// CODEGEN: getelementptr
// CODEGEN: ret
// These two don't.
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUC@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN-NEXT: ret
// CODEGEN-LABEL: define {{.*}} @"\01?foo@K@test1@@QAEPAUD@2@XZ"
// CODEGEN: call {{.*}} @"\01?foo@K@test1@@UAEPAUE@2@XZ"
// CODEGEN-NEXT: ret
}
namespace test2 {
// Covariant types. D* is not trivially convertible to C*.
struct A { int a; };
struct B { int b; };
struct C : B { int c; };
struct D : A, C { int d; };
struct E : D { int e; };
// J's foo will require an adjusting thunk, and K will require a trivial thunk.
struct H { virtual B *foo(); };
struct I : H { virtual C *foo(); };
struct J : I { virtual D *foo(); J(); };
struct K : J { virtual E *foo(); K(); };
J::J() {}
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' (2 entries).
// VFTABLES-NEXT: 0 | test2::D *test2::J::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
// VFTABLES-NEXT: 1 | test2::D *test2::J::foo()
// GLOBALS-LABEL: @"\01??_7J@test2@@6B@" = linkonce_odr unnamed_addr constant { [2 x i8*] }
K::K() {}
// VFTABLES-LABEL: VFTable for 'test2::H' in 'test2::I' in 'test2::J' in 'test2::K' (3 entries).
// VFTABLES-NEXT: 0 | test2::E *test2::K::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual]
// VFTABLES-NEXT: 1 | test2::E *test2::K::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct test2::D *'): 0 non-virtual]
// VFTABLES-NEXT: 2 | test2::E *test2::K::foo()
// GLOBALS-LABEL: @"\01??_7K@test2@@6B@" = linkonce_odr unnamed_addr constant { [3 x i8*] }
}
namespace pr20479 {
struct A {
virtual A *f();
};
struct B : virtual A {
virtual B *f();
};
struct C : virtual A, B {
// VFTABLES-LABEL: VFTable for 'pr20479::A' in 'pr20479::B' in 'pr20479::C' (2 entries).
// VFTABLES-NEXT: 0 | pr20479::B *pr20479::B::f()
// VFTABLES-NEXT: [return adjustment (to type 'struct pr20479::A *'): vbase #1, 0 non-virtual]
// VFTABLES-NEXT: 1 | pr20479::B *pr20479::B::f()
C();
};
C::C() {}
// GLOBALS-LABEL: @"\01??_7C@pr20479@@6B@" = linkonce_odr unnamed_addr constant { [2 x i8*] }
// GLOBALS: @"\01?f@B@pr20479@@QAEPAUA@2@XZ"
// GLOBALS: @"\01?f@B@pr20479@@UAEPAU12@XZ"
}
namespace pr21073 {
struct A {
virtual A *f();
};
struct B : virtual A {
virtual B *f();
};
struct C : virtual A, virtual B {
// VFTABLES-LABEL: VFTable for 'pr21073::A' in 'pr21073::B' in 'pr21073::C' (2 entries).
// VFTABLES-NEXT: 0 | pr21073::B *pr21073::B::f()
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073::A *'): vbase #1, 0 non-virtual]
// VFTABLES-NEXT: [this adjustment: 8 non-virtual]
// VFTABLES-NEXT: 1 | pr21073::B *pr21073::B::f()
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073::B *'): 0 non-virtual]
// VFTABLES-NEXT: [this adjustment: 8 non-virtual]
C();
};
C::C() {}
// GLOBALS-LABEL: @"\01??_7C@pr21073@@6B@" = linkonce_odr unnamed_addr constant { [2 x i8*] }
// GLOBALS: @"\01?f@B@pr21073@@WPPPPPPPI@AEPAUA@2@XZ"
// GLOBALS: @"\01?f@B@pr21073@@WPPPPPPPI@AEPAU12@XZ"
}
namespace pr21073_2 {
struct A { virtual A *foo(); };
struct B : virtual A {};
struct C : virtual A { virtual C *foo(); };
struct D : B, C { D(); };
D::D() {}
// VFTABLES-LABEL: VFTable for 'pr21073_2::A' in 'pr21073_2::C' in 'pr21073_2::D' (2 entries)
// VFTABLES-NEXT: 0 | pr21073_2::C *pr21073_2::C::foo()
// VFTABLES-NEXT: [return adjustment (to type 'struct pr21073_2::A *'): vbase #1, 0 non-virtual]
// VFTABLES-NEXT: 1 | pr21073_2::C *pr21073_2::C::foo()
// GLOBALS-LABEL: @"\01??_7D@pr21073_2@@6B@" = {{.*}} constant { [2 x i8*] }
// GLOBALS: @"\01?foo@C@pr21073_2@@QAEPAUA@2@XZ"
// GLOBALS: @"\01?foo@C@pr21073_2@@UAEPAU12@XZ"
}
namespace test3 {
struct A { virtual A *fn(); };
struct B : virtual A { virtual B *fn(); };
struct X : virtual B {};
struct Y : virtual B {};
struct C : X, Y {};
struct D : virtual B, virtual A, C {
D *fn();
D();
};
D::D() {}
// VFTABLES-LABEL: VFTable for 'test3::A' in 'test3::B' in 'test3::X' in 'test3::C' in 'test3::D' (3 entries).
// VFTABLES-NEXT: 0 | test3::D *test3::D::fn()
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::A *'): vbase #1, 0 non-virtual]
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// VFTABLES-NEXT: 1 | test3::D *test3::D::fn()
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::B *'): vbase #2, 0 non-virtual]
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// VFTABLES-NEXT: 2 | test3::D *test3::D::fn()
// VFTABLES-NEXT: [return adjustment (to type 'struct test3::D *'): 0 non-virtual]
// VFTABLES-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
// GLOBALS-LABEL: @"\01??_7D@test3@@6B@" = {{.*}} constant { [3 x i8*] }
// GLOBALS: @"\01?fn@D@test3@@$4PPPPPPPM@A@AEPAUA@2@XZ"
// GLOBALS: @"\01?fn@D@test3@@$4PPPPPPPM@A@AEPAUB@2@XZ"
// GLOBALS: @"\01?fn@D@test3@@$4PPPPPPPM@A@AEPAU12@XZ"
}
namespace pr34302 {
// C::f is lives in the vftable inside its virtual B subobject. In the MS ABI,
// covariant return type virtual methods extend vftables from virtual bases,
// even though that can make it impossible to implement certain diamond
// hierarchies correctly.
struct A { virtual ~A(); };
struct B : A { virtual B *f(); };
struct C : virtual B { C *f(); };
C c;
// VFTABLES-LABEL: VFTable indices for 'pr34302::C' (2 entries).
// VFTABLES-NEXT: -- accessible via vbtable index 1, vfptr at offset 0 --
// VFTABLES-NEXT: 0 | pr34302::C::~C() [scalar deleting]
// VFTABLES-NEXT: 2 | pr34302::C *pr34302::C::f()
}