1 // RUN: %clang_cc1 -fno-rtti -emit-llvm -fdump-vtable-layouts %s -o %t.ll -cxx-abi microsoft -triple=i386-pc-win32 >%t
2 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-A %s < %t
3 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-B %s < %t
4 // RUN: FileCheck --check-prefix=VTABLE-SIMPLE-C %s < %t
5 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-A %s < %t
6 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-B %s < %t
7 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-C %s < %t
8 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-E %s < %t
9 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-F %s < %t
10 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-G %s < %t
11 // RUN: FileCheck --check-prefix=VTABLE-EXTENDED-H %s < %t
12 // RUN: FileCheck --check-prefix=VTABLE-PR17738-A %s < %t
13 // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll
14 
15 // For now, just make sure x86_64 doesn't crash.
16 // RUN: %clang_cc1 -fno-rtti -emit-llvm-only -fdump-vtable-layouts %s -cxx-abi microsoft -triple=x86_64-pc-win32 >/dev/null
17 
18 struct V1 {
19   virtual void f();
20   virtual ~V1();
21 };
22 
23 struct V2 {
24   virtual void f();
25   virtual ~V2();
26   int v;
27 };
28 
29 struct Z {
30   virtual void g();
31   virtual ~Z();
32   int x;
33 };
34 
35 struct V3 : Z, V2 {
36 };
37 
38 struct V4 : Z, V1, V2 {
39   int y;
40 };
41 
42 void use_somewhere_else(void*);
43 
44 namespace simple {
45 // In case of a single-layer virtual inheritance, the "this" adjustment is done
46 // staically:
47 //   struct A {
48 //     virtual void f();  // Expects "(A*)this" in ECX
49 //   };
50 //   struct B : virtual A {
51 //     virtual void f();  // Expects "(char*)(B*)this + 12" in ECX
52 //     virtual ~B();      // Might call f()
53 //   };
54 //
55 // If a class overrides a virtual function of its base and has a non-trivial
56 // ctor/dtor that call(s) the virtual function (or may escape "this" to some
57 // code that might call it), a virtual adjustment might be needed in case the
58 // current class layout and the most derived class layout are different.
59 // This is done using vtordisp thunks.
60 //
61 // A simple vtordisp{A,B} thunk for Method@Class is something like:
62 //   sub  ecx, [ecx+A]  // apply the vtordisp adjustment
63 //   sub  ecx, B        // apply the subobject adjustment, if needed.
64 //   jmp Method@Class
65 
66 struct A : virtual V1 {
67   // VTABLE-SIMPLE-A: VFTable for 'V1' in 'simple::A' (2 entries).
68   // VTABLE-SIMPLE-A-NEXT: 0 | void simple::A::f()
69   // VTABLE-SIMPLE-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
70   // VTABLE-SIMPLE-A-NEXT: 1 | simple::A::~A() [scalar deleting]
71   // VTABLE-SIMPLE-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
72 
73   virtual void f();
74   // MANGLING-DAG: @"\01?f@A@simple@@$4PPPPPPPM@A@AEXXZ"
75 
76   virtual ~A();
77   // MANGLING-DAG: @"\01??_EA@simple@@$4PPPPPPPM@A@AEPAXI@Z"
78 };
79 
80 A a;
81 
82 struct B : virtual V3 {
83   // VTABLE-SIMPLE-B: VFTable for 'Z' in 'V3' in 'simple::B' (2 entries).
84   // VTABLE-SIMPLE-B-NEXT: 0 | void Z::g()
85   // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
86   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
87 
88   // VTABLE-SIMPLE-B: VFTable for 'V2' in 'V3' in 'simple::B' (2 entries).
89   // VTABLE-SIMPLE-B-NEXT: 0 | void simple::B::f()
90   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
91   // VTABLE-SIMPLE-B-NEXT: 1 | simple::B::~B() [scalar deleting]
92   // VTABLE-SIMPLE-B-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
93 
94   // FIXME: The vtordisp thunk should only get emitted for a constructor
95   // if "this" leaves scope.
96   B() { use_somewhere_else(this); }
97 
98   virtual void f();
99   // MANGLING-DAG: @"\01?f@B@simple@@$4PPPPPPPE@A@AEXXZ"
100 
101   // Has an implicit destructor.
102   // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPE@7AEPAXI@Z"
103   // MANGLING-DAG: @"\01??_EB@simple@@$4PPPPPPPM@A@AEPAXI@Z"
104 };
105 
106 B b;
107 
108 struct C : virtual V4 {
109   // VTABLE-SIMPLE-C: VFTable for 'Z' in 'V4' in 'simple::C' (2 entries).
110   // VTABLE-SIMPLE-C-NEXT: 0 | void Z::g()
111   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
112   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
113 
114   // VTABLE-SIMPLE-C: VFTable for 'V1' in 'V4' in 'simple::C' (2 entries).
115   // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
116   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, 0 non-virtual]
117   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
118   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -12, -8 non-virtual]
119 
120   // VTABLE-SIMPLE-C: VFTable for 'V2' in 'V4' in 'simple::C' (2 entries).
121   // VTABLE-SIMPLE-C-NEXT: 0 | void simple::C::f()
122   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -4 non-virtual]
123   // VTABLE-SIMPLE-C-NEXT: 1 | simple::C::~C() [scalar deleting]
124   // VTABLE-SIMPLE-C-NEXT:     [this adjustment: vtordisp at -16, -12 non-virtual]
125 
126   int x;
127   virtual void f();
128   // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPA@3AEXXZ"
129   // MANGLING-DAG: @"\01?f@C@simple@@$4PPPPPPPE@A@AEXXZ"
130   virtual ~C();
131   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPA@M@AEPAXI@Z"
132   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPE@7AEPAXI@Z"
133   // MANGLING-DAG: @"\01??_EC@simple@@$4PPPPPPPM@A@AEPAXI@Z"
134 };
135 
136 C c;
137 }
138 
139 namespace extended {
140 // If a virtual function requires vtordisp adjustment and the final overrider
141 // is defined in another vitual base of the most derived class,
142 // we need to know two vbase offsets.
143 // In this case, we should use the extended form of vtordisp thunks, called
144 // vtordispex thunks.
145 //
146 // vtordispex{A,B,C,D} thunk for Method@Class is something like:
147 //   sub  ecx, [ecx+C]  // apply the vtordisp adjustment
148 //   sub  ecx, A        // jump to the vbtable of the most derived class
149 //   mov  eax, [ecx]    // load the vbtable address
150 //   add  ecx, [eax+B]  // lookup the final overrider's vbase offset
151 //   add  ecx, D        // apphy the subobject offset if needed
152 //   jmp Method@Class
153 
154 struct A : virtual simple::A {
155   // VTABLE-EXTENDED-A: VFTable for 'V1' in 'simple::A' in 'extended::A' (2 entries).
156   // VTABLE-EXTENDED-A-NEXT: 0 | void simple::A::f()
157   // VTABLE-EXTENDED-A-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
158   // VTABLE-EXTENDED-A-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
159   // VTABLE-EXTENDED-A-NEXT: 1 | extended::A::~A() [scalar deleting]
160   // VTABLE-EXTENDED-A-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
161 
162   // `vtordispex{8,8,4294967292,8}'
163   // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
164 
165   virtual ~A();
166   // vtordisp{4294967292,0}
167   // MANGLING-DAG: @"\01??_EA@extended@@$4PPPPPPPM@A@AEPAXI@Z"
168 };
169 
170 A a;
171 
172 struct B : virtual simple::A {
173   // This class has an implicit dtor.  Vdtors don't require vtordispex thunks
174   // as the most derived class always has an implicit dtor,
175   // which is a final overrider.
176 
177   // VTABLE-EXTENDED-B: VFTable for 'V1' in 'simple::A' in 'extended::B' (2 entries).
178   //  ...
179   // VTABLE-EXTENDED-B: 1 | extended::B::~B() [scalar deleting]
180   // VTABLE-EXTENDED-B-NEXT: [this adjustment: vtordisp at -4, 0 non-virtual]
181 
182   // vtordisp{4294967292,0}
183   // MANGLING-DAG: @"\01??_EB@extended@@$4PPPPPPPM@A@AEPAXI@Z"
184 };
185 
186 B b;
187 
188 struct C : virtual simple::A {
189   // VTABLE-EXTENDED-C: VFTable for 'V1' in 'simple::A' in 'extended::C' (2 entries).
190   // VTABLE-EXTENDED-C-NEXT: 0 | void simple::A::f()
191   // VTABLE-EXTENDED-C-NEXT:     [this adjustment: vtordisp at -4, vbptr at 12 to the left,
192   // VTABLE-EXTENDED-C-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
193 
194   // `vtordispex{12,8,4294967292,8}'
195   // MANGLING-DAG: @"\01?f@A@simple@@$R4M@7PPPPPPPM@7AEXXZ"
196   int x;
197   virtual ~C();
198   // MANGLING-DAG: @"\01??_EC@extended@@$4PPPPPPPM@A@AEPAXI@Z"
199 };
200 
201 C c;
202 
203 struct D : virtual V2 {
204   virtual void f();
205   virtual ~D();
206   int x;
207 };
208 
209 struct E : virtual D {
210   // VTABLE-EXTENDED-E: VFTable for 'V2' in 'extended::D' in 'extended::E' (2 entries).
211   // VTABLE-EXTENDED-E-NEXT: 0 | void extended::D::f()
212   // VTABLE-EXTENDED-E-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
213   // VTABLE-EXTENDED-E-NEXT:      vboffset at 8 in the vbtable, 12 non-virtual]
214 
215   // `vtordispex{8,8,4294967292,12}'
216   // MANGLING-DAG: @"\01?f@D@extended@@$R477PPPPPPPM@M@AEXXZ"
217 
218   virtual ~E();
219   // MANGLING-DAG: @"\01??_EE@extended@@$4PPPPPPPM@A@AEPAXI@Z"
220 };
221 
222 E e;
223 
224 struct F : virtual Z, virtual D {
225   // VTABLE-EXTENDED-F: VFTable for 'V2' in 'extended::D' in 'extended::F' (2 entries).
226   // VTABLE-EXTENDED-F-NEXT: 0 | void extended::D::f()
227   // VTABLE-EXTENDED-F-NEXT:     [this adjustment: vtordisp at -4, vbptr at 20 to the left,
228   // VTABLE-EXTENDED-F-NEXT:      vboffset at 12 in the vbtable, 12 non-virtual]
229 
230   // `vtordispex{20,12,4294967292,12}'
231   // MANGLING-DAG: @"\01?f@D@extended@@$R4BE@M@PPPPPPPM@M@AEXXZ"
232   int x;
233   virtual ~F();
234   // MANGLING-DAG: @"\01??_EF@extended@@$4PPPPPPPM@M@AEPAXI@Z"
235 };
236 
237 F f;
238 
239 struct G : virtual simple::A {
240   // VTABLE-EXTENDED-G: VFTable for 'extended::G' (1 entries).
241   // VTABLE-EXTENDED-G-NEXT: 0 | void extended::G::g()
242 
243   // VTABLE-EXTENDED-G: VFTable for 'V1' in 'simple::A' in 'extended::G' (2 entries).
244   // VTABLE-EXTENDED-G-NEXT: 0 | void simple::A::f()
245   // VTABLE-EXTENDED-G-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
246   // VTABLE-EXTENDED-G-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
247   // VTABLE-EXTENDED-G-NEXT: 1 | extended::G::~G() [scalar deleting]
248   // VTABLE-EXTENDED-G-NEXT:     [this adjustment: vtordisp at -4, 0 non-virtual]
249 
250   // Emits a G's own vfptr, thus moving the vbptr in the layout.
251   virtual void g();
252 
253   virtual ~G();
254   // vtordisp{4294967292,0}
255   // MANGLING-DAG: @"\01??_EG@extended@@$4PPPPPPPM@A@AEPAXI@Z"
256 };
257 
258 G g;
259 
260 struct H : Z, A {
261   // VTABLE-EXTENDED-H: VFTable for 'Z' in 'extended::H' (2 entries).
262   // VTABLE-EXTENDED-H-NEXT: 0 | void Z::g()
263   // VTABLE-EXTENDED-H-NEXT: 1 | extended::H::~H() [scalar deleting]
264 
265   // VTABLE-EXTENDED-H: VFTable for 'V1' in 'simple::A' in 'extended::A' in 'extended::H' (2 entries).
266   // VTABLE-EXTENDED-H-NEXT: 0 | void simple::A::f()
267   // VTABLE-EXTENDED-H-NEXT:     [this adjustment: vtordisp at -4, vbptr at 8 to the left,
268   // VTABLE-EXTENDED-H-NEXT:      vboffset at 8 in the vbtable, 8 non-virtual]
269 
270   // MANGLING-DAG: @"\01?f@A@simple@@$R477PPPPPPPM@7AEXXZ"
271   // MANGLING-DAG: @"\01??_EH@extended@@$4PPPPPPPM@BA@AEPAXI@Z"
272 };
273 
274 H h;
275 }
276 
277 namespace pr17738 {
278 // These classes should have vtordispex thunks but MSVS CL miscompiles them.
279 // Just do the right thing.
280 
281 struct A : virtual simple::B {
282   // VTABLE-PR17738-A: VFTable for 'V2' in 'V3' in 'simple::B' in 'pr17738::A' (2 entries).
283   // VTABLE-PR17738-A-NEXT: 0 | void simple::B::f()
284   // VTABLE-PR17738-A-NEXT:     [this adjustment: vtordisp at -12, vbptr at 20 to the left,
285   // VTABLE-PR17738-A-NEXT:      vboffset at 8 in the vbtable, 16 non-virtual]
286 
287   // MANGLING-DAG: @"\01?f@B@simple@@$R4BE@7PPPPPPPE@BA@AEXXZ"
288   int a;
289   virtual ~A();
290 };
291 
292 A a;
293 }
294 
295 namespace access {
296 struct A {
297   virtual ~A();
298 protected:
299   virtual void prot();
300 private:
301   virtual void priv();
302 };
303 
304 struct B : virtual A {
305   virtual ~B();
306 protected:
307   virtual void prot();
308   // MANGLING-DAG: @"\01?prot@B@access@@$2PPPPPPPM@A@AEXXZ"
309 private:
310   virtual void priv();
311   // MANGLING-DAG: @"\01?priv@B@access@@$0PPPPPPPM@A@AEXXZ"
312 };
313 
314 B b;
315 
316 struct C : virtual B {
317   virtual ~C();
318 
319   // MANGLING-DAG: @"\01?prot@B@access@@$R277PPPPPPPM@7AEXXZ"
320   // MANGLING-DAG: @"\01?priv@B@access@@$R077PPPPPPPM@7AEXXZ"
321 };
322 
323 C c;
324 }
325