From 181d8ded82d49d0133d9d6fd1631d9816c970bfa Mon Sep 17 00:00:00 2001 From: Matthias Benkard Date: Sat, 26 Jan 2008 12:06:34 +0100 Subject: Import libffi from PyObjC 1.3.7. darcs-hash:129bccb59266f997deac9b0353aea2d2d4049f92 --- libffi/src/ia64/ffi.c | 671 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 671 insertions(+) create mode 100644 libffi/src/ia64/ffi.c (limited to 'libffi/src/ia64/ffi.c') diff --git a/libffi/src/ia64/ffi.c b/libffi/src/ia64/ffi.c new file mode 100644 index 0000000..1dc27db --- /dev/null +++ b/libffi/src/ia64/ffi.c @@ -0,0 +1,671 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 1998 Red Hat, Inc. + Copyright (c) 2000 Hewlett Packard Company + + IA64 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 CYGNUS SOLUTIONS 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. + ----------------------------------------------------------------------- */ + +#include +#include + +#include +#include + +#include "ia64_flags.h" + +/* Memory image of fp register contents. Should eventually be an fp */ +/* type long enough to hold an entire register. For now we use double. */ +typedef double float80; + +/* The stack layout at call to ffi_prep_args. Other_args will remain */ +/* on the stack for the actual call. Everything else we be transferred */ +/* to registers and popped by the assembly code. */ + +struct ia64_args { + long scratch[2]; /* Two scratch words at top of stack. */ + /* Allows sp to be passed as arg pointer. */ + void * r8_contents; /* Value to be passed in r8 */ + long spare; /* Not used. */ + float80 fp_regs[8]; /* Contents of 8 floating point argument */ + /* registers. */ + long out_regs[8]; /* Contents of the 8 out registers used */ + /* for integer parameters. */ + long other_args[0]; /* Arguments passed on stack, variable size */ + /* Treated as continuation of out_regs. */ +}; + +static size_t float_type_size(unsigned short tp) +{ + switch(tp) { + case FFI_TYPE_FLOAT: + return sizeof(float); + case FFI_TYPE_DOUBLE: + return sizeof(double); +#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE + case FFI_TYPE_LONGDOUBLE: + return sizeof(long double); +#endif + default: + FFI_ASSERT(0); + } +} + +/* + * Is type a struct containing at most n floats, doubles, or extended + * doubles, all of the same fp type? + * If so, set *element_type to the fp type. + */ +static bool is_homogeneous_fp_aggregate(ffi_type * type, int n, + unsigned short * element_type) +{ + ffi_type **ptr; + unsigned short element, struct_element; + + int type_set = 0; + + FFI_ASSERT(type != NULL); + + FFI_ASSERT(type->elements != NULL); + + ptr = &(type->elements[0]); + + while ((*ptr) != NULL) + { + switch((*ptr) -> type) { + case FFI_TYPE_FLOAT: + if (type_set && element != FFI_TYPE_FLOAT) return 0; + if (--n < 0) return false; + type_set = 1; + element = FFI_TYPE_FLOAT; + break; + case FFI_TYPE_DOUBLE: + if (type_set && element != FFI_TYPE_DOUBLE) return 0; + if (--n < 0) return false; + type_set = 1; + element = FFI_TYPE_DOUBLE; + break; + case FFI_TYPE_STRUCT: + if (!is_homogeneous_fp_aggregate(type, n, &struct_element)) + return false; + if (type_set && struct_element != element) return false; + n -= (type -> size)/float_type_size(element); + element = struct_element; + if (n < 0) return false; + break; + /* case FFI_TYPE_LONGDOUBLE: + Not yet implemented. */ + default: + return false; + } + ptr++; + } + *element_type = element; + return true; + +} + +/* ffi_prep_args is called by the assembly routine once stack space + has been allocated for the function's arguments. It fills in + the arguments in the structure referenced by stack. Returns nonzero + if fp registers are used for arguments. */ + +static bool +ffi_prep_args(struct ia64_args *stack, extended_cif *ecif, int bytes) +{ + register long i, avn; + register void **p_argv; + register long *argp = stack -> out_regs; + register float80 *fp_argp = stack -> fp_regs; + register ffi_type **p_arg; + + /* For big return structs, r8 needs to contain the target address. */ + /* Since r8 is otherwise dead, we set it unconditionally. */ + stack -> r8_contents = ecif -> rvalue; + i = 0; + avn = ecif->cif->nargs; + p_arg = ecif->cif->arg_types; + p_argv = ecif->avalue; + while (i < avn) + { + size_t z; /* z is in units of arg slots or words, not bytes. */ + + switch ((*p_arg)->type) + { + case FFI_TYPE_SINT8: + z = 1; + *(SINT64 *) argp = *(SINT8 *)(* p_argv); + break; + + case FFI_TYPE_UINT8: + z = 1; + *(UINT64 *) argp = *(UINT8 *)(* p_argv); + break; + + case FFI_TYPE_SINT16: + z = 1; + *(SINT64 *) argp = *(SINT16 *)(* p_argv); + break; + + case FFI_TYPE_UINT16: + z = 1; + *(UINT64 *) argp = *(UINT16 *)(* p_argv); + break; + + case FFI_TYPE_SINT32: + z = 1; + *(SINT64 *) argp = *(SINT32 *)(* p_argv); + break; + + case FFI_TYPE_UINT32: + z = 1; + *(UINT64 *) argp = *(UINT32 *)(* p_argv); + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + z = 1; + *(UINT64 *) argp = *(UINT64 *)(* p_argv); + break; + + case FFI_TYPE_FLOAT: + z = 1; + if (fp_argp - stack->fp_regs < 8) + { + /* Note the conversion -- all the fp regs are loaded as + doubles. */ + *fp_argp++ = *(float *)(* p_argv); + } + /* Also put it into the integer registers or memory: */ + *(UINT64 *) argp = *(UINT32 *)(* p_argv); + break; + + case FFI_TYPE_DOUBLE: + z = 1; + if (fp_argp - stack->fp_regs < 8) + *fp_argp++ = *(double *)(* p_argv); + /* Also put it into the integer registers or memory: */ + *(double *) argp = *(double *)(* p_argv); + break; + + case FFI_TYPE_STRUCT: + { + size_t sz = (*p_arg)->size; + unsigned short element_type; + z = ((*p_arg)->size + FFI_SIZEOF_ARG - 1)/FFI_SIZEOF_ARG; + if (is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) { + int i; + int nelements = sz/float_type_size(element_type); + for (i = 0; i < nelements; ++i) { + switch (element_type) { + case FFI_TYPE_FLOAT: + if (fp_argp - stack->fp_regs < 8) + *fp_argp++ = ((float *)(* p_argv))[i]; + break; + case FFI_TYPE_DOUBLE: + if (fp_argp - stack->fp_regs < 8) + *fp_argp++ = ((double *)(* p_argv))[i]; + break; + default: + /* Extended precision not yet implemented. */ + abort(); + } + } + } + /* And pass it in integer registers as a struct, with */ + /* its actual field sizes packed into registers. */ + memcpy(argp, *p_argv, (*p_arg)->size); + } + break; + + default: + FFI_ASSERT(0); + } + + argp += z; + i++, p_arg++, p_argv++; + } + return (fp_argp != stack -> fp_regs); +} + +/* Perform machine dependent cif processing */ +ffi_status +ffi_prep_cif_machdep(ffi_cif *cif) +{ + long i, avn; + bool is_simple = true; + long simple_flag = FFI_SIMPLE_V; + /* Adjust cif->bytes to include space for the 2 scratch words, + r8 register contents, spare word, + the 8 fp register contents, and all 8 integer register contents. + This will be removed before the call, though 2 scratch words must + remain. */ + + cif->bytes += 4*sizeof(long) + 8 *sizeof(float80); + if (cif->bytes < sizeof(struct ia64_args)) + cif->bytes = sizeof(struct ia64_args); + + /* The stack must be double word aligned, so round bytes up + appropriately. */ + + cif->bytes = ALIGN(cif->bytes, 2*sizeof(void*)); + + avn = cif->nargs; + if (avn <= 2) { + for (i = 0; i < avn; ++i) { + switch(cif -> arg_types[i] -> type) { + case FFI_TYPE_SINT32: + simple_flag = FFI_ADD_INT_ARG(simple_flag); + break; + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + simple_flag = FFI_ADD_LONG_ARG(simple_flag); + break; + default: + is_simple = false; + } + } + } else { + is_simple = false; + } + + /* Set the return type flag */ + switch (cif->rtype->type) + { + case FFI_TYPE_VOID: + cif->flags = FFI_TYPE_VOID; + break; + + case FFI_TYPE_STRUCT: + { + size_t sz = cif -> rtype -> size; + unsigned short element_type; + + is_simple = false; + if (is_homogeneous_fp_aggregate(cif -> rtype, 8, &element_type)) { + int nelements = sz/float_type_size(element_type); + if (nelements <= 1) { + if (0 == nelements) { + cif -> flags = FFI_TYPE_VOID; + } else { + cif -> flags = element_type; + } + } else { + switch(element_type) { + case FFI_TYPE_FLOAT: + cif -> flags = FFI_IS_FLOAT_FP_AGGREGATE | nelements; + break; + case FFI_TYPE_DOUBLE: + cif -> flags = FFI_IS_DOUBLE_FP_AGGREGATE | nelements; + break; + default: + /* long double NYI */ + abort(); + } + } + break; + } + if (sz <= 32) { + if (sz <= 8) { + cif->flags = FFI_TYPE_INT; + } else if (sz <= 16) { + cif->flags = FFI_IS_SMALL_STRUCT2; + } else if (sz <= 24) { + cif->flags = FFI_IS_SMALL_STRUCT3; + } else { + cif->flags = FFI_IS_SMALL_STRUCT4; + } + } else { + cif->flags = FFI_TYPE_STRUCT; + } + } + break; + + case FFI_TYPE_FLOAT: + is_simple = false; + cif->flags = FFI_TYPE_FLOAT; + break; + + case FFI_TYPE_DOUBLE: + is_simple = false; + cif->flags = FFI_TYPE_DOUBLE; + break; + + default: + cif->flags = FFI_TYPE_INT; + /* This seems to depend on little endian mode, and the fact that */ + /* the return pointer always points to at least 8 bytes. But */ + /* that also seems to be true for other platforms. */ + break; + } + + if (is_simple) cif -> flags |= simple_flag; + return FFI_OK; +} + +extern int ffi_call_unix(bool (*)(struct ia64_args *, extended_cif *, int), + extended_cif *, unsigned, + unsigned, unsigned *, void (*)()); + +void +ffi_call(ffi_cif *cif, void (*fn)(), void *rvalue, void **avalue) +{ + extended_cif ecif; + long simple = cif -> flags & FFI_SIMPLE; + + /* Should this also check for Unix ABI? */ + /* This is almost, but not quite, machine independent. Note that */ + /* we can get away with not caring about length of the result because */ + /* we assume we are little endian, and the result buffer is large */ + /* enough. */ + /* This needs work for HP/UX. */ + if (simple) { + long (*lfn)() = (long (*)())fn; + long result; + switch(simple) { + case FFI_SIMPLE_V: + result = lfn(); + break; + case FFI_SIMPLE_I: + result = lfn(*(int *)avalue[0]); + break; + case FFI_SIMPLE_L: + result = lfn(*(long *)avalue[0]); + break; + case FFI_SIMPLE_II: + result = lfn(*(int *)avalue[0], *(int *)avalue[1]); + break; + case FFI_SIMPLE_IL: + result = lfn(*(int *)avalue[0], *(long *)avalue[1]); + break; + case FFI_SIMPLE_LI: + result = lfn(*(long *)avalue[0], *(int *)avalue[1]); + break; + case FFI_SIMPLE_LL: + result = lfn(*(long *)avalue[0], *(long *)avalue[1]); + break; + } + if ((cif->flags & ~FFI_SIMPLE) != FFI_TYPE_VOID && 0 != rvalue) { + * (long *)rvalue = result; + } + return; + } + 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->rtype->type == FFI_TYPE_STRUCT) + ecif.rvalue = alloca(cif->rtype->size); + else + ecif.rvalue = rvalue; + + switch (cif->abi) + { + case FFI_UNIX: + ffi_call_unix(ffi_prep_args, &ecif, cif->bytes, + cif->flags, rvalue, fn); + break; + + default: + FFI_ASSERT(0); + break; + } +} + +/* + * Closures represent a pair consisting of a function pointer, and + * some user data. A closure is invoked by reinterpreting the closure + * as a function pointer, and branching to it. Thus we can make an + * interpreted function callable as a C function: We turn the interpreter + * itself, together with a pointer specifying the interpreted procedure, + * into a closure. + * On X86, the first few words of the closure structure actually contain code, + * which will do the right thing. On most other architectures, this + * would raise some Icache/Dcache coherence issues (which can be solved, but + * often not cheaply). + * For IA64, function pointer are already pairs consisting of a code + * pointer, and a gp pointer. The latter is needed to access global variables. + * Here we set up such a pair as the first two words of the closure (in + * the "trampoline" area), but we replace the gp pointer with a pointer + * to the closure itself. We also add the real gp pointer to the + * closure. This allows the function entry code to both retrieve the + * user data, and to restire the correct gp pointer. + */ + +static void +ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue, + void **avalue, ffi_cif *cif); + +/* This function is entered with the doctored gp (r1) value. + * This code is extremely gcc specific. There is some argument that + * it should really be written in assembly code, since it depends on + * gcc properties that might change over time. + */ + +/* ffi_closure_UNIX is an assembly routine, which copies the register */ +/* state into a struct ia64_args, and then invokes */ +/* ffi_closure_UNIX_inner. It also recovers the closure pointer */ +/* from its fake gp pointer. */ +void ffi_closure_UNIX(); + +#ifndef __GNUC__ +# error This requires gcc +#endif +void +ffi_closure_UNIX_inner (ffi_closure *closure, struct ia64_args * args) +/* Hopefully declaring this as a varargs function will force all args */ +/* to memory. */ +{ + // this is our return value storage + long double res; + + // our various things... + ffi_cif *cif; + unsigned short rtype; + void *resp; + void **arg_area; + + resp = (void*)&res; + cif = closure->cif; + arg_area = (void**) alloca (cif->nargs * sizeof (void*)); + + /* this call will initialize ARG_AREA, such that each + * element in that array points to the corresponding + * value on the stack; and if the function returns + * a structure, it will re-set RESP to point to the + * structure return address. */ + + ffi_prep_incoming_args_UNIX(args, (void**)&resp, arg_area, cif); + + (closure->fun) (cif, resp, arg_area, closure->user_data); + + rtype = cif->flags; + + /* now, do a generic return based on the value of rtype */ + if (rtype == FFI_TYPE_INT) + { + asm volatile ("ld8 r8=[%0]" : : "r" (resp) : "r8"); + } + else if (rtype == FFI_TYPE_FLOAT) + { + asm volatile ("ldfs f8=[%0]" : : "r" (resp) : "f8"); + } + else if (rtype == FFI_TYPE_DOUBLE) + { + asm volatile ("ldfd f8=[%0]" : : "r" (resp) : "f8"); + } + else if (rtype == FFI_IS_SMALL_STRUCT2) + { + asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]" + : : "r" (resp), "r" (resp+8) : "r8","r9"); + } + else if (rtype == FFI_IS_SMALL_STRUCT3) + { + asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]" + : : "r" (resp), "r" (resp+8), "r" (resp+16) + : "r8","r9","r10"); + } + else if (rtype == FFI_IS_SMALL_STRUCT4) + { + asm volatile ("ld8 r8=[%0]; ld8 r9=[%1]; ld8 r10=[%2]; ld8 r11=[%3]" + : : "r" (resp), "r" (resp+8), "r" (resp+16), "r" (resp+24) + : "r8","r9","r10","r11"); + } + else if (rtype != FFI_TYPE_VOID && rtype != FFI_TYPE_STRUCT) + { + /* Can only happen for homogeneous FP aggregates? */ + abort(); + } +} + +static void +ffi_prep_incoming_args_UNIX(struct ia64_args *args, void **rvalue, + void **avalue, ffi_cif *cif) +{ + register unsigned int i; + register unsigned int avn; + register void **p_argv; + register long *argp = args -> out_regs; + unsigned fp_reg_num = 0; + register ffi_type **p_arg; + + avn = cif->nargs; + p_argv = avalue; + + for (i = cif->nargs, p_arg = cif->arg_types; i != 0; i--, p_arg++) + { + size_t z; /* In units of words or argument slots. */ + + switch ((*p_arg)->type) + { + case FFI_TYPE_SINT8: + case FFI_TYPE_UINT8: + case FFI_TYPE_SINT16: + case FFI_TYPE_UINT16: + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + z = 1; + *p_argv = (void *)argp; + break; + + case FFI_TYPE_FLOAT: + z = 1; + /* Convert argument back to float in place from the saved value */ + if (argp - args->out_regs < 8 && fp_reg_num < 8) { + *(float *)argp = args -> fp_regs[fp_reg_num++]; + } + *p_argv = (void *)argp; + break; + + case FFI_TYPE_DOUBLE: + z = 1; + if (argp - args->out_regs < 8 && fp_reg_num < 8) { + *p_argv = args -> fp_regs + fp_reg_num++; + } else { + *p_argv = (void *)argp; + } + break; + + case FFI_TYPE_STRUCT: + { + size_t sz = (*p_arg)->size; + unsigned short element_type; + z = ((*p_arg)->size + FFI_SIZEOF_ARG - 1)/FFI_SIZEOF_ARG; + if (argp - args->out_regs < 8 + && is_homogeneous_fp_aggregate(*p_arg, 8, &element_type)) { + int nelements = sz/float_type_size(element_type); + if (nelements + fp_reg_num >= 8) { + /* hard case NYI. */ + abort(); + } + if (element_type == FFI_TYPE_DOUBLE) { + *p_argv = args -> fp_regs + fp_reg_num; + fp_reg_num += nelements; + break; + } + if (element_type == FFI_TYPE_FLOAT) { + int j; + for (j = 0; j < nelements; ++ j) { + ((float *)argp)[j] = args -> fp_regs[fp_reg_num + j]; + } + *p_argv = (void *)argp; + fp_reg_num += nelements; + break; + } + abort(); /* Other fp types NYI */ + } + } + break; + + default: + FFI_ASSERT(0); + } + + argp += z; + p_argv++; + + } + + return; +} + + +/* Fill in a closure to refer to the specified fun and user_data. */ +/* cif specifies the argument and result types for fun. */ +/* the cif must already be prep'ed */ + +/* The layout of a function descriptor. A C function pointer really */ +/* points to one of these. */ +typedef struct ia64_fd_struct { + void *code_pointer; + void *gp; +} ia64_fd; + +ffi_status +ffi_prep_closure (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*,void*,void**,void*), + void *user_data) +{ + struct ffi_ia64_trampoline_struct *tramp = + (struct ffi_ia64_trampoline_struct *) (closure -> tramp); + ia64_fd *fd = (ia64_fd *)(void *)ffi_closure_UNIX; + + FFI_ASSERT (cif->abi == FFI_UNIX); + + tramp -> code_pointer = fd -> code_pointer; + tramp -> real_gp = fd -> gp; + tramp -> fake_gp = closure; + closure->cif = cif; + closure->user_data = user_data; + closure->fun = fun; + + return FFI_OK; +} + + -- cgit v1.2.3