diff options
Diffstat (limited to 'src/alpha/ffi.c')
-rw-r--r-- | src/alpha/ffi.c | 284 |
1 files changed, 284 insertions, 0 deletions
diff --git a/src/alpha/ffi.c b/src/alpha/ffi.c new file mode 100644 index 0000000..8d6b2ba --- /dev/null +++ b/src/alpha/ffi.c @@ -0,0 +1,284 @@ +/* ----------------------------------------------------------------------- + ffi.c - Copyright (c) 1998, 2001, 2007, 2008 Red Hat, Inc. + + Alpha 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. + ----------------------------------------------------------------------- */ + +#include <ffi.h> +#include <ffi_common.h> +#include <stdlib.h> + +/* Force FFI_TYPE_LONGDOUBLE to be different than FFI_TYPE_DOUBLE; + all further uses in this file will refer to the 128-bit type. */ +#if defined(__LONG_DOUBLE_128__) +# if FFI_TYPE_LONGDOUBLE != 4 +# error FFI_TYPE_LONGDOUBLE out of date +# endif +#else +# undef FFI_TYPE_LONGDOUBLE +# define FFI_TYPE_LONGDOUBLE 4 +#endif + +extern void ffi_call_osf(void *, unsigned long, unsigned, void *, void (*)(void)) + FFI_HIDDEN; +extern void ffi_closure_osf(void) FFI_HIDDEN; + + +ffi_status +ffi_prep_cif_machdep(ffi_cif *cif) +{ + /* Adjust cif->bytes to represent a minimum 6 words for the temporary + register argument loading area. */ + if (cif->bytes < 6*FFI_SIZEOF_ARG) + cif->bytes = 6*FFI_SIZEOF_ARG; + + /* Set the return type flag */ + switch (cif->rtype->type) + { + case FFI_TYPE_STRUCT: + case FFI_TYPE_FLOAT: + case FFI_TYPE_DOUBLE: + cif->flags = cif->rtype->type; + break; + + case FFI_TYPE_LONGDOUBLE: + /* 128-bit long double is returned in memory, like a struct. */ + cif->flags = FFI_TYPE_STRUCT; + break; + + default: + cif->flags = FFI_TYPE_INT; + break; + } + + return FFI_OK; +} + + +void +ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue) +{ + unsigned long *stack, *argp; + long i, avn; + ffi_type **arg_types; + + /* 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) + rvalue = alloca(cif->rtype->size); + + /* Allocate the space for the arguments, plus 4 words of temp + space for ffi_call_osf. */ + argp = stack = alloca(cif->bytes + 4*FFI_SIZEOF_ARG); + + if (cif->flags == FFI_TYPE_STRUCT) + *(void **) argp++ = rvalue; + + i = 0; + avn = cif->nargs; + arg_types = cif->arg_types; + + while (i < avn) + { + size_t size = (*arg_types)->size; + + switch ((*arg_types)->type) + { + case FFI_TYPE_SINT8: + *(SINT64 *) argp = *(SINT8 *)(* avalue); + break; + + case FFI_TYPE_UINT8: + *(SINT64 *) argp = *(UINT8 *)(* avalue); + break; + + case FFI_TYPE_SINT16: + *(SINT64 *) argp = *(SINT16 *)(* avalue); + break; + + case FFI_TYPE_UINT16: + *(SINT64 *) argp = *(UINT16 *)(* avalue); + break; + + case FFI_TYPE_SINT32: + case FFI_TYPE_UINT32: + /* Note that unsigned 32-bit quantities are sign extended. */ + *(SINT64 *) argp = *(SINT32 *)(* avalue); + break; + + case FFI_TYPE_SINT64: + case FFI_TYPE_UINT64: + case FFI_TYPE_POINTER: + *(UINT64 *) argp = *(UINT64 *)(* avalue); + break; + + case FFI_TYPE_FLOAT: + if (argp - stack < 6) + { + /* Note the conversion -- all the fp regs are loaded as + doubles. The in-register format is the same. */ + *(double *) argp = *(float *)(* avalue); + } + else + *(float *) argp = *(float *)(* avalue); + break; + + case FFI_TYPE_DOUBLE: + *(double *) argp = *(double *)(* avalue); + break; + + case FFI_TYPE_LONGDOUBLE: + /* 128-bit long double is passed by reference. */ + *(long double **) argp = (long double *)(* avalue); + size = sizeof (long double *); + break; + + case FFI_TYPE_STRUCT: + memcpy(argp, *avalue, (*arg_types)->size); + break; + + default: + FFI_ASSERT(0); + } + + argp += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG; + i++, arg_types++, avalue++; + } + + ffi_call_osf(stack, cif->bytes, cif->flags, rvalue, fn); +} + + +ffi_status +ffi_prep_closure_loc (ffi_closure* closure, + ffi_cif* cif, + void (*fun)(ffi_cif*, void*, void**, void*), + void *user_data, + void *codeloc) +{ + unsigned int *tramp; + + tramp = (unsigned int *) &closure->tramp[0]; + tramp[0] = 0x47fb0401; /* mov $27,$1 */ + tramp[1] = 0xa77b0010; /* ldq $27,16($27) */ + tramp[2] = 0x6bfb0000; /* jmp $31,($27),0 */ + tramp[3] = 0x47ff041f; /* nop */ + *(void **) &tramp[4] = ffi_closure_osf; + + closure->cif = cif; + closure->fun = fun; + closure->user_data = user_data; + + /* Flush the Icache. + + Tru64 UNIX as doesn't understand the imb mnemonic, so use call_pal + instead, since both Compaq as and gas can handle it. + + 0x86 is PAL_imb in Tru64 UNIX <alpha/pal.h>. */ + asm volatile ("call_pal 0x86" : : : "memory"); + + return FFI_OK; +} + + +long FFI_HIDDEN +ffi_closure_osf_inner(ffi_closure *closure, void *rvalue, unsigned long *argp) +{ + ffi_cif *cif; + void **avalue; + ffi_type **arg_types; + long i, avn, argn; + + cif = closure->cif; + avalue = alloca(cif->nargs * sizeof(void *)); + + argn = 0; + + /* Copy the caller's structure return address to that the closure + returns the data directly to the caller. */ + if (cif->flags == FFI_TYPE_STRUCT) + { + rvalue = (void *) argp[0]; + argn = 1; + } + + i = 0; + avn = cif->nargs; + arg_types = cif->arg_types; + + /* Grab the addresses of the arguments from the stack frame. */ + while (i < avn) + { + size_t size = arg_types[i]->size; + + switch (arg_types[i]->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: + case FFI_TYPE_STRUCT: + avalue[i] = &argp[argn]; + break; + + case FFI_TYPE_FLOAT: + if (argn < 6) + { + /* Floats coming from registers need conversion from double + back to float format. */ + *(float *)&argp[argn - 6] = *(double *)&argp[argn - 6]; + avalue[i] = &argp[argn - 6]; + } + else + avalue[i] = &argp[argn]; + break; + + case FFI_TYPE_DOUBLE: + avalue[i] = &argp[argn - (argn < 6 ? 6 : 0)]; + break; + + case FFI_TYPE_LONGDOUBLE: + /* 128-bit long double is passed by reference. */ + avalue[i] = (long double *) argp[argn]; + size = sizeof (long double *); + break; + + default: + abort (); + } + + argn += ALIGN(size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG; + i++; + } + + /* Invoke the closure. */ + closure->fun (cif, rvalue, avalue, closure->user_data); + + /* Tell ffi_closure_osf how to perform return type promotions. */ + return cif->rtype->type; +} |