From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: (qmail 9399 invoked by alias); 7 Nov 2014 15:31:09 -0000 Mailing-List: contact libffi-discuss-help@sourceware.org; run by ezmlm Precedence: bulk List-Id: List-Subscribe: List-Archive: List-Post: List-Help: , Sender: libffi-discuss-owner@sourceware.org Received: (qmail 9343 invoked by uid 89); 7 Nov 2014 15:31:09 -0000 Authentication-Results: sourceware.org; auth=none X-Virus-Found: No X-Spam-SWARE-Status: No, score=-2.5 required=5.0 tests=AWL,BAYES_00,FREEMAIL_ENVFROM_END_DIGIT,FREEMAIL_FROM,RCVD_IN_DNSWL_LOW,SPF_PASS autolearn=ham version=3.3.2 X-HELO: mail-wi0-f175.google.com Received: from mail-wi0-f175.google.com (HELO mail-wi0-f175.google.com) (209.85.212.175) by sourceware.org (qpsmtpd/0.93/v0.84-503-g423c35a) with (AES128-SHA encrypted) ESMTPS; Fri, 07 Nov 2014 15:31:05 +0000 Received: by mail-wi0-f175.google.com with SMTP id ex7so4888808wid.8 for ; Fri, 07 Nov 2014 07:31:02 -0800 (PST) X-Received: by 10.194.94.132 with SMTP id dc4mr17924394wjb.56.1415374260970; Fri, 07 Nov 2014 07:31:00 -0800 (PST) Received: from pike.twiddle.home.com ([87.111.149.167]) by mx.google.com with ESMTPSA id p1sm12186731wjy.22.2014.11.07.07.30.59 for (version=TLSv1.2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Fri, 07 Nov 2014 07:31:00 -0800 (PST) From: Richard Henderson To: libffi-discuss@sourceware.org Subject: [PATCH 06/13] x86: Rewrite ffi_call Date: Fri, 07 Nov 2014 15:31:00 -0000 Message-Id: <1415374240-1792-7-git-send-email-rth@twiddle.net> In-Reply-To: <1415374240-1792-1-git-send-email-rth@twiddle.net> References: <1415374240-1792-1-git-send-email-rth@twiddle.net> X-SW-Source: 2014/txt/msg00195.txt.bz2 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 #include #include +#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 #include #include +#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