Determine whether register arguments (THISCALL/FASTCALL/REGISTER) are really passed via register to closures. Use stack if not.

This commit is contained in:
nielsAD
2014-08-25 17:29:44 +02:00
parent 6e346487b8
commit 5d6340ef2c
3 changed files with 215 additions and 151 deletions

View File

@@ -51,11 +51,11 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
register char *argp; register char *argp;
register ffi_type **p_arg; register ffi_type **p_arg;
#ifndef X86_WIN64 #ifndef X86_WIN64
void *p_stack_data[3];
char *argp2 = stack;
unsigned int stack_args_count = 0;
const int cabi = ecif->cif->abi; const int cabi = ecif->cif->abi;
const int dir = (cabi == FFI_PASCAL || cabi == FFI_REGISTER) ? -1 : +1; 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;
#else #else
#define dir 1 #define dir 1
#endif #endif
@@ -69,7 +69,6 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
#endif #endif
) )
{ {
*(void **) argp = ecif->rvalue;
#ifndef X86_WIN64 #ifndef X86_WIN64
/* For fastcall/thiscall/register this is first register-passed /* For fastcall/thiscall/register this is first register-passed
argument. */ argument. */
@@ -79,6 +78,8 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
++stack_args_count; ++stack_args_count;
} }
#endif #endif
*(void **) argp = ecif->rvalue;
argp += sizeof(void*); argp += sizeof(void*);
} }
@@ -86,7 +87,7 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
p_argv = ecif->avalue; p_argv = ecif->avalue;
if (dir < 0) if (dir < 0)
{ {
const unsigned int nargs = ecif->cif->nargs - 1; const int nargs = ecif->cif->nargs - 1;
if (nargs > 0) if (nargs > 0)
{ {
p_arg += nargs; p_arg += nargs;
@@ -94,15 +95,18 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
} }
} }
for (i = ecif->cif->nargs; i != 0; i--) for (i = ecif->cif->nargs;
i != 0;
i--, p_arg += dir, p_argv += dir)
{ {
/* Align if necessary */ /* Align if necessary */
if ((sizeof(void*) - 1) & (size_t) argp) if ((sizeof(void*) - 1) & (size_t) argp)
argp = (char *) ALIGN(argp, sizeof(void*)); argp = (char *) ALIGN(argp, sizeof(void*));
size_t z = (*p_arg)->size; size_t z = (*p_arg)->size;
#ifdef X86_WIN64 #ifdef X86_WIN64
if (z > sizeof(ffi_arg) if (z > FFI_SIZEOF_ARG
|| ((*p_arg)->type == FFI_TYPE_STRUCT || ((*p_arg)->type == FFI_TYPE_STRUCT
&& (z & (1 | 2 | 4 | 8)) == 0) && (z & (1 | 2 | 4 | 8)) == 0)
#if FFI_TYPE_DOUBLE != FFI_TYPE_LONGDOUBLE #if FFI_TYPE_DOUBLE != FFI_TYPE_LONGDOUBLE
@@ -110,7 +114,7 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
#endif #endif
) )
{ {
z = sizeof(ffi_arg); z = FFI_SIZEOF_ARG;
*(void **)argp = *p_argv; *(void **)argp = *p_argv;
} }
else if ((*p_arg)->type == FFI_TYPE_FLOAT) else if ((*p_arg)->type == FFI_TYPE_FLOAT)
@@ -119,9 +123,9 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
} }
else else
#endif #endif
if (z < sizeof(ffi_arg)) if (z < FFI_SIZEOF_ARG)
{ {
z = sizeof(ffi_arg); z = FFI_SIZEOF_ARG;
switch ((*p_arg)->type) switch ((*p_arg)->type)
{ {
case FFI_TYPE_SINT8: case FFI_TYPE_SINT8:
@@ -165,7 +169,7 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
/* For thiscall/fastcall/register convention register-passed arguments /* For thiscall/fastcall/register convention register-passed arguments
are the first two none-floating-point arguments with a size are the first two none-floating-point arguments with a size
smaller or equal to sizeof (void*). */ smaller or equal to sizeof (void*). */
if ((z == sizeof(ffi_arg)) if ((z == FFI_SIZEOF_ARG)
&& ((cabi == FFI_REGISTER) && ((cabi == FFI_REGISTER)
|| (cabi == FFI_THISCALL && stack_args_count < 1) || (cabi == FFI_THISCALL && stack_args_count < 1)
|| (cabi == FFI_FASTCALL && stack_args_count < 2)) || (cabi == FFI_FASTCALL && stack_args_count < 2))
@@ -186,9 +190,6 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
} }
#endif #endif
p_arg += dir;
p_argv += dir;
#ifdef X86_WIN64 #ifdef X86_WIN64
argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1); argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
#else #else
@@ -201,7 +202,6 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
on top of stack, so that those can be moved to registers by call-handler. */ on top of stack, so that those can be moved to registers by call-handler. */
if (stack_args_count > 0) if (stack_args_count > 0)
{ {
int i;
if (dir < 0 && stack_args_count > 1) if (dir < 0 && stack_args_count > 1)
{ {
/* Reverse order if iterating arguments backwards */ /* Reverse order if iterating arguments backwards */
@@ -210,16 +210,17 @@ unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
*(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp; *(ffi_arg*) p_stack_data[stack_args_count - 1] = tmp;
} }
int i;
for (i = 0; i < stack_args_count; i++) for (i = 0; i < stack_args_count; i++)
{ {
if (p_stack_data[i] != argp2) if (p_stack_data[i] != argp2)
{ {
ffi_arg tmp = *(ffi_arg*) p_stack_data[i]; ffi_arg tmp = *(ffi_arg*) p_stack_data[i];
memmove (argp2 + sizeof(ffi_arg), argp2, (size_t) ((char*) p_stack_data[i] - (char*)argp2)); memmove (argp2 + FFI_SIZEOF_ARG, argp2, (size_t) ((char*) p_stack_data[i] - (char*)argp2));
*(ffi_arg *) argp2 = tmp; *(ffi_arg *) argp2 = tmp;
} }
argp2 += sizeof(ffi_arg); argp2 += FFI_SIZEOF_ARG;
} }
} }
@@ -321,12 +322,12 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
#ifdef X86_WIN64 #ifdef X86_WIN64
/* ensure space for storing four registers */ /* ensure space for storing four registers */
cif->bytes += 4 * sizeof(ffi_arg); cif->bytes += 4 * FFI_SIZEOF_ARG;
#endif #endif
#ifndef X86_WIN32 #ifndef X86_WIN32
#ifndef X86_WIN64 #ifndef X86_WIN64
if (cif->abi != FFI_STDCALL && cif->abi != FFI_THISCALL && cif->abi != FFI_FASTCALL) if (cif->abi == FFI_SYSV || cif->abi == FFI_UNIX64)
#endif #endif
cif->bytes = (cif->bytes + 15) & ~0xF; cif->bytes = (cif->bytes + 15) & ~0xF;
#endif #endif
@@ -336,11 +337,11 @@ ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
#ifdef X86_WIN64 #ifdef X86_WIN64
extern int extern int
ffi_call_win64(void (*)(char *, extended_cif *), extended_cif *, ffi_call_win64(unsigned int (*)(char *, extended_cif *), extended_cif *,
unsigned, unsigned, unsigned *, void (*fn)(void)); unsigned, unsigned, unsigned *, void (*fn)(void));
#else #else
extern void extern void
ffi_call_win32(void (*)(char *, extended_cif *), extended_cif *, ffi_call_win32(unsigned int (*)(char *, extended_cif *), extended_cif *,
unsigned, unsigned, unsigned, unsigned *, void (*fn)(void)); unsigned, unsigned, unsigned, unsigned *, void (*fn)(void));
extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *, extern void ffi_call_SYSV(void (*)(char *, extended_cif *), extended_cif *,
unsigned, unsigned, unsigned *, void (*fn)(void)); unsigned, unsigned, unsigned *, void (*fn)(void));
@@ -359,8 +360,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
#ifdef X86_WIN64 #ifdef X86_WIN64
if (rvalue == NULL if (rvalue == NULL
&& cif->flags == FFI_TYPE_STRUCT && cif->flags == FFI_TYPE_STRUCT
&& cif->rtype->size != 1 && cif->rtype->size != 2 && ((cif->rtype->size & (1 | 2 | 4 | 8)) == 0))
&& cif->rtype->size != 4 && cif->rtype->size != 8)
{ {
ecif.rvalue = alloca((cif->rtype->size + 0xF) & ~0xF); ecif.rvalue = alloca((cif->rtype->size + 0xF) & ~0xF);
} }
@@ -413,7 +413,7 @@ void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
/* The following __attribute__((regparm(1))) decorations will have no effect /* The following __attribute__((regparm(1))) decorations will have no effect
on MSVC or SUNPRO_C -- standard conventions apply. */ on MSVC or SUNPRO_C -- standard conventions apply. */
static void ffi_prep_incoming_args_SYSV (char *stack, void **ret, static unsigned int ffi_prep_incoming_args (char *stack, void **ret,
void** args, ffi_cif* cif); void** args, ffi_cif* cif);
void FFI_HIDDEN ffi_closure_SYSV (ffi_closure *) void FFI_HIDDEN ffi_closure_SYSV (ffi_closure *)
__attribute__ ((regparm(1))); __attribute__ ((regparm(1)));
@@ -455,7 +455,7 @@ ffi_closure_win64_inner (ffi_closure *closure, void *args) {
* a structure, it will change RESP to point to the * a structure, it will change RESP to point to the
* structure return address. */ * structure return address. */
ffi_prep_incoming_args_SYSV(args, &resp, arg_area, cif); ffi_prep_incoming_args(args, &resp, arg_area, cif);
(closure->fun) (cif, resp, arg_area, closure->user_data); (closure->fun) (cif, resp, arg_area, closure->user_data);
@@ -484,7 +484,7 @@ ffi_closure_SYSV_inner (ffi_closure *closure, void **respp, void *args)
* a structure, it will change RESP to point to the * a structure, it will change RESP to point to the
* structure return address. */ * structure return address. */
ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); ffi_prep_incoming_args(args, respp, arg_area, cif);
(closure->fun) (cif, *respp, arg_area, closure->user_data); (closure->fun) (cif, *respp, arg_area, closure->user_data);
@@ -497,6 +497,7 @@ ffi_closure_WIN32_inner (ffi_closure *closure, void **respp, void *args)
/* our various things... */ /* our various things... */
ffi_cif *cif; ffi_cif *cif;
void **arg_area; void **arg_area;
unsigned int ret;
cif = closure->cif; cif = closure->cif;
arg_area = (void**) alloca (cif->nargs * sizeof (void*)); arg_area = (void**) alloca (cif->nargs * sizeof (void*));
@@ -507,72 +508,133 @@ ffi_closure_WIN32_inner (ffi_closure *closure, void **respp, void *args)
* a structure, it will change RESP to point to the * a structure, it will change RESP to point to the
* structure return address. */ * structure return address. */
ffi_prep_incoming_args_SYSV(args, respp, arg_area, cif); ret = ffi_prep_incoming_args(args, respp, arg_area, cif);
(closure->fun) (cif, *respp, arg_area, closure->user_data); (closure->fun) (cif, *respp, arg_area, closure->user_data);
return cif->bytes; return ret;
} }
#endif /* !X86_WIN64 */ #endif /* !X86_WIN64 */
static void static unsigned int
ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue, ffi_prep_incoming_args(char *stack, void **rvalue, void **avalue,
ffi_cif *cif) ffi_cif *cif)
{ {
register unsigned int i; register unsigned int i;
register void **p_argv; register void **p_argv;
register char *argp; register char *argp;
register ffi_type **p_arg; register ffi_type **p_arg;
#ifndef X86_WIN64
argp = stack; const int cabi = cif->abi;
const int dir = (cabi == FFI_PASCAL || cabi == FFI_REGISTER) ? -1 : +1;
#ifdef X86_WIN64 const unsigned int max_stack_count = (cabi == FFI_THISCALL) ? 1
if (cif->rtype->size > sizeof(ffi_arg) : (cabi == FFI_FASTCALL) ? 2
|| (cif->flags == FFI_TYPE_STRUCT : (cabi == FFI_REGISTER) ? 3
&& (cif->rtype->size != 1 && cif->rtype->size != 2 : 0;
&& cif->rtype->size != 4 && cif->rtype->size != 8))) { unsigned int passed_regs = 0;
*rvalue = *(void **) argp; void *p_stack_data[3] = { stack - 1 };
argp += sizeof(void *);
}
#else #else
if ( cif->flags == FFI_TYPE_STRUCT #define dir 1
|| cif->flags == FFI_TYPE_MS_STRUCT ) {
*rvalue = *(void **) argp;
argp += sizeof(void *);
}
#endif #endif
p_argv = avalue; argp = stack;
#ifndef X86_WIN64
for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) argp += max_stack_count * FFI_SIZEOF_ARG;
{ #endif
size_t z;
/* Align if necessary */
if ((sizeof(void*) - 1) & (size_t) argp) {
argp = (char *) ALIGN(argp, sizeof(void*));
}
if ((cif->flags == FFI_TYPE_STRUCT
|| cif->flags == FFI_TYPE_MS_STRUCT)
#ifdef X86_WIN64 #ifdef X86_WIN64
if ((*p_arg)->size > sizeof(ffi_arg) && ((cif->rtype->size & (1 | 2 | 4 | 8)) == 0)
|| ((*p_arg)->type == FFI_TYPE_STRUCT #endif
&& ((*p_arg)->size != 1 && (*p_arg)->size != 2 )
&& (*p_arg)->size != 4 && (*p_arg)->size != 8)))
{ {
z = sizeof(void *); #ifndef X86_WIN64
*p_argv = *(void **)argp; if (passed_regs < max_stack_count)
{
*rvalue = *(void**) (stack + (passed_regs*FFI_SIZEOF_ARG));
++passed_regs;
} }
else else
#endif #endif
{ {
z = (*p_arg)->size; *rvalue = *(void **) argp;
argp += sizeof(void *);
}
}
#ifndef X86_WIN64
/* Do register arguments first */
for (i = 0, p_arg = cif->arg_types;
i < cif->nargs && passed_regs < max_stack_count;
i++, p_arg++)
{
if ((*p_arg)->type == FFI_TYPE_FLOAT
|| (*p_arg)->type == FFI_TYPE_STRUCT)
continue;
size_t sz = (*p_arg)->size;
if(sz == 0 || sz > FFI_SIZEOF_ARG)
continue;
p_stack_data[passed_regs] = avalue + i;
avalue[i] = stack + (passed_regs*FFI_SIZEOF_ARG);
++passed_regs;
}
#endif
p_arg = cif->arg_types;
p_argv = avalue;
if (dir < 0)
{
const int nargs = cif->nargs - 1;
if (nargs > 0)
{
p_arg += nargs;
p_argv += nargs;
}
}
for (i = 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;
#ifdef X86_WIN64
if (z > FFI_SIZEOF_ARG
|| ((*p_arg)->type == FFI_TYPE_STRUCT
&& (z & (1 | 2 | 4 | 8)) == 0)
#if FFI_TYPE_DOUBLE != FFI_TYPE_LONGDOUBLE
|| ((*p_arg)->type == FFI_TYPE_LONGDOUBLE)
#endif
)
{
z = FFI_SIZEOF_ARG;
*p_argv = *(void **)argp;
}
else
#else
if (passed_regs > 0
&& z <= FFI_SIZEOF_ARG
&& (p_argv == p_stack_data[0]
|| p_argv == p_stack_data[1]
|| p_argv == p_stack_data[2]))
{
/* Already assigned a register value */
continue;
}
else
#endif
{
/* because we're little endian, this is what it turns into. */ /* because we're little endian, this is what it turns into. */
*p_argv = (void*) argp; *p_argv = (void*) argp;
} }
p_argv++;
#ifdef X86_WIN64 #ifdef X86_WIN64
argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1); argp += (z + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
#else #else
@@ -580,7 +642,7 @@ ffi_prep_incoming_args_SYSV(char *stack, void **rvalue, void **avalue,
#endif #endif
} }
return; return (size_t)argp - (size_t)stack;
} }
#define FFI_INIT_TRAMPOLINE_WIN64(TRAMP,FUN,CTX,MASK) \ #define FFI_INIT_TRAMPOLINE_WIN64(TRAMP,FUN,CTX,MASK) \
@@ -779,29 +841,31 @@ static unsigned int
ffi_prep_args_raw(char *stack, extended_cif *ecif) ffi_prep_args_raw(char *stack, extended_cif *ecif)
{ {
const ffi_cif *cif = ecif->cif; const ffi_cif *cif = ecif->cif;
unsigned int i, passed_regs = 0;
#ifndef X86_WIN64
const unsigned int abi = cif->abi; const unsigned int abi = cif->abi;
const unsigned int max = (abi == FFI_THISCALL) ? 1 const unsigned int max_regs = (abi == FFI_THISCALL) ? 1
: (abi == FFI_FASTCALL) ? 2 : (abi == FFI_FASTCALL) ? 2
: (abi == FFI_REGISTER) ? 3 : (abi == FFI_REGISTER) ? 3
: 0; : 0;
unsigned int i, passed_regs = 0;
if (cif->flags == FFI_TYPE_STRUCT) if (cif->flags == FFI_TYPE_STRUCT)
++passed_regs; ++passed_regs;
for (i = 0; i < cif->nargs && passed_regs <= max; i++) for (i = 0; i < cif->nargs && passed_regs <= max_regs; i++)
{ {
if (cif->arg_types[i]->type == FFI_TYPE_FLOAT if (cif->arg_types[i]->type == FFI_TYPE_FLOAT
|| cif->arg_types[i]->type == FFI_TYPE_STRUCT) || cif->arg_types[i]->type == FFI_TYPE_STRUCT)
continue; continue;
size_t sz = (cif->arg_types[i]->size + 3) & ~3; size_t sz = cif->arg_types[i]->size;
if (sz == 0 || sz > 4) if (sz == 0 || sz > FFI_SIZEOF_ARG)
continue; continue;
++passed_regs; ++passed_regs;
} }
#endif
memcpy (stack, ecif->avalue, cif->bytes); memcpy (stack, ecif->avalue, cif->bytes);
return passed_regs; return passed_regs;