]>
Commit | Line | Data |
---|---|---|
7257e56c | 1 | // TEST_CFLAGS -Wno-unused-parameter |
8972963c | 2 | |
7af964d1 | 3 | #include "test.h" |
cd5f04f5 | 4 | #include "testroot.i" |
8972963c | 5 | |
cd5f04f5 | 6 | #if __cplusplus && !__clang__ |
8972963c A |
7 | |
8 | int main() | |
9 | { | |
cd5f04f5 | 10 | // llvm-g++ is confused by @selector(foo::) and will never be fixed |
8972963c A |
11 | succeed(__FILE__); |
12 | } | |
13 | ||
14 | #else | |
15 | ||
7af964d1 | 16 | #include <objc/objc.h> |
cd5f04f5 A |
17 | #include <objc/runtime.h> |
18 | #include <objc/objc-internal.h> | |
8972963c A |
19 | #include <objc/objc-abi.h> |
20 | ||
8070259c A |
21 | #if __arm64__ |
22 | // no stret dispatchers | |
23 | # define SUPPORT_STRET 0 | |
24 | # define objc_msgSend_stret objc_msgSend | |
25 | # define objc_msgSendSuper2_stret objc_msgSendSuper2 | |
26 | # define objc_msgSend_stret_debug objc_msgSend_debug | |
27 | # define objc_msgSendSuper2_stret_debug objc_msgSendSuper2_debug | |
28 | # define method_invoke_stret method_invoke | |
29 | #else | |
30 | # define SUPPORT_STRET 1 | |
31 | #endif | |
32 | ||
33 | ||
8972963c A |
34 | #if defined(__arm__) |
35 | // rdar://8331406 | |
36 | # define ALIGN_() | |
37 | #else | |
38 | # define ALIGN_() asm(".align 4"); | |
39 | #endif | |
7af964d1 | 40 | |
cd5f04f5 | 41 | @interface Super : TestRoot @end |
7af964d1 A |
42 | |
43 | @interface Sub : Super @end | |
44 | ||
45 | static int state = 0; | |
46 | ||
cd5f04f5 A |
47 | static id SELF; |
48 | ||
49 | // for typeof() shorthand only | |
50 | id (*idmsg0)(id, SEL) __attribute__((unused)); | |
51 | long long (*llmsg0)(id, SEL) __attribute__((unused)); | |
52 | // struct stret (*stretmsg0)(id, SEL) __attribute__((unused)); | |
53 | double (*fpmsg0)(id, SEL) __attribute__((unused)); | |
54 | long double (*lfpmsg0)(id, SEL) __attribute__((unused)); | |
55 | ||
7af964d1 | 56 | |
cd5f04f5 | 57 | #define CHECK_ARGS(sel) \ |
7af964d1 | 58 | do { \ |
cd5f04f5 | 59 | testassert(self == SELF); \ |
7af964d1 A |
60 | testassert(_cmd == sel_registerName(#sel "::::::::::::::::::::::::::::"));\ |
61 | testassert(i1 == 1); \ | |
62 | testassert(i2 == 2); \ | |
63 | testassert(i3 == 3); \ | |
64 | testassert(i4 == 4); \ | |
65 | testassert(i5 == 5); \ | |
66 | testassert(i6 == 6); \ | |
67 | testassert(i7 == 7); \ | |
68 | testassert(i8 == 8); \ | |
69 | testassert(i9 == 9); \ | |
70 | testassert(i10 == 10); \ | |
71 | testassert(i11 == 11); \ | |
72 | testassert(i12 == 12); \ | |
73 | testassert(i13 == 13); \ | |
74 | testassert(f1 == 1.0); \ | |
75 | testassert(f2 == 2.0); \ | |
76 | testassert(f3 == 3.0); \ | |
77 | testassert(f4 == 4.0); \ | |
78 | testassert(f5 == 5.0); \ | |
79 | testassert(f6 == 6.0); \ | |
80 | testassert(f7 == 7.0); \ | |
81 | testassert(f8 == 8.0); \ | |
82 | testassert(f9 == 9.0); \ | |
83 | testassert(f10 == 10.0); \ | |
84 | testassert(f11 == 11.0); \ | |
85 | testassert(f12 == 12.0); \ | |
86 | testassert(f13 == 13.0); \ | |
87 | testassert(f14 == 14.0); \ | |
88 | testassert(f15 == 15.0); \ | |
89 | } while (0) | |
90 | ||
cd5f04f5 | 91 | #define CHECK_ARGS_NOARG(sel) \ |
8972963c | 92 | do { \ |
cd5f04f5 | 93 | testassert(self == SELF); \ |
8972963c A |
94 | testassert(_cmd == sel_registerName(#sel "_noarg"));\ |
95 | } while (0) | |
96 | ||
7257e56c | 97 | id NIL_RECEIVER; |
cd5f04f5 | 98 | id ID_RESULT; |
7af964d1 | 99 | long long LL_RESULT = __LONG_LONG_MAX__ - 2LL*__INT_MAX__; |
7af964d1 A |
100 | double FP_RESULT = __DBL_MIN__ + __DBL_EPSILON__; |
101 | long double LFP_RESULT = __LDBL_MIN__ + __LDBL_EPSILON__; | |
cd5f04f5 A |
102 | // STRET_RESULT in test.h |
103 | ||
104 | static struct stret zero; | |
7af964d1 | 105 | |
8070259c A |
106 | struct stret_i1 { |
107 | uintptr_t i1; | |
108 | }; | |
109 | struct stret_i2 { | |
110 | uintptr_t i1; | |
111 | uintptr_t i2; | |
112 | }; | |
113 | struct stret_i3 { | |
114 | uintptr_t i1; | |
115 | uintptr_t i2; | |
116 | uintptr_t i3; | |
117 | }; | |
118 | struct stret_i4 { | |
119 | uintptr_t i1; | |
120 | uintptr_t i2; | |
121 | uintptr_t i3; | |
122 | }; | |
123 | struct stret_i5 { | |
124 | uintptr_t i1; | |
125 | uintptr_t i2; | |
126 | uintptr_t i3; | |
127 | uintptr_t i4; | |
128 | uintptr_t i5; | |
129 | }; | |
130 | struct stret_i6 { | |
131 | uintptr_t i1; | |
132 | uintptr_t i2; | |
133 | uintptr_t i3; | |
134 | uintptr_t i4; | |
135 | uintptr_t i5; | |
136 | uintptr_t i6; | |
137 | }; | |
138 | struct stret_i7 { | |
139 | uintptr_t i1; | |
140 | uintptr_t i2; | |
141 | uintptr_t i3; | |
142 | uintptr_t i4; | |
143 | uintptr_t i5; | |
144 | uintptr_t i6; | |
145 | uintptr_t i7; | |
146 | }; | |
147 | struct stret_i8 { | |
148 | uintptr_t i1; | |
149 | uintptr_t i2; | |
150 | uintptr_t i3; | |
151 | uintptr_t i4; | |
152 | uintptr_t i5; | |
153 | uintptr_t i8; | |
154 | uintptr_t i9; | |
155 | }; | |
156 | struct stret_i9 { | |
157 | uintptr_t i1; | |
158 | uintptr_t i2; | |
159 | uintptr_t i3; | |
160 | uintptr_t i4; | |
161 | uintptr_t i5; | |
162 | uintptr_t i6; | |
163 | uintptr_t i7; | |
164 | uintptr_t i8; | |
165 | uintptr_t i9; | |
166 | }; | |
167 | ||
168 | struct stret_d1 { | |
169 | double d1; | |
170 | }; | |
171 | struct stret_d2 { | |
172 | double d1; | |
173 | double d2; | |
174 | }; | |
175 | struct stret_d3 { | |
176 | double d1; | |
177 | double d2; | |
178 | double d3; | |
179 | }; | |
180 | struct stret_d4 { | |
181 | double d1; | |
182 | double d2; | |
183 | double d3; | |
184 | }; | |
185 | struct stret_d5 { | |
186 | double d1; | |
187 | double d2; | |
188 | double d3; | |
189 | double d4; | |
190 | double d5; | |
191 | }; | |
192 | struct stret_d6 { | |
193 | double d1; | |
194 | double d2; | |
195 | double d3; | |
196 | double d4; | |
197 | double d5; | |
198 | double d6; | |
199 | }; | |
200 | struct stret_d7 { | |
201 | double d1; | |
202 | double d2; | |
203 | double d3; | |
204 | double d4; | |
205 | double d5; | |
206 | double d6; | |
207 | double d7; | |
208 | }; | |
209 | struct stret_d8 { | |
210 | double d1; | |
211 | double d2; | |
212 | double d3; | |
213 | double d4; | |
214 | double d5; | |
215 | double d8; | |
216 | double d9; | |
217 | }; | |
218 | struct stret_d9 { | |
219 | double d1; | |
220 | double d2; | |
221 | double d3; | |
222 | double d4; | |
223 | double d5; | |
224 | double d6; | |
225 | double d7; | |
226 | double d8; | |
227 | double d9; | |
228 | }; | |
229 | ||
7af964d1 A |
230 | |
231 | @implementation Super | |
cd5f04f5 | 232 | -(struct stret)stret { return STRET_RESULT; } |
7af964d1 | 233 | |
cd5f04f5 | 234 | -(id)idret: |
7257e56c | 235 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 236 | { |
cd5f04f5 | 237 | CHECK_ARGS(idret); |
7af964d1 A |
238 | state = 1; |
239 | return ID_RESULT; | |
240 | } | |
241 | ||
cd5f04f5 | 242 | -(long long)llret: |
7257e56c | 243 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 244 | { |
cd5f04f5 | 245 | CHECK_ARGS(llret); |
7af964d1 A |
246 | state = 2; |
247 | return LL_RESULT; | |
248 | } | |
249 | ||
cd5f04f5 | 250 | -(struct stret)stret: |
7257e56c | 251 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 252 | { |
cd5f04f5 | 253 | CHECK_ARGS(stret); |
7af964d1 A |
254 | state = 3; |
255 | return STRET_RESULT; | |
256 | } | |
257 | ||
cd5f04f5 | 258 | -(double)fpret: |
7257e56c | 259 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 260 | { |
cd5f04f5 | 261 | CHECK_ARGS(fpret); |
7af964d1 A |
262 | state = 4; |
263 | return FP_RESULT; | |
264 | } | |
265 | ||
cd5f04f5 | 266 | -(long double)lfpret: |
7257e56c | 267 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 268 | { |
cd5f04f5 | 269 | CHECK_ARGS(lfpret); |
7af964d1 A |
270 | state = 5; |
271 | return LFP_RESULT; | |
272 | } | |
273 | ||
274 | ||
cd5f04f5 | 275 | -(id)idret_noarg |
8972963c | 276 | { |
cd5f04f5 | 277 | CHECK_ARGS_NOARG(idret); |
8972963c A |
278 | state = 11; |
279 | return ID_RESULT; | |
280 | } | |
281 | ||
cd5f04f5 | 282 | -(long long)llret_noarg |
8972963c | 283 | { |
cd5f04f5 | 284 | CHECK_ARGS_NOARG(llret); |
8972963c A |
285 | state = 12; |
286 | return LL_RESULT; | |
287 | } | |
288 | ||
cd5f04f5 | 289 | -(struct stret)stret_noarg |
8972963c | 290 | { |
cd5f04f5 | 291 | CHECK_ARGS_NOARG(stret); |
8972963c A |
292 | state = 13; |
293 | return STRET_RESULT; | |
294 | } | |
8972963c | 295 | |
cd5f04f5 | 296 | -(double)fpret_noarg |
8972963c | 297 | { |
cd5f04f5 | 298 | CHECK_ARGS_NOARG(fpret); |
8972963c A |
299 | state = 14; |
300 | return FP_RESULT; | |
301 | } | |
302 | ||
cd5f04f5 | 303 | -(long double)lfpret_noarg |
8972963c | 304 | { |
cd5f04f5 | 305 | CHECK_ARGS_NOARG(lfpret); |
8972963c A |
306 | state = 15; |
307 | return LFP_RESULT; | |
308 | } | |
309 | ||
310 | ||
cd5f04f5 A |
311 | -(void)voidret_nop |
312 | { | |
313 | return; | |
314 | } | |
315 | ||
8070259c A |
316 | -(void)voidret_nop2 |
317 | { | |
318 | return; | |
319 | } | |
320 | ||
cd5f04f5 A |
321 | -(id)idret_nop |
322 | { | |
323 | return ID_RESULT; | |
324 | } | |
325 | ||
326 | -(long long)llret_nop | |
327 | { | |
328 | return LL_RESULT; | |
329 | } | |
330 | ||
331 | -(struct stret)stret_nop | |
332 | { | |
333 | return STRET_RESULT; | |
334 | } | |
335 | ||
336 | -(double)fpret_nop | |
337 | { | |
338 | return FP_RESULT; | |
339 | } | |
340 | ||
341 | -(long double)lfpret_nop | |
342 | { | |
343 | return LFP_RESULT; | |
344 | } | |
345 | ||
8070259c A |
346 | #define STRET_IMP(n) \ |
347 | +(struct stret_##n)stret_##n##_zero \ | |
348 | { \ | |
349 | struct stret_##n ret; \ | |
350 | bzero(&ret, sizeof(ret)); \ | |
351 | return ret; \ | |
352 | } \ | |
353 | +(struct stret_##n)stret_##n##_nonzero \ | |
354 | { \ | |
355 | struct stret_##n ret; \ | |
356 | memset(&ret, 0xff, sizeof(ret)); \ | |
357 | return ret; \ | |
358 | } | |
359 | ||
360 | STRET_IMP(i1) | |
361 | STRET_IMP(i2) | |
362 | STRET_IMP(i3) | |
363 | STRET_IMP(i4) | |
364 | STRET_IMP(i5) | |
365 | STRET_IMP(i6) | |
366 | STRET_IMP(i7) | |
367 | STRET_IMP(i8) | |
368 | STRET_IMP(i9) | |
369 | ||
370 | STRET_IMP(d1) | |
371 | STRET_IMP(d2) | |
372 | STRET_IMP(d3) | |
373 | STRET_IMP(d4) | |
374 | STRET_IMP(d5) | |
375 | STRET_IMP(d6) | |
376 | STRET_IMP(d7) | |
377 | STRET_IMP(d8) | |
378 | STRET_IMP(d9) | |
cd5f04f5 A |
379 | |
380 | ||
381 | +(id)idret: | |
7257e56c | 382 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 383 | { |
cd5f04f5 A |
384 | fail("+idret called instead of -idret"); |
385 | CHECK_ARGS(idret); | |
7af964d1 A |
386 | } |
387 | ||
cd5f04f5 | 388 | +(long long)llret: |
7257e56c | 389 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 390 | { |
cd5f04f5 A |
391 | fail("+llret called instead of -llret"); |
392 | CHECK_ARGS(llret); | |
7af964d1 A |
393 | } |
394 | ||
cd5f04f5 | 395 | +(struct stret)stret: |
7257e56c | 396 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 397 | { |
cd5f04f5 A |
398 | fail("+stret called instead of -stret"); |
399 | CHECK_ARGS(stret); | |
7af964d1 A |
400 | } |
401 | ||
cd5f04f5 | 402 | +(double)fpret: |
7257e56c | 403 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 404 | { |
cd5f04f5 A |
405 | fail("+fpret called instead of -fpret"); |
406 | CHECK_ARGS(fpret); | |
7af964d1 A |
407 | } |
408 | ||
cd5f04f5 | 409 | +(long double)lfpret: |
7257e56c | 410 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 | 411 | { |
cd5f04f5 A |
412 | fail("+lfpret called instead of -lfpret"); |
413 | CHECK_ARGS(lfpret); | |
7af964d1 A |
414 | } |
415 | ||
cd5f04f5 | 416 | +(id)idret_noarg |
8972963c | 417 | { |
cd5f04f5 A |
418 | fail("+idret_noarg called instead of -idret_noarg"); |
419 | CHECK_ARGS_NOARG(idret); | |
8972963c A |
420 | } |
421 | ||
cd5f04f5 | 422 | +(long long)llret_noarg |
8972963c | 423 | { |
cd5f04f5 A |
424 | fail("+llret_noarg called instead of -llret_noarg"); |
425 | CHECK_ARGS_NOARG(llret); | |
8972963c A |
426 | } |
427 | ||
cd5f04f5 | 428 | +(struct stret)stret_noarg |
8972963c | 429 | { |
cd5f04f5 A |
430 | fail("+stret_noarg called instead of -stret_noarg"); |
431 | CHECK_ARGS_NOARG(stret); | |
8972963c A |
432 | } |
433 | ||
cd5f04f5 | 434 | +(double)fpret_noarg |
8972963c | 435 | { |
cd5f04f5 A |
436 | fail("+fpret_noarg called instead of -fpret_noarg"); |
437 | CHECK_ARGS_NOARG(fpret); | |
8972963c A |
438 | } |
439 | ||
cd5f04f5 | 440 | +(long double)lfpret_noarg |
8972963c | 441 | { |
cd5f04f5 A |
442 | fail("+lfpret_noarg called instead of -lfpret_noarg"); |
443 | CHECK_ARGS_NOARG(lfpret); | |
8972963c A |
444 | } |
445 | ||
7af964d1 A |
446 | @end |
447 | ||
448 | ||
449 | @implementation Sub | |
450 | ||
cd5f04f5 | 451 | -(id)idret: |
7257e56c | 452 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 A |
453 | { |
454 | id result; | |
cd5f04f5 | 455 | CHECK_ARGS(idret); |
8972963c | 456 | state = 100; |
7af964d1 A |
457 | result = [super idret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; |
458 | testassert(state == 1); | |
459 | testassert(result == ID_RESULT); | |
8972963c | 460 | state = 101; |
7af964d1 A |
461 | return result; |
462 | } | |
463 | ||
cd5f04f5 | 464 | -(long long)llret: |
7257e56c | 465 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 A |
466 | { |
467 | long long result; | |
cd5f04f5 | 468 | CHECK_ARGS(llret); |
8972963c | 469 | state = 100; |
7af964d1 A |
470 | result = [super llret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; |
471 | testassert(state == 2); | |
472 | testassert(result == LL_RESULT); | |
8972963c | 473 | state = 102; |
7af964d1 A |
474 | return result; |
475 | } | |
476 | ||
cd5f04f5 | 477 | -(struct stret)stret: |
7257e56c | 478 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 A |
479 | { |
480 | struct stret result; | |
cd5f04f5 | 481 | CHECK_ARGS(stret); |
8972963c | 482 | state = 100; |
7af964d1 A |
483 | result = [super stret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; |
484 | testassert(state == 3); | |
485 | testassert(stret_equal(result, STRET_RESULT)); | |
8972963c | 486 | state = 103; |
7af964d1 A |
487 | return result; |
488 | } | |
489 | ||
cd5f04f5 | 490 | -(double)fpret: |
7257e56c | 491 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 A |
492 | { |
493 | double result; | |
cd5f04f5 | 494 | CHECK_ARGS(fpret); |
8972963c | 495 | state = 100; |
7af964d1 A |
496 | result = [super fpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; |
497 | testassert(state == 4); | |
498 | testassert(result == FP_RESULT); | |
8972963c | 499 | state = 104; |
7af964d1 A |
500 | return result; |
501 | } | |
502 | ||
cd5f04f5 | 503 | -(long double)lfpret: |
7257e56c | 504 | (int)i1 :(int)i2 :(int)i3 :(int)i4 :(int)i5 :(int)i6 :(int)i7 :(int)i8 :(int)i9 :(int)i10 :(int)i11 :(int)i12 :(int)i13 :(double)f1 :(double)f2 :(double)f3 :(double)f4 :(double)f5 :(double)f6 :(double)f7 :(double)f8 :(double)f9 :(double)f10 :(double)f11 :(double)f12 :(double)f13 :(double)f14 :(double)f15 |
7af964d1 A |
505 | { |
506 | long double result; | |
cd5f04f5 | 507 | CHECK_ARGS(lfpret); |
8972963c | 508 | state = 100; |
7af964d1 A |
509 | result = [super lfpret:i1:i2:i3:i4:i5:i6:i7:i8:i9:i10:i11:i12:i13:f1:f2:f3:f4:f5:f6:f7:f8:f9:f10:f11:f12:f13:f14:f15]; |
510 | testassert(state == 5); | |
511 | testassert(result == LFP_RESULT); | |
8972963c A |
512 | state = 105; |
513 | return result; | |
514 | } | |
515 | ||
516 | ||
cd5f04f5 | 517 | -(id)idret_noarg |
8972963c A |
518 | { |
519 | id result; | |
cd5f04f5 | 520 | CHECK_ARGS_NOARG(idret); |
8972963c A |
521 | state = 100; |
522 | result = [super idret_noarg]; | |
523 | testassert(state == 11); | |
524 | testassert(result == ID_RESULT); | |
525 | state = 111; | |
526 | return result; | |
527 | } | |
528 | ||
cd5f04f5 | 529 | -(long long)llret_noarg |
8972963c A |
530 | { |
531 | long long result; | |
cd5f04f5 | 532 | CHECK_ARGS_NOARG(llret); |
8972963c A |
533 | state = 100; |
534 | result = [super llret_noarg]; | |
535 | testassert(state == 12); | |
536 | testassert(result == LL_RESULT); | |
537 | state = 112; | |
538 | return result; | |
539 | } | |
cd5f04f5 A |
540 | |
541 | -(struct stret)stret_noarg | |
8972963c A |
542 | { |
543 | struct stret result; | |
cd5f04f5 | 544 | CHECK_ARGS_NOARG(stret); |
8972963c A |
545 | state = 100; |
546 | result = [super stret_noarg]; | |
547 | testassert(state == 13); | |
548 | testassert(stret_equal(result, STRET_RESULT)); | |
549 | state = 113; | |
550 | return result; | |
551 | } | |
cd5f04f5 A |
552 | |
553 | -(double)fpret_noarg | |
8972963c A |
554 | { |
555 | double result; | |
cd5f04f5 | 556 | CHECK_ARGS_NOARG(fpret); |
8972963c A |
557 | state = 100; |
558 | result = [super fpret_noarg]; | |
559 | testassert(state == 14); | |
560 | testassert(result == FP_RESULT); | |
561 | state = 114; | |
562 | return result; | |
563 | } | |
564 | ||
cd5f04f5 | 565 | -(long double)lfpret_noarg |
8972963c A |
566 | { |
567 | long double result; | |
cd5f04f5 | 568 | CHECK_ARGS_NOARG(lfpret); |
8972963c A |
569 | state = 100; |
570 | result = [super lfpret_noarg]; | |
571 | testassert(state == 15); | |
572 | testassert(result == LFP_RESULT); | |
573 | state = 115; | |
7af964d1 A |
574 | return result; |
575 | } | |
576 | ||
cd5f04f5 A |
577 | @end |
578 | ||
579 | ||
7257e56c | 580 | #if OBJC_HAVE_TAGGED_POINTERS |
7af964d1 | 581 | |
cd5f04f5 | 582 | @interface TaggedSub : Sub @end |
7af964d1 | 583 | |
cd5f04f5 | 584 | @implementation TaggedSub : Sub |
7af964d1 | 585 | |
cd5f04f5 | 586 | +(void)initialize |
7af964d1 | 587 | { |
7257e56c | 588 | _objc_registerTaggedPointerClass(OBJC_TAG_7, self); |
7af964d1 A |
589 | } |
590 | ||
cd5f04f5 A |
591 | @end |
592 | ||
8070259c A |
593 | #endif |
594 | ||
595 | ||
cd5f04f5 A |
596 | // DWARF checking machinery |
597 | ||
8070259c A |
598 | #if TARGET_OS_WIN32 |
599 | // unimplemented on this platform | |
600 | #elif !__OBJC2__ | |
601 | // 32-bit Mac doesn't use DWARF unwind | |
602 | #elif TARGET_OS_IPHONE && __arm__ | |
603 | // 32-bit iOS device doesn't use DWARF unwind | |
604 | #elif __has_feature(objc_arc) | |
605 | // ARC's extra RR calls hit the traps at the wrong times | |
606 | #else | |
607 | ||
608 | #define TEST_DWARF 1 | |
609 | ||
610 | // Classes with no implementations and no cache contents from elsewhere. | |
611 | @interface SuperDW : TestRoot @end | |
612 | @implementation SuperDW @end | |
613 | ||
614 | @interface Sub0DW : SuperDW @end | |
615 | @implementation Sub0DW @end | |
616 | ||
617 | @interface SubDW : Sub0DW @end | |
618 | @implementation SubDW @end | |
619 | ||
cd5f04f5 A |
620 | #include <dlfcn.h> |
621 | #include <signal.h> | |
622 | #include <sys/mman.h> | |
623 | #include <libunwind.h> | |
624 | ||
625 | #define UNW_STEP_SUCCESS 1 | |
626 | #define UNW_STEP_END 0 | |
627 | ||
628 | bool caught = false; | |
629 | uintptr_t clobbered; | |
630 | ||
8070259c A |
631 | __BEGIN_DECLS |
632 | extern void callit(void *obj, void *sel, void *fn); | |
633 | extern struct stret callit_stret(void *obj, void *sel, void *fn); | |
634 | __END_DECLS | |
635 | ||
636 | #if __x86_64__ | |
637 | ||
638 | #define OTOOL "/usr/bin/xcrun otool -arch x86_64 " | |
639 | ||
640 | typedef uint8_t insn_t; | |
641 | #define BREAK_INSN ((insn_t)0xcc) // int3 | |
642 | ||
cd5f04f5 A |
643 | uintptr_t r12 = 0; |
644 | uintptr_t r13 = 0; | |
645 | uintptr_t r14 = 0; | |
646 | uintptr_t r15 = 0; | |
647 | uintptr_t rbx = 0; | |
648 | uintptr_t rbp = 0; | |
649 | uintptr_t rsp = 0; | |
650 | uintptr_t rip = 0; | |
651 | ||
652 | void handle_exception(x86_thread_state64_t *state) | |
7af964d1 | 653 | { |
cd5f04f5 A |
654 | unw_cursor_t curs; |
655 | unw_word_t reg; | |
656 | int err; | |
657 | int step; | |
658 | ||
659 | err = unw_init_local(&curs, (unw_context_t *)state); | |
660 | testassert(!err); | |
661 | ||
662 | step = unw_step(&curs); | |
663 | testassert(step == UNW_STEP_SUCCESS); | |
664 | ||
665 | err = unw_get_reg(&curs, UNW_X86_64_R12, ®); | |
666 | testassert(!err); | |
667 | testassert(reg == r12); | |
668 | ||
669 | err = unw_get_reg(&curs, UNW_X86_64_R13, ®); | |
670 | testassert(!err); | |
671 | testassert(reg == r13); | |
672 | ||
673 | err = unw_get_reg(&curs, UNW_X86_64_R14, ®); | |
674 | testassert(!err); | |
675 | testassert(reg == r14); | |
676 | ||
677 | err = unw_get_reg(&curs, UNW_X86_64_R15, ®); | |
678 | testassert(!err); | |
679 | testassert(reg == r15); | |
680 | ||
681 | err = unw_get_reg(&curs, UNW_X86_64_RBX, ®); | |
682 | testassert(!err); | |
683 | testassert(reg == rbx); | |
684 | ||
685 | err = unw_get_reg(&curs, UNW_X86_64_RBP, ®); | |
686 | testassert(!err); | |
687 | testassert(reg == rbp); | |
688 | ||
689 | err = unw_get_reg(&curs, UNW_X86_64_RSP, ®); | |
690 | testassert(!err); | |
691 | testassert(reg == rsp); | |
692 | ||
693 | err = unw_get_reg(&curs, UNW_REG_IP, ®); | |
694 | testassert(!err); | |
695 | testassert(reg == rip); | |
696 | ||
697 | ||
698 | // set thread state to unwound state | |
699 | state->__r12 = r12; | |
700 | state->__r13 = r13; | |
701 | state->__r14 = r14; | |
702 | state->__r15 = r15; | |
703 | state->__rbx = rbx; | |
704 | state->__rbp = rbp; | |
705 | state->__rsp = rsp; | |
706 | state->__rip = rip; | |
707 | ||
708 | caught = true; | |
7af964d1 A |
709 | } |
710 | ||
cd5f04f5 A |
711 | |
712 | void sigtrap(int sig, siginfo_t *info, void *cc) | |
7af964d1 | 713 | { |
cd5f04f5 A |
714 | ucontext_t *uc = (ucontext_t *)cc; |
715 | mcontext_t mc = (mcontext_t)uc->uc_mcontext; | |
716 | ||
717 | testprintf(" handled\n"); | |
718 | ||
719 | testassert(sig == SIGTRAP); | |
720 | testassert((uintptr_t)info->si_addr-1 == clobbered); | |
721 | ||
722 | handle_exception(&mc->__ss); | |
723 | // handle_exception changed register state for continuation | |
7af964d1 A |
724 | } |
725 | ||
cd5f04f5 A |
726 | __asm__( |
727 | "\n .text" | |
728 | "\n .globl _callit" | |
729 | "\n _callit:" | |
8070259c | 730 | // save sp and return address to variables |
cd5f04f5 A |
731 | "\n movq (%rsp), %r10" |
732 | "\n movq %r10, _rip(%rip)" | |
733 | "\n movq %rsp, _rsp(%rip)" | |
734 | "\n addq $8, _rsp(%rip)" // rewind to pre-call value | |
735 | // save other non-volatile registers to variables | |
736 | "\n movq %rbx, _rbx(%rip)" | |
737 | "\n movq %rbp, _rbp(%rip)" | |
738 | "\n movq %r12, _r12(%rip)" | |
739 | "\n movq %r13, _r13(%rip)" | |
740 | "\n movq %r14, _r14(%rip)" | |
741 | "\n movq %r15, _r15(%rip)" | |
742 | "\n jmpq *%rdx" | |
743 | ); | |
744 | ||
745 | __asm__( | |
746 | "\n .text" | |
747 | "\n .globl _callit_stret" | |
748 | "\n _callit_stret:" | |
8070259c | 749 | // save sp and return address to variables |
cd5f04f5 A |
750 | "\n movq (%rsp), %r10" |
751 | "\n movq %r10, _rip(%rip)" | |
752 | "\n movq %rsp, _rsp(%rip)" | |
753 | "\n addq $8, _rsp(%rip)" // rewind to pre-call value | |
754 | // save other non-volatile registers to variables | |
755 | "\n movq %rbx, _rbx(%rip)" | |
756 | "\n movq %rbp, _rbp(%rip)" | |
757 | "\n movq %r12, _r12(%rip)" | |
758 | "\n movq %r13, _r13(%rip)" | |
759 | "\n movq %r14, _r14(%rip)" | |
760 | "\n movq %r15, _r15(%rip)" | |
761 | "\n jmpq *%rcx" | |
762 | ); | |
763 | ||
8070259c A |
764 | |
765 | // x86_64 | |
766 | ||
767 | #elif __i386__ | |
768 | ||
769 | #define OTOOL "/usr/bin/xcrun otool -arch i386 " | |
770 | ||
771 | typedef uint8_t insn_t; | |
772 | #define BREAK_INSN ((insn_t)0xcc) // int3 | |
773 | ||
774 | uintptr_t eip = 0; | |
775 | uintptr_t esp = 0; | |
776 | uintptr_t ebx = 0; | |
777 | uintptr_t ebp = 0; | |
778 | uintptr_t edi = 0; | |
779 | uintptr_t esi = 0; | |
780 | uintptr_t espfix = 0; | |
781 | ||
782 | void handle_exception(i386_thread_state_t *state) | |
783 | { | |
784 | unw_cursor_t curs; | |
785 | unw_word_t reg; | |
786 | int err; | |
787 | int step; | |
788 | ||
789 | err = unw_init_local(&curs, (unw_context_t *)state); | |
790 | testassert(!err); | |
791 | ||
792 | step = unw_step(&curs); | |
793 | testassert(step == UNW_STEP_SUCCESS); | |
794 | ||
795 | err = unw_get_reg(&curs, UNW_REG_IP, ®); | |
796 | testassert(!err); | |
797 | testassert(reg == eip); | |
798 | ||
799 | err = unw_get_reg(&curs, UNW_X86_ESP, ®); | |
800 | testassert(!err); | |
801 | testassert(reg == esp); | |
802 | ||
803 | err = unw_get_reg(&curs, UNW_X86_EBX, ®); | |
804 | testassert(!err); | |
805 | testassert(reg == ebx); | |
806 | ||
807 | err = unw_get_reg(&curs, UNW_X86_EBP, ®); | |
808 | testassert(!err); | |
809 | testassert(reg == ebp); | |
810 | ||
811 | err = unw_get_reg(&curs, UNW_X86_EDI, ®); | |
812 | testassert(!err); | |
813 | testassert(reg == edi); | |
814 | ||
815 | err = unw_get_reg(&curs, UNW_X86_ESI, ®); | |
816 | testassert(!err); | |
817 | testassert(reg == esi); | |
818 | ||
819 | ||
820 | // set thread state to unwound state | |
821 | state->__eip = eip; | |
822 | state->__esp = esp + espfix; | |
823 | state->__ebx = ebx; | |
824 | state->__ebp = ebp; | |
825 | state->__edi = edi; | |
826 | state->__esi = esi; | |
827 | ||
828 | caught = true; | |
829 | } | |
830 | ||
831 | ||
832 | void sigtrap(int sig, siginfo_t *info, void *cc) | |
833 | { | |
834 | ucontext_t *uc = (ucontext_t *)cc; | |
835 | mcontext_t mc = (mcontext_t)uc->uc_mcontext; | |
836 | ||
837 | testprintf(" handled\n"); | |
838 | ||
839 | testassert(sig == SIGTRAP); | |
840 | testassert((uintptr_t)info->si_addr-1 == clobbered); | |
841 | ||
842 | handle_exception(&mc->__ss); | |
843 | // handle_exception changed register state for continuation | |
844 | } | |
845 | ||
846 | __asm__( | |
847 | "\n .text" | |
848 | "\n .globl _callit" | |
849 | "\n _callit:" | |
850 | // save sp and return address to variables | |
851 | "\n call 1f" | |
852 | "\n 1: popl %edx" | |
853 | "\n movl (%esp), %eax" | |
854 | "\n movl %eax, _eip-1b(%edx)" | |
855 | "\n movl %esp, _esp-1b(%edx)" | |
856 | "\n addl $4, _esp-1b(%edx)" // rewind to pre-call value | |
857 | "\n movl $0, _espfix-1b(%edx)" | |
858 | // save other non-volatile registers to variables | |
859 | "\n movl %ebx, _ebx-1b(%edx)" | |
860 | "\n movl %ebp, _ebp-1b(%edx)" | |
861 | "\n movl %edi, _edi-1b(%edx)" | |
862 | "\n movl %esi, _esi-1b(%edx)" | |
863 | "\n jmpl *12(%esp)" | |
864 | ); | |
865 | ||
866 | __asm__( | |
867 | "\n .text" | |
868 | "\n .globl _callit_stret" | |
869 | "\n _callit_stret:" | |
870 | // save sp and return address to variables | |
871 | "\n call 1f" | |
872 | "\n 1: popl %edx" | |
873 | "\n movl (%esp), %eax" | |
874 | "\n movl %eax, _eip-1b(%edx)" | |
875 | "\n movl %esp, _esp-1b(%edx)" | |
876 | "\n addl $4, _esp-1b(%edx)" // rewind to pre-call value | |
877 | "\n movl $4, _espfix-1b(%edx)" | |
878 | // save other non-volatile registers to variables | |
879 | "\n movl %ebx, _ebx-1b(%edx)" | |
880 | "\n movl %ebp, _ebp-1b(%edx)" | |
881 | "\n movl %edi, _edi-1b(%edx)" | |
882 | "\n movl %esi, _esi-1b(%edx)" | |
883 | "\n jmpl *16(%esp)" | |
884 | ); | |
885 | ||
886 | ||
887 | // i386 | |
888 | #elif __arm64__ | |
889 | ||
890 | #include <sys/ucontext.h> | |
891 | ||
892 | // runs on iOS device, no xcrun command present | |
893 | #define OTOOL "/usr/bin/otool -arch arm64 " | |
894 | ||
895 | typedef uint32_t insn_t; | |
896 | #define BREAK_INSN ((insn_t)0xd4200020) // brk #1 | |
897 | ||
898 | uintptr_t x19 = 0; | |
899 | uintptr_t x20 = 0; | |
900 | uintptr_t x21 = 0; | |
901 | uintptr_t x22 = 0; | |
902 | uintptr_t x23 = 0; | |
903 | uintptr_t x24 = 0; | |
904 | uintptr_t x25 = 0; | |
905 | uintptr_t x26 = 0; | |
906 | uintptr_t x27 = 0; | |
907 | uintptr_t x28 = 0; | |
908 | uintptr_t fp = 0; | |
909 | uintptr_t sp = 0; | |
910 | uintptr_t pc = 0; | |
911 | ||
912 | void handle_exception(arm_thread_state64_t *state) | |
913 | { | |
914 | unw_cursor_t curs; | |
915 | unw_word_t reg; | |
916 | int err; | |
917 | int step; | |
918 | ||
919 | err = unw_init_local(&curs, (unw_context_t *)state); | |
920 | testassert(!err); | |
921 | ||
922 | step = unw_step(&curs); | |
923 | testassert(step == UNW_STEP_SUCCESS); | |
924 | ||
925 | err = unw_get_reg(&curs, UNW_ARM64_X19, ®); | |
926 | testassert(!err); | |
927 | testassert(reg == x19); | |
928 | ||
929 | err = unw_get_reg(&curs, UNW_ARM64_X20, ®); | |
930 | testassert(!err); | |
931 | testassert(reg == x20); | |
932 | ||
933 | err = unw_get_reg(&curs, UNW_ARM64_X21, ®); | |
934 | testassert(!err); | |
935 | testassert(reg == x21); | |
936 | ||
937 | err = unw_get_reg(&curs, UNW_ARM64_X22, ®); | |
938 | testassert(!err); | |
939 | testassert(reg == x22); | |
940 | ||
941 | err = unw_get_reg(&curs, UNW_ARM64_X23, ®); | |
942 | testassert(!err); | |
943 | testassert(reg == x23); | |
944 | ||
945 | err = unw_get_reg(&curs, UNW_ARM64_X24, ®); | |
946 | testassert(!err); | |
947 | testassert(reg == x24); | |
948 | ||
949 | err = unw_get_reg(&curs, UNW_ARM64_X25, ®); | |
950 | testassert(!err); | |
951 | testassert(reg == x25); | |
952 | ||
953 | err = unw_get_reg(&curs, UNW_ARM64_X26, ®); | |
954 | testassert(!err); | |
955 | testassert(reg == x26); | |
956 | ||
957 | err = unw_get_reg(&curs, UNW_ARM64_X27, ®); | |
958 | testassert(!err); | |
959 | testassert(reg == x27); | |
960 | ||
961 | err = unw_get_reg(&curs, UNW_ARM64_X28, ®); | |
962 | testassert(!err); | |
963 | testassert(reg == x28); | |
964 | ||
965 | err = unw_get_reg(&curs, UNW_ARM64_FP, ®); | |
966 | testassert(!err); | |
967 | testassert(reg == fp); | |
968 | ||
969 | err = unw_get_reg(&curs, UNW_ARM64_SP, ®); | |
970 | testassert(!err); | |
971 | testassert(reg == sp); | |
972 | ||
973 | err = unw_get_reg(&curs, UNW_REG_IP, ®); | |
974 | testassert(!err); | |
975 | testassert(reg == pc); | |
976 | ||
977 | // libunwind restores PC into LR and doesn't track LR | |
978 | // err = unw_get_reg(&curs, UNW_ARM64_LR, ®); | |
979 | // testassert(!err); | |
980 | // testassert(reg == lr); | |
981 | ||
982 | // set thread state to unwound state | |
983 | state->__x[19] = x19; | |
984 | state->__x[20] = x20; | |
985 | state->__x[20] = x21; | |
986 | state->__x[22] = x22; | |
987 | state->__x[23] = x23; | |
988 | state->__x[24] = x24; | |
989 | state->__x[25] = x25; | |
990 | state->__x[26] = x26; | |
991 | state->__x[27] = x27; | |
992 | state->__x[28] = x28; | |
993 | state->__fp = fp; | |
994 | state->__lr = pc; // libunwind restores PC into LR | |
995 | state->__sp = sp; | |
996 | state->__pc = pc; | |
997 | ||
998 | caught = true; | |
999 | } | |
1000 | ||
1001 | ||
1002 | void sigtrap(int sig, siginfo_t *info, void *cc) | |
1003 | { | |
1004 | ucontext_t *uc = (ucontext_t *)cc; | |
1005 | struct __darwin_mcontext64 *mc = (struct __darwin_mcontext64 *)uc->uc_mcontext; | |
1006 | ||
1007 | testprintf(" handled\n"); | |
1008 | ||
1009 | testassert(sig == SIGTRAP); | |
1010 | testassert((uintptr_t)info->si_addr == clobbered); | |
1011 | ||
1012 | handle_exception(&mc->__ss); | |
1013 | // handle_exception changed register state for continuation | |
1014 | } | |
1015 | ||
1016 | ||
1017 | __asm__( | |
1018 | "\n .text" | |
1019 | "\n .globl _callit" | |
1020 | "\n _callit:" | |
1021 | // save sp and return address to variables | |
1022 | "\n mov x16, sp" | |
1023 | "\n adrp x17, _sp@PAGE" | |
1024 | "\n str x16, [x17, _sp@PAGEOFF]" | |
1025 | "\n adrp x17, _pc@PAGE" | |
1026 | "\n str lr, [x17, _pc@PAGEOFF]" | |
1027 | // save other non-volatile registers to variables | |
1028 | "\n adrp x17, _x19@PAGE" | |
1029 | "\n str x19, [x17, _x19@PAGEOFF]" | |
1030 | "\n adrp x17, _x19@PAGE" | |
1031 | "\n str x20, [x17, _x20@PAGEOFF]" | |
1032 | "\n adrp x17, _x19@PAGE" | |
1033 | "\n str x21, [x17, _x21@PAGEOFF]" | |
1034 | "\n adrp x17, _x19@PAGE" | |
1035 | "\n str x22, [x17, _x22@PAGEOFF]" | |
1036 | "\n adrp x17, _x19@PAGE" | |
1037 | "\n str x23, [x17, _x23@PAGEOFF]" | |
1038 | "\n adrp x17, _x19@PAGE" | |
1039 | "\n str x24, [x17, _x24@PAGEOFF]" | |
1040 | "\n adrp x17, _x19@PAGE" | |
1041 | "\n str x25, [x17, _x25@PAGEOFF]" | |
1042 | "\n adrp x17, _x19@PAGE" | |
1043 | "\n str x26, [x17, _x26@PAGEOFF]" | |
1044 | "\n adrp x17, _x19@PAGE" | |
1045 | "\n str x27, [x17, _x27@PAGEOFF]" | |
1046 | "\n adrp x17, _x19@PAGE" | |
1047 | "\n str x28, [x17, _x28@PAGEOFF]" | |
1048 | "\n adrp x17, _x19@PAGE" | |
1049 | "\n str fp, [x17, _fp@PAGEOFF]" | |
1050 | "\n br x2" | |
1051 | ); | |
1052 | ||
1053 | ||
1054 | // arm64 | |
1055 | #else | |
1056 | ||
1057 | #error unknown architecture | |
1058 | ||
1059 | #endif | |
1060 | ||
1061 | ||
1062 | insn_t set(uintptr_t dst, insn_t newvalue) | |
7af964d1 | 1063 | { |
8070259c A |
1064 | uintptr_t start = dst & ~(PAGE_MAX_SIZE-1); |
1065 | mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_WRITE); | |
1066 | insn_t oldvalue = *(insn_t *)dst; | |
1067 | *(insn_t *)dst = newvalue; | |
1068 | mprotect((void*)start, PAGE_MAX_SIZE, PROT_READ|PROT_EXEC); | |
1069 | return oldvalue; | |
1070 | } | |
1071 | ||
1072 | insn_t clobber(void *fn, uintptr_t offset) | |
1073 | { | |
1074 | clobbered = (uintptr_t)fn + offset; | |
1075 | return set((uintptr_t)fn + offset, BREAK_INSN); | |
1076 | } | |
1077 | ||
1078 | void unclobber(void *fn, uintptr_t offset, insn_t oldvalue) | |
1079 | { | |
1080 | set((uintptr_t)fn + offset, oldvalue); | |
1081 | } | |
1082 | ||
1083 | ||
1084 | uintptr_t *getOffsets(void *symbol, const char *symname, uintptr_t *outBase) | |
1085 | { | |
1086 | uintptr_t *result = (uintptr_t *)malloc(1000 * sizeof(uintptr_t)); | |
1087 | uintptr_t *end = result + 1000; | |
cd5f04f5 A |
1088 | uintptr_t *p = result; |
1089 | ||
1090 | // find library | |
1091 | Dl_info dl; | |
1092 | dladdr(symbol, &dl); | |
1093 | ||
1094 | // call `otool` on library | |
1095 | unsetenv("DYLD_LIBRARY_PATH"); | |
1096 | unsetenv("DYLD_ROOT_PATH"); | |
1097 | unsetenv("DYLD_INSERT_LIBRARIES"); | |
8070259c A |
1098 | unsetenv("DYLD_SHARED_REGION"); |
1099 | unsetenv("DYLD_SHARED_CACHE_DIR"); | |
1100 | unsetenv("DYLD_SHARED_CACHE_DONT_VALIDATE"); | |
cd5f04f5 | 1101 | char *cmd; |
8070259c | 1102 | asprintf(&cmd, OTOOL "-tv -p _%s %s", |
cd5f04f5 | 1103 | symname, dl.dli_fname); |
7257e56c | 1104 | testprintf("%s\n", cmd); |
cd5f04f5 A |
1105 | FILE *disa = popen(cmd, "r"); |
1106 | free(cmd); | |
1107 | testassert(disa); | |
1108 | ||
1109 | // read past "_symname:" line | |
1110 | char *line; | |
1111 | size_t len; | |
1112 | while ((line = fgetln(disa, &len))) { | |
1113 | if (0 == strncmp(1+line, symname, MIN(len-1, strlen(symname)))) break; | |
1114 | } | |
7af964d1 | 1115 | |
cd5f04f5 A |
1116 | // read instructions and save offsets |
1117 | char op[128]; | |
1118 | long base = 0; | |
1119 | long addr; | |
1120 | while (2 == fscanf(disa, "%lx%s%*[^\n]\n", &addr, op)) { | |
1121 | if (base == 0) base = addr; | |
1122 | if (0 != strncmp(op, "nop", 3)) { | |
1123 | testassert(p < end); | |
1124 | *p++ = addr - base; | |
1125 | } else { | |
1126 | // assume nops are unreached (e.g. alignment padding) | |
1127 | } | |
1128 | } | |
1129 | pclose(disa); | |
7af964d1 | 1130 | |
8070259c A |
1131 | #if __arm64__ |
1132 | // Also add breakpoints in _objc_msgSend_uncached_impcache | |
1133 | // (which is the slow path and has a frame to unwind) | |
1134 | if (0 != strcmp(symname, "_objc_msgSend_uncached_impcache")) { | |
1135 | uintptr_t base2; | |
1136 | uintptr_t *more_offsets = getOffsets(symbol, "_objc_msgSend_uncached_impcache", &base2); | |
1137 | uintptr_t *q = more_offsets; | |
1138 | // Skip prologue because it's imprecisely modeled in compact unwind | |
1139 | testassert(*q != ~0UL); | |
1140 | q++; | |
1141 | testassert(*q != ~0UL); | |
1142 | q++; | |
1143 | while (*q != ~0UL) *p++ = *q++ + base2 - base; | |
1144 | // Skip return because it's imprecisely modeled in compact unwind | |
1145 | p--; | |
1146 | free(more_offsets); | |
1147 | } | |
1148 | #endif | |
1149 | ||
cd5f04f5 A |
1150 | testassert(p > result); |
1151 | testassert(p < end); | |
1152 | *p = ~0UL; | |
8070259c | 1153 | #if __x86_64__ |
7257e56c A |
1154 | // hack: skip last instruction because libunwind blows up if it's |
1155 | // one byte long and followed by the next function with no NOPs first | |
8070259c A |
1156 | p[-1] = ~0UL; |
1157 | #endif | |
1158 | if (outBase) *outBase = base; | |
cd5f04f5 A |
1159 | return result; |
1160 | } | |
7af964d1 | 1161 | |
8070259c A |
1162 | void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) __attribute__((noinline)); |
1163 | void CALLIT(void *o, void *sel_arg, SEL s, void *f, bool stret) | |
cd5f04f5 A |
1164 | { |
1165 | uintptr_t message_ref[2]; | |
1166 | if (sel_arg != s) { | |
1167 | // fixup dispatch | |
1168 | // copy to a local buffer to keep sel_arg un-fixed-up | |
1169 | memcpy(message_ref, sel_arg, sizeof(message_ref)); | |
1170 | sel_arg = message_ref; | |
1171 | } | |
8070259c A |
1172 | if (!stret) callit(o, sel_arg, f); |
1173 | #if SUPPORT_STRET | |
1174 | else callit_stret(o, sel_arg, f); | |
1175 | #else | |
1176 | else fail("stret?"); | |
1177 | #endif | |
1178 | } | |
1179 | ||
1180 | void test_dw_forward(void) | |
1181 | { | |
1182 | return; | |
1183 | } | |
1184 | ||
1185 | struct stret test_dw_forward_stret(void) | |
1186 | { | |
1187 | return zero; | |
cd5f04f5 | 1188 | } |
7af964d1 | 1189 | |
cd5f04f5 A |
1190 | // sub = ordinary receiver object |
1191 | // tagged = tagged receiver object | |
1192 | // SEL = selector to send | |
1193 | // sub_arg = arg to pass in receiver register (may be objc_super struct) | |
1194 | // tagged_arg = arg to pass in receiver register (may be objc_super struct) | |
1195 | // sel_arg = arg to pass in sel register (may be message_ref) | |
8070259c A |
1196 | // uncaughtAllowed is the number of acceptable unreachable instructions |
1197 | // (for example, the ones that handle the corrupt-cache-error case) | |
1198 | void test_dw(const char *name, id sub, id tagged, bool stret, | |
1199 | int uncaughtAllowed) | |
cd5f04f5 | 1200 | { |
8070259c A |
1201 | SEL sel = @selector(a); |
1202 | ||
1203 | testprintf("DWARF FOR %s%s\n", name, stret ? " (stret)" : ""); | |
1204 | ||
1205 | // We need 2 SELs of each alignment so we can generate hash collisions. | |
1206 | // sel_registerName() never returns those alignments because they | |
1207 | // differ from malloc's alignment. So we create lots of compiled-in | |
1208 | // SELs here and hope something fits. | |
1209 | SEL lotsOfSels[] = { | |
1210 | @selector(a1), @selector(a2), @selector(a3), @selector(a4), | |
1211 | @selector(a5), @selector(a6), @selector(a7), @selector(a8), | |
1212 | @selector(aa), @selector(ab), @selector(ac), @selector(ad), | |
1213 | @selector(ae), @selector(af), @selector(ag), @selector(ah), | |
1214 | @selector(A1), @selector(A2), @selector(A3), @selector(A4), | |
1215 | @selector(A5), @selector(A6), @selector(A7), @selector(A8), | |
1216 | @selector(AA), @selector(Ab), @selector(Ac), @selector(Ad), | |
1217 | @selector(Ae), @selector(Af), @selector(Ag), @selector(Ah), | |
1218 | @selector(bb1), @selector(bb2), @selector(bb3), @selector(bb4), | |
1219 | @selector(bb5), @selector(bb6), @selector(bb7), @selector(bb8), | |
1220 | @selector(bba), @selector(bbb), @selector(bbc), @selector(bbd), | |
1221 | @selector(bbe), @selector(bbf), @selector(bbg), @selector(bbh), | |
1222 | @selector(BB1), @selector(BB2), @selector(BB3), @selector(BB4), | |
1223 | @selector(BB5), @selector(BB6), @selector(BB7), @selector(BB8), | |
1224 | @selector(BBa), @selector(BBb), @selector(BBc), @selector(BBd), | |
1225 | @selector(BBe), @selector(BBf), @selector(BBg), @selector(BBh), | |
1226 | @selector(ccc1), @selector(ccc2), @selector(ccc3), @selector(ccc4), | |
1227 | @selector(ccc5), @selector(ccc6), @selector(ccc7), @selector(ccc8), | |
1228 | @selector(ccca), @selector(cccb), @selector(cccc), @selector(cccd), | |
1229 | @selector(ccce), @selector(cccf), @selector(cccg), @selector(ccch), | |
1230 | @selector(CCC1), @selector(CCC2), @selector(CCC3), @selector(CCC4), | |
1231 | @selector(CCC5), @selector(CCC6), @selector(CCC7), @selector(CCC8), | |
1232 | @selector(CCCa), @selector(CCCb), @selector(CCCc), @selector(CCCd), | |
1233 | @selector(CCCe), @selector(CCCf), @selector(CCCg), @selector(CCCh), | |
1234 | }; | |
1235 | #define ALIGNCOUNT 16 | |
1236 | SEL sels[ALIGNCOUNT][2] = {{0}}; | |
1237 | for (int align = 0; align < ALIGNCOUNT; align++) { | |
1238 | for (size_t i = 0; i < sizeof(lotsOfSels)/sizeof(lotsOfSels[0]); i++) { | |
1239 | if ((uintptr_t)(void*)lotsOfSels[i] % ALIGNCOUNT == align) { | |
1240 | if (sels[align][0]) { | |
1241 | sels[align][1] = lotsOfSels[i]; | |
1242 | } else { | |
1243 | sels[align][0] = lotsOfSels[i]; | |
1244 | } | |
1245 | } | |
1246 | } | |
1247 | if (!sels[align][0]) fail("no SEL with alignment %d", align); | |
1248 | if (!sels[align][1]) fail("only one SEL with alignment %d", align); | |
1249 | } | |
7af964d1 | 1250 | |
cd5f04f5 A |
1251 | void *fn = dlsym(RTLD_DEFAULT, name); |
1252 | testassert(fn); | |
7af964d1 | 1253 | |
cd5f04f5 | 1254 | // argument substitutions |
7af964d1 | 1255 | |
cd5f04f5 A |
1256 | void *sub_arg = (void*)objc_unretainedPointer(sub); |
1257 | void *tagged_arg = (void*)objc_unretainedPointer(tagged); | |
1258 | void *sel_arg = (void*)sel; | |
7af964d1 | 1259 | |
cd5f04f5 A |
1260 | struct objc_super sup_st = { sub, object_getClass(sub) }; |
1261 | struct objc_super tagged_sup_st = { tagged, object_getClass(tagged) }; | |
1262 | struct { void *imp; SEL sel; } message_ref = { fn, sel }; | |
1263 | ||
8070259c A |
1264 | Class cache_cls = object_getClass(sub); |
1265 | ||
cd5f04f5 A |
1266 | if (strstr(name, "Super")) { |
1267 | // super version - replace receiver with objc_super | |
8070259c A |
1268 | // clear caches of superclass |
1269 | cache_cls = class_getSuperclass(cache_cls); | |
cd5f04f5 A |
1270 | sub_arg = &sup_st; |
1271 | tagged_arg = &tagged_sup_st; | |
1272 | } | |
1273 | ||
1274 | if (strstr(name, "_fixup")) { | |
1275 | // fixup version - replace sel with message_ref | |
1276 | sel_arg = &message_ref; | |
1277 | } | |
1278 | ||
1279 | ||
8070259c | 1280 | uintptr_t *insnOffsets = getOffsets(fn, name, nil); |
cd5f04f5 | 1281 | uintptr_t offset; |
8070259c A |
1282 | int uncaughtCount = 0; |
1283 | for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) { | |
1284 | offset = insnOffsets[oo]; | |
cd5f04f5 A |
1285 | testprintf("OFFSET %lu\n", offset); |
1286 | ||
8070259c | 1287 | insn_t saved_insn = clobber(fn, offset); |
cd5f04f5 A |
1288 | caught = false; |
1289 | ||
1290 | // nil | |
1291 | if ((void*)objc_unretainedPointer(sub) == sub_arg) { | |
1292 | SELF = nil; | |
1293 | testprintf(" nil\n"); | |
8070259c A |
1294 | CALLIT(nil, sel_arg, sel, fn, stret); |
1295 | CALLIT(nil, sel_arg, sel, fn, stret); | |
cd5f04f5 A |
1296 | } |
1297 | ||
1298 | // uncached | |
1299 | SELF = sub; | |
1300 | testprintf(" uncached\n"); | |
8070259c A |
1301 | _objc_flush_caches(cache_cls); |
1302 | CALLIT(sub_arg, sel_arg, sel, fn, stret); | |
1303 | _objc_flush_caches(cache_cls); | |
1304 | CALLIT(sub_arg, sel_arg, sel, fn, stret); | |
cd5f04f5 A |
1305 | |
1306 | // cached | |
1307 | SELF = sub; | |
1308 | testprintf(" cached\n"); | |
8070259c A |
1309 | CALLIT(sub_arg, sel_arg, sel, fn, stret); |
1310 | CALLIT(sub_arg, sel_arg, sel, fn, stret); | |
cd5f04f5 A |
1311 | |
1312 | // uncached,tagged | |
1313 | SELF = tagged; | |
1314 | testprintf(" uncached,tagged\n"); | |
8070259c A |
1315 | _objc_flush_caches(cache_cls); |
1316 | CALLIT(tagged_arg, sel_arg, sel, fn, stret); | |
1317 | _objc_flush_caches(cache_cls); | |
1318 | CALLIT(tagged_arg, sel_arg, sel, fn, stret); | |
cd5f04f5 A |
1319 | |
1320 | // cached,tagged | |
1321 | SELF = tagged; | |
1322 | testprintf(" cached,tagged\n"); | |
8070259c A |
1323 | CALLIT(tagged_arg, sel_arg, sel, fn, stret); |
1324 | CALLIT(tagged_arg, sel_arg, sel, fn, stret); | |
1325 | ||
1326 | // multiple SEL alignments, collisions, wraps | |
1327 | SELF = sub; | |
1328 | for (int a = 0; a < ALIGNCOUNT; a++) { | |
1329 | testprintf(" cached, SEL alignment %d\n", a); | |
1330 | ||
1331 | // Count both up and down to be independent of | |
1332 | // implementation's cache scan direction | |
1333 | ||
1334 | _objc_flush_caches(cache_cls); | |
1335 | for (int x2 = 0; x2 < 1; x2++) { | |
1336 | for (int s = 0; s < 4; s++) { | |
1337 | int align = (a+s) % ALIGNCOUNT; | |
1338 | CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret); | |
1339 | CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret); | |
1340 | } | |
1341 | } | |
1342 | ||
1343 | _objc_flush_caches(cache_cls); | |
1344 | for (int x2 = 0; x2 < 1; x2++) { | |
1345 | for (int s = 0; s < 4; s++) { | |
1346 | int align = abs(a-s) % ALIGNCOUNT; | |
1347 | CALLIT(sub_arg, sels[align][0], sels[align][0], fn, stret); | |
1348 | CALLIT(sub_arg, sels[align][1], sels[align][1], fn, stret); | |
1349 | } | |
1350 | } | |
1351 | } | |
cd5f04f5 | 1352 | |
8070259c A |
1353 | unclobber(fn, offset, saved_insn); |
1354 | ||
1355 | // remember offsets that were caught by none of the above | |
1356 | if (caught) { | |
1357 | insnOffsets[oo] = 0; | |
1358 | } else { | |
1359 | uncaughtCount++; | |
1360 | testprintf("offset %s+%lu not caught (%d/%d)\n", | |
1361 | name, offset, uncaughtCount, uncaughtAllowed); | |
1362 | } | |
1363 | } | |
cd5f04f5 | 1364 | |
8070259c A |
1365 | // Complain if too many offsets went uncaught. |
1366 | // Acceptably-uncaught offsets include the corrupt-cache-error handler. | |
1367 | if (uncaughtCount != uncaughtAllowed) { | |
1368 | for (int oo = 0; insnOffsets[oo] != ~0UL; oo++) { | |
1369 | if (insnOffsets[oo]) { | |
1370 | fprintf(stderr, "BAD: offset %s+%lu not caught\n", | |
1371 | name, insnOffsets[oo]); | |
1372 | } | |
1373 | } | |
1374 | fail("wrong instructions not reached for %s (missed %d, expected %d)", | |
1375 | name, uncaughtCount, uncaughtAllowed); | |
cd5f04f5 | 1376 | } |
8070259c | 1377 | |
cd5f04f5 A |
1378 | free(insnOffsets); |
1379 | } | |
1380 | ||
8070259c A |
1381 | |
1382 | // TEST_DWARF | |
cd5f04f5 A |
1383 | #endif |
1384 | ||
1385 | ||
1386 | void test_basic(id receiver) | |
1387 | { | |
1388 | id idval; | |
1389 | long long llval; | |
1390 | struct stret stretval; | |
1391 | double fpval; | |
1392 | long double lfpval; | |
7af964d1 A |
1393 | |
1394 | // message uncached | |
1395 | // message uncached long long | |
1396 | // message uncached stret | |
1397 | // message uncached fpret | |
1398 | // message uncached fpret long double | |
8972963c | 1399 | // message uncached noarg (as above) |
7af964d1 A |
1400 | // message cached |
1401 | // message cached long long | |
1402 | // message cached stret | |
1403 | // message cached fpret | |
1404 | // message cached fpret long double | |
8972963c | 1405 | // message cached noarg (as above) |
7af964d1 | 1406 | // fixme verify that uncached lookup didn't happen the 2nd time? |
cd5f04f5 A |
1407 | SELF = receiver; |
1408 | for (int i = 0; i < 5; i++) { | |
8070259c | 1409 | testprintf("idret\n"); |
7af964d1 A |
1410 | state = 0; |
1411 | idval = nil; | |
cd5f04f5 | 1412 | idval = [receiver idret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
8972963c | 1413 | testassert(state == 101); |
7af964d1 A |
1414 | testassert(idval == ID_RESULT); |
1415 | ||
8070259c | 1416 | testprintf("llret\n"); |
7af964d1 | 1417 | llval = 0; |
cd5f04f5 | 1418 | llval = [receiver llret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
8972963c | 1419 | testassert(state == 102); |
7af964d1 A |
1420 | testassert(llval == LL_RESULT); |
1421 | ||
8070259c | 1422 | testprintf("stret\n"); |
7af964d1 | 1423 | stretval = zero; |
cd5f04f5 | 1424 | stretval = [receiver stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
8972963c | 1425 | testassert(state == 103); |
7af964d1 A |
1426 | testassert(stret_equal(stretval, STRET_RESULT)); |
1427 | ||
8070259c | 1428 | testprintf("fpret\n"); |
7af964d1 | 1429 | fpval = 0; |
cd5f04f5 | 1430 | fpval = [receiver fpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
8972963c | 1431 | testassert(state == 104); |
7af964d1 A |
1432 | testassert(fpval == FP_RESULT); |
1433 | ||
8070259c | 1434 | testprintf("lfpret\n"); |
7af964d1 | 1435 | lfpval = 0; |
cd5f04f5 | 1436 | lfpval = [receiver lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
8972963c | 1437 | testassert(state == 105); |
7af964d1 | 1438 | testassert(lfpval == LFP_RESULT); |
8972963c A |
1439 | |
1440 | #if __OBJC2__ | |
1441 | // explicitly call noarg messenger, even if compiler doesn't emit it | |
1442 | state = 0; | |
8070259c | 1443 | testprintf("idret noarg\n"); |
8972963c | 1444 | idval = nil; |
cd5f04f5 | 1445 | idval = ((typeof(idmsg0))objc_msgSend_noarg)(receiver, @selector(idret_noarg)); |
8972963c A |
1446 | testassert(state == 111); |
1447 | testassert(idval == ID_RESULT); | |
1448 | ||
1449 | llval = 0; | |
8070259c | 1450 | testprintf("llret noarg\n"); |
cd5f04f5 | 1451 | llval = ((typeof(llmsg0))objc_msgSend_noarg)(receiver, @selector(llret_noarg)); |
8972963c A |
1452 | testassert(state == 112); |
1453 | testassert(llval == LL_RESULT); | |
1454 | /* | |
cd5f04f5 | 1455 | no objc_msgSend_stret_noarg |
8972963c | 1456 | stretval = zero; |
cd5f04f5 A |
1457 | stretval = ((typeof(stretmsg0))objc_msgSend_stret_noarg)(receiver, @selector(stret_noarg)); |
1458 | stretval = [receiver stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; | |
8972963c A |
1459 | testassert(state == 113); |
1460 | testassert(stret_equal(stretval, STRET_RESULT)); | |
1461 | */ | |
1462 | # if !__i386__ | |
8070259c | 1463 | testprintf("fpret noarg\n"); |
8972963c | 1464 | fpval = 0; |
cd5f04f5 | 1465 | fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(receiver, @selector(fpret_noarg)); |
8972963c A |
1466 | testassert(state == 114); |
1467 | testassert(fpval == FP_RESULT); | |
1468 | # endif | |
1469 | # if !__i386__ && !__x86_64__ | |
8070259c | 1470 | testprintf("lfpret noarg\n"); |
8972963c | 1471 | lfpval = 0; |
cd5f04f5 | 1472 | lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(receiver, @selector(lfpret_noarg)); |
8972963c A |
1473 | testassert(state == 115); |
1474 | testassert(lfpval == LFP_RESULT); | |
1475 | # endif | |
1476 | #endif | |
7af964d1 | 1477 | } |
8070259c A |
1478 | |
1479 | testprintf("basic done\n"); | |
cd5f04f5 A |
1480 | } |
1481 | ||
1482 | int main() | |
1483 | { | |
1484 | PUSH_POOL { | |
1485 | int i; | |
1486 | ||
1487 | id idval; | |
1488 | long long llval; | |
1489 | struct stret stretval; | |
1490 | double fpval; | |
1491 | long double lfpval; | |
1492 | ||
7257e56c A |
1493 | #if __x86_64__ |
1494 | struct stret *stretptr; | |
1495 | #endif | |
1496 | ||
cd5f04f5 A |
1497 | uint64_t startTime; |
1498 | uint64_t totalTime; | |
1499 | uint64_t targetTime; | |
1500 | ||
1501 | Method idmethod; | |
1502 | Method llmethod; | |
1503 | Method stretmethod; | |
1504 | Method fpmethod; | |
1505 | Method lfpmethod; | |
1506 | ||
1507 | id (*idfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); | |
1508 | long long (*llfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); | |
1509 | struct stret (*stretfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); | |
1510 | double (*fpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); | |
1511 | long double (*lfpfn)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double); | |
1512 | ||
1513 | id (*idmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1514 | id (*idmsgsuper)(struct objc_super *, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1515 | long long (*llmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1516 | struct stret (*stretmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1517 | struct stret (*stretmsgsuper)(struct objc_super *, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1518 | double (*fpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1519 | long double (*lfpmsg)(id, SEL, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double) __attribute__((unused)); | |
1520 | ||
1521 | // get +initialize out of the way | |
1522 | [Sub class]; | |
7257e56c | 1523 | #if OBJC_HAVE_TAGGED_POINTERS |
cd5f04f5 A |
1524 | [TaggedSub class]; |
1525 | #endif | |
1526 | ||
1527 | ID_RESULT = [Super new]; | |
7af964d1 | 1528 | |
cd5f04f5 A |
1529 | Sub *sub = [Sub new]; |
1530 | Super *sup = [Super new]; | |
7257e56c A |
1531 | #if OBJC_HAVE_TAGGED_POINTERS |
1532 | TaggedSub *tagged = objc_unretainedObject(_objc_makeTaggedPointer(OBJC_TAG_7, 999)); | |
cd5f04f5 | 1533 | #endif |
7257e56c | 1534 | |
cd5f04f5 A |
1535 | // Basic cached and uncached dispatch. |
1536 | // Do this first before anything below caches stuff. | |
7257e56c | 1537 | testprintf("basic\n"); |
cd5f04f5 | 1538 | test_basic(sub); |
7257e56c A |
1539 | #if OBJC_HAVE_TAGGED_POINTERS |
1540 | testprintf("basic tagged\n"); | |
cd5f04f5 A |
1541 | test_basic(tagged); |
1542 | #endif | |
1543 | ||
1544 | idmethod = class_getInstanceMethod([Super class], @selector(idret::::::::::::::::::::::::::::)); | |
8972963c | 1545 | testassert(idmethod); |
cd5f04f5 | 1546 | llmethod = class_getInstanceMethod([Super class], @selector(llret::::::::::::::::::::::::::::)); |
8972963c | 1547 | testassert(llmethod); |
cd5f04f5 | 1548 | stretmethod = class_getInstanceMethod([Super class], @selector(stret::::::::::::::::::::::::::::)); |
8972963c | 1549 | testassert(stretmethod); |
cd5f04f5 | 1550 | fpmethod = class_getInstanceMethod([Super class], @selector(fpret::::::::::::::::::::::::::::)); |
8972963c | 1551 | testassert(fpmethod); |
cd5f04f5 | 1552 | lfpmethod = class_getInstanceMethod([Super class], @selector(lfpret::::::::::::::::::::::::::::)); |
8972963c A |
1553 | testassert(lfpmethod); |
1554 | ||
1555 | idfn = (id (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke; | |
1556 | llfn = (long long (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke; | |
1557 | stretfn = (struct stret (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke_stret; | |
1558 | fpfn = (double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke; | |
1559 | lfpfn = (long double (*)(id, Method, int, int, int, int, int, int, int, int, int, int, int, int, int, double, double, double, double, double, double, double, double, double, double, double, double, double, double, double)) method_invoke; | |
1560 | ||
7af964d1 A |
1561 | // cached message performance |
1562 | // catches failure to cache or (abi=2) failure to fixup (#5584187) | |
1563 | // fixme unless they all fail | |
1564 | // `.align 4` matches loop alignment to make -O0 work | |
8972963c | 1565 | // fill cache first |
7257e56c A |
1566 | testprintf("time checks\n"); |
1567 | ||
cd5f04f5 A |
1568 | SELF = sub; |
1569 | [sub voidret_nop]; | |
8070259c | 1570 | [sub voidret_nop2]; |
cd5f04f5 A |
1571 | [sub llret_nop]; |
1572 | [sub stret_nop]; | |
1573 | [sub fpret_nop]; | |
1574 | [sub lfpret_nop]; | |
1575 | [sub voidret_nop]; | |
8070259c | 1576 | [sub voidret_nop2]; |
cd5f04f5 A |
1577 | [sub llret_nop]; |
1578 | [sub stret_nop]; | |
1579 | [sub fpret_nop]; | |
1580 | [sub lfpret_nop]; | |
1581 | [sub voidret_nop]; | |
8070259c | 1582 | [sub voidret_nop2]; |
cd5f04f5 A |
1583 | [sub llret_nop]; |
1584 | [sub stret_nop]; | |
1585 | [sub fpret_nop]; | |
1586 | [sub lfpret_nop]; | |
8972963c A |
1587 | |
1588 | // Some of these times have high variance on some compilers. | |
1589 | // The errors we're trying to catch should be catastrophically slow, | |
1590 | // so the margins here are generous to avoid false failures. | |
1591 | ||
8070259c A |
1592 | // Use voidret because id return is too slow for perf test with ARC. |
1593 | ||
1594 | // Pick smallest of voidret_nop and voidret_nop2 time | |
1595 | // in the hopes that one of them didn't collide in the method cache. | |
1596 | ||
8972963c | 1597 | #define COUNT 1000000 |
8070259c | 1598 | |
7af964d1 | 1599 | startTime = mach_absolute_time(); |
8972963c | 1600 | ALIGN_(); |
7af964d1 | 1601 | for (i = 0; i < COUNT; i++) { |
8070259c | 1602 | [sub voidret_nop]; |
cd5f04f5 A |
1603 | } |
1604 | totalTime = mach_absolute_time() - startTime; | |
8070259c | 1605 | testprintf("time: voidret %llu\n", totalTime); |
cd5f04f5 A |
1606 | targetTime = totalTime; |
1607 | ||
1608 | startTime = mach_absolute_time(); | |
1609 | ALIGN_(); | |
1610 | for (i = 0; i < COUNT; i++) { | |
8070259c | 1611 | [sub voidret_nop2]; |
7af964d1 A |
1612 | } |
1613 | totalTime = mach_absolute_time() - startTime; | |
8070259c A |
1614 | testprintf("time: voidret2 %llu\n", totalTime); |
1615 | if (totalTime < targetTime) targetTime = totalTime; | |
7af964d1 | 1616 | |
7af964d1 | 1617 | startTime = mach_absolute_time(); |
8972963c | 1618 | ALIGN_(); |
7af964d1 | 1619 | for (i = 0; i < COUNT; i++) { |
cd5f04f5 | 1620 | [sub llret_nop]; |
7af964d1 A |
1621 | } |
1622 | totalTime = mach_absolute_time() - startTime; | |
cd5f04f5 | 1623 | timecheck("llret ", totalTime, targetTime * 0.7, targetTime * 2.0); |
8972963c | 1624 | |
7af964d1 | 1625 | startTime = mach_absolute_time(); |
8972963c | 1626 | ALIGN_(); |
7af964d1 | 1627 | for (i = 0; i < COUNT; i++) { |
cd5f04f5 | 1628 | [sub stret_nop]; |
7af964d1 A |
1629 | } |
1630 | totalTime = mach_absolute_time() - startTime; | |
cd5f04f5 | 1631 | timecheck("stret ", totalTime, targetTime * 0.7, targetTime * 5.0); |
7af964d1 | 1632 | |
7af964d1 | 1633 | startTime = mach_absolute_time(); |
8972963c | 1634 | ALIGN_(); |
7af964d1 | 1635 | for (i = 0; i < COUNT; i++) { |
cd5f04f5 | 1636 | [sub fpret_nop]; |
7af964d1 A |
1637 | } |
1638 | totalTime = mach_absolute_time() - startTime; | |
cd5f04f5 | 1639 | timecheck("fpret ", totalTime, targetTime * 0.7, targetTime * 4.0); |
7af964d1 | 1640 | |
7af964d1 | 1641 | startTime = mach_absolute_time(); |
8972963c | 1642 | ALIGN_(); |
7af964d1 | 1643 | for (i = 0; i < COUNT; i++) { |
cd5f04f5 | 1644 | [sub lfpret_nop]; |
7af964d1 A |
1645 | } |
1646 | totalTime = mach_absolute_time() - startTime; | |
cd5f04f5 | 1647 | timecheck("lfpret", totalTime, targetTime * 0.7, targetTime * 4.0); |
8070259c A |
1648 | |
1649 | #if __arm64__ | |
1650 | // Removing this testwarn(), or changing voidret_nop to nop;ret, | |
1651 | // changes the voidret_nop and stret_nop times above by a factor of 2. | |
1652 | testwarn("rdar://13896922 nop;ret is faster than ret?"); | |
1653 | #endif | |
1654 | ||
7af964d1 A |
1655 | #undef COUNT |
1656 | ||
1657 | // method_invoke | |
1658 | // method_invoke long long | |
1659 | // method_invoke_stret stret | |
1660 | // method_invoke_stret fpret | |
1661 | // method_invoke fpret long double | |
7257e56c A |
1662 | testprintf("method_invoke\n"); |
1663 | ||
cd5f04f5 | 1664 | SELF = sup; |
7af964d1 A |
1665 | |
1666 | state = 0; | |
1667 | idval = nil; | |
cd5f04f5 | 1668 | idval = (*idfn)(sup, idmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
7af964d1 A |
1669 | testassert(state == 1); |
1670 | testassert(idval == ID_RESULT); | |
1671 | ||
1672 | llval = 0; | |
cd5f04f5 | 1673 | llval = (*llfn)(sup, llmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
7af964d1 A |
1674 | testassert(state == 2); |
1675 | testassert(llval == LL_RESULT); | |
1676 | ||
1677 | stretval = zero; | |
cd5f04f5 | 1678 | stretval = (*stretfn)(sup, stretmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
7af964d1 A |
1679 | testassert(state == 3); |
1680 | testassert(stret_equal(stretval, STRET_RESULT)); | |
1681 | ||
1682 | fpval = 0; | |
cd5f04f5 | 1683 | fpval = (*fpfn)(sup, fpmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
7af964d1 A |
1684 | testassert(state == 4); |
1685 | testassert(fpval == FP_RESULT); | |
1686 | ||
1687 | lfpval = 0; | |
cd5f04f5 | 1688 | lfpval = (*lfpfn)(sup, lfpmethod, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
7af964d1 A |
1689 | testassert(state == 5); |
1690 | testassert(lfpval == LFP_RESULT); | |
1691 | ||
1692 | ||
1693 | // message to nil | |
1694 | // message to nil long long | |
1695 | // message to nil stret | |
1696 | // message to nil fpret | |
1697 | // message to nil fpret long double | |
7257e56c A |
1698 | // Use NIL_RECEIVER to avoid compiler optimizations. |
1699 | testprintf("message to nil\n"); | |
1700 | ||
7af964d1 A |
1701 | state = 0; |
1702 | idval = ID_RESULT; | |
7257e56c | 1703 | idval = [(id)NIL_RECEIVER idret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
7af964d1 A |
1704 | testassert(state == 0); |
1705 | testassert(idval == nil); | |
1706 | ||
1707 | state = 0; | |
1708 | llval = LL_RESULT; | |
7257e56c | 1709 | llval = [(id)NIL_RECEIVER llret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
7af964d1 A |
1710 | testassert(state == 0); |
1711 | testassert(llval == 0LL); | |
1712 | ||
1713 | state = 0; | |
1714 | stretval = zero; | |
7257e56c | 1715 | stretval = [(id)NIL_RECEIVER stret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
7af964d1 | 1716 | testassert(state == 0); |
cd5f04f5 A |
1717 | #if __clang__ |
1718 | testassert(0 == memcmp(&stretval, &zero, sizeof(stretval))); | |
1719 | #else | |
7af964d1 | 1720 | // no stret result guarantee |
cd5f04f5 | 1721 | #endif |
7257e56c A |
1722 | |
1723 | #if __x86_64__ | |
1724 | // check stret return register | |
1725 | state = 0; | |
1726 | stretval = zero; | |
1727 | stretptr = ((struct stret *(*)(struct stret *, id, SEL))objc_msgSend_stret) | |
1728 | (&stretval, nil, @selector(stret_nop)); | |
1729 | testassert(stretptr == &stretval); | |
1730 | testassert(state == 0); | |
1731 | // no stret result guarantee for hand-written calls, even with clang | |
1732 | #endif | |
1733 | ||
8070259c A |
1734 | #if __i386__ |
1735 | // check struct-return address stack pop | |
1736 | for (int i = 0; i < 10000000; i++) { | |
1737 | state = 0; | |
1738 | ((struct stret (*)(id, SEL))objc_msgSend_stret) | |
1739 | (nil, @selector(stret_nop)); | |
1740 | } | |
1741 | #endif | |
1742 | ||
7af964d1 A |
1743 | state = 0; |
1744 | fpval = FP_RESULT; | |
7257e56c | 1745 | fpval = [(id)NIL_RECEIVER fpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
7af964d1 A |
1746 | testassert(state == 0); |
1747 | testassert(fpval == 0.0); | |
1748 | ||
1749 | state = 0; | |
1750 | lfpval = LFP_RESULT; | |
7257e56c | 1751 | lfpval = [(id)NIL_RECEIVER lfpret :1:2:3:4:5:6:7:8:9:10:11:12:13:1.0:2.0:3.0:4.0:5.0:6.0:7.0:8.0:9.0:10.0:11.0:12.0:13.0:14.0:15.0]; |
7af964d1 A |
1752 | testassert(state == 0); |
1753 | testassert(lfpval == 0.0); | |
8972963c | 1754 | |
8070259c A |
1755 | // message to nil, different struct types |
1756 | // This verifies that ordinary objc_msgSend() erases enough registers | |
1757 | // for structs that return in registers. | |
1758 | #define TEST_NIL_STRUCT(i,n) \ | |
1759 | do { \ | |
1760 | struct stret_##i##n z; \ | |
1761 | bzero(&z, sizeof(z)); \ | |
1762 | [Super stret_i##n##_nonzero]; \ | |
1763 | [Super stret_d##n##_nonzero]; \ | |
1764 | struct stret_##i##n val = [(id)NIL_RECEIVER stret_##i##n##_zero]; \ | |
1765 | testassert(0 == memcmp(&z, &val, sizeof(val))); \ | |
1766 | } while (0) | |
1767 | ||
1768 | TEST_NIL_STRUCT(i,1); | |
1769 | TEST_NIL_STRUCT(i,2); | |
1770 | TEST_NIL_STRUCT(i,3); | |
1771 | TEST_NIL_STRUCT(i,4); | |
1772 | TEST_NIL_STRUCT(i,5); | |
1773 | TEST_NIL_STRUCT(i,6); | |
1774 | TEST_NIL_STRUCT(i,7); | |
1775 | TEST_NIL_STRUCT(i,8); | |
1776 | TEST_NIL_STRUCT(i,9); | |
1777 | ||
1778 | #if __i386__ | |
1779 | testwarn("rdar://16267205 i386 struct{float} and struct{double}"); | |
1780 | #else | |
1781 | TEST_NIL_STRUCT(d,1); | |
1782 | #endif | |
1783 | TEST_NIL_STRUCT(d,2); | |
1784 | TEST_NIL_STRUCT(d,3); | |
1785 | TEST_NIL_STRUCT(d,4); | |
1786 | TEST_NIL_STRUCT(d,5); | |
1787 | TEST_NIL_STRUCT(d,6); | |
1788 | TEST_NIL_STRUCT(d,7); | |
1789 | TEST_NIL_STRUCT(d,8); | |
1790 | TEST_NIL_STRUCT(d,9); | |
1791 | ||
1792 | ||
cd5f04f5 | 1793 | #if __OBJC2__ |
8972963c A |
1794 | // message to nil noarg |
1795 | // explicitly call noarg messenger, even if compiler doesn't emit it | |
1796 | state = 0; | |
1797 | idval = ID_RESULT; | |
1798 | idval = ((typeof(idmsg0))objc_msgSend_noarg)(nil, @selector(idret_noarg)); | |
1799 | testassert(state == 0); | |
1800 | testassert(idval == nil); | |
7af964d1 | 1801 | |
8972963c A |
1802 | state = 0; |
1803 | llval = LL_RESULT; | |
1804 | llval = ((typeof(llmsg0))objc_msgSend_noarg)(nil, @selector(llret_noarg)); | |
1805 | testassert(state == 0); | |
1806 | testassert(llval == 0LL); | |
cd5f04f5 A |
1807 | |
1808 | // no stret_noarg messenger | |
1809 | ||
8972963c A |
1810 | # if !__i386__ |
1811 | state = 0; | |
1812 | fpval = FP_RESULT; | |
1813 | fpval = ((typeof(fpmsg0))objc_msgSend_noarg)(nil, @selector(fpret_noarg)); | |
1814 | testassert(state == 0); | |
1815 | testassert(fpval == 0.0); | |
1816 | # endif | |
1817 | # if !__i386__ && !__x86_64__ | |
1818 | state = 0; | |
1819 | lfpval = LFP_RESULT; | |
1820 | lfpval = ((typeof(lfpmsg0))objc_msgSend_noarg)(nil, @selector(lfpret_noarg)); | |
1821 | testassert(state == 0); | |
1822 | testassert(lfpval == 0.0); | |
1823 | # endif | |
1824 | #endif | |
7257e56c | 1825 | |
7af964d1 | 1826 | |
8972963c A |
1827 | #if __OBJC2__ |
1828 | // rdar://8271364 objc_msgSendSuper2 must not change objc_super | |
7257e56c | 1829 | testprintf("super struct\n"); |
cd5f04f5 A |
1830 | struct objc_super sup_st = { |
1831 | sub, | |
1832 | object_getClass(sub), | |
8972963c A |
1833 | }; |
1834 | ||
cd5f04f5 A |
1835 | SELF = sub; |
1836 | ||
8972963c A |
1837 | state = 100; |
1838 | idval = nil; | |
cd5f04f5 | 1839 | idval = ((id(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2) (&sup_st, @selector(idret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); |
8972963c A |
1840 | testassert(state == 1); |
1841 | testassert(idval == ID_RESULT); | |
cd5f04f5 A |
1842 | testassert(sup_st.receiver == sub); |
1843 | testassert(sup_st.super_class == object_getClass(sub)); | |
8972963c A |
1844 | |
1845 | state = 100; | |
1846 | stretval = zero; | |
cd5f04f5 | 1847 | stretval = ((struct stret(*)(struct objc_super *, SEL, int,int,int,int,int,int,int,int,int,int,int,int,int, double,double,double,double,double,double,double,double,double,double,double,double,double,double,double))objc_msgSendSuper2_stret) (&sup_st, @selector(stret::::::::::::::::::::::::::::), 1,2,3,4,5,6,7,8,9,10,11,12,13, 1.0,2.0,3.0,4.0,5.0,6.0,7.0,8.0,9.0,10.0,11.0,12.0,13.0,14.0,15.0); |
8972963c A |
1848 | testassert(state == 3); |
1849 | testassert(stret_equal(stretval, STRET_RESULT)); | |
cd5f04f5 A |
1850 | testassert(sup_st.receiver == sub); |
1851 | testassert(sup_st.super_class == object_getClass(sub)); | |
8972963c A |
1852 | #endif |
1853 | ||
8070259c | 1854 | #if __OBJC2__ && !__arm64__ |
8972963c | 1855 | // Debug messengers. |
7257e56c A |
1856 | testprintf("debug messengers\n"); |
1857 | ||
8972963c A |
1858 | state = 0; |
1859 | idmsg = (typeof(idmsg))objc_msgSend_debug; | |
1860 | idval = nil; | |
cd5f04f5 | 1861 | idval = (*idmsg)(sub, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1862 | testassert(state == 101); |
1863 | testassert(idval == ID_RESULT); | |
1864 | ||
1865 | state = 0; | |
1866 | llmsg = (typeof(llmsg))objc_msgSend_debug; | |
1867 | llval = 0; | |
cd5f04f5 | 1868 | llval = (*llmsg)(sub, @selector(llret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1869 | testassert(state == 102); |
1870 | testassert(llval == LL_RESULT); | |
1871 | ||
1872 | state = 0; | |
1873 | stretmsg = (typeof(stretmsg))objc_msgSend_stret_debug; | |
1874 | stretval = zero; | |
cd5f04f5 | 1875 | stretval = (*stretmsg)(sub, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1876 | testassert(state == 103); |
1877 | testassert(stret_equal(stretval, STRET_RESULT)); | |
1878 | ||
1879 | state = 100; | |
cd5f04f5 A |
1880 | sup_st.receiver = sub; |
1881 | sup_st.super_class = object_getClass(sub); | |
1882 | idmsgsuper = (typeof(idmsgsuper))objc_msgSendSuper2_debug; | |
8972963c | 1883 | idval = nil; |
cd5f04f5 | 1884 | idval = (*idmsgsuper)(&sup_st, @selector(idret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1885 | testassert(state == 1); |
1886 | testassert(idval == ID_RESULT); | |
1887 | ||
1888 | state = 100; | |
cd5f04f5 A |
1889 | sup_st.receiver = sub; |
1890 | sup_st.super_class = object_getClass(sub); | |
1891 | stretmsgsuper = (typeof(stretmsgsuper))objc_msgSendSuper2_stret_debug; | |
8972963c | 1892 | stretval = zero; |
cd5f04f5 | 1893 | stretval = (*stretmsgsuper)(&sup_st, @selector(stret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1894 | testassert(state == 3); |
1895 | testassert(stret_equal(stretval, STRET_RESULT)); | |
1896 | ||
1897 | #if __i386__ | |
1898 | state = 0; | |
1899 | fpmsg = (typeof(fpmsg))objc_msgSend_fpret_debug; | |
1900 | fpval = 0; | |
cd5f04f5 | 1901 | fpval = (*fpmsg)(sub, @selector(fpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1902 | testassert(state == 104); |
1903 | testassert(fpval == FP_RESULT); | |
1904 | #endif | |
1905 | #if __x86_64__ | |
1906 | state = 0; | |
1907 | lfpmsg = (typeof(lfpmsg))objc_msgSend_fpret_debug; | |
1908 | lfpval = 0; | |
cd5f04f5 | 1909 | lfpval = (*lfpmsg)(sub, @selector(lfpret::::::::::::::::::::::::::::), 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0, 13.0, 14.0, 15.0); |
8972963c A |
1910 | testassert(state == 105); |
1911 | testassert(lfpval == LFP_RESULT); | |
1912 | ||
1913 | // fixme fp2ret | |
1914 | #endif | |
1915 | ||
1916 | // debug messengers | |
1917 | #endif | |
1918 | ||
cd5f04f5 | 1919 | |
8070259c A |
1920 | #if !TEST_DWARF |
1921 | testwarn("no unwind tables in this configuration"); | |
1922 | #else | |
cd5f04f5 | 1923 | // DWARF unwind tables |
7257e56c | 1924 | testprintf("unwind tables\n"); |
cd5f04f5 A |
1925 | |
1926 | // install exception handler | |
1927 | struct sigaction act; | |
1928 | act.sa_sigaction = sigtrap; | |
1929 | act.sa_mask = 0; | |
1930 | act.sa_flags = SA_SIGINFO; | |
1931 | sigaction(SIGTRAP, &act, NULL); | |
1932 | ||
8070259c A |
1933 | SubDW *dw = [[SubDW alloc] init]; |
1934 | ||
1935 | objc_setForwardHandler((void*)test_dw_forward, (void*)test_dw_forward_stret); | |
1936 | ||
1937 | # if __x86_64__ | |
1938 | test_dw("objc_msgSend", dw, tagged, false, 2); | |
1939 | test_dw("objc_msgSend_stret", dw, tagged, true, 4); | |
1940 | test_dw("objc_msgSend_fpret", dw, tagged, false, 2); | |
1941 | test_dw("objc_msgSend_fp2ret", dw, tagged, false, 2); | |
1942 | test_dw("objc_msgSendSuper", dw, tagged, false, 2); | |
1943 | test_dw("objc_msgSendSuper2", dw, tagged, false, 2); | |
1944 | test_dw("objc_msgSendSuper_stret", dw, tagged, true, 4); | |
1945 | test_dw("objc_msgSendSuper2_stret", dw, tagged, true, 4); | |
1946 | # elif __i386__ | |
1947 | test_dw("objc_msgSend", dw, dw, false, 10); | |
1948 | test_dw("objc_msgSend_stret", dw, dw, true, 10); | |
1949 | test_dw("objc_msgSend_fpret", dw, dw, false, 10); | |
1950 | test_dw("objc_msgSendSuper", dw, dw, false, 10); | |
1951 | test_dw("objc_msgSendSuper2", dw, dw, false, 10); | |
1952 | test_dw("objc_msgSendSuper_stret", dw, dw, true, 10); | |
1953 | test_dw("objc_msgSendSuper2_stret", dw, dw, true, 10); | |
1954 | # elif __arm64__ | |
1955 | test_dw("objc_msgSend", dw, tagged, false, 2); | |
1956 | test_dw("objc_msgSendSuper", dw, tagged, false, 2); | |
1957 | test_dw("objc_msgSendSuper2", dw, tagged, false, 2); | |
1958 | # else | |
1959 | # error unknown architecture | |
1960 | # endif | |
cd5f04f5 A |
1961 | |
1962 | // DWARF unwind tables | |
1963 | #endif | |
8070259c | 1964 | |
cd5f04f5 | 1965 | } POP_POOL; |
7af964d1 A |
1966 | succeed(__FILE__); |
1967 | } | |
8972963c A |
1968 | |
1969 | #endif |