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
8struct V1 {
9  virtual void f();
10  virtual ~V1();
11};
12
13struct V2 {
14  virtual void f();
15  virtual ~V2();
16  int v;
17};
18
19struct Z {
20  virtual void g();
21  virtual ~Z();
22  int x;
23};
24
25struct V3 : Z, V2 {
26};
27
28struct V4 : Z, V1, V2 {
29  int y;
30};
31
32void use_somewhere_else(void*);
33
34namespace simple {
35// In case of a single-layer virtual inheritance, the "this" adjustment is done
36// staically:
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{A,B} thunk for Method@Class is something like:
52//   sub  ecx, [ecx+A]  // apply the vtordisp adjustment
53//   sub  ecx, B        // apply the subobject adjustment, if needed.
54//   jmp Method@Class
55
56struct 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
76A a;
77void use(A *obj) { obj->f(); }
78
79struct 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.
102  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
112B b;
113void use(B *obj) { obj->f(); }
114
115struct 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
158C c;
159void use(C *obj) { obj->f(); }
160
161class 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
173D::D() {}
174
175struct E : V3 {
176  virtual void f();
177};
178
179struct 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
199F::F() {}
200
201struct 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
220G::G() {}
221}
222
223namespace extended {
224// If a virtual function requires vtordisp adjustment and the final overrider
225// is defined in another vitual 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{A,B,C,D} thunk for Method@Class is something like:
231//   sub  ecx, [ecx+C]  // apply the vtordisp adjustment
232//   sub  ecx, A        // jump to the vbtable of the most derived class
233//   mov  eax, [ecx]    // load the vbtable address
234//   add  ecx, [eax+B]  // lookup the final overrider's vbase offset
235//   add  ecx, D        // apphy the subobject offset if needed
236//   jmp Method@Class
237
238struct 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
258A a;
259void use(A *obj) { delete obj; }
260
261struct 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
279B b;
280void use(B *obj) { delete obj; }
281
282struct 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
299C c;
300void use(C *obj) { delete obj; }
301
302struct D : virtual V2 {
303  virtual void f();
304  virtual ~D();
305  int x;
306};
307
308struct 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
325E e;
326void use(E *obj) { delete obj; }
327
328struct 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
345F f;
346void use(F *obj) { delete obj; }
347
348struct 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
371G g;
372void use(G *obj) { obj->g(); }
373
374struct 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
392H h;
393void use(H *obj) { delete obj; }
394}
395
396namespace pr17738 {
397// These classes should have vtordispex thunks but MSVS CL miscompiles them.
398// Just do the right thing.
399
400struct 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
415A a;
416void use(A *obj) { delete obj; }
417}
418
419namespace 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.
423struct A {
424  A();
425  virtual void f();
426  int a;
427};
428
429struct B : virtual A {
430  B();
431  int b;
432};
433
434struct C : B {
435  C();
436  virtual void f();
437  int c;
438};
439
440struct 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
450D::D() {}
451}
452
453namespace access {
454struct A {
455  virtual ~A();
456protected:
457  virtual void prot();
458private:
459  virtual void priv();
460};
461
462struct B : virtual A {
463  virtual ~B();
464protected:
465  virtual void prot();
466  // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
467private:
468  virtual void priv();
469  // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
470};
471
472B b;
473
474struct 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
481C c;
482}
483
484namespace pr19505 {
485struct A {
486  virtual void f();
487  virtual void z();
488};
489
490struct B : A {
491  virtual void f();
492};
493
494struct C : A, B {
495  virtual void g();
496};
497
498struct X : B, virtual C {
499  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
509void build_vftable(X *obj) { obj->g(); }
510}
511
512namespace pr19506 {
513struct A {
514  virtual void f();
515  virtual void g();
516};
517
518struct B : A {
519  virtual void f();
520};
521
522struct C : B {};
523
524struct X : C, virtual B {
525  virtual void g();
526  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
536void build_vftable(X *obj) { obj->g(); }
537}
538
539namespace pr19519 {
540// VS2013 CL miscompiles this, just make sure we don't regress.
541
542struct A {
543  virtual void f();
544  virtual void g();
545};
546
547struct B : virtual A {
548  virtual void f();
549  B();
550};
551
552struct C : virtual A {
553  virtual void g();
554};
555
556struct 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
568X::X() {}
569}
570