summaryrefslogtreecommitdiff
path: root/ext/ffi_c/libffi/src/xtensa/ffi.c
diff options
context:
space:
mode:
Diffstat (limited to 'ext/ffi_c/libffi/src/xtensa/ffi.c')
-rw-r--r--ext/ffi_c/libffi/src/xtensa/ffi.c298
1 files changed, 298 insertions, 0 deletions
diff --git a/ext/ffi_c/libffi/src/xtensa/ffi.c b/ext/ffi_c/libffi/src/xtensa/ffi.c
new file mode 100644
index 0000000..fd94daf
--- /dev/null
+++ b/ext/ffi_c/libffi/src/xtensa/ffi.c
@@ -0,0 +1,298 @@
+/* -----------------------------------------------------------------------
+ ffi.c - Copyright (c) 2013 Tensilica, Inc.
+
+ XTENSA 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>
+
+/*
+ |----------------------------------------|
+ | |
+ on entry to ffi_call ----> |----------------------------------------|
+ | caller stack frame for registers a0-a3 |
+ |----------------------------------------|
+ | |
+ | additional arguments |
+ entry of the function ---> |----------------------------------------|
+ | copy of function arguments a2-a7 |
+ | - - - - - - - - - - - - - |
+ | |
+
+ The area below the entry line becomes the new stack frame for the function.
+
+*/
+
+
+#define FFI_TYPE_STRUCT_REGS FFI_TYPE_LAST
+
+
+extern void ffi_call_SYSV(void *rvalue, unsigned rsize, unsigned flags,
+ void(*fn)(void), unsigned nbytes, extended_cif*);
+extern void ffi_closure_SYSV(void) FFI_HIDDEN;
+
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
+{
+ switch(cif->rtype->type) {
+ case FFI_TYPE_SINT8:
+ case FFI_TYPE_UINT8:
+ case FFI_TYPE_SINT16:
+ case FFI_TYPE_UINT16:
+ cif->flags = cif->rtype->type;
+ break;
+ case FFI_TYPE_VOID:
+ case FFI_TYPE_FLOAT:
+ cif->flags = FFI_TYPE_UINT32;
+ break;
+ case FFI_TYPE_DOUBLE:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ cif->flags = FFI_TYPE_UINT64; // cif->rtype->type;
+ break;
+ case FFI_TYPE_STRUCT:
+ cif->flags = FFI_TYPE_STRUCT; //_REGS;
+ /* Up to 16 bytes are returned in registers */
+ if (cif->rtype->size > 4 * 4) {
+ /* returned structure is referenced by a register; use 8 bytes
+ (including 4 bytes for potential additional alignment) */
+ cif->flags = FFI_TYPE_STRUCT;
+ cif->bytes += 8;
+ }
+ break;
+
+ default:
+ cif->flags = FFI_TYPE_UINT32;
+ break;
+ }
+
+ /* Round the stack up to a full 4 register frame, just in case
+ (we use this size in movsp). This way, it's also a multiple of
+ 8 bytes for 64-bit arguments. */
+ cif->bytes = ALIGN(cif->bytes, 16);
+
+ return FFI_OK;
+}
+
+void ffi_prep_args(extended_cif *ecif, unsigned char* stack)
+{
+ unsigned int i;
+ unsigned long *addr;
+ ffi_type **ptr;
+
+ union {
+ void **v;
+ char **c;
+ signed char **sc;
+ unsigned char **uc;
+ signed short **ss;
+ unsigned short **us;
+ unsigned int **i;
+ long long **ll;
+ float **f;
+ double **d;
+ } p_argv;
+
+ /* Verify that everything is aligned up properly */
+ FFI_ASSERT (((unsigned long) stack & 0x7) == 0);
+
+ p_argv.v = ecif->avalue;
+ addr = (unsigned long*)stack;
+
+ /* structures with a size greater than 16 bytes are passed in memory */
+ if (ecif->cif->rtype->type == FFI_TYPE_STRUCT && ecif->cif->rtype->size > 16)
+ {
+ *addr++ = (unsigned long)ecif->rvalue;
+ }
+
+ for (i = ecif->cif->nargs, ptr = ecif->cif->arg_types;
+ i > 0;
+ i--, ptr++, p_argv.v++)
+ {
+ switch ((*ptr)->type)
+ {
+ case FFI_TYPE_SINT8:
+ *addr++ = **p_argv.sc;
+ break;
+ case FFI_TYPE_UINT8:
+ *addr++ = **p_argv.uc;
+ break;
+ case FFI_TYPE_SINT16:
+ *addr++ = **p_argv.ss;
+ break;
+ case FFI_TYPE_UINT16:
+ *addr++ = **p_argv.us;
+ break;
+ case FFI_TYPE_FLOAT:
+ case FFI_TYPE_INT:
+ case FFI_TYPE_UINT32:
+ case FFI_TYPE_SINT32:
+ case FFI_TYPE_POINTER:
+ *addr++ = **p_argv.i;
+ break;
+ case FFI_TYPE_DOUBLE:
+ case FFI_TYPE_UINT64:
+ case FFI_TYPE_SINT64:
+ if (((unsigned long)addr & 4) != 0)
+ addr++;
+ *(unsigned long long*)addr = **p_argv.ll;
+ addr += sizeof(unsigned long long) / sizeof (addr);
+ break;
+
+ case FFI_TYPE_STRUCT:
+ {
+ unsigned long offs;
+ unsigned long size;
+
+ if (((unsigned long)addr & 4) != 0 && (*ptr)->alignment > 4)
+ addr++;
+
+ offs = (unsigned long) addr - (unsigned long) stack;
+ size = (*ptr)->size;
+
+ /* Entire structure must fit the argument registers or referenced */
+ if (offs < FFI_REGISTER_NARGS * 4
+ && offs + size > FFI_REGISTER_NARGS * 4)
+ addr = (unsigned long*) (stack + FFI_REGISTER_NARGS * 4);
+
+ memcpy((char*) addr, *p_argv.c, size);
+ addr += (size + 3) / 4;
+ break;
+ }
+
+ default:
+ FFI_ASSERT(0);
+ }
+ }
+}
+
+
+void ffi_call(ffi_cif* cif, void(*fn)(void), void *rvalue, void **avalue)
+{
+ extended_cif ecif;
+ unsigned long rsize = cif->rtype->size;
+ int flags = cif->flags;
+ void *alloc = NULL;
+
+ ecif.cif = cif;
+ ecif.avalue = avalue;
+
+ /* Note that for structures that are returned in registers (size <= 16 bytes)
+ we allocate a temporary buffer and use memcpy to copy it to the final
+ destination. The reason is that the target address might be misaligned or
+ the length not a multiple of 4 bytes. Handling all those cases would be
+ very complex. */
+
+ if (flags == FFI_TYPE_STRUCT && (rsize <= 16 || rvalue == NULL))
+ {
+ alloc = alloca(ALIGN(rsize, 4));
+ ecif.rvalue = alloc;
+ }
+ else
+ {
+ ecif.rvalue = rvalue;
+ }
+
+ if (cif->abi != FFI_SYSV)
+ FFI_ASSERT(0);
+
+ ffi_call_SYSV (ecif.rvalue, rsize, cif->flags, fn, cif->bytes, &ecif);
+
+ if (alloc != NULL && rvalue != NULL)
+ memcpy(rvalue, alloc, rsize);
+}
+
+extern void ffi_trampoline();
+extern void ffi_cacheflush(void* start, void* end);
+
+ffi_status
+ffi_prep_closure_loc (ffi_closure* closure,
+ ffi_cif* cif,
+ void (*fun)(ffi_cif*, void*, void**, void*),
+ void *user_data,
+ void *codeloc)
+{
+ /* copye trampoline to stack and patch 'ffi_closure_SYSV' pointer */
+ memcpy(closure->tramp, ffi_trampoline, FFI_TRAMPOLINE_SIZE);
+ *(unsigned int*)(&closure->tramp[8]) = (unsigned int)ffi_closure_SYSV;
+
+ // Do we have this function?
+ // __builtin___clear_cache(closer->tramp, closer->tramp + FFI_TRAMPOLINE_SIZE)
+ ffi_cacheflush(closure->tramp, closure->tramp + FFI_TRAMPOLINE_SIZE);
+
+ closure->cif = cif;
+ closure->fun = fun;
+ closure->user_data = user_data;
+ return FFI_OK;
+}
+
+
+long FFI_HIDDEN
+ffi_closure_SYSV_inner(ffi_closure *closure, void **values, void *rvalue)
+{
+ ffi_cif *cif;
+ ffi_type **arg_types;
+ void **avalue;
+ int i, areg;
+
+ cif = closure->cif;
+ if (cif->abi != FFI_SYSV)
+ return FFI_BAD_ABI;
+
+ areg = 0;
+
+ int rtype = cif->rtype->type;
+ if (rtype == FFI_TYPE_STRUCT && cif->rtype->size > 4 * 4)
+ {
+ rvalue = *values;
+ areg++;
+ }
+
+ cif = closure->cif;
+ arg_types = cif->arg_types;
+ avalue = alloca(cif->nargs * sizeof(void *));
+
+ for (i = 0; i < cif->nargs; i++)
+ {
+ if (arg_types[i]->alignment == 8 && (areg & 1) != 0)
+ areg++;
+
+ // skip the entry 16,a1 framework, add 16 bytes (4 registers)
+ if (areg == FFI_REGISTER_NARGS)
+ areg += 4;
+
+ if (arg_types[i]->type == FFI_TYPE_STRUCT)
+ {
+ int numregs = ((arg_types[i]->size + 3) & ~3) / 4;
+ if (areg < FFI_REGISTER_NARGS && areg + numregs > FFI_REGISTER_NARGS)
+ areg = FFI_REGISTER_NARGS + 4;
+ }
+
+ avalue[i] = &values[areg];
+ areg += (arg_types[i]->size + 3) / 4;
+ }
+
+ (closure->fun)(cif, rvalue, avalue, closure->user_data);
+
+ return rtype;
+}