Add Meta processor support
authorAnthony Green <green@moxielogic.com>
Sat, 16 Mar 2013 11:46:38 +0000 (07:46 -0400)
committerAnthony Green <green@moxielogic.com>
Sat, 16 Mar 2013 11:46:38 +0000 (07:46 -0400)
ChangeLog
Makefile.am
README
configure.ac
src/metag/ffi.c [new file with mode: 0644]
src/metag/ffitarget.h [new file with mode: 0644]
src/metag/sysv.S [new file with mode: 0644]

index 5e9e369..525e518 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -2,6 +2,15 @@
 
        * src/x86/ffi.c (ffi_prep_cif_machdep): Always align stack.
 
+2013-03-13  Markos Chandras <markos.chandras@imgtec.com>
+
+       * configure.ac: Add support for Imagination Technologies Meta.
+       * Makefile.am: Likewise.
+       * README: Add Imagination Technologies Meta details.
+       * src/metag/ffi.c: New.
+       * src/metag/ffitarget.h: Likewise.
+       * src/metag/sysv.S: Likewise.
+
 2013-02-11  Anthony Green <green@moxielogic.com>
 
        * configure.ac: Update release number to 3.0.12.
index 8f1362a..cf5caa9 100644 (file)
@@ -38,6 +38,7 @@ EXTRA_DIST = LICENSE ChangeLog.v1 ChangeLog.libgcj configure.host     \
        src/frv/ffitarget.h src/dlmalloc.c src/tile/ffi.c               \
        src/tile/ffitarget.h src/tile/tile.S libtool-version            \
        src/xtensa/ffitarget.h src/xtensa/ffi.c src/xtensa/sysv.S       \
+       src/metag/ffi.c src/metag/ffitarget.h src/metag/sysv.S          \
         ChangeLog.libffi m4/libtool.m4 m4/lt~obsolete.m4               \
         m4/ltoptions.m4 m4/ltsugar.m4 m4/ltversion.m4                  \
         m4/ltversion.m4 src/arm/gentramp.sh src/debug.c msvcc.sh       \
@@ -208,6 +209,9 @@ endif
 if XTENSA
 nodist_libffi_la_SOURCES += src/xtensa/sysv.S src/xtensa/ffi.c
 endif
+if METAG
+nodist_libffi_la_SOURCES += src/metag/sysv.S src/metag/ffi.c
+endif
 
 libffi_convenience_la_SOURCES = $(libffi_la_SOURCES)
 nodist_libffi_convenience_la_SOURCES = $(nodist_libffi_la_SOURCES)
diff --git a/README b/README
index 82b2104..f1a68f2 100644 (file)
--- a/README
+++ b/README
@@ -1,7 +1,7 @@
 Status
 ======
 
-libffi-3.0.12 was released on February 11, 2013.  Check the libffi web
+libffi-3.0.13 was released on March 16, 2013.  Check the libffi web
 page for updates: <URL:http://sourceware.org/libffi/>.
 
 
@@ -63,6 +63,7 @@ tested:
 | M68K            | FreeMiNT         | GCC                     |
 | M68K            | Linux           | GCC                     |
 | M68K            | RTEMS            | GCC                     |
+| Meta            | Linux            | GCC                     |
 | MicroBlaze      | Linux            | GCC                     |
 | MIPS            | IRIX             | GCC                     |
 | MIPS            | Linux            | GCC                     |
@@ -162,6 +163,11 @@ History
 
 See the ChangeLog files for details.
 
+3.0.13 Mar-16-13
+       
+       Add Meta support.
+       Fix stack alignment bug on 32-bit x86.
+
 3.0.12 Feb-11-13
         Add Moxie support.
        Add AArch64 support.
index 428d938..99a2f22 100644 (file)
@@ -199,6 +199,10 @@ case "$host" in
        TARGET=MOXIE; TARGETDIR=moxie
        ;;
 
+  metag-*-*)
+       TARGET=METAG; TARGETDIR=metag
+       ;;
+
   mips-sgi-irix5.* | mips-sgi-irix6.* | mips*-*-rtems*)
        TARGET=MIPS; TARGETDIR=mips
        ;;
@@ -278,6 +282,7 @@ AM_CONDITIONAL(IA64, test x$TARGET = xIA64)
 AM_CONDITIONAL(M32R, test x$TARGET = xM32R)
 AM_CONDITIONAL(M68K, test x$TARGET = xM68K)
 AM_CONDITIONAL(MICROBLAZE, test x$TARGET = xMICROBLAZE)
+AM_CONDITIONAL(METAG, test x$TARGET = xMETAG)
 AM_CONDITIONAL(MOXIE, test x$TARGET = xMOXIE)
 AM_CONDITIONAL(POWERPC, test x$TARGET = xPOWERPC)
 AM_CONDITIONAL(POWERPC_AIX, test x$TARGET = xPOWERPC_AIX)
diff --git a/src/metag/ffi.c b/src/metag/ffi.c
new file mode 100644 (file)
index 0000000..46b383e
--- /dev/null
@@ -0,0 +1,330 @@
+/* ----------------------------------------------------------------------
+  ffi.c - Copyright (c) 2013 Imagination Technologies
+
+  Meta 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 SIMON POSNJAK 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>
+
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
+/*
+ * ffi_prep_args is called by the assembly routine once stack space has been
+ * allocated for the function's arguments
+ */
+
+unsigned int ffi_prep_args(char *stack, extended_cif *ecif)
+{
+       register unsigned int i;
+       register void **p_argv;
+       register char *argp;
+       register ffi_type **p_arg;
+
+       argp = stack;
+
+       /* Store return value */
+       if ( ecif->cif->flags == FFI_TYPE_STRUCT ) {
+               argp -= 4;
+               *(void **) argp = ecif->rvalue;
+       }
+
+       p_argv = ecif->avalue;
+
+       /* point to next location */
+       for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types; (i != 0); i--, p_arg++, p_argv++)
+       {
+               size_t z;
+
+               /* Move argp to address of argument */
+               z = (*p_arg)->size;
+               argp -= z;
+
+               /* Align if necessary */
+               argp = (char *) ALIGN_DOWN(ALIGN_DOWN(argp, (*p_arg)->alignment), 4);
+
+               if (z < sizeof(int)) {
+                       z = sizeof(int);
+                       switch ((*p_arg)->type)
+                       {
+                       case FFI_TYPE_SINT8:
+                               *(signed int *) argp = (signed int)*(SINT8 *)(* p_argv);
+                               break;
+                       case FFI_TYPE_UINT8:
+                               *(unsigned int *) argp = (unsigned int)*(UINT8 *)(* p_argv);
+                               break;
+                       case FFI_TYPE_SINT16:
+                               *(signed int *) argp = (signed int)*(SINT16 *)(* p_argv);
+                               break;
+                       case FFI_TYPE_UINT16:
+                               *(unsigned int *) argp = (unsigned int)*(UINT16 *)(* p_argv);
+                       case FFI_TYPE_STRUCT:
+                               memcpy(argp, *p_argv, (*p_arg)->size);
+                               break;
+                       default:
+                               FFI_ASSERT(0);
+                       }
+               } else if ( z == sizeof(int)) {
+                       *(unsigned int *) argp = (unsigned int)*(UINT32 *)(* p_argv);
+               } else {
+                       memcpy(argp, *p_argv, z);
+               }
+       }
+
+       /* return the size of the arguments to be passed in registers,
+          padded to an 8 byte boundary to preserve stack alignment */
+       return ALIGN(MIN(stack - argp, 6*4), 8);
+}
+
+/* Perform machine dependent cif processing */
+ffi_status ffi_prep_cif_machdep(ffi_cif *cif)
+{
+       ffi_type **ptr;
+       unsigned i, bytes = 0;
+
+       for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++) {
+               if ((*ptr)->size == 0)
+                       return FFI_BAD_TYPEDEF;
+
+               /* Perform a sanity check on the argument type, do this
+                  check after the initialization.  */
+               FFI_ASSERT_VALID_TYPE(*ptr);
+
+               /* Add any padding if necessary */
+               if (((*ptr)->alignment - 1) & bytes)
+                       bytes = ALIGN(bytes, (*ptr)->alignment);
+
+               bytes += ALIGN((*ptr)->size, 4);
+       }
+
+       /* Ensure arg space is aligned to an 8-byte boundary */
+       bytes = ALIGN(bytes, 8);
+
+       /* Make space for the return structure pointer */
+       if (cif->rtype->type == FFI_TYPE_STRUCT) {
+               bytes += sizeof(void*);
+
+               /* Ensure stack is aligned to an 8-byte boundary */
+               bytes = ALIGN(bytes, 8);
+       }
+
+       cif->bytes = bytes;
+
+       /* Set the return type flag */
+       switch (cif->rtype->type) {
+       case FFI_TYPE_VOID:
+       case FFI_TYPE_FLOAT:
+       case FFI_TYPE_DOUBLE:
+               cif->flags = (unsigned) cif->rtype->type;
+               break;
+       case FFI_TYPE_SINT64:
+       case FFI_TYPE_UINT64:
+               cif->flags = (unsigned) FFI_TYPE_SINT64;
+               break;
+       case FFI_TYPE_STRUCT:
+               /* Meta can store return values which are <= 64 bits */
+               if (cif->rtype->size <= 4)
+                       /* Returned to D0Re0 as 32-bit value */
+                       cif->flags = (unsigned)FFI_TYPE_INT;
+               else if ((cif->rtype->size > 4) && (cif->rtype->size <= 8))
+                       /* Returned valued is stored to D1Re0|R0Re0 */
+                       cif->flags = (unsigned)FFI_TYPE_DOUBLE;
+               else
+                       /* value stored in memory */
+                       cif->flags = (unsigned)FFI_TYPE_STRUCT;
+               break;
+       default:
+               cif->flags = (unsigned)FFI_TYPE_INT;
+               break;
+       }
+       return FFI_OK;
+}
+
+extern void ffi_call_SYSV(void (*fn)(void), extended_cif *, unsigned, unsigned, double *);
+
+/*
+ * Exported in API. Entry point
+ * cif -> ffi_cif object
+ * fn -> function pointer
+ * rvalue -> pointer to return value
+ * avalue -> vector of void * pointers pointing to memory locations holding the
+ * arguments
+ */
+void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
+{
+       extended_cif ecif;
+
+       int small_struct = (((cif->flags == FFI_TYPE_INT) || (cif->flags == FFI_TYPE_DOUBLE)) && (cif->rtype->type == FFI_TYPE_STRUCT));
+       ecif.cif = cif;
+       ecif.avalue = avalue;
+
+       double temp;
+
+       /*
+        * 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))
+               ecif.rvalue = alloca(cif->rtype->size);
+       else if (small_struct)
+               ecif.rvalue = &temp;
+       else
+               ecif.rvalue = rvalue;
+
+       switch (cif->abi) {
+       case FFI_SYSV:
+               ffi_call_SYSV(fn, &ecif, cif->bytes, cif->flags, ecif.rvalue);
+               break;
+       default:
+               FFI_ASSERT(0);
+               break;
+       }
+
+       if (small_struct)
+               memcpy (rvalue, &temp, cif->rtype->size);
+}
+
+/* private members */
+
+static void ffi_prep_incoming_args_SYSV (char *, void **, void **,
+       ffi_cif*, float *);
+
+void ffi_closure_SYSV (ffi_closure *);
+
+/* Do NOT change that without changing the FFI_TRAMPOLINE_SIZE */
+extern unsigned int ffi_metag_trampoline[10]; /* 10 instructions */
+
+/* end of private members */
+
+/*
+ * __tramp: trampoline memory location
+ * __fun: assembly routine
+ * __ctx: memory location for wrapper
+ *
+ * At this point, tramp[0] == __ctx !
+ */
+void ffi_init_trampoline(unsigned char *__tramp, unsigned int __fun, unsigned int __ctx) {
+       memcpy (__tramp, ffi_metag_trampoline, sizeof(ffi_metag_trampoline));
+       *(unsigned int*) &__tramp[40] = __ctx;
+       *(unsigned int*) &__tramp[44] = __fun;
+       /* This will flush the instruction cache */
+       __builtin_meta2_cachewd(&__tramp[0], 1);
+       __builtin_meta2_cachewd(&__tramp[47], 1);
+}
+
+
+
+/* the cif must already be prepared */
+
+ffi_status
+ffi_prep_closure_loc (ffi_closure *closure,
+       ffi_cif* cif,
+       void (*fun)(ffi_cif*,void*,void**,void*),
+       void *user_data,
+       void *codeloc)
+{
+       void (*closure_func)(ffi_closure*) = NULL;
+
+       if (cif->abi == FFI_SYSV)
+               closure_func = &ffi_closure_SYSV;
+       else
+               return FFI_BAD_ABI;
+
+       ffi_init_trampoline(
+               (unsigned char*)&closure->tramp[0],
+               (unsigned int)closure_func,
+               (unsigned int)codeloc);
+
+       closure->cif = cif;
+       closure->user_data = user_data;
+       closure->fun = fun;
+
+       return FFI_OK;
+}
+
+
+/* This function is jumped to by the trampoline */
+unsigned int ffi_closure_SYSV_inner (closure, respp, args, vfp_args)
+       ffi_closure *closure;
+       void **respp;
+       void *args;
+       void *vfp_args;
+{
+       ffi_cif *cif;
+       void **arg_area;
+
+       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_SYSV(args, respp, arg_area, cif, vfp_args);
+
+       (closure->fun) ( cif, *respp, arg_area, closure->user_data);
+
+       return cif->flags;
+}
+
+static void ffi_prep_incoming_args_SYSV(char *stack, void **rvalue,
+       void **avalue, ffi_cif *cif,
+       float *vfp_stack)
+{
+       register unsigned int i;
+       register void **p_argv;
+       register char *argp;
+       register ffi_type **p_arg;
+
+       /* stack points to original arguments */
+       argp = stack;
+
+       /* Store return value */
+       if ( cif->flags == FFI_TYPE_STRUCT ) {
+               argp -= 4;
+               *rvalue = *(void **) argp;
+       }
+
+       p_argv = avalue;
+
+       for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++) {
+               size_t z;
+               size_t alignment;
+
+               alignment = (*p_arg)->alignment;
+               if (alignment < 4)
+                       alignment = 4;
+               if ((alignment - 1) & (unsigned)argp)
+                       argp = (char *) ALIGN(argp, alignment);
+
+               z = (*p_arg)->size;
+               *p_argv = (void*) argp;
+               p_argv++;
+               argp -= z;
+       }
+       return;
+}
diff --git a/src/metag/ffitarget.h b/src/metag/ffitarget.h
new file mode 100644 (file)
index 0000000..7b9dbeb
--- /dev/null
@@ -0,0 +1,53 @@
+/* -----------------------------------------------------------------*-C-*-
+   ffitarget.h - Copyright (c) 2013 Imagination Technologies Ltd.
+   Target configuration macros for Meta
+
+   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
+
+#ifndef LIBFFI_H
+#error "Please do not include ffitarget.h directly into your source.  Use ffi.h instead."
+#endif
+
+#ifndef LIBFFI_ASM
+typedef unsigned long          ffi_arg;
+typedef signed long            ffi_sarg;
+
+typedef enum ffi_abi {
+  FFI_FIRST_ABI = 0,
+  FFI_SYSV,
+  FFI_DEFAULT_ABI = FFI_SYSV,
+  FFI_LAST_ABI = FFI_DEFAULT_ABI + 1,
+} ffi_abi;
+#endif
+
+/* ---- Definitions for closures ----------------------------------------- */
+
+#define FFI_CLOSURES 1
+#define FFI_TRAMPOLINE_SIZE 48
+#define FFI_NATIVE_RAW_API 0
+
+#endif
+
diff --git a/src/metag/sysv.S b/src/metag/sysv.S
new file mode 100644 (file)
index 0000000..b4b2a3b
--- /dev/null
@@ -0,0 +1,311 @@
+/* -----------------------------------------------------------------------
+   sysv.S - Copyright (c) 2013 Imagination Technologies Ltd.
+
+   Meta 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>
+#ifdef HAVE_MACHINE_ASM_H
+#include <machine/asm.h>
+#else
+#ifdef __USER_LABEL_PREFIX__
+#define CONCAT1(a, b) CONCAT2(a, b)
+#define CONCAT2(a, b) a ## b
+
+/* Use the right prefix for global labels. */
+#define CNAME(x) CONCAT1 (__USER_LABEL_PREFIX__, x)
+#else
+#define CNAME(x) x
+#endif
+#define ENTRY(x) .globl CNAME(x); .type CNAME(x), %function; CNAME(x):
+#endif
+
+#ifdef __ELF__
+#define LSYM(x) .x
+#else
+#define LSYM(x) x
+#endif
+
+.macro call_reg x=
+       .text
+       .balign 4
+       mov D1RtP, \x
+       swap D1RtP, PC
+.endm
+
+! Save register arguments
+.macro SAVE_ARGS
+       .text
+       .balign 4
+       setl    [A0StP++], D0Ar6, D1Ar5
+       setl    [A0StP++], D0Ar4, D1Ar3
+       setl    [A0StP++], D0Ar2, D1Ar1
+.endm
+
+! Save retrun, frame pointer and other regs
+.macro SAVE_REGS regs=
+       .text
+       .balign 4
+       setl    [A0StP++], D0FrT, D1RtP
+       ! Needs to be a pair of regs
+       .ifnc "\regs",""
+       setl    [A0StP++], \regs
+       .endif
+.endm
+
+! Declare a global function
+.macro METAG_FUNC_START name
+       .text
+       .balign 4
+       ENTRY(\name)
+.endm
+
+! Return registers from the stack. Reverse SAVE_REGS operation
+.macro RET_REGS regs=, cond=
+       .ifnc "\regs", ""
+       getl    \regs, [--A0StP]
+       .endif
+       getl    D0FrT, D1RtP, [--A0StP]
+.endm
+
+! Return arguments
+.macro RET_ARGS
+       getl    D0Ar2, D1Ar1, [--A0StP]
+       getl    D0Ar4, D1Ar3, [--A0StP]
+       getl    D0Ar6, D1Ar5, [--A0StP]
+.endm
+
+
+       ! D1Ar1:        fn
+       ! D0Ar2:        &ecif
+       ! D1Ar3:        cif->bytes
+       ! D0Ar4:        fig->flags
+       ! D1Ar5:        ecif.rvalue
+
+       ! This assumes we are using GNU as
+METAG_FUNC_START ffi_call_SYSV
+       ! Save argument registers
+
+       SAVE_ARGS
+
+       ! new frame
+       mov     D0FrT, A0FrP
+       add     A0FrP, A0StP, #0
+
+       ! Preserve the old frame pointer
+       SAVE_REGS "D1.5, D0.5"
+
+       ! Make room for new args. cifs->bytes is the total space for input
+       ! and return arguments
+
+       add     A0StP, A0StP, D1Ar3
+
+       ! Preserve cifs->bytes & fn
+       mov     D0.5, D1Ar3
+       mov     D1.5, D1Ar1
+
+       ! Place all of the ffi_prep_args in position
+       mov     D1Ar1, A0StP
+
+       ! Call ffi_prep_args(stack, &ecif)
+#ifdef __PIC__
+       callr  D1RtP, CNAME(ffi_prep_args@PLT)
+#else
+       callr  D1RtP, CNAME(ffi_prep_args)
+#endif
+
+       ! Restore fn pointer
+
+       ! The foreign stack should look like this
+       ! XXXXX XXXXXX <--- stack pointer
+       ! FnArgN rvalue
+       ! FnArgN+2 FnArgN+1
+       ! FnArgN+4 FnArgN+3
+       ! ....
+       !
+
+       ! A0StP now points to the first (or return) argument + 4
+
+       ! Preserve cif->bytes
+       getl    D0Ar2, D1Ar1, [--A0StP]
+       getl    D0Ar4, D1Ar3, [--A0StP]
+       getl    D0Ar6, D1Ar5, [--A0StP]
+
+       ! Place A0StP to the first argument again
+       add     A0StP, A0StP, #24 ! That's because we loaded 6 regs x 4 byte each
+
+       ! A0FrP points to the initial stack without the reserved space for the
+       ! cifs->bytes, whilst A0StP points to the stack after the space allocation
+
+       ! fn was the first argument of ffi_call_SYSV.
+       ! The stack at this point looks like this:
+       !
+       ! A0StP(on entry to _SYSV) ->   Arg6    Arg5     | low
+       !                               Arg4    Arg3     |
+       !                               Arg2    Arg1     |
+       ! A0FrP ---->                   D0FrtP  D1RtP    |
+       !                               D1.5    D0.5     |
+       ! A0StP(bf prep_args) ->        FnArgn  FnArgn-1 |
+       !                               FnArgn-2FnArgn-3 |
+       !                               ................ | <= cifs->bytes
+       !                               FnArg4  FnArg3   |
+       ! A0StP (prv_A0StP+cifs->bytes) FnArg2  FnArg1   | high
+       !
+       ! fn was in Arg1 so it's located in in A0FrP+#-0xC
+       !
+
+       ! D0Re0 contains the size of arguments stored in registers
+       sub     A0StP, A0StP, D0Re0
+
+       ! Arg1 is the function pointer for the foreign call. This has been
+       ! preserved in D1.5
+
+       ! Time to call (fn). Arguments should be like this:
+       ! Arg1-Arg6 are loaded to regs
+       ! The rest of the arguments are stored in stack pointed by A0StP
+
+       call_reg D1.5
+
+       ! Reset stack.
+
+       mov     A0StP, A0FrP
+
+       ! Load Arg1 with the pointer to storage for the return type
+       ! This was stored in Arg5
+
+       getd    D1Ar1, [A0FrP+#-20]
+
+       ! Load D0Ar2 with the return type code. This was stored in Arg4 (flags)
+
+       getd    D0Ar2, [A0FrP+#-16]
+
+       ! We are ready to start processing the return value
+       ! D0Re0 (and D1Re0) hold the return value
+
+       ! If the return value is NULL, assume no return value
+       cmp     D1Ar1, #0
+       beq     LSYM(Lepilogue)
+
+       ! return INT
+       cmp             D0Ar2, #FFI_TYPE_INT
+       ! Sadly, there is no setd{cc} instruction so we need to workaround that
+       bne     .INT64
+       setd    [D1Ar1], D0Re0
+       b       LSYM(Lepilogue)
+
+       ! return INT64
+.INT64:
+       cmp     D0Ar2, #FFI_TYPE_SINT64
+       setleq  [D1Ar1], D0Re0, D1Re0
+
+       ! return DOUBLE
+       cmp     D0Ar2, #FFI_TYPE_DOUBLE
+       setl    [D1AR1++], D0Re0, D1Re0
+
+LSYM(Lepilogue):
+       ! At this point, the stack pointer points right after the argument
+       ! saved area. We need to restore 4 regs, therefore we need to move
+       ! 16 bytes ahead.
+       add     A0StP, A0StP, #16
+       RET_REGS "D1.5, D0.5"
+       RET_ARGS
+       getd    D0Re0, [A0StP]
+       mov     A0FrP, D0FrT
+       swap    D1RtP, PC
+
+.ffi_call_SYSV_end:
+       .size   CNAME(ffi_call_SYSV),.ffi_call_SYSV_end-CNAME(ffi_call_SYSV)
+
+
+/*
+       (called by ffi_metag_trampoline)
+       void ffi_closure_SYSV (ffi_closure*)
+
+       (called by ffi_closure_SYSV)
+       unsigned int FFI_HIDDEN
+       ffi_closure_SYSV_inner (closure,respp, args)
+               ffi_closure *closure;
+               void **respp;
+               void *args;
+*/
+
+METAG_FUNC_START ffi_closure_SYSV
+       ! We assume that D1Ar1 holds the address of the
+       ! ffi_closure struct. We will use that to fetch the
+       ! arguments. The stack pointer points to an empty space
+       ! and it is ready to store more data.
+
+       ! D1Ar1 is ready
+       ! Allocate stack space for return value
+       add A0StP, A0StP, #8
+       ! Store it to D0Ar2
+       sub D0Ar2, A0StP, #8
+
+       sub D1Ar3, A0FrP, #4
+
+       ! D1Ar3 contains the address of the original D1Ar1 argument
+       ! We need to subtract #4 later on
+
+       ! Preverve D0Ar2
+       mov D0.5, D0Ar2
+
+#ifdef __PIC__
+       callr D1RtP, CNAME(ffi_closure_SYSV_inner@PLT)
+#else
+       callr D1RtP, CNAME(ffi_closure_SYSV_inner)
+#endif
+
+       ! Check the return value and store it to D0.5
+       cmp D0Re0, #FFI_TYPE_INT
+       beq .Lretint
+       cmp D0Re0, #FFI_TYPE_DOUBLE
+       beq .Lretdouble
+.Lclosure_epilogue:
+       sub A0StP, A0StP, #8
+       RET_REGS "D1.5, D0.5"
+       RET_ARGS
+       swap    D1RtP, PC
+
+.Lretint:
+       setd [D0.5], D0Re0
+       b .Lclosure_epilogue
+.Lretdouble:
+       setl [D0.5++], D0Re0, D1Re0
+       b .Lclosure_epilogue
+.ffi_closure_SYSV_end:
+.size CNAME(ffi_closure_SYSV),.ffi_closure_SYSV_end-CNAME(ffi_closure_SYSV)
+
+
+ENTRY(ffi_metag_trampoline)
+       SAVE_ARGS
+       ! New frame
+       mov A0FrP, A0StP
+       SAVE_REGS "D1.5, D0.5"
+       mov D0.5, PC
+       ! Load D1Ar1 the value of ffi_metag_trampoline
+       getd D1Ar1, [D0.5 + #8]
+       ! Jump to ffi_closure_SYSV
+       getd PC, [D0.5 + #12]