1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -triple=i386-pc-win32 > %t
2 // RUN: FileCheck %s < %t
3 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
5 // For now, just make sure x86_64 doesn't crash.
6 // RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -triple=x86_64-pc-win32 > /dev/null
28 struct V4
: Z
, V1
, V2
{
32 void use_somewhere_else(void*);
35 // In case of a single-layer virtual inheritance, the "this" adjustment for a
36 // virtual method is done statically:
38 // virtual void f(); // Expects "(A*)this" in ECX
40 // struct B : virtual A {
41 // virtual void f(); // Expects "(char*)(B*)this + 12" in ECX
42 // virtual ~B(); // Might call f()
45 // If a class overrides a virtual function of its base and has a non-trivial
46 // ctor/dtor that call(s) the virtual function (or may escape "this" to some
47 // code that might call it), a virtual adjustment might be needed in case the
48 // current class layout and the most derived class layout are different.
49 // This is done using vtordisp thunks.
51 // A simple vtordisp{x,y} thunk for Method@Class is something like:
52 // sub ecx, [ecx+x] // apply the vtordisp adjustment
53 // sub ecx, y // apply the subobject adjustment, if needed.
56 struct A
: virtual V1
{
57 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' (2 entries).
58 // CHECK-NEXT: 0 | void simple::A::f()
59 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
60 // CHECK-NEXT: 1 | simple::A::~A() [scalar deleting]
61 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
63 // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
64 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
66 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
67 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
70 // MANGLING-DAG: @"?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
73 // MANGLING-DAG: @"??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
77 void use(A
*obj
) { obj
->f(); }
79 struct B
: virtual V3
{
80 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
81 // CHECK-NEXT: 0 | void Z::g()
82 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
83 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
85 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
86 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
88 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
89 // CHECK-NEXT: 0 | void simple::B::f()
90 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
91 // CHECK-NEXT: 1 | simple::B::~B() [scalar deleting]
92 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
94 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
95 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
97 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
98 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
100 // FIXME: The vtordisp thunk should only get emitted for a constructor
101 // if "this" leaves scope.
102 B() { use_somewhere_else(this); }
105 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
107 // Has an implicit destructor.
108 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
109 // MANGLING-DAG: @"??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
113 void use(B
*obj
) { obj
->f(); }
115 struct C
: virtual V4
{
116 // CHECK-LABEL: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
117 // CHECK-NEXT: 0 | void Z::g()
118 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
119 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
121 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
122 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
124 // CHECK-LABEL: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
125 // CHECK-NEXT: 0 | void simple::C::f()
126 // CHECK-NEXT: [this adjustment: vtordisp at -12, 0 non-virtual]
127 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
128 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
130 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
131 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
133 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
134 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
136 // CHECK-LABEL: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
137 // CHECK-NEXT: 0 | void simple::C::f()
138 // CHECK-NEXT: [this adjustment: vtordisp at -16, -4 non-virtual]
139 // CHECK-NEXT: 1 | simple::C::~C() [scalar deleting]
140 // CHECK-NEXT: [this adjustment: vtordisp at -16, -12 non-virtual]
142 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
143 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
145 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
146 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
150 // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPA@3AEXXZ"
151 // MANGLING-DAG: @"?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
153 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
154 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
155 // MANGLING-DAG: @"??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
159 void use(C
*obj
) { obj
->f(); }
162 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'simple::D' (2 entries).
163 // CHECK-NEXT: 0 | void simple::B::f()
164 // CHECK-NEXT: [this adjustment: vtordisp at -12, -4 non-virtual]
165 // CHECK-NEXT: 1 | simple::D::~D() [scalar deleting]
166 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
170 // MANGLING-DAG: @"?f@B@simple@@$4PPPPPPPE@3AEXXZ"
179 struct F
: virtual E
{
180 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
181 // CHECK-NEXT: 0 | void simple::F::g()
182 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
183 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
184 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
186 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' (2 entries).
187 // CHECK-NEXT: 0 | void simple::E::f()
188 // CHECK-NEXT: 1 | simple::F::~F() [scalar deleting]
189 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
192 virtual void g(); // Force a vtordisp.
195 // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z
196 // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z
202 // CHECK-LABEL: VFTable for 'Z' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
203 // CHECK-NEXT: 0 | void simple::F::g()
204 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
205 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
206 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
208 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::E' in 'simple::F' in 'simple::G' (2 entries).
209 // CHECK-NEXT: 0 | void simple::E::f()
210 // CHECK-NEXT: 1 | simple::G::~G() [scalar deleting]
211 // CHECK-NEXT: [this adjustment: vtordisp at -12, -8 non-virtual]
216 // MANGLING-DAG: @"?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z"
217 // MANGLING-DAG: @"?f@E@simple@@UAEXXZ"{{.*}}@"??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z"
224 // If a virtual function requires vtordisp adjustment and the final overrider
225 // is defined in another virtual base of the most derived class,
226 // we need to know two vbase offsets.
227 // In this case, we should use the extended form of vtordisp thunks, called
228 // vtordispex thunks.
230 // vtordispex{x,y,z,w} thunk for Method@Class is something like:
231 // sub ecx, [ecx+z] // apply the vtordisp adjustment
232 // sub ecx, x // jump to the vbptr of the most derived class
233 // mov eax, [ecx] // load the vbtable address
234 // add ecx, [eax+y] // lookup the final overrider's vbase offset
235 // add ecx, w // apphy the subobject offset if needed
238 struct A
: virtual simple::A
{
239 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
240 // CHECK-NEXT: 0 | void simple::A::f()
241 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
242 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
243 // CHECK-NEXT: 1 | extended::A::~A() [scalar deleting]
244 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
246 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
247 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
248 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
250 // `vtordispex{8,8,4294967292,8}'
251 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
254 // vtordisp{4294967292,0}
255 // MANGLING-DAG: @"??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
259 void use(A
*obj
) { delete obj
; }
261 struct B
: virtual simple::A
{
262 // This class has an implicit dtor. Vdtors don't require vtordispex thunks
263 // as the most derived class always has an implicit dtor,
264 // which is a final overrider.
266 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
268 // CHECK: 1 | extended::B::~B() [scalar deleting]
269 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
271 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
272 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
273 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
275 // vtordisp{4294967292,0}
276 // MANGLING-DAG: @"??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
280 void use(B
*obj
) { delete obj
; }
282 struct C
: virtual simple::A
{
283 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
284 // CHECK-NEXT: 0 | void simple::A::f()
285 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 12 to the left,
286 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
288 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
289 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 12 to the left,
290 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
292 // `vtordispex{12,8,4294967292,8}'
293 // MANGLING-DAG: @"?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
296 // MANGLING-DAG: @"??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
300 void use(C
*obj
) { delete obj
; }
302 struct D
: virtual V2
{
308 struct E
: virtual D
{
309 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
310 // CHECK-NEXT: 0 | void extended::D::f()
311 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
312 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
314 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).
315 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
316 // CHECK-NEXT: vboffset at 8 in the vbtable, 12 non-virtual]
318 // `vtordispex{8,8,4294967292,12}'
319 // MANGLING-DAG: @"?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
322 // MANGLING-DAG: @"??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
326 void use(E
*obj
) { delete obj
; }
328 struct F
: virtual Z
, virtual D
{
329 // CHECK-LABEL: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
330 // CHECK-NEXT: 0 | void extended::D::f()
331 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 20 to the left,
332 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
334 // CHECK-LABEL: Thunks for 'void extended::D::f()' (1 entry).
335 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 20 to the left,
336 // CHECK-NEXT: vboffset at 12 in the vbtable, 12 non-virtual]
338 // `vtordispex{20,12,4294967292,12}'
339 // MANGLING-DAG: @"?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
342 // MANGLING-DAG: @"??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
346 void use(F
*obj
) { delete obj
; }
348 struct G
: virtual simple::A
{
349 // CHECK-LABEL: VFTable for 'extended::G' (1 entry).
350 // CHECK-NEXT: 0 | void extended::G::g()
352 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
353 // CHECK-NEXT: 0 | void simple::A::f()
354 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
355 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
356 // CHECK-NEXT: 1 | extended::G::~G() [scalar deleting]
357 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
359 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
360 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
361 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
363 // Emits a G's own vfptr, thus moving the vbptr in the layout.
367 // vtordisp{4294967292,0}
368 // MANGLING-DAG: @"??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
372 void use(G
*obj
) { obj
->g(); }
375 // CHECK-LABEL: VFTable for 'Z' in 'extended::H' (2 entries).
376 // CHECK-NEXT: 0 | void Z::g()
377 // CHECK-NEXT: 1 | extended::H::~H() [scalar deleting]
379 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
380 // CHECK-NEXT: 0 | void simple::A::f()
381 // CHECK-NEXT: [this adjustment: vtordisp at -4, vbptr at 8 to the left,
382 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
384 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
385 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, vbptr at 8 to the left,
386 // CHECK-NEXT: vboffset at 8 in the vbtable, 8 non-virtual]
388 // MANGLING-DAG: @"?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
389 // MANGLING-DAG: @"??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
393 void use(H
*obj
) { delete obj
; }
397 // These classes should have vtordispex thunks but MSVS CL miscompiles them.
398 // Just do the right thing.
400 struct A
: virtual simple::B
{
401 // CHECK-LABEL: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
402 // CHECK-NEXT: 0 | void simple::B::f()
403 // CHECK-NEXT: [this adjustment: vtordisp at -12, vbptr at 20 to the left,
404 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
406 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
407 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, vbptr at 20 to the left,
408 // CHECK-NEXT: vboffset at 8 in the vbtable, 16 non-virtual]
410 // MANGLING-DAG: @"?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
416 void use(A
*obj
) { delete obj
; }
420 // In this test, the vptr used to vcall D::f() is located in the A vbase.
421 // The offset of A in different in C and D, so the D vtordisp thunk should
422 // adjust "this" so C::f gets the right value.
429 struct B
: virtual A
{
441 // CHECK-LABEL: VFTable for 'pr19408::A' in 'pr19408::B' in 'pr19408::C' in 'pr19408::D' (1 entry).
442 // CHECK-NEXT: 0 | void pr19408::C::f()
443 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
445 // MANGLING-DAG: @"?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"
462 struct B
: virtual A
{
466 // MANGLING-DAG: @"?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
469 // MANGLING-DAG: @"?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
474 struct C
: virtual B
{
477 // MANGLING-DAG: @"?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
478 // MANGLING-DAG: @"?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
498 struct X
: B
, virtual C
{
502 // CHECK-LABEL: VFTable for 'pr19505::A' in 'pr19505::B' in 'pr19505::C' in 'pr19505::X' (2 entries).
503 // CHECK-NEXT: 0 | void pr19505::B::f()
504 // CHECK-NEXT: 1 | void pr19505::A::z()
506 // MANGLING-DAG: @"??_7X@pr19505@@6BB@1@@" = {{.*}}@"?f@B@pr19505@@UAEXXZ"
509 void build_vftable(X
*obj
) { obj
->g(); }
524 struct X
: C
, virtual B
{
528 // CHECK-LABEL: VFTable for 'pr19506::A' in 'pr19506::B' in 'pr19506::X' (2 entries).
529 // CHECK-NEXT: 0 | void pr19506::B::f()
530 // CHECK-NEXT: 1 | void pr19506::X::g()
531 // CHECK-NEXT: [this adjustment: vtordisp at -4, -12 non-virtual]
533 // MANGLING-DAG: @"??_7X@pr19506@@6BB@1@@" = {{.*}}@"?f@B@pr19506@@UAEXXZ"
536 void build_vftable(X
*obj
) { obj
->g(); }
540 // VS2013 CL miscompiles this, just make sure we don't regress.
547 struct B
: virtual A
{
552 struct C
: virtual A
{
559 // CHECK-LABEL: VFTable for 'pr19519::A' in 'pr19519::B' in 'pr19519::X' (2 entries).
560 // CHECK-NEXT: 0 | void pr19519::B::f()
561 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
562 // CHECK-NEXT: 1 | void pr19519::C::g()
563 // CHECK-NEXT: [this adjustment: vtordisp at -4, -4 non-virtual]
565 // MANGLING-DAG: @"??_7X@pr19519@@6B@" = {{.*}}@"?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"