1 | // RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - | FileCheck --check-prefix=CHECK --check-prefix=CHECK-NONOPT %s |
2 | // RUN: %clang_cc1 %s -triple=x86_64-pc-linux-gnu -munwind-tables -emit-llvm -o - -O1 -disable-llvm-passes | FileCheck --check-prefix=CHECK --check-prefix=CHECK-OPT %s |
3 | |
4 | namespace Test1 { |
5 | |
6 | // Check that we emit a non-virtual thunk for C::f. |
7 | |
8 | struct A { |
9 | virtual void f(); |
10 | }; |
11 | |
12 | struct B { |
13 | virtual void f(); |
14 | }; |
15 | |
16 | struct C : A, B { |
17 | virtual void c(); |
18 | |
19 | virtual void f(); |
20 | }; |
21 | |
22 | // CHECK-LABEL: define void @_ZThn8_N5Test11C1fEv( |
23 | void C::f() { } |
24 | |
25 | } |
26 | |
27 | namespace Test2 { |
28 | |
29 | // Check that we emit a thunk for B::f since it's overriding a virtual base. |
30 | |
31 | struct A { |
32 | virtual void f(); |
33 | }; |
34 | |
35 | struct B : virtual A { |
36 | virtual void b(); |
37 | virtual void f(); |
38 | }; |
39 | |
40 | // CHECK-LABEL: define void @_ZTv0_n24_N5Test21B1fEv( |
41 | void B::f() { } |
42 | |
43 | } |
44 | |
45 | namespace Test3 { |
46 | |
47 | // Check that we emit a covariant thunk for B::f. |
48 | |
49 | struct V1 { }; |
50 | struct V2 : virtual V1 { }; |
51 | |
52 | struct A { |
53 | virtual V1 *f(); |
54 | }; |
55 | |
56 | struct B : A { |
57 | virtual void b(); |
58 | |
59 | virtual V2 *f(); |
60 | }; |
61 | |
62 | // CHECK: define %{{.*}}* @_ZTch0_v0_n24_N5Test31B1fEv( |
63 | V2 *B::f() { return 0; } |
64 | |
65 | } |
66 | |
67 | namespace Test4 { |
68 | |
69 | // Check that the thunk for 'C::f' has the same visibility as the function itself. |
70 | |
71 | struct A { |
72 | virtual void f(); |
73 | }; |
74 | |
75 | struct B { |
76 | virtual void f(); |
77 | }; |
78 | |
79 | struct __attribute__((visibility("protected"))) C : A, B { |
80 | virtual void c(); |
81 | |
82 | virtual void f(); |
83 | }; |
84 | |
85 | // CHECK-LABEL: define protected void @_ZThn8_N5Test41C1fEv( |
86 | void C::f() { } |
87 | |
88 | } |
89 | |
90 | // Check that the thunk gets internal linkage. |
91 | namespace Test4B { |
92 | struct A { |
93 | virtual void f(); |
94 | }; |
95 | |
96 | struct B { |
97 | virtual void f(); |
98 | }; |
99 | |
100 | namespace { |
101 | struct C : A, B { |
102 | virtual void c(); |
103 | virtual void f(); |
104 | }; |
105 | } |
106 | void C::c() {} |
107 | void C::f() {} |
108 | |
109 | // Force C::f to be used. |
110 | void f() { |
111 | C c; |
112 | c.f(); |
113 | } |
114 | } |
115 | |
116 | namespace Test5 { |
117 | |
118 | // Check that the thunk for 'B::f' gets the same linkage as the function itself. |
119 | struct A { |
120 | virtual void f(); |
121 | }; |
122 | |
123 | struct B : virtual A { |
124 | virtual void f() { } |
125 | }; |
126 | |
127 | void f(B b) { |
128 | b.f(); |
129 | } |
130 | } |
131 | |
132 | namespace Test6 { |
133 | struct X { |
134 | X(); |
135 | X(const X&); |
136 | X &operator=(const X&); |
137 | ~X(); |
138 | }; |
139 | |
140 | struct P { |
141 | P(); |
142 | P(const P&); |
143 | ~P(); |
144 | X first; |
145 | X second; |
146 | }; |
147 | |
148 | P getP(); |
149 | |
150 | struct Base1 { |
151 | int i; |
152 | |
153 | virtual X f() { return X(); } |
154 | }; |
155 | |
156 | struct Base2 { |
157 | float real; |
158 | |
159 | virtual X f() { return X(); } |
160 | }; |
161 | |
162 | struct Thunks : Base1, Base2 { |
163 | long l; |
164 | |
165 | virtual X f(); |
166 | }; |
167 | |
168 | // CHECK-LABEL: define void @_ZThn16_N5Test66Thunks1fEv |
169 | // CHECK-NOT: memcpy |
170 | // CHECK: {{call void @_ZN5Test66Thunks1fEv.*sret}} |
171 | // CHECK: ret void |
172 | X Thunks::f() { return X(); } |
173 | } |
174 | |
175 | namespace Test7 { |
176 | // PR7188 |
177 | struct X { |
178 | X(); |
179 | X(const X&); |
180 | X &operator=(const X&); |
181 | ~X(); |
182 | }; |
183 | |
184 | struct Small { short s; }; |
185 | struct Large { |
186 | char array[1024]; |
187 | }; |
188 | |
189 | class A { |
190 | protected: |
191 | virtual void foo() = 0; |
192 | }; |
193 | |
194 | class B : public A { |
195 | protected: |
196 | virtual void bar() = 0; |
197 | }; |
198 | |
199 | class C : public A { |
200 | protected: |
201 | virtual void baz(X, X&, _Complex float, Small, Small&, Large) = 0; |
202 | }; |
203 | |
204 | class D : public B, |
205 | public C { |
206 | |
207 | void foo() {} |
208 | void bar() {} |
209 | void baz(X, X&, _Complex float, Small, Small&, Large); |
210 | }; |
211 | |
212 | void D::baz(X, X&, _Complex float, Small, Small&, Large) { } |
213 | |
214 | // CHECK-LABEL: define void @_ZThn8_N5Test71D3bazENS_1XERS1_CfNS_5SmallERS4_NS_5LargeE( |
215 | // CHECK-NOT: memcpy |
216 | // CHECK: ret void |
217 | void testD() { D d; } |
218 | } |
219 | |
220 | namespace Test8 { |
221 | struct NonPOD { ~NonPOD(); int x, y, z; }; |
222 | struct A { virtual void foo(); }; |
223 | struct B { virtual void bar(NonPOD); }; |
224 | struct C : A, B { virtual void bar(NonPOD); static void helper(NonPOD); }; |
225 | |
226 | // CHECK: define void @_ZN5Test81C6helperENS_6NonPODE([[NONPODTYPE:%.*]]* |
227 | void C::helper(NonPOD var) {} |
228 | |
229 | // CHECK-LABEL: define void @_ZThn8_N5Test81C3barENS_6NonPODE( |
230 | // CHECK-NOT: load [[NONPODTYPE]], [[NONPODTYPE]]* |
231 | // CHECK-NOT: memcpy |
232 | // CHECK: ret void |
233 | void C::bar(NonPOD var) {} |
234 | } |
235 | |
236 | // PR7241: Emitting thunks for a method shouldn't require the vtable for |
237 | // that class to be emitted. |
238 | namespace Test9 { |
239 | struct A { virtual ~A() { } }; |
240 | struct B : A { virtual void test() const {} }; |
241 | struct C : B { C(); ~C(); }; |
242 | struct D : C { D() {} }; |
243 | void test() { |
244 | D d; |
245 | } |
246 | } |
247 | |
248 | namespace Test10 { |
249 | struct A { virtual void foo(); }; |
250 | struct B { virtual void foo(); }; |
251 | struct C : A, B { void foo() {} }; |
252 | |
253 | // Test later. |
254 | void test() { |
255 | C c; |
256 | } |
257 | } |
258 | |
259 | // PR7611 |
260 | namespace Test11 { |
261 | struct A { virtual A* f(); }; |
262 | struct B : virtual A { virtual A* f(); }; |
263 | struct C : B { virtual C* f(); }; |
264 | C* C::f() { return 0; } |
265 | |
266 | // C::f itself. |
267 | // CHECK: define {{.*}} @_ZN6Test111C1fEv( |
268 | |
269 | // The this-adjustment and return-adjustment thunk required when |
270 | // C::f appears in a vtable where A is at a nonzero offset from C. |
271 | // CHECK: define {{.*}} @_ZTcv0_n24_v0_n32_N6Test111C1fEv( |
272 | |
273 | // The return-adjustment thunk required when C::f appears in a vtable |
274 | // where A is at a zero offset from C. |
275 | // CHECK: define {{.*}} @_ZTch0_v0_n32_N6Test111C1fEv( |
276 | } |
277 | |
278 | // Varargs thunk test. |
279 | namespace Test12 { |
280 | struct A { |
281 | virtual A* f(int x, ...); |
282 | }; |
283 | struct B { |
284 | virtual B* f(int x, ...); |
285 | }; |
286 | struct C : A, B { |
287 | virtual void c(); |
288 | virtual C* f(int x, ...); |
289 | }; |
290 | C* C::f(int x, ...) { return this; } |
291 | |
292 | // C::f |
293 | // CHECK: define {{.*}} @_ZN6Test121C1fEiz |
294 | |
295 | // Varargs thunk; check that both the this and covariant adjustments |
296 | // are generated. |
297 | // CHECK: define {{.*}} @_ZTchn8_h8_N6Test121C1fEiz |
298 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -8 |
299 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8 |
300 | } |
301 | |
302 | // PR13832 |
303 | namespace Test13 { |
304 | struct B1 { |
305 | virtual B1 &foo1(); |
306 | }; |
307 | struct Pad1 { |
308 | virtual ~Pad1(); |
309 | }; |
310 | struct Proxy1 : Pad1, B1 { |
311 | virtual ~Proxy1(); |
312 | }; |
313 | struct D : virtual Proxy1 { |
314 | virtual ~D(); |
315 | virtual D &foo1(); |
316 | }; |
317 | D& D::foo1() { |
318 | return *this; |
319 | } |
320 | // CHECK: define {{.*}} @_ZTcvn8_n32_v8_n24_N6Test131D4foo1Ev |
321 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -8 |
322 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -32 |
323 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 -24 |
324 | // CHECK: getelementptr inbounds i8, i8* {{.*}}, i64 8 |
325 | // CHECK: ret %"struct.Test13::D"* |
326 | } |
327 | |
328 | namespace Test14 { |
329 | class A { |
330 | virtual void f(); |
331 | }; |
332 | class B { |
333 | virtual void f(); |
334 | }; |
335 | class C : public A, public B { |
336 | virtual void f(); |
337 | }; |
338 | void C::f() { |
339 | } |
340 | // CHECK: define void @_ZThn8_N6Test141C1fEv({{.*}}) unnamed_addr [[NUW:#[0-9]+]] |
341 | } |
342 | |
343 | // Varargs non-covariant thunk test. |
344 | // PR18098 |
345 | namespace Test15 { |
346 | struct A { |
347 | virtual ~A(); |
348 | }; |
349 | struct B { |
350 | virtual void f(int x, ...); |
351 | }; |
352 | struct C : A, B { |
353 | virtual void c(); |
354 | virtual void f(int x, ...); |
355 | }; |
356 | void C::c() {} |
357 | |
358 | // C::c |
359 | // CHECK: declare void @_ZN6Test151C1fEiz |
360 | // non-virtual thunk to C::f |
361 | // CHECK: declare void @_ZThn8_N6Test151C1fEiz |
362 | } |
363 | |
364 | namespace Test16 { |
365 | struct A { |
366 | virtual ~A(); |
367 | }; |
368 | struct B { |
369 | virtual void foo(); |
370 | }; |
371 | struct C : public A, public B { |
372 | void foo() {} |
373 | }; |
374 | struct D : public C { |
375 | ~D(); |
376 | }; |
377 | D::~D() {} |
378 | // CHECK: define linkonce_odr void @_ZThn8_N6Test161C3fooEv({{.*}}) {{.*}} comdat |
379 | } |
380 | |
381 | /**** The following has to go at the end of the file ****/ |
382 | |
383 | // checking without opt |
384 | // CHECK-NONOPT-LABEL: define internal void @_ZThn8_N6Test4B12_GLOBAL__N_11C1fEv( |
385 | // CHECK-NONOPT-NOT: comdat |
386 | |
387 | // This is from Test5: |
388 | // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test51B1fEv |
389 | |
390 | // This is from Test10: |
391 | // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZN6Test101C3fooEv |
392 | // CHECK-NONOPT-LABEL: define linkonce_odr void @_ZThn8_N6Test101C3fooEv |
393 | |
394 | // Checking with opt |
395 | // CHECK-OPT-LABEL: define internal void @_ZThn8_N6Test4B12_GLOBAL__N_11C1fEv(%"struct.Test4B::(anonymous namespace)::C"* %this) unnamed_addr #0 align 2 |
396 | |
397 | // This is from Test5: |
398 | // CHECK-OPT-LABEL: define linkonce_odr void @_ZTv0_n24_N5Test51B1fEv |
399 | |
400 | // This is from Test10: |
401 | // CHECK-OPT-LABEL: define linkonce_odr void @_ZN6Test101C3fooEv |
402 | // CHECK-OPT-LABEL: define linkonce_odr void @_ZThn8_N6Test101C3fooEv |
403 | |
404 | // CHECK-NONOPT: attributes [[NUW]] = { noinline nounwind optnone uwtable{{.*}} } |
405 | // CHECK-OPT: attributes [[NUW]] = { nounwind uwtable{{.*}} } |
406 | |