]>
Commit | Line | Data |
---|---|---|
63e5e3e0 | 1 | /* ----------------------------------------------------------------------- |
34fa7690 AG |
2 | ffi.c - Copyright (c) 2011 Timothy Wall |
3 | Copyright (c) 2011 Plausible Labs Cooperative, Inc. | |
4 | Copyright (c) 2011 Anthony Green | |
5 | Copyright (c) 2011 Free Software Foundation | |
6 | Copyright (c) 1998, 2008, 2011 Red Hat, Inc. | |
b1760f7f RH |
7 | |
8 | ARM Foreign Function Interface | |
63e5e3e0 | 9 | |
63e5e3e0 AG |
10 | Permission is hereby granted, free of charge, to any person obtaining |
11 | a copy of this software and associated documentation files (the | |
12 | ``Software''), to deal in the Software without restriction, including | |
13 | without limitation the rights to use, copy, modify, merge, publish, | |
14 | distribute, sublicense, and/or sell copies of the Software, and to | |
15 | permit persons to whom the Software is furnished to do so, subject to | |
16 | the following conditions: | |
17 | ||
18 | The above copyright notice and this permission notice shall be included | |
19 | in all copies or substantial portions of the Software. | |
20 | ||
5f933ef0 AH |
21 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, |
22 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
23 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
24 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
25 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
26 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
27 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
28 | DEALINGS IN THE SOFTWARE. | |
63e5e3e0 AG |
29 | ----------------------------------------------------------------------- */ |
30 | ||
92456a4e L |
31 | #if defined(__arm__) || defined(_M_ARM) |
32 | #include <fficonfig.h> | |
63e5e3e0 AG |
33 | #include <ffi.h> |
34 | #include <ffi_common.h> | |
92456a4e | 35 | #include <stdint.h> |
63e5e3e0 | 36 | #include <stdlib.h> |
92456a4e | 37 | #include <tramp.h> |
b1760f7f | 38 | #include "internal.h" |
63e5e3e0 | 39 | |
92456a4e L |
40 | #if defined(_WIN32) |
41 | #define WIN32_LEAN_AND_MEAN | |
42 | #include <windows.h> | |
43 | #endif | |
44 | ||
45 | #if FFI_EXEC_TRAMPOLINE_TABLE | |
46 | ||
47 | #ifdef __MACH__ | |
48 | #include <mach/machine/vm_param.h> | |
49 | #endif | |
50 | ||
51 | #else | |
52 | #ifndef _WIN32 | |
53 | extern unsigned int ffi_arm_trampoline[2] FFI_HIDDEN; | |
54 | #else | |
55 | // Declare this as an array of char, instead of array of int, | |
56 | // otherwise Clang optimizes out the "& 0xFFFFFFFE" for clearing | |
57 | // the thumb bit. | |
58 | extern unsigned char ffi_arm_trampoline[12] FFI_HIDDEN; | |
59 | #endif | |
60 | #endif | |
61 | ||
62 | #if defined(__FreeBSD__) && defined(__arm__) | |
63 | #include <sys/types.h> | |
64 | #include <machine/sysarch.h> | |
65 | #endif | |
66 | ||
46e0720d | 67 | /* Forward declares. */ |
b1760f7f | 68 | static int vfp_type_p (const ffi_type *); |
46e0720d | 69 | static void layout_vfp_args (ffi_cif *); |
63e5e3e0 | 70 | |
b1760f7f RH |
71 | static void * |
72 | ffi_align (ffi_type *ty, void *p) | |
73 | { | |
74 | /* Align if necessary */ | |
75 | size_t alignment; | |
76 | #ifdef _WIN32_WCE | |
77 | alignment = 4; | |
78 | #else | |
79 | alignment = ty->alignment; | |
80 | if (alignment < 4) | |
81 | alignment = 4; | |
82 | #endif | |
92456a4e | 83 | return (void *) FFI_ALIGN (p, alignment); |
b1760f7f RH |
84 | } |
85 | ||
86 | static size_t | |
87 | ffi_put_arg (ffi_type *ty, void *src, void *dst) | |
88 | { | |
89 | size_t z = ty->size; | |
90 | ||
91 | switch (ty->type) | |
92 | { | |
93 | case FFI_TYPE_SINT8: | |
94 | *(UINT32 *)dst = *(SINT8 *)src; | |
95 | break; | |
96 | case FFI_TYPE_UINT8: | |
97 | *(UINT32 *)dst = *(UINT8 *)src; | |
98 | break; | |
99 | case FFI_TYPE_SINT16: | |
100 | *(UINT32 *)dst = *(SINT16 *)src; | |
101 | break; | |
102 | case FFI_TYPE_UINT16: | |
103 | *(UINT32 *)dst = *(UINT16 *)src; | |
104 | break; | |
105 | ||
106 | case FFI_TYPE_INT: | |
107 | case FFI_TYPE_SINT32: | |
108 | case FFI_TYPE_UINT32: | |
109 | case FFI_TYPE_POINTER: | |
92456a4e | 110 | #ifndef _WIN32 |
b1760f7f | 111 | case FFI_TYPE_FLOAT: |
92456a4e | 112 | #endif |
b1760f7f RH |
113 | *(UINT32 *)dst = *(UINT32 *)src; |
114 | break; | |
115 | ||
92456a4e L |
116 | #ifdef _WIN32 |
117 | // casting a float* to a UINT32* doesn't work on Windows | |
118 | case FFI_TYPE_FLOAT: | |
119 | *(uintptr_t *)dst = 0; | |
120 | *(float *)dst = *(float *)src; | |
121 | break; | |
122 | #endif | |
123 | ||
b1760f7f RH |
124 | case FFI_TYPE_SINT64: |
125 | case FFI_TYPE_UINT64: | |
126 | case FFI_TYPE_DOUBLE: | |
127 | *(UINT64 *)dst = *(UINT64 *)src; | |
128 | break; | |
129 | ||
130 | case FFI_TYPE_STRUCT: | |
131 | case FFI_TYPE_COMPLEX: | |
132 | memcpy (dst, src, z); | |
133 | break; | |
134 | ||
135 | default: | |
136 | abort(); | |
137 | } | |
138 | ||
92456a4e | 139 | return FFI_ALIGN (z, 4); |
b1760f7f RH |
140 | } |
141 | ||
142 | /* ffi_prep_args is called once stack space has been allocated | |
143 | for the function's arguments. | |
144 | ||
46e0720d CLT |
145 | The vfp_space parameter is the load area for VFP regs, the return |
146 | value is cif->vfp_used (word bitset of VFP regs used for passing | |
147 | arguments). These are only used for the VFP hard-float ABI. | |
148 | */ | |
b1760f7f RH |
149 | static void |
150 | ffi_prep_args_SYSV (ffi_cif *cif, int flags, void *rvalue, | |
151 | void **avalue, char *argp) | |
63e5e3e0 | 152 | { |
b1760f7f RH |
153 | ffi_type **arg_types = cif->arg_types; |
154 | int i, n; | |
63e5e3e0 | 155 | |
b1760f7f RH |
156 | if (flags == ARM_TYPE_STRUCT) |
157 | { | |
158 | *(void **) argp = rvalue; | |
159 | argp += 4; | |
160 | } | |
63e5e3e0 | 161 | |
b1760f7f RH |
162 | for (i = 0, n = cif->nargs; i < n; i++) |
163 | { | |
164 | ffi_type *ty = arg_types[i]; | |
165 | argp = ffi_align (ty, argp); | |
166 | argp += ffi_put_arg (ty, avalue[i], argp); | |
167 | } | |
168 | } | |
63e5e3e0 | 169 | |
b1760f7f RH |
170 | static void |
171 | ffi_prep_args_VFP (ffi_cif *cif, int flags, void *rvalue, | |
172 | void **avalue, char *stack, char *vfp_space) | |
173 | { | |
174 | ffi_type **arg_types = cif->arg_types; | |
175 | int i, n, vi = 0; | |
176 | char *argp, *regp, *eo_regp; | |
177 | char stack_used = 0; | |
178 | char done_with_regs = 0; | |
179 | ||
180 | /* The first 4 words on the stack are used for values | |
181 | passed in core registers. */ | |
182 | regp = stack; | |
183 | eo_regp = argp = regp + 16; | |
184 | ||
185 | /* If the function returns an FFI_TYPE_STRUCT in memory, | |
186 | that address is passed in r0 to the function. */ | |
187 | if (flags == ARM_TYPE_STRUCT) | |
188 | { | |
189 | *(void **) regp = rvalue; | |
190 | regp += 4; | |
191 | } | |
63e5e3e0 | 192 | |
b1760f7f | 193 | for (i = 0, n = cif->nargs; i < n; i++) |
63e5e3e0 | 194 | { |
b1760f7f RH |
195 | ffi_type *ty = arg_types[i]; |
196 | void *a = avalue[i]; | |
197 | int is_vfp_type = vfp_type_p (ty); | |
63e5e3e0 | 198 | |
46e0720d | 199 | /* Allocated in VFP registers. */ |
b1760f7f | 200 | if (vi < cif->vfp_nargs && is_vfp_type) |
46e0720d | 201 | { |
b1760f7f RH |
202 | char *vfp_slot = vfp_space + cif->vfp_args[vi++] * 4; |
203 | ffi_put_arg (ty, a, vfp_slot); | |
46e0720d CLT |
204 | continue; |
205 | } | |
b1760f7f RH |
206 | /* Try allocating in core registers. */ |
207 | else if (!done_with_regs && !is_vfp_type) | |
208 | { | |
209 | char *tregp = ffi_align (ty, regp); | |
210 | size_t size = ty->size; | |
211 | size = (size < 4) ? 4 : size; // pad | |
212 | /* Check if there is space left in the aligned register | |
213 | area to place the argument. */ | |
214 | if (tregp + size <= eo_regp) | |
63e5e3e0 | 215 | { |
b1760f7f RH |
216 | regp = tregp + ffi_put_arg (ty, a, tregp); |
217 | done_with_regs = (regp == argp); | |
218 | // ensure we did not write into the stack area | |
219 | FFI_ASSERT (regp <= argp); | |
220 | continue; | |
63e5e3e0 | 221 | } |
b1760f7f RH |
222 | /* In case there are no arguments in the stack area yet, |
223 | the argument is passed in the remaining core registers | |
224 | and on the stack. */ | |
225 | else if (!stack_used) | |
63e5e3e0 | 226 | { |
b1760f7f RH |
227 | stack_used = 1; |
228 | done_with_regs = 1; | |
229 | argp = tregp + ffi_put_arg (ty, a, tregp); | |
230 | FFI_ASSERT (eo_regp < argp); | |
231 | continue; | |
63e5e3e0 | 232 | } |
b1760f7f RH |
233 | } |
234 | /* Base case, arguments are passed on the stack */ | |
235 | stack_used = 1; | |
236 | argp = ffi_align (ty, argp); | |
237 | argp += ffi_put_arg (ty, a, argp); | |
63e5e3e0 | 238 | } |
63e5e3e0 AG |
239 | } |
240 | ||
241 | /* Perform machine dependent cif processing */ | |
92456a4e | 242 | ffi_status FFI_HIDDEN |
b1760f7f | 243 | ffi_prep_cif_machdep (ffi_cif *cif) |
63e5e3e0 | 244 | { |
b1760f7f RH |
245 | int flags = 0, cabi = cif->abi; |
246 | size_t bytes = cif->bytes; | |
247 | ||
248 | /* Map out the register placements of VFP register args. The VFP | |
249 | hard-float calling conventions are slightly more sophisticated | |
250 | than the base calling conventions, so we do it here instead of | |
251 | in ffi_prep_args(). */ | |
252 | if (cabi == FFI_VFP) | |
253 | layout_vfp_args (cif); | |
f20459f1 | 254 | |
63e5e3e0 AG |
255 | /* Set the return type flag */ |
256 | switch (cif->rtype->type) | |
257 | { | |
258 | case FFI_TYPE_VOID: | |
b1760f7f RH |
259 | flags = ARM_TYPE_VOID; |
260 | break; | |
261 | ||
262 | case FFI_TYPE_INT: | |
263 | case FFI_TYPE_UINT8: | |
264 | case FFI_TYPE_SINT8: | |
265 | case FFI_TYPE_UINT16: | |
266 | case FFI_TYPE_SINT16: | |
267 | case FFI_TYPE_UINT32: | |
268 | case FFI_TYPE_SINT32: | |
269 | case FFI_TYPE_POINTER: | |
270 | flags = ARM_TYPE_INT; | |
63e5e3e0 AG |
271 | break; |
272 | ||
f20459f1 RE |
273 | case FFI_TYPE_SINT64: |
274 | case FFI_TYPE_UINT64: | |
b1760f7f RH |
275 | flags = ARM_TYPE_INT64; |
276 | break; | |
277 | ||
278 | case FFI_TYPE_FLOAT: | |
279 | flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_S : ARM_TYPE_INT); | |
280 | break; | |
281 | case FFI_TYPE_DOUBLE: | |
282 | flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_D : ARM_TYPE_INT64); | |
f20459f1 RE |
283 | break; |
284 | ||
cb642590 | 285 | case FFI_TYPE_STRUCT: |
b1760f7f RH |
286 | case FFI_TYPE_COMPLEX: |
287 | if (cabi == FFI_VFP) | |
46e0720d | 288 | { |
b1760f7f RH |
289 | int h = vfp_type_p (cif->rtype); |
290 | ||
291 | flags = ARM_TYPE_VFP_N; | |
292 | if (h == 0x100 + FFI_TYPE_FLOAT) | |
293 | flags = ARM_TYPE_VFP_S; | |
294 | if (h == 0x100 + FFI_TYPE_DOUBLE) | |
295 | flags = ARM_TYPE_VFP_D; | |
296 | if (h != 0) | |
297 | break; | |
46e0720d | 298 | } |
b1760f7f RH |
299 | |
300 | /* A Composite Type not larger than 4 bytes is returned in r0. | |
301 | A Composite Type larger than 4 bytes, or whose size cannot | |
302 | be determined statically ... is stored in memory at an | |
303 | address passed [in r0]. */ | |
304 | if (cif->rtype->size <= 4) | |
305 | flags = ARM_TYPE_INT; | |
cb642590 | 306 | else |
b1760f7f RH |
307 | { |
308 | flags = ARM_TYPE_STRUCT; | |
309 | bytes += 4; | |
310 | } | |
cb642590 AH |
311 | break; |
312 | ||
63e5e3e0 | 313 | default: |
b1760f7f | 314 | abort(); |
63e5e3e0 AG |
315 | } |
316 | ||
b1760f7f RH |
317 | /* Round the stack up to a multiple of 8 bytes. This isn't needed |
318 | everywhere, but it is on some platforms, and it doesn't harm anything | |
319 | when it isn't needed. */ | |
92456a4e | 320 | bytes = FFI_ALIGN (bytes, 8); |
b1760f7f RH |
321 | |
322 | /* Minimum stack space is the 4 register arguments that we pop. */ | |
323 | if (bytes < 4*4) | |
324 | bytes = 4*4; | |
325 | ||
326 | cif->bytes = bytes; | |
327 | cif->flags = flags; | |
46e0720d | 328 | |
63e5e3e0 AG |
329 | return FFI_OK; |
330 | } | |
331 | ||
34fa7690 | 332 | /* Perform machine dependent cif processing for variadic calls */ |
92456a4e | 333 | ffi_status FFI_HIDDEN |
b1760f7f RH |
334 | ffi_prep_cif_machdep_var (ffi_cif * cif, |
335 | unsigned int nfixedargs, unsigned int ntotalargs) | |
34fa7690 AG |
336 | { |
337 | /* VFP variadic calls actually use the SYSV ABI */ | |
338 | if (cif->abi == FFI_VFP) | |
b1760f7f | 339 | cif->abi = FFI_SYSV; |
34fa7690 | 340 | |
b1760f7f | 341 | return ffi_prep_cif_machdep (cif); |
34fa7690 AG |
342 | } |
343 | ||
b1760f7f | 344 | /* Prototypes for assembly functions, in sysv.S. */ |
ac6ed182 | 345 | |
b1760f7f | 346 | struct call_frame |
63e5e3e0 | 347 | { |
b1760f7f RH |
348 | void *fp; |
349 | void *lr; | |
350 | void *rvalue; | |
351 | int flags; | |
352 | void *closure; | |
353 | }; | |
cb642590 | 354 | |
b1760f7f RH |
355 | extern void ffi_call_SYSV (void *stack, struct call_frame *, |
356 | void (*fn) (void)) FFI_HIDDEN; | |
357 | extern void ffi_call_VFP (void *vfp_space, struct call_frame *, | |
358 | void (*fn) (void), unsigned vfp_used) FFI_HIDDEN; | |
63e5e3e0 | 359 | |
b1760f7f RH |
360 | static void |
361 | ffi_call_int (ffi_cif * cif, void (*fn) (void), void *rvalue, | |
362 | void **avalue, void *closure) | |
363 | { | |
364 | int flags = cif->flags; | |
365 | ffi_type *rtype = cif->rtype; | |
366 | size_t bytes, rsize, vfp_size; | |
367 | char *stack, *vfp_space, *new_rvalue; | |
368 | struct call_frame *frame; | |
369 | ||
370 | rsize = 0; | |
371 | if (rvalue == NULL) | |
63e5e3e0 | 372 | { |
b1760f7f RH |
373 | /* If the return value is a struct and we don't have a return |
374 | value address then we need to make one. Otherwise the return | |
375 | value is in registers and we can ignore them. */ | |
376 | if (flags == ARM_TYPE_STRUCT) | |
377 | rsize = rtype->size; | |
378 | else | |
379 | flags = ARM_TYPE_VOID; | |
63e5e3e0 | 380 | } |
b1760f7f | 381 | else if (flags == ARM_TYPE_VFP_N) |
46e0720d CLT |
382 | { |
383 | /* Largest case is double x 4. */ | |
b1760f7f | 384 | rsize = 32; |
46e0720d | 385 | } |
b1760f7f RH |
386 | else if (flags == ARM_TYPE_INT && rtype->type == FFI_TYPE_STRUCT) |
387 | rsize = 4; | |
cb642590 | 388 | |
b1760f7f RH |
389 | /* Largest case. */ |
390 | vfp_size = (cif->abi == FFI_VFP && cif->vfp_used ? 8*8: 0); | |
ac6ed182 | 391 | |
b1760f7f RH |
392 | bytes = cif->bytes; |
393 | stack = alloca (vfp_size + bytes + sizeof(struct call_frame) + rsize); | |
46e0720d | 394 | |
b1760f7f RH |
395 | vfp_space = NULL; |
396 | if (vfp_size) | |
397 | { | |
398 | vfp_space = stack; | |
399 | stack += vfp_size; | |
63e5e3e0 | 400 | } |
0959e2b8 | 401 | |
b1760f7f | 402 | frame = (struct call_frame *)(stack + bytes); |
0959e2b8 | 403 | |
b1760f7f RH |
404 | new_rvalue = rvalue; |
405 | if (rsize) | |
406 | new_rvalue = (void *)(frame + 1); | |
0959e2b8 | 407 | |
b1760f7f RH |
408 | frame->rvalue = new_rvalue; |
409 | frame->flags = flags; | |
410 | frame->closure = closure; | |
0959e2b8 | 411 | |
b1760f7f RH |
412 | if (vfp_space) |
413 | { | |
414 | ffi_prep_args_VFP (cif, flags, new_rvalue, avalue, stack, vfp_space); | |
415 | ffi_call_VFP (vfp_space, frame, fn, cif->vfp_used); | |
416 | } | |
417 | else | |
418 | { | |
419 | ffi_prep_args_SYSV (cif, flags, new_rvalue, avalue, stack); | |
420 | ffi_call_SYSV (stack, frame, fn); | |
421 | } | |
46e0720d | 422 | |
b1760f7f RH |
423 | if (rvalue && rvalue != new_rvalue) |
424 | memcpy (rvalue, new_rvalue, rtype->size); | |
425 | } | |
0959e2b8 | 426 | |
b1760f7f RH |
427 | void |
428 | ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue) | |
0959e2b8 | 429 | { |
b1760f7f RH |
430 | ffi_call_int (cif, fn, rvalue, avalue, NULL); |
431 | } | |
0959e2b8 | 432 | |
92456a4e | 433 | #ifdef FFI_GO_CLOSURES |
b1760f7f RH |
434 | void |
435 | ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue, | |
436 | void **avalue, void *closure) | |
437 | { | |
438 | ffi_call_int (cif, fn, rvalue, avalue, closure); | |
439 | } | |
92456a4e | 440 | #endif |
0959e2b8 | 441 | |
b1760f7f RH |
442 | static void * |
443 | ffi_prep_incoming_args_SYSV (ffi_cif *cif, void *rvalue, | |
444 | char *argp, void **avalue) | |
445 | { | |
446 | ffi_type **arg_types = cif->arg_types; | |
447 | int i, n; | |
0959e2b8 | 448 | |
b1760f7f RH |
449 | if (cif->flags == ARM_TYPE_STRUCT) |
450 | { | |
451 | rvalue = *(void **) argp; | |
452 | argp += 4; | |
453 | } | |
92456a4e L |
454 | else |
455 | { | |
456 | if (cif->rtype->size && cif->rtype->size < 4) | |
457 | *(uint32_t *) rvalue = 0; | |
458 | } | |
0959e2b8 | 459 | |
b1760f7f RH |
460 | for (i = 0, n = cif->nargs; i < n; i++) |
461 | { | |
462 | ffi_type *ty = arg_types[i]; | |
463 | size_t z = ty->size; | |
0959e2b8 | 464 | |
b1760f7f RH |
465 | argp = ffi_align (ty, argp); |
466 | avalue[i] = (void *) argp; | |
467 | argp += z; | |
468 | } | |
469 | ||
470 | return rvalue; | |
0959e2b8 AH |
471 | } |
472 | ||
b1760f7f RH |
473 | static void * |
474 | ffi_prep_incoming_args_VFP (ffi_cif *cif, void *rvalue, char *stack, | |
475 | char *vfp_space, void **avalue) | |
0959e2b8 | 476 | { |
b1760f7f RH |
477 | ffi_type **arg_types = cif->arg_types; |
478 | int i, n, vi = 0; | |
479 | char *argp, *regp, *eo_regp; | |
480 | char done_with_regs = 0; | |
481 | char stack_used = 0; | |
0959e2b8 | 482 | |
b1760f7f RH |
483 | regp = stack; |
484 | eo_regp = argp = regp + 16; | |
0959e2b8 | 485 | |
b1760f7f RH |
486 | if (cif->flags == ARM_TYPE_STRUCT) |
487 | { | |
488 | rvalue = *(void **) regp; | |
489 | regp += 4; | |
490 | } | |
0959e2b8 | 491 | |
b1760f7f | 492 | for (i = 0, n = cif->nargs; i < n; i++) |
0959e2b8 | 493 | { |
b1760f7f RH |
494 | ffi_type *ty = arg_types[i]; |
495 | int is_vfp_type = vfp_type_p (ty); | |
496 | size_t z = ty->size; | |
497 | ||
498 | if (vi < cif->vfp_nargs && is_vfp_type) | |
46e0720d | 499 | { |
b1760f7f | 500 | avalue[i] = vfp_space + cif->vfp_args[vi++] * 4; |
46e0720d CLT |
501 | continue; |
502 | } | |
b1760f7f RH |
503 | else if (!done_with_regs && !is_vfp_type) |
504 | { | |
505 | char *tregp = ffi_align (ty, regp); | |
46e0720d | 506 | |
b1760f7f | 507 | z = (z < 4) ? 4 : z; // pad |
0959e2b8 | 508 | |
b1760f7f RH |
509 | /* If the arguments either fits into the registers or uses registers |
510 | and stack, while we haven't read other things from the stack */ | |
511 | if (tregp + z <= eo_regp || !stack_used) | |
512 | { | |
513 | /* Because we're little endian, this is what it turns into. */ | |
514 | avalue[i] = (void *) tregp; | |
515 | regp = tregp + z; | |
516 | ||
517 | /* If we read past the last core register, make sure we | |
518 | have not read from the stack before and continue | |
519 | reading after regp. */ | |
520 | if (regp > eo_regp) | |
521 | { | |
522 | FFI_ASSERT (!stack_used); | |
523 | argp = regp; | |
524 | } | |
525 | if (regp >= eo_regp) | |
526 | { | |
527 | done_with_regs = 1; | |
528 | stack_used = 1; | |
529 | } | |
530 | continue; | |
531 | } | |
532 | } | |
0959e2b8 | 533 | |
b1760f7f RH |
534 | stack_used = 1; |
535 | argp = ffi_align (ty, argp); | |
536 | avalue[i] = (void *) argp; | |
0959e2b8 AH |
537 | argp += z; |
538 | } | |
b1760f7f RH |
539 | |
540 | return rvalue; | |
0959e2b8 AH |
541 | } |
542 | ||
92456a4e L |
543 | #if FFI_CLOSURES |
544 | ||
b1760f7f RH |
545 | struct closure_frame |
546 | { | |
547 | char vfp_space[8*8] __attribute__((aligned(8))); | |
548 | char result[8*4]; | |
549 | char argp[]; | |
550 | }; | |
551 | ||
552 | int FFI_HIDDEN | |
553 | ffi_closure_inner_SYSV (ffi_cif *cif, | |
554 | void (*fun) (ffi_cif *, void *, void **, void *), | |
555 | void *user_data, | |
556 | struct closure_frame *frame) | |
557 | { | |
558 | void **avalue = (void **) alloca (cif->nargs * sizeof (void *)); | |
559 | void *rvalue = ffi_prep_incoming_args_SYSV (cif, frame->result, | |
560 | frame->argp, avalue); | |
561 | fun (cif, rvalue, avalue, user_data); | |
562 | return cif->flags; | |
563 | } | |
564 | ||
565 | int FFI_HIDDEN | |
566 | ffi_closure_inner_VFP (ffi_cif *cif, | |
567 | void (*fun) (ffi_cif *, void *, void **, void *), | |
568 | void *user_data, | |
569 | struct closure_frame *frame) | |
570 | { | |
571 | void **avalue = (void **) alloca (cif->nargs * sizeof (void *)); | |
572 | void *rvalue = ffi_prep_incoming_args_VFP (cif, frame->result, frame->argp, | |
573 | frame->vfp_space, avalue); | |
574 | fun (cif, rvalue, avalue, user_data); | |
575 | return cif->flags; | |
576 | } | |
0959e2b8 | 577 | |
b1760f7f RH |
578 | void ffi_closure_SYSV (void) FFI_HIDDEN; |
579 | void ffi_closure_VFP (void) FFI_HIDDEN; | |
92456a4e L |
580 | #if defined(FFI_EXEC_STATIC_TRAMP) |
581 | void ffi_closure_SYSV_alt (void) FFI_HIDDEN; | |
582 | void ffi_closure_VFP_alt (void) FFI_HIDDEN; | |
583 | #endif | |
584 | ||
585 | #ifdef FFI_GO_CLOSURES | |
b1760f7f RH |
586 | void ffi_go_closure_SYSV (void) FFI_HIDDEN; |
587 | void ffi_go_closure_VFP (void) FFI_HIDDEN; | |
34fa7690 | 588 | #endif |
0959e2b8 AH |
589 | |
590 | /* the cif must already be prep'ed */ | |
591 | ||
92456a4e L |
592 | #if defined(__FreeBSD__) && defined(__arm__) |
593 | #define __clear_cache(start, end) do { \ | |
594 | struct arm_sync_icache_args ua; \ | |
595 | \ | |
596 | ua.addr = (uintptr_t)(start); \ | |
597 | ua.len = (char *)(end) - (char *)start; \ | |
598 | sysarch(ARM_SYNC_ICACHE, &ua); \ | |
599 | } while (0); | |
600 | #endif | |
601 | ||
0959e2b8 | 602 | ffi_status |
b1760f7f RH |
603 | ffi_prep_closure_loc (ffi_closure * closure, |
604 | ffi_cif * cif, | |
605 | void (*fun) (ffi_cif *, void *, void **, void *), | |
606 | void *user_data, void *codeloc) | |
0959e2b8 | 607 | { |
b1760f7f | 608 | void (*closure_func) (void) = ffi_closure_SYSV; |
0959e2b8 | 609 | |
b1760f7f RH |
610 | if (cif->abi == FFI_VFP) |
611 | { | |
612 | /* We only need take the vfp path if there are vfp arguments. */ | |
613 | if (cif->vfp_used) | |
614 | closure_func = ffi_closure_VFP; | |
615 | } | |
616 | else if (cif->abi != FFI_SYSV) | |
34fa7690 | 617 | return FFI_BAD_ABI; |
b1760f7f | 618 | |
34fa7690 | 619 | #if FFI_EXEC_TRAMPOLINE_TABLE |
92456a4e | 620 | void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE); |
34fa7690 AG |
621 | config[0] = closure; |
622 | config[1] = closure_func; | |
623 | #else | |
92456a4e L |
624 | |
625 | #if defined(FFI_EXEC_STATIC_TRAMP) | |
626 | if (ffi_tramp_is_present(closure)) | |
627 | { | |
628 | /* Initialize the static trampoline's parameters. */ | |
629 | if (closure_func == ffi_closure_SYSV) | |
630 | closure_func = ffi_closure_SYSV_alt; | |
631 | else | |
632 | closure_func = ffi_closure_VFP_alt; | |
633 | ffi_tramp_set_parms (closure->ftramp, closure_func, closure); | |
634 | goto out; | |
635 | } | |
636 | #endif | |
637 | ||
638 | /* Initialize the dynamic trampoline. */ | |
639 | #ifndef _WIN32 | |
640 | memcpy(closure->tramp, ffi_arm_trampoline, 8); | |
641 | #else | |
642 | // cast away function type so MSVC doesn't set the lower bit of the function pointer | |
643 | memcpy(closure->tramp, (void*)((uintptr_t)ffi_arm_trampoline & 0xFFFFFFFE), FFI_TRAMPOLINE_CLOSURE_OFFSET); | |
644 | #endif | |
645 | ||
646 | #if defined (__QNX__) | |
647 | msync(closure->tramp, 8, 0x1000000); /* clear data map */ | |
648 | msync(codeloc, 8, 0x1000000); /* clear insn map */ | |
649 | #elif defined(_WIN32) | |
650 | FlushInstructionCache(GetCurrentProcess(), closure->tramp, FFI_TRAMPOLINE_SIZE); | |
651 | #else | |
b1760f7f RH |
652 | __clear_cache(closure->tramp, closure->tramp + 8); /* clear data map */ |
653 | __clear_cache(codeloc, codeloc + 8); /* clear insn map */ | |
92456a4e L |
654 | #endif |
655 | #ifdef _WIN32 | |
656 | *(void(**)(void))(closure->tramp + FFI_TRAMPOLINE_CLOSURE_FUNCTION) = closure_func; | |
657 | #else | |
b1760f7f | 658 | *(void (**)(void))(closure->tramp + 8) = closure_func; |
92456a4e L |
659 | #endif |
660 | out: | |
34fa7690 AG |
661 | #endif |
662 | ||
b1760f7f RH |
663 | closure->cif = cif; |
664 | closure->fun = fun; | |
0959e2b8 | 665 | closure->user_data = user_data; |
b1760f7f RH |
666 | |
667 | return FFI_OK; | |
668 | } | |
669 | ||
92456a4e | 670 | #ifdef FFI_GO_CLOSURES |
b1760f7f RH |
671 | ffi_status |
672 | ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif, | |
673 | void (*fun) (ffi_cif *, void *, void **, void *)) | |
674 | { | |
675 | void (*closure_func) (void) = ffi_go_closure_SYSV; | |
676 | ||
677 | if (cif->abi == FFI_VFP) | |
678 | { | |
679 | /* We only need take the vfp path if there are vfp arguments. */ | |
680 | if (cif->vfp_used) | |
681 | closure_func = ffi_go_closure_VFP; | |
682 | } | |
683 | else if (cif->abi != FFI_SYSV) | |
684 | return FFI_BAD_ABI; | |
685 | ||
686 | closure->tramp = closure_func; | |
687 | closure->cif = cif; | |
688 | closure->fun = fun; | |
0959e2b8 AH |
689 | |
690 | return FFI_OK; | |
691 | } | |
92456a4e L |
692 | #endif |
693 | ||
694 | #endif /* FFI_CLOSURES */ | |
46e0720d CLT |
695 | |
696 | /* Below are routines for VFP hard-float support. */ | |
697 | ||
b1760f7f RH |
698 | /* A subroutine of vfp_type_p. Given a structure type, return the type code |
699 | of the first non-structure element. Recurse for structure elements. | |
700 | Return -1 if the structure is in fact empty, i.e. no nested elements. */ | |
701 | ||
702 | static int | |
703 | is_hfa0 (const ffi_type *ty) | |
46e0720d | 704 | { |
b1760f7f RH |
705 | ffi_type **elements = ty->elements; |
706 | int i, ret = -1; | |
707 | ||
708 | if (elements != NULL) | |
709 | for (i = 0; elements[i]; ++i) | |
710 | { | |
711 | ret = elements[i]->type; | |
712 | if (ret == FFI_TYPE_STRUCT || ret == FFI_TYPE_COMPLEX) | |
713 | { | |
714 | ret = is_hfa0 (elements[i]); | |
715 | if (ret < 0) | |
716 | continue; | |
717 | } | |
718 | break; | |
719 | } | |
46e0720d | 720 | |
b1760f7f RH |
721 | return ret; |
722 | } | |
46e0720d | 723 | |
b1760f7f RH |
724 | /* A subroutine of vfp_type_p. Given a structure type, return true if all |
725 | of the non-structure elements are the same as CANDIDATE. */ | |
46e0720d | 726 | |
b1760f7f RH |
727 | static int |
728 | is_hfa1 (const ffi_type *ty, int candidate) | |
729 | { | |
730 | ffi_type **elements = ty->elements; | |
731 | int i; | |
732 | ||
733 | if (elements != NULL) | |
734 | for (i = 0; elements[i]; ++i) | |
46e0720d | 735 | { |
b1760f7f RH |
736 | int t = elements[i]->type; |
737 | if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX) | |
738 | { | |
739 | if (!is_hfa1 (elements[i], candidate)) | |
740 | return 0; | |
741 | } | |
742 | else if (t != candidate) | |
743 | return 0; | |
46e0720d | 744 | } |
b1760f7f RH |
745 | |
746 | return 1; | |
46e0720d CLT |
747 | } |
748 | ||
b1760f7f RH |
749 | /* Determine if TY is an homogenous floating point aggregate (HFA). |
750 | That is, a structure consisting of 1 to 4 members of all the same type, | |
751 | where that type is a floating point scalar. | |
752 | ||
753 | Returns non-zero iff TY is an HFA. The result is an encoded value where | |
754 | bits 0-7 contain the type code, and bits 8-10 contain the element count. */ | |
755 | ||
756 | static int | |
757 | vfp_type_p (const ffi_type *ty) | |
46e0720d | 758 | { |
b1760f7f RH |
759 | ffi_type **elements; |
760 | int candidate, i; | |
761 | size_t size, ele_count; | |
762 | ||
763 | /* Quickest tests first. */ | |
764 | candidate = ty->type; | |
765 | switch (ty->type) | |
46e0720d | 766 | { |
b1760f7f RH |
767 | default: |
768 | return 0; | |
769 | case FFI_TYPE_FLOAT: | |
770 | case FFI_TYPE_DOUBLE: | |
771 | ele_count = 1; | |
772 | goto done; | |
773 | case FFI_TYPE_COMPLEX: | |
774 | candidate = ty->elements[0]->type; | |
775 | if (candidate != FFI_TYPE_FLOAT && candidate != FFI_TYPE_DOUBLE) | |
776 | return 0; | |
777 | ele_count = 2; | |
778 | goto done; | |
779 | case FFI_TYPE_STRUCT: | |
780 | break; | |
46e0720d | 781 | } |
b1760f7f RH |
782 | |
783 | /* No HFA types are smaller than 4 bytes, or larger than 32 bytes. */ | |
784 | size = ty->size; | |
785 | if (size < 4 || size > 32) | |
786 | return 0; | |
787 | ||
788 | /* Find the type of the first non-structure member. */ | |
789 | elements = ty->elements; | |
790 | candidate = elements[0]->type; | |
791 | if (candidate == FFI_TYPE_STRUCT || candidate == FFI_TYPE_COMPLEX) | |
792 | { | |
793 | for (i = 0; ; ++i) | |
794 | { | |
795 | candidate = is_hfa0 (elements[i]); | |
796 | if (candidate >= 0) | |
797 | break; | |
798 | } | |
799 | } | |
800 | ||
801 | /* If the first member is not a floating point type, it's not an HFA. | |
802 | Also quickly re-check the size of the structure. */ | |
803 | switch (candidate) | |
804 | { | |
805 | case FFI_TYPE_FLOAT: | |
806 | ele_count = size / sizeof(float); | |
807 | if (size != ele_count * sizeof(float)) | |
808 | return 0; | |
809 | break; | |
810 | case FFI_TYPE_DOUBLE: | |
811 | ele_count = size / sizeof(double); | |
812 | if (size != ele_count * sizeof(double)) | |
813 | return 0; | |
814 | break; | |
815 | default: | |
816 | return 0; | |
817 | } | |
818 | if (ele_count > 4) | |
819 | return 0; | |
820 | ||
821 | /* Finally, make sure that all scalar elements are the same type. */ | |
822 | for (i = 0; elements[i]; ++i) | |
823 | { | |
824 | int t = elements[i]->type; | |
825 | if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX) | |
826 | { | |
827 | if (!is_hfa1 (elements[i], candidate)) | |
828 | return 0; | |
829 | } | |
830 | else if (t != candidate) | |
831 | return 0; | |
832 | } | |
833 | ||
834 | /* All tests succeeded. Encode the result. */ | |
835 | done: | |
836 | return (ele_count << 8) | candidate; | |
46e0720d CLT |
837 | } |
838 | ||
b1760f7f RH |
839 | static int |
840 | place_vfp_arg (ffi_cif *cif, int h) | |
46e0720d | 841 | { |
b1760f7f RH |
842 | unsigned short reg = cif->vfp_reg_free; |
843 | int align = 1, nregs = h >> 8; | |
844 | ||
845 | if ((h & 0xff) == FFI_TYPE_DOUBLE) | |
846 | align = 2, nregs *= 2; | |
847 | ||
46e0720d CLT |
848 | /* Align register number. */ |
849 | if ((reg & 1) && align == 2) | |
850 | reg++; | |
b1760f7f | 851 | |
46e0720d CLT |
852 | while (reg + nregs <= 16) |
853 | { | |
854 | int s, new_used = 0; | |
855 | for (s = reg; s < reg + nregs; s++) | |
856 | { | |
857 | new_used |= (1 << s); | |
858 | if (cif->vfp_used & (1 << s)) | |
859 | { | |
860 | reg += align; | |
861 | goto next_reg; | |
862 | } | |
863 | } | |
864 | /* Found regs to allocate. */ | |
865 | cif->vfp_used |= new_used; | |
92456a4e | 866 | cif->vfp_args[cif->vfp_nargs++] = (signed char)reg; |
46e0720d CLT |
867 | |
868 | /* Update vfp_reg_free. */ | |
869 | if (cif->vfp_used & (1 << cif->vfp_reg_free)) | |
870 | { | |
871 | reg += nregs; | |
872 | while (cif->vfp_used & (1 << reg)) | |
873 | reg += 1; | |
874 | cif->vfp_reg_free = reg; | |
875 | } | |
b1760f7f RH |
876 | return 0; |
877 | next_reg:; | |
46e0720d | 878 | } |
b1760f7f RH |
879 | // done, mark all regs as used |
880 | cif->vfp_reg_free = 16; | |
881 | cif->vfp_used = 0xFFFF; | |
882 | return 1; | |
46e0720d CLT |
883 | } |
884 | ||
b1760f7f RH |
885 | static void |
886 | layout_vfp_args (ffi_cif * cif) | |
46e0720d | 887 | { |
92456a4e | 888 | unsigned int i; |
46e0720d CLT |
889 | /* Init VFP fields */ |
890 | cif->vfp_used = 0; | |
891 | cif->vfp_nargs = 0; | |
892 | cif->vfp_reg_free = 0; | |
b1760f7f | 893 | memset (cif->vfp_args, -1, 16); /* Init to -1. */ |
46e0720d CLT |
894 | |
895 | for (i = 0; i < cif->nargs; i++) | |
896 | { | |
b1760f7f RH |
897 | int h = vfp_type_p (cif->arg_types[i]); |
898 | if (h && place_vfp_arg (cif, h) == 1) | |
899 | break; | |
46e0720d CLT |
900 | } |
901 | } | |
92456a4e L |
902 | |
903 | #if defined(FFI_EXEC_STATIC_TRAMP) | |
904 | void * | |
905 | ffi_tramp_arch (size_t *tramp_size, size_t *map_size) | |
906 | { | |
907 | extern void *trampoline_code_table; | |
908 | ||
909 | *tramp_size = ARM_TRAMP_SIZE; | |
910 | *map_size = ARM_TRAMP_MAP_SIZE; | |
911 | return &trampoline_code_table; | |
912 | } | |
913 | #endif | |
914 | ||
915 | #endif /* __arm__ or _M_ARM */ |