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
4
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
7
8 struct V1 {
9 virtual void f();
10 virtual ~V1();
11 };
12
13 struct V2 {
14 virtual void f();
15 virtual ~V2();
16 int v;
17 };
18
19 struct Z {
20 virtual void g();
21 virtual ~Z();
22 int x;
23 };
24
25 struct V3 : Z, V2 {
26 };
27
28 struct V4 : Z, V1, V2 {
29 int y;
30 };
31
32 void use_somewhere_else(void*);
33
34 namespace simple {
35 // In case of a single-layer virtual inheritance, the "this" adjustment for a
36 // virtual method is done statically:
37 // struct A {
38 // virtual void f(); // Expects "(A*)this" in ECX
39 // };
40 // struct B : virtual A {
41 // virtual void f(); // Expects "(char*)(B*)this + 12" in ECX
42 // virtual ~B(); // Might call f()
43 // };
44 //
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.
50 //
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.
54 // jmp Method@Class
55
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]
62
63 // CHECK-LABEL: Thunks for 'simple::A::~A()' (1 entry).
64 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
65
66 // CHECK-LABEL: Thunks for 'void simple::A::f()' (1 entry).
67 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
68
69 virtual void f();
70 // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
71
72 virtual ~A();
73 // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
74 };
75
76 A a;
use(A * obj)77 void use(A *obj) { obj->f(); }
78
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]
84
85 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
86 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
87
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]
93
94 // CHECK-LABEL: Thunks for 'simple::B::~B()' (1 entry).
95 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
96
97 // CHECK-LABEL: Thunks for 'void simple::B::f()' (1 entry).
98 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
99
100 // FIXME: The vtordisp thunk should only get emitted for a constructor
101 // if "this" leaves scope.
Bsimple::B102 B() { use_somewhere_else(this); }
103
104 virtual void f();
105 // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
106
107 // Has an implicit destructor.
108 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
109 // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
110 };
111
112 B b;
use(B * obj)113 void use(B *obj) { obj->f(); }
114
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]
120
121 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
122 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -4, 0 non-virtual]
123
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]
129
130 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
131 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, -8 non-virtual]
132
133 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
134 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -12, 0 non-virtual]
135
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]
141
142 // CHECK-LABEL: Thunks for 'simple::C::~C()' (1 entry).
143 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -12 non-virtual]
144
145 // CHECK-LABEL: Thunks for 'void simple::C::f()' (1 entry).
146 // CHECK-NEXT: 0 | [this adjustment: vtordisp at -16, -4 non-virtual]
147
148 int x;
149 virtual void f();
150 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
151 // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
152 virtual ~C();
153 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
154 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
155 // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
156 };
157
158 C c;
use(C * obj)159 void use(C *obj) { obj->f(); }
160
161 class D : B {
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]
167 D();
168 int z;
169
170 // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@3AEXXZ"
171 };
172
D()173 D::D() {}
174
175 struct E : V3 {
176 virtual void f();
177 };
178
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]
185
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]
190
191 F();
192 virtual void g(); // Force a vtordisp.
193 int f;
194
195 // MANGLING-DAG: @"\01?g@F@simple@@$4PPPPPPPM@A@AEXXZ"{{.*}}??_EF@simple@@$4PPPPPPPM@A@AEPAXI@Z
196 // MANGLING-DAG: ?f@E@simple@@UAEXXZ{{.*}}??_EF@simple@@$4PPPPPPPE@7AEPAXI@Z
197 };
198
F()199 F::F() {}
200
201 struct G : F {
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]
207
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]
212
213 G();
214 int g;
215
216 // MANGLING-DAG: @"\01?g@F@simple@@$4PPPPPPPM@3AEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPM@A@AEPAXI@Z"
217 // MANGLING-DAG: @"\01?f@E@simple@@UAEXXZ"{{.*}}@"\01??_EG@simple@@$4PPPPPPPE@7AEPAXI@Z"
218 };
219
G()220 G::G() {}
221 }
222
223 namespace extended {
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.
229 //
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
236 // jmp Method@Class
237
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]
245
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]
249
250 // `vtordispex{8,8,4294967292,8}'
251 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
252
253 virtual ~A();
254 // vtordisp{4294967292,0}
255 // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
256 };
257
258 A a;
use(A * obj)259 void use(A *obj) { delete obj; }
260
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.
265
266 // CHECK-LABEL: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
267 // ...
268 // CHECK: 1 | extended::B::~B() [scalar deleting]
269 // CHECK-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
270
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]
274
275 // vtordisp{4294967292,0}
276 // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
277 };
278
279 B b;
use(B * obj)280 void use(B *obj) { delete obj; }
281
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]
287
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]
291
292 // `vtordispex{12,8,4294967292,8}'
293 // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
294 int x;
295 virtual ~C();
296 // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
297 };
298
299 C c;
use(C * obj)300 void use(C *obj) { delete obj; }
301
302 struct D : virtual V2 {
303 virtual void f();
304 virtual ~D();
305 int x;
306 };
307
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]
313
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]
317
318 // `vtordispex{8,8,4294967292,12}'
319 // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
320
321 virtual ~E();
322 // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
323 };
324
325 E e;
use(E * obj)326 void use(E *obj) { delete obj; }
327
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]
333
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]
337
338 // `vtordispex{20,12,4294967292,12}'
339 // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
340 int x;
341 virtual ~F();
342 // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
343 };
344
345 F f;
use(F * obj)346 void use(F *obj) { delete obj; }
347
348 struct G : virtual simple::A {
349 // CHECK-LABEL: VFTable for 'extended::G' (1 entry).
350 // CHECK-NEXT: 0 | void extended::G::g()
351
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]
358
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]
362
363 // Emits a G's own vfptr, thus moving the vbptr in the layout.
364 virtual void g();
365
366 virtual ~G();
367 // vtordisp{4294967292,0}
368 // MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
369 };
370
371 G g;
use(G * obj)372 void use(G *obj) { obj->g(); }
373
374 struct H : Z, A {
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]
378
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]
383
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]
387
388 // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
389 // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
390 };
391
392 H h;
use(H * obj)393 void use(H *obj) { delete obj; }
394 }
395
396 namespace pr17738 {
397 // These classes should have vtordispex thunks but MSVS CL miscompiles them.
398 // Just do the right thing.
399
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]
405
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]
409
410 // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
411 int a;
412 virtual ~A();
413 };
414
415 A a;
use(A * obj)416 void use(A *obj) { delete obj; }
417 }
418
419 namespace pr19408 {
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.
423 struct A {
424 A();
425 virtual void f();
426 int a;
427 };
428
429 struct B : virtual A {
430 B();
431 int b;
432 };
433
434 struct C : B {
435 C();
436 virtual void f();
437 int c;
438 };
439
440 struct D : C {
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]
444
445 // MANGLING-DAG: @"\01?f@C@pr19408@@$4PPPPPPPM@3AEXXZ"
446 D();
447 int d;
448 };
449
D()450 D::D() {}
451 }
452
453 namespace access {
454 struct A {
455 virtual ~A();
456 protected:
457 virtual void prot();
458 private:
459 virtual void priv();
460 };
461
462 struct B : virtual A {
463 virtual ~B();
464 protected:
465 virtual void prot();
466 // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
467 private:
468 virtual void priv();
469 // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
470 };
471
472 B b;
473
474 struct C : virtual B {
475 virtual ~C();
476
477 // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
478 // MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
479 };
480
481 C c;
482 }
483
484 namespace pr19505 {
485 struct A {
486 virtual void f();
487 virtual void z();
488 };
489
490 struct B : A {
491 virtual void f();
492 };
493
494 struct C : A, B {
495 virtual void g();
496 };
497
498 struct X : B, virtual C {
Xpr19505::X499 X() {}
500 virtual void g();
501
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()
505
506 // MANGLING-DAG: @"\01??_7X@pr19505@@6BB@1@@" = {{.*}}@"\01?f@B@pr19505@@UAEXXZ"
507 } x;
508
build_vftable(X * obj)509 void build_vftable(X *obj) { obj->g(); }
510 }
511
512 namespace pr19506 {
513 struct A {
514 virtual void f();
515 virtual void g();
516 };
517
518 struct B : A {
519 virtual void f();
520 };
521
522 struct C : B {};
523
524 struct X : C, virtual B {
525 virtual void g();
Xpr19506::X526 X() {}
527
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]
532
533 // MANGLING-DAG: @"\01??_7X@pr19506@@6BB@1@@" = {{.*}}@"\01?f@B@pr19506@@UAEXXZ"
534 } x;
535
build_vftable(X * obj)536 void build_vftable(X *obj) { obj->g(); }
537 }
538
539 namespace pr19519 {
540 // VS2013 CL miscompiles this, just make sure we don't regress.
541
542 struct A {
543 virtual void f();
544 virtual void g();
545 };
546
547 struct B : virtual A {
548 virtual void f();
549 B();
550 };
551
552 struct C : virtual A {
553 virtual void g();
554 };
555
556 struct X : B, C {
557 X();
558
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]
564
565 // MANGLING-DAG: @"\01??_7X@pr19519@@6B@" = {{.*}}@"\01?g@C@pr19519@@$4PPPPPPPM@3AEXXZ"
566 };
567
X()568 X::X() {}
569 }
570