| /* ----------------------------------------------------------------------- | |
| 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; | |
| 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 *); | |
| 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__ */ |