Apparently, PCC doesn't support the fastcall calling convention. Nor does it issue a warning or error for the attribute that it does not understand.
726 lines
17 KiB
C
726 lines
17 KiB
C
/* -----------------------------------------------------------------------
|
|
ffi.c - Copyright (c) 1996, 1998, 1999, 2001, 2007, 2008 Red Hat, Inc.
|
|
Copyright (c) 2002 Ranjit Mathew
|
|
Copyright (c) 2002 Bo Thorsen
|
|
Copyright (c) 2002 Roger Sayle
|
|
Copyright (C) 2008, 2010 Free Software Foundation, Inc.
|
|
|
|
x86 Foreign Function Interface
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining
|
|
a copy of this software and associated documentation files (the
|
|
``Software''), to deal in the Software without restriction, including
|
|
without limitation the rights to use, copy, modify, merge, publish,
|
|
distribute, sublicense, and/or sell copies of the Software, and to
|
|
permit persons to whom the Software is furnished to do so, subject to
|
|
the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included
|
|
in all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED ``AS IS'', WITHOUT WARRANTY OF ANY KIND,
|
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
|
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
|
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
DEALINGS IN THE SOFTWARE.
|
|
----------------------------------------------------------------------- */
|
|
|
|
#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. */
|
|
#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
|
|
# if FFI_TYPE_LONGDOUBLE != 4
|
|
# error FFI_TYPE_LONGDOUBLE out of date
|
|
# endif
|
|
#else
|
|
# undef FFI_TYPE_LONGDOUBLE
|
|
# define FFI_TYPE_LONGDOUBLE 4
|
|
#endif
|
|
|
|
#if defined(__GNUC__) && !defined(__declspec)
|
|
# define __declspec(x) __attribute__((x))
|
|
#endif
|
|
|
|
/* Perform machine dependent cif processing. */
|
|
ffi_status FFI_HIDDEN
|
|
ffi_prep_cif_machdep(ffi_cif *cif)
|
|
{
|
|
size_t bytes = 0;
|
|
int i, n, flags, cabi = cif->abi;
|
|
|
|
switch (cabi)
|
|
{
|
|
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;
|
|
}
|
|
|
|
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:
|
|
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:
|
|
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)
|
|
flags = X86_RET_STRUCT_1B;
|
|
else if (cif->rtype->size == 2)
|
|
flags = X86_RET_STRUCT_2B;
|
|
else if (cif->rtype->size == 4)
|
|
flags = X86_RET_INT32;
|
|
else if (cif->rtype->size == 8)
|
|
flags = X86_RET_INT64;
|
|
else
|
|
#endif
|
|
{
|
|
do_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. */
|
|
bytes += ALIGN (sizeof(void*), FFI_SIZEOF_ARG);
|
|
}
|
|
break;
|
|
case FFI_TYPE_COMPLEX:
|
|
switch (cif->rtype->elements[0]->type)
|
|
{
|
|
case FFI_TYPE_DOUBLE:
|
|
case FFI_TYPE_LONGDOUBLE:
|
|
case FFI_TYPE_SINT64:
|
|
case FFI_TYPE_UINT64:
|
|
goto do_struct;
|
|
case FFI_TYPE_FLOAT:
|
|
case FFI_TYPE_INT:
|
|
case FFI_TYPE_SINT32:
|
|
case FFI_TYPE_UINT32:
|
|
flags = X86_RET_INT64;
|
|
break;
|
|
case FFI_TYPE_SINT16:
|
|
case FFI_TYPE_UINT16:
|
|
flags = X86_RET_INT32;
|
|
break;
|
|
case FFI_TYPE_SINT8:
|
|
case FFI_TYPE_UINT8:
|
|
flags = X86_RET_STRUCT_2B;
|
|
break;
|
|
default:
|
|
return FFI_BAD_TYPEDEF;
|
|
}
|
|
break;
|
|
default:
|
|
return FFI_BAD_TYPEDEF;
|
|
}
|
|
cif->flags = flags;
|
|
|
|
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;
|
|
}
|
|
|
|
static ffi_arg
|
|
extend_basic_type(void *arg, int type)
|
|
{
|
|
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();
|
|
}
|
|
}
|
|
|
|
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 static_chain; /* the static chain register used by gcc */
|
|
int nregs; /* number of register parameters */
|
|
int regs[3];
|
|
};
|
|
|
|
static const struct abi_params abi_params[FFI_LAST_ABI] = {
|
|
[FFI_SYSV] = { 1, R_ECX, 0 },
|
|
[FFI_THISCALL] = { 1, R_EAX, 1, { R_ECX } },
|
|
[FFI_FASTCALL] = { 1, R_EAX, 2, { R_ECX, R_EDX } },
|
|
[FFI_STDCALL] = { 1, R_ECX, 0 },
|
|
[FFI_PASCAL] = { -1, R_ECX, 0 },
|
|
/* ??? No defined static chain; gcc does not support REGISTER. */
|
|
[FFI_REGISTER] = { -1, R_ECX, 3, { R_EAX, R_EDX, R_ECX } },
|
|
[FFI_MS_CDECL] = { 1, R_ECX, 0 }
|
|
};
|
|
|
|
extern void ffi_call_i386(struct call_frame *, char *)
|
|
#if HAVE_FASTCALL
|
|
__declspec(fastcall)
|
|
#endif
|
|
FFI_HIDDEN;
|
|
|
|
static void
|
|
ffi_call_int (ffi_cif *cif, void (*fn)(void), void *rvalue,
|
|
void **avalue, void *closure)
|
|
{
|
|
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;
|
|
frame->regs[pabi->static_chain] = (unsigned)closure;
|
|
|
|
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);
|
|
size_t align = FFI_SIZEOF_ARG;
|
|
|
|
/* Alignment rules for arguments are quite complex. Vectors and
|
|
structures with 16 byte alignment get it. Note that long double
|
|
on Darwin does have 16 byte alignment, and does not get this
|
|
alignment if passed directly; a structure with a long double
|
|
inside, however, would get 16 byte alignment. Since libffi does
|
|
not support vectors, we need non concern ourselves with other
|
|
cases. */
|
|
if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
|
|
align = 16;
|
|
|
|
if (dir < 0)
|
|
{
|
|
/* ??? These reverse argument ABIs are probably too old
|
|
to have cared about alignment. Someone should check. */
|
|
argp -= za;
|
|
memcpy (argp, valp, z);
|
|
}
|
|
else
|
|
{
|
|
argp = (char *)ALIGN (argp, align);
|
|
memcpy (argp, valp, z);
|
|
argp += za;
|
|
}
|
|
}
|
|
}
|
|
FFI_ASSERT (dir > 0 || argp == stack);
|
|
|
|
ffi_call_i386 (frame, stack);
|
|
}
|
|
|
|
void
|
|
ffi_call (ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
|
|
{
|
|
ffi_call_int (cif, fn, rvalue, avalue, NULL);
|
|
}
|
|
|
|
void
|
|
ffi_call_go (ffi_cif *cif, void (*fn)(void), void *rvalue,
|
|
void **avalue, void *closure)
|
|
{
|
|
ffi_call_int (cif, fn, rvalue, avalue, closure);
|
|
}
|
|
|
|
/** private members **/
|
|
|
|
void FFI_HIDDEN ffi_closure_i386(void);
|
|
void FFI_HIDDEN ffi_closure_STDCALL(void);
|
|
void FFI_HIDDEN ffi_closure_REGISTER(void);
|
|
|
|
struct closure_frame
|
|
{
|
|
unsigned rettemp[4]; /* 0 */
|
|
unsigned regs[3]; /* 16-24 */
|
|
ffi_cif *cif; /* 28 */
|
|
void (*fun)(ffi_cif*,void*,void**,void*); /* 32 */
|
|
void *user_data; /* 36 */
|
|
};
|
|
|
|
int FFI_HIDDEN
|
|
#if HAVE_FASTCALL
|
|
__declspec(fastcall)
|
|
#endif
|
|
ffi_closure_inner (struct closure_frame *frame, char *stack)
|
|
{
|
|
ffi_cif *cif = frame->cif;
|
|
int cabi, i, n, flags, dir, narg_reg;
|
|
const struct abi_params *pabi;
|
|
ffi_type **arg_types;
|
|
char *argp;
|
|
void *rvalue;
|
|
void **avalue;
|
|
|
|
cabi = cif->abi;
|
|
flags = cif->flags;
|
|
narg_reg = 0;
|
|
rvalue = frame->rettemp;
|
|
pabi = &abi_params[cabi];
|
|
dir = pabi->dir;
|
|
argp = (dir < 0 ? stack + cif->bytes : stack);
|
|
|
|
switch (flags)
|
|
{
|
|
case X86_RET_STRUCTARG:
|
|
if (pabi->nregs > 0)
|
|
{
|
|
rvalue = (void *)frame->regs[pabi->regs[0]];
|
|
narg_reg = 1;
|
|
frame->rettemp[0] = (unsigned)rvalue;
|
|
break;
|
|
}
|
|
/* fallthru */
|
|
case X86_RET_STRUCTPOP:
|
|
rvalue = *(void **)argp;
|
|
argp += sizeof(void *);
|
|
frame->rettemp[0] = (unsigned)rvalue;
|
|
break;
|
|
}
|
|
|
|
n = cif->nargs;
|
|
avalue = alloca(sizeof(void *) * n);
|
|
|
|
arg_types = cif->arg_types;
|
|
for (i = 0; i < n; ++i)
|
|
{
|
|
ffi_type *ty = arg_types[i];
|
|
size_t z = ty->size;
|
|
int t = ty->type;
|
|
void *valp;
|
|
|
|
if (z <= FFI_SIZEOF_ARG && t != FFI_TYPE_STRUCT)
|
|
{
|
|
if (t != FFI_TYPE_FLOAT && narg_reg < pabi->nregs)
|
|
valp = &frame->regs[pabi->regs[narg_reg++]];
|
|
else if (dir < 0)
|
|
{
|
|
argp -= 4;
|
|
valp = argp;
|
|
}
|
|
else
|
|
{
|
|
valp = argp;
|
|
argp += 4;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
size_t za = ALIGN (z, FFI_SIZEOF_ARG);
|
|
size_t align = FFI_SIZEOF_ARG;
|
|
|
|
/* See the comment in ffi_call_int. */
|
|
if (t == FFI_TYPE_STRUCT && ty->alignment >= 16)
|
|
align = 16;
|
|
|
|
if (dir < 0)
|
|
{
|
|
/* ??? These reverse argument ABIs are probably too old
|
|
to have cared about alignment. Someone should check. */
|
|
argp -= za;
|
|
valp = argp;
|
|
}
|
|
else
|
|
{
|
|
argp = (char *)ALIGN (argp, align);
|
|
valp = argp;
|
|
argp += za;
|
|
}
|
|
}
|
|
|
|
avalue[i] = valp;
|
|
}
|
|
|
|
frame->fun (cif, rvalue, avalue, frame->user_data);
|
|
|
|
if (cabi == FFI_STDCALL)
|
|
return flags + (cif->bytes << X86_RET_POP_SHIFT);
|
|
else
|
|
return flags;
|
|
}
|
|
|
|
ffi_status
|
|
ffi_prep_closure_loc (ffi_closure* closure,
|
|
ffi_cif* cif,
|
|
void (*fun)(ffi_cif*,void*,void**,void*),
|
|
void *user_data,
|
|
void *codeloc)
|
|
{
|
|
char *tramp = closure->tramp;
|
|
void (*dest)(void);
|
|
int op = 0xb8; /* movl imm, %eax */
|
|
|
|
switch (cif->abi)
|
|
{
|
|
case FFI_SYSV:
|
|
case FFI_THISCALL:
|
|
case FFI_FASTCALL:
|
|
case FFI_MS_CDECL:
|
|
dest = ffi_closure_i386;
|
|
break;
|
|
case FFI_STDCALL:
|
|
case FFI_PASCAL:
|
|
dest = ffi_closure_STDCALL;
|
|
break;
|
|
case FFI_REGISTER:
|
|
dest = ffi_closure_REGISTER;
|
|
op = 0x68; /* pushl imm */
|
|
default:
|
|
return FFI_BAD_ABI;
|
|
}
|
|
|
|
/* movl or pushl immediate. */
|
|
tramp[0] = op;
|
|
*(void **)(tramp + 1) = codeloc;
|
|
|
|
/* jmp dest */
|
|
tramp[5] = 0xe9;
|
|
*(unsigned *)(tramp + 6) = (unsigned)dest - ((unsigned)codeloc + 10);
|
|
|
|
closure->cif = cif;
|
|
closure->fun = fun;
|
|
closure->user_data = user_data;
|
|
|
|
return FFI_OK;
|
|
}
|
|
|
|
void FFI_HIDDEN ffi_go_closure_EAX(void);
|
|
void FFI_HIDDEN ffi_go_closure_ECX(void);
|
|
void FFI_HIDDEN ffi_go_closure_STDCALL(void);
|
|
|
|
ffi_status
|
|
ffi_prep_go_closure (ffi_go_closure* closure, ffi_cif* cif,
|
|
void (*fun)(ffi_cif*,void*,void**,void*))
|
|
{
|
|
void (*dest)(void);
|
|
|
|
switch (cif->abi)
|
|
{
|
|
case FFI_SYSV:
|
|
case FFI_MS_CDECL:
|
|
dest = ffi_go_closure_ECX;
|
|
break;
|
|
case FFI_THISCALL:
|
|
case FFI_FASTCALL:
|
|
dest = ffi_go_closure_EAX;
|
|
break;
|
|
case FFI_STDCALL:
|
|
case FFI_PASCAL:
|
|
dest = ffi_go_closure_STDCALL;
|
|
break;
|
|
case FFI_REGISTER:
|
|
default:
|
|
return FFI_BAD_ABI;
|
|
}
|
|
|
|
closure->tramp = dest;
|
|
closure->cif = cif;
|
|
closure->fun = fun;
|
|
|
|
return FFI_OK;
|
|
}
|
|
|
|
/* ------- Native raw API support -------------------------------- */
|
|
|
|
#if !FFI_NO_RAW_API
|
|
|
|
void FFI_HIDDEN ffi_closure_raw_SYSV(void);
|
|
void FFI_HIDDEN ffi_closure_raw_THISCALL(void);
|
|
|
|
ffi_status
|
|
ffi_prep_raw_closure_loc (ffi_raw_closure *closure,
|
|
ffi_cif *cif,
|
|
void (*fun)(ffi_cif*,void*,ffi_raw*,void*),
|
|
void *user_data,
|
|
void *codeloc)
|
|
{
|
|
char *tramp = closure->tramp;
|
|
void (*dest)(void);
|
|
int i;
|
|
|
|
/* We currently don't support certain kinds of arguments for raw
|
|
closures. This should be implemented by a separate assembly
|
|
language routine, since it would require argument processing,
|
|
something we don't do now for performance. */
|
|
for (i = cif->nargs-1; i >= 0; i--)
|
|
switch (cif->arg_types[i]->type)
|
|
{
|
|
case FFI_TYPE_STRUCT:
|
|
case FFI_TYPE_LONGDOUBLE:
|
|
return FFI_BAD_TYPEDEF;
|
|
}
|
|
|
|
switch (cif->abi)
|
|
{
|
|
case FFI_THISCALL:
|
|
dest = ffi_closure_raw_THISCALL;
|
|
break;
|
|
case FFI_SYSV:
|
|
dest = ffi_closure_raw_SYSV;
|
|
break;
|
|
default:
|
|
return FFI_BAD_ABI;
|
|
}
|
|
|
|
/* movl imm, %eax. */
|
|
tramp[0] = 0xb8;
|
|
*(void **)(tramp + 1) = codeloc;
|
|
|
|
/* jmp dest */
|
|
tramp[5] = 0xe9;
|
|
*(unsigned *)(tramp + 6) = (unsigned)dest - ((unsigned)codeloc + 10);
|
|
|
|
closure->cif = cif;
|
|
closure->fun = fun;
|
|
closure->user_data = user_data;
|
|
|
|
return FFI_OK;
|
|
}
|
|
|
|
void
|
|
ffi_raw_call(ffi_cif *cif, void (*fn)(void), void *rvalue, ffi_raw *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;
|
|
|
|
flags = cif->flags;
|
|
cabi = cif->abi;
|
|
pabi = &abi_params[cabi];
|
|
|
|
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;
|
|
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__ */
|