#if defined(__i386__) || defined(_M_IX86)
#include <ffi.h>
#include <ffi_common.h>
#include <stdint.h>
#include <stdlib.h>
#include "internal.h"
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
# if FFI_TYPE_LONGDOUBLE != 4
# error FFI_TYPE_LONGDOUBLE out of date
# endif
#else
# undef FFI_TYPE_LONGDOUBLE
# define FFI_TYPE_LONGDOUBLE 4
#endif
#if defined(__GNUC__) && !defined(__declspec)
# define __declspec(x) __attribute__((x))
#endif
#if defined(_MSC_VER) && defined(_M_IX86)
#define STACK_ALIGN(bytes) (bytes)
#else
#define STACK_ALIGN(bytes) FFI_ALIGN (bytes, 16)
#endif
ffi_status FFI_HIDDEN
ffi_prep_cif_machdep(ffi_cif *cif)
{
size_t bytes = 0;
int i, n, flags, cabi = cif->abi;
switch (cabi)
{
case FFI_SYSV:
case FFI_STDCALL:
case FFI_THISCALL:
case FFI_FASTCALL:
case FFI_MS_CDECL:
case FFI_PASCAL:
case FFI_REGISTER:
break;
default:
return FFI_BAD_ABI;
}
switch (cif->rtype->type)
{
case FFI_TYPE_VOID:
flags = X86_RET_VOID;
break;
case FFI_TYPE_FLOAT:
flags = X86_RET_FLOAT;
break;
case FFI_TYPE_DOUBLE:
flags = X86_RET_DOUBLE;
break;
case FFI_TYPE_LONGDOUBLE:
flags = X86_RET_LDOUBLE;
break;
case FFI_TYPE_UINT8:
flags = X86_RET_UINT8;
break;
case FFI_TYPE_UINT16:
flags = X86_RET_UINT16;
break;
case FFI_TYPE_SINT8:
flags = X86_RET_SINT8;
break;
case FFI_TYPE_SINT16:
flags = X86_RET_SINT16;
break;
case FFI_TYPE_INT:
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
case FFI_TYPE_POINTER:
flags = X86_RET_INT32;
break;
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
flags = X86_RET_INT64;
break;
case FFI_TYPE_STRUCT:
#ifndef X86
if (cif->rtype->size == 1)
flags = X86_RET_STRUCT_1B;
else if (cif->rtype->size == 2)
flags = X86_RET_STRUCT_2B;
else if (cif->rtype->size == 4)
flags = X86_RET_INT32;
else if (cif->rtype->size == 8)
flags = X86_RET_INT64;
else
#endif
{
do_struct:
switch (cabi)
{
case FFI_THISCALL:
case FFI_FASTCALL:
case FFI_STDCALL:
case FFI_MS_CDECL:
flags = X86_RET_STRUCTARG;
break;
default:
flags = X86_RET_STRUCTPOP;
break;
}
bytes += FFI_ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
}
break;
case FFI_TYPE_COMPLEX:
switch (cif->rtype->elements[0]->type)
{
case FFI_TYPE_DOUBLE:
case FFI_TYPE_LONGDOUBLE:
case FFI_TYPE_SINT64:
case FFI_TYPE_UINT64:
goto do_struct;
case FFI_TYPE_FLOAT:
case FFI_TYPE_INT:
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
flags = X86_RET_INT64;
break;
case FFI_TYPE_SINT16:
case FFI_TYPE_UINT16:
flags = X86_RET_INT32;
break;
case FFI_TYPE_SINT8:
case FFI_TYPE_UINT8:
flags = X86_RET_STRUCT_2B;
break;
default:
return FFI_BAD_TYPEDEF;
}
break;
default:
return FFI_BAD_TYPEDEF;
}
cif->flags = flags;
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *t = cif->arg_types[i];
bytes = FFI_ALIGN (bytes, t->alignment);
bytes += FFI_ALIGN (t->size, FFI_SIZEOF_ARG);
}
cif->bytes = bytes;
return FFI_OK;
}
static ffi_arg
extend_basic_type(void *arg, int type)
{
switch (type)
{
case FFI_TYPE_SINT8:
return *(SINT8 *)arg;
case FFI_TYPE_UINT8:
return *(UINT8 *)arg;
case FFI_TYPE_SINT16:
return *(SINT16 *)arg;
case FFI_TYPE_UINT16:
return *(UINT16 *)arg;
case FFI_TYPE_SINT32:
case FFI_TYPE_UINT32:
case FFI_TYPE_POINTER:
case FFI_TYPE_FLOAT:
return *(UINT32 *)arg;
default:
abort();
}
}
struct call_frame
{
void *ebp;
void *retaddr;
void (*fn)(void);
int flags;
void *rvalue;
unsigned regs[3];
};
struct abi_params
{
int dir;
int static_chain;
int nregs;
int regs[3];
};
static const struct abi_params abi_params[FFI_LAST_ABI] = {
[FFI_SYSV] = { 1, R_ECX, 0 },
[FFI_THISCALL] = { 1, R_EAX, 1, { R_ECX } },
[FFI_FASTCALL] = { 1, R_EAX, 2, { R_ECX, R_EDX } },
[FFI_STDCALL] = { 1, R_ECX, 0 },
[FFI_PASCAL] = { -1, R_ECX, 0 },
[FFI_REGISTER] = { -1, R_ECX, 3, { R_EAX, R_EDX, R_ECX } },
[FFI_MS_CDECL] = { 1, R_ECX, 0 }
};
#ifdef HAVE_FASTCALL
#ifdef _MSC_VER
#define FFI_DECLARE_FASTCALL __fastcall
#else
#define FFI_DECLARE_FASTCALL __declspec(fastcall)
#endif
#else
#define FFI_DECLARE_FASTCALL
#endif
extern void FFI_DECLARE_FASTCALL ffi_call_i386(struct call_frame *, char *) FFI_HIDDEN;
static void
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
void **avalue, void *closure)
{
size_t rsize, bytes;
struct call_frame *frame;
char *stack, *argp;
ffi_type **arg_types;
int flags, cabi, i, n, dir, narg_reg;
const struct abi_params *pabi;
flags = cif->flags;
cabi = cif->abi;
pabi = &abi_params[cabi];
dir = pabi->dir;
rsize = 0;
if (rvalue == NULL)
{
switch (flags)
{
case X86_RET_FLOAT:
case X86_RET_DOUBLE:
case X86_RET_LDOUBLE:
case X86_RET_STRUCTPOP:
case X86_RET_STRUCTARG:
rsize = cif->rtype->size;
break;
default:
flags = X86_RET_VOID;
break;
}
}
bytes = STACK_ALIGN (cif->bytes);
stack = alloca(bytes + sizeof(*frame) + rsize);
argp = (dir < 0 ? stack + bytes : stack);
frame = (struct call_frame *)(stack + bytes);
if (rsize)
rvalue = frame + 1;
frame->fn = fn;
frame->flags = flags;
frame->rvalue = rvalue;
frame->regs[pabi->static_chain] = (unsigned)closure;
narg_reg = 0;
switch (flags)
{
case X86_RET_STRUCTARG:
if (pabi->nregs > 0)
{
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
narg_reg = 1;
break;
}
case X86_RET_STRUCTPOP:
*(void **)argp = rvalue;
argp += sizeof(void *);
break;
}
arg_types = cif->arg_types;
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *ty = arg_types[i];
void *valp = avalue[i];
size_t z = ty->size;
int t = ty->type;
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT)
{
ffi_arg val = extend_basic_type (valp, t);
if (t != FFI_TYPE_FLOAT && narg_reg < pabi->nregs)
frame->regs[pabi->regs[narg_reg++]] = val;
else if (dir < 0)
{
argp -= 4;
*(ffi_arg *)argp = val;
}
else
{
*(ffi_arg *)argp = val;
argp += 4;
}
}
else
{
size_t za = FFI_ALIGN (z, FFI_SIZEOF_ARG);
size_t align = FFI_SIZEOF_ARG;
if ((cabi == FFI_THISCALL || cabi == FFI_FASTCALL)
&& (t == FFI_TYPE_SINT64
|| t == FFI_TYPE_UINT64
|| t == FFI_TYPE_STRUCT))
narg_reg = 2;
if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
align = 16;
if (dir < 0)
{
argp -= za;
memcpy (argp, valp, z);
}
else
{
argp = (char *)FFI_ALIGN (argp, align);
memcpy (argp, valp, z);
argp += za;
}
}
}
FFI_ASSERT (dir > 0 || argp == stack);
ffi_call_i386 (frame, stack);
}
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
void FFI_HIDDEN ffi_closure_i386(void);
void FFI_HIDDEN ffi_closure_STDCALL(void);
void FFI_HIDDEN ffi_closure_REGISTER(void);
struct closure_frame
{
unsigned rettemp[4];
unsigned regs[3];
ffi_cif *cif;
void (*fun)(ffi_cif*,void*,void**,void*);
void *user_data;
};
int FFI_HIDDEN FFI_DECLARE_FASTCALL
ffi_closure_inner (struct closure_frame *frame, char *stack)
{
ffi_cif *cif = frame->cif;
int cabi, i, n, flags, dir, narg_reg;
const struct abi_params *pabi;
ffi_type **arg_types;
char *argp;
void *rvalue;
void **avalue;
cabi = cif->abi;
flags = cif->flags;
narg_reg = 0;
rvalue = frame->rettemp;
pabi = &abi_params[cabi];
dir = pabi->dir;
argp = (dir < 0 ? stack + STACK_ALIGN (cif->bytes) : stack);
switch (flags)
{
case X86_RET_STRUCTARG:
if (pabi->nregs > 0)
{
rvalue = (void *)frame->regs[pabi->regs[0]];
narg_reg = 1;
frame->rettemp[0] = (unsigned)rvalue;
break;
}
case X86_RET_STRUCTPOP:
rvalue = *(void **)argp;
argp += sizeof(void *);
frame->rettemp[0] = (unsigned)rvalue;
break;
}
n = cif->nargs;
avalue = alloca(sizeof(void *) * n);
arg_types = cif->arg_types;
for (i = 0; i < n; ++i)
{
ffi_type *ty = arg_types[i];
size_t z = ty->size;
int t = ty->type;
void *valp;
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT)
{
if (t != FFI_TYPE_FLOAT && narg_reg < pabi->nregs)
valp = &frame->regs[pabi->regs[narg_reg++]];
else if (dir < 0)
{
argp -= 4;
valp = argp;
}
else
{
valp = argp;
argp += 4;
}
}
else
{
size_t za = FFI_ALIGN (z, FFI_SIZEOF_ARG);
size_t align = FFI_SIZEOF_ARG;
if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
align = 16;
if ((cabi == FFI_THISCALL || cabi == FFI_FASTCALL)
&& (t == FFI_TYPE_SINT64
|| t == FFI_TYPE_UINT64
|| t == FFI_TYPE_STRUCT))
narg_reg = 2;
if (dir < 0)
{
argp -= za;
valp = argp;
}
else
{
argp = (char *)FFI_ALIGN (argp, align);
valp = argp;
argp += za;
}
}
avalue[i] = valp;
}
frame->fun (cif, rvalue, avalue, frame->user_data);
if (cabi == FFI_STDCALL)
return flags + (cif->bytes << X86_RET_POP_SHIFT);
else
return flags;
}
ffi_status
ffi_prep_closure_loc (ffi_closure* closure,
ffi_cif* cif,
void (*fun)(ffi_cif*,void*,void**,void*),
void *user_data,
void *codeloc)
{
char *tramp = closure->tramp;
void (*dest)(void);
int op = 0xb8;
switch (cif->abi)
{
case FFI_SYSV:
case FFI_THISCALL:
case FFI_FASTCALL:
case FFI_MS_CDECL:
dest = ffi_closure_i386;
break;
case FFI_STDCALL:
case FFI_PASCAL:
dest = ffi_closure_STDCALL;
break;
case FFI_REGISTER:
dest = ffi_closure_REGISTER;
op = 0x68;
break;
default:
return FFI_BAD_ABI;
}
tramp[0] = op;
*(void **)(tramp + 1) = codeloc;
tramp[5] = 0xe9;
*(unsigned *)(tramp + 6) = (unsigned)dest - ((unsigned)codeloc + 10);
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
#ifdef FFI_GO_CLOSURES
void FFI_HIDDEN ffi_go_closure_EAX(void);
void FFI_HIDDEN ffi_go_closure_ECX(void);
void FFI_HIDDEN ffi_go_closure_STDCALL(void);
ffi_status
ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
void (*fun)(ffi_cif*,void*,void**,void*))
{
void (*dest)(void);
switch (cif->abi)
{
case FFI_SYSV:
case FFI_MS_CDECL:
dest = ffi_go_closure_ECX;
break;
case FFI_THISCALL:
case FFI_FASTCALL:
dest = ffi_go_closure_EAX;
break;
case FFI_STDCALL:
case FFI_PASCAL:
dest = ffi_go_closure_STDCALL;
break;
case FFI_REGISTER:
default:
return FFI_BAD_ABI;
}
closure->tramp = dest;
closure->cif = cif;
closure->fun = fun;
return FFI_OK;
}
#endif
#if !FFI_NO_RAW_API
void FFI_HIDDEN ffi_closure_raw_SYSV(void);
void FFI_HIDDEN ffi_closure_raw_THISCALL(void);
ffi_status
ffi_prep_raw_closure_loc (ffi_raw_closure *closure,
ffi_cif *cif,
void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
void *user_data,
void *codeloc)
{
char *tramp = closure->tramp;
void (*dest)(void);
int i;
for (i = cif->nargs-1; i >= 0; i--)
switch (cif->arg_types[i]->type)
{
case FFI_TYPE_STRUCT:
case FFI_TYPE_LONGDOUBLE:
return FFI_BAD_TYPEDEF;
}
switch (cif->abi)
{
case FFI_THISCALL:
dest = ffi_closure_raw_THISCALL;
break;
case FFI_SYSV:
dest = ffi_closure_raw_SYSV;
break;
default:
return FFI_BAD_ABI;
}
tramp[0] = 0xb8;
*(void **)(tramp + 1) = codeloc;
tramp[5] = 0xe9;
*(unsigned *)(tramp + 6) = (unsigned)dest - ((unsigned)codeloc + 10);
closure->cif = cif;
closure->fun = fun;
closure->user_data = user_data;
return FFI_OK;
}
void
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue)
{
size_t rsize, bytes;
struct call_frame *frame;
char *stack, *argp;
ffi_type **arg_types;
int flags, cabi, i, n, narg_reg;
const struct abi_params *pabi;
flags = cif->flags;
cabi = cif->abi;
pabi = &abi_params[cabi];
rsize = 0;
if (rvalue == NULL)
{
switch (flags)
{
case X86_RET_FLOAT:
case X86_RET_DOUBLE:
case X86_RET_LDOUBLE:
case X86_RET_STRUCTPOP:
case X86_RET_STRUCTARG:
rsize = cif->rtype->size;
break;
default:
flags = X86_RET_VOID;
break;
}
}
bytes = STACK_ALIGN (cif->bytes);
argp = stack =
(void *)((uintptr_t)alloca(bytes + sizeof(*frame) + rsize + 15) & ~16);
frame = (struct call_frame *)(stack + bytes);
if (rsize)
rvalue = frame + 1;
frame->fn = fn;
frame->flags = flags;
frame->rvalue = rvalue;
narg_reg = 0;
switch (flags)
{
case X86_RET_STRUCTARG:
if (pabi->nregs > 0)
{
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
narg_reg = 1;
break;
}
case X86_RET_STRUCTPOP:
*(void **)argp = rvalue;
argp += sizeof(void *);
bytes -= sizeof(void *);
break;
}
arg_types = cif->arg_types;
for (i = 0, n = cif->nargs; narg_reg < pabi->nregs && i < n; i++)
{
ffi_type *ty = arg_types[i];
size_t z = ty->size;
int t = ty->type;
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT && t != FFI_TYPE_FLOAT)
{
ffi_arg val = extend_basic_type (avalue, t);
frame->regs[pabi->regs[narg_reg++]] = val;
z = FFI_SIZEOF_ARG;
}
else
{
memcpy (argp, avalue, z);
z = FFI_ALIGN (z, FFI_SIZEOF_ARG);
argp += z;
}
avalue += z;
bytes -= z;
}
if (i < n)
memcpy (argp, avalue, bytes);
ffi_call_i386 (frame, stack);
}
#endif
#endif