#if defined(__arm__) || defined(_M_ARM)
#include <fficonfig.h>
#include <ffi.h>
#include <ffi_common.h>
#include <stdint.h>
#include <stdlib.h>
#include "internal.h"
#if defined(_MSC_VER) && defined(_M_ARM)
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
#endif
#if FFI_EXEC_TRAMPOLINE_TABLE
#ifdef __MACH__
#include <mach/machine/vm_param.h>
#endif
#else
#ifndef _M_ARM
extern unsigned int ffi_arm_trampoline[2] FFI_HIDDEN;
#else
extern unsigned int ffi_arm_trampoline[3] FFI_HIDDEN;
#endif
#endif
static int vfp_type_p (const ffi_type *);
static void layout_vfp_args (ffi_cif *);
static void *
ffi_align (ffi_type *ty, void *p)
{
size_t alignment;
#ifdef _WIN32_WCE
alignment = 4;
#else
alignment = ty->alignment;
if (alignment < 4)
alignment = 4;
#endif
return (void *) FFI_ALIGN (p, alignment);
}
static size_t
ffi_put_arg (ffi_type *ty, void *src, void *dst)
{
size_t z = ty->size;
switch (ty->type)
{
case FFI_TYPE_SINT8:
*(UINT32 *)dst = *(SINT8 *)src;
break;
case FFI_TYPE_UINT8:
*(UINT32 *)dst = *(UINT8 *)src;
break;
case FFI_TYPE_SINT16:
*(UINT32 *)dst = *(SINT16 *)src;
break;
case FFI_TYPE_UINT16:
*(UINT32 *)dst = *(UINT16 *)src;
break;
case FFI_TYPE_INT:
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
case FFI_TYPE_POINTER:
#ifndef _MSC_VER
case FFI_TYPE_FLOAT:
#endif
*(UINT32 *)dst = *(UINT32 *)src;
break;
#ifdef _MSC_VER
case FFI_TYPE_FLOAT:
*(uintptr_t *)dst = 0;
*(float *)dst = *(float *)src;
break;
#endif
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
case FFI_TYPE_DOUBLE:
*(UINT64 *)dst = *(UINT64 *)src;
break;
case FFI_TYPE_STRUCT:
case FFI_TYPE_COMPLEX:
memcpy (dst, src, z);
break;
default:
abort();
}
return FFI_ALIGN (z, 4);
}
static void
ffi_prep_args_SYSV (ffi_cif *cif, int flags, void *rvalue,
void **avalue, char *argp)
{
ffi_type **arg_types = cif->arg_types;
int i, n;
if (flags == ARM_TYPE_STRUCT)
{
*(void **) argp = rvalue;
argp += 4;
}
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *ty = arg_types[i];
argp = ffi_align (ty, argp);
argp += ffi_put_arg (ty, avalue[i], argp);
}
}
static void
ffi_prep_args_VFP (ffi_cif *cif, int flags, void *rvalue,
void **avalue, char *stack, char *vfp_space)
{
ffi_type **arg_types = cif->arg_types;
int i, n, vi = 0;
char *argp, *regp, *eo_regp;
char stack_used = 0;
char done_with_regs = 0;
regp = stack;
eo_regp = argp = regp + 16;
if (flags == ARM_TYPE_STRUCT)
{
*(void **) regp = rvalue;
regp += 4;
}
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *ty = arg_types[i];
void *a = avalue[i];
int is_vfp_type = vfp_type_p (ty);
if (vi < cif->vfp_nargs && is_vfp_type)
{
char *vfp_slot = vfp_space + cif->vfp_args[vi++] * 4;
ffi_put_arg (ty, a, vfp_slot);
continue;
}
else if (!done_with_regs && !is_vfp_type)
{
char *tregp = ffi_align (ty, regp);
size_t size = ty->size;
size = (size < 4) ? 4 : size;
if (tregp + size <= eo_regp)
{
regp = tregp + ffi_put_arg (ty, a, tregp);
done_with_regs = (regp == argp);
FFI_ASSERT (regp <= argp);
continue;
}
else if (!stack_used)
{
stack_used = 1;
done_with_regs = 1;
argp = tregp + ffi_put_arg (ty, a, tregp);
FFI_ASSERT (eo_regp < argp);
continue;
}
}
stack_used = 1;
argp = ffi_align (ty, argp);
argp += ffi_put_arg (ty, a, argp);
}
}
ffi_status FFI_HIDDEN
ffi_prep_cif_machdep (ffi_cif *cif)
{
int flags = 0, cabi = cif->abi;
size_t bytes = cif->bytes;
if (cabi == FFI_VFP)
layout_vfp_args (cif);
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
flags = ARM_TYPE_VOID;
break;
case FFI_TYPE_INT:
case FFI_TYPE_UINT8:
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT16:
case FFI_TYPE_SINT16:
case FFI_TYPE_UINT32:
case FFI_TYPE_SINT32:
case FFI_TYPE_POINTER:
flags = ARM_TYPE_INT;
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
flags = ARM_TYPE_INT64;
break;
case FFI_TYPE_FLOAT:
flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_S : ARM_TYPE_INT);
break;
case FFI_TYPE_DOUBLE:
flags = (cabi == FFI_VFP ? ARM_TYPE_VFP_D : ARM_TYPE_INT64);
break;
case FFI_TYPE_STRUCT:
case FFI_TYPE_COMPLEX:
if (cabi == FFI_VFP)
{
int h = vfp_type_p (cif->rtype);
flags = ARM_TYPE_VFP_N;
if (h == 0x100 + FFI_TYPE_FLOAT)
flags = ARM_TYPE_VFP_S;
if (h == 0x100 + FFI_TYPE_DOUBLE)
flags = ARM_TYPE_VFP_D;
if (h != 0)
break;
}
if (cif->rtype->size <= 4)
flags = ARM_TYPE_INT;
else
{
flags = ARM_TYPE_STRUCT;
bytes += 4;
}
break;
default:
abort();
}
bytes = FFI_ALIGN (bytes, 8);
if (bytes < 4*4)
bytes = 4*4;
cif->bytes = bytes;
cif->flags = flags;
return FFI_OK;
}
ffi_status FFI_HIDDEN
ffi_prep_cif_machdep_var (ffi_cif * cif,
unsigned int nfixedargs, unsigned int ntotalargs)
{
if (cif->abi == FFI_VFP)
cif->abi = FFI_SYSV;
return ffi_prep_cif_machdep (cif);
}
struct call_frame
{
void *fp;
void *lr;
void *rvalue;
int flags;
void *closure;
};
extern void ffi_call_SYSV (void *stack, struct call_frame *,
void (*fn) (void)) FFI_HIDDEN;
extern void ffi_call_VFP (void *vfp_space, struct call_frame *,
void (*fn) (void), unsigned vfp_used) FFI_HIDDEN;
static void
ffi_call_int (ffi_cif * cif, void (*fn) (void), void *rvalue,
void **avalue, void *closure)
{
int flags = cif->flags;
ffi_type *rtype = cif->rtype;
size_t bytes, rsize, vfp_size;
char *stack, *vfp_space, *new_rvalue;
struct call_frame *frame;
rsize = 0;
if (rvalue == NULL)
{
if (flags == ARM_TYPE_STRUCT)
rsize = rtype->size;
else
flags = ARM_TYPE_VOID;
}
else if (flags == ARM_TYPE_VFP_N)
{
rsize = 32;
}
else if (flags == ARM_TYPE_INT && rtype->type == FFI_TYPE_STRUCT)
rsize = 4;
vfp_size = (cif->abi == FFI_VFP && cif->vfp_used ? 8*8: 0);
bytes = cif->bytes;
stack = alloca (vfp_size + bytes + sizeof(struct call_frame) + rsize);
vfp_space = NULL;
if (vfp_size)
{
vfp_space = stack;
stack += vfp_size;
}
frame = (struct call_frame *)(stack + bytes);
new_rvalue = rvalue;
if (rsize)
new_rvalue = (void *)(frame + 1);
frame->rvalue = new_rvalue;
frame->flags = flags;
frame->closure = closure;
if (vfp_space)
{
ffi_prep_args_VFP (cif, flags, new_rvalue, avalue, stack, vfp_space);
ffi_call_VFP (vfp_space, frame, fn, cif->vfp_used);
}
else
{
ffi_prep_args_SYSV (cif, flags, new_rvalue, avalue, stack);
ffi_call_SYSV (stack, frame, fn);
}
if (rvalue && rvalue != new_rvalue)
memcpy (rvalue, new_rvalue, rtype->size);
}
void
ffi_call (ffi_cif *cif, void (*fn) (void), void *rvalue, void **avalue)
{
ffi_call_int (cif, fn, rvalue, avalue, NULL);
}
#ifdef FFI_GO_CLOSURES
void
ffi_call_go (ffi_cif *cif, void (*fn) (void), void *rvalue,
void **avalue, void *closure)
{
ffi_call_int (cif, fn, rvalue, avalue, closure);
}
#endif
static void *
ffi_prep_incoming_args_SYSV (ffi_cif *cif, void *rvalue,
char *argp, void **avalue)
{
ffi_type **arg_types = cif->arg_types;
int i, n;
if (cif->flags == ARM_TYPE_STRUCT)
{
rvalue = *(void **) argp;
argp += 4;
}
else
{
if (cif->rtype->size && cif->rtype->size < 4)
*(uint32_t *) rvalue = 0;
}
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *ty = arg_types[i];
size_t z = ty->size;
argp = ffi_align (ty, argp);
avalue[i] = (void *) argp;
argp += z;
}
return rvalue;
}
static void *
ffi_prep_incoming_args_VFP (ffi_cif *cif, void *rvalue, char *stack,
char *vfp_space, void **avalue)
{
ffi_type **arg_types = cif->arg_types;
int i, n, vi = 0;
char *argp, *regp, *eo_regp;
char done_with_regs = 0;
char stack_used = 0;
regp = stack;
eo_regp = argp = regp + 16;
if (cif->flags == ARM_TYPE_STRUCT)
{
rvalue = *(void **) regp;
regp += 4;
}
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *ty = arg_types[i];
int is_vfp_type = vfp_type_p (ty);
size_t z = ty->size;
if (vi < cif->vfp_nargs && is_vfp_type)
{
avalue[i] = vfp_space + cif->vfp_args[vi++] * 4;
continue;
}
else if (!done_with_regs && !is_vfp_type)
{
char *tregp = ffi_align (ty, regp);
z = (z < 4) ? 4 : z;
if (tregp + z <= eo_regp || !stack_used)
{
avalue[i] = (void *) tregp;
regp = tregp + z;
if (regp > eo_regp)
{
FFI_ASSERT (!stack_used);
argp = regp;
}
if (regp >= eo_regp)
{
done_with_regs = 1;
stack_used = 1;
}
continue;
}
}
stack_used = 1;
argp = ffi_align (ty, argp);
avalue[i] = (void *) argp;
argp += z;
}
return rvalue;
}
#if FFI_CLOSURES
struct closure_frame
{
char vfp_space[8*8] __attribute__((aligned(8)));
char result[8*4];
char argp[];
};
int FFI_HIDDEN
ffi_closure_inner_SYSV (ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data,
struct closure_frame *frame)
{
void **avalue = (void **) alloca (cif->nargs * sizeof (void *));
void *rvalue = ffi_prep_incoming_args_SYSV (cif, frame->result,
frame->argp, avalue);
fun (cif, rvalue, avalue, user_data);
return cif->flags;
}
int FFI_HIDDEN
ffi_closure_inner_VFP (ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data,
struct closure_frame *frame)
{
void **avalue = (void **) alloca (cif->nargs * sizeof (void *));
void *rvalue = ffi_prep_incoming_args_VFP (cif, frame->result, frame->argp,
frame->vfp_space, avalue);
fun (cif, rvalue, avalue, user_data);
return cif->flags;
}
void ffi_closure_SYSV (void) FFI_HIDDEN;
void ffi_closure_VFP (void) FFI_HIDDEN;
#ifdef FFI_GO_CLOSURES
void ffi_go_closure_SYSV (void) FFI_HIDDEN;
void ffi_go_closure_VFP (void) FFI_HIDDEN;
#endif
ffi_status
ffi_prep_closure_loc (ffi_closure * closure,
ffi_cif * cif,
void (*fun) (ffi_cif *, void *, void **, void *),
void *user_data, void *codeloc)
{
void (*closure_func) (void) = ffi_closure_SYSV;
if (cif->abi == FFI_VFP)
{
if (cif->vfp_used)
closure_func = ffi_closure_VFP;
}
else if (cif->abi != FFI_SYSV)
return FFI_BAD_ABI;
#if FFI_EXEC_TRAMPOLINE_TABLE
void **config = (void **)((uint8_t *)codeloc - PAGE_MAX_SIZE);
config[0] = closure;
config[1] = closure_func;
#else
#ifndef _M_ARM
memcpy(closure->tramp, ffi_arm_trampoline, 8);
#else
memcpy(closure->tramp, (void*)((uintptr_t)ffi_arm_trampoline & 0xFFFFFFFE), FFI_TRAMPOLINE_CLOSURE_OFFSET);
#endif
#if defined (__QNX__)
msync(closure->tramp, 8, 0x1000000);
msync(codeloc, 8, 0x1000000);
#elif defined(_MSC_VER)
FlushInstructionCache(GetCurrentProcess(), closure->tramp, FFI_TRAMPOLINE_SIZE);
#else
__clear_cache(closure->tramp, closure->tramp + 8);
__clear_cache(codeloc, codeloc + 8);
#endif
#ifdef _M_ARM
*(void(**)(void))(closure->tramp + FFI_TRAMPOLINE_CLOSURE_FUNCTION) = closure_func;
#else
*(void (**)(void))(closure->tramp + 8) = closure_func;
#endif
#endif
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
#ifdef FFI_GO_CLOSURES
ffi_status
ffi_prep_go_closure (ffi_go_closure *closure, ffi_cif *cif,
void (*fun) (ffi_cif *, void *, void **, void *))
{
void (*closure_func) (void) = ffi_go_closure_SYSV;
if (cif->abi == FFI_VFP)
{
if (cif->vfp_used)
closure_func = ffi_go_closure_VFP;
}
else if (cif->abi != FFI_SYSV)
return FFI_BAD_ABI;
closure->tramp = closure_func;
closure->cif = cif;
closure->fun = fun;
return FFI_OK;
}
#endif
#endif
static int
is_hfa0 (const ffi_type *ty)
{
ffi_type **elements = ty->elements;
int i, ret = -1;
if (elements != NULL)
for (i = 0; elements[i]; ++i)
{
ret = elements[i]->type;
if (ret == FFI_TYPE_STRUCT || ret == FFI_TYPE_COMPLEX)
{
ret = is_hfa0 (elements[i]);
if (ret < 0)
continue;
}
break;
}
return ret;
}
static int
is_hfa1 (const ffi_type *ty, int candidate)
{
ffi_type **elements = ty->elements;
int i;
if (elements != NULL)
for (i = 0; elements[i]; ++i)
{
int t = elements[i]->type;
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX)
{
if (!is_hfa1 (elements[i], candidate))
return 0;
}
else if (t != candidate)
return 0;
}
return 1;
}
static int
vfp_type_p (const ffi_type *ty)
{
ffi_type **elements;
int candidate, i;
size_t size, ele_count;
candidate = ty->type;
switch (ty->type)
{
default:
return 0;
case FFI_TYPE_FLOAT:
case FFI_TYPE_DOUBLE:
ele_count = 1;
goto done;
case FFI_TYPE_COMPLEX:
candidate = ty->elements[0]->type;
if (candidate != FFI_TYPE_FLOAT && candidate != FFI_TYPE_DOUBLE)
return 0;
ele_count = 2;
goto done;
case FFI_TYPE_STRUCT:
break;
}
size = ty->size;
if (size < 4 || size > 32)
return 0;
elements = ty->elements;
candidate = elements[0]->type;
if (candidate == FFI_TYPE_STRUCT || candidate == FFI_TYPE_COMPLEX)
{
for (i = 0; ; ++i)
{
candidate = is_hfa0 (elements[i]);
if (candidate >= 0)
break;
}
}
switch (candidate)
{
case FFI_TYPE_FLOAT:
ele_count = size / sizeof(float);
if (size != ele_count * sizeof(float))
return 0;
break;
case FFI_TYPE_DOUBLE:
ele_count = size / sizeof(double);
if (size != ele_count * sizeof(double))
return 0;
break;
default:
return 0;
}
if (ele_count > 4)
return 0;
for (i = 0; elements[i]; ++i)
{
int t = elements[i]->type;
if (t == FFI_TYPE_STRUCT || t == FFI_TYPE_COMPLEX)
{
if (!is_hfa1 (elements[i], candidate))
return 0;
}
else if (t != candidate)
return 0;
}
done:
return (ele_count << 8) | candidate;
}
static int
place_vfp_arg (ffi_cif *cif, int h)
{
unsigned short reg = cif->vfp_reg_free;
int align = 1, nregs = h >> 8;
if ((h & 0xff) == FFI_TYPE_DOUBLE)
align = 2, nregs *= 2;
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;
}
}
cif->vfp_used |= new_used;
cif->vfp_args[cif->vfp_nargs++] = (signed char)reg;
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 0;
next_reg:;
}
cif->vfp_reg_free = 16;
cif->vfp_used = 0xFFFF;
return 1;
}
static void
layout_vfp_args (ffi_cif * cif)
{
unsigned int i;
cif->vfp_used = 0;
cif->vfp_nargs = 0;
cif->vfp_reg_free = 0;
memset (cif->vfp_args, -1, 16);
for (i = 0; i < cif->nargs; i++)
{
int h = vfp_type_p (cif->arg_types[i]);
if (h && place_vfp_arg (cif, h) == 1)
break;
}
}
#endif