]>
Commit | Line | Data |
---|---|---|
63e5e3e0 | 1 | /* ----------------------------------------------------------------------- |
b1760f7f | 2 | ffi.c - Copyright (c) 2011, 2013 Anthony Green |
34fa7690 | 3 | Copyright (c) 1996, 2003-2004, 2007-2008 Red Hat, Inc. |
b1760f7f | 4 | |
0ce78f01 | 5 | SPARC Foreign Function Interface |
63e5e3e0 | 6 | |
63e5e3e0 AG |
7 | Permission is hereby granted, free of charge, to any person obtaining |
8 | a copy of this software and associated documentation files (the | |
9 | ``Software''), to deal in the Software without restriction, including | |
10 | without limitation the rights to use, copy, modify, merge, publish, | |
11 | distribute, sublicense, and/or sell copies of the Software, and to | |
12 | permit persons to whom the Software is furnished to do so, subject to | |
13 | the following conditions: | |
14 | ||
15 | The above copyright notice and this permission notice shall be included | |
16 | in all copies or substantial portions of the Software. | |
17 | ||
5f933ef0 AH |
18 | THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, |
19 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF | |
20 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND | |
21 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT | |
22 | HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, | |
23 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
24 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER | |
25 | DEALINGS IN THE SOFTWARE. | |
63e5e3e0 AG |
26 | ----------------------------------------------------------------------- */ |
27 | ||
28 | #include <ffi.h> | |
29 | #include <ffi_common.h> | |
63e5e3e0 | 30 | #include <stdlib.h> |
b1760f7f | 31 | #include "internal.h" |
63e5e3e0 | 32 | |
b1760f7f | 33 | #ifndef SPARC64 |
c75c7793 | 34 | |
b1760f7f RH |
35 | /* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE; |
36 | all further uses in this file will refer to the 128-bit type. */ | |
37 | #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE | |
38 | # if FFI_TYPE_LONGDOUBLE != 4 | |
39 | # error FFI_TYPE_LONGDOUBLE out of date | |
40 | # endif | |
41 | #else | |
42 | # undef FFI_TYPE_LONGDOUBLE | |
43 | # define FFI_TYPE_LONGDOUBLE 4 | |
3791773c JJ |
44 | #endif |
45 | ||
b1760f7f RH |
46 | /* Perform machine dependent cif processing */ |
47 | ffi_status FFI_HIDDEN | |
48 | ffi_prep_cif_machdep(ffi_cif *cif) | |
49 | { | |
50 | ffi_type *rtype = cif->rtype; | |
51 | int rtt = rtype->type; | |
52 | size_t bytes; | |
53 | int i, n, flags; | |
3791773c | 54 | |
b1760f7f RH |
55 | /* Set the return type flag */ |
56 | switch (rtt) | |
3791773c | 57 | { |
b1760f7f RH |
58 | case FFI_TYPE_VOID: |
59 | flags = SPARC_RET_VOID; | |
60 | break; | |
61 | case FFI_TYPE_FLOAT: | |
62 | flags = SPARC_RET_F_1; | |
63 | break; | |
64 | case FFI_TYPE_DOUBLE: | |
65 | flags = SPARC_RET_F_2; | |
66 | break; | |
67 | case FFI_TYPE_LONGDOUBLE: | |
68 | case FFI_TYPE_STRUCT: | |
69 | flags = (rtype->size & 0xfff) << SPARC_SIZEMASK_SHIFT; | |
70 | flags |= SPARC_RET_STRUCT; | |
71 | break; | |
72 | case FFI_TYPE_SINT8: | |
73 | flags = SPARC_RET_SINT8; | |
74 | break; | |
75 | case FFI_TYPE_UINT8: | |
76 | flags = SPARC_RET_UINT8; | |
77 | break; | |
78 | case FFI_TYPE_SINT16: | |
79 | flags = SPARC_RET_SINT16; | |
80 | break; | |
81 | case FFI_TYPE_UINT16: | |
82 | flags = SPARC_RET_UINT16; | |
83 | break; | |
84 | case FFI_TYPE_INT: | |
85 | case FFI_TYPE_SINT32: | |
86 | case FFI_TYPE_UINT32: | |
87 | case FFI_TYPE_POINTER: | |
88 | flags = SPARC_RET_UINT32; | |
89 | break; | |
90 | case FFI_TYPE_SINT64: | |
91 | case FFI_TYPE_UINT64: | |
92 | flags = SPARC_RET_INT64; | |
93 | break; | |
94 | case FFI_TYPE_COMPLEX: | |
95 | rtt = rtype->elements[0]->type; | |
96 | switch (rtt) | |
97 | { | |
98 | case FFI_TYPE_FLOAT: | |
99 | flags = SPARC_RET_F_2; | |
100 | break; | |
101 | case FFI_TYPE_DOUBLE: | |
102 | flags = SPARC_RET_F_4; | |
103 | break; | |
104 | case FFI_TYPE_LONGDOUBLE: | |
105 | flags = SPARC_RET_F_8; | |
106 | break; | |
107 | case FFI_TYPE_SINT64: | |
108 | case FFI_TYPE_UINT64: | |
109 | flags = SPARC_RET_INT128; | |
110 | break; | |
111 | case FFI_TYPE_INT: | |
112 | case FFI_TYPE_SINT32: | |
113 | case FFI_TYPE_UINT32: | |
114 | flags = SPARC_RET_INT64; | |
115 | break; | |
116 | case FFI_TYPE_SINT16: | |
117 | case FFI_TYPE_UINT16: | |
118 | flags = SP_V8_RET_CPLX16; | |
119 | break; | |
120 | case FFI_TYPE_SINT8: | |
121 | case FFI_TYPE_UINT8: | |
122 | flags = SP_V8_RET_CPLX8; | |
123 | break; | |
124 | default: | |
125 | abort(); | |
126 | } | |
127 | break; | |
128 | default: | |
129 | abort(); | |
3791773c | 130 | } |
b1760f7f | 131 | cif->flags = flags; |
3791773c | 132 | |
b1760f7f RH |
133 | bytes = 0; |
134 | for (i = 0, n = cif->nargs; i < n; ++i) | |
3791773c | 135 | { |
b1760f7f RH |
136 | ffi_type *ty = cif->arg_types[i]; |
137 | size_t z = ty->size; | |
138 | int tt = ty->type; | |
3791773c | 139 | |
b1760f7f | 140 | switch (tt) |
3791773c JJ |
141 | { |
142 | case FFI_TYPE_STRUCT: | |
3791773c | 143 | case FFI_TYPE_LONGDOUBLE: |
b1760f7f RH |
144 | by_reference: |
145 | /* Passed by reference. */ | |
146 | z = 4; | |
3791773c | 147 | break; |
b1760f7f RH |
148 | |
149 | case FFI_TYPE_COMPLEX: | |
150 | tt = ty->elements[0]->type; | |
151 | if (tt == FFI_TYPE_FLOAT || z > 8) | |
152 | goto by_reference; | |
153 | /* FALLTHRU */ | |
154 | ||
155 | default: | |
92456a4e | 156 | z = FFI_ALIGN(z, 4); |
3791773c | 157 | } |
b1760f7f RH |
158 | bytes += z; |
159 | } | |
3791773c | 160 | |
b1760f7f RH |
161 | /* Sparc call frames require that space is allocated for 6 args, |
162 | even if they aren't used. Make that space if necessary. */ | |
163 | if (bytes < 6 * 4) | |
164 | bytes = 6 * 4; | |
3791773c | 165 | |
b1760f7f RH |
166 | /* The ABI always requires space for the struct return pointer. */ |
167 | bytes += 4; | |
3791773c | 168 | |
b1760f7f | 169 | /* The stack must be 2 word aligned, so round bytes up appropriately. */ |
92456a4e | 170 | bytes = FFI_ALIGN(bytes, 2 * 4); |
3791773c | 171 | |
b1760f7f RH |
172 | /* Include the call frame to prep_args. */ |
173 | bytes += 4*16 + 4*8; | |
174 | cif->bytes = bytes; | |
3791773c | 175 | |
b1760f7f RH |
176 | return FFI_OK; |
177 | } | |
3791773c | 178 | |
b1760f7f RH |
179 | extern void ffi_call_v8(ffi_cif *cif, void (*fn)(void), void *rvalue, |
180 | void **avalue, size_t bytes, void *closure) FFI_HIDDEN; | |
3791773c | 181 | |
b1760f7f RH |
182 | int FFI_HIDDEN |
183 | ffi_prep_args_v8(ffi_cif *cif, unsigned long *argp, void *rvalue, void **avalue) | |
184 | { | |
185 | ffi_type **p_arg; | |
186 | int flags = cif->flags; | |
187 | int i, nargs; | |
3791773c | 188 | |
b1760f7f RH |
189 | if (rvalue == NULL) |
190 | { | |
191 | if ((flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT) | |
3791773c | 192 | { |
b1760f7f RH |
193 | /* Since we pass the pointer to the callee, we need a value. |
194 | We allowed for this space in ffi_call, before ffi_call_v8 | |
195 | alloca'd the space. */ | |
196 | rvalue = (char *)argp + cif->bytes; | |
3791773c JJ |
197 | } |
198 | else | |
199 | { | |
b1760f7f RH |
200 | /* Otherwise, we can ignore the return value. */ |
201 | flags = SPARC_RET_VOID; | |
3791773c | 202 | } |
3791773c JJ |
203 | } |
204 | ||
b1760f7f RH |
205 | /* This could only really be done when we are returning a structure. |
206 | However, the space is reserved so we can do it unconditionally. */ | |
207 | *argp++ = (unsigned long)rvalue; | |
248d745a EB |
208 | |
209 | #ifdef USING_PURIFY | |
b1760f7f RH |
210 | /* Purify will probably complain in our assembly routine, |
211 | unless we zero out this memory. */ | |
212 | memset(argp, 0, 6*4); | |
248d745a EB |
213 | #endif |
214 | ||
b1760f7f RH |
215 | p_arg = cif->arg_types; |
216 | for (i = 0, nargs = cif->nargs; i < nargs; i++) | |
248d745a | 217 | { |
b1760f7f RH |
218 | ffi_type *ty = p_arg[i]; |
219 | void *a = avalue[i]; | |
220 | int tt = ty->type; | |
248d745a EB |
221 | size_t z; |
222 | ||
b1760f7f RH |
223 | switch (tt) |
224 | { | |
225 | case FFI_TYPE_STRUCT: | |
226 | case FFI_TYPE_LONGDOUBLE: | |
227 | by_reference: | |
228 | *argp++ = (unsigned long)a; | |
229 | break; | |
230 | ||
231 | case FFI_TYPE_DOUBLE: | |
232 | case FFI_TYPE_UINT64: | |
233 | case FFI_TYPE_SINT64: | |
234 | memcpy(argp, a, 8); | |
235 | argp += 2; | |
236 | break; | |
237 | ||
238 | case FFI_TYPE_INT: | |
239 | case FFI_TYPE_FLOAT: | |
240 | case FFI_TYPE_UINT32: | |
241 | case FFI_TYPE_SINT32: | |
242 | case FFI_TYPE_POINTER: | |
243 | *argp++ = *(unsigned *)a; | |
244 | break; | |
245 | ||
246 | case FFI_TYPE_UINT8: | |
247 | *argp++ = *(UINT8 *)a; | |
248 | break; | |
249 | case FFI_TYPE_SINT8: | |
250 | *argp++ = *(SINT8 *)a; | |
251 | break; | |
252 | case FFI_TYPE_UINT16: | |
253 | *argp++ = *(UINT16 *)a; | |
254 | break; | |
255 | case FFI_TYPE_SINT16: | |
256 | *argp++ = *(SINT16 *)a; | |
257 | break; | |
258 | ||
259 | case FFI_TYPE_COMPLEX: | |
260 | tt = ty->elements[0]->type; | |
261 | z = ty->size; | |
262 | if (tt == FFI_TYPE_FLOAT || z > 8) | |
263 | goto by_reference; | |
264 | if (z < 4) | |
248d745a | 265 | { |
b1760f7f RH |
266 | memcpy((char *)argp + 4 - z, a, z); |
267 | argp++; | |
248d745a EB |
268 | } |
269 | else | |
270 | { | |
b1760f7f RH |
271 | memcpy(argp, a, z); |
272 | argp += z / 4; | |
248d745a | 273 | } |
b1760f7f | 274 | break; |
3791773c | 275 | |
b1760f7f RH |
276 | default: |
277 | abort(); | |
278 | } | |
3791773c | 279 | } |
63e5e3e0 | 280 | |
b1760f7f RH |
281 | return flags; |
282 | } | |
63e5e3e0 | 283 | |
b1760f7f RH |
284 | static void |
285 | ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue, | |
286 | void **avalue, void *closure) | |
287 | { | |
288 | size_t bytes = cif->bytes; | |
61aa380b RO |
289 | size_t i, nargs = cif->nargs; |
290 | ffi_type **arg_types = cif->arg_types; | |
63e5e3e0 | 291 | |
b1760f7f | 292 | FFI_ASSERT (cif->abi == FFI_V8); |
63e5e3e0 | 293 | |
b1760f7f RH |
294 | /* If we've not got a return value, we need to create one if we've |
295 | got to pass the return value to the callee. Otherwise ignore it. */ | |
296 | if (rvalue == NULL | |
297 | && (cif->flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT) | |
92456a4e | 298 | bytes += FFI_ALIGN (cif->rtype->size, 8); |
da17a98b | 299 | |
61aa380b RO |
300 | /* If we have any structure arguments, make a copy so we are passing |
301 | by value. */ | |
302 | for (i = 0; i < nargs; i++) | |
303 | { | |
304 | ffi_type *at = arg_types[i]; | |
305 | int size = at->size; | |
306 | if (at->type == FFI_TYPE_STRUCT) | |
307 | { | |
308 | char *argcopy = alloca (size); | |
309 | memcpy (argcopy, avalue[i], size); | |
310 | avalue[i] = argcopy; | |
311 | } | |
312 | } | |
313 | ||
b1760f7f | 314 | ffi_call_v8(cif, fn, rvalue, avalue, -bytes, closure); |
63e5e3e0 AG |
315 | } |
316 | ||
b1760f7f RH |
317 | void |
318 | ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) | |
30255340 | 319 | { |
b1760f7f | 320 | ffi_call_int (cif, fn, rvalue, avalue, NULL); |
30255340 EB |
321 | } |
322 | ||
b1760f7f RH |
323 | void |
324 | ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue, | |
325 | void **avalue, void *closure) | |
30255340 | 326 | { |
b1760f7f | 327 | ffi_call_int (cif, fn, rvalue, avalue, closure); |
30255340 EB |
328 | } |
329 | ||
b1760f7f RH |
330 | #ifdef __GNUC__ |
331 | static inline void | |
332 | ffi_flush_icache (void *p) | |
3791773c | 333 | { |
b1760f7f RH |
334 | /* SPARC v8 requires 5 instructions for flush to be visible */ |
335 | asm volatile ("iflush %0; iflush %0+8; nop; nop; nop; nop; nop" | |
336 | : : "r" (p) : "memory"); | |
3791773c | 337 | } |
3791773c | 338 | #else |
b1760f7f | 339 | extern void ffi_flush_icache (void *) FFI_HIDDEN; |
34fa7690 | 340 | #endif |
c75c7793 | 341 | |
b1760f7f RH |
342 | extern void ffi_closure_v8(void) FFI_HIDDEN; |
343 | extern void ffi_go_closure_v8(void) FFI_HIDDEN; | |
0ce78f01 | 344 | |
c75c7793 | 345 | ffi_status |
b1760f7f RH |
346 | ffi_prep_closure_loc (ffi_closure *closure, |
347 | ffi_cif *cif, | |
18fa3240 AO |
348 | void (*fun)(ffi_cif*, void*, void**, void*), |
349 | void *user_data, | |
350 | void *codeloc) | |
c75c7793 JS |
351 | { |
352 | unsigned int *tramp = (unsigned int *) &closure->tramp[0]; | |
b1760f7f RH |
353 | unsigned long ctx = (unsigned long) closure; |
354 | unsigned long fn = (unsigned long) ffi_closure_v8; | |
355 | ||
356 | if (cif->abi != FFI_V8) | |
34fa7690 | 357 | return FFI_BAD_ABI; |
b1760f7f | 358 | |
c75c7793 JS |
359 | tramp[0] = 0x03000000 | fn >> 10; /* sethi %hi(fn), %g1 */ |
360 | tramp[1] = 0x05000000 | ctx >> 10; /* sethi %hi(ctx), %g2 */ | |
361 | tramp[2] = 0x81c06000 | (fn & 0x3ff); /* jmp %g1+%lo(fn) */ | |
362 | tramp[3] = 0x8410a000 | (ctx & 0x3ff);/* or %g2, %lo(ctx) */ | |
c75c7793 JS |
363 | |
364 | closure->cif = cif; | |
365 | closure->fun = fun; | |
366 | closure->user_data = user_data; | |
367 | ||
b1760f7f | 368 | ffi_flush_icache (closure); |
c75c7793 JS |
369 | |
370 | return FFI_OK; | |
371 | } | |
372 | ||
b1760f7f RH |
373 | ffi_status |
374 | ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif, | |
375 | void (*fun)(ffi_cif*, void*, void**, void*)) | |
376 | { | |
377 | if (cif->abi != FFI_V8) | |
378 | return FFI_BAD_ABI; | |
0ce78f01 | 379 | |
b1760f7f RH |
380 | closure->tramp = ffi_go_closure_v8; |
381 | closure->cif = cif; | |
382 | closure->fun = fun; | |
383 | ||
384 | return FFI_OK; | |
385 | } | |
386 | ||
387 | int FFI_HIDDEN | |
388 | ffi_closure_sparc_inner_v8(ffi_cif *cif, | |
389 | void (*fun)(ffi_cif*, void*, void**, void*), | |
390 | void *user_data, void *rvalue, | |
391 | unsigned long *argp) | |
0ce78f01 | 392 | { |
0ce78f01 EB |
393 | ffi_type **arg_types; |
394 | void **avalue; | |
b1760f7f | 395 | int i, nargs, flags; |
0ce78f01 | 396 | |
0ce78f01 | 397 | arg_types = cif->arg_types; |
b1760f7f RH |
398 | nargs = cif->nargs; |
399 | flags = cif->flags; | |
400 | avalue = alloca(nargs * sizeof(void *)); | |
0ce78f01 EB |
401 | |
402 | /* Copy the caller's structure return address so that the closure | |
b1760f7f RH |
403 | returns the data directly to the caller. Also install it so we |
404 | can return the address in %o0. */ | |
405 | if ((flags & SPARC_FLAG_RET_MASK) == SPARC_RET_STRUCT) | |
c75c7793 | 406 | { |
b1760f7f RH |
407 | void *new_rvalue = (void *)*argp; |
408 | *(void **)rvalue = new_rvalue; | |
409 | rvalue = new_rvalue; | |
c75c7793 | 410 | } |
0ce78f01 | 411 | |
b1760f7f RH |
412 | /* Always skip the structure return address. */ |
413 | argp++; | |
c75c7793 | 414 | |
c75c7793 | 415 | /* Grab the addresses of the arguments from the stack frame. */ |
b1760f7f | 416 | for (i = 0; i < nargs; i++) |
c75c7793 | 417 | { |
b1760f7f RH |
418 | ffi_type *ty = arg_types[i]; |
419 | int tt = ty->type; | |
420 | void *a = argp; | |
421 | size_t z; | |
30255340 | 422 | |
b1760f7f | 423 | switch (tt) |
0ce78f01 | 424 | { |
b1760f7f RH |
425 | case FFI_TYPE_STRUCT: |
426 | case FFI_TYPE_LONGDOUBLE: | |
427 | by_reference: | |
428 | /* Straight copy of invisible reference. */ | |
429 | a = (void *)*argp; | |
430 | break; | |
431 | ||
432 | case FFI_TYPE_DOUBLE: | |
433 | case FFI_TYPE_SINT64: | |
434 | case FFI_TYPE_UINT64: | |
435 | if ((unsigned long)a & 7) | |
0ce78f01 | 436 | { |
b1760f7f RH |
437 | /* Align on a 8-byte boundary. */ |
438 | UINT64 *tmp = alloca(8); | |
439 | *tmp = ((UINT64)argp[0] << 32) | argp[1]; | |
440 | a = tmp; | |
0ce78f01 | 441 | } |
b1760f7f RH |
442 | argp++; |
443 | break; | |
248d745a | 444 | |
b1760f7f RH |
445 | case FFI_TYPE_INT: |
446 | case FFI_TYPE_FLOAT: | |
447 | case FFI_TYPE_UINT32: | |
448 | case FFI_TYPE_SINT32: | |
449 | case FFI_TYPE_POINTER: | |
450 | break; | |
451 | case FFI_TYPE_UINT16: | |
452 | case FFI_TYPE_SINT16: | |
453 | a += 2; | |
454 | break; | |
455 | case FFI_TYPE_UINT8: | |
456 | case FFI_TYPE_SINT8: | |
457 | a += 3; | |
458 | break; | |
248d745a | 459 | |
b1760f7f RH |
460 | case FFI_TYPE_COMPLEX: |
461 | tt = ty->elements[0]->type; | |
462 | z = ty->size; | |
463 | if (tt == FFI_TYPE_FLOAT || z > 8) | |
464 | goto by_reference; | |
465 | if (z < 4) | |
466 | a += 4 - z; | |
467 | else if (z > 4) | |
468 | argp++; | |
469 | break; | |
248d745a | 470 | |
b1760f7f RH |
471 | default: |
472 | abort(); | |
248d745a | 473 | } |
b1760f7f RH |
474 | argp++; |
475 | avalue[i] = a; | |
248d745a EB |
476 | } |
477 | ||
478 | /* Invoke the closure. */ | |
b1760f7f | 479 | fun (cif, rvalue, avalue, user_data); |
248d745a EB |
480 | |
481 | /* Tell ffi_closure_sparc how to perform return type promotions. */ | |
b1760f7f | 482 | return flags; |
248d745a | 483 | } |
b1760f7f | 484 | #endif /* !SPARC64 */ |