]> git.ipfire.org Git - thirdparty/gcc.git/blobdiff - libffi/src/powerpc/ffi.c
ffitarget.h: Import from upstream.
[thirdparty/gcc.git] / libffi / src / powerpc / ffi.c
index feb21447b64f5492caef02374ab0da6037e66622..efb441bbfc03fd036735bff340e27441cd742aa0 100644 (file)
@@ -1,5 +1,6 @@
 /* -----------------------------------------------------------------------
-   ffi.c - Copyright (C) 2011 Anthony Green
+   ffi.c - Copyright (C) 2013 IBM
+           Copyright (C) 2011 Anthony Green
            Copyright (C) 2011 Kyle Moffett
            Copyright (C) 2008 Red Hat, Inc
            Copyright (C) 2007, 2008 Free Software Foundation, Inc
    OTHER DEALINGS IN THE SOFTWARE.
    ----------------------------------------------------------------------- */
 
-#include <ffi.h>
-#include <ffi_common.h>
-
-#include <stdlib.h>
-#include <stdio.h>
-
-
-extern void ffi_closure_SYSV (void);
-extern void FFI_HIDDEN ffi_closure_LINUX64 (void);
-
-enum {
-  /* The assembly depends on these exact flags.  */
-  FLAG_RETURNS_SMST    = 1 << (31-31), /* Used for FFI_SYSV small structs.  */
-  FLAG_RETURNS_NOTHING  = 1 << (31-30), /* These go in cr7 */
-#ifndef __NO_FPRS__
-  FLAG_RETURNS_FP       = 1 << (31-29),
-#endif
-  FLAG_RETURNS_64BITS   = 1 << (31-28),
-
-  FLAG_RETURNS_128BITS  = 1 << (31-27), /* cr6  */
-
-  FLAG_ARG_NEEDS_COPY   = 1 << (31- 7),
-  FLAG_ARG_NEEDS_PSAVE  = FLAG_ARG_NEEDS_COPY, /* Used by ELFv2 */
-#ifndef __NO_FPRS__
-  FLAG_FP_ARGUMENTS     = 1 << (31- 6), /* cr1.eq; specified by ABI */
-#endif
-  FLAG_4_GPR_ARGUMENTS  = 1 << (31- 5),
-  FLAG_RETVAL_REFERENCE = 1 << (31- 4)
-};
-
-/* About the SYSV ABI.  */
-#define ASM_NEEDS_REGISTERS 4
-#define NUM_GPR_ARG_REGISTERS 8
-#ifndef __NO_FPRS__
-# define NUM_FPR_ARG_REGISTERS 8
-#endif
-
-/* ffi_prep_args_SYSV is called by the assembly routine once stack space
-   has been allocated for the function's arguments.
-
-   The stack layout we want looks like this:
-
-   |   Return address from ffi_call_SYSV 4bytes        |       higher addresses
-   |--------------------------------------------|
-   |   Previous backchain pointer      4       |       stack pointer here
-   |--------------------------------------------|<+ <<<        on entry to
-   |   Saved r28-r31                   4*4     | |     ffi_call_SYSV
-   |--------------------------------------------| |
-   |   GPR registers r3-r10            8*4     | |     ffi_call_SYSV
-   |--------------------------------------------| |
-   |   FPR registers f1-f8 (optional)  8*8     | |
-   |--------------------------------------------| |    stack   |
-   |   Space for copied structures             | |     grows   |
-   |--------------------------------------------| |    down    V
-   |   Parameters that didn't fit in registers  | |
-   |--------------------------------------------| |    lower addresses
-   |   Space for callee's LR           4       | |
-   |--------------------------------------------| |    stack pointer here
-   |   Current backchain pointer       4       |-/     during
-   |--------------------------------------------|   <<<        ffi_call_SYSV
-
-*/
-
-void
-ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const stack)
-{
-  const unsigned bytes = ecif->cif->bytes;
-  const unsigned flags = ecif->cif->flags;
-
-  typedef union {
-    char *c;
-    unsigned *u;
-    long long *ll;
-    float *f;
-    double *d;
-  } valp;
-
-  /* 'stacktop' points at the previous backchain pointer.  */
-  valp stacktop;
-
-  /* 'gpr_base' points at the space for gpr3, and grows upwards as
-     we use GPR registers.  */
-  valp gpr_base;
-  int intarg_count;
-
-#ifndef __NO_FPRS__
-  /* 'fpr_base' points at the space for fpr1, and grows upwards as
-     we use FPR registers.  */
-  valp fpr_base;
-  int fparg_count;
-#endif
-
-  /* 'copy_space' grows down as we put structures in it.  It should
-     stay 16-byte aligned.  */
-  valp copy_space;
-
-  /* 'next_arg' grows up as we put parameters in it.  */
-  valp next_arg;
-
-  int i;
-  ffi_type **ptr;
-#ifndef __NO_FPRS__
-  double double_tmp;
-#endif
-  union {
-    void **v;
-    char **c;
-    signed char **sc;
-    unsigned char **uc;
-    signed short **ss;
-    unsigned short **us;
-    unsigned int **ui;
-    long long **ll;
-    float **f;
-    double **d;
-  } p_argv;
-  size_t struct_copy_size;
-  unsigned gprvalue;
-
-  stacktop.c = (char *) stack + bytes;
-  gpr_base.u = stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS;
-  intarg_count = 0;
-#ifndef __NO_FPRS__
-  fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS;
-  fparg_count = 0;
-  copy_space.c = ((flags & FLAG_FP_ARGUMENTS) ? fpr_base.c : gpr_base.c);
-#else
-  copy_space.c = gpr_base.c;
-#endif
-  next_arg.u = stack + 2;
-
-  /* Check that everything starts aligned properly.  */
-  FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0);
-  FFI_ASSERT (((unsigned long) copy_space.c & 0xF) == 0);
-  FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0);
-  FFI_ASSERT ((bytes & 0xF) == 0);
-  FFI_ASSERT (copy_space.c >= next_arg.c);
-
-  /* Deal with return values that are actually pass-by-reference.  */
-  if (flags & FLAG_RETVAL_REFERENCE)
-    {
-      *gpr_base.u++ = (unsigned long) (char *) ecif->rvalue;
-      intarg_count++;
-    }
-
-  /* Now for the arguments.  */
-  p_argv.v = ecif->avalue;
-  for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs;
-       i > 0;
-       i--, ptr++, p_argv.v++)
-    {
-      unsigned short typenum = (*ptr)->type;
-
-      /* We may need to handle some values depending on ABI */
-      if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT) {
-               if (typenum == FFI_TYPE_FLOAT)
-                       typenum = FFI_TYPE_UINT32;
-               if (typenum == FFI_TYPE_DOUBLE)
-                       typenum = FFI_TYPE_UINT64;
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_UINT128;
-      } else if (ecif->cif->abi != FFI_LINUX) {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_STRUCT;
-#endif
-      }
-
-      /* Now test the translated value */
-      switch (typenum) {
-#ifndef __NO_FPRS__
-       case FFI_TYPE_FLOAT:
-         /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */
-         double_tmp = **p_argv.f;
-         if (fparg_count >= NUM_FPR_ARG_REGISTERS)
-           {
-             *next_arg.f = (float) double_tmp;
-             next_arg.u += 1;
-             intarg_count++;
-           }
-         else
-           *fpr_base.d++ = double_tmp;
-         fparg_count++;
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-
-       case FFI_TYPE_DOUBLE:
-         /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */
-         double_tmp = **p_argv.d;
-
-         if (fparg_count >= NUM_FPR_ARG_REGISTERS)
-           {
-             if (intarg_count >= NUM_GPR_ARG_REGISTERS
-                 && intarg_count % 2 != 0)
-               {
-                 intarg_count++;
-                 next_arg.u++;
-               }
-             *next_arg.d = double_tmp;
-             next_arg.u += 2;
-           }
-         else
-           *fpr_base.d++ = double_tmp;
-         fparg_count++;
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-       case FFI_TYPE_LONGDOUBLE:
-             double_tmp = (*p_argv.d)[0];
-
-             if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)
-               {
-                 if (intarg_count >= NUM_GPR_ARG_REGISTERS
-                     && intarg_count % 2 != 0)
-                   {
-                     intarg_count++;
-                     next_arg.u++;
-                   }
-                 *next_arg.d = double_tmp;
-                 next_arg.u += 2;
-                 double_tmp = (*p_argv.d)[1];
-                 *next_arg.d = double_tmp;
-                 next_arg.u += 2;
-               }
-             else
-               {
-                 *fpr_base.d++ = double_tmp;
-                 double_tmp = (*p_argv.d)[1];
-                 *fpr_base.d++ = double_tmp;
-               }
-
-             fparg_count += 2;
-             FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-#endif
-#endif /* have FPRs */
-
-       /*
-        * The soft float ABI for long doubles works like this, a long double
-        * is passed in four consecutive GPRs if available.  A maximum of 2
-        * long doubles can be passed in gprs.  If we do not have 4 GPRs
-        * left, the long double is passed on the stack, 4-byte aligned.
-        */
-       case FFI_TYPE_UINT128: {
-               unsigned int int_tmp = (*p_argv.ui)[0];
-               unsigned int ii;
-               if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3) {
-                       if (intarg_count < NUM_GPR_ARG_REGISTERS)
-                               intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;
-                       *(next_arg.u++) = int_tmp;
-                       for (ii = 1; ii < 4; ii++) {
-                               int_tmp = (*p_argv.ui)[ii];
-                               *(next_arg.u++) = int_tmp;
-                       }
-               } else {
-                       *(gpr_base.u++) = int_tmp;
-                       for (ii = 1; ii < 4; ii++) {
-                               int_tmp = (*p_argv.ui)[ii];
-                               *(gpr_base.u++) = int_tmp;
-                       }
-               }
-               intarg_count += 4;
-               break;
-       }
-
-       case FFI_TYPE_UINT64:
-       case FFI_TYPE_SINT64:
-         if (intarg_count == NUM_GPR_ARG_REGISTERS-1)
-           intarg_count++;
-         if (intarg_count >= NUM_GPR_ARG_REGISTERS)
-           {
-             if (intarg_count % 2 != 0)
-               {
-                 intarg_count++;
-                 next_arg.u++;
-               }
-             *next_arg.ll = **p_argv.ll;
-             next_arg.u += 2;
-           }
-         else
-           {
-             /* whoops: abi states only certain register pairs
-              * can be used for passing long long int
-              * specifically (r3,r4), (r5,r6), (r7,r8),
-              * (r9,r10) and if next arg is long long but
-              * not correct starting register of pair then skip
-              * until the proper starting register
-              */
-             if (intarg_count % 2 != 0)
-               {
-                 intarg_count ++;
-                 gpr_base.u++;
-               }
-             *gpr_base.ll++ = **p_argv.ll;
-           }
-         intarg_count += 2;
-         break;
-
-       case FFI_TYPE_STRUCT:
-         struct_copy_size = ((*ptr)->size + 15) & ~0xF;
-         copy_space.c -= struct_copy_size;
-         memcpy (copy_space.c, *p_argv.c, (*ptr)->size);
-
-         gprvalue = (unsigned long) copy_space.c;
-
-         FFI_ASSERT (copy_space.c > next_arg.c);
-         FFI_ASSERT (flags & FLAG_ARG_NEEDS_COPY);
-         goto putgpr;
-
-       case FFI_TYPE_UINT8:
-         gprvalue = **p_argv.uc;
-         goto putgpr;
-       case FFI_TYPE_SINT8:
-         gprvalue = **p_argv.sc;
-         goto putgpr;
-       case FFI_TYPE_UINT16:
-         gprvalue = **p_argv.us;
-         goto putgpr;
-       case FFI_TYPE_SINT16:
-         gprvalue = **p_argv.ss;
-         goto putgpr;
-
-       case FFI_TYPE_INT:
-       case FFI_TYPE_UINT32:
-       case FFI_TYPE_SINT32:
-       case FFI_TYPE_POINTER:
-
-         gprvalue = **p_argv.ui;
-
-       putgpr:
-         if (intarg_count >= NUM_GPR_ARG_REGISTERS)
-           *next_arg.u++ = gprvalue;
-         else
-           *gpr_base.u++ = gprvalue;
-         intarg_count++;
-         break;
-       }
-    }
-
-  /* Check that we didn't overrun the stack...  */
-  FFI_ASSERT (copy_space.c >= next_arg.c);
-  FFI_ASSERT (gpr_base.u <= stacktop.u - ASM_NEEDS_REGISTERS);
-  /* The assert below is testing that the number of integer arguments agrees
-     with the number found in ffi_prep_cif_machdep().  However, intarg_count
-     is incremented whenever we place an FP arg on the stack, so account for
-     that before our assert test.  */
-#ifndef __NO_FPRS__
-  if (fparg_count > NUM_FPR_ARG_REGISTERS)
-    intarg_count -= fparg_count - NUM_FPR_ARG_REGISTERS;
-  FFI_ASSERT (fpr_base.u
-             <= stacktop.u - ASM_NEEDS_REGISTERS - NUM_GPR_ARG_REGISTERS);
-#endif
-  FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS || intarg_count <= 4);
-}
-
-/* About the LINUX64 ABI.  */
-enum {
-  NUM_GPR_ARG_REGISTERS64 = 8,
-  NUM_FPR_ARG_REGISTERS64 = 13
-};
-enum { ASM_NEEDS_REGISTERS64 = 4 };
-
-#if _CALL_ELF == 2
-static unsigned int
-discover_homogeneous_aggregate (const ffi_type *t, unsigned int *elnum)
-{
-  switch (t->type)
-    {
-    case FFI_TYPE_FLOAT:
-    case FFI_TYPE_DOUBLE:
-      *elnum = 1;
-      return (int) t->type;
-
-    case FFI_TYPE_STRUCT:;
-      {
-       unsigned int base_elt = 0, total_elnum = 0;
-       ffi_type **el = t->elements;
-       while (*el)
-         {
-           unsigned int el_elt, el_elnum = 0;
-           el_elt = discover_homogeneous_aggregate (*el, &el_elnum);
-           if (el_elt == 0
-               || (base_elt && base_elt != el_elt))
-             return 0;
-           base_elt = el_elt;
-           total_elnum += el_elnum;
-           if (total_elnum > 8)
-             return 0;
-           el++;
-         }
-       *elnum = total_elnum;
-       return base_elt;
-      }
-
-    default:
-      return 0;
-    }
-}
-#endif
-
-
-/* ffi_prep_args64 is called by the assembly routine once stack space
-   has been allocated for the function's arguments.
-
-   The stack layout we want looks like this:
-
-   |   Ret addr from ffi_call_LINUX64  8bytes  |       higher addresses
-   |--------------------------------------------|
-   |   CR save area                    8bytes  |
-   |--------------------------------------------|
-   |   Previous backchain pointer      8       |       stack pointer here
-   |--------------------------------------------|<+ <<<        on entry to
-   |   Saved r28-r31                   4*8     | |     ffi_call_LINUX64
-   |--------------------------------------------| |
-   |   GPR registers r3-r10            8*8     | |
-   |--------------------------------------------| |
-   |   FPR registers f1-f13 (optional) 13*8    | |
-   |--------------------------------------------| |
-   |   Parameter save area                     | |
-   |--------------------------------------------| |
-   |   TOC save area                   8       | |
-   |--------------------------------------------| |    stack   |
-   |   Linker doubleword               8       | |     grows   |
-   |--------------------------------------------| |    down    V
-   |   Compiler doubleword             8       | |
-   |--------------------------------------------| |    lower addresses
-   |   Space for callee's LR           8       | |
-   |--------------------------------------------| |
-   |   CR save area                    8       | |
-   |--------------------------------------------| |    stack pointer here
-   |   Current backchain pointer       8       |-/     during
-   |--------------------------------------------|   <<<        ffi_call_LINUX64
-
-*/
+#include "ffi.h"
+#include "ffi_common.h"
+#include "ffi_powerpc.h"
 
+#if HAVE_LONG_DOUBLE_VARIANT
+/* Adjust ffi_type_longdouble.  */
 void FFI_HIDDEN
-ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
+ffi_prep_types (ffi_abi abi)
 {
-  const unsigned long bytes = ecif->cif->bytes;
-  const unsigned long flags = ecif->cif->flags;
-
-  typedef union {
-    char *c;
-    unsigned long *ul;
-    float *f;
-    double *d;
-    size_t p;
-  } valp;
-
-  /* 'stacktop' points at the previous backchain pointer.  */
-  valp stacktop;
-
-  /* 'next_arg' points at the space for gpr3, and grows upwards as
-     we use GPR registers, then continues at rest.  */
-  valp gpr_base;
-  valp gpr_end;
-  valp rest;
-  valp next_arg;
-
-  /* 'fpr_base' points at the space for fpr3, and grows upwards as
-     we use FPR registers.  */
-  valp fpr_base;
-  unsigned int fparg_count;
-
-  unsigned int i, words, nargs, nfixedargs;
-  ffi_type **ptr;
-  double double_tmp;
-  union {
-    void **v;
-    char **c;
-    signed char **sc;
-    unsigned char **uc;
-    signed short **ss;
-    unsigned short **us;
-    signed int **si;
-    unsigned int **ui;
-    unsigned long **ul;
-    float **f;
-    double **d;
-  } p_argv;
-  unsigned long gprvalue;
-#ifdef __STRUCT_PARM_ALIGN__
-  unsigned long align;
-#endif
-
-  stacktop.c = (char *) stack + bytes;
-  gpr_base.ul = stacktop.ul - ASM_NEEDS_REGISTERS64 - NUM_GPR_ARG_REGISTERS64;
-  gpr_end.ul = gpr_base.ul + NUM_GPR_ARG_REGISTERS64;
-#if _CALL_ELF == 2
-  rest.ul = stack + 4 + NUM_GPR_ARG_REGISTERS64;
-#else
-  rest.ul = stack + 6 + NUM_GPR_ARG_REGISTERS64;
-#endif
-  fpr_base.d = gpr_base.d - NUM_FPR_ARG_REGISTERS64;
-  fparg_count = 0;
-  next_arg.ul = gpr_base.ul;
-
-  /* Check that everything starts aligned properly.  */
-  FFI_ASSERT (((unsigned long) (char *) stack & 0xF) == 0);
-  FFI_ASSERT (((unsigned long) stacktop.c & 0xF) == 0);
-  FFI_ASSERT ((bytes & 0xF) == 0);
-
-  /* Deal with return values that are actually pass-by-reference.  */
-  if (flags & FLAG_RETVAL_REFERENCE)
-    *next_arg.ul++ = (unsigned long) (char *) ecif->rvalue;
-
-  /* Now for the arguments.  */
-  p_argv.v = ecif->avalue;
-  nargs = ecif->cif->nargs;
-  nfixedargs = ecif->cif->nfixedargs;
-  for (ptr = ecif->cif->arg_types, i = 0;
-       i < nargs;
-       i++, ptr++, p_argv.v++)
-    {
-      unsigned int elt, elnum;
-
-      switch ((*ptr)->type)
-       {
-       case FFI_TYPE_FLOAT:
-         double_tmp = **p_argv.f;
-         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
-           *fpr_base.d++ = double_tmp;
-         else
-           *next_arg.f = (float) double_tmp;
-         if (++next_arg.ul == gpr_end.ul)
-           next_arg.ul = rest.ul;
-         fparg_count++;
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-
-       case FFI_TYPE_DOUBLE:
-         double_tmp = **p_argv.d;
-         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
-           *fpr_base.d++ = double_tmp;
-         else
-           *next_arg.d = double_tmp;
-         if (++next_arg.ul == gpr_end.ul)
-           next_arg.ul = rest.ul;
-         fparg_count++;
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-       case FFI_TYPE_LONGDOUBLE:
-         double_tmp = (*p_argv.d)[0];
-         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
-           *fpr_base.d++ = double_tmp;
-         else
-           *next_arg.d = double_tmp;
-         if (++next_arg.ul == gpr_end.ul)
-           next_arg.ul = rest.ul;
-         fparg_count++;
-         double_tmp = (*p_argv.d)[1];
-         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
-           *fpr_base.d++ = double_tmp;
-         else
-           *next_arg.d = double_tmp;
-         if (++next_arg.ul == gpr_end.ul)
-           next_arg.ul = rest.ul;
-         fparg_count++;
-         FFI_ASSERT (__LDBL_MANT_DIG__ == 106);
-         FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
-         break;
-#endif
-
-       case FFI_TYPE_STRUCT:
-#ifdef __STRUCT_PARM_ALIGN__
-         align = (*ptr)->alignment;
-         if (align > __STRUCT_PARM_ALIGN__)
-           align = __STRUCT_PARM_ALIGN__;
-         if (align > 1)
-           next_arg.p = ALIGN (next_arg.p, align);
-#endif
-         elt = 0;
-#if _CALL_ELF == 2
-         elt = discover_homogeneous_aggregate (*ptr, &elnum);
-#endif
-         if (elt)
-           {
-             union {
-               void *v;
-               float *f;
-               double *d;
-             } arg;
-
-             arg.v = *p_argv.v;
-             if (elt == FFI_TYPE_FLOAT)
-               {
-                 do
-                   {
-                     double_tmp = *arg.f++;
-                     if (fparg_count < NUM_FPR_ARG_REGISTERS64
-                         && i < nfixedargs)
-                       *fpr_base.d++ = double_tmp;
-                     else
-                       *next_arg.f = (float) double_tmp;
-                     if (++next_arg.f == gpr_end.f)
-                       next_arg.f = rest.f;
-                     fparg_count++;
-                   }
-                 while (--elnum != 0);
-                 if ((next_arg.p & 3) != 0)
-                   {
-                     if (++next_arg.f == gpr_end.f)
-                       next_arg.f = rest.f;
-                   }
-               }
-             else
-               do
-                 {
-                   double_tmp = *arg.d++;
-                   if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
-                     *fpr_base.d++ = double_tmp;
-                   else
-                     *next_arg.d = double_tmp;
-                   if (++next_arg.d == gpr_end.d)
-                     next_arg.d = rest.d;
-                   fparg_count++;
-                 }
-               while (--elnum != 0);
-           }
-         else
-           {
-             words = ((*ptr)->size + 7) / 8;
-             if (next_arg.ul >= gpr_base.ul && next_arg.ul + words > gpr_end.ul)
-               {
-                 size_t first = gpr_end.c - next_arg.c;
-                 memcpy (next_arg.c, *p_argv.c, first);
-                 memcpy (rest.c, *p_argv.c + first, (*ptr)->size - first);
-                 next_arg.c = rest.c + words * 8 - first;
-               }
-             else
-               {
-                 char *where = next_arg.c;
-
-#ifndef __LITTLE_ENDIAN__
-                 /* Structures with size less than eight bytes are passed
-                    left-padded.  */
-                 if ((*ptr)->size < 8)
-                   where += 8 - (*ptr)->size;
-#endif
-                 memcpy (where, *p_argv.c, (*ptr)->size);
-                 next_arg.ul += words;
-                 if (next_arg.ul == gpr_end.ul)
-                   next_arg.ul = rest.ul;
-               }
-           }
-         break;
-
-       case FFI_TYPE_UINT8:
-         gprvalue = **p_argv.uc;
-         goto putgpr;
-       case FFI_TYPE_SINT8:
-         gprvalue = **p_argv.sc;
-         goto putgpr;
-       case FFI_TYPE_UINT16:
-         gprvalue = **p_argv.us;
-         goto putgpr;
-       case FFI_TYPE_SINT16:
-         gprvalue = **p_argv.ss;
-         goto putgpr;
-       case FFI_TYPE_UINT32:
-         gprvalue = **p_argv.ui;
-         goto putgpr;
-       case FFI_TYPE_INT:
-       case FFI_TYPE_SINT32:
-         gprvalue = **p_argv.si;
-         goto putgpr;
-
-       case FFI_TYPE_UINT64:
-       case FFI_TYPE_SINT64:
-       case FFI_TYPE_POINTER:
-         gprvalue = **p_argv.ul;
-       putgpr:
-         *next_arg.ul++ = gprvalue;
-         if (next_arg.ul == gpr_end.ul)
-           next_arg.ul = rest.ul;
-         break;
-       }
-    }
-
-  FFI_ASSERT (flags & FLAG_4_GPR_ARGUMENTS
-             || (next_arg.ul >= gpr_base.ul
-                 && next_arg.ul <= gpr_base.ul + 4));
+# if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+#  ifdef POWERPC64
+  ffi_prep_types_linux64 (abi);
+#  else
+  ffi_prep_types_sysv (abi);
+#  endif
+# endif
 }
-
-
+#endif
 
 /* Perform machine dependent cif processing */
-static ffi_status
-ffi_prep_cif_machdep_core (ffi_cif *cif)
+ffi_status FFI_HIDDEN
+ffi_prep_cif_machdep (ffi_cif *cif)
 {
-  /* All this is for the SYSV and LINUX64 ABI.  */
-  ffi_type **ptr;
-  unsigned bytes;
-  unsigned i, fparg_count = 0, intarg_count = 0;
-  unsigned flags = cif->flags;
-  unsigned struct_copy_size = 0;
-  unsigned type = cif->rtype->type;
-  unsigned size = cif->rtype->size;
-
-  /* The machine-independent calculation of cif->bytes doesn't work
-     for us.  Redo the calculation.  */
-  if (cif->abi != FFI_LINUX64)
-    {
-      /* Space for the frame pointer, callee's LR, and the asm's temp regs.  */
-      bytes = (2 + ASM_NEEDS_REGISTERS) * sizeof (int);
-
-      /* Space for the GPR registers.  */
-      bytes += NUM_GPR_ARG_REGISTERS * sizeof (int);
-    }
-  else
-    {
-      /* 64-bit ABI.  */
-#if _CALL_ELF == 2
-      /* Space for backchain, CR, LR, TOC and the asm's temp regs.  */
-      bytes = (4 + ASM_NEEDS_REGISTERS64) * sizeof (long);
-
-      /* Space for the general registers.  */
-      bytes += NUM_GPR_ARG_REGISTERS64 * sizeof (long);
-#else
-      /* Space for backchain, CR, LR, cc/ld doubleword, TOC and the asm's temp
-        regs.  */
-      bytes = (6 + ASM_NEEDS_REGISTERS64) * sizeof (long);
-
-      /* Space for the mandatory parm save area and general registers.  */
-      bytes += 2 * NUM_GPR_ARG_REGISTERS64 * sizeof (long);
-#endif
-    }
-
-  /* Return value handling.  The rules for SYSV are as follows:
-     - 32-bit (or less) integer values are returned in gpr3;
-     - Structures of size <= 4 bytes also returned in gpr3;
-     - 64-bit integer values and structures between 5 and 8 bytes are returned
-     in gpr3 and gpr4;
-     - Single/double FP values are returned in fpr1;
-     - Larger structures are allocated space and a pointer is passed as
-     the first argument.
-     - long doubles (if not equivalent to double) are returned in
-     fpr1,fpr2 for Linux and as for large structs for SysV.
-     For LINUX64:
-     - integer values in gpr3;
-     - Structures/Unions by reference;
-     - Single/double FP values in fpr1, long double in fpr1,fpr2.
-     - soft-float float/doubles are treated as UINT32/UINT64 respectivley.
-     - soft-float long doubles are returned in gpr3-gpr6.  */
-  /* First translate for softfloat/nonlinux */
-  if (cif->abi == FFI_LINUX_SOFT_FLOAT)
-    {
-      if (type == FFI_TYPE_FLOAT)
-       type = FFI_TYPE_UINT32;
-      if (type == FFI_TYPE_DOUBLE)
-       type = FFI_TYPE_UINT64;
-      if (type == FFI_TYPE_LONGDOUBLE)
-       type = FFI_TYPE_UINT128;
-    }
-  else if (cif->abi != FFI_LINUX
-          && cif->abi != FFI_LINUX64)
-    {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-      if (type == FFI_TYPE_LONGDOUBLE)
-       type = FFI_TYPE_STRUCT;
-#endif
-    }
-
-  switch (type)
-    {
-#ifndef __NO_FPRS__
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-    case FFI_TYPE_LONGDOUBLE:
-      flags |= FLAG_RETURNS_128BITS;
-      /* Fall through.  */
-#endif
-    case FFI_TYPE_DOUBLE:
-      flags |= FLAG_RETURNS_64BITS;
-      /* Fall through.  */
-    case FFI_TYPE_FLOAT:
-      flags |= FLAG_RETURNS_FP;
-      break;
-#endif
-
-    case FFI_TYPE_UINT128:
-      flags |= FLAG_RETURNS_128BITS;
-      /* Fall through.  */
-    case FFI_TYPE_UINT64:
-    case FFI_TYPE_SINT64:
-      flags |= FLAG_RETURNS_64BITS;
-      break;
-
-    case FFI_TYPE_STRUCT:
-      /*
-       * The final SYSV ABI says that structures smaller or equal 8 bytes
-       * are returned in r3/r4.  The FFI_GCC_SYSV ABI instead returns them
-       * in memory.
-       *
-       * NOTE: The assembly code can safely assume that it just needs to
-       *       store both r3 and r4 into a 8-byte word-aligned buffer, as
-       *       we allocate a temporary buffer in ffi_call() if this flag is
-       *       set.
-       */
-      if (cif->abi == FFI_SYSV && size <= 8)
-       {
-         flags |= FLAG_RETURNS_SMST;
-         break;
-       }
-#if _CALL_ELF == 2
-      if (cif->abi == FFI_LINUX64)
-       {
-         unsigned int elt, elnum;
-         elt = discover_homogeneous_aggregate (cif->rtype, &elnum);
-         if (elt)
-           {
-             if (elt == FFI_TYPE_DOUBLE)
-               flags |= FLAG_RETURNS_64BITS;
-             flags |= FLAG_RETURNS_FP | FLAG_RETURNS_SMST;
-             break;
-           }
-         if (size <= 16)
-           {
-             flags |= FLAG_RETURNS_SMST;
-             break;
-           }
-       }
-#endif
-      intarg_count++;
-      flags |= FLAG_RETVAL_REFERENCE;
-      /* Fall through.  */
-    case FFI_TYPE_VOID:
-      flags |= FLAG_RETURNS_NOTHING;
-      break;
-
-    default:
-      /* Returns 32-bit integer, or similar.  Nothing to do here.  */
-      break;
-    }
-
-  if (cif->abi != FFI_LINUX64)
-    /* The first NUM_GPR_ARG_REGISTERS words of integer arguments, and the
-       first NUM_FPR_ARG_REGISTERS fp arguments, go in registers; the rest
-       goes on the stack.  Structures and long doubles (if not equivalent
-       to double) are passed as a pointer to a copy of the structure.
-       Stuff on the stack needs to keep proper alignment.  */
-    for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
-      {
-       unsigned short typenum = (*ptr)->type;
-
-       /* We may need to handle some values depending on ABI */
-       if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
-               if (typenum == FFI_TYPE_FLOAT)
-                       typenum = FFI_TYPE_UINT32;
-               if (typenum == FFI_TYPE_DOUBLE)
-                       typenum = FFI_TYPE_UINT64;
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_UINT128;
-       } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_STRUCT;
-#endif
-       }
-
-       switch (typenum) {
-#ifndef __NO_FPRS__
-         case FFI_TYPE_FLOAT:
-           fparg_count++;
-           /* floating singles are not 8-aligned on stack */
-           break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-         case FFI_TYPE_LONGDOUBLE:
-           fparg_count++;
-           /* Fall thru */
-#endif
-         case FFI_TYPE_DOUBLE:
-           fparg_count++;
-           /* If this FP arg is going on the stack, it must be
-              8-byte-aligned.  */
-           if (fparg_count > NUM_FPR_ARG_REGISTERS
-               && intarg_count >= NUM_GPR_ARG_REGISTERS
-               && intarg_count % 2 != 0)
-             intarg_count++;
-           break;
-#endif
-         case FFI_TYPE_UINT128:
-               /*
-                * A long double in FFI_LINUX_SOFT_FLOAT can use only a set
-                * of four consecutive gprs. If we do not have enough, we
-                * have to adjust the intarg_count value.
-                */
-               if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3
-                               && intarg_count < NUM_GPR_ARG_REGISTERS)
-                       intarg_count = NUM_GPR_ARG_REGISTERS;
-               intarg_count += 4;
-               break;
-
-         case FFI_TYPE_UINT64:
-         case FFI_TYPE_SINT64:
-           /* 'long long' arguments are passed as two words, but
-              either both words must fit in registers or both go
-              on the stack.  If they go on the stack, they must
-              be 8-byte-aligned.
-
-              Also, only certain register pairs can be used for
-              passing long long int -- specifically (r3,r4), (r5,r6),
-              (r7,r8), (r9,r10).
-           */
-           if (intarg_count == NUM_GPR_ARG_REGISTERS-1
-               || intarg_count % 2 != 0)
-             intarg_count++;
-           intarg_count += 2;
-           break;
-
-         case FFI_TYPE_STRUCT:
-           /* We must allocate space for a copy of these to enforce
-              pass-by-value.  Pad the space up to a multiple of 16
-              bytes (the maximum alignment required for anything under
-              the SYSV ABI).  */
-           struct_copy_size += ((*ptr)->size + 15) & ~0xF;
-           /* Fall through (allocate space for the pointer).  */
-
-         case FFI_TYPE_POINTER:
-         case FFI_TYPE_INT:
-         case FFI_TYPE_UINT32:
-         case FFI_TYPE_SINT32:
-         case FFI_TYPE_UINT16:
-         case FFI_TYPE_SINT16:
-         case FFI_TYPE_UINT8:
-         case FFI_TYPE_SINT8:
-           /* Everything else is passed as a 4-byte word in a GPR, either
-              the object itself or a pointer to it.  */
-           intarg_count++;
-           break;
-         default:
-               FFI_ASSERT (0);
-         }
-      }
-  else
-    for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
-      {
-       unsigned int elt, elnum;
-#ifdef __STRUCT_PARM_ALIGN__
-       unsigned int align;
-#endif
-
-       switch ((*ptr)->type)
-         {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-         case FFI_TYPE_LONGDOUBLE:
-           fparg_count += 2;
-           intarg_count += 2;
-           if (fparg_count > NUM_FPR_ARG_REGISTERS)
-             flags |= FLAG_ARG_NEEDS_PSAVE;
-           break;
-#endif
-         case FFI_TYPE_FLOAT:
-         case FFI_TYPE_DOUBLE:
-           fparg_count++;
-           intarg_count++;
-           if (fparg_count > NUM_FPR_ARG_REGISTERS)
-             flags |= FLAG_ARG_NEEDS_PSAVE;
-           break;
-
-         case FFI_TYPE_STRUCT:
-#ifdef __STRUCT_PARM_ALIGN__
-           align = (*ptr)->alignment;
-           if (align > __STRUCT_PARM_ALIGN__)
-             align = __STRUCT_PARM_ALIGN__;
-           align = align / 8;
-           if (align > 1)
-             intarg_count = ALIGN (intarg_count, align);
-#endif
-           intarg_count += ((*ptr)->size + 7) / 8;
-           elt = 0;
-#if _CALL_ELF == 2
-           elt = discover_homogeneous_aggregate (*ptr, &elnum);
-#endif
-           if (elt)
-             {
-               fparg_count += elnum;
-               if (fparg_count > NUM_FPR_ARG_REGISTERS)
-                 flags |= FLAG_ARG_NEEDS_PSAVE;
-             }
-           else
-             {
-               if (intarg_count > NUM_GPR_ARG_REGISTERS)
-                 flags |= FLAG_ARG_NEEDS_PSAVE;
-             }
-           break;
-
-         case FFI_TYPE_POINTER:
-         case FFI_TYPE_UINT64:
-         case FFI_TYPE_SINT64:
-         case FFI_TYPE_INT:
-         case FFI_TYPE_UINT32:
-         case FFI_TYPE_SINT32:
-         case FFI_TYPE_UINT16:
-         case FFI_TYPE_SINT16:
-         case FFI_TYPE_UINT8:
-         case FFI_TYPE_SINT8:
-           /* Everything else is passed as a 8-byte word in a GPR, either
-              the object itself or a pointer to it.  */
-           intarg_count++;
-           if (intarg_count > NUM_GPR_ARG_REGISTERS)
-             flags |= FLAG_ARG_NEEDS_PSAVE;
-           break;
-         default:
-           FFI_ASSERT (0);
-         }
-      }
-
-#ifndef __NO_FPRS__
-  if (fparg_count != 0)
-    flags |= FLAG_FP_ARGUMENTS;
-#endif
-  if (intarg_count > 4)
-    flags |= FLAG_4_GPR_ARGUMENTS;
-  if (struct_copy_size != 0)
-    flags |= FLAG_ARG_NEEDS_COPY;
-
-  if (cif->abi != FFI_LINUX64)
-    {
-#ifndef __NO_FPRS__
-      /* Space for the FPR registers, if needed.  */
-      if (fparg_count != 0)
-       bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
-#endif
-
-      /* Stack space.  */
-      if (intarg_count > NUM_GPR_ARG_REGISTERS)
-       bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int);
-#ifndef __NO_FPRS__
-      if (fparg_count > NUM_FPR_ARG_REGISTERS)
-       bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double);
-#endif
-    }
-  else
-    {
-#ifndef __NO_FPRS__
-      /* Space for the FPR registers, if needed.  */
-      if (fparg_count != 0)
-       bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double);
-#endif
-
-      /* Stack space.  */
-#if _CALL_ELF == 2
-      if ((flags & FLAG_ARG_NEEDS_PSAVE) != 0)
-       bytes += intarg_count * sizeof (long);
+#ifdef POWERPC64
+  return ffi_prep_cif_linux64 (cif);
 #else
-      if (intarg_count > NUM_GPR_ARG_REGISTERS64)
-       bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof (long);
+  return ffi_prep_cif_sysv (cif);
 #endif
-    }
-
-  /* The stack space allocated needs to be a multiple of 16 bytes.  */
-  bytes = (bytes + 15) & ~0xF;
-
-  /* Add in the space for the copied structures.  */
-  bytes += struct_copy_size;
-
-  cif->flags = flags;
-  cif->bytes = bytes;
-
-  return FFI_OK;
-}
-
-ffi_status
-ffi_prep_cif_machdep (ffi_cif *cif)
-{
-  cif->nfixedargs = cif->nargs;
-  return ffi_prep_cif_machdep_core (cif);
 }
 
-ffi_status
+ffi_status FFI_HIDDEN
 ffi_prep_cif_machdep_var (ffi_cif *cif,
-                         unsigned int nfixedargs,
+                         unsigned int nfixedargs MAYBE_UNUSED,
                          unsigned int ntotalargs MAYBE_UNUSED)
 {
-  cif->nfixedargs = nfixedargs;
-#if _CALL_ELF == 2
-  if (cif->abi == FFI_LINUX64)
-    cif->flags |= FLAG_ARG_NEEDS_PSAVE;
+#ifdef POWERPC64
+  return ffi_prep_cif_linux64_var (cif, nfixedargs, ntotalargs);
+#else
+  return ffi_prep_cif_sysv (cif);
 #endif
-  return ffi_prep_cif_machdep_core (cif);
 }
 
-extern void ffi_call_SYSV(extended_cif *, unsigned, unsigned, unsigned *,
-                         void (*fn)(void));
-extern void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long,
-                                       unsigned long, unsigned long *,
-                                       void (*fn)(void));
-
 void
 ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 {
-  /*
-   * The final SYSV ABI says that structures smaller or equal 8 bytes
-   * are returned in r3/r4.  The FFI_GCC_SYSV ABI instead returns them
-   * in memory.
-   *
-   * We bounce-buffer SYSV small struct return values so that sysv.S
-   * can write r3 and r4 to memory without worrying about struct size.
-   *
-   * For ELFv2 ABI, use a bounce buffer for homogeneous structs too,
-   * for similar reasons.
-   */
+  /* The final SYSV ABI says that structures smaller or equal 8 bytes
+     are returned in r3/r4.  A draft ABI used by linux instead returns
+     them in memory.
+
+     We bounce-buffer SYSV small struct return values so that sysv.S
+     can write r3 and r4 to memory without worrying about struct size.
+   
+     For ELFv2 ABI, use a bounce buffer for homogeneous structs too,
+     for similar reasons.  */
   unsigned long smst_buffer[8];
   extended_cif ecif;
 
@@ -1147,26 +96,11 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
   else if (!rvalue && cif->rtype->type == FFI_TYPE_STRUCT)
     ecif.rvalue = alloca (cif->rtype->size);
 
-  switch (cif->abi)
-    {
-#ifndef POWERPC64
-# ifndef __NO_FPRS__
-    case FFI_SYSV:
-    case FFI_GCC_SYSV:
-    case FFI_LINUX:
-# endif
-    case FFI_LINUX_SOFT_FLOAT:
-      ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
-      break;
+#ifdef POWERPC64
+  ffi_call_LINUX64 (&ecif, -(long) cif->bytes, cif->flags, ecif.rvalue, fn);
 #else
-    case FFI_LINUX64:
-      ffi_call_LINUX64 (&ecif, -(long) cif->bytes, cif->flags, ecif.rvalue, fn);
-      break;
+  ffi_call_SYSV (&ecif, -cif->bytes, cif->flags, ecif.rvalue, fn);
 #endif
-    default:
-      FFI_ASSERT (0);
-      break;
-    }
 
   /* Check for a bounce-buffered return value */
   if (rvalue && ecif.rvalue == smst_buffer)
@@ -1175,36 +109,23 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue)
 #ifndef __LITTLE_ENDIAN__
       /* The SYSV ABI returns a structure of up to 4 bytes in size
         left-padded in r3.  */
-      if (cif->abi == FFI_SYSV && rsize <= 4)
+# ifndef POWERPC64
+      if (rsize <= 4)
        memcpy (rvalue, (char *) smst_buffer + 4 - rsize, rsize);
-      /* The SYSV ABI returns a structure of up to 8 bytes in size
-        left-padded in r3/r4, and the ELFv2 ABI similarly returns a
-        structure of up to 8 bytes in size left-padded in r3.  */
-      else if (rsize <= 8)
-       memcpy (rvalue, (char *) smst_buffer + 8 - rsize, rsize);
       else
+# endif
+       /* The SYSV ABI returns a structure of up to 8 bytes in size
+          left-padded in r3/r4, and the ELFv2 ABI similarly returns a
+          structure of up to 8 bytes in size left-padded in r3.  */
+       if (rsize <= 8)
+         memcpy (rvalue, (char *) smst_buffer + 8 - rsize, rsize);
+       else
 #endif
-       memcpy (rvalue, smst_buffer, rsize);
+         memcpy (rvalue, smst_buffer, rsize);
     }
 }
 
 
-#if !defined POWERPC64 || _CALL_ELF == 2
-#define MIN_CACHE_LINE_SIZE 8
-
-static void
-flush_icache (char *wraddr, char *xaddr, int size)
-{
-  int i;
-  for (i = 0; i < size; i += MIN_CACHE_LINE_SIZE)
-    __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;"
-                     : : "r" (xaddr + i), "r" (wraddr + i) : "memory");
-  __asm__ volatile ("icbi 0,%0;" "dcbf 0,%1;" "sync;" "isync;"
-                   : : "r"(xaddr + size - 1), "r"(wraddr + size - 1)
-                   : "memory");
-}
-#endif
-
 ffi_status
 ffi_prep_closure_loc (ffi_closure *closure,
                      ffi_cif *cif,
@@ -1213,593 +134,8 @@ ffi_prep_closure_loc (ffi_closure *closure,
                      void *codeloc)
 {
 #ifdef POWERPC64
-# if _CALL_ELF == 2
-  unsigned int *tramp = (unsigned int *) &closure->tramp[0];
-
-  if (cif->abi != FFI_LINUX64)
-    return FFI_BAD_ABI;
-
-  tramp[0] = 0xe96c0018;       /* 0:   ld      11,2f-0b(12)    */
-  tramp[1] = 0xe98c0010;       /*      ld      12,1f-0b(12)    */
-  tramp[2] = 0x7d8903a6;       /*      mtctr   12              */
-  tramp[3] = 0x4e800420;       /*      bctr                    */
-                               /* 1:   .quad   function_addr   */
-                               /* 2:   .quad   context         */
-  *(void **) &tramp[4] = (void *) ffi_closure_LINUX64;
-  *(void **) &tramp[6] = codeloc;
-  flush_icache ((char *)tramp, (char *)codeloc, FFI_TRAMPOLINE_SIZE);
-# else
-  void **tramp = (void **) &closure->tramp[0];
-
-  if (cif->abi != FFI_LINUX64)
-    return FFI_BAD_ABI;
-  /* Copy function address and TOC from ffi_closure_LINUX64.  */
-  memcpy (tramp, (char *) ffi_closure_LINUX64, 16);
-  tramp[2] = codeloc;
-# endif
+  return ffi_prep_closure_loc_linux64 (closure, cif, fun, user_data, codeloc);
 #else
-  unsigned int *tramp;
-
-  if (! (cif->abi == FFI_GCC_SYSV 
-        || cif->abi == FFI_SYSV
-        || cif->abi == FFI_LINUX
-        || cif->abi == FFI_LINUX_SOFT_FLOAT))
-    return FFI_BAD_ABI;
-
-  tramp = (unsigned int *) &closure->tramp[0];
-  tramp[0] = 0x7c0802a6;  /*   mflr    r0 */
-  tramp[1] = 0x4800000d;  /*   bl      10 <trampoline_initial+0x10> */
-  tramp[4] = 0x7d6802a6;  /*   mflr    r11 */
-  tramp[5] = 0x7c0803a6;  /*   mtlr    r0 */
-  tramp[6] = 0x800b0000;  /*   lwz     r0,0(r11) */
-  tramp[7] = 0x816b0004;  /*   lwz     r11,4(r11) */
-  tramp[8] = 0x7c0903a6;  /*   mtctr   r0 */
-  tramp[9] = 0x4e800420;  /*   bctr */
-  *(void **) &tramp[2] = (void *) ffi_closure_SYSV; /* function */
-  *(void **) &tramp[3] = codeloc;                   /* context */
-
-  /* Flush the icache.  */
-  flush_icache ((char *)tramp, (char *)codeloc, FFI_TRAMPOLINE_SIZE);
-#endif
-
-  closure->cif = cif;
-  closure->fun = fun;
-  closure->user_data = user_data;
-
-  return FFI_OK;
-}
-
-typedef union
-{
-  float f;
-  double d;
-} ffi_dblfl;
-
-int ffi_closure_helper_SYSV (ffi_closure *, void *, unsigned long *,
-                            ffi_dblfl *, unsigned long *);
-
-/* Basically the trampoline invokes ffi_closure_SYSV, and on
- * entry, r11 holds the address of the closure.
- * After storing the registers that could possibly contain
- * parameters to be passed into the stack frame and setting
- * up space for a return value, ffi_closure_SYSV invokes the
- * following helper function to do most of the work
- */
-
-int
-ffi_closure_helper_SYSV (ffi_closure *closure, void *rvalue,
-                        unsigned long *pgr, ffi_dblfl *pfr,
-                        unsigned long *pst)
-{
-  /* rvalue is the pointer to space for return value in closure assembly */
-  /* pgr is the pointer to where r3-r10 are stored in ffi_closure_SYSV */
-  /* pfr is the pointer to where f1-f8 are stored in ffi_closure_SYSV  */
-  /* pst is the pointer to outgoing parameter stack in original caller */
-
-  void **          avalue;
-  ffi_type **      arg_types;
-  long             i, avn;
-#ifndef __NO_FPRS__
-  long             nf = 0;   /* number of floating registers already used */
-#endif
-  long             ng = 0;   /* number of general registers already used */
-
-  ffi_cif *cif = closure->cif;
-  unsigned       size     = cif->rtype->size;
-  unsigned short rtypenum = cif->rtype->type;
-
-  avalue = alloca (cif->nargs * sizeof (void *));
-
-  /* First translate for softfloat/nonlinux */
-  if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
-       if (rtypenum == FFI_TYPE_FLOAT)
-               rtypenum = FFI_TYPE_UINT32;
-       if (rtypenum == FFI_TYPE_DOUBLE)
-               rtypenum = FFI_TYPE_UINT64;
-       if (rtypenum == FFI_TYPE_LONGDOUBLE)
-               rtypenum = FFI_TYPE_UINT128;
-  } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-       if (rtypenum == FFI_TYPE_LONGDOUBLE)
-               rtypenum = FFI_TYPE_STRUCT;
-#endif
-  }
-
-
-  /* Copy the caller's structure return value address so that the closure
-     returns the data directly to the caller.
-     For FFI_SYSV the result is passed in r3/r4 if the struct size is less
-     or equal 8 bytes.  */
-  if (rtypenum == FFI_TYPE_STRUCT && ((cif->abi != FFI_SYSV) || (size > 8))) {
-      rvalue = (void *) *pgr;
-      ng++;
-      pgr++;
-    }
-
-  i = 0;
-  avn = cif->nargs;
-  arg_types = cif->arg_types;
-
-  /* Grab the addresses of the arguments from the stack frame.  */
-  while (i < avn) {
-      unsigned short typenum = arg_types[i]->type;
-
-      /* We may need to handle some values depending on ABI */
-      if (cif->abi == FFI_LINUX_SOFT_FLOAT) {
-               if (typenum == FFI_TYPE_FLOAT)
-                       typenum = FFI_TYPE_UINT32;
-               if (typenum == FFI_TYPE_DOUBLE)
-                       typenum = FFI_TYPE_UINT64;
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_UINT128;
-      } else if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64) {
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-               if (typenum == FFI_TYPE_LONGDOUBLE)
-                       typenum = FFI_TYPE_STRUCT;
+  return ffi_prep_closure_loc_sysv (closure, cif, fun, user_data, codeloc);
 #endif
-      }
-
-      switch (typenum) {
-#ifndef __NO_FPRS__
-       case FFI_TYPE_FLOAT:
-         /* unfortunately float values are stored as doubles
-          * in the ffi_closure_SYSV code (since we don't check
-          * the type in that routine).
-          */
-
-         /* there are 8 64bit floating point registers */
-
-         if (nf < 8)
-           {
-             double temp = pfr->d;
-             pfr->f = (float) temp;
-             avalue[i] = pfr;
-             nf++;
-             pfr++;
-           }
-         else
-           {
-             /* FIXME? here we are really changing the values
-              * stored in the original calling routines outgoing
-              * parameter stack.  This is probably a really
-              * naughty thing to do but...
-              */
-             avalue[i] = pst;
-             pst += 1;
-           }
-         break;
-
-       case FFI_TYPE_DOUBLE:
-         /* On the outgoing stack all values are aligned to 8 */
-         /* there are 8 64bit floating point registers */
-
-         if (nf < 8)
-           {
-             avalue[i] = pfr;
-             nf++;
-             pfr++;
-           }
-         else
-           {
-             if (((long) pst) & 4)
-               pst++;
-             avalue[i] = pst;
-             pst += 2;
-           }
-         break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-       case FFI_TYPE_LONGDOUBLE:
-         if (nf < 7)
-           {
-             avalue[i] = pfr;
-             pfr += 2;
-             nf += 2;
-           }
-         else
-           {
-             if (((long) pst) & 4)
-               pst++;
-             avalue[i] = pst;
-             pst += 4;
-             nf = 8;
-           }
-         break;
-#endif
-#endif /* have FPRS */
-
-       case FFI_TYPE_UINT128:
-               /*
-                * Test if for the whole long double, 4 gprs are available.
-                * otherwise the stuff ends up on the stack.
-                */
-               if (ng < 5) {
-                       avalue[i] = pgr;
-                       pgr += 4;
-                       ng += 4;
-               } else {
-                       avalue[i] = pst;
-                       pst += 4;
-                       ng = 8+4;
-               }
-               break;
-
-       case FFI_TYPE_SINT8:
-       case FFI_TYPE_UINT8:
-#ifndef __LITTLE_ENDIAN__
-         /* there are 8 gpr registers used to pass values */
-         if (ng < 8)
-           {
-             avalue[i] = (char *) pgr + 3;
-             ng++;
-             pgr++;
-           }
-         else
-           {
-             avalue[i] = (char *) pst + 3;
-             pst++;
-           }
-         break;
-#endif
-
-       case FFI_TYPE_SINT16:
-       case FFI_TYPE_UINT16:
-#ifndef __LITTLE_ENDIAN__
-         /* there are 8 gpr registers used to pass values */
-         if (ng < 8)
-           {
-             avalue[i] = (char *) pgr + 2;
-             ng++;
-             pgr++;
-           }
-         else
-           {
-             avalue[i] = (char *) pst + 2;
-             pst++;
-           }
-         break;
-#endif
-
-       case FFI_TYPE_SINT32:
-       case FFI_TYPE_UINT32:
-       case FFI_TYPE_POINTER:
-         /* there are 8 gpr registers used to pass values */
-         if (ng < 8)
-           {
-             avalue[i] = pgr;
-             ng++;
-             pgr++;
-           }
-         else
-           {
-             avalue[i] = pst;
-             pst++;
-           }
-         break;
-
-       case FFI_TYPE_STRUCT:
-         /* Structs are passed by reference. The address will appear in a
-            gpr if it is one of the first 8 arguments.  */
-         if (ng < 8)
-           {
-             avalue[i] = (void *) *pgr;
-             ng++;
-             pgr++;
-           }
-         else
-           {
-             avalue[i] = (void *) *pst;
-             pst++;
-           }
-         break;
-
-       case FFI_TYPE_SINT64:
-       case FFI_TYPE_UINT64:
-         /* passing long long ints are complex, they must
-          * be passed in suitable register pairs such as
-          * (r3,r4) or (r5,r6) or (r6,r7), or (r7,r8) or (r9,r10)
-          * and if the entire pair aren't available then the outgoing
-          * parameter stack is used for both but an alignment of 8
-          * must will be kept.  So we must either look in pgr
-          * or pst to find the correct address for this type
-          * of parameter.
-          */
-         if (ng < 7)
-           {
-             if (ng & 0x01)
-               {
-                 /* skip r4, r6, r8 as starting points */
-                 ng++;
-                 pgr++;
-               }
-             avalue[i] = pgr;
-             ng += 2;
-             pgr += 2;
-           }
-         else
-           {
-             if (((long) pst) & 4)
-               pst++;
-             avalue[i] = pst;
-             pst += 2;
-             ng = 8;
-           }
-         break;
-
-       default:
-               FFI_ASSERT (0);
-       }
-
-      i++;
-    }
-
-
-  (closure->fun) (cif, rvalue, avalue, closure->user_data);
-
-  /* Tell ffi_closure_SYSV how to perform return type promotions.
-     Because the FFI_SYSV ABI returns the structures <= 8 bytes in r3/r4
-     we have to tell ffi_closure_SYSV how to treat them. We combine the base
-     type FFI_SYSV_TYPE_SMALL_STRUCT - 1  with the size of the struct.
-     So a one byte struct gets the return type 16. Return type 1 to 15 are
-     already used and we never have a struct with size zero. That is the reason
-     for the subtraction of 1. See the comment in ffitarget.h about ordering.
-  */
-  if (cif->abi == FFI_SYSV && rtypenum == FFI_TYPE_STRUCT && size <= 8)
-    return (FFI_SYSV_TYPE_SMALL_STRUCT - 1) + size;
-  return rtypenum;
-}
-
-int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
-                                          unsigned long *, ffi_dblfl *);
-
-int FFI_HIDDEN
-ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
-                           unsigned long *pst, ffi_dblfl *pfr)
-{
-  /* rvalue is the pointer to space for return value in closure assembly */
-  /* pst is the pointer to parameter save area
-     (r3-r10 are stored into its first 8 slots by ffi_closure_LINUX64) */
-  /* pfr is the pointer to where f1-f13 are stored in ffi_closure_LINUX64 */
-
-  void **avalue;
-  ffi_type **arg_types;
-  unsigned long i, avn, nfixedargs;
-  ffi_cif *cif;
-  ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64;
-#ifdef __STRUCT_PARM_ALIGN__
-  unsigned long align;
-#endif
-
-  cif = closure->cif;
-  avalue = alloca (cif->nargs * sizeof (void *));
-
-  /* Copy the caller's structure return value address so that the
-     closure returns the data directly to the caller.  */
-  if (cif->rtype->type == FFI_TYPE_STRUCT
-      && (cif->flags & FLAG_RETURNS_SMST) == 0)
-    {
-      rvalue = (void *) *pst;
-      pst++;
-    }
-
-  i = 0;
-  avn = cif->nargs;
-  nfixedargs = cif->nfixedargs;
-  arg_types = cif->arg_types;
-
-  /* Grab the addresses of the arguments from the stack frame.  */
-  while (i < avn)
-    {
-      unsigned int elt, elnum;
-
-      switch (arg_types[i]->type)
-       {
-       case FFI_TYPE_SINT8:
-       case FFI_TYPE_UINT8:
-#ifndef __LITTLE_ENDIAN__
-         avalue[i] = (char *) pst + 7;
-         pst++;
-         break;
-#endif
-
-       case FFI_TYPE_SINT16:
-       case FFI_TYPE_UINT16:
-#ifndef __LITTLE_ENDIAN__
-         avalue[i] = (char *) pst + 6;
-         pst++;
-         break;
-#endif
-
-       case FFI_TYPE_SINT32:
-       case FFI_TYPE_UINT32:
-#ifndef __LITTLE_ENDIAN__
-         avalue[i] = (char *) pst + 4;
-         pst++;
-         break;
-#endif
-
-       case FFI_TYPE_SINT64:
-       case FFI_TYPE_UINT64:
-       case FFI_TYPE_POINTER:
-         avalue[i] = pst;
-         pst++;
-         break;
-
-       case FFI_TYPE_STRUCT:
-#ifdef __STRUCT_PARM_ALIGN__
-         align = arg_types[i]->alignment;
-         if (align > __STRUCT_PARM_ALIGN__)
-           align = __STRUCT_PARM_ALIGN__;
-         if (align > 1)
-           pst = (unsigned long *) ALIGN ((size_t) pst, align);
-#endif
-         elt = 0;
-#if _CALL_ELF == 2
-         elt = discover_homogeneous_aggregate (arg_types[i], &elnum);
-#endif
-         if (elt)
-           {
-             union {
-               void *v;
-               unsigned long *ul;
-               float *f;
-               double *d;
-               size_t p;
-             } to, from;
-
-             /* Repackage the aggregate from its parts.  The
-                aggregate size is not greater than the space taken by
-                the registers so store back to the register/parameter
-                save arrays.  */
-             if (pfr + elnum <= end_pfr)
-               to.v = pfr;
-             else
-               to.v = pst;
-
-             avalue[i] = to.v;
-             from.ul = pst;
-             if (elt == FFI_TYPE_FLOAT)
-               {
-                 do
-                   {
-                     if (pfr < end_pfr && i < nfixedargs)
-                       {
-                         *to.f = (float) pfr->d;
-                         pfr++;
-                       }
-                     else
-                       *to.f = *from.f;
-                     to.f++;
-                     from.f++;
-                   }
-                 while (--elnum != 0);
-               }
-             else
-               {
-                 do
-                   {
-                     if (pfr < end_pfr && i < nfixedargs)
-                       {
-                         *to.d = pfr->d;
-                         pfr++;
-                       }
-                     else
-                       *to.d = *from.d;
-                     to.d++;
-                     from.d++;
-                   }
-                 while (--elnum != 0);
-               }
-           }
-         else
-           {
-#ifndef __LITTLE_ENDIAN__
-             /* Structures with size less than eight bytes are passed
-                left-padded.  */
-             if (arg_types[i]->size < 8)
-               avalue[i] = (char *) pst + 8 - arg_types[i]->size;
-             else
-#endif
-               avalue[i] = pst;
-           }
-         pst += (arg_types[i]->size + 7) / 8;
-         break;
-
-       case FFI_TYPE_FLOAT:
-         /* unfortunately float values are stored as doubles
-          * in the ffi_closure_LINUX64 code (since we don't check
-          * the type in that routine).
-          */
-
-         /* there are 13 64bit floating point registers */
-
-         if (pfr < end_pfr && i < nfixedargs)
-           {
-             double temp = pfr->d;
-             pfr->f = (float) temp;
-             avalue[i] = pfr;
-             pfr++;
-           }
-         else
-           avalue[i] = pst;
-         pst++;
-         break;
-
-       case FFI_TYPE_DOUBLE:
-         /* On the outgoing stack all values are aligned to 8 */
-         /* there are 13 64bit floating point registers */
-
-         if (pfr < end_pfr && i < nfixedargs)
-           {
-             avalue[i] = pfr;
-             pfr++;
-           }
-         else
-           avalue[i] = pst;
-         pst++;
-         break;
-
-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
-       case FFI_TYPE_LONGDOUBLE:
-         if (pfr + 1 < end_pfr && i + 1 < nfixedargs)
-           {
-             avalue[i] = pfr;
-             pfr += 2;
-           }
-         else
-           {
-             if (pfr < end_pfr && i < nfixedargs)
-               {
-                 /* Passed partly in f13 and partly on the stack.
-                    Move it all to the stack.  */
-                 *pst = *(unsigned long *) pfr;
-                 pfr++;
-               }
-             avalue[i] = pst;
-           }
-         pst += 2;
-         break;
-#endif
-
-       default:
-         FFI_ASSERT (0);
-       }
-
-      i++;
-    }
-
-
-  (closure->fun) (cif, rvalue, avalue, closure->user_data);
-
-  /* Tell ffi_closure_LINUX64 how to perform return type promotions.  */
-  if ((cif->flags & FLAG_RETURNS_SMST) != 0)
-    {
-      if ((cif->flags & FLAG_RETURNS_FP) == 0)
-       return FFI_V2_TYPE_SMALL_STRUCT + cif->rtype->size - 1;
-      else if ((cif->flags & FLAG_RETURNS_64BITS) != 0)
-       return FFI_V2_TYPE_DOUBLE_HOMOG;
-      else
-       return FFI_V2_TYPE_FLOAT_HOMOG;
-    }
-  return cif->rtype->type;
 }