#include "ffi.h"
#ifndef POWERPC64
#include "ffi_common.h"
#include "ffi_powerpc.h"
#define ASM_NEEDS_REGISTERS 6
#define NUM_GPR_ARG_REGISTERS 8
#define NUM_FPR_ARG_REGISTERS 8
#if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
void FFI_HIDDEN
ffi_prep_types_sysv (ffi_abi abi)
{
if ((abi & (FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128)) == FFI_SYSV)
{
ffi_type_longdouble.size = 8;
ffi_type_longdouble.alignment = 8;
}
else
{
ffi_type_longdouble.size = 16;
ffi_type_longdouble.alignment = 16;
}
}
#endif
static int
translate_float (int abi, int type)
{
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
if (type == FFI_TYPE_LONGDOUBLE
&& (abi & FFI_SYSV_LONG_DOUBLE_128) == 0)
type = FFI_TYPE_DOUBLE;
#endif
if ((abi & FFI_SYSV_SOFT_FLOAT) != 0)
{
if (type == FFI_TYPE_FLOAT)
type = FFI_TYPE_UINT32;
else if (type == FFI_TYPE_DOUBLE)
type = FFI_TYPE_UINT64;
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
else if (type == FFI_TYPE_LONGDOUBLE)
type = FFI_TYPE_UINT128;
}
else if ((abi & FFI_SYSV_IBM_LONG_DOUBLE) == 0)
{
if (type == FFI_TYPE_LONGDOUBLE)
type = FFI_TYPE_STRUCT;
#endif
}
return type;
}
static ffi_status
ffi_prep_cif_sysv_core (ffi_cif *cif)
{
ffi_type **ptr;
unsigned bytes;
unsigned i, fpr_count = 0, gpr_count = 0, stack_count = 0;
unsigned flags = cif->flags;
unsigned struct_copy_size = 0;
unsigned type = cif->rtype->type;
unsigned size = cif->rtype->size;
bytes = (2 + ASM_NEEDS_REGISTERS) * sizeof (int);
bytes += NUM_GPR_ARG_REGISTERS * sizeof (int);
type = translate_float (cif->abi, type);
switch (type)
{
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
flags |= FLAG_RETURNS_128BITS;
#endif
case FFI_TYPE_DOUBLE:
flags |= FLAG_RETURNS_64BITS;
case FFI_TYPE_FLOAT:
flags |= FLAG_RETURNS_FP;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
case FFI_TYPE_UINT128:
flags |= FLAG_RETURNS_128BITS;
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
flags |= FLAG_RETURNS_64BITS;
break;
case FFI_TYPE_STRUCT:
if ((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
{
flags |= FLAG_RETURNS_SMST;
break;
}
gpr_count++;
flags |= FLAG_RETVAL_REFERENCE;
case FFI_TYPE_VOID:
flags |= FLAG_RETURNS_NOTHING;
break;
default:
break;
}
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
{
unsigned short typenum = (*ptr)->type;
typenum = translate_float (cif->abi, typenum);
switch (typenum)
{
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
if (fpr_count >= NUM_FPR_ARG_REGISTERS - 1)
{
fpr_count = NUM_FPR_ARG_REGISTERS;
stack_count += stack_count & 1;
stack_count += 4;
}
else
fpr_count += 2;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
#endif
case FFI_TYPE_DOUBLE:
if (fpr_count >= NUM_FPR_ARG_REGISTERS)
{
stack_count += stack_count & 1;
stack_count += 2;
}
else
fpr_count += 1;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
case FFI_TYPE_FLOAT:
if (fpr_count >= NUM_FPR_ARG_REGISTERS)
stack_count += 1;
else
fpr_count += 1;
#ifdef __NO_FPRS__
return FFI_BAD_ABI;
#endif
break;
case FFI_TYPE_UINT128:
if (gpr_count >= NUM_GPR_ARG_REGISTERS - 3)
gpr_count = NUM_GPR_ARG_REGISTERS;
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
stack_count += 4;
else
gpr_count += 4;
break;
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
gpr_count += gpr_count & 1;
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
{
stack_count += stack_count & 1;
stack_count += 2;
}
else
gpr_count += 2;
break;
case FFI_TYPE_STRUCT:
struct_copy_size += ((*ptr)->size + 15) & ~0xF;
case FFI_TYPE_POINTER:
case FFI_TYPE_INT:
case FFI_TYPE_UINT32:
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT16:
case FFI_TYPE_SINT16:
case FFI_TYPE_UINT8:
case FFI_TYPE_SINT8:
if (gpr_count >= NUM_GPR_ARG_REGISTERS)
stack_count += 1;
else
gpr_count += 1;
break;
default:
FFI_ASSERT (0);
}
}
if (fpr_count != 0)
flags |= FLAG_FP_ARGUMENTS;
if (gpr_count > 4)
flags |= FLAG_4_GPR_ARGUMENTS;
if (struct_copy_size != 0)
flags |= FLAG_ARG_NEEDS_COPY;
if (fpr_count != 0)
bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
bytes += stack_count * sizeof (int);
bytes = (bytes + 15) & ~0xF;
bytes += struct_copy_size;
cif->flags = flags;
cif->bytes = bytes;
return FFI_OK;
}
ffi_status FFI_HIDDEN
ffi_prep_cif_sysv (ffi_cif *cif)
{
if ((cif->abi & FFI_SYSV) == 0)
{
cif->flags |= FLAG_COMPAT;
switch (cif->abi)
{
default:
return FFI_BAD_ABI;
case FFI_COMPAT_SYSV:
cif->abi = FFI_SYSV | FFI_SYSV_STRUCT_RET | FFI_SYSV_LONG_DOUBLE_128;
break;
case FFI_COMPAT_GCC_SYSV:
cif->abi = FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128;
break;
case FFI_COMPAT_LINUX:
cif->abi = (FFI_SYSV | FFI_SYSV_IBM_LONG_DOUBLE
| FFI_SYSV_LONG_DOUBLE_128);
break;
case FFI_COMPAT_LINUX_SOFT_FLOAT:
cif->abi = (FFI_SYSV | FFI_SYSV_SOFT_FLOAT | FFI_SYSV_IBM_LONG_DOUBLE
| FFI_SYSV_LONG_DOUBLE_128);
break;
}
}
return ffi_prep_cif_sysv_core (cif);
}
void FFI_HIDDEN
ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
{
const unsigned bytes = ecif->cif->bytes;
const unsigned flags = ecif->cif->flags;
typedef union
{
char *c;
unsigned *u;
long long *ll;
float *f;
double *d;
} valp;
valp stacktop;
valp gpr_base;
valp gpr_end;
#ifndef __NO_FPRS__
valp fpr_base;
valp fpr_end;
#endif
valp copy_space;
valp next_arg;
int i;
ffi_type **ptr;
#ifndef __NO_FPRS__
double double_tmp;
#endif
union
{
void **v;
char **c;
signed char **sc;
unsigned char **uc;
signed short **ss;
unsigned short **us;
unsigned int **ui;
long long **ll;
float **f;
double **d;
} p_argv;
size_t struct_copy_size;
unsigned gprvalue;
stacktop.c = (char *) stack + bytes;
gpr_end.u = stacktop.u - ASM_NEEDS_REGISTERS;
gpr_base.u = gpr_end.u - NUM_GPR_ARG_REGISTERS;
#ifndef __NO_FPRS__
fpr_end.d = gpr_base.d;
fpr_base.d = fpr_end.d - NUM_FPR_ARG_REGISTERS;
copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c);
#else
copy_space.c = gpr_base.c;
#endif
next_arg.u = stack + 2;
FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0);
FFI_ASSERT (((unsigned long) copy_space.c & 0xF) == 0);
FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0);
FFI_ASSERT ((bytes & 0xF) == 0);
FFI_ASSERT (copy_space.c >= next_arg.c);
if (flags & FLAG_RETVAL_REFERENCE)
*gpr_base.u++ = (unsigned) (char *) ecif->rvalue;
p_argv.v = ecif->avalue;
for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs;
i > 0;
i--, ptr++, p_argv.v++)
{
unsigned int typenum = (*ptr)->type;
typenum = translate_float (ecif->cif->abi, typenum);
switch (typenum)
{
#ifndef __NO_FPRS__
# if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
double_tmp = (*p_argv.d)[0];
if (fpr_base.d >= fpr_end.d - 1)
{
fpr_base.d = fpr_end.d;
if (((next_arg.u - stack) & 1) != 0)
next_arg.u += 1;
*next_arg.d = double_tmp;
next_arg.u += 2;
double_tmp = (*p_argv.d)[1];
*next_arg.d = double_tmp;
next_arg.u += 2;
}
else
{
*fpr_base.d++ = double_tmp;
double_tmp = (*p_argv.d)[1];
*fpr_base.d++ = double_tmp;
}
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
# endif
case FFI_TYPE_DOUBLE:
double_tmp = **p_argv.d;
if (fpr_base.d >= fpr_end.d)
{
if (((next_arg.u - stack) & 1) != 0)
next_arg.u += 1;
*next_arg.d = double_tmp;
next_arg.u += 2;
}
else
*fpr_base.d++ = double_tmp;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
case FFI_TYPE_FLOAT:
double_tmp = **p_argv.f;
if (fpr_base.d >= fpr_end.d)
{
*next_arg.f = (float) double_tmp;
next_arg.u += 1;
}
else
*fpr_base.d++ = double_tmp;
FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
break;
#endif
case FFI_TYPE_UINT128:
if (gpr_base.u >= gpr_end.u - 3)
{
unsigned int ii;
gpr_base.u = gpr_end.u;
for (ii = 0; ii < 4; ii++)
{
unsigned int int_tmp = (*p_argv.ui)[ii];
*next_arg.u++ = int_tmp;
}
}
else
{
unsigned int ii;
for (ii = 0; ii < 4; ii++)
{
unsigned int int_tmp = (*p_argv.ui)[ii];
*gpr_base.u++ = int_tmp;
}
}
break;
case FFI_TYPE_UINT64:
case FFI_TYPE_SINT64:
if (gpr_base.u >= gpr_end.u - 1)
{
gpr_base.u = gpr_end.u;
if (((next_arg.u - stack) & 1) != 0)
next_arg.u++;
*next_arg.ll = **p_argv.ll;
next_arg.u += 2;
}
else
{
if (((gpr_end.u - gpr_base.u) & 1) != 0)
gpr_base.u++;
*gpr_base.ll++ = **p_argv.ll;
}
break;
case FFI_TYPE_STRUCT:
struct_copy_size = ((*ptr)->size + 15) & ~0xF;
copy_space.c -= struct_copy_size;
memcpy (copy_space.c, *p_argv.c, (*ptr)->size);
gprvalue = (unsigned long) copy_space.c;
FFI_ASSERT (copy_space.c > next_arg.c);
FFI_ASSERT (flags & FLAG_ARG_NEEDS_COPY);
goto putgpr;
case FFI_TYPE_UINT8:
gprvalue = **p_argv.uc;
goto putgpr;
case FFI_TYPE_SINT8:
gprvalue = **p_argv.sc;
goto putgpr;
case FFI_TYPE_UINT16:
gprvalue = **p_argv.us;
goto putgpr;
case FFI_TYPE_SINT16:
gprvalue = **p_argv.ss;
goto putgpr;
case FFI_TYPE_INT:
case FFI_TYPE_UINT32:
case FFI_TYPE_SINT32:
case FFI_TYPE_POINTER:
gprvalue = **p_argv.ui;
putgpr:
if (gpr_base.u >= gpr_end.u)
*next_arg.u++ = gprvalue;
else
*gpr_base.u++ = gprvalue;
break;
}
}
FFI_ASSERT (copy_space.c >= next_arg.c);
FFI_ASSERT (gpr_base.u <= gpr_end.u);
#ifndef __NO_FPRS__
FFI_ASSERT (fpr_base.u <= fpr_end.u);
#endif
FFI_ASSERT (((flags & FLAG_4_GPR_ARGUMENTS) != 0)
== (gpr_end.u - gpr_base.u < 4));
}
#define MIN_CACHE_LINE_SIZE 8
static void
flush_icache (char *wraddr, char *xaddr, int size)
{
int i;
for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE)
__asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;"
: : "r" (xaddr + i), "r" (wraddr + i) : "memory");
__asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" "sync;" "isync;"
: : "r"(xaddr + size - 1), "r"(wraddr + size - 1)
: "memory");
}
ffi_status FFI_HIDDEN
ffi_prep_closure_loc_sysv (ffi_closure *closure,
ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data,
void *codeloc)
{
unsigned int *tramp;
if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI)
return FFI_BAD_ABI;
tramp = (unsigned int *) &closure->tramp[0];
tramp[0] = 0x7c0802a6;
tramp[1] = 0x429f0005;
tramp[2] = 0x7d6802a6;
tramp[3] = 0x7c0803a6;
tramp[4] = 0x800b0018;
tramp[5] = 0x816b001c;
tramp[6] = 0x7c0903a6;
tramp[7] = 0x4e800420;
*(void **) &tramp[8] = (void *) ffi_closure_SYSV;
*(void **) &tramp[9] = codeloc;
flush_icache ((char *)tramp, (char *)codeloc, 8 * 4);
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
int
ffi_closure_helper_SYSV (ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data,
void *rvalue,
unsigned long *pgr,
ffi_dblfl *pfr,
unsigned long *pst)
{
void ** avalue;
ffi_type ** arg_types;
long i, avn;
#ifndef __NO_FPRS__
long nf = 0;
#endif
long ng = 0;
unsigned size = cif->rtype->size;
unsigned short rtypenum = cif->rtype->type;
avalue = alloca (cif->nargs * sizeof (void *));
rtypenum = translate_float (cif->abi, rtypenum);
if (rtypenum == FFI_TYPE_STRUCT
&& !((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8))
{
rvalue = (void *) *pgr;
ng++;
pgr++;
}
i = 0;
avn = cif->nargs;
arg_types = cif->arg_types;
while (i < avn) {
unsigned short typenum = arg_types[i]->type;
typenum = translate_float (cif->abi, typenum);
switch (typenum)
{
#ifndef __NO_FPRS__
case FFI_TYPE_FLOAT:
if (nf < NUM_FPR_ARG_REGISTERS)
{
double temp = pfr->d;
pfr->f = (float) temp;
avalue[i] = pfr;
nf++;
pfr++;
}
else
{
avalue[i] = pst;
pst += 1;
}
break;
case FFI_TYPE_DOUBLE:
if (nf < NUM_FPR_ARG_REGISTERS)
{
avalue[i] = pfr;
nf++;
pfr++;
}
else
{
if (((long) pst) & 4)
pst++;
avalue[i] = pst;
pst += 2;
}
break;
# if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
case FFI_TYPE_LONGDOUBLE:
if (nf < NUM_FPR_ARG_REGISTERS - 1)
{
avalue[i] = pfr;
pfr += 2;
nf += 2;
}
else
{
if (((long) pst) & 4)
pst++;
avalue[i] = pst;
pst += 4;
nf = 8;
}
break;
# endif
#endif
case FFI_TYPE_UINT128:
if (ng < NUM_GPR_ARG_REGISTERS - 3)
{
avalue[i] = pgr;
pgr += 4;
ng += 4;
}
else
{
avalue[i] = pst;
pst += 4;
ng = 8+4;
}
break;
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT8:
#ifndef __LITTLE_ENDIAN__
if (ng < NUM_GPR_ARG_REGISTERS)
{
avalue[i] = (char *) pgr + 3;
ng++;
pgr++;
}
else
{
avalue[i] = (char *) pst + 3;
pst++;
}
break;
#endif
case FFI_TYPE_SINT16:
case FFI_TYPE_UINT16:
#ifndef __LITTLE_ENDIAN__
if (ng < NUM_GPR_ARG_REGISTERS)
{
avalue[i] = (char *) pgr + 2;
ng++;
pgr++;
}
else
{
avalue[i] = (char *) pst + 2;
pst++;
}
break;
#endif
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
case FFI_TYPE_POINTER:
if (ng < NUM_GPR_ARG_REGISTERS)
{
avalue[i] = pgr;
ng++;
pgr++;
}
else
{
avalue[i] = pst;
pst++;
}
break;
case FFI_TYPE_STRUCT:
if (ng < NUM_GPR_ARG_REGISTERS)
{
avalue[i] = (void *) *pgr;
ng++;
pgr++;
}
else
{
avalue[i] = (void *) *pst;
pst++;
}
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
if (ng < NUM_GPR_ARG_REGISTERS - 1)
{
if (ng & 1)
{
ng++;
pgr++;
}
avalue[i] = pgr;
ng += 2;
pgr += 2;
}
else
{
if (((long) pst) & 4)
pst++;
avalue[i] = pst;
pst += 2;
ng = NUM_GPR_ARG_REGISTERS;
}
break;
default:
FFI_ASSERT (0);
}
i++;
}
(*fun) (cif, rvalue, avalue, user_data);
if (rtypenum == FFI_TYPE_STRUCT
&& (cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
return FFI_SYSV_TYPE_SMALL_STRUCT - 1 + size;
return rtypenum;
}
#endif