#include <ffi.h>
#include <ffi_common.h>
#include <stdlib.h>
#include "ia64_flags.h"
typedef double float80;
struct ia64_args {
long scratch[2];
void * r8_contents;
long spare;
float80 fp_regs[8];
long out_regs[8];
long other_args[0];
};
static size_t float_type_size(unsigned short tp)
{
switch(tp) {
case FFI_TYPE_FLOAT:
return sizeof(float);
case FFI_TYPE_DOUBLE:
return sizeof(double);
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
return sizeof(long double);
#endif
default:
FFI_ASSERT(0);
}
}
static bool is_homogeneous_fp_aggregate(ffi_type * type, int n,
unsigned short * element_type)
{
ffi_type **ptr;
unsigned short element, struct_element;
int type_set = 0;
FFI_ASSERT(type != NULL);
FFI_ASSERT(type->elements != NULL);
ptr = &(type->elements[0]);
while ((*ptr) != NULL)
{
switch((*ptr) -> type) {
case FFI_TYPE_FLOAT:
if (type_set && element != FFI_TYPE_FLOAT) return 0;
if (--n < 0) return FALSE;
type_set = 1;
element = FFI_TYPE_FLOAT;
break;
case FFI_TYPE_DOUBLE:
if (type_set && element != FFI_TYPE_DOUBLE) return 0;
if (--n < 0) return FALSE;
type_set = 1;
element = FFI_TYPE_DOUBLE;
break;
case FFI_TYPE_STRUCT:
if (!is_homogeneous_fp_aggregate(type, n, &struct_element))
return FALSE;
if (type_set && struct_element != element) return FALSE;
n -= (type -> size)/float_type_size(element);
element = struct_element;
if (n < 0) return FALSE;
break;
default:
return FALSE;
}
ptr++;
}
*element_type = element;
return TRUE;
}
static bool
ffi_prep_args(struct ia64_args *stack, extended_cif *ecif, int bytes)
{
register long i, avn;
register void **p_argv;
register long *argp = stack -> out_regs;
register float80 *fp_argp = stack -> fp_regs;
register ffi_type **p_arg;
stack -> r8_contents = ecif -> rvalue;
i = 0;
avn = ecif->cif->nargs;
p_arg = ecif->cif->arg_types;
p_argv = ecif->avalue;
while (i < avn)
{
size_t z;
switch ((*p_arg)->type)
{
case FFI_TYPE_SINT8:
z = 1;
*(SINT64 *) argp = *(SINT8 *)(* p_argv);
break;
case FFI_TYPE_UINT8:
z = 1;
*(UINT64 *) argp = *(UINT8 *)(* p_argv);
break;
case FFI_TYPE_SINT16:
z = 1;
*(SINT64 *) argp = *(SINT16 *)(* p_argv);
break;
case FFI_TYPE_UINT16:
z = 1;
*(UINT64 *) argp = *(UINT16 *)(* p_argv);
break;
case FFI_TYPE_SINT32:
z = 1;
*(SINT64 *) argp = *(SINT32 *)(* p_argv);
break;
case FFI_TYPE_UINT32:
z = 1;
*(UINT64 *) argp = *(UINT32 *)(* p_argv);
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
z = 1;
*(UINT64 *) argp = *(UINT64 *)(* p_argv);
break;
case FFI_TYPE_FLOAT:
z = 1;
if (fp_argp - stack->fp_regs < 8)
{
*fp_argp++ = *(float *)(* p_argv);
}
*(UINT64 *) argp = *(UINT32 *)(* p_argv);
break;
case FFI_TYPE_DOUBLE:
z = 1;
if (fp_argp - stack->fp_regs < 8)
*fp_argp++ = *(double *)(* p_argv);
*(double *) argp = *(double *)(* p_argv);
break;
case FFI_TYPE_STRUCT:
{
size_t sz = (*p_arg)->size;
unsigned short element_type;
z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
int i;
int nelements = sz/float_type_size(element_type);
for (i = 0; i < nelements; ++i) {
switch (element_type) {
case FFI_TYPE_FLOAT:
if (fp_argp - stack->fp_regs < 8)
*fp_argp++ = ((float *)(* p_argv))[i];
break;
case FFI_TYPE_DOUBLE:
if (fp_argp - stack->fp_regs < 8)
*fp_argp++ = ((double *)(* p_argv))[i];
break;
default:
abort();
}
}
}
memcpy(argp, *p_argv, (*p_arg)->size);
}
break;
default:
FFI_ASSERT(0);
}
argp += z;
i++, p_arg++, p_argv++;
}
return (fp_argp != stack -> fp_regs);
}
ffi_status
ffi_prep_cif_machdep(ffi_cif *cif)
{
long i, avn;
bool is_simple = TRUE;
long simple_flag = FFI_SIMPLE_V;
cif->bytes += 4*sizeof(long) + 8 *sizeof(float80);
if (cif->bytes < sizeof(struct ia64_args))
cif->bytes = sizeof(struct ia64_args);
cif->bytes = ALIGN(cif->bytes, 2*sizeof(void*));
avn = cif->nargs;
if (avn <= 2) {
for (i = 0; i < avn; ++i) {
switch(cif -> arg_types[i] -> type) {
case FFI_TYPE_SINT32:
simple_flag = FFI_ADD_INT_ARG(simple_flag);
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
simple_flag = FFI_ADD_LONG_ARG(simple_flag);
break;
default:
is_simple = FALSE;
}
}
} else {
is_simple = FALSE;
}
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
cif->flags = FFI_TYPE_VOID;
break;
case FFI_TYPE_STRUCT:
{
size_t sz = cif -> rtype -> size;
unsigned short element_type;
is_simple = FALSE;
if (is_homogeneous_fp_aggregate(cif -> rtype, 8, &element_type)) {
int nelements = sz/float_type_size(element_type);
if (nelements <= 1) {
if (0 == nelements) {
cif -> flags = FFI_TYPE_VOID;
} else {
cif -> flags = element_type;
}
} else {
switch(element_type) {
case FFI_TYPE_FLOAT:
cif -> flags = FFI_IS_FLOAT_FP_AGGREGATE | nelements;
break;
case FFI_TYPE_DOUBLE:
cif -> flags = FFI_IS_DOUBLE_FP_AGGREGATE | nelements;
break;
default:
abort();
}
}
break;
}
if (sz <= 32) {
if (sz <= 8) {
cif->flags = FFI_TYPE_INT;
} else if (sz <= 16) {
cif->flags = FFI_IS_SMALL_STRUCT2;
} else if (sz <= 24) {
cif->flags = FFI_IS_SMALL_STRUCT3;
} else {
cif->flags = FFI_IS_SMALL_STRUCT4;
}
} else {
cif->flags = FFI_TYPE_STRUCT;
}
}
break;
case FFI_TYPE_FLOAT:
is_simple = FALSE;
cif->flags = FFI_TYPE_FLOAT;
break;
case FFI_TYPE_DOUBLE:
is_simple = FALSE;
cif->flags = FFI_TYPE_DOUBLE;
break;
default:
cif->flags = FFI_TYPE_INT;
break;
}
if (is_simple) cif -> flags |= simple_flag;
return FFI_OK;
}
extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int),
extended_cif *, unsigned,
unsigned, unsigned *, void (*)());
void
ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue)
{
extended_cif ecif;
long simple = cif -> flags & FFI_SIMPLE;
if (simple) {
long (*lfn)() = (long (*)())fn;
long result;
switch(simple) {
case FFI_SIMPLE_V:
result = lfn();
break;
case FFI_SIMPLE_I:
result = lfn(*(int *)avalue[0]);
break;
case FFI_SIMPLE_L:
result = lfn(*(long *)avalue[0]);
break;
case FFI_SIMPLE_II:
result = lfn(*(int *)avalue[0], *(int *)avalue[1]);
break;
case FFI_SIMPLE_IL:
result = lfn(*(int *)avalue[0], *(long *)avalue[1]);
break;
case FFI_SIMPLE_LI:
result = lfn(*(long *)avalue[0], *(int *)avalue[1]);
break;
case FFI_SIMPLE_LL:
result = lfn(*(long *)avalue[0], *(long *)avalue[1]);
break;
}
if ((cif->flags & ~FFI_SIMPLE) != FFI_TYPE_VOID && 0 != rvalue) {
* (long *)rvalue = result;
}
return;
}
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)
{
case FFI_UNIX:
ffi_call_unix(ffi_prep_args, &ecif, cif->bytes,
cif->flags, rvalue, fn);
break;
default:
FFI_ASSERT(0);
break;
}
}
static void
ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
void **avalue, ffi_cif *cif);
void ffi_closure_UNIX();
#ifndef __GNUC__
# error This requires gcc
#endif
void
ffi_closure_UNIX_inner (ffi_closure *closure, struct ia64_args * args)
{
long double res;
ffi_cif *cif;
unsigned short rtype;
void *resp;
void **arg_area;
resp = (void*)&res;
cif = closure->cif;
arg_area = (void**) alloca (cif->nargs * sizeof (void*));
ffi_prep_incoming_args_UNIX(args, (void**)&resp, arg_area, cif);
(closure->fun) (cif, resp, arg_area, closure->user_data);
rtype = cif->flags;
if (rtype == FFI_TYPE_INT)
{
asm volatile ("ld8 r8=[%0]" : : "r" (resp) : "r8");
}
else if (rtype == FFI_TYPE_FLOAT)
{
asm volatile ("ldfs f8=[%0]" : : "r" (resp) : "f8");
}
else if (rtype == FFI_TYPE_DOUBLE)
{
asm volatile ("ldfd f8=[%0]" : : "r" (resp) : "f8");
}
else if (rtype == FFI_IS_SMALL_STRUCT2)
{
asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]"
: : "r" (resp), "r" (resp+8) : "r8","r9");
}
else if (rtype == FFI_IS_SMALL_STRUCT3)
{
asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]"
: : "r" (resp), "r" (resp+8), "r" (resp+16)
: "r8","r9","r10");
}
else if (rtype == FFI_IS_SMALL_STRUCT4)
{
asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]; ld8 r11=[%3]"
: : "r" (resp), "r" (resp+8), "r" (resp+16), "r" (resp+24)
: "r8","r9","r10","r11");
}
else if (rtype != FFI_TYPE_VOID && rtype != FFI_TYPE_STRUCT)
{
abort();
}
}
static void
ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue,
void **avalue, ffi_cif *cif)
{
register unsigned int i;
register unsigned int avn;
register void **p_argv;
register unsigned long *argp = args -> out_regs;
unsigned fp_reg_num = 0;
register ffi_type **p_arg;
avn = cif->nargs;
p_argv = avalue;
for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++)
{
size_t z;
switch ((*p_arg)->type)
{
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT8:
case FFI_TYPE_SINT16:
case FFI_TYPE_UINT16:
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_POINTER:
z = 1;
*p_argv = (void *)argp;
break;
case FFI_TYPE_FLOAT:
z = 1;
if (fp_reg_num < 8) {
*(float *)argp = args -> fp_regs[fp_reg_num++];
} else {
*(float *)argp = *(double *)argp;
}
*p_argv = (void *)argp;
break;
case FFI_TYPE_DOUBLE:
z = 1;
if (fp_reg_num < 8) {
*p_argv = args -> fp_regs + fp_reg_num++;
} else {
*p_argv = (void *)argp;
}
break;
case FFI_TYPE_STRUCT:
{
size_t sz = (*p_arg)->size;
unsigned short element_type;
z = ((*p_arg)->size + SIZEOF_ARG - 1)/SIZEOF_ARG;
if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) {
int nelements = sz/float_type_size(element_type);
if (nelements + fp_reg_num >= 8) {
abort();
}
if (element_type == FFI_TYPE_DOUBLE) {
*p_argv = args -> fp_regs + fp_reg_num;
fp_reg_num += nelements;
break;
}
if (element_type == FFI_TYPE_FLOAT) {
int j;
for (j = 0; j < nelements; ++ j) {
((float *)argp)[j] = args -> fp_regs[fp_reg_num + j];
}
*p_argv = (void *)argp;
fp_reg_num += nelements;
break;
}
abort();
}
}
break;
default:
FFI_ASSERT(0);
}
argp += z;
p_argv++;
}
return;
}
typedef struct ia64_fd_struct {
void *code_pointer;
void *gp;
} ia64_fd;
ffi_status
ffi_prep_closure (ffi_closure* closure,
ffi_cif* cif,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data)
{
struct ffi_ia64_trampoline_struct *tramp =
(struct ffi_ia64_trampoline_struct *) (closure -> tramp);
ia64_fd *fd = (ia64_fd *)(void *)ffi_closure_UNIX;
FFI_ASSERT (cif->abi == FFI_UNIX);
tramp -> code_pointer = fd -> code_pointer;
tramp -> real_gp = fd -> gp;
tramp -> fake_gp = closure;
closure->cif = cif;
closure->user_data = user_data;
closure->fun = fun;
return FFI_OK;
}