summaryrefslogtreecommitdiff
path: root/src/alpha/ffi.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/alpha/ffi.c')
-rw-r--r--src/alpha/ffi.c284
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;
+}