This is the mail archive of the
libffi-discuss@sourceware.org
mailing list for the libffi project.
[PATCH 06/13] x86: Rewrite ffi_call
- From: Richard Henderson <rth at twiddle dot net>
- To: libffi-discuss at sourceware dot org
- Date: Fri, 7 Nov 2014 16:30:33 +0100
- Subject: [PATCH 06/13] x86: Rewrite ffi_call
- Authentication-results: sourceware.org; auth=none
- References: <1415374240-1792-1-git-send-email-rth at twiddle dot net>
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.
---
src/x86/ffi.c | 601 ++++++++++++++++++++++++++--------------------------
src/x86/ffitarget.h | 4 -
src/x86/internal.h | 23 ++
src/x86/sysv.S | 243 ++++++++++-----------
4 files changed, 448 insertions(+), 423 deletions(-)
create mode 100644 src/x86/internal.h
diff --git a/src/x86/ffi.c b/src/x86/ffi.c
index 339ca89..1c77bb8 100644
--- a/src/x86/ffi.c
+++ b/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;
-
- argp = stack;
+ size_t bytes = 0;
+ int i, n, flags, cabi = cif->abi;
- 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*);
- }
-
- 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;
- }
+ 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;
}
- 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:
+ 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:
- case FFI_TYPE_SINT64:
- case FFI_TYPE_FLOAT:
- case FFI_TYPE_DOUBLE:
- case FFI_TYPE_LONGDOUBLE:
- cif->flags = (unsigned) cif->rtype->type;
+ 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;
+ 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();
+ }
+}
- 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 */
+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);
- if (rvalue == NULL
- && (cif->flags == FFI_TYPE_STRUCT
- || cif->flags == FFI_TYPE_MS_STRUCT))
+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)
{
- 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;
+ 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)
{
-#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;
- 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 *);
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)
+void
+ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *avalue)
{
- 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++)
+ 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)
{
- 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;
+ 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;
+ }
}
- 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.)
- */
+ bytes = cif->bytes;
+ argp = stack = alloca(bytes + sizeof(*frame) + rsize);
+ frame = (struct call_frame *)(stack + bytes);
+ if (rsize)
+ rvalue = frame + 1;
-void
-ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *fake_avalue)
-{
- extended_cif ecif;
- void **avalue = (void **)fake_avalue;
-
- 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))
+ narg_reg = 0;
+ switch (flags)
{
- ecif.rvalue = alloca(cif->rtype->size);
+ 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;
}
- else
- ecif.rvalue = rvalue;
-
-
- switch (cif->abi)
+
+ arg_types = cif->arg_types;
+ for (i = 0, n = cif->nargs; narg_reg < pabi->nregs && i < n; i++)
{
-#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);
- break;
+ 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__ */
diff --git a/src/x86/ffitarget.h b/src/x86/ffitarget.h
index a4c9573..91e429c 100644
--- a/src/x86/ffitarget.h
+++ b/src/x86/ffitarget.h
@@ -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,
diff --git a/src/x86/internal.h b/src/x86/internal.h
new file mode 100644
index 0000000..480c1d0
--- /dev/null
+++ b/src/x86/internal.h
@@ -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
diff --git a/src/x86/sysv.S b/src/x86/sysv.S
index fd13bc0..d0b8417 100644
--- a/src/x86/sysv.S
+++ b/src/x86/sysv.S
@@ -31,143 +31,144 @@
#include <fficonfig.h>
#include <ffi.h>
#include <ffi_cfi.h>
+#include "internal.h"
-.text
-
-.globl ffi_prep_args
+#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
- .align 4
-.globl ffi_call_SYSV
- .type ffi_call_SYSV,@function
+#ifdef __ELF__
+# define ENDF(X) .type X,@function; .size X, . - X
+#else
+# define ENDF(X)
+#endif
-ffi_call_SYSV:
- cfi_startproc
- pushl %ebp
- cfi_adjust_cfa_offset(4)
- 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
+/* 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
- /* Align the stack pointer to 16-bytes */
- andl $0xfffffff0, %esp
+ .text
+ .align 16
+ .globl C(ffi_call_i386)
+ FFI_HIDDEN(C(ffi_call_i386))
- movl %esp,%eax
+/* This is declared as
- /* Place all of the ffi_prep_args in position */
- pushl 12(%ebp)
- pushl %eax
- call *8(%ebp)
+ void ffi_call_i386(struct ffi_call_frame *frame, char *argp)
+ __attribute__((fastcall));
- /* Return stack to previous state and call the function */
- addl $8,%esp
+ This the arguments are present in
- call *28(%ebp)
+ ecx: frame
+ edx: argp
+*/
- /* Load %ecx with the return type code */
- movl 20(%ebp),%ecx
+C(ffi_call_i386):
+ cfi_startproc
+ 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)
- /* Protect %esi. We're going to pop it in the epilogue. */
- pushl %esi
+ 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
- /* If the return value pointer is NULL, assume no return value. */
- cmpl $0,24(%ebp)
- jne 0f
+ call *8(%ebp)
- /* 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)
+ movl 12(%ebp), %ecx /* load return type code */
+ movl %ebx, 8(%ebp) /* preserve %ebx */
+ cfi_rel_offset(%ebp, 8)
- 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
-
-.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 */
-
-1:
- pop %esi
- add (%esi, %ecx, 4), %esi
- jmp *%esi
-
- /* 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
+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
+
+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
+
+ /* Fill out the table so that bad values are predictable. */
+E(X86_RET_UNUSED14)
+ ud2
+E(X86_RET_UNUSED15)
+ ud2
+
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)
--
1.9.3