x86: Rewrite ffi_call
Decouple the assembly from FFI_TYPE_*. Merge prep_args with ffi_call, passing the frame and the stack to the assembly. Note that this patch isn't really standalone, as this breaks closures.
This commit is contained in:
609
src/x86/ffi.c
609
src/x86/ffi.c
@@ -29,10 +29,10 @@
|
||||
----------------------------------------------------------------------- */
|
||||
|
||||
#ifndef __x86_64__
|
||||
|
||||
#include <ffi.h>
|
||||
#include <ffi_common.h>
|
||||
#include <stdlib.h>
|
||||
#include "internal.h"
|
||||
|
||||
/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE;
|
||||
all further uses in this file will refer to the 80-bit type. */
|
||||
@@ -45,276 +45,277 @@
|
||||
# define FFI_TYPE_LONGDOUBLE 4
|
||||
#endif
|
||||
|
||||
#if defined(__GNUC__) && !defined(__declspec)
|
||||
# define __declspec(x) __attribute__((x))
|
||||
#endif
|
||||
|
||||
/* ffi_prep_args is called by the assembly routine once stack space
|
||||
has been allocated for the function's arguments */
|
||||
|
||||
unsigned int ffi_prep_args(char *stack, extended_cif *ecif);
|
||||
unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
|
||||
/* Perform machine dependent cif processing. */
|
||||
ffi_status FFI_HIDDEN
|
||||
ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
{
|
||||
register unsigned int i;
|
||||
register void **p_argv;
|
||||
register char *argp;
|
||||
register ffi_type **p_arg;
|
||||
const int cabi = ecif->cif->abi;
|
||||
const int dir = (cabi == FFI_PASCAL || cabi == FFI_REGISTER) ? -1 : +1;
|
||||
unsigned int stack_args_count = 0;
|
||||
void *p_stack_data[3];
|
||||
char *argp2 = stack;
|
||||
size_t bytes = 0;
|
||||
int i, n, flags, cabi = cif->abi;
|
||||
|
||||
argp = stack;
|
||||
|
||||
if ((ecif->cif->flags == FFI_TYPE_STRUCT
|
||||
|| ecif->cif->flags == FFI_TYPE_MS_STRUCT))
|
||||
switch (cabi)
|
||||
{
|
||||
/* For fastcall/thiscall/register this is first register-passed
|
||||
argument. */
|
||||
if (cabi == FFI_THISCALL || cabi == FFI_FASTCALL || cabi == FFI_REGISTER)
|
||||
{
|
||||
p_stack_data[stack_args_count] = argp;
|
||||
++stack_args_count;
|
||||
}
|
||||
|
||||
*(void **) argp = ecif->rvalue;
|
||||
argp += sizeof(void*);
|
||||
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;
|
||||
}
|
||||
|
||||
p_arg = ecif->cif->arg_types;
|
||||
p_argv = ecif->avalue;
|
||||
if (dir < 0)
|
||||
{
|
||||
const int nargs = ecif->cif->nargs - 1;
|
||||
if (nargs > 0)
|
||||
{
|
||||
p_arg += nargs;
|
||||
p_argv += nargs;
|
||||
}
|
||||
}
|
||||
|
||||
for (i = ecif->cif->nargs;
|
||||
i != 0;
|
||||
i--, p_arg += dir, p_argv += dir)
|
||||
{
|
||||
/* Align if necessary */
|
||||
if ((sizeof(void*) - 1) & (size_t) argp)
|
||||
argp = (char *) ALIGN(argp, sizeof(void*));
|
||||
|
||||
size_t z = (*p_arg)->size;
|
||||
|
||||
if (z < FFI_SIZEOF_ARG)
|
||||
{
|
||||
z = FFI_SIZEOF_ARG;
|
||||
switch ((*p_arg)->type)
|
||||
{
|
||||
case FFI_TYPE_SINT8:
|
||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT8 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_UINT8:
|
||||
*(ffi_arg *) argp = (ffi_arg)*(UINT8 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_SINT16:
|
||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT16 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_UINT16:
|
||||
*(ffi_arg *) argp = (ffi_arg)*(UINT16 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_SINT32:
|
||||
*(ffi_sarg *) argp = (ffi_sarg)*(SINT32 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_UINT32:
|
||||
*(ffi_arg *) argp = (ffi_arg)*(UINT32 *)(* p_argv);
|
||||
break;
|
||||
|
||||
case FFI_TYPE_STRUCT:
|
||||
*(ffi_arg *) argp = *(ffi_arg *)(* p_argv);
|
||||
break;
|
||||
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy(argp, *p_argv, z);
|
||||
}
|
||||
|
||||
/* For thiscall/fastcall/register convention register-passed arguments
|
||||
are the first two none-floating-point arguments with a size
|
||||
smaller or equal to sizeof (void*). */
|
||||
if ((z == FFI_SIZEOF_ARG)
|
||||
&& ((cabi == FFI_REGISTER)
|
||||
|| (cabi == FFI_THISCALL && stack_args_count < 1)
|
||||
|| (cabi == FFI_FASTCALL && stack_args_count < 2))
|
||||
&& ((*p_arg)->type != FFI_TYPE_FLOAT && (*p_arg)->type != FFI_TYPE_STRUCT)
|
||||
)
|
||||
{
|
||||
if (dir < 0 && stack_args_count > 2)
|
||||
{
|
||||
/* Iterating arguments backwards, so first register-passed argument
|
||||
will be passed last. Shift temporary values to make place. */
|
||||
p_stack_data[0] = p_stack_data[1];
|
||||
p_stack_data[1] = p_stack_data[2];
|
||||
stack_args_count = 2;
|
||||
}
|
||||
|
||||
p_stack_data[stack_args_count] = argp;
|
||||
++stack_args_count;
|
||||
}
|
||||
|
||||
argp += z;
|
||||
}
|
||||
|
||||
/* We need to move the register-passed arguments for thiscall,
|
||||
fastcall, register on top of stack, so that those can be moved
|
||||
to registers by call-handler. */
|
||||
if (stack_args_count > 0)
|
||||
{
|
||||
if (dir < 0 && stack_args_count > 1)
|
||||
{
|
||||
/* Reverse order if iterating arguments backwards */
|
||||
ffi_arg tmp = *(ffi_arg*) p_stack_data[0];
|
||||
*(ffi_arg*) p_stack_data[0] = *(ffi_arg*) p_stack_data[stack_args_count - 1];
|
||||
*(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp;
|
||||
}
|
||||
|
||||
int i;
|
||||
for (i = 0; i < stack_args_count; i++)
|
||||
{
|
||||
if (p_stack_data[i] != argp2)
|
||||
{
|
||||
ffi_arg tmp = *(ffi_arg*) p_stack_data[i];
|
||||
memmove (argp2 + FFI_SIZEOF_ARG, argp2, (size_t) ((char*) p_stack_data[i] - (char*)argp2));
|
||||
*(ffi_arg *) argp2 = tmp;
|
||||
}
|
||||
|
||||
argp2 += FFI_SIZEOF_ARG;
|
||||
}
|
||||
}
|
||||
|
||||
return stack_args_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Perform machine dependent cif processing */
|
||||
ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
|
||||
{
|
||||
unsigned int i;
|
||||
ffi_type **ptr;
|
||||
|
||||
/* Set the return type flag */
|
||||
switch (cif->rtype->type)
|
||||
{
|
||||
case FFI_TYPE_VOID:
|
||||
case FFI_TYPE_UINT8:
|
||||
case FFI_TYPE_UINT16:
|
||||
case FFI_TYPE_SINT8:
|
||||
case FFI_TYPE_SINT16:
|
||||
case FFI_TYPE_SINT64:
|
||||
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:
|
||||
cif->flags = (unsigned) cif->rtype->type;
|
||||
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:
|
||||
cif->flags = FFI_TYPE_SINT64;
|
||||
flags = X86_RET_INT64;
|
||||
break;
|
||||
|
||||
case FFI_TYPE_STRUCT:
|
||||
#ifndef X86
|
||||
/* ??? This should be a different ABI rather than an ifdef. */
|
||||
if (cif->rtype->size == 1)
|
||||
cif->flags = FFI_TYPE_SMALL_STRUCT_1B; /* same as char size */
|
||||
flags = X86_RET_STRUCT_1B;
|
||||
else if (cif->rtype->size == 2)
|
||||
cif->flags = FFI_TYPE_SMALL_STRUCT_2B; /* same as short size */
|
||||
flags = X86_RET_STRUCT_2B;
|
||||
else if (cif->rtype->size == 4)
|
||||
cif->flags = FFI_TYPE_INT; /* same as int type */
|
||||
flags = X86_RET_INT32;
|
||||
else if (cif->rtype->size == 8)
|
||||
cif->flags = FFI_TYPE_SINT64; /* same as int64 type */
|
||||
flags = X86_RET_INT64;
|
||||
else
|
||||
#endif
|
||||
{
|
||||
if (cif->abi == FFI_MS_CDECL)
|
||||
cif->flags = FFI_TYPE_MS_STRUCT;
|
||||
else
|
||||
cif->flags = FFI_TYPE_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;
|
||||
}
|
||||
/* Allocate space for return value pointer. */
|
||||
cif->bytes += ALIGN(sizeof(void*), FFI_SIZEOF_ARG);
|
||||
bytes += ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
cif->flags = FFI_TYPE_INT;
|
||||
break;
|
||||
return FFI_BAD_TYPEDEF;
|
||||
}
|
||||
cif->flags = flags;
|
||||
|
||||
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
|
||||
for (i = 0, n = cif->nargs; i < n; i++)
|
||||
{
|
||||
if (((*ptr)->alignment - 1) & cif->bytes)
|
||||
cif->bytes = ALIGN(cif->bytes, (*ptr)->alignment);
|
||||
cif->bytes += (unsigned)ALIGN((*ptr)->size, FFI_SIZEOF_ARG);
|
||||
}
|
||||
ffi_type *t = cif->arg_types[i];
|
||||
|
||||
if (cif->abi == FFI_SYSV)
|
||||
cif->bytes = ALIGN (cif->bytes, 15);
|
||||
bytes = ALIGN (bytes, t->alignment);
|
||||
bytes += ALIGN (t->size, FFI_SIZEOF_ARG);
|
||||
}
|
||||
cif->bytes = ALIGN (bytes, 16);
|
||||
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
extern void
|
||||
ffi_call_win32(unsigned int (*)(char *, extended_cif *), extended_cif *,
|
||||
unsigned, unsigned, unsigned, unsigned *, void (*fn)(void));
|
||||
extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *,
|
||||
unsigned, unsigned, unsigned *, void (*fn)(void));
|
||||
|
||||
void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
static ffi_arg
|
||||
extend_basic_type(void *arg, int type)
|
||||
{
|
||||
extended_cif ecif;
|
||||
|
||||
ecif.cif = cif;
|
||||
ecif.avalue = avalue;
|
||||
|
||||
/* If the return value is a struct and we don't have a return */
|
||||
/* value address then we need to make one */
|
||||
|
||||
if (rvalue == NULL
|
||||
&& (cif->flags == FFI_TYPE_STRUCT
|
||||
|| cif->flags == FFI_TYPE_MS_STRUCT))
|
||||
switch (type)
|
||||
{
|
||||
ecif.rvalue = alloca(cif->rtype->size);
|
||||
}
|
||||
else
|
||||
ecif.rvalue = rvalue;
|
||||
|
||||
|
||||
switch (cif->abi)
|
||||
{
|
||||
#ifndef X86_WIN32
|
||||
case FFI_SYSV:
|
||||
ffi_call_SYSV(ffi_prep_args, &ecif, cif->bytes, cif->flags, ecif.rvalue,
|
||||
fn);
|
||||
break;
|
||||
#else
|
||||
case FFI_SYSV:
|
||||
case FFI_MS_CDECL:
|
||||
#endif
|
||||
case FFI_STDCALL:
|
||||
case FFI_THISCALL:
|
||||
case FFI_FASTCALL:
|
||||
case FFI_PASCAL:
|
||||
case FFI_REGISTER:
|
||||
ffi_call_win32(ffi_prep_args, &ecif, cif->abi, cif->bytes, cif->flags,
|
||||
ecif.rvalue, fn);
|
||||
break;
|
||||
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:
|
||||
FFI_ASSERT(0);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
struct call_frame
|
||||
{
|
||||
void *ebp; /* 0 */
|
||||
void *retaddr; /* 4 */
|
||||
void (*fn)(void); /* 8 */
|
||||
int flags; /* 12 */
|
||||
void *rvalue; /* 16 */
|
||||
unsigned regs[3]; /* 20-28 */
|
||||
};
|
||||
|
||||
struct abi_params
|
||||
{
|
||||
int dir; /* parameter growth direction */
|
||||
int nregs; /* number of register parameters */
|
||||
int regs[3];
|
||||
};
|
||||
|
||||
static const struct abi_params abi_params[FFI_LAST_ABI] = {
|
||||
[FFI_SYSV] = { 1, 0 },
|
||||
[FFI_THISCALL] = { 1, 1, { R_ECX } },
|
||||
[FFI_FASTCALL] = { 1, 2, { R_ECX, R_EDX } },
|
||||
[FFI_STDCALL] = { 1, 0 },
|
||||
[FFI_PASCAL] = { -1, 0 },
|
||||
[FFI_REGISTER] = { -1, 3, { R_EAX, R_EDX, R_ECX } },
|
||||
[FFI_MS_CDECL] = { 1, 0 }
|
||||
};
|
||||
|
||||
extern void ffi_call_i386(struct call_frame *, char *)
|
||||
FFI_HIDDEN __declspec(fastcall);
|
||||
|
||||
void
|
||||
ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
||||
{
|
||||
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:
|
||||
/* The float cases need to pop the 387 stack.
|
||||
The struct cases need to pass a valid pointer to the callee. */
|
||||
rsize = cif->rtype->size;
|
||||
break;
|
||||
default:
|
||||
/* We can pretend that the callee returns nothing. */
|
||||
flags = X86_RET_VOID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bytes = 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;
|
||||
|
||||
narg_reg = 0;
|
||||
switch (flags)
|
||||
{
|
||||
case X86_RET_STRUCTARG:
|
||||
/* The pointer is passed as the first argument. */
|
||||
if (pabi->nregs > 0)
|
||||
{
|
||||
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
|
||||
narg_reg = 1;
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
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 = ALIGN (z, FFI_SIZEOF_ARG);
|
||||
if (dir < 0)
|
||||
{
|
||||
argp -= za;
|
||||
memcpy (argp, valp, z);
|
||||
}
|
||||
else
|
||||
{
|
||||
memcpy (argp, valp, z);
|
||||
argp += za;
|
||||
}
|
||||
}
|
||||
}
|
||||
FFI_ASSERT (dir > 0 || argp == stack);
|
||||
|
||||
ffi_call_i386 (frame, stack);
|
||||
}
|
||||
|
||||
|
||||
@@ -641,88 +642,92 @@ ffi_prep_raw_closure_loc (ffi_raw_closure* closure,
|
||||
return FFI_OK;
|
||||
}
|
||||
|
||||
static unsigned int
|
||||
ffi_prep_args_raw(char *stack, extended_cif *ecif)
|
||||
{
|
||||
const ffi_cif *cif = ecif->cif;
|
||||
unsigned int i, passed_regs = 0;
|
||||
|
||||
const unsigned int abi = cif->abi;
|
||||
const unsigned int max_regs = (abi == FFI_THISCALL) ? 1
|
||||
: (abi == FFI_FASTCALL) ? 2
|
||||
: (abi == FFI_REGISTER) ? 3
|
||||
: 0;
|
||||
|
||||
if (cif->flags == FFI_TYPE_STRUCT)
|
||||
++passed_regs;
|
||||
|
||||
for (i = 0; i < cif->nargs && passed_regs <= max_regs; i++)
|
||||
{
|
||||
if (cif->arg_types[i]->type == FFI_TYPE_FLOAT
|
||||
|| cif->arg_types[i]->type == FFI_TYPE_STRUCT)
|
||||
continue;
|
||||
|
||||
size_t sz = cif->arg_types[i]->size;
|
||||
if (sz == 0 || sz > FFI_SIZEOF_ARG)
|
||||
continue;
|
||||
|
||||
++passed_regs;
|
||||
}
|
||||
|
||||
memcpy (stack, ecif->avalue, cif->bytes);
|
||||
return passed_regs;
|
||||
}
|
||||
|
||||
/* we borrow this routine from libffi (it must be changed, though, to
|
||||
* actually call the function passed in the first argument. as of
|
||||
* libffi-1.20, this is not the case.)
|
||||
*/
|
||||
|
||||
void
|
||||
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *fake_avalue)
|
||||
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue)
|
||||
{
|
||||
extended_cif ecif;
|
||||
void **avalue = (void **)fake_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;
|
||||
|
||||
ecif.cif = cif;
|
||||
ecif.avalue = avalue;
|
||||
|
||||
/* If the return value is a struct and we don't have a return */
|
||||
/* value address then we need to make one */
|
||||
flags = cif->flags;
|
||||
cabi = cif->abi;
|
||||
pabi = &abi_params[cabi];
|
||||
|
||||
if (rvalue == NULL
|
||||
&& (cif->flags == FFI_TYPE_STRUCT
|
||||
|| cif->flags == FFI_TYPE_MS_STRUCT))
|
||||
rsize = 0;
|
||||
if (rvalue == NULL)
|
||||
{
|
||||
ecif.rvalue = alloca(cif->rtype->size);
|
||||
switch (flags)
|
||||
{
|
||||
case X86_RET_FLOAT:
|
||||
case X86_RET_DOUBLE:
|
||||
case X86_RET_LDOUBLE:
|
||||
case X86_RET_STRUCTPOP:
|
||||
case X86_RET_STRUCTARG:
|
||||
/* The float cases need to pop the 387 stack.
|
||||
The struct cases need to pass a valid pointer to the callee. */
|
||||
rsize = cif->rtype->size;
|
||||
break;
|
||||
default:
|
||||
/* We can pretend that the callee returns nothing. */
|
||||
flags = X86_RET_VOID;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
ecif.rvalue = rvalue;
|
||||
|
||||
|
||||
switch (cif->abi)
|
||||
|
||||
bytes = cif->bytes;
|
||||
argp = stack = alloca(bytes + sizeof(*frame) + rsize);
|
||||
frame = (struct call_frame *)(stack + bytes);
|
||||
if (rsize)
|
||||
rvalue = frame + 1;
|
||||
|
||||
narg_reg = 0;
|
||||
switch (flags)
|
||||
{
|
||||
#ifndef X86_WIN32
|
||||
case FFI_SYSV:
|
||||
ffi_call_SYSV(ffi_prep_args_raw, &ecif, cif->bytes, cif->flags,
|
||||
ecif.rvalue, fn);
|
||||
break;
|
||||
#else
|
||||
case FFI_SYSV:
|
||||
case FFI_MS_CDECL:
|
||||
#endif
|
||||
case FFI_STDCALL:
|
||||
case FFI_THISCALL:
|
||||
case FFI_FASTCALL:
|
||||
case FFI_PASCAL:
|
||||
case FFI_REGISTER:
|
||||
ffi_call_win32(ffi_prep_args_raw, &ecif, cif->abi, cif->bytes, cif->flags,
|
||||
ecif.rvalue, fn);
|
||||
break;
|
||||
default:
|
||||
FFI_ASSERT(0);
|
||||
case X86_RET_STRUCTARG:
|
||||
/* The pointer is passed as the first argument. */
|
||||
if (pabi->nregs > 0)
|
||||
{
|
||||
frame->regs[pabi->regs[0]] = (unsigned)rvalue;
|
||||
narg_reg = 1;
|
||||
break;
|
||||
}
|
||||
/* fallthru */
|
||||
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 = ALIGN (z, FFI_SIZEOF_ARG);
|
||||
argp += z;
|
||||
}
|
||||
avalue += z;
|
||||
bytes -= z;
|
||||
}
|
||||
if (i < n)
|
||||
memcpy (argp, avalue, bytes);
|
||||
|
||||
ffi_call_i386 (frame, stack);
|
||||
}
|
||||
#endif /* !FFI_NO_RAW_API */
|
||||
#endif /* !__x86_64__ */
|
||||
|
||||
@@ -98,11 +98,7 @@ typedef enum ffi_abi {
|
||||
FFI_PASCAL = 6,
|
||||
FFI_REGISTER = 7,
|
||||
FFI_LAST_ABI,
|
||||
# ifdef _MSC_VER
|
||||
FFI_DEFAULT_ABI = FFI_MS_CDECL
|
||||
# else
|
||||
FFI_DEFAULT_ABI = FFI_SYSV
|
||||
# endif
|
||||
#else
|
||||
FFI_FIRST_ABI = 0,
|
||||
FFI_SYSV = 1,
|
||||
|
||||
23
src/x86/internal.h
Normal file
23
src/x86/internal.h
Normal file
@@ -0,0 +1,23 @@
|
||||
#define X86_RET_FLOAT 0
|
||||
#define X86_RET_DOUBLE 1
|
||||
#define X86_RET_LDOUBLE 2
|
||||
#define X86_RET_SINT8 3
|
||||
#define X86_RET_SINT16 4
|
||||
#define X86_RET_UINT8 5
|
||||
#define X86_RET_UINT16 6
|
||||
#define X86_RET_INT64 7
|
||||
#define X86_RET_INT32 8
|
||||
#define X86_RET_VOID 9
|
||||
#define X86_RET_STRUCTPOP 10
|
||||
#define X86_RET_STRUCTARG 11
|
||||
#define X86_RET_STRUCT_1B 12
|
||||
#define X86_RET_STRUCT_2B 13
|
||||
#define X86_RET_UNUSED14 14
|
||||
#define X86_RET_UNUSED15 15
|
||||
|
||||
#define X86_RET_TYPE_MASK 15
|
||||
#define X86_RET_POP_SHIFT 4
|
||||
|
||||
#define R_EAX 0
|
||||
#define R_EDX 1
|
||||
#define R_ECX 2
|
||||
243
src/x86/sysv.S
243
src/x86/sysv.S
@@ -31,143 +31,144 @@
|
||||
#include <fficonfig.h>
|
||||
#include <ffi.h>
|
||||
#include <ffi_cfi.h>
|
||||
#include "internal.h"
|
||||
|
||||
.text
|
||||
#define C2(X, Y) X ## Y
|
||||
#define C1(X, Y) C2(X, Y)
|
||||
#ifdef __USER_LABEL_PREFIX__
|
||||
# define C(X) C1(__USER_LABEL_PREFIX__, X)
|
||||
#else
|
||||
# define C(X) X
|
||||
#endif
|
||||
|
||||
.globl ffi_prep_args
|
||||
#ifdef __ELF__
|
||||
# define ENDF(X) .type X,@function; .size X, . - X
|
||||
#else
|
||||
# define ENDF(X)
|
||||
#endif
|
||||
|
||||
.align 4
|
||||
.globl ffi_call_SYSV
|
||||
.type ffi_call_SYSV,@function
|
||||
/* This macro allows the safe creation of jump tables without an
|
||||
actual table. The entry points into the table are all 8 bytes.
|
||||
The use of ORG asserts that we're at the correct location. */
|
||||
#define E(X) .align 8; .org 0b + X * 8
|
||||
|
||||
ffi_call_SYSV:
|
||||
.text
|
||||
.align 16
|
||||
.globl C(ffi_call_i386)
|
||||
FFI_HIDDEN(C(ffi_call_i386))
|
||||
|
||||
/* This is declared as
|
||||
|
||||
void ffi_call_i386(struct ffi_call_frame *frame, char *argp)
|
||||
__attribute__((fastcall));
|
||||
|
||||
This the arguments are present in
|
||||
|
||||
ecx: frame
|
||||
edx: argp
|
||||
*/
|
||||
|
||||
C(ffi_call_i386):
|
||||
cfi_startproc
|
||||
pushl %ebp
|
||||
cfi_adjust_cfa_offset(4)
|
||||
movl (%esp), %eax /* move the return address */
|
||||
movl %ebp, (%ecx) /* store %ebp into local frame */
|
||||
movl %eax, 4(%ecx) /* store retaddr into local frame */
|
||||
|
||||
/* New stack frame based off ebp. This is a itty bit of unwind
|
||||
trickery in that the CFA *has* changed. There is no easy way
|
||||
to describe it correctly on entry to the function. Fortunately,
|
||||
it doesn't matter too much since at all points we can correctly
|
||||
unwind back to ffi_call. Note that the location to which we
|
||||
moved the return address is (the new) CFA-4, so from the
|
||||
perspective of the unwind info, it hasn't moved. */
|
||||
movl %ecx, %ebp
|
||||
cfi_def_cfa(%ebp, 8)
|
||||
cfi_rel_offset(%ebp, 0)
|
||||
movl %esp,%ebp
|
||||
cfi_def_cfa_register(%ebp)
|
||||
/* Make room for all of the new args. */
|
||||
movl 16(%ebp),%ecx
|
||||
subl %ecx,%esp
|
||||
|
||||
/* Align the stack pointer to 16-bytes */
|
||||
andl $0xfffffff0, %esp
|
||||
movl %edx, %esp /* set outgoing argument stack */
|
||||
movl 20+R_EAX*4(%ebp), %eax /* set register arguments */
|
||||
movl 20+R_EDX*4(%ebp), %edx
|
||||
movl 20+R_ECX*4(%ebp), %ecx
|
||||
|
||||
movl %esp,%eax
|
||||
call *8(%ebp)
|
||||
|
||||
/* Place all of the ffi_prep_args in position */
|
||||
pushl 12(%ebp)
|
||||
pushl %eax
|
||||
call *8(%ebp)
|
||||
movl 12(%ebp), %ecx /* load return type code */
|
||||
movl %ebx, 8(%ebp) /* preserve %ebx */
|
||||
cfi_rel_offset(%ebp, 8)
|
||||
|
||||
/* Return stack to previous state and call the function */
|
||||
addl $8,%esp
|
||||
|
||||
call *28(%ebp)
|
||||
|
||||
/* Load %ecx with the return type code */
|
||||
movl 20(%ebp),%ecx
|
||||
|
||||
/* Protect %esi. We're going to pop it in the epilogue. */
|
||||
pushl %esi
|
||||
|
||||
/* If the return value pointer is NULL, assume no return value. */
|
||||
cmpl $0,24(%ebp)
|
||||
jne 0f
|
||||
|
||||
/* Even if there is no space for the return value, we are
|
||||
obliged to handle floating-point values. */
|
||||
cmpl $FFI_TYPE_FLOAT,%ecx
|
||||
jne noretval
|
||||
fstp %st(0)
|
||||
|
||||
jmp epilogue
|
||||
andl $X86_RET_TYPE_MASK, %ecx
|
||||
#ifdef __PIC__
|
||||
call __x86.get_pc_thunk.bx
|
||||
1: leal 0f-1b(%ebx, %ecx, 8), %ebx
|
||||
#else
|
||||
leal 0f(,%ecx, 8), %ebx
|
||||
#endif
|
||||
movl 16(%ebp), %ecx /* load result address */
|
||||
jmp *%ebx
|
||||
|
||||
.align 8
|
||||
0:
|
||||
call 1f
|
||||
E(X86_RET_FLOAT)
|
||||
fstps (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_DOUBLE)
|
||||
fstpl (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_LDOUBLE)
|
||||
fstpt (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_SINT8)
|
||||
movsbl %al, %eax
|
||||
mov %eax, (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_SINT16)
|
||||
movswl %ax, %eax
|
||||
mov %eax, (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_UINT8)
|
||||
movzbl %al, %eax
|
||||
mov %eax, (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_UINT16)
|
||||
movzwl %ax, %eax
|
||||
mov %eax, (%ecx)
|
||||
jmp 9f
|
||||
E(X86_RET_INT64)
|
||||
movl %edx, 4(%ecx)
|
||||
/* fallthru */
|
||||
E(X86_RET_INT32)
|
||||
movl %eax, (%ecx)
|
||||
/* fallthru */
|
||||
E(X86_RET_VOID)
|
||||
9: movl 8(%ebp), %ebx
|
||||
movl %ebp, %esp
|
||||
popl %ebp
|
||||
cfi_remember_state
|
||||
cfi_def_cfa(%esp, 4)
|
||||
cfi_restore(%ebx)
|
||||
cfi_restore(%ebp)
|
||||
ret
|
||||
cfi_restore_state
|
||||
|
||||
.Lstore_table:
|
||||
.long noretval-.Lstore_table /* FFI_TYPE_VOID */
|
||||
.long retint-.Lstore_table /* FFI_TYPE_INT */
|
||||
.long retfloat-.Lstore_table /* FFI_TYPE_FLOAT */
|
||||
.long retdouble-.Lstore_table /* FFI_TYPE_DOUBLE */
|
||||
.long retlongdouble-.Lstore_table /* FFI_TYPE_LONGDOUBLE */
|
||||
.long retuint8-.Lstore_table /* FFI_TYPE_UINT8 */
|
||||
.long retsint8-.Lstore_table /* FFI_TYPE_SINT8 */
|
||||
.long retuint16-.Lstore_table /* FFI_TYPE_UINT16 */
|
||||
.long retsint16-.Lstore_table /* FFI_TYPE_SINT16 */
|
||||
.long retint-.Lstore_table /* FFI_TYPE_UINT32 */
|
||||
.long retint-.Lstore_table /* FFI_TYPE_SINT32 */
|
||||
.long retint64-.Lstore_table /* FFI_TYPE_UINT64 */
|
||||
.long retint64-.Lstore_table /* FFI_TYPE_SINT64 */
|
||||
.long retstruct-.Lstore_table /* FFI_TYPE_STRUCT */
|
||||
.long retint-.Lstore_table /* FFI_TYPE_POINTER */
|
||||
E(X86_RET_STRUCTPOP)
|
||||
jmp 9b
|
||||
E(X86_RET_STRUCTARG)
|
||||
jmp 9b
|
||||
E(X86_RET_STRUCT_1B)
|
||||
movb %al, (%ecx)
|
||||
jmp 9b
|
||||
E(X86_RET_STRUCT_2B)
|
||||
movw %ax, (%ecx)
|
||||
jmp 9b
|
||||
|
||||
1:
|
||||
pop %esi
|
||||
add (%esi, %ecx, 4), %esi
|
||||
jmp *%esi
|
||||
/* Fill out the table so that bad values are predictable. */
|
||||
E(X86_RET_UNUSED14)
|
||||
ud2
|
||||
E(X86_RET_UNUSED15)
|
||||
ud2
|
||||
|
||||
/* Sign/zero extend as appropriate. */
|
||||
retsint8:
|
||||
movsbl %al, %eax
|
||||
jmp retint
|
||||
|
||||
retsint16:
|
||||
movswl %ax, %eax
|
||||
jmp retint
|
||||
|
||||
retuint8:
|
||||
movzbl %al, %eax
|
||||
jmp retint
|
||||
|
||||
retuint16:
|
||||
movzwl %ax, %eax
|
||||
jmp retint
|
||||
|
||||
retfloat:
|
||||
/* Load %ecx with the pointer to storage for the return value */
|
||||
movl 24(%ebp),%ecx
|
||||
fstps (%ecx)
|
||||
jmp epilogue
|
||||
|
||||
retdouble:
|
||||
/* Load %ecx with the pointer to storage for the return value */
|
||||
movl 24(%ebp),%ecx
|
||||
fstpl (%ecx)
|
||||
jmp epilogue
|
||||
|
||||
retlongdouble:
|
||||
/* Load %ecx with the pointer to storage for the return value */
|
||||
movl 24(%ebp),%ecx
|
||||
fstpt (%ecx)
|
||||
jmp epilogue
|
||||
|
||||
retint64:
|
||||
/* Load %ecx with the pointer to storage for the return value */
|
||||
movl 24(%ebp),%ecx
|
||||
movl %eax,0(%ecx)
|
||||
movl %edx,4(%ecx)
|
||||
jmp epilogue
|
||||
|
||||
retint:
|
||||
/* Load %ecx with the pointer to storage for the return value */
|
||||
movl 24(%ebp),%ecx
|
||||
movl %eax,0(%ecx)
|
||||
|
||||
retstruct:
|
||||
/* Nothing to do! */
|
||||
|
||||
noretval:
|
||||
epilogue:
|
||||
popl %esi
|
||||
movl %ebp,%esp
|
||||
popl %ebp
|
||||
ret
|
||||
cfi_endproc
|
||||
.ffi_call_SYSV_end:
|
||||
.size ffi_call_SYSV,.ffi_call_SYSV_end-ffi_call_SYSV
|
||||
ENDF(C(ffi_call_i386))
|
||||
|
||||
.align 4
|
||||
FFI_HIDDEN (ffi_closure_SYSV)
|
||||
|
||||
Reference in New Issue
Block a user