summaryrefslogtreecommitdiff
path: root/src/mips
diff options
context:
space:
mode:
authorvivian, zhang <vivian.zhang@intel.com>2012-06-03 11:36:11 +0800
committervivian, zhang <vivian.zhang@intel.com>2012-06-03 11:36:11 +0800
commit2ea3addd3001ed339f0f0600d5c8845aa15d962e (patch)
tree22d5c87bdc38f47e4309166c81503afe0d6e09fc /src/mips
parenta2cc1325a624fb4155b158646444c1723ff19092 (diff)
downloadlibffi-2ea3addd3001ed339f0f0600d5c8845aa15d962e.tar.gz
libffi-2ea3addd3001ed339f0f0600d5c8845aa15d962e.tar.bz2
libffi-2ea3addd3001ed339f0f0600d5c8845aa15d962e.zip
Initial import package libffi: A portable foreign function interface libraryHEAD2.0_alphamaster2.0alpha1.0_post
Diffstat (limited to 'src/mips')
-rw-r--r--src/mips/ffi.c1029
-rw-r--r--src/mips/ffitarget.h235
-rw-r--r--src/mips/n32.S591
-rw-r--r--src/mips/o32.S381
4 files changed, 2236 insertions, 0 deletions
diff --git a/src/mips/ffi.c b/src/mips/ffi.c
new file mode 100644
index 0000000..d714cc9
--- /dev/null
+++ b/src/mips/ffi.c
@@ -0,0 +1,1029 @@
+/* -----------------------------------------------------------------------
+ ffi.c - Copyright (c) 1996, 2007, 2008 Red Hat, Inc.
+ Copyright (c) 2008 David Daney
+
+ MIPS 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>
+
+#ifdef __GNUC__
+# if (__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ >= 3))
+# define USE__BUILTIN___CLEAR_CACHE 1
+# endif
+#endif
+
+#ifndef USE__BUILTIN___CLEAR_CACHE
+#include <sys/cachectl.h>
+#endif
+
+#ifdef FFI_DEBUG
+# define FFI_MIPS_STOP_HERE() ffi_stop_here()
+#else
+# define FFI_MIPS_STOP_HERE() do {} while(0)
+#endif
+
+#ifdef FFI_MIPS_N32
+#define FIX_ARGP \
+FFI_ASSERT(argp <= &stack[bytes]); \
+if (argp == &stack[bytes]) \
+{ \
+ argp = stack; \
+ FFI_MIPS_STOP_HERE(); \
+}
+#else
+#define FIX_ARGP
+#endif
+
+
+/* ffi_prep_args is called by the assembly routine once stack space
+ has been allocated for the function's arguments */
+
+static void ffi_prep_args(char *stack,
+ extended_cif *ecif,
+ int bytes,
+ int flags)
+{
+ int i;
+ void **p_argv;
+ char *argp;
+ ffi_type **p_arg;
+
+#ifdef FFI_MIPS_N32
+ /* If more than 8 double words are used, the remainder go
+ on the stack. We reorder stuff on the stack here to
+ support this easily. */
+ if (bytes > 8 * sizeof(ffi_arg))
+ argp = &stack[bytes - (8 * sizeof(ffi_arg))];
+ else
+ argp = stack;
+#else
+ argp = stack;
+#endif
+
+ memset(stack, 0, bytes);
+
+#ifdef FFI_MIPS_N32
+ if ( ecif->cif->rstruct_flag != 0 )
+#else
+ if ( ecif->cif->rtype->type == FFI_TYPE_STRUCT )
+#endif
+ {
+ *(ffi_arg *) argp = (ffi_arg) ecif->rvalue;
+ argp += sizeof(ffi_arg);
+ FIX_ARGP;
+ }
+
+ p_argv = ecif->avalue;
+
+ for (i = 0, p_arg = ecif->cif->arg_types; i < ecif->cif->nargs; i++, p_arg++)
+ {
+ size_t z;
+ unsigned int a;
+
+ /* Align if necessary. */
+ a = (*p_arg)->alignment;
+ if (a < sizeof(ffi_arg))
+ a = sizeof(ffi_arg);
+
+ if ((a - 1) & (unsigned long) argp)
+ {
+ argp = (char *) ALIGN(argp, a);
+ FIX_ARGP;
+ }
+
+ z = (*p_arg)->size;
+ if (z <= sizeof(ffi_arg))
+ {
+ int type = (*p_arg)->type;
+ z = sizeof(ffi_arg);
+
+ /* The size of a pointer depends on the ABI */
+ if (type == FFI_TYPE_POINTER)
+ type = (ecif->cif->abi == FFI_N64
+ || ecif->cif->abi == FFI_N64_SOFT_FLOAT)
+ ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32;
+
+ if (i < 8 && (ecif->cif->abi == FFI_N32_SOFT_FLOAT
+ || ecif->cif->abi == FFI_N64_SOFT_FLOAT))
+ {
+ switch (type)
+ {
+ case FFI_TYPE_FLOAT:
+ type = FFI_TYPE_UINT32;
+ break;
+ case FFI_TYPE_DOUBLE:
+ type = FFI_TYPE_UINT64;
+ break;
+ default:
+ break;
+ }
+ }
+ switch (type)
+ {
+ case FFI_TYPE_SINT8:
+ *(ffi_arg *)argp = *(SINT8 *)(* p_argv);
+ break;
+
+ case FFI_TYPE_UINT8:
+ *(ffi_arg *)argp = *(UINT8 *)(* p_argv);
+ break;
+
+ case FFI_TYPE_SINT16:
+ *(ffi_arg *)argp = *(SINT16 *)(* p_argv);
+ break;
+
+ case FFI_TYPE_UINT16:
+ *(ffi_arg *)argp = *(UINT16 *)(* p_argv);
+ break;
+
+ case FFI_TYPE_SINT32:
+ *(ffi_arg *)argp = *(SINT32 *)(* p_argv);
+ break;
+
+ case FFI_TYPE_UINT32:
+ *(ffi_arg *)argp = *(UINT32 *)(* p_argv);
+ break;
+
+ /* This can only happen with 64bit slots. */
+ case FFI_TYPE_FLOAT:
+ *(float *) argp = *(float *)(* p_argv);
+ break;
+
+ /* Handle structures. */
+ default:
+ memcpy(argp, *p_argv, (*p_arg)->size);
+ break;
+ }
+ }
+ else
+ {
+#ifdef FFI_MIPS_O32
+ memcpy(argp, *p_argv, z);
+#else
+ {
+ unsigned long end = (unsigned long) argp + z;
+ unsigned long cap = (unsigned long) stack + bytes;
+
+ /* Check if the data will fit within the register space.
+ Handle it if it doesn't. */
+
+ if (end <= cap)
+ memcpy(argp, *p_argv, z);
+ else
+ {
+ unsigned long portion = cap - (unsigned long)argp;
+
+ memcpy(argp, *p_argv, portion);
+ argp = stack;
+ z -= portion;
+ memcpy(argp, (void*)((unsigned long)(*p_argv) + portion),
+ z);
+ }
+ }
+#endif
+ }
+ p_argv++;
+ argp += z;
+ FIX_ARGP;
+ }
+}
+
+#ifdef FFI_MIPS_N32
+
+/* The n32 spec says that if "a chunk consists solely of a double
+ float field (but not a double, which is part of a union), it
+ is passed in a floating point register. Any other chunk is
+ passed in an integer register". This code traverses structure
+ definitions and generates the appropriate flags. */
+
+static unsigned
+calc_n32_struct_flags(int soft_float, ffi_type *arg,
+ unsigned *loc, unsigned *arg_reg)
+{
+ unsigned flags = 0;
+ unsigned index = 0;
+
+ ffi_type *e;
+
+ if (soft_float)
+ return 0;
+
+ while ((e = arg->elements[index]))
+ {
+ /* Align this object. */
+ *loc = ALIGN(*loc, e->alignment);
+ if (e->type == FFI_TYPE_DOUBLE)
+ {
+ /* Already aligned to FFI_SIZEOF_ARG. */
+ *arg_reg = *loc / FFI_SIZEOF_ARG;
+ if (*arg_reg > 7)
+ break;
+ flags += (FFI_TYPE_DOUBLE << (*arg_reg * FFI_FLAG_BITS));
+ *loc += e->size;
+ }
+ else
+ *loc += e->size;
+ index++;
+ }
+ /* Next Argument register at alignment of FFI_SIZEOF_ARG. */
+ *arg_reg = ALIGN(*loc, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
+
+ return flags;
+}
+
+static unsigned
+calc_n32_return_struct_flags(int soft_float, ffi_type *arg)
+{
+ unsigned flags = 0;
+ unsigned small = FFI_TYPE_SMALLSTRUCT;
+ ffi_type *e;
+
+ /* Returning structures under n32 is a tricky thing.
+ A struct with only one or two floating point fields
+ is returned in $f0 (and $f2 if necessary). Any other
+ struct results at most 128 bits are returned in $2
+ (the first 64 bits) and $3 (remainder, if necessary).
+ Larger structs are handled normally. */
+
+ if (arg->size > 16)
+ return 0;
+
+ if (arg->size > 8)
+ small = FFI_TYPE_SMALLSTRUCT2;
+
+ e = arg->elements[0];
+
+ if (e->type == FFI_TYPE_DOUBLE)
+ flags = FFI_TYPE_DOUBLE;
+ else if (e->type == FFI_TYPE_FLOAT)
+ flags = FFI_TYPE_FLOAT;
+
+ if (flags && (e = arg->elements[1]))
+ {
+ if (e->type == FFI_TYPE_DOUBLE)
+ flags += FFI_TYPE_DOUBLE << FFI_FLAG_BITS;
+ else if (e->type == FFI_TYPE_FLOAT)
+ flags += FFI_TYPE_FLOAT << FFI_FLAG_BITS;
+ else
+ return small;
+
+ if (flags && (arg->elements[2]))
+ {
+ /* There are three arguments and the first two are
+ floats! This must be passed the old way. */
+ return small;
+ }
+ if (soft_float)
+ flags += FFI_TYPE_STRUCT_SOFT;
+ }
+ else
+ if (!flags)
+ return small;
+
+ return flags;
+}
+
+#endif
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
+{
+ cif->flags = 0;
+
+#ifdef FFI_MIPS_O32
+ /* Set the flags necessary for O32 processing. FFI_O32_SOFT_FLOAT
+ * does not have special handling for floating point args.
+ */
+
+ if (cif->rtype->type != FFI_TYPE_STRUCT && cif->abi == FFI_O32)
+ {
+ if (cif->nargs > 0)
+ {
+ switch ((cif->arg_types)[0]->type)
+ {
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ cif->flags += (cif->arg_types)[0]->type;
+ break;
+
+ default:
+ break;
+ }
+
+ if (cif->nargs > 1)
+ {
+ /* Only handle the second argument if the first
+ is a float or double. */
+ if (cif->flags)
+ {
+ switch ((cif->arg_types)[1]->type)
+ {
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ cif->flags += (cif->arg_types)[1]->type << FFI_FLAG_BITS;
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ /* Set the return type flag */
+
+ if (cif->abi == FFI_O32_SOFT_FLOAT)
+ {
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_VOID:
+ case FFI_TYPE_STRUCT:
+ cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 2);
+ break;
+
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_DOUBLE:
+ cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 2);
+ break;
+
+ case FFI_TYPE_FLOAT:
+ default:
+ cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 2);
+ break;
+ }
+ }
+ else
+ {
+ /* FFI_O32 */
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_VOID:
+ case FFI_TYPE_STRUCT:
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 2);
+ break;
+
+ case FFI_TYPE_SINT64:
+ case FFI_TYPE_UINT64:
+ cif->flags += FFI_TYPE_UINT64 << (FFI_FLAG_BITS * 2);
+ break;
+
+ default:
+ cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 2);
+ break;
+ }
+ }
+#endif
+
+#ifdef FFI_MIPS_N32
+ /* Set the flags necessary for N32 processing */
+ {
+ int type;
+ unsigned arg_reg = 0;
+ unsigned loc = 0;
+ unsigned count = (cif->nargs < 8) ? cif->nargs : 8;
+ unsigned index = 0;
+
+ unsigned struct_flags = 0;
+ int soft_float = (cif->abi == FFI_N32_SOFT_FLOAT
+ || cif->abi == FFI_N64_SOFT_FLOAT);
+
+ if (cif->rtype->type == FFI_TYPE_STRUCT)
+ {
+ struct_flags = calc_n32_return_struct_flags(soft_float, cif->rtype);
+
+ if (struct_flags == 0)
+ {
+ /* This means that the structure is being passed as
+ a hidden argument */
+
+ arg_reg = 1;
+ count = (cif->nargs < 7) ? cif->nargs : 7;
+
+ cif->rstruct_flag = !0;
+ }
+ else
+ cif->rstruct_flag = 0;
+ }
+ else
+ cif->rstruct_flag = 0;
+
+ while (count-- > 0 && arg_reg < 8)
+ {
+ type = (cif->arg_types)[index]->type;
+ if (soft_float)
+ {
+ switch (type)
+ {
+ case FFI_TYPE_FLOAT:
+ type = FFI_TYPE_UINT32;
+ break;
+ case FFI_TYPE_DOUBLE:
+ type = FFI_TYPE_UINT64;
+ break;
+ default:
+ break;
+ }
+ }
+ switch (type)
+ {
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_DOUBLE:
+ cif->flags +=
+ ((cif->arg_types)[index]->type << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ break;
+ case FFI_TYPE_LONGDOUBLE:
+ /* Align it. */
+ arg_reg = ALIGN(arg_reg, 2);
+ /* Treat it as two adjacent doubles. */
+ if (soft_float)
+ {
+ arg_reg += 2;
+ }
+ else
+ {
+ cif->flags +=
+ (FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ cif->flags +=
+ (FFI_TYPE_DOUBLE << (arg_reg * FFI_FLAG_BITS));
+ arg_reg++;
+ }
+ break;
+
+ case FFI_TYPE_STRUCT:
+ loc = arg_reg * FFI_SIZEOF_ARG;
+ cif->flags += calc_n32_struct_flags(soft_float,
+ (cif->arg_types)[index],
+ &loc, &arg_reg);
+ break;
+
+ default:
+ arg_reg++;
+ break;
+ }
+
+ index++;
+ }
+
+ /* Set the return type flag */
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_STRUCT:
+ {
+ if (struct_flags == 0)
+ {
+ /* The structure is returned through a hidden
+ first argument. Do nothing, 'cause FFI_TYPE_VOID
+ is 0 */
+ }
+ else
+ {
+ /* The structure is returned via some tricky
+ mechanism */
+ cif->flags += FFI_TYPE_STRUCT << (FFI_FLAG_BITS * 8);
+ cif->flags += struct_flags << (4 + (FFI_FLAG_BITS * 8));
+ }
+ break;
+ }
+
+ case FFI_TYPE_VOID:
+ /* Do nothing, 'cause FFI_TYPE_VOID is 0 */
+ break;
+
+ case FFI_TYPE_POINTER:
+ if (cif->abi == FFI_N32_SOFT_FLOAT || cif->abi == FFI_N32)
+ cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
+ else
+ cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ break;
+
+ case FFI_TYPE_FLOAT:
+ if (soft_float)
+ {
+ cif->flags += FFI_TYPE_SINT32 << (FFI_FLAG_BITS * 8);
+ break;
+ }
+ /* else fall through */
+ case FFI_TYPE_DOUBLE:
+ if (soft_float)
+ cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ else
+ cif->flags += cif->rtype->type << (FFI_FLAG_BITS * 8);
+ break;
+
+ case FFI_TYPE_LONGDOUBLE:
+ /* Long double is returned as if it were a struct containing
+ two doubles. */
+ if (soft_float)
+ {
+ cif->flags += FFI_TYPE_STRUCT << (FFI_FLAG_BITS * 8);
+ cif->flags += FFI_TYPE_SMALLSTRUCT2 << (4 + (FFI_FLAG_BITS * 8));
+ }
+ else
+ {
+ cif->flags += FFI_TYPE_STRUCT << (FFI_FLAG_BITS * 8);
+ cif->flags += (FFI_TYPE_DOUBLE
+ + (FFI_TYPE_DOUBLE << FFI_FLAG_BITS))
+ << (4 + (FFI_FLAG_BITS * 8));
+ }
+ break;
+ default:
+ cif->flags += FFI_TYPE_INT << (FFI_FLAG_BITS * 8);
+ break;
+ }
+ }
+#endif
+
+ return FFI_OK;
+}
+
+/* Low level routine for calling O32 functions */
+extern int ffi_call_O32(void (*)(char *, extended_cif *, int, int),
+ extended_cif *, unsigned,
+ unsigned, unsigned *, void (*)(void));
+
+/* Low level routine for calling N32 functions */
+extern int ffi_call_N32(void (*)(char *, extended_cif *, int, int),
+ extended_cif *, unsigned,
+ unsigned, void *, void (*)(void));
+
+void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+ extended_cif ecif;
+
+ 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)
+ {
+#ifdef FFI_MIPS_O32
+ case FFI_O32:
+ case FFI_O32_SOFT_FLOAT:
+ ffi_call_O32(ffi_prep_args, &ecif, cif->bytes,
+ cif->flags, ecif.rvalue, fn);
+ break;
+#endif
+
+#ifdef FFI_MIPS_N32
+ case FFI_N32:
+ case FFI_N32_SOFT_FLOAT:
+ case FFI_N64:
+ case FFI_N64_SOFT_FLOAT:
+ {
+ int copy_rvalue = 0;
+ int copy_offset = 0;
+ char *rvalue_copy = ecif.rvalue;
+ if (cif->rtype->type == FFI_TYPE_STRUCT && cif->rtype->size < 16)
+ {
+ /* For structures smaller than 16 bytes we clobber memory
+ in 8 byte increments. Make a copy so we don't clobber
+ the callers memory outside of the struct bounds. */
+ rvalue_copy = alloca(16);
+ copy_rvalue = 1;
+ }
+ else if (cif->rtype->type == FFI_TYPE_FLOAT
+ && (cif->abi == FFI_N64_SOFT_FLOAT
+ || cif->abi == FFI_N32_SOFT_FLOAT))
+ {
+ rvalue_copy = alloca (8);
+ copy_rvalue = 1;
+#if defined(__MIPSEB__) || defined(_MIPSEB)
+ copy_offset = 4;
+#endif
+ }
+ ffi_call_N32(ffi_prep_args, &ecif, cif->bytes,
+ cif->flags, rvalue_copy, fn);
+ if (copy_rvalue)
+ memcpy(ecif.rvalue, rvalue_copy + copy_offset, cif->rtype->size);
+ }
+ break;
+#endif
+
+ default:
+ FFI_ASSERT(0);
+ break;
+ }
+}
+
+#if FFI_CLOSURES
+#if defined(FFI_MIPS_O32)
+extern void ffi_closure_O32(void);
+#else
+extern void ffi_closure_N32(void);
+#endif /* FFI_MIPS_O32 */
+
+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 = (unsigned int *) &closure->tramp[0];
+ void * fn;
+ char *clear_location = (char *) codeloc;
+
+#if defined(FFI_MIPS_O32)
+ FFI_ASSERT(cif->abi == FFI_O32 || cif->abi == FFI_O32_SOFT_FLOAT);
+ fn = ffi_closure_O32;
+#else /* FFI_MIPS_N32 */
+ FFI_ASSERT(cif->abi == FFI_N32 || cif->abi == FFI_N64);
+ fn = ffi_closure_N32;
+#endif /* FFI_MIPS_O32 */
+
+#if defined(FFI_MIPS_O32) || (_MIPS_SIM ==_ABIN32)
+ /* lui $25,high(fn) */
+ tramp[0] = 0x3c190000 | ((unsigned)fn >> 16);
+ /* ori $25,low(fn) */
+ tramp[1] = 0x37390000 | ((unsigned)fn & 0xffff);
+ /* lui $12,high(codeloc) */
+ tramp[2] = 0x3c0c0000 | ((unsigned)codeloc >> 16);
+ /* jr $25 */
+ tramp[3] = 0x03200008;
+ /* ori $12,low(codeloc) */
+ tramp[4] = 0x358c0000 | ((unsigned)codeloc & 0xffff);
+#else
+ /* N64 has a somewhat larger trampoline. */
+ /* lui $25,high(fn) */
+ tramp[0] = 0x3c190000 | ((unsigned long)fn >> 48);
+ /* lui $12,high(codeloc) */
+ tramp[1] = 0x3c0c0000 | ((unsigned long)codeloc >> 48);
+ /* ori $25,mid-high(fn) */
+ tramp[2] = 0x37390000 | (((unsigned long)fn >> 32 ) & 0xffff);
+ /* ori $12,mid-high(codeloc) */
+ tramp[3] = 0x358c0000 | (((unsigned long)codeloc >> 32) & 0xffff);
+ /* dsll $25,$25,16 */
+ tramp[4] = 0x0019cc38;
+ /* dsll $12,$12,16 */
+ tramp[5] = 0x000c6438;
+ /* ori $25,mid-low(fn) */
+ tramp[6] = 0x37390000 | (((unsigned long)fn >> 16 ) & 0xffff);
+ /* ori $12,mid-low(codeloc) */
+ tramp[7] = 0x358c0000 | (((unsigned long)codeloc >> 16) & 0xffff);
+ /* dsll $25,$25,16 */
+ tramp[8] = 0x0019cc38;
+ /* dsll $12,$12,16 */
+ tramp[9] = 0x000c6438;
+ /* ori $25,low(fn) */
+ tramp[10] = 0x37390000 | ((unsigned long)fn & 0xffff);
+ /* jr $25 */
+ tramp[11] = 0x03200008;
+ /* ori $12,low(codeloc) */
+ tramp[12] = 0x358c0000 | ((unsigned long)codeloc & 0xffff);
+
+#endif
+
+ closure->cif = cif;
+ closure->fun = fun;
+ closure->user_data = user_data;
+
+#ifdef USE__BUILTIN___CLEAR_CACHE
+ __builtin___clear_cache(clear_location, clear_location + FFI_TRAMPOLINE_SIZE);
+#else
+ cacheflush (clear_location, FFI_TRAMPOLINE_SIZE, ICACHE);
+#endif
+ return FFI_OK;
+}
+
+/*
+ * Decodes the arguments to a function, which will be stored on the
+ * stack. AR is the pointer to the beginning of the integer arguments
+ * (and, depending upon the arguments, some floating-point arguments
+ * as well). FPR is a pointer to the area where floating point
+ * registers have been saved, if any.
+ *
+ * RVALUE is the location where the function return value will be
+ * stored. CLOSURE is the prepared closure to invoke.
+ *
+ * This function should only be called from assembly, which is in
+ * turn called from a trampoline.
+ *
+ * Returns the function return type.
+ *
+ * Based on the similar routine for sparc.
+ */
+int
+ffi_closure_mips_inner_O32 (ffi_closure *closure,
+ void *rvalue, ffi_arg *ar,
+ double *fpr)
+{
+ ffi_cif *cif;
+ void **avaluep;
+ ffi_arg *avalue;
+ ffi_type **arg_types;
+ int i, avn, argn, seen_int;
+
+ cif = closure->cif;
+ avalue = alloca (cif->nargs * sizeof (ffi_arg));
+ avaluep = alloca (cif->nargs * sizeof (ffi_arg));
+
+ seen_int = (cif->abi == FFI_O32_SOFT_FLOAT);
+ argn = 0;
+
+ if ((cif->flags >> (FFI_FLAG_BITS * 2)) == FFI_TYPE_STRUCT)
+ {
+ rvalue = (void *)(UINT32)ar[0];
+ argn = 1;
+ }
+
+ i = 0;
+ avn = cif->nargs;
+ arg_types = cif->arg_types;
+
+ while (i < avn)
+ {
+ if (i < 2 && !seen_int &&
+ (arg_types[i]->type == FFI_TYPE_FLOAT ||
+ arg_types[i]->type == FFI_TYPE_DOUBLE ||
+ arg_types[i]->type == FFI_TYPE_LONGDOUBLE))
+ {
+#if defined(__MIPSEB__) || defined(_MIPSEB)
+ if (arg_types[i]->type == FFI_TYPE_FLOAT)
+ avaluep[i] = ((char *) &fpr[i]) + sizeof (float);
+ else
+#endif
+ avaluep[i] = (char *) &fpr[i];
+ }
+ else
+ {
+ if (arg_types[i]->alignment == 8 && (argn & 0x1))
+ argn++;
+ switch (arg_types[i]->type)
+ {
+ case FFI_TYPE_SINT8:
+ avaluep[i] = &avalue[i];
+ *(SINT8 *) &avalue[i] = (SINT8) ar[argn];
+ break;
+
+ case FFI_TYPE_UINT8:
+ avaluep[i] = &avalue[i];
+ *(UINT8 *) &avalue[i] = (UINT8) ar[argn];
+ break;
+
+ case FFI_TYPE_SINT16:
+ avaluep[i] = &avalue[i];
+ *(SINT16 *) &avalue[i] = (SINT16) ar[argn];
+ break;
+
+ case FFI_TYPE_UINT16:
+ avaluep[i] = &avalue[i];
+ *(UINT16 *) &avalue[i] = (UINT16) ar[argn];
+ break;
+
+ default:
+ avaluep[i] = (char *) &ar[argn];
+ break;
+ }
+ seen_int = 1;
+ }
+ argn += ALIGN(arg_types[i]->size, FFI_SIZEOF_ARG) / FFI_SIZEOF_ARG;
+ i++;
+ }
+
+ /* Invoke the closure. */
+ (closure->fun) (cif, rvalue, avaluep, closure->user_data);
+
+ if (cif->abi == FFI_O32_SOFT_FLOAT)
+ {
+ switch (cif->rtype->type)
+ {
+ case FFI_TYPE_FLOAT:
+ return FFI_TYPE_INT;
+ case FFI_TYPE_DOUBLE:
+ return FFI_TYPE_UINT64;
+ default:
+ return cif->rtype->type;
+ }
+ }
+ else
+ {
+ return cif->rtype->type;
+ }
+}
+
+#if defined(FFI_MIPS_N32)
+
+static void
+copy_struct_N32(char *target, unsigned offset, ffi_abi abi, ffi_type *type,
+ int argn, unsigned arg_offset, ffi_arg *ar,
+ ffi_arg *fpr, int soft_float)
+{
+ ffi_type **elt_typep = type->elements;
+ while(*elt_typep)
+ {
+ ffi_type *elt_type = *elt_typep;
+ unsigned o;
+ char *tp;
+ char *argp;
+ char *fpp;
+
+ o = ALIGN(offset, elt_type->alignment);
+ arg_offset += o - offset;
+ offset = o;
+ argn += arg_offset / sizeof(ffi_arg);
+ arg_offset = arg_offset % sizeof(ffi_arg);
+
+ argp = (char *)(ar + argn);
+ fpp = (char *)(argn >= 8 ? ar + argn : fpr + argn);
+
+ tp = target + offset;
+
+ if (elt_type->type == FFI_TYPE_DOUBLE && !soft_float)
+ *(double *)tp = *(double *)fpp;
+ else
+ memcpy(tp, argp + arg_offset, elt_type->size);
+
+ offset += elt_type->size;
+ arg_offset += elt_type->size;
+ elt_typep++;
+ argn += arg_offset / sizeof(ffi_arg);
+ arg_offset = arg_offset % sizeof(ffi_arg);
+ }
+}
+
+/*
+ * Decodes the arguments to a function, which will be stored on the
+ * stack. AR is the pointer to the beginning of the integer
+ * arguments. FPR is a pointer to the area where floating point
+ * registers have been saved.
+ *
+ * RVALUE is the location where the function return value will be
+ * stored. CLOSURE is the prepared closure to invoke.
+ *
+ * This function should only be called from assembly, which is in
+ * turn called from a trampoline.
+ *
+ * Returns the function return flags.
+ *
+ */
+int
+ffi_closure_mips_inner_N32 (ffi_closure *closure,
+ void *rvalue, ffi_arg *ar,
+ ffi_arg *fpr)
+{
+ ffi_cif *cif;
+ void **avaluep;
+ ffi_arg *avalue;
+ ffi_type **arg_types;
+ int i, avn, argn;
+ int soft_float;
+ ffi_arg *argp;
+
+ cif = closure->cif;
+ soft_float = cif->abi == FFI_N64_SOFT_FLOAT
+ || cif->abi == FFI_N32_SOFT_FLOAT;
+ avalue = alloca (cif->nargs * sizeof (ffi_arg));
+ avaluep = alloca (cif->nargs * sizeof (ffi_arg));
+
+ argn = 0;
+
+ if (cif->rstruct_flag)
+ {
+#if _MIPS_SIM==_ABIN32
+ rvalue = (void *)(UINT32)ar[0];
+#else /* N64 */
+ rvalue = (void *)ar[0];
+#endif
+ argn = 1;
+ }
+
+ i = 0;
+ avn = cif->nargs;
+ arg_types = cif->arg_types;
+
+ while (i < avn)
+ {
+ if (arg_types[i]->type == FFI_TYPE_FLOAT
+ || arg_types[i]->type == FFI_TYPE_DOUBLE
+ || arg_types[i]->type == FFI_TYPE_LONGDOUBLE)
+ {
+ argp = (argn >= 8 || soft_float) ? ar + argn : fpr + argn;
+ if ((arg_types[i]->type == FFI_TYPE_LONGDOUBLE) && ((unsigned)argp & (arg_types[i]->alignment-1)))
+ {
+ argp=(ffi_arg*)ALIGN(argp,arg_types[i]->alignment);
+ argn++;
+ }
+#if defined(__MIPSEB__) || defined(_MIPSEB)
+ if (arg_types[i]->type == FFI_TYPE_FLOAT && argn < 8)
+ avaluep[i] = ((char *) argp) + sizeof (float);
+ else
+#endif
+ avaluep[i] = (char *) argp;
+ }
+ else
+ {
+ unsigned type = arg_types[i]->type;
+
+ if (arg_types[i]->alignment > sizeof(ffi_arg))
+ argn = ALIGN(argn, arg_types[i]->alignment / sizeof(ffi_arg));
+
+ argp = ar + argn;
+
+ /* The size of a pointer depends on the ABI */
+ if (type == FFI_TYPE_POINTER)
+ type = (cif->abi == FFI_N64 || cif->abi == FFI_N64_SOFT_FLOAT)
+ ? FFI_TYPE_SINT64 : FFI_TYPE_SINT32;
+
+ if (soft_float && type == FFI_TYPE_FLOAT)
+ type = FFI_TYPE_UINT32;
+
+ switch (type)
+ {
+ case FFI_TYPE_SINT8:
+ avaluep[i] = &avalue[i];
+ *(SINT8 *) &avalue[i] = (SINT8) *argp;
+ break;
+
+ case FFI_TYPE_UINT8:
+ avaluep[i] = &avalue[i];
+ *(UINT8 *) &avalue[i] = (UINT8) *argp;
+ break;
+
+ case FFI_TYPE_SINT16:
+ avaluep[i] = &avalue[i];
+ *(SINT16 *) &avalue[i] = (SINT16) *argp;
+ break;
+
+ case FFI_TYPE_UINT16:
+ avaluep[i] = &avalue[i];
+ *(UINT16 *) &avalue[i] = (UINT16) *argp;
+ break;
+
+ case FFI_TYPE_SINT32:
+ avaluep[i] = &avalue[i];
+ *(SINT32 *) &avalue[i] = (SINT32) *argp;
+ break;
+
+ case FFI_TYPE_UINT32:
+ avaluep[i] = &avalue[i];
+ *(UINT32 *) &avalue[i] = (UINT32) *argp;
+ break;
+
+ case FFI_TYPE_STRUCT:
+ if (argn < 8)
+ {
+ /* Allocate space for the struct as at least part of
+ it was passed in registers. */
+ avaluep[i] = alloca(arg_types[i]->size);
+ copy_struct_N32(avaluep[i], 0, cif->abi, arg_types[i],
+ argn, 0, ar, fpr, soft_float);
+
+ break;
+ }
+ /* Else fall through. */
+ default:
+ avaluep[i] = (char *) argp;
+ break;
+ }
+ }
+ argn += ALIGN(arg_types[i]->size, sizeof(ffi_arg)) / sizeof(ffi_arg);
+ i++;
+ }
+
+ /* Invoke the closure. */
+ (closure->fun) (cif, rvalue, avaluep, closure->user_data);
+
+ return cif->flags >> (FFI_FLAG_BITS * 8);
+}
+
+#endif /* FFI_MIPS_N32 */
+
+#endif /* FFI_CLOSURES */
diff --git a/src/mips/ffitarget.h b/src/mips/ffitarget.h
new file mode 100644
index 0000000..c5f4e05
--- /dev/null
+++ b/src/mips/ffitarget.h
@@ -0,0 +1,235 @@
+/* -----------------------------------------------------------------*-C-*-
+ ffitarget.h - Copyright (c) 1996-2003 Red Hat, Inc.
+ Target configuration macros for MIPS.
+
+ 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 LIBFFI_TARGET_H
+#define LIBFFI_TARGET_H
+
+#ifdef linux
+# include <asm/sgidefs.h>
+#else
+# include <sgidefs.h>
+#endif
+# ifndef _ABIN32
+# define _ABIN32 _MIPS_SIM_NABI32
+# endif
+# ifndef _ABI64
+# define _ABI64 _MIPS_SIM_ABI64
+# endif
+# ifndef _ABIO32
+# define _ABIO32 _MIPS_SIM_ABI32
+# endif
+
+#if !defined(_MIPS_SIM)
+-- something is very wrong --
+#else
+# if (_MIPS_SIM==_ABIN32 && defined(_ABIN32)) || (_MIPS_SIM==_ABI64 && defined(_ABI64))
+# define FFI_MIPS_N32
+# else
+# if (_MIPS_SIM==_ABIO32 && defined(_ABIO32))
+# define FFI_MIPS_O32
+# else
+-- this is an unsupported platform --
+# endif
+# endif
+#endif
+
+#ifdef FFI_MIPS_O32
+/* O32 stack frames have 32bit integer args */
+# define FFI_SIZEOF_ARG 4
+#else
+/* N32 and N64 frames have 64bit integer args */
+# define FFI_SIZEOF_ARG 8
+# if _MIPS_SIM == _ABIN32
+# define FFI_SIZEOF_JAVA_RAW 4
+# endif
+#endif
+
+#define FFI_FLAG_BITS 2
+
+/* SGI's strange assembler requires that we multiply by 4 rather
+ than shift left by FFI_FLAG_BITS */
+
+#define FFI_ARGS_D FFI_TYPE_DOUBLE
+#define FFI_ARGS_F FFI_TYPE_FLOAT
+#define FFI_ARGS_DD FFI_TYPE_DOUBLE * 4 + FFI_TYPE_DOUBLE
+#define FFI_ARGS_FF FFI_TYPE_FLOAT * 4 + FFI_TYPE_FLOAT
+#define FFI_ARGS_FD FFI_TYPE_DOUBLE * 4 + FFI_TYPE_FLOAT
+#define FFI_ARGS_DF FFI_TYPE_FLOAT * 4 + FFI_TYPE_DOUBLE
+
+/* Needed for N32 structure returns */
+#define FFI_TYPE_SMALLSTRUCT FFI_TYPE_UINT8
+#define FFI_TYPE_SMALLSTRUCT2 FFI_TYPE_SINT8
+
+#if 0
+/* The SGI assembler can't handle this.. */
+#define FFI_TYPE_STRUCT_DD (( FFI_ARGS_DD ) << 4) + FFI_TYPE_STRUCT
+/* (and so on) */
+#else
+/* ...so we calculate these by hand! */
+#define FFI_TYPE_STRUCT_D 61
+#define FFI_TYPE_STRUCT_F 45
+#define FFI_TYPE_STRUCT_DD 253
+#define FFI_TYPE_STRUCT_FF 173
+#define FFI_TYPE_STRUCT_FD 237
+#define FFI_TYPE_STRUCT_DF 189
+#define FFI_TYPE_STRUCT_SMALL 93
+#define FFI_TYPE_STRUCT_SMALL2 109
+
+/* and for n32 soft float, add 16 * 2^4 */
+#define FFI_TYPE_STRUCT_D_SOFT 317
+#define FFI_TYPE_STRUCT_F_SOFT 301
+#define FFI_TYPE_STRUCT_DD_SOFT 509
+#define FFI_TYPE_STRUCT_FF_SOFT 429
+#define FFI_TYPE_STRUCT_FD_SOFT 493
+#define FFI_TYPE_STRUCT_DF_SOFT 445
+#define FFI_TYPE_STRUCT_SOFT 16
+#endif
+
+#ifdef LIBFFI_ASM
+#define v0 $2
+#define v1 $3
+#define a0 $4
+#define a1 $5
+#define a2 $6
+#define a3 $7
+#define a4 $8
+#define a5 $9
+#define a6 $10
+#define a7 $11
+#define t0 $8
+#define t1 $9
+#define t2 $10
+#define t3 $11
+#define t4 $12
+#define t5 $13
+#define t6 $14
+#define t7 $15
+#define t8 $24
+#define t9 $25
+#define ra $31
+
+#ifdef FFI_MIPS_O32
+# define REG_L lw
+# define REG_S sw
+# define SUBU subu
+# define ADDU addu
+# define SRL srl
+# define LI li
+#else /* !FFI_MIPS_O32 */
+# define REG_L ld
+# define REG_S sd
+# define SUBU dsubu
+# define ADDU daddu
+# define SRL dsrl
+# define LI dli
+# if (_MIPS_SIM==_ABI64)
+# define LA dla
+# define EH_FRAME_ALIGN 3
+# define FDE_ADDR_BYTES .8byte
+# else
+# define LA la
+# define EH_FRAME_ALIGN 2
+# define FDE_ADDR_BYTES .4byte
+# endif /* _MIPS_SIM==_ABI64 */
+#endif /* !FFI_MIPS_O32 */
+#else /* !LIBFFI_ASM */
+# ifdef __GNUC__
+# ifdef FFI_MIPS_O32
+/* O32 stack frames have 32bit integer args */
+typedef unsigned int ffi_arg __attribute__((__mode__(__SI__)));
+typedef signed int ffi_sarg __attribute__((__mode__(__SI__)));
+#else
+/* N32 and N64 frames have 64bit integer args */
+typedef unsigned int ffi_arg __attribute__((__mode__(__DI__)));
+typedef signed int ffi_sarg __attribute__((__mode__(__DI__)));
+# endif
+# else
+# ifdef FFI_MIPS_O32
+/* O32 stack frames have 32bit integer args */
+typedef __uint32_t ffi_arg;
+typedef __int32_t ffi_sarg;
+# else
+/* N32 and N64 frames have 64bit integer args */
+typedef __uint64_t ffi_arg;
+typedef __int64_t ffi_sarg;
+# endif
+# endif /* __GNUC__ */
+
+typedef enum ffi_abi {
+ FFI_FIRST_ABI = 0,
+ FFI_O32,
+ FFI_N32,
+ FFI_N64,
+ FFI_O32_SOFT_FLOAT,
+ FFI_N32_SOFT_FLOAT,
+ FFI_N64_SOFT_FLOAT,
+
+#ifdef FFI_MIPS_O32
+#ifdef __mips_soft_float
+ FFI_DEFAULT_ABI = FFI_O32_SOFT_FLOAT,
+#else
+ FFI_DEFAULT_ABI = FFI_O32,
+#endif
+#else
+# if _MIPS_SIM==_ABI64
+# ifdef __mips_soft_float
+ FFI_DEFAULT_ABI = FFI_N64_SOFT_FLOAT,
+# else
+ FFI_DEFAULT_ABI = FFI_N64,
+# endif
+# else
+# ifdef __mips_soft_float
+ FFI_DEFAULT_ABI = FFI_N32_SOFT_FLOAT,
+# else
+ FFI_DEFAULT_ABI = FFI_N32,
+# endif
+# endif
+#endif
+
+ FFI_LAST_ABI = FFI_DEFAULT_ABI + 1
+} ffi_abi;
+
+#define FFI_EXTRA_CIF_FIELDS unsigned rstruct_flag
+#endif /* !LIBFFI_ASM */
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#if defined(FFI_MIPS_O32)
+#define FFI_CLOSURES 1
+#define FFI_TRAMPOLINE_SIZE 20
+#else
+/* N32/N64. */
+# define FFI_CLOSURES 1
+#if _MIPS_SIM==_ABI64
+#define FFI_TRAMPOLINE_SIZE 52
+#else
+#define FFI_TRAMPOLINE_SIZE 20
+#endif
+#endif /* FFI_MIPS_O32 */
+#define FFI_NATIVE_RAW_API 0
+
+#endif
+
diff --git a/src/mips/n32.S b/src/mips/n32.S
new file mode 100644
index 0000000..81e81bc
--- /dev/null
+++ b/src/mips/n32.S
@@ -0,0 +1,591 @@
+/* -----------------------------------------------------------------------
+ n32.S - Copyright (c) 1996, 1998, 2005 Red Hat, Inc.
+
+ MIPS 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.
+ ----------------------------------------------------------------------- */
+
+#define LIBFFI_ASM
+#include <fficonfig.h>
+#include <ffi.h>
+
+/* Only build this code if we are compiling for n32 */
+
+#if defined(FFI_MIPS_N32)
+
+#define callback a0
+#define bytes a2
+#define flags a3
+#define raddr a4
+#define fn a5
+
+#define SIZEOF_FRAME ( 8 * FFI_SIZEOF_ARG )
+
+#ifdef linux
+ .abicalls
+#endif
+ .text
+ .align 2
+ .globl ffi_call_N32
+ .ent ffi_call_N32
+ffi_call_N32:
+.LFB3:
+ .frame $fp, SIZEOF_FRAME, ra
+ .mask 0xc0000000,-FFI_SIZEOF_ARG
+ .fmask 0x00000000,0
+
+ # Prologue
+ SUBU $sp, SIZEOF_FRAME # Frame size
+.LCFI0:
+ REG_S $fp, SIZEOF_FRAME - 2*FFI_SIZEOF_ARG($sp) # Save frame pointer
+ REG_S ra, SIZEOF_FRAME - 1*FFI_SIZEOF_ARG($sp) # Save return address
+.LCFI1:
+ move $fp, $sp
+.LCFI3:
+ move t9, callback # callback function pointer
+ REG_S bytes, 2*FFI_SIZEOF_ARG($fp) # bytes
+ REG_S flags, 3*FFI_SIZEOF_ARG($fp) # flags
+ REG_S raddr, 4*FFI_SIZEOF_ARG($fp) # raddr
+ REG_S fn, 5*FFI_SIZEOF_ARG($fp) # fn
+
+ # Allocate at least 4 words in the argstack
+ move v0, bytes
+ bge bytes, 4 * FFI_SIZEOF_ARG, bigger
+ LI v0, 4 * FFI_SIZEOF_ARG
+ b sixteen
+
+ bigger:
+ ADDU t4, v0, 2 * FFI_SIZEOF_ARG -1 # make sure it is aligned
+ and v0, t4, -2 * FFI_SIZEOF_ARG # to a proper boundry.
+
+sixteen:
+ SUBU $sp, $sp, v0 # move the stack pointer to reflect the
+ # arg space
+
+ move a0, $sp # 4 * FFI_SIZEOF_ARG
+ ADDU a3, $fp, 3 * FFI_SIZEOF_ARG
+
+ # Call ffi_prep_args
+ jal t9
+
+ # Copy the stack pointer to t9
+ move t9, $sp
+
+ # Fix the stack if there are more than 8 64bit slots worth
+ # of arguments.
+
+ # Load the number of bytes
+ REG_L t6, 2*FFI_SIZEOF_ARG($fp)
+
+ # Is it bigger than 8 * FFI_SIZEOF_ARG?
+ daddiu t8, t6, -(8 * FFI_SIZEOF_ARG)
+ bltz t8, loadregs
+
+ ADDU t9, t9, t8
+
+loadregs:
+
+ REG_L t6, 3*FFI_SIZEOF_ARG($fp) # load the flags word into t6.
+
+ and t4, t6, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg1_floatp
+ REG_L a0, 0*FFI_SIZEOF_ARG(t9)
+ b arg1_next
+arg1_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg1_doublep
+ l.s $f12, 0*FFI_SIZEOF_ARG(t9)
+ b arg1_next
+arg1_doublep:
+ l.d $f12, 0*FFI_SIZEOF_ARG(t9)
+arg1_next:
+
+ SRL t4, t6, 1*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg2_floatp
+ REG_L a1, 1*FFI_SIZEOF_ARG(t9)
+ b arg2_next
+arg2_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg2_doublep
+ l.s $f13, 1*FFI_SIZEOF_ARG(t9)
+ b arg2_next
+arg2_doublep:
+ l.d $f13, 1*FFI_SIZEOF_ARG(t9)
+arg2_next:
+
+ SRL t4, t6, 2*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg3_floatp
+ REG_L a2, 2*FFI_SIZEOF_ARG(t9)
+ b arg3_next
+arg3_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg3_doublep
+ l.s $f14, 2*FFI_SIZEOF_ARG(t9)
+ b arg3_next
+arg3_doublep:
+ l.d $f14, 2*FFI_SIZEOF_ARG(t9)
+arg3_next:
+
+ SRL t4, t6, 3*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg4_floatp
+ REG_L a3, 3*FFI_SIZEOF_ARG(t9)
+ b arg4_next
+arg4_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg4_doublep
+ l.s $f15, 3*FFI_SIZEOF_ARG(t9)
+ b arg4_next
+arg4_doublep:
+ l.d $f15, 3*FFI_SIZEOF_ARG(t9)
+arg4_next:
+
+ SRL t4, t6, 4*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg5_floatp
+ REG_L a4, 4*FFI_SIZEOF_ARG(t9)
+ b arg5_next
+arg5_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg5_doublep
+ l.s $f16, 4*FFI_SIZEOF_ARG(t9)
+ b arg5_next
+arg5_doublep:
+ l.d $f16, 4*FFI_SIZEOF_ARG(t9)
+arg5_next:
+
+ SRL t4, t6, 5*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg6_floatp
+ REG_L a5, 5*FFI_SIZEOF_ARG(t9)
+ b arg6_next
+arg6_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg6_doublep
+ l.s $f17, 5*FFI_SIZEOF_ARG(t9)
+ b arg6_next
+arg6_doublep:
+ l.d $f17, 5*FFI_SIZEOF_ARG(t9)
+arg6_next:
+
+ SRL t4, t6, 6*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg7_floatp
+ REG_L a6, 6*FFI_SIZEOF_ARG(t9)
+ b arg7_next
+arg7_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg7_doublep
+ l.s $f18, 6*FFI_SIZEOF_ARG(t9)
+ b arg7_next
+arg7_doublep:
+ l.d $f18, 6*FFI_SIZEOF_ARG(t9)
+arg7_next:
+
+ SRL t4, t6, 7*FFI_FLAG_BITS
+ and t4, ((1<<FFI_FLAG_BITS)-1)
+ bnez t4, arg8_floatp
+ REG_L a7, 7*FFI_SIZEOF_ARG(t9)
+ b arg8_next
+arg8_floatp:
+ bne t4, FFI_TYPE_FLOAT, arg8_doublep
+ l.s $f19, 7*FFI_SIZEOF_ARG(t9)
+ b arg8_next
+arg8_doublep:
+ l.d $f19, 7*FFI_SIZEOF_ARG(t9)
+arg8_next:
+
+callit:
+ # Load the function pointer
+ REG_L t9, 5*FFI_SIZEOF_ARG($fp)
+
+ # If the return value pointer is NULL, assume no return value.
+ REG_L t5, 4*FFI_SIZEOF_ARG($fp)
+ beqz t5, noretval
+
+ # Shift the return type flag over
+ SRL t6, 8*FFI_FLAG_BITS
+
+ beq t6, FFI_TYPE_SINT32, retint
+ bne t6, FFI_TYPE_INT, retfloat
+retint:
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ b epilogue
+
+retfloat:
+ bne t6, FFI_TYPE_FLOAT, retdouble
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t4)
+ b epilogue
+
+retdouble:
+ bne t6, FFI_TYPE_DOUBLE, retstruct_d
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t4)
+ b epilogue
+
+retstruct_d:
+ bne t6, FFI_TYPE_STRUCT_D, retstruct_f
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t4)
+ b epilogue
+
+retstruct_f:
+ bne t6, FFI_TYPE_STRUCT_F, retstruct_d_d
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t4)
+ b epilogue
+
+retstruct_d_d:
+ bne t6, FFI_TYPE_STRUCT_DD, retstruct_f_f
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t4)
+ s.d $f2, 8(t4)
+ b epilogue
+
+retstruct_f_f:
+ bne t6, FFI_TYPE_STRUCT_FF, retstruct_d_f
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t4)
+ s.s $f2, 4(t4)
+ b epilogue
+
+retstruct_d_f:
+ bne t6, FFI_TYPE_STRUCT_DF, retstruct_f_d
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t4)
+ s.s $f2, 8(t4)
+ b epilogue
+
+retstruct_f_d:
+ bne t6, FFI_TYPE_STRUCT_FD, retstruct_d_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t4)
+ s.d $f2, 8(t4)
+ b epilogue
+
+retstruct_d_soft:
+ bne t6, FFI_TYPE_STRUCT_D_SOFT, retstruct_f_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sd v0, 0(t4)
+ b epilogue
+
+retstruct_f_soft:
+ bne t6, FFI_TYPE_STRUCT_F_SOFT, retstruct_d_d_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sw v0, 0(t4)
+ b epilogue
+
+retstruct_d_d_soft:
+ bne t6, FFI_TYPE_STRUCT_DD_SOFT, retstruct_f_f_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sd v0, 0(t4)
+ sd v1, 8(t4)
+ b epilogue
+
+retstruct_f_f_soft:
+ bne t6, FFI_TYPE_STRUCT_FF_SOFT, retstruct_d_f_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sw v0, 0(t4)
+ sw v1, 4(t4)
+ b epilogue
+
+retstruct_d_f_soft:
+ bne t6, FFI_TYPE_STRUCT_DF_SOFT, retstruct_f_d_soft
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sd v0, 0(t4)
+ sw v1, 8(t4)
+ b epilogue
+
+retstruct_f_d_soft:
+ bne t6, FFI_TYPE_STRUCT_FD_SOFT, retstruct_small
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ sw v0, 0(t4)
+ sd v1, 8(t4)
+ b epilogue
+
+retstruct_small:
+ bne t6, FFI_TYPE_STRUCT_SMALL, retstruct_small2
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ b epilogue
+
+retstruct_small2:
+ bne t6, FFI_TYPE_STRUCT_SMALL2, retstruct
+ jal t9
+ REG_L t4, 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t4)
+ REG_S v1, 8(t4)
+ b epilogue
+
+retstruct:
+noretval:
+ jal t9
+
+ # Epilogue
+epilogue:
+ move $sp, $fp
+ REG_L $fp, SIZEOF_FRAME - 2*FFI_SIZEOF_ARG($sp) # Restore frame pointer
+ REG_L ra, SIZEOF_FRAME - 1*FFI_SIZEOF_ARG($sp) # Restore return address
+ ADDU $sp, SIZEOF_FRAME # Fix stack pointer
+ j ra
+
+.LFE3:
+ .end ffi_call_N32
+
+/* ffi_closure_N32. Expects address of the passed-in ffi_closure in t0
+ ($12). Stores any arguments passed in registers onto the stack,
+ then calls ffi_closure_mips_inner_N32, which then decodes
+ them.
+
+ Stack layout:
+
+ 20 - Start of parameters, original sp
+ 19 - Called function a7 save
+ 18 - Called function a6 save
+ 17 - Called function a5 save
+ 16 - Called function a4 save
+ 15 - Called function a3 save
+ 14 - Called function a2 save
+ 13 - Called function a1 save
+ 12 - Called function a0 save
+ 11 - Called function f19
+ 10 - Called function f18
+ 9 - Called function f17
+ 8 - Called function f16
+ 7 - Called function f15
+ 6 - Called function f14
+ 5 - Called function f13
+ 4 - Called function f12
+ 3 - return value high (v1 or $f2)
+ 2 - return value low (v0 or $f0)
+ 1 - ra save
+ 0 - gp save our sp points here
+ */
+
+#define SIZEOF_FRAME2 (20 * FFI_SIZEOF_ARG)
+
+#define A7_OFF2 (19 * FFI_SIZEOF_ARG)
+#define A6_OFF2 (18 * FFI_SIZEOF_ARG)
+#define A5_OFF2 (17 * FFI_SIZEOF_ARG)
+#define A4_OFF2 (16 * FFI_SIZEOF_ARG)
+#define A3_OFF2 (15 * FFI_SIZEOF_ARG)
+#define A2_OFF2 (14 * FFI_SIZEOF_ARG)
+#define A1_OFF2 (13 * FFI_SIZEOF_ARG)
+#define A0_OFF2 (12 * FFI_SIZEOF_ARG)
+
+#define F19_OFF2 (11 * FFI_SIZEOF_ARG)
+#define F18_OFF2 (10 * FFI_SIZEOF_ARG)
+#define F17_OFF2 (9 * FFI_SIZEOF_ARG)
+#define F16_OFF2 (8 * FFI_SIZEOF_ARG)
+#define F15_OFF2 (7 * FFI_SIZEOF_ARG)
+#define F14_OFF2 (6 * FFI_SIZEOF_ARG)
+#define F13_OFF2 (5 * FFI_SIZEOF_ARG)
+#define F12_OFF2 (4 * FFI_SIZEOF_ARG)
+
+#define V1_OFF2 (3 * FFI_SIZEOF_ARG)
+#define V0_OFF2 (2 * FFI_SIZEOF_ARG)
+
+#define RA_OFF2 (1 * FFI_SIZEOF_ARG)
+#define GP_OFF2 (0 * FFI_SIZEOF_ARG)
+
+ .align 2
+ .globl ffi_closure_N32
+ .ent ffi_closure_N32
+ffi_closure_N32:
+.LFB2:
+ .frame $sp, SIZEOF_FRAME2, ra
+ .mask 0x90000000,-(SIZEOF_FRAME2 - RA_OFF2)
+ .fmask 0x00000000,0
+ SUBU $sp, SIZEOF_FRAME2
+.LCFI5:
+ .cpsetup t9, GP_OFF2, ffi_closure_N32
+ REG_S ra, RA_OFF2($sp) # Save return address
+.LCFI6:
+ # Store all possible argument registers. If there are more than
+ # fit in registers, then they were stored on the stack.
+ REG_S a0, A0_OFF2($sp)
+ REG_S a1, A1_OFF2($sp)
+ REG_S a2, A2_OFF2($sp)
+ REG_S a3, A3_OFF2($sp)
+ REG_S a4, A4_OFF2($sp)
+ REG_S a5, A5_OFF2($sp)
+ REG_S a6, A6_OFF2($sp)
+ REG_S a7, A7_OFF2($sp)
+
+ # Store all possible float/double registers.
+ s.d $f12, F12_OFF2($sp)
+ s.d $f13, F13_OFF2($sp)
+ s.d $f14, F14_OFF2($sp)
+ s.d $f15, F15_OFF2($sp)
+ s.d $f16, F16_OFF2($sp)
+ s.d $f17, F17_OFF2($sp)
+ s.d $f18, F18_OFF2($sp)
+ s.d $f19, F19_OFF2($sp)
+
+ # Call ffi_closure_mips_inner_N32 to do the real work.
+ LA t9, ffi_closure_mips_inner_N32
+ move a0, $12 # Pointer to the ffi_closure
+ ADDU a1, $sp, V0_OFF2
+ ADDU a2, $sp, A0_OFF2
+ ADDU a3, $sp, F12_OFF2
+ jalr t9
+
+ # Return flags are in v0
+ bne v0, FFI_TYPE_SINT32, cls_retint
+ lw v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retint:
+ bne v0, FFI_TYPE_INT, cls_retfloat
+ REG_L v0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retfloat:
+ bne v0, FFI_TYPE_FLOAT, cls_retdouble
+ l.s $f0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retdouble:
+ bne v0, FFI_TYPE_DOUBLE, cls_retstruct_d
+ l.d $f0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_d:
+ bne v0, FFI_TYPE_STRUCT_D, cls_retstruct_f
+ l.d $f0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_f:
+ bne v0, FFI_TYPE_STRUCT_F, cls_retstruct_d_d
+ l.s $f0, V0_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_d_d:
+ bne v0, FFI_TYPE_STRUCT_DD, cls_retstruct_f_f
+ l.d $f0, V0_OFF2($sp)
+ l.d $f2, V1_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_f_f:
+ bne v0, FFI_TYPE_STRUCT_FF, cls_retstruct_d_f
+ l.s $f0, V0_OFF2($sp)
+ l.s $f2, V1_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_d_f:
+ bne v0, FFI_TYPE_STRUCT_DF, cls_retstruct_f_d
+ l.d $f0, V0_OFF2($sp)
+ l.s $f2, V1_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_f_d:
+ bne v0, FFI_TYPE_STRUCT_FD, cls_retstruct_small2
+ l.s $f0, V0_OFF2($sp)
+ l.d $f2, V1_OFF2($sp)
+ b cls_epilogue
+
+cls_retstruct_small2:
+ REG_L v0, V0_OFF2($sp)
+ REG_L v1, V1_OFF2($sp)
+
+ # Epilogue
+cls_epilogue:
+ REG_L ra, RA_OFF2($sp) # Restore return address
+ .cpreturn
+ ADDU $sp, SIZEOF_FRAME2
+ j ra
+.LFE2:
+ .end ffi_closure_N32
+
+#ifdef linux
+ .section .eh_frame,"aw",@progbits
+.Lframe1:
+ .4byte .LECIE1-.LSCIE1 # length
+.LSCIE1:
+ .4byte 0x0 # CIE
+ .byte 0x1 # Version 1
+ .ascii "\000" # Augmentation
+ .uleb128 0x1 # Code alignment 1
+ .sleb128 -4 # Data alignment -4
+ .byte 0x1f # Return Address $31
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x1d # in $sp
+ .uleb128 0x0 # offset 0
+ .align EH_FRAME_ALIGN
+.LECIE1:
+
+.LSFDE1:
+ .4byte .LEFDE1-.LASFDE1 # length.
+.LASFDE1:
+ .4byte .LASFDE1-.Lframe1 # CIE_pointer.
+ FDE_ADDR_BYTES .LFB3 # initial_location.
+ FDE_ADDR_BYTES .LFE3-.LFB3 # address_range.
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte .LCFI0-.LFB3 # to .LCFI0
+ .byte 0xe # DW_CFA_def_cfa_offset
+ .uleb128 SIZEOF_FRAME # adjust stack.by SIZEOF_FRAME
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte .LCFI1-.LCFI0 # to .LCFI1
+ .byte 0x9e # DW_CFA_offset of $fp
+ .uleb128 2*FFI_SIZEOF_ARG/4 #
+ .byte 0x9f # DW_CFA_offset of ra
+ .uleb128 1*FFI_SIZEOF_ARG/4 #
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte .LCFI3-.LCFI1 # to .LCFI3
+ .byte 0xd # DW_CFA_def_cfa_register
+ .uleb128 0x1e # in $fp
+ .align EH_FRAME_ALIGN
+.LEFDE1:
+.LSFDE3:
+ .4byte .LEFDE3-.LASFDE3 # length
+.LASFDE3:
+ .4byte .LASFDE3-.Lframe1 # CIE_pointer.
+ FDE_ADDR_BYTES .LFB2 # initial_location.
+ FDE_ADDR_BYTES .LFE2-.LFB2 # address_range.
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte .LCFI5-.LFB2 # to .LCFI5
+ .byte 0xe # DW_CFA_def_cfa_offset
+ .uleb128 SIZEOF_FRAME2 # adjust stack.by SIZEOF_FRAME
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte .LCFI6-.LCFI5 # to .LCFI6
+ .byte 0x9c # DW_CFA_offset of $gp ($28)
+ .uleb128 (SIZEOF_FRAME2 - GP_OFF2)/4
+ .byte 0x9f # DW_CFA_offset of ra ($31)
+ .uleb128 (SIZEOF_FRAME2 - RA_OFF2)/4
+ .align EH_FRAME_ALIGN
+.LEFDE3:
+#endif /* linux */
+
+#endif
diff --git a/src/mips/o32.S b/src/mips/o32.S
new file mode 100644
index 0000000..eb27981
--- /dev/null
+++ b/src/mips/o32.S
@@ -0,0 +1,381 @@
+/* -----------------------------------------------------------------------
+ o32.S - Copyright (c) 1996, 1998, 2005 Red Hat, Inc.
+
+ MIPS 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.
+ ----------------------------------------------------------------------- */
+
+#define LIBFFI_ASM
+#include <fficonfig.h>
+#include <ffi.h>
+
+/* Only build this code if we are compiling for o32 */
+
+#if defined(FFI_MIPS_O32)
+
+#define callback a0
+#define bytes a2
+#define flags a3
+
+#define SIZEOF_FRAME (4 * FFI_SIZEOF_ARG + 2 * FFI_SIZEOF_ARG)
+#define A3_OFF (SIZEOF_FRAME + 3 * FFI_SIZEOF_ARG)
+#define FP_OFF (SIZEOF_FRAME - 2 * FFI_SIZEOF_ARG)
+#define RA_OFF (SIZEOF_FRAME - 1 * FFI_SIZEOF_ARG)
+
+ .abicalls
+ .text
+ .align 2
+ .globl ffi_call_O32
+ .ent ffi_call_O32
+ffi_call_O32:
+$LFB0:
+ # Prologue
+ SUBU $sp, SIZEOF_FRAME # Frame size
+$LCFI0:
+ REG_S $fp, FP_OFF($sp) # Save frame pointer
+$LCFI1:
+ REG_S ra, RA_OFF($sp) # Save return address
+$LCFI2:
+ move $fp, $sp
+
+$LCFI3:
+ move t9, callback # callback function pointer
+ REG_S flags, A3_OFF($fp) # flags
+
+ # Allocate at least 4 words in the argstack
+ LI v0, 4 * FFI_SIZEOF_ARG
+ blt bytes, v0, sixteen
+
+ ADDU v0, bytes, 7 # make sure it is aligned
+ and v0, -8 # to an 8 byte boundry
+
+sixteen:
+ SUBU $sp, v0 # move the stack pointer to reflect the
+ # arg space
+
+ ADDU a0, $sp, 4 * FFI_SIZEOF_ARG
+
+ jalr t9
+
+ REG_L t0, A3_OFF($fp) # load the flags word
+ SRL t2, t0, 4 # shift our arg info
+ and t0, ((1<<4)-1) # mask out the return type
+
+ ADDU $sp, 4 * FFI_SIZEOF_ARG # adjust $sp to new args
+
+ bnez t0, pass_d # make it quick for int
+ REG_L a0, 0*FFI_SIZEOF_ARG($sp) # just go ahead and load the
+ REG_L a1, 1*FFI_SIZEOF_ARG($sp) # four regs.
+ REG_L a2, 2*FFI_SIZEOF_ARG($sp)
+ REG_L a3, 3*FFI_SIZEOF_ARG($sp)
+ b call_it
+
+pass_d:
+ bne t0, FFI_ARGS_D, pass_f
+ l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ REG_L a2, 2*FFI_SIZEOF_ARG($sp) # passing a double
+ REG_L a3, 3*FFI_SIZEOF_ARG($sp)
+ b call_it
+
+pass_f:
+ bne t0, FFI_ARGS_F, pass_d_d
+ l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ REG_L a1, 1*FFI_SIZEOF_ARG($sp) # passing a float
+ REG_L a2, 2*FFI_SIZEOF_ARG($sp)
+ REG_L a3, 3*FFI_SIZEOF_ARG($sp)
+ b call_it
+
+pass_d_d:
+ bne t0, FFI_ARGS_DD, pass_f_f
+ l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ l.d $f14, 2*FFI_SIZEOF_ARG($sp) # passing two doubles
+ b call_it
+
+pass_f_f:
+ bne t0, FFI_ARGS_FF, pass_d_f
+ l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ l.s $f14, 1*FFI_SIZEOF_ARG($sp) # passing two floats
+ REG_L a2, 2*FFI_SIZEOF_ARG($sp)
+ REG_L a3, 3*FFI_SIZEOF_ARG($sp)
+ b call_it
+
+pass_d_f:
+ bne t0, FFI_ARGS_DF, pass_f_d
+ l.d $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ l.s $f14, 2*FFI_SIZEOF_ARG($sp) # passing double and float
+ REG_L a3, 3*FFI_SIZEOF_ARG($sp)
+ b call_it
+
+pass_f_d:
+ # assume that the only other combination must be float then double
+ # bne t0, FFI_ARGS_F_D, call_it
+ l.s $f12, 0*FFI_SIZEOF_ARG($sp) # load $fp regs from args
+ l.d $f14, 2*FFI_SIZEOF_ARG($sp) # passing double and float
+
+call_it:
+ # Load the function pointer
+ REG_L t9, SIZEOF_FRAME + 5*FFI_SIZEOF_ARG($fp)
+
+ # If the return value pointer is NULL, assume no return value.
+ REG_L t1, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ beqz t1, noretval
+
+ bne t2, FFI_TYPE_INT, retlonglong
+ jalr t9
+ REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ REG_S v0, 0(t0)
+ b epilogue
+
+retlonglong:
+ # Really any 64-bit int, signed or not.
+ bne t2, FFI_TYPE_UINT64, retfloat
+ jalr t9
+ REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ REG_S v1, 4(t0)
+ REG_S v0, 0(t0)
+ b epilogue
+
+retfloat:
+ bne t2, FFI_TYPE_FLOAT, retdouble
+ jalr t9
+ REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ s.s $f0, 0(t0)
+ b epilogue
+
+retdouble:
+ bne t2, FFI_TYPE_DOUBLE, noretval
+ jalr t9
+ REG_L t0, SIZEOF_FRAME + 4*FFI_SIZEOF_ARG($fp)
+ s.d $f0, 0(t0)
+ b epilogue
+
+noretval:
+ jalr t9
+
+ # Epilogue
+epilogue:
+ move $sp, $fp
+ REG_L $fp, FP_OFF($sp) # Restore frame pointer
+ REG_L ra, RA_OFF($sp) # Restore return address
+ ADDU $sp, SIZEOF_FRAME # Fix stack pointer
+ j ra
+
+$LFE0:
+ .end ffi_call_O32
+
+
+/* ffi_closure_O32. Expects address of the passed-in ffi_closure
+ in t4 ($12). Stores any arguments passed in registers onto the
+ stack, then calls ffi_closure_mips_inner_O32, which
+ then decodes them.
+
+ Stack layout:
+
+ 3 - a3 save
+ 2 - a2 save
+ 1 - a1 save
+ 0 - a0 save, original sp
+ -1 - ra save
+ -2 - fp save
+ -3 - $16 (s0) save
+ -4 - cprestore
+ -5 - return value high (v1)
+ -6 - return value low (v0)
+ -7 - f14 (le high, be low)
+ -8 - f14 (le low, be high)
+ -9 - f12 (le high, be low)
+ -10 - f12 (le low, be high)
+ -11 - Called function a3 save
+ -12 - Called function a2 save
+ -13 - Called function a1 save
+ -14 - Called function a0 save, our sp and fp point here
+ */
+
+#define SIZEOF_FRAME2 (14 * FFI_SIZEOF_ARG)
+#define A3_OFF2 (SIZEOF_FRAME2 + 3 * FFI_SIZEOF_ARG)
+#define A2_OFF2 (SIZEOF_FRAME2 + 2 * FFI_SIZEOF_ARG)
+#define A1_OFF2 (SIZEOF_FRAME2 + 1 * FFI_SIZEOF_ARG)
+#define A0_OFF2 (SIZEOF_FRAME2 + 0 * FFI_SIZEOF_ARG)
+#define RA_OFF2 (SIZEOF_FRAME2 - 1 * FFI_SIZEOF_ARG)
+#define FP_OFF2 (SIZEOF_FRAME2 - 2 * FFI_SIZEOF_ARG)
+#define S0_OFF2 (SIZEOF_FRAME2 - 3 * FFI_SIZEOF_ARG)
+#define GP_OFF2 (SIZEOF_FRAME2 - 4 * FFI_SIZEOF_ARG)
+#define V1_OFF2 (SIZEOF_FRAME2 - 5 * FFI_SIZEOF_ARG)
+#define V0_OFF2 (SIZEOF_FRAME2 - 6 * FFI_SIZEOF_ARG)
+#define FA_1_1_OFF2 (SIZEOF_FRAME2 - 7 * FFI_SIZEOF_ARG)
+#define FA_1_0_OFF2 (SIZEOF_FRAME2 - 8 * FFI_SIZEOF_ARG)
+#define FA_0_1_OFF2 (SIZEOF_FRAME2 - 9 * FFI_SIZEOF_ARG)
+#define FA_0_0_OFF2 (SIZEOF_FRAME2 - 10 * FFI_SIZEOF_ARG)
+
+ .text
+ .align 2
+ .globl ffi_closure_O32
+ .ent ffi_closure_O32
+ffi_closure_O32:
+$LFB1:
+ # Prologue
+ .frame $fp, SIZEOF_FRAME2, ra
+ .set noreorder
+ .cpload t9
+ .set reorder
+ SUBU $sp, SIZEOF_FRAME2
+ .cprestore GP_OFF2
+$LCFI4:
+ REG_S $16, S0_OFF2($sp) # Save s0
+ REG_S $fp, FP_OFF2($sp) # Save frame pointer
+ REG_S ra, RA_OFF2($sp) # Save return address
+$LCFI6:
+ move $fp, $sp
+
+$LCFI7:
+ # Store all possible argument registers. If there are more than
+ # four arguments, then they are stored above where we put a3.
+ REG_S a0, A0_OFF2($fp)
+ REG_S a1, A1_OFF2($fp)
+ REG_S a2, A2_OFF2($fp)
+ REG_S a3, A3_OFF2($fp)
+
+ # Load ABI enum to s0
+ REG_L $16, 20($12) # cif pointer follows tramp.
+ REG_L $16, 0($16) # abi is first member.
+
+ li $13, 1 # FFI_O32
+ bne $16, $13, 1f # Skip fp save if FFI_O32_SOFT_FLOAT
+
+ # Store all possible float/double registers.
+ s.d $f12, FA_0_0_OFF2($fp)
+ s.d $f14, FA_1_0_OFF2($fp)
+1:
+ # Call ffi_closure_mips_inner_O32 to do the work.
+ la t9, ffi_closure_mips_inner_O32
+ move a0, $12 # Pointer to the ffi_closure
+ addu a1, $fp, V0_OFF2
+ addu a2, $fp, A0_OFF2
+ addu a3, $fp, FA_0_0_OFF2
+ jalr t9
+
+ # Load the return value into the appropriate register.
+ move $8, $2
+ li $9, FFI_TYPE_VOID
+ beq $8, $9, closure_done
+
+ li $13, 1 # FFI_O32
+ bne $16, $13, 1f # Skip fp restore if FFI_O32_SOFT_FLOAT
+
+ li $9, FFI_TYPE_FLOAT
+ l.s $f0, V0_OFF2($fp)
+ beq $8, $9, closure_done
+
+ li $9, FFI_TYPE_DOUBLE
+ l.d $f0, V0_OFF2($fp)
+ beq $8, $9, closure_done
+1:
+ REG_L $3, V1_OFF2($fp)
+ REG_L $2, V0_OFF2($fp)
+
+closure_done:
+ # Epilogue
+ move $sp, $fp
+ REG_L $16, S0_OFF2($sp) # Restore s0
+ REG_L $fp, FP_OFF2($sp) # Restore frame pointer
+ REG_L ra, RA_OFF2($sp) # Restore return address
+ ADDU $sp, SIZEOF_FRAME2
+ j ra
+$LFE1:
+ .end ffi_closure_O32
+
+/* DWARF-2 unwind info. */
+
+ .section .eh_frame,"a",@progbits
+$Lframe0:
+ .4byte $LECIE0-$LSCIE0 # Length of Common Information Entry
+$LSCIE0:
+ .4byte 0x0 # CIE Identifier Tag
+ .byte 0x1 # CIE Version
+ .ascii "zR\0" # CIE Augmentation
+ .uleb128 0x1 # CIE Code Alignment Factor
+ .sleb128 4 # CIE Data Alignment Factor
+ .byte 0x1f # CIE RA Column
+ .uleb128 0x1 # Augmentation size
+ .byte 0x00 # FDE Encoding (absptr)
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x1d
+ .uleb128 0x0
+ .align 2
+$LECIE0:
+$LSFDE0:
+ .4byte $LEFDE0-$LASFDE0 # FDE Length
+$LASFDE0:
+ .4byte $LASFDE0-$Lframe0 # FDE CIE offset
+ .4byte $LFB0 # FDE initial location
+ .4byte $LFE0-$LFB0 # FDE address range
+ .uleb128 0x0 # Augmentation size
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI0-$LFB0
+ .byte 0xe # DW_CFA_def_cfa_offset
+ .uleb128 0x18
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI2-$LCFI0
+ .byte 0x11 # DW_CFA_offset_extended_sf
+ .uleb128 0x1e # $fp
+ .sleb128 -2 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
+ .byte 0x11 # DW_CFA_offset_extended_sf
+ .uleb128 0x1f # $ra
+ .sleb128 -1 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI3-$LCFI2
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x1e
+ .uleb128 0x18
+ .align 2
+$LEFDE0:
+$LSFDE1:
+ .4byte $LEFDE1-$LASFDE1 # FDE Length
+$LASFDE1:
+ .4byte $LASFDE1-$Lframe0 # FDE CIE offset
+ .4byte $LFB1 # FDE initial location
+ .4byte $LFE1-$LFB1 # FDE address range
+ .uleb128 0x0 # Augmentation size
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI4-$LFB1
+ .byte 0xe # DW_CFA_def_cfa_offset
+ .uleb128 0x38
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI6-$LCFI4
+ .byte 0x11 # DW_CFA_offset_extended_sf
+ .uleb128 0x10 # $16
+ .sleb128 -3 # SIZEOF_FRAME2 - 3*FFI_SIZEOF_ARG($sp)
+ .byte 0x11 # DW_CFA_offset_extended_sf
+ .uleb128 0x1e # $fp
+ .sleb128 -2 # SIZEOF_FRAME2 - 2*FFI_SIZEOF_ARG($sp)
+ .byte 0x11 # DW_CFA_offset_extended_sf
+ .uleb128 0x1f # $ra
+ .sleb128 -1 # SIZEOF_FRAME2 - 1*FFI_SIZEOF_ARG($sp)
+ .byte 0x4 # DW_CFA_advance_loc4
+ .4byte $LCFI7-$LCFI6
+ .byte 0xc # DW_CFA_def_cfa
+ .uleb128 0x1e
+ .uleb128 0x38
+ .align 2
+$LEFDE1:
+
+#endif