#include <ffi.h>
#include <ffi_common.h>
#include <stdlib.h>
#include <stdint.h>
#include <sys/cachectl.h>
#define NARGREG 8
#define STKALIGN 4
#define MAXCOPYARG (2 * sizeof(double))
typedef struct call_context
{
size_t r[8];
char frame[16];
} call_context;
typedef struct call_builder
{
call_context *aregs;
int used_integer;
size_t *used_stack;
void *struct_stack;
} call_builder;
#if defined(__ARC64_ARCH64__)
#define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT64)
#else
#define IS_INT(type) ((type) >= FFI_TYPE_UINT8 && (type) <= FFI_TYPE_SINT32)
#endif
#if __BIG_ENDIAN__
#define CODE_ENDIAN(x) (x)
#else
#define CODE_ENDIAN(x) ( (((uint32_t) (x)) << 16) | (((uint32_t) (x)) >> 16))
#endif
ffi_status
ffi_prep_cif_machdep (ffi_cif * cif)
{
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
cif->flags = (unsigned) cif->rtype->type;
break;
case FFI_TYPE_STRUCT:
cif->flags = (unsigned) cif->rtype->type;
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_DOUBLE:
cif->flags = FFI_TYPE_DOUBLE;
break;
case FFI_TYPE_FLOAT:
default:
cif->flags = FFI_TYPE_INT;
break;
}
return FFI_OK;
}
static void marshal_atom(call_builder *cb, int type, void *data) {
size_t value = 0;
switch (type) {
case FFI_TYPE_UINT8: value = *(uint8_t *)data; break;
case FFI_TYPE_SINT8: value = *(int8_t *)data; break;
case FFI_TYPE_UINT16: value = *(uint16_t *)data; break;
case FFI_TYPE_SINT16: value = *(int16_t *)data; break;
case FFI_TYPE_UINT32: value = *(int32_t *)data; break;
case FFI_TYPE_SINT32: value = *(int32_t *)data; break;
#if defined(__ARC64_ARCH64__)
case FFI_TYPE_UINT64: value = *(uint64_t *)data; break;
case FFI_TYPE_SINT64: value = *(int64_t *)data; break;
#endif
case FFI_TYPE_POINTER: value = *(size_t *)data; break;
default: FFI_ASSERT(0); break;
}
if (cb->used_integer == NARGREG) {
*cb->used_stack++ = value;
} else {
cb->aregs->r[cb->used_integer++] = value;
}
}
static void marshal(call_builder *cb, ffi_type *type, int var, void *data) {
size_t realign[2];
#if (defined(__ARC64_ARCH64__) || defined(__ARC64_ARCH32__))
if (type->size > 2 * __SIZEOF_POINTER__) {
if (var) {
marshal_atom(cb, FFI_TYPE_POINTER, &data);
} else {
data = memcpy (cb->struct_stack, data, type->size);
cb->struct_stack = (size_t *) FFI_ALIGN ((char *) cb->struct_stack + type->size, __SIZEOF_POINTER__);
marshal_atom(cb, FFI_TYPE_POINTER, &data);
}
}
#else
if (type->type == FFI_TYPE_STRUCT) {
if (var) {
if (type->size > 0)
marshal_atom(cb, FFI_TYPE_POINTER, data);
} else {
int i;
for (i = 0; i < type->size; i += sizeof(size_t)) {
marshal_atom(cb, FFI_TYPE_POINTER, data);
data += sizeof(size_t);
}
}
}
#endif
else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
marshal_atom(cb, type->type, data);
} else {
memcpy(realign, data, type->size);
if (type->size > 0)
marshal_atom(cb, FFI_TYPE_POINTER, realign);
if (type->size > __SIZEOF_POINTER__)
marshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
}
}
static void unmarshal_atom(call_builder *cb, int type, void *data) {
size_t value;
if (cb->used_integer == NARGREG) {
value = *cb->used_stack++;
} else {
value = cb->aregs->r[cb->used_integer++];
}
switch (type) {
case FFI_TYPE_UINT8: *(uint8_t *)data = value; break;
case FFI_TYPE_SINT8: *(uint8_t *)data = value; break;
case FFI_TYPE_UINT16: *(uint16_t *)data = value; break;
case FFI_TYPE_SINT16: *(uint16_t *)data = value; break;
case FFI_TYPE_UINT32: *(uint32_t *)data = value; break;
case FFI_TYPE_SINT32: *(uint32_t *)data = value; break;
#if defined(__ARC64_ARCH64__)
case FFI_TYPE_UINT64: *(uint64_t *)data = value; break;
case FFI_TYPE_SINT64: *(uint64_t *)data = value; break;
#endif
case FFI_TYPE_POINTER: *(size_t *)data = value; break;
default: FFI_ASSERT(0); break;
}
}
static void *unmarshal(call_builder *cb, ffi_type *type, int var, void *data) {
size_t realign[2];
#if defined(__ARC64_ARCH64__)
void *pointer;
if (type->size > 2 * __SIZEOF_POINTER__) {
unmarshal_atom(cb, FFI_TYPE_POINTER, (char*)&pointer);
return pointer;
}
#elif defined(__ARC64_ARCH32__)
if (type->type == FFI_TYPE_STRUCT) {
if (type->size > 2 * __SIZEOF_POINTER__) {
unmarshal_atom(cb, FFI_TYPE_POINTER, &realign[0]);
memcpy(data, (const void*)realign[0], type->size);
return data;
} else {
int i;
void *pdata = data;
for (i = 0; i < type->size; i += sizeof(size_t)) {
unmarshal_atom(cb, FFI_TYPE_POINTER, pdata);
pdata += sizeof(size_t);
}
return data;
}
}
#else
if (type->type == FFI_TYPE_STRUCT) {
if (var) {
int i;
void *pdata = data;
for (i = 0; i < type->size; i += sizeof(size_t)) {
unmarshal_atom(cb, FFI_TYPE_POINTER, pdata);
pdata += sizeof(size_t);
}
return data;
} else {
if (type->size > 0)
unmarshal_atom(cb, FFI_TYPE_POINTER, &realign[0]);
memcpy(data, (const void*)realign[0], type->size);
return data;
}
}
#endif
else if (IS_INT(type->type) || type->type == FFI_TYPE_POINTER) {
unmarshal_atom(cb, type->type, data);
return data;
} else {
if (type->size > 0)
unmarshal_atom(cb, FFI_TYPE_POINTER, realign);
if (type->size > __SIZEOF_POINTER__)
unmarshal_atom(cb, FFI_TYPE_POINTER, realign + 1);
memcpy(data, realign, type->size);
return data;
}
}
static int passed_by_ref(ffi_type *type, int var) {
if (type->type == FFI_TYPE_STRUCT)
return 1;
return type->size > 2 * __SIZEOF_POINTER__;
}
extern void ffi_call_asm (void *stack, struct call_context *regs,
void (*fn) (void), void *closure) FFI_HIDDEN;
static void
ffi_call_int (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue,
void *closure)
{
int return_by_ref = passed_by_ref(cif->rtype, 0);
size_t arg_bytes = FFI_ALIGN(2 * sizeof(size_t) * cif->nargs, STKALIGN);
size_t struct_bytes = FFI_ALIGN(cif->bytes, STKALIGN);
size_t alloc_size = arg_bytes + struct_bytes + sizeof(call_context);
size_t alloc_base = (size_t)alloca(alloc_size);
call_builder cb;
cb.used_integer = 0;
cb.aregs = (call_context*)(alloc_base + arg_bytes + struct_bytes);
cb.used_stack = (void*)alloc_base;
cb.struct_stack = (void *)(alloc_base + arg_bytes );
if (return_by_ref)
marshal(&cb, &ffi_type_pointer, 0, &rvalue);
int i;
for (i = 0; i < cif->nargs; i++)
marshal(&cb, cif->arg_types[i], 0, avalue[i]);
ffi_call_asm ((void *) alloc_base, cb.aregs, fn, closure);
cb.used_integer = 0;
if (!return_by_ref && rvalue)
{
if (IS_INT(cif->rtype->type)
&& cif->rtype->size < sizeof (ffi_arg))
{
switch (cif->rtype->type) {
case FFI_TYPE_SINT8:
case FFI_TYPE_SINT16:
case FFI_TYPE_SINT32:
unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
? FFI_TYPE_SINT64 : FFI_TYPE_SINT32),
rvalue);
break;
case FFI_TYPE_UINT8:
case FFI_TYPE_UINT16:
case FFI_TYPE_UINT32:
unmarshal_atom (&cb, (sizeof (ffi_arg) > 4
? FFI_TYPE_UINT64 : FFI_TYPE_UINT32),
rvalue);
break;
}
}
else
unmarshal(&cb, cif->rtype, 0, rvalue);
}
}
void
ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue)
{
ffi_call_int(cif, fn, rvalue, avalue, NULL);
}
void
ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue,
void **avalue, void *closure)
{
ffi_call_int(cif, fn, rvalue, avalue, closure);
}
extern void ffi_closure_asm(void) FFI_HIDDEN;
ffi_status
ffi_prep_closure_loc (ffi_closure * closure, ffi_cif * cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data, void *codeloc)
{
uint32_t *tramp = (uint32_t *) & (closure->tramp[0]);
#if defined(__ARC64_ARCH64__)
size_t address_ffi_closure = (size_t) ffi_closure_asm;
#endif
switch (cif->abi)
{
#if defined(__ARC64_ARCH64__)
case FFI_ARC64:
FFI_ASSERT (tramp == codeloc);
tramp[0] = CODE_ENDIAN (0x580a1fc0);
tramp[1] = CODE_ENDIAN (0x5c0b1f80);
tramp[2] = CODE_ENDIAN ((uint32_t)(address_ffi_closure >> 32));
tramp[3] = CODE_ENDIAN (0x5c051f8c);
tramp[4] = CODE_ENDIAN ((uint32_t)(address_ffi_closure & 0xffffffff));
tramp[5] = CODE_ENDIAN (0x20200300);
break;
#else
case FFI_ARCOMPACT:
FFI_ASSERT (tramp == codeloc);
tramp[0] = CODE_ENDIAN (0x200a1fc0);
tramp[1] = CODE_ENDIAN (0x20200f80);
tramp[2] = CODE_ENDIAN ((uint32_t) ffi_closure_asm);
break;
#endif
default:
return FFI_BAD_ABI;
}
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
cacheflush (codeloc, FFI_TRAMPOLINE_SIZE, BCACHE);
return FFI_OK;
}
extern void ffi_go_closure_asm (void) FFI_HIDDEN;
ffi_status
ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *))
{
if (cif->abi <= FFI_FIRST_ABI || cif->abi >= FFI_LAST_ABI)
return FFI_BAD_ABI;
closure->tramp = (void *) ffi_go_closure_asm;
closure->cif = cif;
closure->fun = fun;
return FFI_OK;
}
void FFI_HIDDEN
ffi_closure_inner (ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data,
size_t *stack, call_context *aregs)
{
void **avalue = alloca(cif->nargs * sizeof(void*));
char *astorage = alloca(cif->bytes);
char *ptr = astorage;
void *rvalue;
call_builder cb;
int i;
cb.aregs = aregs;
cb.used_integer = 0;
cb.used_stack = stack;
if (cif->flags == FFI_TYPE_STRUCT)
unmarshal(&cb, &ffi_type_pointer, 0, &rvalue);
else
rvalue = alloca(cif->rtype->size);
for (i = 0; i < cif->nargs; i++) {
avalue[i] = unmarshal(&cb, cif->arg_types[i], 1, ptr);
ptr += cif->arg_types[i]->size;
}
fun (cif, rvalue, avalue, user_data);
if (cif->rtype->type != FFI_TYPE_VOID) {
cb.used_integer = 0;
marshal(&cb, cif->rtype, 1, rvalue);
}
}