|  | // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -triple=i386-pc-win32 > %t | 
|  | // RUN: FileCheck %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 -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 for a | 
|  | // virtual method is done statically: | 
|  | //   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{x,y} thunk for Method@Class is something like: | 
|  | //   sub  ecx, [ecx+x]  // apply the vtordisp adjustment | 
|  | //   sub  ecx, y        // apply the subobject adjustment, if needed. | 
|  | //   jmp Method@Class | 
|  |  | 
|  | struct A : virtual V1 { | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::A::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  | // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | virtual void f(); | 
|  | // MANGLING-DAG: @"?f@A@simple@@$4PPPPPPPM@A@AEXXZ" | 
|  |  | 
|  | virtual ~A(); | 
|  | // MANGLING-DAG: @"??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | A a; | 
|  | void use(A *obj) { obj->f(); } | 
|  |  | 
|  | struct B : virtual V3 { | 
|  | // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries). | 
|  | // CHECK-NEXT: 0 | void Z::g() | 
|  | // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::B::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual] | 
|  | // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 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: @"?f@B@simple@@$4PPPPPPPE@A@AEXXZ" | 
|  |  | 
|  | // Has an implicit destructor. | 
|  | // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z" | 
|  | // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | B b; | 
|  | void use(B *obj) { obj->f(); } | 
|  |  | 
|  | struct C : virtual V4 { | 
|  | // CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries). | 
|  | // CHECK-NEXT: 0 | void Z::g() | 
|  | // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::C::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual] | 
|  | // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::C::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -16, -4 non-virtual] | 
|  | // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -16, -12 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual] | 
|  |  | 
|  | int x; | 
|  | virtual void f(); | 
|  | // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPA@3AEXXZ" | 
|  | // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPE@A@AEXXZ" | 
|  | virtual ~C(); | 
|  | // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z" | 
|  | // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z" | 
|  | // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | C c; | 
|  | void use(C *obj) { obj->f(); } | 
|  |  | 
|  | class D : B { | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::B::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, -4 non-virtual] | 
|  | // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  | D(); | 
|  | int z; | 
|  |  | 
|  | // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@3AEXXZ" | 
|  | }; | 
|  |  | 
|  | D::D() {} | 
|  |  | 
|  | struct E : V3 { | 
|  | virtual void f(); | 
|  | }; | 
|  |  | 
|  | struct F : virtual E { | 
|  | // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries). | 
|  | // CHECK-NEXT:   0 | void simple::F::g() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  | // CHECK-NEXT:   1 | simple::F::~F() [scalar deleting] | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries). | 
|  | // CHECK-NEXT:   0 | void simple::E::f() | 
|  | // CHECK-NEXT:   1 | simple::F::~F() [scalar deleting] | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | F(); | 
|  | virtual void g();  // Force a vtordisp. | 
|  | int f; | 
|  |  | 
|  | // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z | 
|  | // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z | 
|  | }; | 
|  |  | 
|  | F::F() {} | 
|  |  | 
|  | struct G : F { | 
|  | // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries). | 
|  | // CHECK-NEXT:   0 | void simple::F::g() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual] | 
|  | // CHECK-NEXT:   1 | simple::G::~G() [scalar deleting] | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries). | 
|  | // CHECK-NEXT:   0 | void simple::E::f() | 
|  | // CHECK-NEXT:   1 | simple::G::~G() [scalar deleting] | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -12, -8 non-virtual] | 
|  |  | 
|  | G(); | 
|  | int g; | 
|  |  | 
|  | // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | // MANGLING-DAG: @"?f@E@simple@@UAEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | G::G() {} | 
|  | } | 
|  |  | 
|  | namespace extended { | 
|  | // If a virtual function requires vtordisp adjustment and the final overrider | 
|  | // is defined in another virtual 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{x,y,z,w} thunk for Method@Class is something like: | 
|  | //   sub  ecx, [ecx+z]  // apply the vtordisp adjustment | 
|  | //   sub  ecx, x        // jump to the vbptr of the most derived class | 
|  | //   mov  eax, [ecx]    // load the vbtable address | 
|  | //   add  ecx, [eax+y]  // lookup the final overrider's vbase offset | 
|  | //   add  ecx, w        // apphy the subobject offset if needed | 
|  | //   jmp Method@Class | 
|  |  | 
|  | struct A : virtual simple::A { | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::A::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  | // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // `vtordispex{8,8,4294967292,8}' | 
|  | // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ" | 
|  |  | 
|  | virtual ~A(); | 
|  | // vtordisp{4294967292,0} | 
|  | // MANGLING-DAG: @"??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | A a; | 
|  | void use(A *obj) { delete obj; } | 
|  |  | 
|  | 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. | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries). | 
|  | //  ... | 
|  | // CHECK: 1 | extended::B::~B() [scalar deleting] | 
|  | // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // vtordisp{4294967292,0} | 
|  | // MANGLING-DAG: @"??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | B b; | 
|  | void use(B *obj) { delete obj; } | 
|  |  | 
|  | struct C : virtual simple::A { | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::A::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 12 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // `vtordispex{12,8,4294967292,8}' | 
|  | // MANGLING-DAG: @"?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ" | 
|  | int x; | 
|  | virtual ~C(); | 
|  | // MANGLING-DAG: @"??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | C c; | 
|  | void use(C *obj) { delete obj; } | 
|  |  | 
|  | struct D : virtual V2 { | 
|  | virtual void f(); | 
|  | virtual ~D(); | 
|  | int x; | 
|  | }; | 
|  |  | 
|  | struct E : virtual D { | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries). | 
|  | // CHECK-NEXT: 0 | void extended::D::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual] | 
|  |  | 
|  | // `vtordispex{8,8,4294967292,12}' | 
|  | // MANGLING-DAG: @"?f@D@extended@@$R477PPPPPPPM@M@AEXXZ" | 
|  |  | 
|  | virtual ~E(); | 
|  | // MANGLING-DAG: @"??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | E e; | 
|  | void use(E *obj) { delete obj; } | 
|  |  | 
|  | struct F : virtual Z, virtual D { | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries). | 
|  | // CHECK-NEXT: 0 | void extended::D::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 20 to the left, | 
|  | // CHECK-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left, | 
|  | // CHECK-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual] | 
|  |  | 
|  | // `vtordispex{20,12,4294967292,12}' | 
|  | // MANGLING-DAG: @"?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ" | 
|  | int x; | 
|  | virtual ~F(); | 
|  | // MANGLING-DAG: @"??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | F f; | 
|  | void use(F *obj) { delete obj; } | 
|  |  | 
|  | struct G : virtual simple::A { | 
|  | // CHECK-LABEL: VFTable for 'extended::G' (1 entry). | 
|  | // CHECK-NEXT: 0 | void extended::G::g() | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::A::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  | // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting] | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 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: @"??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | G g; | 
|  | void use(G *obj) { obj->g(); } | 
|  |  | 
|  | struct H : Z, A { | 
|  | // CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries). | 
|  | // CHECK-NEXT: 0 | void Z::g() | 
|  | // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting] | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::A::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual] | 
|  |  | 
|  | // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ" | 
|  | // MANGLING-DAG: @"??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z" | 
|  | }; | 
|  |  | 
|  | H h; | 
|  | void use(H *obj) { delete obj; } | 
|  | } | 
|  |  | 
|  | namespace pr17738 { | 
|  | // These classes should have vtordispex thunks but MSVS CL miscompiles them. | 
|  | // Just do the right thing. | 
|  |  | 
|  | struct A : virtual simple::B { | 
|  | // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries). | 
|  | // CHECK-NEXT: 0 | void simple::B::f() | 
|  | // CHECK-NEXT:     [this adjustment: vtordisp at -12, vbptr at 20 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual] | 
|  |  | 
|  | // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry). | 
|  | // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left, | 
|  | // CHECK-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual] | 
|  |  | 
|  | // MANGLING-DAG: @"?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ" | 
|  | int a; | 
|  | virtual ~A(); | 
|  | }; | 
|  |  | 
|  | A a; | 
|  | void use(A *obj) { delete obj; } | 
|  | } | 
|  |  | 
|  | namespace pr19408 { | 
|  | // In this test, the vptr used to vcall D::f() is located in the A vbase. | 
|  | // The offset of A in different in C and D, so the D vtordisp thunk should | 
|  | // adjust "this" so C::f gets the right value. | 
|  | struct A { | 
|  | A(); | 
|  | virtual void f(); | 
|  | int a; | 
|  | }; | 
|  |  | 
|  | struct B : virtual A { | 
|  | B(); | 
|  | int b; | 
|  | }; | 
|  |  | 
|  | struct C : B { | 
|  | C(); | 
|  | virtual void f(); | 
|  | int c; | 
|  | }; | 
|  |  | 
|  | struct D : C { | 
|  | // CHECK-LABEL: VFTable for 'pr19408::A' in 'pr19408::B' in 'pr19408::C' in 'pr19408::D' (1 entry). | 
|  | // CHECK-NEXT:   0 | void pr19408::C::f() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual] | 
|  |  | 
|  | // MANGLING-DAG: @"?f@C@pr19408@@$4PPPPPPPM@3AEXXZ" | 
|  | D(); | 
|  | int d; | 
|  | }; | 
|  |  | 
|  | D::D() {} | 
|  | } | 
|  |  | 
|  | 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: @"?prot@B@access@@$2PPPPPPPM@A@AEXXZ" | 
|  | private: | 
|  | virtual void priv(); | 
|  | // MANGLING-DAG: @"?priv@B@access@@$0PPPPPPPM@A@AEXXZ" | 
|  | }; | 
|  |  | 
|  | B b; | 
|  |  | 
|  | struct C : virtual B { | 
|  | virtual ~C(); | 
|  |  | 
|  | // MANGLING-DAG: @"?prot@B@access@@$R277PPPPPPPM@7AEXXZ" | 
|  | // MANGLING-DAG: @"?priv@B@access@@$R077PPPPPPPM@7AEXXZ" | 
|  | }; | 
|  |  | 
|  | C c; | 
|  | } | 
|  |  | 
|  | namespace pr19505 { | 
|  | struct A { | 
|  | virtual void f(); | 
|  | virtual void z(); | 
|  | }; | 
|  |  | 
|  | struct B : A { | 
|  | virtual void f(); | 
|  | }; | 
|  |  | 
|  | struct C : A, B { | 
|  | virtual void g(); | 
|  | }; | 
|  |  | 
|  | struct X : B, virtual C { | 
|  | X() {} | 
|  | virtual void g(); | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'pr19505::A' in 'pr19505::B' in 'pr19505::C' in 'pr19505::X' (2 entries). | 
|  | // CHECK-NEXT:   0 | void pr19505::B::f() | 
|  | // CHECK-NEXT:   1 | void pr19505::A::z() | 
|  |  | 
|  | // MANGLING-DAG: @"??_7X@pr19505@@6BB@1@@" = {{.*}}@"?f@B@pr19505@@UAEXXZ" | 
|  | } x; | 
|  |  | 
|  | void build_vftable(X *obj) { obj->g(); } | 
|  | } | 
|  |  | 
|  | namespace pr19506 { | 
|  | struct A { | 
|  | virtual void f(); | 
|  | virtual void g(); | 
|  | }; | 
|  |  | 
|  | struct B : A { | 
|  | virtual void f(); | 
|  | }; | 
|  |  | 
|  | struct C : B {}; | 
|  |  | 
|  | struct X : C, virtual B { | 
|  | virtual void g(); | 
|  | X() {} | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'pr19506::A' in 'pr19506::B' in 'pr19506::X' (2 entries). | 
|  | // CHECK-NEXT:   0 | void pr19506::B::f() | 
|  | // CHECK-NEXT:   1 | void pr19506::X::g() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, -12 non-virtual] | 
|  |  | 
|  | // MANGLING-DAG: @"??_7X@pr19506@@6BB@1@@" = {{.*}}@"?f@B@pr19506@@UAEXXZ" | 
|  | } x; | 
|  |  | 
|  | void build_vftable(X *obj) { obj->g(); } | 
|  | } | 
|  |  | 
|  | namespace pr19519 { | 
|  | // VS2013 CL miscompiles this, just make sure we don't regress. | 
|  |  | 
|  | struct A { | 
|  | virtual void f(); | 
|  | virtual void g(); | 
|  | }; | 
|  |  | 
|  | struct B : virtual A { | 
|  | virtual void f(); | 
|  | B(); | 
|  | }; | 
|  |  | 
|  | struct C : virtual A { | 
|  | virtual void g(); | 
|  | }; | 
|  |  | 
|  | struct X : B, C { | 
|  | X(); | 
|  |  | 
|  | // CHECK-LABEL: VFTable for 'pr19519::A' in 'pr19519::B' in 'pr19519::X' (2 entries). | 
|  | // CHECK-NEXT:   0 | void pr19519::B::f() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual] | 
|  | // CHECK-NEXT:   1 | void pr19519::C::g() | 
|  | // CHECK-NEXT:       [this adjustment: vtordisp at -4, -4 non-virtual] | 
|  |  | 
|  | // MANGLING-DAG: @"??_7X@pr19519@@6B@" = {{.*}}@"?g@C@pr19519@@$4PPPPPPPM@3AEXXZ" | 
|  | }; | 
|  |  | 
|  | X::X() {} | 
|  | } |