Files
llvm-project/clang/test/CodeGenCXX/microsoft-abi-vtables-single-inheritance.cpp
David Majnemer 70effde0af [MS ABI] Let arbitrary entities participate in vftable ordering
In the Microsoft ABI, the vftable is laid out in the order in the
declaration order of the entities defined within it.

Obviously, only virtual methods end up in the vftable but they will be
placed into the table at the same position as the first entity with the
same name.

llvm-svn: 253523
2015-11-19 00:03:54 +00:00

317 lines
8.5 KiB
C++

// RUN: %clang_cc1 %s -fno-rtti -triple=i386-pc-win32 -emit-llvm -fdump-vtable-layouts -o %t.ll > %t
// RUN: FileCheck --check-prefix=EMITS-VFTABLE %s < %t.ll
// RUN: FileCheck --check-prefix=NO-VFTABLE %s < %t.ll
// RUN: FileCheck %s < %t
struct A {
// CHECK-LABEL: VFTable for 'A' (3 entries)
// CHECK-NEXT: 0 | void A::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-LABEL: VFTable indices for 'A' (3 entries)
// CHECK-NEXT: 0 | void A::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
virtual void f();
virtual void g();
virtual void h();
int ia;
};
A a;
// EMITS-VFTABLE-DAG: @"\01??_7A@@6B@" = linkonce_odr unnamed_addr constant [3 x i8*]
void use(A *obj) { obj->f(); }
struct B : A {
// CHECK-LABEL: VFTable for 'A' in 'B' (5 entries)
// CHECK-NEXT: 0 | void B::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-NEXT: 3 | void B::i()
// CHECK-NEXT: 4 | void B::j()
// CHECK-LABEL: VFTable indices for 'B' (3 entries)
// CHECK-NEXT: 0 | void B::f()
// CHECK-NEXT: 3 | void B::i()
// CHECK-NEXT: 4 | void B::j()
virtual void f(); // overrides A::f()
virtual void i();
virtual void j();
};
B b;
// EMITS-VFTABLE-DAG: @"\01??_7B@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
void use(B *obj) { obj->f(); }
struct C {
// CHECK-LABEL: VFTable for 'C' (2 entries)
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-NEXT: 1 | void C::f()
// CHECK-LABEL: VFTable indices for 'C' (2 entries).
// CHECK-NEXT: 0 | C::~C() [scalar deleting]
// CHECK-NEXT: 1 | void C::f()
virtual ~C();
virtual void f();
};
void C::f() {}
// NO-VFTABLE-NOT: @"\01??_7C@@6B@"
void use(C *obj) { obj->f(); }
struct D {
// CHECK-LABEL: VFTable for 'D' (2 entries)
// CHECK-NEXT: 0 | void D::f()
// CHECK-NEXT: 1 | D::~D() [scalar deleting]
// CHECK-LABEL: VFTable indices for 'D' (2 entries)
// CHECK-NEXT: 0 | void D::f()
// CHECK-NEXT: 1 | D::~D() [scalar deleting]
virtual void f();
virtual ~D();
};
D d;
// EMITS-VFTABLE-DAG: @"\01??_7D@@6B@" = linkonce_odr unnamed_addr constant [2 x i8*]
void use(D *obj) { obj->f(); }
struct E : A {
// CHECK-LABEL: VFTable for 'A' in 'E' (5 entries)
// CHECK-NEXT: 0 | void A::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-NEXT: 4 | void E::i()
// CHECK-LABEL: VFTable indices for 'E' (2 entries).
// CHECK-NEXT: 3 | E::~E() [scalar deleting]
// CHECK-NEXT: 4 | void E::i()
// ~E would be the key method, but it isn't used, and MS ABI has no key
// methods.
virtual ~E();
virtual void i();
};
void E::i() {}
// NO-VFTABLE-NOT: @"\01??_7E@@6B@"
void use(E *obj) { obj->i(); }
struct F : A {
// CHECK-LABEL: VFTable for 'A' in 'F' (5 entries)
// CHECK-NEXT: 0 | void A::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-NEXT: 3 | void F::i()
// CHECK-NEXT: 4 | F::~F() [scalar deleting]
// CHECK-LABEL: VFTable indices for 'F' (2 entries).
// CHECK-NEXT: 3 | void F::i()
// CHECK-NEXT: 4 | F::~F() [scalar deleting]
virtual void i();
virtual ~F();
};
F f;
// EMITS-VFTABLE-DAG: @"\01??_7F@@6B@" = linkonce_odr unnamed_addr constant [5 x i8*]
void use(F *obj) { obj->i(); }
struct G : E {
// CHECK-LABEL: VFTable for 'A' in 'E' in 'G' (6 entries)
// CHECK-NEXT: 0 | void G::f()
// CHECK-NEXT: 1 | void A::g()
// CHECK-NEXT: 2 | void A::h()
// CHECK-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-NEXT: 4 | void E::i()
// CHECK-NEXT: 5 | void G::j()
// CHECK-LABEL: VFTable indices for 'G' (3 entries).
// CHECK-NEXT: 0 | void G::f()
// CHECK-NEXT: 3 | G::~G() [scalar deleting]
// CHECK-NEXT: 5 | void G::j()
virtual void f(); // overrides A::f()
virtual ~G();
virtual void j();
};
void G::j() {}
// NO-VFTABLE-NOT: @"\01??_7G@@6B@"
void use(G *obj) { obj->j(); }
// Test that the usual Itanium-style key method does not emit a vtable.
struct H {
virtual void f();
};
void H::f() {}
// NO-VFTABLE-NOT: @"\01??_7H@@6B@"
struct Empty { };
struct I : Empty {
// CHECK-LABEL: VFTable for 'I' (2 entries)
// CHECK-NEXT: 0 | void I::f()
// CHECK-NEXT: 1 | void I::g()
virtual void f();
virtual void g();
};
I i;
void use(I *obj) { obj->f(); }
struct J {
// CHECK-LABEL: VFTable for 'J' (6 entries)
// CHECK-NEXT: 0 | void J::foo(long)
// CHECK-NEXT: 1 | void J::foo(int)
// CHECK-NEXT: 2 | void J::foo(short)
// CHECK-NEXT: 3 | void J::bar(long)
// CHECK-NEXT: 4 | void J::bar(int)
// CHECK-NEXT: 5 | void J::bar(short)
virtual void foo(short);
virtual void bar(short);
virtual void foo(int);
virtual void bar(int);
virtual void foo(long);
virtual void bar(long);
};
J j;
void use(J *obj) { obj->foo(42); }
struct K : J {
// CHECK-LABEL: VFTable for 'J' in 'K' (9 entries)
// CHECK-NEXT: 0 | void J::foo(long)
// CHECK-NEXT: 1 | void J::foo(int)
// CHECK-NEXT: 2 | void J::foo(short)
// CHECK-NEXT: 3 | void J::bar(long)
// CHECK-NEXT: 4 | void J::bar(int)
// CHECK-NEXT: 5 | void J::bar(short)
// CHECK-NEXT: 6 | void K::bar(double)
// CHECK-NEXT: 7 | void K::bar(float)
// CHECK-NEXT: 8 | void K::foo(float)
virtual void bar(float);
virtual void foo(float);
virtual void bar(double);
};
K k;
void use(K *obj) { obj->foo(42.0f); }
struct L : J {
// CHECK-LABEL: VFTable for 'J' in 'L' (9 entries)
// CHECK-NEXT: 0 | void J::foo(long)
// CHECK-NEXT: 1 | void L::foo(int)
// CHECK-NEXT: 2 | void J::foo(short)
// CHECK-NEXT: 3 | void J::bar(long)
// CHECK-NEXT: 4 | void J::bar(int)
// CHECK-NEXT: 5 | void J::bar(short)
// CHECK-NEXT: 6 | void L::foo(float)
// CHECK-NEXT: 7 | void L::bar(double)
// CHECK-NEXT: 8 | void L::bar(float)
// This case is interesting. Since the J::foo(int) override is the first method in
// the class, foo(float) precedes the bar(double) and bar(float) in the vftable.
virtual void foo(int);
virtual void bar(float);
virtual void foo(float);
virtual void bar(double);
};
L l;
void use(L *obj) { obj->foo(42.0f); }
struct M : J {
// CHECK-LABEL: VFTable for 'J' in 'M' (11 entries)
// CHECK-NEXT: 0 | void J::foo(long)
// CHECK-NEXT: 1 | void M::foo(int)
// CHECK-NEXT: 2 | void J::foo(short)
// CHECK-NEXT: 3 | void J::bar(long)
// CHECK-NEXT: 4 | void J::bar(int)
// CHECK-NEXT: 5 | void J::bar(short)
// CHECK-NEXT: 6 | void M::foo(float)
// CHECK-NEXT: 7 | void M::spam(long)
// CHECK-NEXT: 8 | void M::spam(int)
// CHECK-NEXT: 9 | void M::bar(double)
// CHECK-NEXT: 10 | void M::bar(float)
virtual void foo(int);
virtual void spam(int);
virtual void bar(float);
virtual void bar(double);
virtual void foo(float);
virtual void spam(long);
};
M m;
void use(M *obj) { obj->foo(42.0f); }
struct N {
// CHECK-LABEL: VFTable for 'N' (4 entries)
// CHECK-NEXT: 0 | void N::operator+(int)
// CHECK-NEXT: 1 | void N::operator+(short)
// CHECK-NEXT: 2 | void N::operator*(int)
// CHECK-NEXT: 3 | void N::operator*(short)
virtual void operator+(short);
virtual void operator*(short);
virtual void operator+(int);
virtual void operator*(int);
};
N n;
void use(N *obj) { obj->operator+(42); }
struct O { virtual A *f(); };
struct P : O { virtual B *f(); };
P p;
void use(O *obj) { obj->f(); }
void use(P *obj) { obj->f(); }
// CHECK-LABEL: VFTable for 'O' (1 entry)
// CHECK-NEXT: 0 | A *O::f()
// CHECK-LABEL: VFTable for 'O' in 'P' (1 entry)
// CHECK-NEXT: 0 | B *P::f()
struct Q {
// CHECK-LABEL: VFTable for 'Q' (2 entries)
// CHECK-NEXT: 0 | void Q::foo(int)
// CHECK-NEXT: 1 | void Q::bar(int)
void foo(short);
void bar(short);
virtual void bar(int);
virtual void foo(int);
};
Q q;
void use(Q *obj) { obj->foo(42); }
// Inherited non-virtual overloads don't participate in the ordering.
struct R : Q {
// CHECK-LABEL: VFTable for 'Q' in 'R' (4 entries)
// CHECK-NEXT: 0 | void Q::foo(int)
// CHECK-NEXT: 1 | void Q::bar(int)
// CHECK-NEXT: 2 | void R::bar(long)
// CHECK-NEXT: 3 | void R::foo(long)
virtual void bar(long);
virtual void foo(long);
};
R r;
void use(R *obj) { obj->foo(42l); }
struct S {
// CHECK-LABEL: VFTable for 'S' (1 entry).
// CHECK-NEXT: 0 | void S::f() [deleted]
virtual void f() = delete;
S();
// EMITS-VFTABLE-DAG: @"\01??_7S@@6B@" = linkonce_odr unnamed_addr constant [1 x i8*] [i8* bitcast (void ()* @_purecall to i8*)]
};
S::S() {}
struct T {
struct U {};
};
struct V : T {
// CHECK-LABEL: VFTable for 'V' (2 entries).
// CHECK-NEXT: 0 | void V::U()
// CHECK-NEXT: 1 | void V::f()
using T::U;
virtual void f();
virtual void U();
V();
};
V::V() {}