diff options
Diffstat (limited to 'test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp')
| -rw-r--r-- | test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp | 324 | 
1 files changed, 324 insertions, 0 deletions
diff --git a/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp new file mode 100644 index 000000000000..3fef0e409349 --- /dev/null +++ b/test/CodeGenCXX/microsoft-abi-vtables-virtual-inheritance-vtordisps.cpp @@ -0,0 +1,324 @@ +// 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; +}  | 
