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:
Richard Henderson
2014-11-01 15:10:34 -07:00
parent 159d3788eb
commit b9ac94f3af
4 changed files with 452 additions and 427 deletions

View File

@@ -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,278 +45,279 @@
# 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*);
}
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);
case FFI_SYSV:
case FFI_STDCALL:
case FFI_THISCALL:
case FFI_FASTCALL:
case FFI_MS_CDECL:
case FFI_PASCAL:
case FFI_REGISTER:
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);
return FFI_BAD_ABI;
}
/* 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;
/* Allocate space for return value pointer. */
cif->bytes += ALIGN(sizeof(void*), FFI_SIZEOF_ARG);
}
break;
default:
cif->flags = FFI_TYPE_INT;
break;
}
for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
switch (cabi)
{
if (((*ptr)->alignment - 1) & cif->bytes)
cif->bytes = ALIGN(cif->bytes, (*ptr)->alignment);
cif->bytes += (unsigned)ALIGN((*ptr)->size, FFI_SIZEOF_ARG);
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. */
bytes += ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
}
break;
default:
return FFI_BAD_TYPEDEF;
}
cif->flags = flags;
if (cif->abi == FFI_SYSV)
cif->bytes = ALIGN (cif->bytes, 15);
for (i = 0, n = cif->nargs; i < n; i++)
{
ffi_type *t = cif->arg_types[i];
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);
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();
}
}
else
ecif.rvalue = rvalue;
switch (cif->abi)
struct call_frame
{
#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);
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:
FFI_ASSERT(0);
/* 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);
}
/** private members **/
@@ -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;
flags = cif->flags;
cabi = cif->abi;
pabi = &abi_params[cabi];
/* 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))
rsize = 0;
if (rvalue == NULL)
{
ecif.rvalue = alloca(cif->rtype->size);
}
else
ecif.rvalue = rvalue;
switch (cif->abi)
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);
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:
FFI_ASSERT(0);
/* We can pretend that the callee returns nothing. */
flags = X86_RET_VOID;
break;
}
}
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)
{
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__ */

View File

@@ -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
View 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

View File

@@ -31,143 +31,144 @@
#include <fficonfig.h>
#include <ffi.h>
#include <ffi_cfi.h>
#include "internal.h"
#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
#ifdef __ELF__
# define ENDF(X) .type X,@function; .size X, . - X
#else
# define ENDF(X)
#endif
/* 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
.text
.align 16
.globl C(ffi_call_i386)
FFI_HIDDEN(C(ffi_call_i386))
.globl ffi_prep_args
/* This is declared as
.align 4
.globl ffi_call_SYSV
.type ffi_call_SYSV,@function
void ffi_call_i386(struct ffi_call_frame *frame, char *argp)
__attribute__((fastcall));
ffi_call_SYSV:
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
/* Place all of the ffi_prep_args in position */
pushl 12(%ebp)
pushl %eax
call *8(%ebp)
/* Return stack to previous state and call the function */
addl $8,%esp
movl 12(%ebp), %ecx /* load return type code */
movl %ebx, 8(%ebp) /* preserve %ebx */
cfi_rel_offset(%ebp, 8)
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
.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
E(X86_RET_FLOAT)
fstps (%ecx)
jmp epilogue
retdouble:
/* Load %ecx with the pointer to storage for the return value */
movl 24(%ebp),%ecx
jmp 9f
E(X86_RET_DOUBLE)
fstpl (%ecx)
jmp epilogue
retlongdouble:
/* Load %ecx with the pointer to storage for the return value */
movl 24(%ebp),%ecx
jmp 9f
E(X86_RET_LDOUBLE)
fstpt (%ecx)
jmp epilogue
retint64:
/* Load %ecx with the pointer to storage for the return value */
movl 24(%ebp),%ecx
movl %eax,0(%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)
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
/* 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)