]>
Commit | Line | Data |
---|---|---|
e73d2479 AM |
1 | /* ----------------------------------------------------------------------- |
2 | ffi_linux64.c - Copyright (C) 2013 IBM | |
3 | Copyright (C) 2011 Anthony Green | |
4 | Copyright (C) 2011 Kyle Moffett | |
5 | Copyright (C) 2008 Red Hat, Inc | |
6 | Copyright (C) 2007, 2008 Free Software Foundation, Inc | |
7 | Copyright (c) 1998 Geoffrey Keating | |
8 | ||
9 | PowerPC Foreign Function Interface | |
10 | ||
11 | Permission is hereby granted, free of charge, to any person obtaining | |
12 | a copy of this software and associated documentation files (the | |
13 | ``Software''), to deal in the Software without restriction, including | |
14 | without limitation the rights to use, copy, modify, merge, publish, | |
15 | distribute, sublicense, and/or sell copies of the Software, and to | |
16 | permit persons to whom the Software is furnished to do so, subject to | |
17 | the following conditions: | |
18 | ||
19 | The above copyright notice and this permission notice shall be included | |
20 | in all copies or substantial portions of the Software. | |
21 | ||
22 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS | |
23 | OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
24 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. | |
25 | IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR | |
26 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, | |
27 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR | |
28 | OTHER DEALINGS IN THE SOFTWARE. | |
29 | ----------------------------------------------------------------------- */ | |
30 | ||
31 | #include "ffi.h" | |
32 | ||
33 | #ifdef POWERPC64 | |
34 | #include "ffi_common.h" | |
35 | #include "ffi_powerpc.h" | |
36 | ||
37 | ||
38 | /* About the LINUX64 ABI. */ | |
39 | enum { | |
40 | NUM_GPR_ARG_REGISTERS64 = 8, | |
41 | NUM_FPR_ARG_REGISTERS64 = 13 | |
42 | }; | |
43 | enum { ASM_NEEDS_REGISTERS64 = 4 }; | |
44 | ||
45 | ||
46 | #if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
47 | /* Adjust size of ffi_type_longdouble. */ | |
48 | void FFI_HIDDEN | |
49 | ffi_prep_types_linux64 (ffi_abi abi) | |
50 | { | |
51 | if ((abi & (FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128)) == FFI_LINUX) | |
52 | { | |
53 | ffi_type_longdouble.size = 8; | |
54 | ffi_type_longdouble.alignment = 8; | |
55 | } | |
56 | else | |
57 | { | |
58 | ffi_type_longdouble.size = 16; | |
59 | ffi_type_longdouble.alignment = 16; | |
60 | } | |
61 | } | |
62 | #endif | |
63 | ||
64 | ||
e73d2479 AM |
65 | static unsigned int |
66 | discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum) | |
67 | { | |
68 | switch (t->type) | |
69 | { | |
70 | case FFI_TYPE_FLOAT: | |
71 | case FFI_TYPE_DOUBLE: | |
72 | *elnum = 1; | |
73 | return (int) t->type; | |
74 | ||
75 | case FFI_TYPE_STRUCT:; | |
76 | { | |
77 | unsigned int base_elt = 0, total_elnum = 0; | |
78 | ffi_type **el = t->elements; | |
79 | while (*el) | |
80 | { | |
81 | unsigned int el_elt, el_elnum = 0; | |
82 | el_elt = discover_homogeneous_aggregate (*el, &el_elnum); | |
83 | if (el_elt == 0 | |
84 | || (base_elt && base_elt != el_elt)) | |
85 | return 0; | |
86 | base_elt = el_elt; | |
87 | total_elnum += el_elnum; | |
71d372eb | 88 | #if _CALL_ELF == 2 |
e73d2479 AM |
89 | if (total_elnum > 8) |
90 | return 0; | |
71d372eb AM |
91 | #else |
92 | if (total_elnum > 1) | |
93 | return 0; | |
94 | #endif | |
e73d2479 AM |
95 | el++; |
96 | } | |
97 | *elnum = total_elnum; | |
98 | return base_elt; | |
99 | } | |
100 | ||
101 | default: | |
102 | return 0; | |
103 | } | |
104 | } | |
e73d2479 AM |
105 | |
106 | ||
107 | /* Perform machine dependent cif processing */ | |
108 | static ffi_status | |
109 | ffi_prep_cif_linux64_core (ffi_cif *cif) | |
110 | { | |
111 | ffi_type **ptr; | |
112 | unsigned bytes; | |
113 | unsigned i, fparg_count = 0, intarg_count = 0; | |
114 | unsigned flags = cif->flags; | |
e73d2479 | 115 | unsigned int elt, elnum; |
e73d2479 AM |
116 | |
117 | #if FFI_TYPE_LONGDOUBLE == FFI_TYPE_DOUBLE | |
118 | /* If compiled without long double support.. */ | |
119 | if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) | |
120 | return FFI_BAD_ABI; | |
121 | #endif | |
122 | ||
123 | /* The machine-independent calculation of cif->bytes doesn't work | |
124 | for us. Redo the calculation. */ | |
125 | #if _CALL_ELF == 2 | |
126 | /* Space for backchain, CR, LR, TOC and the asm's temp regs. */ | |
127 | bytes = (4 + ASM_NEEDS_REGISTERS64) * sizeof (long); | |
128 | ||
129 | /* Space for the general registers. */ | |
130 | bytes += NUM_GPR_ARG_REGISTERS64 * sizeof (long); | |
131 | #else | |
132 | /* Space for backchain, CR, LR, cc/ld doubleword, TOC and the asm's temp | |
133 | regs. */ | |
134 | bytes = (6 + ASM_NEEDS_REGISTERS64) * sizeof (long); | |
135 | ||
136 | /* Space for the mandatory parm save area and general registers. */ | |
137 | bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof (long); | |
138 | #endif | |
139 | ||
140 | /* Return value handling. */ | |
141 | switch (cif->rtype->type) | |
142 | { | |
143 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
144 | case FFI_TYPE_LONGDOUBLE: | |
145 | if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) | |
146 | flags |= FLAG_RETURNS_128BITS; | |
147 | /* Fall through. */ | |
148 | #endif | |
149 | case FFI_TYPE_DOUBLE: | |
150 | flags |= FLAG_RETURNS_64BITS; | |
151 | /* Fall through. */ | |
152 | case FFI_TYPE_FLOAT: | |
153 | flags |= FLAG_RETURNS_FP; | |
154 | break; | |
155 | ||
156 | case FFI_TYPE_UINT128: | |
157 | flags |= FLAG_RETURNS_128BITS; | |
158 | /* Fall through. */ | |
159 | case FFI_TYPE_UINT64: | |
160 | case FFI_TYPE_SINT64: | |
71d372eb | 161 | case FFI_TYPE_POINTER: |
e73d2479 AM |
162 | flags |= FLAG_RETURNS_64BITS; |
163 | break; | |
164 | ||
165 | case FFI_TYPE_STRUCT: | |
166 | #if _CALL_ELF == 2 | |
167 | elt = discover_homogeneous_aggregate (cif->rtype, &elnum); | |
168 | if (elt) | |
169 | { | |
170 | if (elt == FFI_TYPE_DOUBLE) | |
171 | flags |= FLAG_RETURNS_64BITS; | |
172 | flags |= FLAG_RETURNS_FP | FLAG_RETURNS_SMST; | |
173 | break; | |
174 | } | |
175 | if (cif->rtype->size <= 16) | |
176 | { | |
177 | flags |= FLAG_RETURNS_SMST; | |
178 | break; | |
179 | } | |
180 | #endif | |
181 | intarg_count++; | |
182 | flags |= FLAG_RETVAL_REFERENCE; | |
183 | /* Fall through. */ | |
184 | case FFI_TYPE_VOID: | |
185 | flags |= FLAG_RETURNS_NOTHING; | |
186 | break; | |
187 | ||
188 | default: | |
189 | /* Returns 32-bit integer, or similar. Nothing to do here. */ | |
190 | break; | |
191 | } | |
192 | ||
193 | for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) | |
194 | { | |
195 | unsigned int align; | |
196 | ||
197 | switch ((*ptr)->type) | |
198 | { | |
199 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
200 | case FFI_TYPE_LONGDOUBLE: | |
201 | if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) | |
202 | { | |
203 | fparg_count++; | |
204 | intarg_count++; | |
205 | } | |
206 | /* Fall through. */ | |
207 | #endif | |
208 | case FFI_TYPE_DOUBLE: | |
209 | case FFI_TYPE_FLOAT: | |
210 | fparg_count++; | |
211 | intarg_count++; | |
212 | if (fparg_count > NUM_FPR_ARG_REGISTERS64) | |
213 | flags |= FLAG_ARG_NEEDS_PSAVE; | |
214 | break; | |
215 | ||
216 | case FFI_TYPE_STRUCT: | |
217 | if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) | |
218 | { | |
219 | align = (*ptr)->alignment; | |
220 | if (align > 16) | |
221 | align = 16; | |
222 | align = align / 8; | |
223 | if (align > 1) | |
224 | intarg_count = ALIGN (intarg_count, align); | |
225 | } | |
226 | intarg_count += ((*ptr)->size + 7) / 8; | |
e73d2479 AM |
227 | elt = discover_homogeneous_aggregate (*ptr, &elnum); |
228 | if (elt) | |
229 | { | |
230 | fparg_count += elnum; | |
231 | if (fparg_count > NUM_FPR_ARG_REGISTERS64) | |
232 | flags |= FLAG_ARG_NEEDS_PSAVE; | |
233 | } | |
234 | else | |
e73d2479 AM |
235 | { |
236 | if (intarg_count > NUM_GPR_ARG_REGISTERS64) | |
237 | flags |= FLAG_ARG_NEEDS_PSAVE; | |
238 | } | |
239 | break; | |
240 | ||
241 | case FFI_TYPE_POINTER: | |
242 | case FFI_TYPE_UINT64: | |
243 | case FFI_TYPE_SINT64: | |
244 | case FFI_TYPE_INT: | |
245 | case FFI_TYPE_UINT32: | |
246 | case FFI_TYPE_SINT32: | |
247 | case FFI_TYPE_UINT16: | |
248 | case FFI_TYPE_SINT16: | |
249 | case FFI_TYPE_UINT8: | |
250 | case FFI_TYPE_SINT8: | |
251 | /* Everything else is passed as a 8-byte word in a GPR, either | |
252 | the object itself or a pointer to it. */ | |
253 | intarg_count++; | |
254 | if (intarg_count > NUM_GPR_ARG_REGISTERS64) | |
255 | flags |= FLAG_ARG_NEEDS_PSAVE; | |
256 | break; | |
257 | default: | |
258 | FFI_ASSERT (0); | |
259 | } | |
260 | } | |
261 | ||
262 | if (fparg_count != 0) | |
263 | flags |= FLAG_FP_ARGUMENTS; | |
264 | if (intarg_count > 4) | |
265 | flags |= FLAG_4_GPR_ARGUMENTS; | |
266 | ||
267 | /* Space for the FPR registers, if needed. */ | |
268 | if (fparg_count != 0) | |
269 | bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double); | |
270 | ||
271 | /* Stack space. */ | |
272 | #if _CALL_ELF == 2 | |
273 | if ((flags & FLAG_ARG_NEEDS_PSAVE) != 0) | |
274 | bytes += intarg_count * sizeof (long); | |
275 | #else | |
276 | if (intarg_count > NUM_GPR_ARG_REGISTERS64) | |
277 | bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof (long); | |
278 | #endif | |
279 | ||
280 | /* The stack space allocated needs to be a multiple of 16 bytes. */ | |
281 | bytes = (bytes + 15) & ~0xF; | |
282 | ||
283 | cif->flags = flags; | |
284 | cif->bytes = bytes; | |
285 | ||
286 | return FFI_OK; | |
287 | } | |
288 | ||
289 | ffi_status FFI_HIDDEN | |
290 | ffi_prep_cif_linux64 (ffi_cif *cif) | |
291 | { | |
292 | if ((cif->abi & FFI_LINUX) != 0) | |
293 | cif->nfixedargs = cif->nargs; | |
294 | #if _CALL_ELF != 2 | |
295 | else if (cif->abi == FFI_COMPAT_LINUX64) | |
296 | { | |
297 | /* This call is from old code. Don't touch cif->nfixedargs | |
298 | since old code will be using a smaller cif. */ | |
299 | cif->flags |= FLAG_COMPAT; | |
300 | /* Translate to new abi value. */ | |
301 | cif->abi = FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128; | |
302 | } | |
303 | #endif | |
304 | else | |
305 | return FFI_BAD_ABI; | |
306 | return ffi_prep_cif_linux64_core (cif); | |
307 | } | |
308 | ||
309 | ffi_status FFI_HIDDEN | |
310 | ffi_prep_cif_linux64_var (ffi_cif *cif, | |
311 | unsigned int nfixedargs, | |
312 | unsigned int ntotalargs MAYBE_UNUSED) | |
313 | { | |
314 | if ((cif->abi & FFI_LINUX) != 0) | |
315 | cif->nfixedargs = nfixedargs; | |
316 | #if _CALL_ELF != 2 | |
317 | else if (cif->abi == FFI_COMPAT_LINUX64) | |
318 | { | |
319 | /* This call is from old code. Don't touch cif->nfixedargs | |
320 | since old code will be using a smaller cif. */ | |
321 | cif->flags |= FLAG_COMPAT; | |
322 | /* Translate to new abi value. */ | |
323 | cif->abi = FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128; | |
324 | } | |
325 | #endif | |
326 | else | |
327 | return FFI_BAD_ABI; | |
328 | #if _CALL_ELF == 2 | |
329 | cif->flags |= FLAG_ARG_NEEDS_PSAVE; | |
330 | #endif | |
331 | return ffi_prep_cif_linux64_core (cif); | |
332 | } | |
333 | ||
334 | ||
335 | /* ffi_prep_args64 is called by the assembly routine once stack space | |
336 | has been allocated for the function's arguments. | |
337 | ||
338 | The stack layout we want looks like this: | |
339 | ||
340 | | Ret addr from ffi_call_LINUX64 8bytes | higher addresses | |
341 | |--------------------------------------------| | |
342 | | CR save area 8bytes | | |
343 | |--------------------------------------------| | |
344 | | Previous backchain pointer 8 | stack pointer here | |
345 | |--------------------------------------------|<+ <<< on entry to | |
346 | | Saved r28-r31 4*8 | | ffi_call_LINUX64 | |
347 | |--------------------------------------------| | | |
348 | | GPR registers r3-r10 8*8 | | | |
349 | |--------------------------------------------| | | |
350 | | FPR registers f1-f13 (optional) 13*8 | | | |
351 | |--------------------------------------------| | | |
352 | | Parameter save area | | | |
353 | |--------------------------------------------| | | |
354 | | TOC save area 8 | | | |
355 | |--------------------------------------------| | stack | | |
356 | | Linker doubleword 8 | | grows | | |
357 | |--------------------------------------------| | down V | |
358 | | Compiler doubleword 8 | | | |
359 | |--------------------------------------------| | lower addresses | |
360 | | Space for callee's LR 8 | | | |
361 | |--------------------------------------------| | | |
362 | | CR save area 8 | | | |
363 | |--------------------------------------------| | stack pointer here | |
364 | | Current backchain pointer 8 |-/ during | |
365 | |--------------------------------------------| <<< ffi_call_LINUX64 | |
366 | ||
367 | */ | |
368 | ||
369 | void FFI_HIDDEN | |
370 | ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack) | |
371 | { | |
372 | const unsigned long bytes = ecif->cif->bytes; | |
373 | const unsigned long flags = ecif->cif->flags; | |
374 | ||
375 | typedef union | |
376 | { | |
377 | char *c; | |
378 | unsigned long *ul; | |
379 | float *f; | |
380 | double *d; | |
381 | size_t p; | |
382 | } valp; | |
383 | ||
384 | /* 'stacktop' points at the previous backchain pointer. */ | |
385 | valp stacktop; | |
386 | ||
387 | /* 'next_arg' points at the space for gpr3, and grows upwards as | |
388 | we use GPR registers, then continues at rest. */ | |
389 | valp gpr_base; | |
390 | valp gpr_end; | |
391 | valp rest; | |
392 | valp next_arg; | |
393 | ||
394 | /* 'fpr_base' points at the space for fpr3, and grows upwards as | |
395 | we use FPR registers. */ | |
396 | valp fpr_base; | |
397 | unsigned int fparg_count; | |
398 | ||
399 | unsigned int i, words, nargs, nfixedargs; | |
400 | ffi_type **ptr; | |
401 | double double_tmp; | |
402 | union | |
403 | { | |
404 | void **v; | |
405 | char **c; | |
406 | signed char **sc; | |
407 | unsigned char **uc; | |
408 | signed short **ss; | |
409 | unsigned short **us; | |
410 | signed int **si; | |
411 | unsigned int **ui; | |
412 | unsigned long **ul; | |
413 | float **f; | |
414 | double **d; | |
415 | } p_argv; | |
416 | unsigned long gprvalue; | |
417 | unsigned long align; | |
418 | ||
419 | stacktop.c = (char *) stack + bytes; | |
420 | gpr_base.ul = stacktop.ul - ASM_NEEDS_REGISTERS64 - NUM_GPR_ARG_REGISTERS64; | |
421 | gpr_end.ul = gpr_base.ul + NUM_GPR_ARG_REGISTERS64; | |
422 | #if _CALL_ELF == 2 | |
423 | rest.ul = stack + 4 + NUM_GPR_ARG_REGISTERS64; | |
424 | #else | |
425 | rest.ul = stack + 6 + NUM_GPR_ARG_REGISTERS64; | |
426 | #endif | |
427 | fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS64; | |
428 | fparg_count = 0; | |
429 | next_arg.ul = gpr_base.ul; | |
430 | ||
431 | /* Check that everything starts aligned properly. */ | |
432 | FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0); | |
433 | FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0); | |
434 | FFI_ASSERT ((bytes & 0xF) == 0); | |
435 | ||
436 | /* Deal with return values that are actually pass-by-reference. */ | |
437 | if (flags & FLAG_RETVAL_REFERENCE) | |
438 | *next_arg.ul++ = (unsigned long) (char *) ecif->rvalue; | |
439 | ||
440 | /* Now for the arguments. */ | |
441 | p_argv.v = ecif->avalue; | |
442 | nargs = ecif->cif->nargs; | |
443 | #if _CALL_ELF != 2 | |
444 | nfixedargs = (unsigned) -1; | |
445 | if ((flags & FLAG_COMPAT) == 0) | |
446 | #endif | |
447 | nfixedargs = ecif->cif->nfixedargs; | |
448 | for (ptr = ecif->cif->arg_types, i = 0; | |
449 | i < nargs; | |
450 | i++, ptr++, p_argv.v++) | |
451 | { | |
e73d2479 | 452 | unsigned int elt, elnum; |
e73d2479 AM |
453 | |
454 | switch ((*ptr)->type) | |
455 | { | |
456 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
457 | case FFI_TYPE_LONGDOUBLE: | |
458 | if ((ecif->cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) | |
459 | { | |
460 | double_tmp = (*p_argv.d)[0]; | |
461 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) | |
462 | { | |
463 | *fpr_base.d++ = double_tmp; | |
464 | # if _CALL_ELF != 2 | |
465 | if ((flags & FLAG_COMPAT) != 0) | |
466 | *next_arg.d = double_tmp; | |
467 | # endif | |
468 | } | |
469 | else | |
470 | *next_arg.d = double_tmp; | |
471 | if (++next_arg.ul == gpr_end.ul) | |
472 | next_arg.ul = rest.ul; | |
473 | fparg_count++; | |
474 | double_tmp = (*p_argv.d)[1]; | |
475 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) | |
476 | { | |
477 | *fpr_base.d++ = double_tmp; | |
478 | # if _CALL_ELF != 2 | |
479 | if ((flags & FLAG_COMPAT) != 0) | |
480 | *next_arg.d = double_tmp; | |
481 | # endif | |
482 | } | |
483 | else | |
484 | *next_arg.d = double_tmp; | |
485 | if (++next_arg.ul == gpr_end.ul) | |
486 | next_arg.ul = rest.ul; | |
487 | fparg_count++; | |
488 | FFI_ASSERT (__LDBL_MANT_DIG__ == 106); | |
489 | FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
490 | break; | |
491 | } | |
492 | /* Fall through. */ | |
493 | #endif | |
494 | case FFI_TYPE_DOUBLE: | |
71d372eb | 495 | do_double: |
e73d2479 AM |
496 | double_tmp = **p_argv.d; |
497 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) | |
498 | { | |
499 | *fpr_base.d++ = double_tmp; | |
500 | #if _CALL_ELF != 2 | |
501 | if ((flags & FLAG_COMPAT) != 0) | |
502 | *next_arg.d = double_tmp; | |
503 | #endif | |
504 | } | |
505 | else | |
506 | *next_arg.d = double_tmp; | |
507 | if (++next_arg.ul == gpr_end.ul) | |
508 | next_arg.ul = rest.ul; | |
509 | fparg_count++; | |
510 | FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
511 | break; | |
512 | ||
513 | case FFI_TYPE_FLOAT: | |
71d372eb | 514 | do_float: |
e73d2479 AM |
515 | double_tmp = **p_argv.f; |
516 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) | |
517 | { | |
518 | *fpr_base.d++ = double_tmp; | |
519 | #if _CALL_ELF != 2 | |
520 | if ((flags & FLAG_COMPAT) != 0) | |
71d372eb AM |
521 | { |
522 | # ifndef __LITTLE_ENDIAN__ | |
523 | next_arg.f[1] = (float) double_tmp; | |
524 | # else | |
525 | next_arg.f[0] = (float) double_tmp; | |
526 | # endif | |
527 | } | |
e73d2479 AM |
528 | #endif |
529 | } | |
530 | else | |
71d372eb AM |
531 | { |
532 | # ifndef __LITTLE_ENDIAN__ | |
533 | next_arg.f[1] = (float) double_tmp; | |
534 | # else | |
535 | next_arg.f[0] = (float) double_tmp; | |
536 | # endif | |
537 | } | |
e73d2479 AM |
538 | if (++next_arg.ul == gpr_end.ul) |
539 | next_arg.ul = rest.ul; | |
540 | fparg_count++; | |
541 | FFI_ASSERT (flags & FLAG_FP_ARGUMENTS); | |
542 | break; | |
543 | ||
544 | case FFI_TYPE_STRUCT: | |
545 | if ((ecif->cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) | |
546 | { | |
547 | align = (*ptr)->alignment; | |
548 | if (align > 16) | |
549 | align = 16; | |
550 | if (align > 1) | |
551 | next_arg.p = ALIGN (next_arg.p, align); | |
552 | } | |
e73d2479 AM |
553 | elt = discover_homogeneous_aggregate (*ptr, &elnum); |
554 | if (elt) | |
555 | { | |
71d372eb | 556 | #if _CALL_ELF == 2 |
e73d2479 AM |
557 | union { |
558 | void *v; | |
559 | float *f; | |
560 | double *d; | |
561 | } arg; | |
562 | ||
563 | arg.v = *p_argv.v; | |
564 | if (elt == FFI_TYPE_FLOAT) | |
565 | { | |
566 | do | |
567 | { | |
568 | double_tmp = *arg.f++; | |
569 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 | |
570 | && i < nfixedargs) | |
571 | *fpr_base.d++ = double_tmp; | |
572 | else | |
573 | *next_arg.f = (float) double_tmp; | |
574 | if (++next_arg.f == gpr_end.f) | |
575 | next_arg.f = rest.f; | |
576 | fparg_count++; | |
577 | } | |
578 | while (--elnum != 0); | |
579 | if ((next_arg.p & 3) != 0) | |
580 | { | |
581 | if (++next_arg.f == gpr_end.f) | |
582 | next_arg.f = rest.f; | |
583 | } | |
584 | } | |
585 | else | |
586 | do | |
587 | { | |
588 | double_tmp = *arg.d++; | |
589 | if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs) | |
590 | *fpr_base.d++ = double_tmp; | |
591 | else | |
592 | *next_arg.d = double_tmp; | |
593 | if (++next_arg.d == gpr_end.d) | |
594 | next_arg.d = rest.d; | |
595 | fparg_count++; | |
596 | } | |
597 | while (--elnum != 0); | |
71d372eb AM |
598 | #else |
599 | if (elt == FFI_TYPE_FLOAT) | |
600 | goto do_float; | |
601 | else | |
602 | goto do_double; | |
603 | #endif | |
e73d2479 AM |
604 | } |
605 | else | |
e73d2479 AM |
606 | { |
607 | words = ((*ptr)->size + 7) / 8; | |
608 | if (next_arg.ul >= gpr_base.ul && next_arg.ul + words > gpr_end.ul) | |
609 | { | |
610 | size_t first = gpr_end.c - next_arg.c; | |
611 | memcpy (next_arg.c, *p_argv.c, first); | |
612 | memcpy (rest.c, *p_argv.c + first, (*ptr)->size - first); | |
613 | next_arg.c = rest.c + words * 8 - first; | |
614 | } | |
615 | else | |
616 | { | |
617 | char *where = next_arg.c; | |
618 | ||
619 | #ifndef __LITTLE_ENDIAN__ | |
620 | /* Structures with size less than eight bytes are passed | |
621 | left-padded. */ | |
622 | if ((*ptr)->size < 8) | |
623 | where += 8 - (*ptr)->size; | |
624 | #endif | |
625 | memcpy (where, *p_argv.c, (*ptr)->size); | |
626 | next_arg.ul += words; | |
627 | if (next_arg.ul == gpr_end.ul) | |
628 | next_arg.ul = rest.ul; | |
629 | } | |
630 | } | |
631 | break; | |
632 | ||
633 | case FFI_TYPE_UINT8: | |
634 | gprvalue = **p_argv.uc; | |
635 | goto putgpr; | |
636 | case FFI_TYPE_SINT8: | |
637 | gprvalue = **p_argv.sc; | |
638 | goto putgpr; | |
639 | case FFI_TYPE_UINT16: | |
640 | gprvalue = **p_argv.us; | |
641 | goto putgpr; | |
642 | case FFI_TYPE_SINT16: | |
643 | gprvalue = **p_argv.ss; | |
644 | goto putgpr; | |
645 | case FFI_TYPE_UINT32: | |
646 | gprvalue = **p_argv.ui; | |
647 | goto putgpr; | |
648 | case FFI_TYPE_INT: | |
649 | case FFI_TYPE_SINT32: | |
650 | gprvalue = **p_argv.si; | |
651 | goto putgpr; | |
652 | ||
653 | case FFI_TYPE_UINT64: | |
654 | case FFI_TYPE_SINT64: | |
655 | case FFI_TYPE_POINTER: | |
656 | gprvalue = **p_argv.ul; | |
657 | putgpr: | |
658 | *next_arg.ul++ = gprvalue; | |
659 | if (next_arg.ul == gpr_end.ul) | |
660 | next_arg.ul = rest.ul; | |
661 | break; | |
662 | } | |
663 | } | |
664 | ||
665 | FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS | |
666 | || (next_arg.ul >= gpr_base.ul | |
667 | && next_arg.ul <= gpr_base.ul + 4)); | |
668 | } | |
669 | ||
670 | ||
671 | #if _CALL_ELF == 2 | |
672 | #define MIN_CACHE_LINE_SIZE 8 | |
673 | ||
674 | static void | |
675 | flush_icache (char *wraddr, char *xaddr, int size) | |
676 | { | |
677 | int i; | |
678 | for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE) | |
679 | __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" | |
680 | : : "r" (xaddr + i), "r" (wraddr + i) : "memory"); | |
681 | __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" "sync;" "isync;" | |
682 | : : "r"(xaddr + size - 1), "r"(wraddr + size - 1) | |
683 | : "memory"); | |
684 | } | |
685 | #endif | |
686 | ||
b1760f7f RH |
687 | |
688 | ffi_status FFI_HIDDEN | |
e73d2479 AM |
689 | ffi_prep_closure_loc_linux64 (ffi_closure *closure, |
690 | ffi_cif *cif, | |
691 | void (*fun) (ffi_cif *, void *, void **, void *), | |
692 | void *user_data, | |
693 | void *codeloc) | |
694 | { | |
695 | #if _CALL_ELF == 2 | |
696 | unsigned int *tramp = (unsigned int *) &closure->tramp[0]; | |
697 | ||
698 | if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI) | |
699 | return FFI_BAD_ABI; | |
700 | ||
701 | tramp[0] = 0xe96c0018; /* 0: ld 11,2f-0b(12) */ | |
702 | tramp[1] = 0xe98c0010; /* ld 12,1f-0b(12) */ | |
703 | tramp[2] = 0x7d8903a6; /* mtctr 12 */ | |
704 | tramp[3] = 0x4e800420; /* bctr */ | |
705 | /* 1: .quad function_addr */ | |
706 | /* 2: .quad context */ | |
707 | *(void **) &tramp[4] = (void *) ffi_closure_LINUX64; | |
708 | *(void **) &tramp[6] = codeloc; | |
b1760f7f | 709 | flush_icache ((char *) tramp, (char *) codeloc, 4 * 4); |
e73d2479 AM |
710 | #else |
711 | void **tramp = (void **) &closure->tramp[0]; | |
712 | ||
713 | if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI) | |
714 | return FFI_BAD_ABI; | |
715 | ||
b1760f7f RH |
716 | /* Copy function address and TOC from ffi_closure_LINUX64 OPD. */ |
717 | memcpy (&tramp[0], (void **) ffi_closure_LINUX64, sizeof (void *)); | |
718 | tramp[1] = codeloc; | |
719 | memcpy (&tramp[2], (void **) ffi_closure_LINUX64 + 1, sizeof (void *)); | |
e73d2479 AM |
720 | #endif |
721 | ||
722 | closure->cif = cif; | |
723 | closure->fun = fun; | |
724 | closure->user_data = user_data; | |
725 | ||
726 | return FFI_OK; | |
727 | } | |
728 | ||
729 | ||
730 | int FFI_HIDDEN | |
b1760f7f RH |
731 | ffi_closure_helper_LINUX64 (ffi_cif *cif, |
732 | void (*fun) (ffi_cif *, void *, void **, void *), | |
733 | void *user_data, | |
734 | void *rvalue, | |
735 | unsigned long *pst, | |
736 | ffi_dblfl *pfr) | |
e73d2479 AM |
737 | { |
738 | /* rvalue is the pointer to space for return value in closure assembly */ | |
739 | /* pst is the pointer to parameter save area | |
740 | (r3-r10 are stored into its first 8 slots by ffi_closure_LINUX64) */ | |
741 | /* pfr is the pointer to where f1-f13 are stored in ffi_closure_LINUX64 */ | |
742 | ||
743 | void **avalue; | |
744 | ffi_type **arg_types; | |
745 | unsigned long i, avn, nfixedargs; | |
e73d2479 AM |
746 | ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64; |
747 | unsigned long align; | |
748 | ||
e73d2479 AM |
749 | avalue = alloca (cif->nargs * sizeof (void *)); |
750 | ||
751 | /* Copy the caller's structure return value address so that the | |
752 | closure returns the data directly to the caller. */ | |
753 | if (cif->rtype->type == FFI_TYPE_STRUCT | |
754 | && (cif->flags & FLAG_RETURNS_SMST) == 0) | |
755 | { | |
756 | rvalue = (void *) *pst; | |
757 | pst++; | |
758 | } | |
759 | ||
760 | i = 0; | |
761 | avn = cif->nargs; | |
762 | #if _CALL_ELF != 2 | |
763 | nfixedargs = (unsigned) -1; | |
764 | if ((cif->flags & FLAG_COMPAT) == 0) | |
765 | #endif | |
766 | nfixedargs = cif->nfixedargs; | |
767 | arg_types = cif->arg_types; | |
768 | ||
769 | /* Grab the addresses of the arguments from the stack frame. */ | |
770 | while (i < avn) | |
771 | { | |
772 | unsigned int elt, elnum; | |
773 | ||
774 | switch (arg_types[i]->type) | |
775 | { | |
776 | case FFI_TYPE_SINT8: | |
777 | case FFI_TYPE_UINT8: | |
778 | #ifndef __LITTLE_ENDIAN__ | |
779 | avalue[i] = (char *) pst + 7; | |
780 | pst++; | |
781 | break; | |
782 | #endif | |
783 | ||
784 | case FFI_TYPE_SINT16: | |
785 | case FFI_TYPE_UINT16: | |
786 | #ifndef __LITTLE_ENDIAN__ | |
787 | avalue[i] = (char *) pst + 6; | |
788 | pst++; | |
789 | break; | |
790 | #endif | |
791 | ||
792 | case FFI_TYPE_SINT32: | |
793 | case FFI_TYPE_UINT32: | |
794 | #ifndef __LITTLE_ENDIAN__ | |
795 | avalue[i] = (char *) pst + 4; | |
796 | pst++; | |
797 | break; | |
798 | #endif | |
799 | ||
800 | case FFI_TYPE_SINT64: | |
801 | case FFI_TYPE_UINT64: | |
802 | case FFI_TYPE_POINTER: | |
803 | avalue[i] = pst; | |
804 | pst++; | |
805 | break; | |
806 | ||
807 | case FFI_TYPE_STRUCT: | |
808 | if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0) | |
809 | { | |
810 | align = arg_types[i]->alignment; | |
811 | if (align > 16) | |
812 | align = 16; | |
813 | if (align > 1) | |
814 | pst = (unsigned long *) ALIGN ((size_t) pst, align); | |
815 | } | |
e73d2479 | 816 | elt = discover_homogeneous_aggregate (arg_types[i], &elnum); |
e73d2479 AM |
817 | if (elt) |
818 | { | |
71d372eb | 819 | #if _CALL_ELF == 2 |
e73d2479 AM |
820 | union { |
821 | void *v; | |
822 | unsigned long *ul; | |
823 | float *f; | |
824 | double *d; | |
825 | size_t p; | |
826 | } to, from; | |
827 | ||
828 | /* Repackage the aggregate from its parts. The | |
829 | aggregate size is not greater than the space taken by | |
830 | the registers so store back to the register/parameter | |
831 | save arrays. */ | |
832 | if (pfr + elnum <= end_pfr) | |
833 | to.v = pfr; | |
834 | else | |
835 | to.v = pst; | |
836 | ||
837 | avalue[i] = to.v; | |
838 | from.ul = pst; | |
839 | if (elt == FFI_TYPE_FLOAT) | |
840 | { | |
841 | do | |
842 | { | |
843 | if (pfr < end_pfr && i < nfixedargs) | |
844 | { | |
845 | *to.f = (float) pfr->d; | |
846 | pfr++; | |
847 | } | |
848 | else | |
849 | *to.f = *from.f; | |
850 | to.f++; | |
851 | from.f++; | |
852 | } | |
853 | while (--elnum != 0); | |
854 | } | |
855 | else | |
856 | { | |
857 | do | |
858 | { | |
859 | if (pfr < end_pfr && i < nfixedargs) | |
860 | { | |
861 | *to.d = pfr->d; | |
862 | pfr++; | |
863 | } | |
864 | else | |
865 | *to.d = *from.d; | |
866 | to.d++; | |
867 | from.d++; | |
868 | } | |
869 | while (--elnum != 0); | |
870 | } | |
71d372eb AM |
871 | #else |
872 | if (elt == FFI_TYPE_FLOAT) | |
873 | goto do_float; | |
874 | else | |
875 | goto do_double; | |
876 | #endif | |
e73d2479 AM |
877 | } |
878 | else | |
879 | { | |
880 | #ifndef __LITTLE_ENDIAN__ | |
881 | /* Structures with size less than eight bytes are passed | |
882 | left-padded. */ | |
883 | if (arg_types[i]->size < 8) | |
884 | avalue[i] = (char *) pst + 8 - arg_types[i]->size; | |
885 | else | |
886 | #endif | |
887 | avalue[i] = pst; | |
888 | } | |
889 | pst += (arg_types[i]->size + 7) / 8; | |
890 | break; | |
891 | ||
892 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
893 | case FFI_TYPE_LONGDOUBLE: | |
894 | if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0) | |
895 | { | |
896 | if (pfr + 1 < end_pfr && i + 1 < nfixedargs) | |
897 | { | |
898 | avalue[i] = pfr; | |
899 | pfr += 2; | |
900 | } | |
901 | else | |
902 | { | |
903 | if (pfr < end_pfr && i < nfixedargs) | |
904 | { | |
905 | /* Passed partly in f13 and partly on the stack. | |
906 | Move it all to the stack. */ | |
907 | *pst = *(unsigned long *) pfr; | |
908 | pfr++; | |
909 | } | |
910 | avalue[i] = pst; | |
911 | } | |
912 | pst += 2; | |
913 | break; | |
914 | } | |
915 | /* Fall through. */ | |
916 | #endif | |
917 | case FFI_TYPE_DOUBLE: | |
71d372eb | 918 | do_double: |
e73d2479 AM |
919 | /* On the outgoing stack all values are aligned to 8 */ |
920 | /* there are 13 64bit floating point registers */ | |
921 | ||
922 | if (pfr < end_pfr && i < nfixedargs) | |
923 | { | |
924 | avalue[i] = pfr; | |
925 | pfr++; | |
926 | } | |
927 | else | |
928 | avalue[i] = pst; | |
929 | pst++; | |
930 | break; | |
931 | ||
932 | case FFI_TYPE_FLOAT: | |
71d372eb | 933 | do_float: |
e73d2479 AM |
934 | if (pfr < end_pfr && i < nfixedargs) |
935 | { | |
936 | /* Float values are stored as doubles in the | |
937 | ffi_closure_LINUX64 code. Fix them here. */ | |
938 | pfr->f = (float) pfr->d; | |
939 | avalue[i] = pfr; | |
940 | pfr++; | |
941 | } | |
942 | else | |
71d372eb AM |
943 | { |
944 | #ifndef __LITTLE_ENDIAN__ | |
945 | avalue[i] = (char *) pst + 4; | |
946 | #else | |
947 | avalue[i] = pst; | |
948 | #endif | |
949 | } | |
e73d2479 AM |
950 | pst++; |
951 | break; | |
952 | ||
953 | default: | |
954 | FFI_ASSERT (0); | |
955 | } | |
956 | ||
957 | i++; | |
958 | } | |
959 | ||
b1760f7f | 960 | (*fun) (cif, rvalue, avalue, user_data); |
e73d2479 AM |
961 | |
962 | /* Tell ffi_closure_LINUX64 how to perform return type promotions. */ | |
963 | if ((cif->flags & FLAG_RETURNS_SMST) != 0) | |
964 | { | |
965 | if ((cif->flags & FLAG_RETURNS_FP) == 0) | |
966 | return FFI_V2_TYPE_SMALL_STRUCT + cif->rtype->size - 1; | |
967 | else if ((cif->flags & FLAG_RETURNS_64BITS) != 0) | |
968 | return FFI_V2_TYPE_DOUBLE_HOMOG; | |
969 | else | |
970 | return FFI_V2_TYPE_FLOAT_HOMOG; | |
971 | } | |
972 | return cif->rtype->type; | |
973 | } | |
974 | #endif |