#include <ffi.h>
#include <ffi_common.h>
#include <stdlib.h>
#if _MIPS_SIM == _ABIN32
#define FIX_ARGP \
FFI_ASSERT(argp <= &stack[bytes]); \
if (argp == &stack[bytes]) \
{ \
argp = stack; \
ffi_stop_here(); \
}
#else
#define FIX_ARGP
#endif
static void ffi_prep_args(char *stack,
extended_cif *ecif,
int bytes,
int flags)
{
register int i;
register void **p_argv;
register char *argp;
register ffi_type **p_arg;
#if _MIPS_SIM == _ABIN32
if (bytes > 8 * FFI_SIZEOF_ARG)
argp = &stack[bytes - (8 * FFI_SIZEOF_ARG)];
else
argp = stack;
#else
argp = stack;
#endif
memset(stack, 0, bytes);
#if _MIPS_SIM == _ABIN32
if ( ecif->cif->rstruct_flag != 0 )
#else
if ( ecif->cif->rtype->type == FFI_TYPE_STRUCT )
#endif
{
*(ffi_arg *) argp = (ffi_arg) ecif->rvalue;
argp += sizeof(ffi_arg);
FIX_ARGP;
}
p_argv = ecif->avalue;
for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; i; i--, p_arg++)
{
size_t z;
if (((*p_arg)->alignment - 1) & (unsigned) argp) {
argp = (char *) ALIGN(argp, (*p_arg)->alignment);
FIX_ARGP;
}
#if _MIPS_SIM == _ABIO32
#define OFFSET 0
#else
#define OFFSET sizeof(int)
#endif
z = (*p_arg)->size;
if (z < sizeof(ffi_arg))
{
z = sizeof(ffi_arg);
switch ((*p_arg)->type)
{
case FFI_TYPE_SINT8:
*(SINT32 *) &argp[OFFSET] = (SINT32)*(SINT8 *)(* p_argv);
break;
case FFI_TYPE_UINT8:
*(UINT32 *) &argp[OFFSET] = (UINT32)*(UINT8 *)(* p_argv);
break;
case FFI_TYPE_SINT16:
*(SINT32 *) &argp[OFFSET] = (SINT32)*(SINT16 *)(* p_argv);
break;
case FFI_TYPE_UINT16:
*(UINT32 *) &argp[OFFSET] = (UINT32)*(UINT16 *)(* p_argv);
break;
case FFI_TYPE_SINT32:
*(SINT32 *) &argp[OFFSET] = (SINT32)*(SINT32 *)(* p_argv);
break;
case FFI_TYPE_UINT32:
case FFI_TYPE_POINTER:
*(UINT32 *) &argp[OFFSET] = (UINT32)*(UINT32 *)(* p_argv);
break;
case FFI_TYPE_FLOAT:
*(float *) argp = *(float *)(* p_argv);
break;
case FFI_TYPE_STRUCT:
memcpy(argp, *p_argv, (*p_arg)->size);
break;
default:
FFI_ASSERT(0);
}
}
else
{
#if _MIPS_SIM == _ABIO32
memcpy(argp, *p_argv, z);
#else
{
unsigned end = (unsigned) argp+z;
unsigned cap = (unsigned) stack+bytes;
if (end <= cap)
memcpy(argp, *p_argv, z);
else
{
unsigned portion = end - cap;
memcpy(argp, *p_argv, portion);
argp = stack;
memcpy(argp,
(void*)((unsigned)(*p_argv)+portion), z - portion);
}
}
#endif
}
p_argv++;
argp += z;
FIX_ARGP;
}
return;
}
#if _MIPS_SIM == _ABIN32
unsigned calc_n32_struct_flags(ffi_type *arg, unsigned *shift)
{
unsigned flags = 0;
unsigned index = 0;
ffi_type *e;
while (e = arg->elements[index])
{
if (e->type == FFI_TYPE_DOUBLE)
{
flags += (FFI_TYPE_DOUBLE << *shift);
*shift += FFI_FLAG_BITS;
}
else if (e->type == FFI_TYPE_STRUCT)
flags += calc_n32_struct_flags(e, shift);
else
*shift += FFI_FLAG_BITS;
index++;
}
return flags;
}
unsigned calc_n32_return_struct_flags(ffi_type *arg)
{
unsigned flags = 0;
unsigned index = 0;
unsigned small = FFI_TYPE_SMALLSTRUCT;
ffi_type *e;
if (arg->size > 16)
return 0;
if (arg->size > 8)
small = FFI_TYPE_SMALLSTRUCT2;
e = arg->elements[0];
if (e->type == FFI_TYPE_DOUBLE)
flags = FFI_TYPE_DOUBLE << FFI_FLAG_BITS;
else if (e->type == FFI_TYPE_FLOAT)
flags = FFI_TYPE_FLOAT << FFI_FLAG_BITS;
if (flags && (e = arg->elements[1]))
{
if (e->type == FFI_TYPE_DOUBLE)
flags += FFI_TYPE_DOUBLE;
else if (e->type == FFI_TYPE_FLOAT)
flags += FFI_TYPE_FLOAT;
else
return small;
if (flags && (arg->elements[2]))
{
return small;
}
}
else
if (!flags)
return small;
return flags;
}
#endif
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
{
cif->flags = 0;
#if _MIPS_SIM == _ABIO32
if (cif->rtype->type != FFI_TYPE_STRUCT)
{
if (cif->nargs > 0)
{
switch ((cif->arg_types)[0]->type)
{
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
cif->flags += (cif->arg_types)[0]->type;
break;
default:
break;
}
if (cif->nargs > 1)
{
if (cif->flags)
{
switch ((cif->arg_types)[1]->type)
{
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
cif->flags += (cif->arg_types)[1]->type << FFI_FLAG_BITS;
break;
default:
break;
}
}
}
}
}
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
case FFI_TYPE_STRUCT:
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 2);
break;
default:
cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 2);
break;
}
#endif
#if _MIPS_SIM == _ABIN32
{
unsigned shift = 0;
unsigned count = (cif->nargs < 8) ? cif->nargs : 8;
unsigned index = 0;
unsigned struct_flags = 0;
if (cif->rtype->type == FFI_TYPE_STRUCT)
{
struct_flags = calc_n32_return_struct_flags(cif->rtype);
if (struct_flags == 0)
{
shift = FFI_FLAG_BITS;
count = (cif->nargs < 7) ? cif->nargs : 7;
cif->rstruct_flag = !0;
}
else
cif->rstruct_flag = 0;
}
else
cif->rstruct_flag = 0;
while (count-- > 0)
{
switch ((cif->arg_types)[index]->type)
{
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
cif->flags += ((cif->arg_types)[index]->type << shift);
shift += FFI_FLAG_BITS;
break;
case FFI_TYPE_STRUCT:
cif->flags += calc_n32_struct_flags((cif->arg_types)[index],
&shift);
break;
default:
shift += FFI_FLAG_BITS;
}
index++;
}
switch (cif->rtype->type)
{
case FFI_TYPE_STRUCT:
{
if (struct_flags == 0)
{
}
else
{
cif->flags += FFI_TYPE_STRUCT << (FFI_FLAG_BITS * 8);
cif->flags += struct_flags << (4 + (FFI_FLAG_BITS * 8));
}
break;
}
case FFI_TYPE_VOID:
break;
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
break;
default:
cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
break;
}
}
#endif
return FFI_OK;
}
extern int ffi_call_O32(void (*)(char *, extended_cif *, int, int),
extended_cif *, unsigned,
unsigned, unsigned *, void (*)());
extern int ffi_call_N32(void (*)(char *, extended_cif *, int, int),
extended_cif *, unsigned,
unsigned, unsigned *, void (*)());
void ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
{
extended_cif ecif;
ecif.cif = cif;
ecif.avalue = avalue;
if ((rvalue == NULL) &&
(cif->rtype->type == FFI_TYPE_STRUCT))
ecif.rvalue = alloca(cif->rtype->size);
else
ecif.rvalue = rvalue;
switch (cif->abi)
{
#if _MIPS_SIM == _ABIO32
case FFI_O32:
ffi_call_O32(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
break;
#endif
#if _MIPS_SIM == _ABIN32
case FFI_N32:
ffi_call_N32(ffi_prep_args, &ecif, cif->bytes,
cif->flags, ecif.rvalue, fn);
break;
#endif
default:
FFI_ASSERT(0);
break;
}
}