1 /* -----------------------------------------------------------------------
2 ffi.c - Copyright (c) 2003, 2004, 2006 Kaz Kojima
4 SuperH SHmedia Foreign Function Interface
6 Permission is hereby granted, free of charge, to any person obtaining
7 a copy of this software and associated documentation files (the
8 ``Software''), to deal in the Software without restriction, including
9 without limitation the rights to use, copy, modify, merge, publish,
10 distribute, sublicense, and/or sell copies of the Software, and to
11 permit persons to whom the Software is furnished to do so, subject to
12 the following conditions:
14 The above copyright notice and this permission notice shall be included
15 in all copies or substantial portions of the Software.
17 THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND, EXPRESS
18 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
19 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
20 IN NO EVENT SHALL CYGNUS SOLUTIONS BE LIABLE FOR ANY CLAIM, DAMAGES OR
21 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
22 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
23 OTHER DEALINGS IN THE SOFTWARE.
24 ----------------------------------------------------------------------- */
27 #include <ffi_common.h>
35 return_type (ffi_type
*arg
)
38 if (arg
->type
!= FFI_TYPE_STRUCT
)
41 /* gcc uses r2 if the result can be packed in on register. */
42 if (arg
->size
<= sizeof (UINT8
))
43 return FFI_TYPE_UINT8
;
44 else if (arg
->size
<= sizeof (UINT16
))
45 return FFI_TYPE_UINT16
;
46 else if (arg
->size
<= sizeof (UINT32
))
47 return FFI_TYPE_UINT32
;
48 else if (arg
->size
<= sizeof (UINT64
))
49 return FFI_TYPE_UINT64
;
51 return FFI_TYPE_STRUCT
;
54 /* ffi_prep_args is called by the assembly routine once stack space
55 has been allocated for the function's arguments */
57 void ffi_prep_args(char *stack
, extended_cif
*ecif
)
59 register unsigned int i
;
60 register unsigned int avn
;
61 register void **p_argv
;
63 register ffi_type
**p_arg
;
67 if (return_type (ecif
->cif
->rtype
) == FFI_TYPE_STRUCT
)
69 *(void **) argp
= ecif
->rvalue
;
70 argp
+= sizeof (UINT64
);
73 avn
= ecif
->cif
->nargs
;
74 p_argv
= ecif
->avalue
;
76 for (i
= 0, p_arg
= ecif
->cif
->arg_types
; i
< avn
; i
++, p_arg
++, p_argv
++)
82 align
= (*p_arg
)->alignment
;
83 if (z
< sizeof (UINT32
))
85 switch ((*p_arg
)->type
)
88 *(SINT64
*) argp
= (SINT64
) *(SINT8
*)(*p_argv
);
92 *(UINT64
*) argp
= (UINT64
) *(UINT8
*)(*p_argv
);
96 *(SINT64
*) argp
= (SINT64
) *(SINT16
*)(*p_argv
);
100 *(UINT64
*) argp
= (UINT64
) *(UINT16
*)(*p_argv
);
103 case FFI_TYPE_STRUCT
:
104 memcpy (argp
, *p_argv
, z
);
110 argp
+= sizeof (UINT64
);
112 else if (z
== sizeof (UINT32
) && align
== sizeof (UINT32
))
114 switch ((*p_arg
)->type
)
117 case FFI_TYPE_SINT32
:
118 *(SINT64
*) argp
= (SINT64
) *(SINT32
*) (*p_argv
);
122 case FFI_TYPE_POINTER
:
123 case FFI_TYPE_UINT32
:
124 case FFI_TYPE_STRUCT
:
125 *(UINT64
*) argp
= (UINT64
) *(UINT32
*) (*p_argv
);
132 argp
+= sizeof (UINT64
);
134 else if (z
== sizeof (UINT64
)
135 && align
== sizeof (UINT64
)
136 && ((int) *p_argv
& (sizeof (UINT64
) - 1)) == 0)
138 *(UINT64
*) argp
= *(UINT64
*) (*p_argv
);
139 argp
+= sizeof (UINT64
);
143 int n
= (z
+ sizeof (UINT64
) - 1) / sizeof (UINT64
);
145 memcpy (argp
, *p_argv
, z
);
146 argp
+= n
* sizeof (UINT64
);
153 /* Perform machine dependent cif processing */
154 ffi_status
ffi_prep_cif_machdep(ffi_cif
*cif
)
163 greg
= (return_type (cif
->rtype
) == FFI_TYPE_STRUCT
? 1 : 0);
167 for (i
= j
= 0; i
< cif
->nargs
; i
++)
169 type
= (cif
->arg_types
)[i
]->type
;
174 cif
->bytes
+= sizeof (UINT64
) - sizeof (float);
175 if (freg
>= NFREGARG
- 1)
184 cif
->flags2
+= ((cif
->arg_types
)[i
]->type
) << (2 * j
++);
187 case FFI_TYPE_DOUBLE
:
188 if (greg
++ >= NGREGARG
&& (freg
+ 1) >= NFREGARG
)
190 if ((freg
+ 1) < NFREGARG
)
193 cif
->flags2
+= ((cif
->arg_types
)[i
]->type
) << (2 * j
++);
196 cif
->flags2
+= FFI_TYPE_INT
<< (2 * j
++);
200 size
= (cif
->arg_types
)[i
]->size
;
201 if (size
< sizeof (UINT64
))
202 cif
->bytes
+= sizeof (UINT64
) - size
;
203 n
= (size
+ sizeof (UINT64
) - 1) / sizeof (UINT64
);
204 if (greg
>= NGREGARG
)
206 else if (greg
+ n
- 1 >= NGREGARG
)
210 for (m
= 0; m
< n
; m
++)
211 cif
->flags2
+= FFI_TYPE_INT
<< (2 * j
++);
216 /* Set the return type flag */
217 switch (cif
->rtype
->type
)
219 case FFI_TYPE_STRUCT
:
220 cif
->flags
= return_type (cif
->rtype
);
225 case FFI_TYPE_DOUBLE
:
226 case FFI_TYPE_SINT64
:
227 case FFI_TYPE_UINT64
:
228 cif
->flags
= cif
->rtype
->type
;
232 cif
->flags
= FFI_TYPE_INT
;
239 extern void ffi_call_SYSV(void (*)(char *, extended_cif
*), extended_cif
*,
240 unsigned, unsigned, long long, unsigned *,
243 void ffi_call(ffi_cif
*cif
, void (*fn
)(), void *rvalue
, void **avalue
)
249 ecif
.avalue
= avalue
;
251 /* If the return value is a struct and we don't have a return */
252 /* value address then we need to make one */
254 if (cif
->rtype
->type
== FFI_TYPE_STRUCT
255 && return_type (cif
->rtype
) != FFI_TYPE_STRUCT
)
256 ecif
.rvalue
= &trvalue
;
257 else if ((rvalue
== NULL
) &&
258 (cif
->rtype
->type
== FFI_TYPE_STRUCT
))
260 ecif
.rvalue
= alloca(cif
->rtype
->size
);
263 ecif
.rvalue
= rvalue
;
268 ffi_call_SYSV(ffi_prep_args
, &ecif
, cif
->bytes
, cif
->flags
, cif
->flags2
,
277 && cif
->rtype
->type
== FFI_TYPE_STRUCT
278 && return_type (cif
->rtype
) != FFI_TYPE_STRUCT
)
279 memcpy (rvalue
, &trvalue
, cif
->rtype
->size
);
282 extern void ffi_closure_SYSV (void);
283 extern void __ic_invalidate (void *line
);
286 ffi_prep_closure (ffi_closure
*closure
,
288 void (*fun
)(ffi_cif
*, void*, void**, void*),
293 FFI_ASSERT (cif
->abi
== FFI_GCC_SYSV
);
295 tramp
= (unsigned int *) &closure
->tramp
[0];
296 /* Since ffi_closure is an aligned object, the ffi trampoline is
297 called as an SHcompact code. Sigh.
299 mova @(1,pc),r0; add #1,r0; jmp @r0; nop;
301 movi fnaddr >> 16,r1; shori fnaddr,r1; ptabs/l r1,tr0
302 movi cxt >> 16,r1; shori cxt,r1; blink tr0,r63 */
303 #ifdef __LITTLE_ENDIAN__
304 tramp
[0] = 0x7001c701;
305 tramp
[1] = 0x0009402b;
307 tramp
[0] = 0xc7017001;
308 tramp
[1] = 0x402b0009;
310 tramp
[2] = 0xcc000010 | (((UINT32
) ffi_closure_SYSV
) >> 16) << 10;
311 tramp
[3] = 0xc8000010 | (((UINT32
) ffi_closure_SYSV
) & 0xffff) << 10;
312 tramp
[4] = 0x6bf10600;
313 tramp
[5] = 0xcc000010 | (((UINT32
) closure
) >> 16) << 10;
314 tramp
[6] = 0xc8000010 | (((UINT32
) closure
) & 0xffff) << 10;
315 tramp
[7] = 0x4401fff0;
319 closure
->user_data
= user_data
;
321 /* Flush the icache. */
322 asm volatile ("ocbwb %0,0; synco; icbi %0,0; synci" : : "r" (tramp
));
327 /* Basically the trampoline invokes ffi_closure_SYSV, and on
328 * entry, r3 holds the address of the closure.
329 * After storing the registers that could possibly contain
330 * parameters to be passed into the stack frame and setting
331 * up space for a return value, ffi_closure_SYSV invokes the
332 * following helper function to do most of the work.
336 ffi_closure_helper_SYSV (ffi_closure
*closure
, UINT64
*rvalue
,
337 UINT64
*pgr
, UINT64
*pfr
, UINT64
*pst
)
347 avalue
= alloca (cif
->nargs
* sizeof (void *));
349 /* Copy the caller's structure return value address so that the closure
350 returns the data directly to the caller. */
351 if (return_type (cif
->rtype
) == FFI_TYPE_STRUCT
)
353 rvalue
= (UINT64
*) *pgr
;
363 /* Grab the addresses of the arguments from the stack frame. */
364 for (i
= 0, p_arg
= cif
->arg_types
; i
< avn
; i
++, p_arg
++)
370 if (z
< sizeof (UINT32
))
374 switch ((*p_arg
)->type
)
378 case FFI_TYPE_SINT16
:
379 case FFI_TYPE_UINT16
:
380 case FFI_TYPE_STRUCT
:
381 #ifdef __LITTLE_ENDIAN__
384 avalue
[i
] = ((char *) p
) + sizeof (UINT32
) - z
;
392 else if (z
== sizeof (UINT32
))
394 if ((*p_arg
)->type
== FFI_TYPE_FLOAT
)
396 if (freg
< NFREGARG
- 1)
400 avalue
[i
] = (UINT32
*) pfr
+ fpair
;
405 #ifdef __LITTLE_ENDIAN__
407 avalue
[i
] = (UINT32
*) pfr
+ (1 ^ freg
);
410 avalue
[i
] = (UINT32
*) pfr
+ freg
;
416 #ifdef __LITTLE_ENDIAN__
417 avalue
[i
] = pgr
+ greg
;
419 avalue
[i
] = (UINT32
*) (pgr
+ greg
) + 1;
423 #ifdef __LITTLE_ENDIAN__
424 avalue
[i
] = pgr
+ greg
;
426 avalue
[i
] = (UINT32
*) (pgr
+ greg
) + 1;
430 else if ((*p_arg
)->type
== FFI_TYPE_DOUBLE
)
432 if (freg
+ 1 >= NFREGARG
)
433 avalue
[i
] = pgr
+ greg
;
436 avalue
[i
] = pfr
+ (freg
>> 1);
443 int n
= (z
+ sizeof (UINT64
) - 1) / sizeof (UINT64
);
445 avalue
[i
] = pgr
+ greg
;
450 (closure
->fun
) (cif
, rvalue
, avalue
, closure
->user_data
);
452 /* Tell ffi_closure_SYSV how to perform return type promotions. */
453 return return_type (cif
->rtype
);