Rebase
This commit is contained in:
231
src/arm/ffi.c
231
src/arm/ffi.c
@@ -29,12 +29,20 @@
|
||||
|
||||
#include <stdlib.h>
|
||||
|
||||
/* ffi_prep_args is called by the assembly routine once stack space
|
||||
has been allocated for the function's arguments */
|
||||
/* Forward declares. */
|
||||
static int vfp_type_p (ffi_type *);
|
||||
static void layout_vfp_args (ffi_cif *);
|
||||
|
||||
void ffi_prep_args(char *stack, extended_cif *ecif)
|
||||
/* ffi_prep_args is called by the assembly routine once stack space
|
||||
has been allocated for the function's arguments
|
||||
|
||||
The vfp_space parameter is the load area for VFP regs, the return
|
||||
value is cif->vfp_used (word bitset of VFP regs used for passing
|
||||
arguments). These are only used for the VFP hard-float ABI.
|
||||
*/
|
||||
int ffi_prep_args(char *stack, extended_cif *ecif, float *vfp_space)
|
||||
{
|
||||
register unsigned int i;
|
||||
register unsigned int i, vi = 0;
|
||||
register void **p_argv;
|
||||
register char *argp;
|
||||
register ffi_type **p_arg;
|
||||
@@ -54,6 +62,21 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
|
||||
{
|
||||
size_t z;
|
||||
|
||||
/* Allocated in VFP registers. */
|
||||
if (ecif->cif->abi == FFI_VFP
|
||||
&& vi < ecif->cif->vfp_nargs && vfp_type_p (*p_arg))
|
||||
{
|
||||
float* vfp_slot = vfp_space + ecif->cif->vfp_args[vi++];
|
||||
if ((*p_arg)->type == FFI_TYPE_FLOAT)
|
||||
*((float*)vfp_slot) = *((float*)*p_argv);
|
||||
else if ((*p_arg)->type == FFI_TYPE_DOUBLE)
|
||||
*((double*)vfp_slot) = *((double*)*p_argv);
|
||||
else
|
||||
memcpy(vfp_slot, *p_argv, (*p_arg)->size);
|
||||
p_argv++;
|
||||
continue;
|
||||
}
|
||||
|
||||
/* Align if necessary */
|
||||
if (((*p_arg)->alignment - 1) & (unsigned) argp) {
|
||||
argp = (char *) ALIGN(argp, (*p_arg)->alignment);
|
||||
@@ -103,13 +126,15 @@ void ffi_prep_args(char *stack, extended_cif *ecif)
|
||||
p_argv++;
|
||||
argp += z;
|
||||
}
|
||||
|
||||
return;
|
||||
|
||||
/* Indicate the VFP registers used. */
|
||||
return ecif->cif->vfp_used;
|
||||
}
|
||||
|
||||
/* Perform machine dependent cif processing */
|
||||
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
{
|
||||
int type_code;
|
||||
/* Round the stack up to a multiple of 8 bytes. This isn't needed
|
||||
everywhere, but it is on some platforms, and it doesn't harm anything
|
||||
when it isn't needed. */
|
||||
@@ -130,7 +155,14 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
break;
|
||||
|
||||
case FFI_TYPE_STRUCT:
|
||||
if (cif->rtype->size <= 4)
|
||||
if (cif->abi == FFI_VFP
|
||||
&& (type_code = vfp_type_p (cif->rtype)) != 0)
|
||||
{
|
||||
/* A Composite Type passed in VFP registers, either
|
||||
FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */
|
||||
cif->flags = (unsigned) type_code;
|
||||
}
|
||||
else if (cif->rtype->size <= 4)
|
||||
/* A Composite Type not larger than 4 bytes is returned in r0. */
|
||||
cif->flags = (unsigned)FFI_TYPE_INT;
|
||||
else
|
||||
@@ -145,11 +177,18 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
break;
|
||||
}
|
||||
|
||||
/* Map out the register placements of VFP register args.
|
||||
The VFP hard-float calling conventions are slightly more sophisticated than
|
||||
the base calling conventions, so we do it here instead of in ffi_prep_args(). */
|
||||
if (cif->abi == FFI_VFP)
|
||||
layout_vfp_args (cif);
|
||||
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *,
|
||||
unsigned, unsigned, unsigned *, void (*fn)(void));
|
||||
/* Prototypes for assembly functions, in sysv.S */
|
||||
extern void ffi_call_SYSV (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *);
|
||||
extern void ffi_call_VFP (void (*fn)(void), extended_cif *, unsigned, unsigned, unsigned *);
|
||||
|
||||
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
{
|
||||
@@ -157,6 +196,8 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
|
||||
int small_struct = (cif->flags == FFI_TYPE_INT
|
||||
&& cif->rtype->type == FFI_TYPE_STRUCT);
|
||||
int vfp_struct = (cif->flags == FFI_TYPE_STRUCT_VFP_FLOAT
|
||||
|| cif->flags == FFI_TYPE_STRUCT_VFP_DOUBLE);
|
||||
|
||||
ecif.cif = cif;
|
||||
ecif.avalue = avalue;
|
||||
@@ -173,38 +214,51 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
}
|
||||
else if (small_struct)
|
||||
ecif.rvalue = &temp;
|
||||
else if (vfp_struct)
|
||||
{
|
||||
/* Largest case is double x 4. */
|
||||
ecif.rvalue = alloca(32);
|
||||
}
|
||||
else
|
||||
ecif.rvalue = rvalue;
|
||||
|
||||
switch (cif->abi)
|
||||
{
|
||||
case FFI_SYSV:
|
||||
ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue,
|
||||
fn);
|
||||
|
||||
ffi_call_SYSV (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
|
||||
break;
|
||||
|
||||
case FFI_VFP:
|
||||
ffi_call_VFP (fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
|
||||
break;
|
||||
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
break;
|
||||
}
|
||||
if (small_struct)
|
||||
memcpy (rvalue, &temp, cif->rtype->size);
|
||||
else if (vfp_struct)
|
||||
memcpy (rvalue, ecif.rvalue, cif->rtype->size);
|
||||
}
|
||||
|
||||
/** private members **/
|
||||
|
||||
static void ffi_prep_incoming_args_SYSV (char *stack, void **ret,
|
||||
void** args, ffi_cif* cif);
|
||||
void** args, ffi_cif* cif, float *vfp_stack);
|
||||
|
||||
void ffi_closure_SYSV (ffi_closure *);
|
||||
|
||||
void ffi_closure_VFP (ffi_closure *);
|
||||
|
||||
/* This function is jumped to by the trampoline */
|
||||
|
||||
unsigned int
|
||||
ffi_closure_SYSV_inner (closure, respp, args)
|
||||
ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
|
||||
ffi_closure *closure;
|
||||
void **respp;
|
||||
void *args;
|
||||
void *vfp_args;
|
||||
{
|
||||
// our various things...
|
||||
ffi_cif *cif;
|
||||
@@ -219,7 +273,7 @@ ffi_closure_SYSV_inner (closure, respp, args)
|
||||
* a structure, it will re-set RESP to point to the
|
||||
* structure return address. */
|
||||
|
||||
ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif);
|
||||
ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif, vfp_args);
|
||||
|
||||
(closure->fun) (cif, *respp, arg_area, closure->user_data);
|
||||
|
||||
@@ -229,10 +283,12 @@ ffi_closure_SYSV_inner (closure, respp, args)
|
||||
/*@-exportheader@*/
|
||||
static void
|
||||
ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
|
||||
void **avalue, ffi_cif *cif)
|
||||
void **avalue, ffi_cif *cif,
|
||||
/* Used only under VFP hard-float ABI. */
|
||||
float *vfp_stack)
|
||||
/*@=exportheader@*/
|
||||
{
|
||||
register unsigned int i;
|
||||
register unsigned int i, vi = 0;
|
||||
register void **p_argv;
|
||||
register char *argp;
|
||||
register ffi_type **p_arg;
|
||||
@@ -249,8 +305,16 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
|
||||
for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
|
||||
{
|
||||
size_t z;
|
||||
size_t alignment;
|
||||
|
||||
if (cif->abi == FFI_VFP
|
||||
&& vi < cif->vfp_nargs && vfp_type_p (*p_arg))
|
||||
{
|
||||
*p_argv++ = (void*)(vfp_stack + cif->vfp_args[vi++]);
|
||||
continue;
|
||||
}
|
||||
|
||||
size_t alignment = (*p_arg)->alignment;
|
||||
alignment = (*p_arg)->alignment;
|
||||
if (alignment < 4)
|
||||
alignment = 4;
|
||||
/* Align if necessary */
|
||||
@@ -295,10 +359,17 @@ ffi_prep_closure_loc (ffi_closure* closure,
|
||||
void *user_data,
|
||||
void *codeloc)
|
||||
{
|
||||
FFI_ASSERT (cif->abi == FFI_SYSV);
|
||||
void (*closure_func)(ffi_closure*) = NULL;
|
||||
|
||||
if (cif->abi == FFI_SYSV)
|
||||
closure_func = &ffi_closure_SYSV;
|
||||
else if (cif->abi == FFI_VFP)
|
||||
closure_func = &ffi_closure_VFP;
|
||||
else
|
||||
FFI_ASSERT (0);
|
||||
|
||||
FFI_INIT_TRAMPOLINE (&closure->tramp[0], \
|
||||
&ffi_closure_SYSV, \
|
||||
closure_func, \
|
||||
codeloc);
|
||||
|
||||
closure->cif = cif;
|
||||
@@ -307,3 +378,123 @@ ffi_prep_closure_loc (ffi_closure* closure,
|
||||
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
/* Below are routines for VFP hard-float support. */
|
||||
|
||||
static int rec_vfp_type_p (ffi_type *t, int *elt, int *elnum)
|
||||
{
|
||||
switch (t->type)
|
||||
{
|
||||
case FFI_TYPE_FLOAT:
|
||||
case FFI_TYPE_DOUBLE:
|
||||
*elt = (int) t->type;
|
||||
*elnum = 1;
|
||||
return 1;
|
||||
|
||||
case FFI_TYPE_STRUCT_VFP_FLOAT:
|
||||
*elt = FFI_TYPE_FLOAT;
|
||||
*elnum = t->size / sizeof (float);
|
||||
return 1;
|
||||
|
||||
case FFI_TYPE_STRUCT_VFP_DOUBLE:
|
||||
*elt = FFI_TYPE_DOUBLE;
|
||||
*elnum = t->size / sizeof (double);
|
||||
return 1;
|
||||
|
||||
case FFI_TYPE_STRUCT:;
|
||||
{
|
||||
int base_elt = 0, total_elnum = 0;
|
||||
ffi_type **el = t->elements;
|
||||
while (*el)
|
||||
{
|
||||
int el_elt = 0, el_elnum = 0;
|
||||
if (! rec_vfp_type_p (*el, &el_elt, &el_elnum)
|
||||
|| (base_elt && base_elt != el_elt)
|
||||
|| total_elnum + el_elnum > 4)
|
||||
return 0;
|
||||
base_elt = el_elt;
|
||||
total_elnum += el_elnum;
|
||||
el++;
|
||||
}
|
||||
*elnum = total_elnum;
|
||||
*elt = base_elt;
|
||||
return 1;
|
||||
}
|
||||
default: ;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int vfp_type_p (ffi_type *t)
|
||||
{
|
||||
int elt, elnum;
|
||||
if (rec_vfp_type_p (t, &elt, &elnum))
|
||||
{
|
||||
if (t->type == FFI_TYPE_STRUCT)
|
||||
{
|
||||
if (elnum == 1)
|
||||
t->type = elt;
|
||||
else
|
||||
t->type = (elt == FFI_TYPE_FLOAT
|
||||
? FFI_TYPE_STRUCT_VFP_FLOAT
|
||||
: FFI_TYPE_STRUCT_VFP_DOUBLE);
|
||||
}
|
||||
return (int) t->type;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void place_vfp_arg (ffi_cif *cif, ffi_type *t)
|
||||
{
|
||||
int reg = cif->vfp_reg_free;
|
||||
int nregs = t->size / sizeof (float);
|
||||
int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT
|
||||
|| t->type == FFI_TYPE_FLOAT) ? 1 : 2);
|
||||
/* Align register number. */
|
||||
if ((reg & 1) && align == 2)
|
||||
reg++;
|
||||
while (reg + nregs <= 16)
|
||||
{
|
||||
int s, new_used = 0;
|
||||
for (s = reg; s < reg + nregs; s++)
|
||||
{
|
||||
new_used |= (1 << s);
|
||||
if (cif->vfp_used & (1 << s))
|
||||
{
|
||||
reg += align;
|
||||
goto next_reg;
|
||||
}
|
||||
}
|
||||
/* Found regs to allocate. */
|
||||
cif->vfp_used |= new_used;
|
||||
cif->vfp_args[cif->vfp_nargs++] = reg;
|
||||
|
||||
/* Update vfp_reg_free. */
|
||||
if (cif->vfp_used & (1 << cif->vfp_reg_free))
|
||||
{
|
||||
reg += nregs;
|
||||
while (cif->vfp_used & (1 << reg))
|
||||
reg += 1;
|
||||
cif->vfp_reg_free = reg;
|
||||
}
|
||||
return;
|
||||
next_reg: ;
|
||||
}
|
||||
}
|
||||
|
||||
static void layout_vfp_args (ffi_cif *cif)
|
||||
{
|
||||
int i;
|
||||
/* Init VFP fields */
|
||||
cif->vfp_used = 0;
|
||||
cif->vfp_nargs = 0;
|
||||
cif->vfp_reg_free = 0;
|
||||
memset (cif->vfp_args, -1, 16); /* Init to -1. */
|
||||
|
||||
for (i = 0; i < cif->nargs; i++)
|
||||
{
|
||||
ffi_type *t = cif->arg_types[i];
|
||||
if (vfp_type_p (t))
|
||||
place_vfp_arg (cif, t);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user