f4a2713ac8
Change-Id: Ia40e9ffdf29b5dab2f122f673ff6802a58bc690f
324 lines
11 KiB
C++
324 lines
11 KiB
C++
// RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -cxx-abi microsoft -triple=i386-pc-win32 >%t
|
|
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t
|
|
// RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t
|
|
// RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
|
|
|
|
// For now, just make sure x86_64 doesn't crash.
|
|
// RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -cxx-abi microsoft -triple=x86_64-pc-win32 >/dev/null
|
|
|
|
struct V1 {
|
|
virtual void f();
|
|
virtual ~V1();
|
|
};
|
|
|
|
struct V2 {
|
|
virtual void f();
|
|
virtual ~V2();
|
|
int v;
|
|
};
|
|
|
|
struct Z {
|
|
virtual void g();
|
|
virtual ~Z();
|
|
int x;
|
|
};
|
|
|
|
struct V3 : Z, V2 {
|
|
};
|
|
|
|
struct V4 : Z, V1, V2 {
|
|
int y;
|
|
};
|
|
|
|
void use_somewhere_else(void*);
|
|
|
|
namespace simple {
|
|
// In case of a single-layer virtual inheritance, the "this" adjustment is done
|
|
// staically:
|
|
// struct A {
|
|
// virtual void f(); // Expects "(A*)this" in ECX
|
|
// };
|
|
// struct B : virtual A {
|
|
// virtual void f(); // Expects "(char*)(B*)this + 12" in ECX
|
|
// virtual ~B(); // Might call f()
|
|
// };
|
|
//
|
|
// If a class overrides a virtual function of its base and has a non-trivial
|
|
// ctor/dtor that call(s) the virtual function (or may escape "this" to some
|
|
// code that might call it), a virtual adjustment might be needed in case the
|
|
// current class layout and the most derived class layout are different.
|
|
// This is done using vtordisp thunks.
|
|
//
|
|
// A simple vtordisp{A,B} thunk for Method@Class is something like:
|
|
// sub ecx, [ecx+A] // apply the vtordisp adjustment
|
|
// sub ecx, B // apply the subobject adjustment, if needed.
|
|
// jmp Method@Class
|
|
|
|
struct A : virtual V1 {
|
|
// VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries).
|
|
// VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f()
|
|
// VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
// VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
|
|
// VTABLE-SIMPLE-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
virtual void f();
|
|
// MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
|
|
|
|
virtual ~A();
|
|
// MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
A a;
|
|
|
|
struct B : virtual V3 {
|
|
// VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
|
|
// VTABLE-SIMPLE-B-NEXT: 0 | void Z::g()
|
|
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
|
|
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
|
|
// VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
|
|
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
|
// VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
|
|
// VTABLE-SIMPLE-B-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
|
|
|
// FIXME: The vtordisp thunk should only get emitted for a constructor
|
|
// if "this" leaves scope.
|
|
B() { use_somewhere_else(this); }
|
|
|
|
virtual void f();
|
|
// MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
|
|
|
|
// Has an implicit destructor.
|
|
// MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
|
|
// MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
B b;
|
|
|
|
struct C : virtual V4 {
|
|
// VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
|
|
// VTABLE-SIMPLE-C-NEXT: 0 | void Z::g()
|
|
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
|
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
|
|
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
|
|
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
|
|
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
|
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
|
|
|
|
// VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
|
|
// VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
|
|
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
|
|
// VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
|
|
// VTABLE-SIMPLE-C-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
|
|
|
|
int x;
|
|
virtual void f();
|
|
// MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
|
|
// MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
|
|
virtual ~C();
|
|
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
|
|
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
|
|
// MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
C c;
|
|
}
|
|
|
|
namespace extended {
|
|
// If a virtual function requires vtordisp adjustment and the final overrider
|
|
// is defined in another vitual base of the most derived class,
|
|
// we need to know two vbase offsets.
|
|
// In this case, we should use the extended form of vtordisp thunks, called
|
|
// vtordispex thunks.
|
|
//
|
|
// vtordispex{A,B,C,D} thunk for Method@Class is something like:
|
|
// sub ecx, [ecx+C] // apply the vtordisp adjustment
|
|
// sub ecx, A // jump to the vbtable of the most derived class
|
|
// mov eax, [ecx] // load the vbtable address
|
|
// add ecx, [eax+B] // lookup the final overrider's vbase offset
|
|
// add ecx, D // apphy the subobject offset if needed
|
|
// jmp Method@Class
|
|
|
|
struct A : virtual simple::A {
|
|
// VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
|
|
// VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f()
|
|
// VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
|
// VTABLE-EXTENDED-A-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
|
// VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
|
|
// VTABLE-EXTENDED-A-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// `vtordispex{8,8,4294967292,8}'
|
|
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
|
|
|
|
virtual ~A();
|
|
// vtordisp{4294967292,0}
|
|
// MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
A a;
|
|
|
|
struct B : virtual simple::A {
|
|
// This class has an implicit dtor. Vdtors don't require vtordispex thunks
|
|
// as the most derived class always has an implicit dtor,
|
|
// which is a final overrider.
|
|
|
|
// VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
|
|
// ...
|
|
// VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
|
|
// VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// vtordisp{4294967292,0}
|
|
// MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
B b;
|
|
|
|
struct C : virtual simple::A {
|
|
// VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
|
|
// VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f()
|
|
// VTABLE-EXTENDED-C-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left,
|
|
// VTABLE-EXTENDED-C-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
|
|
|
// `vtordispex{12,8,4294967292,8}'
|
|
// MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
|
|
int x;
|
|
virtual ~C();
|
|
// MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
C c;
|
|
|
|
struct D : virtual V2 {
|
|
virtual void f();
|
|
virtual ~D();
|
|
int x;
|
|
};
|
|
|
|
struct E : virtual D {
|
|
// VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
|
|
// VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f()
|
|
// VTABLE-EXTENDED-E-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
|
// VTABLE-EXTENDED-E-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
|
|
|
|
// `vtordispex{8,8,4294967292,12}'
|
|
// MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
|
|
|
|
virtual ~E();
|
|
// MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
E e;
|
|
|
|
struct F : virtual Z, virtual D {
|
|
// VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
|
|
// VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f()
|
|
// VTABLE-EXTENDED-F-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left,
|
|
// VTABLE-EXTENDED-F-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
|
|
|
|
// `vtordispex{20,12,4294967292,12}'
|
|
// MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
|
|
int x;
|
|
virtual ~F();
|
|
// MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
|
|
};
|
|
|
|
F f;
|
|
|
|
struct G : virtual simple::A {
|
|
// VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries).
|
|
// VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g()
|
|
|
|
// VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
|
|
// VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f()
|
|
// VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
|
// VTABLE-EXTENDED-G-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
|
// VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
|
|
// VTABLE-EXTENDED-G-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
|
|
|
|
// Emits a G's own vfptr, thus moving the vbptr in the layout.
|
|
virtual void g();
|
|
|
|
virtual ~G();
|
|
// vtordisp{4294967292,0}
|
|
// MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
|
|
};
|
|
|
|
G g;
|
|
|
|
struct H : Z, A {
|
|
// VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries).
|
|
// VTABLE-EXTENDED-H-NEXT: 0 | void Z::g()
|
|
// VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting]
|
|
|
|
// VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
|
|
// VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f()
|
|
// VTABLE-EXTENDED-H-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
|
|
// VTABLE-EXTENDED-H-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
|
|
|
|
// MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
|
|
// MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
|
|
};
|
|
|
|
H h;
|
|
}
|
|
|
|
namespace pr17738 {
|
|
// These classes should have vtordispex thunks but MSVS CL miscompiles them.
|
|
// Just do the right thing.
|
|
|
|
struct A : virtual simple::B {
|
|
// VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
|
|
// VTABLE-PR17738-A-NEXT: 0 | void simple::B::f()
|
|
// VTABLE-PR17738-A-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left,
|
|
// VTABLE-PR17738-A-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
|
|
|
|
// MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
|
|
int a;
|
|
virtual ~A();
|
|
};
|
|
|
|
A a;
|
|
}
|
|
|
|
namespace access {
|
|
struct A {
|
|
virtual ~A();
|
|
protected:
|
|
virtual void prot();
|
|
private:
|
|
virtual void priv();
|
|
};
|
|
|
|
struct B : virtual A {
|
|
virtual ~B();
|
|
protected:
|
|
virtual void prot();
|
|
// MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
|
|
private:
|
|
virtual void priv();
|
|
// MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
|
|
};
|
|
|
|
B b;
|
|
|
|
struct C : virtual B {
|
|
virtual ~C();
|
|
|
|
// MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
|
|
// MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
|
|
};
|
|
|
|
C c;
|
|
}
|