1 | // RUN: %clang_cc1 %s -fno-rtti -triple=i386-pc-win32 -emit-llvm -o %t.ll -fdump-vtable-layouts >%t |
2 | // RUN: FileCheck %s < %t |
3 | // RUN: FileCheck --check-prefix=MANGLING %s < %t.ll |
4 | |
5 | namespace test1 { |
6 | struct A { |
7 | virtual void g(); |
8 | // Add an extra virtual method so it's easier to check for the absence of thunks. |
9 | virtual void h(); |
10 | }; |
11 | |
12 | struct B { |
13 | virtual void g(); |
14 | }; |
15 | |
16 | // Overrides a method of two bases at the same time, thus needing thunks. |
17 | struct C : A, B { |
18 | virtual void g(); |
19 | }; |
20 | |
21 | struct D { |
22 | virtual B* foo(); |
23 | virtual void z(); |
24 | }; |
25 | |
26 | struct X : D { |
27 | // CHECK-LABEL: VFTable for 'test1::D' in 'test1::X' (3 entries). |
28 | // CHECK-NEXT: 0 | test1::C *test1::X::foo() |
29 | // CHECK-NEXT: [return adjustment (to type 'struct test1::B *'): 4 non-virtual] |
30 | // CHECK-NEXT: 1 | void test1::D::z() |
31 | // CHECK-NEXT: 2 | test1::C *test1::X::foo() |
32 | |
33 | // CHECK-LABEL: Thunks for 'test1::C *test1::X::foo()' (1 entry). |
34 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test1::B *'): 4 non-virtual] |
35 | |
36 | // CHECK-LABEL: VFTable indices for 'test1::X' (1 entry). |
37 | // CHECK-NEXT: 2 | test1::C *test1::X::foo() |
38 | |
39 | // MANGLING-DAG: @"??_7X@test1@@6B@" |
40 | |
41 | virtual C* foo(); |
42 | } x; |
43 | |
44 | void build_vftable(X *obj) { obj->foo(); } |
45 | } |
46 | |
47 | namespace test2 { |
48 | struct A { |
49 | virtual void g(); |
50 | virtual void h(); |
51 | }; |
52 | |
53 | struct B { |
54 | virtual void g(); |
55 | }; |
56 | |
57 | struct C : A, B { |
58 | virtual void g(); |
59 | }; |
60 | |
61 | struct D { |
62 | virtual B* foo(); |
63 | virtual void z(); |
64 | }; |
65 | |
66 | struct E : D { |
67 | virtual C* foo(); |
68 | }; |
69 | |
70 | struct F : C { }; |
71 | |
72 | struct X : E { |
73 | virtual F* foo(); |
74 | // CHECK-LABEL: VFTable for 'test2::D' in 'test2::E' in 'test2::X' (4 entries). |
75 | // CHECK-NEXT: 0 | test2::F *test2::X::foo() |
76 | // CHECK-NEXT: [return adjustment (to type 'struct test2::B *'): 4 non-virtual] |
77 | // CHECK-NEXT: 1 | void test2::D::z() |
78 | // CHECK-NEXT: 2 | test2::F *test2::X::foo() |
79 | // CHECK-NEXT: [return adjustment (to type 'struct test2::C *'): 0 non-virtual] |
80 | // CHECK-NEXT: 3 | test2::F *test2::X::foo() |
81 | |
82 | // CHECK-LABEL: Thunks for 'test2::F *test2::X::foo()' (2 entries). |
83 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test2::C *'): 0 non-virtual] |
84 | // CHECK-NEXT: 1 | [return adjustment (to type 'struct test2::B *'): 4 non-virtual] |
85 | |
86 | // CHECK-LABEL: VFTable indices for 'test2::X' (1 entry). |
87 | // CHECK-NEXT: 3 | test2::F *test2::X::foo() |
88 | }; |
89 | |
90 | void build_vftable(X *obj) { obj->foo(); } |
91 | } |
92 | |
93 | namespace test3 { |
94 | struct A { |
95 | virtual void g(); |
96 | virtual void h(); |
97 | }; |
98 | |
99 | struct B { |
100 | virtual void g(); |
101 | }; |
102 | |
103 | struct C : A, B { |
104 | virtual void g(); |
105 | }; |
106 | |
107 | struct D { |
108 | virtual B* foo(); |
109 | virtual void z(); |
110 | }; |
111 | |
112 | struct E : D { |
113 | virtual C* foo(); |
114 | }; |
115 | |
116 | struct F : A, C { }; |
117 | |
118 | struct X : E { |
119 | // CHECK-LABEL: VFTable for 'test3::D' in 'test3::E' in 'test3::X' (4 entries). |
120 | // CHECK-NEXT: 0 | test3::F *test3::X::foo() |
121 | // CHECK-NEXT: [return adjustment (to type 'struct test3::B *'): 8 non-virtual] |
122 | // CHECK-NEXT: 1 | void test3::D::z() |
123 | // CHECK-NEXT: 2 | test3::F *test3::X::foo() |
124 | // CHECK-NEXT: [return adjustment (to type 'struct test3::C *'): 4 non-virtual] |
125 | // CHECK-NEXT: 3 | test3::F *test3::X::foo() |
126 | |
127 | // CHECK-LABEL: Thunks for 'test3::F *test3::X::foo()' (2 entries). |
128 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test3::C *'): 4 non-virtual] |
129 | // CHECK-NEXT: 1 | [return adjustment (to type 'struct test3::B *'): 8 non-virtual] |
130 | |
131 | // CHECK-LABEL: VFTable indices for 'test3::X' (1 entry). |
132 | // CHECK-NEXT: 3 | test3::F *test3::X::foo() |
133 | |
134 | virtual F* foo(); |
135 | }; |
136 | |
137 | void build_vftable(X *obj) { obj->foo(); } |
138 | } |
139 | |
140 | namespace test4 { |
141 | struct A { |
142 | virtual void g(); |
143 | virtual void h(); |
144 | }; |
145 | |
146 | struct B { |
147 | virtual void g(); |
148 | }; |
149 | |
150 | struct C : A, B { |
151 | virtual void g(); |
152 | }; |
153 | |
154 | struct D { |
155 | virtual B* foo(); |
156 | virtual void z(); |
157 | }; |
158 | |
159 | struct E : D { |
160 | virtual C* foo(); |
161 | }; |
162 | |
163 | struct F : A, C { }; |
164 | |
165 | struct X : D, E { |
166 | // CHECK-LABEL: VFTable for 'test4::D' in 'test4::X' (3 entries). |
167 | // CHECK-NEXT: 0 | test4::F *test4::X::foo() |
168 | // CHECK-NEXT: [return adjustment (to type 'struct test4::B *'): 8 non-virtual] |
169 | // CHECK-NEXT: 1 | void test4::D::z() |
170 | // CHECK-NEXT: 2 | test4::F *test4::X::foo() |
171 | |
172 | // CHECK-LABEL: Thunks for 'test4::F *test4::X::foo()' (1 entry). |
173 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test4::B *'): 8 non-virtual] |
174 | |
175 | // CHECK-LABEL: VFTable for 'test4::D' in 'test4::E' in 'test4::X' (4 entries). |
176 | // CHECK-NEXT: 0 | test4::F *test4::X::foo() |
177 | // CHECK-NEXT: [return adjustment (to type 'struct test4::B *'): 8 non-virtual] |
178 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
179 | // CHECK-NEXT: 1 | void test4::D::z() |
180 | // CHECK-NEXT: 2 | test4::F *test4::X::foo() |
181 | // CHECK-NEXT: [return adjustment (to type 'struct test4::C *'): 4 non-virtual] |
182 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
183 | // CHECK-NEXT: 3 | test4::F *test4::X::foo() |
184 | // CHECK-NEXT: [return adjustment (to type 'struct test4::F *'): 0 non-virtual] |
185 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
186 | |
187 | // CHECK-LABEL: Thunks for 'test4::F *test4::X::foo()' (3 entries). |
188 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test4::F *'): 0 non-virtual] |
189 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
190 | // CHECK-NEXT: 1 | [return adjustment (to type 'struct test4::C *'): 4 non-virtual] |
191 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
192 | // CHECK-NEXT: 2 | [return adjustment (to type 'struct test4::B *'): 8 non-virtual] |
193 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
194 | |
195 | // CHECK-LABEL: VFTable indices for 'test4::X' (1 entry). |
196 | // CHECK-NEXT: 2 | test4::F *test4::X::foo() |
197 | |
198 | virtual F* foo(); |
199 | }; |
200 | |
201 | void build_vftable(X *obj) { obj->foo(); } |
202 | } |
203 | |
204 | namespace test5 { |
205 | struct A { |
206 | virtual void g(); |
207 | virtual void h(); |
208 | }; |
209 | |
210 | struct B { |
211 | virtual void g(); |
212 | }; |
213 | |
214 | struct C : A, B { |
215 | virtual void g(); |
216 | }; |
217 | |
218 | struct D { |
219 | virtual B* foo(); |
220 | virtual void z(); |
221 | }; |
222 | |
223 | struct X : A, D { |
224 | // CHECK-LABEL: VFTable for 'test5::A' in 'test5::X' (2 entries). |
225 | // CHECK-NEXT: 0 | void test5::A::g() |
226 | // CHECK-NEXT: 1 | void test5::A::h() |
227 | |
228 | // CHECK-LABEL: VFTable for 'test5::D' in 'test5::X' (3 entries). |
229 | // CHECK-NEXT: 0 | test5::C *test5::X::foo() |
230 | // CHECK-NEXT: [return adjustment (to type 'struct test5::B *'): 4 non-virtual] |
231 | // CHECK-NEXT: 1 | void test5::D::z() |
232 | // CHECK-NEXT: 2 | test5::C *test5::X::foo() |
233 | |
234 | // CHECK-LABEL: Thunks for 'test5::C *test5::X::foo()' (1 entry). |
235 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test5::B *'): 4 non-virtual] |
236 | |
237 | // CHECK-LABEL: VFTable indices for 'test5::X' (1 entry). |
238 | // CHECK-NEXT: via vfptr at offset 4 |
239 | // CHECK-NEXT: 2 | test5::C *test5::X::foo() |
240 | |
241 | virtual C* foo(); |
242 | }; |
243 | |
244 | void build_vftable(X *obj) { obj->foo(); } |
245 | } |
246 | |
247 | namespace test6 { |
248 | struct A { |
249 | virtual void g(); |
250 | virtual void h(); |
251 | }; |
252 | |
253 | struct B { |
254 | virtual void g(); |
255 | }; |
256 | |
257 | struct C : A, B { |
258 | virtual void g(); |
259 | }; |
260 | |
261 | struct D { |
262 | virtual B* foo(); |
263 | virtual void z(); |
264 | }; |
265 | |
266 | struct E : A, D { |
267 | virtual C* foo(); |
268 | }; |
269 | |
270 | struct F : A, C { }; |
271 | |
272 | struct X : E { |
273 | // CHECK-LABEL: VFTable for 'test6::A' in 'test6::E' in 'test6::X' (2 entries). |
274 | // CHECK-NEXT: 0 | void test6::A::g() |
275 | // CHECK-NEXT: 1 | void test6::A::h() |
276 | |
277 | // CHECK-LABEL: VFTable for 'test6::D' in 'test6::E' in 'test6::X' (4 entries). |
278 | // CHECK-NEXT: 0 | test6::F *test6::X::foo() |
279 | // CHECK-NEXT: [return adjustment (to type 'struct test6::B *'): 8 non-virtual] |
280 | // CHECK-NEXT: 1 | void test6::D::z() |
281 | // CHECK-NEXT: 2 | test6::F *test6::X::foo() |
282 | // CHECK-NEXT: [return adjustment (to type 'struct test6::C *'): 4 non-virtual] |
283 | // CHECK-NEXT: 3 | test6::F *test6::X::foo() |
284 | |
285 | // CHECK-LABEL: Thunks for 'test6::F *test6::X::foo()' (2 entries). |
286 | // CHECK-NEXT: 0 | [return adjustment (to type 'struct test6::C *'): 4 non-virtual] |
287 | // CHECK-NEXT: 1 | [return adjustment (to type 'struct test6::B *'): 8 non-virtual] |
288 | |
289 | // CHECK-LABEL: VFTable indices for 'test6::X' (1 entry). |
290 | // CHECK-NEXT: -- accessible via vfptr at offset 4 -- |
291 | // CHECK-NEXT: 3 | test6::F *test6::X::foo() |
292 | |
293 | virtual F* foo(); |
294 | }; |
295 | |
296 | void build_vftable(X *obj) { obj->foo(); } |
297 | } |
298 | |
299 | namespace test7 { |
300 | struct A { |
301 | virtual A *f() = 0; |
302 | }; |
303 | struct B { |
304 | virtual void g(); |
305 | }; |
306 | struct C : B, A { |
307 | virtual void g(); |
308 | virtual C *f() = 0; |
309 | // CHECK-LABEL: VFTable for 'test7::B' in 'test7::C' (1 entry). |
310 | // CHECK-NEXT: 0 | void test7::C::g() |
311 | |
312 | // CHECK-LABEL: VFTable for 'test7::A' in 'test7::C' (2 entries). |
313 | // CHECK-NEXT: 0 | test7::C *test7::C::f() [pure] |
314 | // CHECK-NEXT: 1 | test7::C *test7::C::f() [pure] |
315 | |
316 | // No return adjusting thunks needed for pure virtual methods. |
317 | // CHECK-NOT: Thunks for 'test7::C *test7::C::f()' |
318 | }; |
319 | |
320 | void build_vftable(C *obj) { obj->g(); } |
321 | } |
322 | |
323 | namespace pr20444 { |
324 | struct A { |
325 | virtual A* f(); |
326 | }; |
327 | struct B { |
328 | virtual B* f(); |
329 | }; |
330 | struct C : A, B { |
331 | virtual C* f(); |
332 | // CHECK-LABEL: VFTable for 'pr20444::A' in 'pr20444::C' (1 entry). |
333 | // CHECK-NEXT: 0 | pr20444::C *pr20444::C::f() |
334 | |
335 | // CHECK-LABEL: VFTable for 'pr20444::B' in 'pr20444::C' (2 entries). |
336 | // CHECK-NEXT: 0 | pr20444::C *pr20444::C::f() |
337 | // CHECK-NEXT: [return adjustment (to type 'struct pr20444::B *'): 4 non-virtual] |
338 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
339 | // CHECK-NEXT: 1 | pr20444::C *pr20444::C::f() |
340 | // CHECK-NEXT: [return adjustment (to type 'struct pr20444::C *'): 0 non-virtual] |
341 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
342 | }; |
343 | |
344 | void build_vftable(C *obj) { obj->f(); } |
345 | |
346 | struct D : C { |
347 | virtual D* f(); |
348 | // CHECK-LABEL: VFTable for 'pr20444::A' in 'pr20444::C' in 'pr20444::D' (1 entry). |
349 | // CHECK-NEXT: 0 | pr20444::D *pr20444::D::f() |
350 | |
351 | // CHECK-LABEL: VFTable for 'pr20444::B' in 'pr20444::C' in 'pr20444::D' (3 entries). |
352 | // CHECK-NEXT: 0 | pr20444::D *pr20444::D::f() |
353 | // CHECK-NEXT: [return adjustment (to type 'struct pr20444::B *'): 4 non-virtual] |
354 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
355 | // CHECK-NEXT: 1 | pr20444::D *pr20444::D::f() |
356 | // CHECK-NEXT: [return adjustment (to type 'struct pr20444::C *'): 0 non-virtual] |
357 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
358 | // CHECK-NEXT: 2 | pr20444::D *pr20444::D::f() |
359 | // CHECK-NEXT: [return adjustment (to type 'struct pr20444::D *'): 0 non-virtual] |
360 | // CHECK-NEXT: [this adjustment: -4 non-virtual] |
361 | }; |
362 | |
363 | void build_vftable(D *obj) { obj->f(); } |
364 | } |
365 | |