This separates the 32-bit sysv/linux/bsd code from the 64-bit linux
authorAlan Modra <amodra@gmail.com>
Thu, 21 Nov 2013 11:12:35 +0000 (06:12 -0500)
committerAnthony Green <green@moxielogic.com>
Thu, 21 Nov 2013 11:12:35 +0000 (06:12 -0500)
code, and makes it possible to link code compiled with different
options to those used to compile libffi.  For example, a
-mlong-double-128 libffi can be used with -mlong-double-64 code.

Using the return value area as a place to pass parameters wasn't such
a good idea, causing a failure of cls_ulonglong.c.  I didn't see this
when running the mainline gcc libffi testsuite because that version of
the test is inferior to the upstreamm libffi test.

Using NUM_FPR_ARG_REGISTERS rather than NUM_FPR_ARG_REGISTERS64 meant
that a parameter save area could be allocated before it was strictly
necessary.  Wrong but harmless.  Found when splitting apart ffi.c
into 32-bit and 64-bit support.

20 files changed:
ChangeLog
Makefile.am
Makefile.in
configure
configure.ac
include/Makefile.in
include/ffi.h.in
man/Makefile.in
src/powerpc/ffi.c
src/powerpc/ffi_linux64.c [new file with mode: 0644]
src/powerpc/ffi_powerpc.h [new file with mode: 0644]
src/powerpc/ffi_sysv.c [new file with mode: 0644]
src/powerpc/ffitarget.h
src/powerpc/linux64.S
src/powerpc/linux64_closure.S
src/powerpc/ppc_closure.S
src/powerpc/sysv.S
src/prep_cif.c
src/types.c
testsuite/Makefile.in

index ba12595..d82bba0 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,41 @@
+2013-11-21  Anthony Green  <green@moxielogic.com>
+
+       * configure, Makefile.in, include/Makefile.in, include/ffi.h.in,
+       man/Makefile.in, testsuite/Makefile.in: Rebuilt.
+
+2013-11-21  Alan Modra  <amodra@gmail.com>
+
+       * Makefile.am (EXTRA_DIST): Add new src/powerpc files.
+       (nodist_libffi_la_SOURCES <POWERPC, POWERPC_FREEBSD>): Likewise.
+       * configure.ac (HAVE_LONG_DOUBLE_VARIANT): Define for powerpc.
+       * include/ffi.h.in (ffi_prep_types): Declare.
+       * src/prep_cif.c (ffi_prep_cif_core): Call ffi_prep_types.
+       * src/types.c (FFI_NONCONST_TYPEDEF): Define and use for
+       HAVE_LONG_DOUBLE_VARIANT.
+       * src/powerpc/ffi_powerpc.h: New file.
+       * src/powerpc/ffi.c: Split into..
+       * src/powerpc/ffi_sysv.c: ..new file, and..
+       * src/powerpc/ffi_linux64.c: ..new file, rewriting parts.
+       * src/powerpc/ffitarget.h (enum ffi_abi): Rewrite powerpc ABI
+       selection as bits controlling features.
+       * src/powerpc/linux64.S: For consistency, use POWERPC64 rather
+       than __powerpc64__.
+       * src/powerpc/linux64_closure.S: Likewise.
+       * src/powerpc/ppc_closure.S: Likewise.  Move .note.FNU-stack
+       inside guard.
+       * src/powerpc/sysv.S: Likewise.
+       * configure: Regenerate.
+       * fficonfig.h.in: Regenerate.
+       * Makefile.in: Regenerate.
+
+2013-11-20  Alan Modra  <amodra@gmail.com>
+
+       * src/powerpc/ffi.c (ffi_prep_cif_machdep_core): Use
+       NUM_FPR_ARG_REGISTERS64 and NUM_GPR_ARG_REGISTERS64 not their
+       32-bit versions for 64-bit code.
+       * src/powerpc/linux64_closure.S: Don't use the return value area
+       as a parameter save area on ELFv2.
+
 2013-11-18  Iain Sandoe  <iain@codesourcery.com>
 
        * src/powerpc/darwin.S (EH): Correct use of pcrel FDE encoding.
index e9ba8cb..4b5d1c7 100644 (file)
@@ -24,7 +24,8 @@ EXTRA_DIST = LICENSE ChangeLog.v1 ChangeLog.libgcj configure.host     \
         src/microblaze/ffi.c src/microblaze/sysv.S                     \
         src/microblaze/ffitarget.h                                     \
         src/nios2/ffi.c src/nios2/ffitarget.h src/nios2/sysv.S         \
-        src/powerpc/ffi.c                                              \
+        src/powerpc/ffi.c src/powerpc/ffi_powerpc.h                    \
+        src/powerpc/ffi_sysv.c src/powerpc/ffi_linux64.c               \
         src/powerpc/sysv.S src/powerpc/linux64.S                       \
         src/powerpc/linux64_closure.S src/powerpc/ppc_closure.S        \
         src/powerpc/asm.h src/powerpc/aix.S src/powerpc/darwin.S       \
@@ -167,7 +168,7 @@ if NIOS2
 nodist_libffi_la_SOURCES += src/nios2/sysv.S src/nios2/ffi.c
 endif
 if POWERPC
-nodist_libffi_la_SOURCES += src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S
+nodist_libffi_la_SOURCES += src/powerpc/ffi.c src/powerpc/ffi_sysv.c src/powerpc/ffi_linux64.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S
 endif
 if POWERPC_AIX
 nodist_libffi_la_SOURCES += src/powerpc/ffi_darwin.c src/powerpc/aix.S src/powerpc/aix_closure.S
@@ -176,7 +177,7 @@ if POWERPC_DARWIN
 nodist_libffi_la_SOURCES += src/powerpc/ffi_darwin.c src/powerpc/darwin.S src/powerpc/darwin_closure.S
 endif
 if POWERPC_FREEBSD
-nodist_libffi_la_SOURCES += src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S
+nodist_libffi_la_SOURCES += src/powerpc/ffi.c src/powerpc/ffi_sysv.c src/powerpc/sysv.S src/powerpc/ppc_closure.S
 endif
 if AARCH64
 nodist_libffi_la_SOURCES += src/aarch64/sysv.S src/aarch64/ffi.c
index 9e645e5..bf77c44 100644 (file)
@@ -97,10 +97,10 @@ target_triplet = @target@
 @MOXIE_TRUE@am__append_15 = src/moxie/ffi.c src/moxie/eabi.S
 @MICROBLAZE_TRUE@am__append_16 = src/microblaze/ffi.c src/microblaze/sysv.S
 @NIOS2_TRUE@am__append_17 = src/nios2/sysv.S src/nios2/ffi.c
-@POWERPC_TRUE@am__append_18 = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S
+@POWERPC_TRUE@am__append_18 = src/powerpc/ffi.c src/powerpc/ffi_sysv.c src/powerpc/ffi_linux64.c src/powerpc/sysv.S src/powerpc/ppc_closure.S src/powerpc/linux64.S src/powerpc/linux64_closure.S
 @POWERPC_AIX_TRUE@am__append_19 = src/powerpc/ffi_darwin.c src/powerpc/aix.S src/powerpc/aix_closure.S
 @POWERPC_DARWIN_TRUE@am__append_20 = src/powerpc/ffi_darwin.c src/powerpc/darwin.S src/powerpc/darwin_closure.S
-@POWERPC_FREEBSD_TRUE@am__append_21 = src/powerpc/ffi.c src/powerpc/sysv.S src/powerpc/ppc_closure.S
+@POWERPC_FREEBSD_TRUE@am__append_21 = src/powerpc/ffi.c src/powerpc/ffi_sysv.c src/powerpc/sysv.S src/powerpc/ppc_closure.S
 @AARCH64_TRUE@am__append_22 = src/aarch64/sysv.S src/aarch64/ffi.c
 @ARC_TRUE@am__append_23 = src/arc/arcompact.S src/arc/ffi.c
 @ARM_TRUE@am__append_24 = src/arm/sysv.S src/arm/ffi.c
@@ -203,7 +203,9 @@ am_libffi_la_OBJECTS = src/prep_cif.lo src/types.lo src/raw_api.lo \
 @MICROBLAZE_TRUE@am__objects_16 = src/microblaze/ffi.lo \
 @MICROBLAZE_TRUE@      src/microblaze/sysv.lo
 @NIOS2_TRUE@am__objects_17 = src/nios2/sysv.lo src/nios2/ffi.lo
-@POWERPC_TRUE@am__objects_18 = src/powerpc/ffi.lo src/powerpc/sysv.lo \
+@POWERPC_TRUE@am__objects_18 = src/powerpc/ffi.lo \
+@POWERPC_TRUE@ src/powerpc/ffi_sysv.lo \
+@POWERPC_TRUE@ src/powerpc/ffi_linux64.lo src/powerpc/sysv.lo \
 @POWERPC_TRUE@ src/powerpc/ppc_closure.lo \
 @POWERPC_TRUE@ src/powerpc/linux64.lo \
 @POWERPC_TRUE@ src/powerpc/linux64_closure.lo
@@ -214,6 +216,7 @@ am_libffi_la_OBJECTS = src/prep_cif.lo src/types.lo src/raw_api.lo \
 @POWERPC_DARWIN_TRUE@  src/powerpc/darwin.lo \
 @POWERPC_DARWIN_TRUE@  src/powerpc/darwin_closure.lo
 @POWERPC_FREEBSD_TRUE@am__objects_21 = src/powerpc/ffi.lo \
+@POWERPC_FREEBSD_TRUE@ src/powerpc/ffi_sysv.lo \
 @POWERPC_FREEBSD_TRUE@ src/powerpc/sysv.lo \
 @POWERPC_FREEBSD_TRUE@ src/powerpc/ppc_closure.lo
 @AARCH64_TRUE@am__objects_22 = src/aarch64/sysv.lo src/aarch64/ffi.lo
@@ -484,6 +487,7 @@ FFI_EXEC_TRAMPOLINE_TABLE = @FFI_EXEC_TRAMPOLINE_TABLE@
 FGREP = @FGREP@
 GREP = @GREP@
 HAVE_LONG_DOUBLE = @HAVE_LONG_DOUBLE@
+HAVE_LONG_DOUBLE_VARIANT = @HAVE_LONG_DOUBLE_VARIANT@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
@@ -605,7 +609,8 @@ EXTRA_DIST = LICENSE ChangeLog.v1 ChangeLog.libgcj configure.host   \
         src/microblaze/ffi.c src/microblaze/sysv.S                     \
         src/microblaze/ffitarget.h                                     \
         src/nios2/ffi.c src/nios2/ffitarget.h src/nios2/sysv.S         \
-        src/powerpc/ffi.c                                              \
+        src/powerpc/ffi.c src/powerpc/ffi_powerpc.h                    \
+        src/powerpc/ffi_sysv.c src/powerpc/ffi_linux64.c               \
         src/powerpc/sysv.S src/powerpc/linux64.S                       \
         src/powerpc/linux64_closure.S src/powerpc/ppc_closure.S        \
         src/powerpc/asm.h src/powerpc/aix.S src/powerpc/darwin.S       \
@@ -961,6 +966,10 @@ src/powerpc/$(DEPDIR)/$(am__dirstamp):
        @: > src/powerpc/$(DEPDIR)/$(am__dirstamp)
 src/powerpc/ffi.lo: src/powerpc/$(am__dirstamp) \
        src/powerpc/$(DEPDIR)/$(am__dirstamp)
+src/powerpc/ffi_sysv.lo: src/powerpc/$(am__dirstamp) \
+       src/powerpc/$(DEPDIR)/$(am__dirstamp)
+src/powerpc/ffi_linux64.lo: src/powerpc/$(am__dirstamp) \
+       src/powerpc/$(DEPDIR)/$(am__dirstamp)
 src/powerpc/sysv.lo: src/powerpc/$(am__dirstamp) \
        src/powerpc/$(DEPDIR)/$(am__dirstamp)
 src/powerpc/ppc_closure.lo: src/powerpc/$(am__dirstamp) \
@@ -1243,6 +1252,8 @@ distclean-compile:
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/darwin_closure.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/ffi.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/ffi_darwin.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/ffi_linux64.Plo@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/ffi_sysv.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/linux64.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/linux64_closure.Plo@am__quote@
 @AMDEP_TRUE@@am__include@ @am__quote@src/powerpc/$(DEPDIR)/ppc_closure.Plo@am__quote@
index 6bb05b1..5d187fe 100755 (executable)
--- a/configure
+++ b/configure
@@ -645,6 +645,7 @@ FFI_EXEC_TRAMPOLINE_TABLE
 FFI_EXEC_TRAMPOLINE_TABLE_FALSE
 FFI_EXEC_TRAMPOLINE_TABLE_TRUE
 sys_symbol_underscore
+HAVE_LONG_DOUBLE_VARIANT
 HAVE_LONG_DOUBLE
 ALLOCA
 XTENSA_FALSE
@@ -13345,6 +13346,7 @@ fi
 
 
 TARGETDIR="unknown"
+HAVE_LONG_DOUBLE_VARIANT=0
 case "$host" in
   aarch64*-*-*)
        TARGET=AARCH64; TARGETDIR=aarch64
@@ -13505,6 +13507,7 @@ case "$host" in
 
   powerpc*-*-linux* | powerpc-*-sysv*)
        TARGET=POWERPC; TARGETDIR=powerpc
+       HAVE_LONG_DOUBLE_VARIANT=1
        ;;
   powerpc-*-amigaos*)
        TARGET=POWERPC; TARGETDIR=powerpc
@@ -13520,6 +13523,7 @@ case "$host" in
        ;;
   powerpc-*-freebsd* | powerpc-*-openbsd*)
        TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
+       HAVE_LONG_DOUBLE_VARIANT=1
        ;;
   powerpc64-*-freebsd*)
        TARGET=POWERPC; TARGETDIR=powerpc
@@ -14252,17 +14256,25 @@ _ACEOF
 # Also AC_SUBST this variable for ffi.h.
 if test -z "$HAVE_LONG_DOUBLE"; then
   HAVE_LONG_DOUBLE=0
-  if test $ac_cv_sizeof_double != $ac_cv_sizeof_long_double; then
-    if test $ac_cv_sizeof_long_double != 0; then
+  if test $ac_cv_sizeof_long_double != 0; then
+    if test $HAVE_LONG_DOUBLE_VARIANT != 0; then
+
+$as_echo "#define HAVE_LONG_DOUBLE_VARIANT 1" >>confdefs.h
+
       HAVE_LONG_DOUBLE=1
+    else
+      if test $ac_cv_sizeof_double != $ac_cv_sizeof_long_double; then
+        HAVE_LONG_DOUBLE=1
 
 $as_echo "#define HAVE_LONG_DOUBLE 1" >>confdefs.h
 
+      fi
     fi
   fi
 fi
 
 
+
  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether byte ordering is bigendian" >&5
 $as_echo_n "checking whether byte ordering is bigendian... " >&6; }
 if ${ac_cv_c_bigendian+:} false; then :
index 8246ee9..0d768c6 100644 (file)
@@ -66,6 +66,7 @@ dnl The -no-testsuite modules omit the test subdir.
 AM_CONDITIONAL(TESTSUBDIR, test -d $srcdir/testsuite)
 
 TARGETDIR="unknown"
+HAVE_LONG_DOUBLE_VARIANT=0
 case "$host" in
   aarch64*-*-*)
        TARGET=AARCH64; TARGETDIR=aarch64
@@ -226,6 +227,7 @@ case "$host" in
 
   powerpc*-*-linux* | powerpc-*-sysv*)
        TARGET=POWERPC; TARGETDIR=powerpc
+       HAVE_LONG_DOUBLE_VARIANT=1
        ;;
   powerpc-*-amigaos*)
        TARGET=POWERPC; TARGETDIR=powerpc
@@ -241,6 +243,7 @@ case "$host" in
        ;;
   powerpc-*-freebsd* | powerpc-*-openbsd*)
        TARGET=POWERPC_FREEBSD; TARGETDIR=powerpc
+       HAVE_LONG_DOUBLE_VARIANT=1
        ;;
   powerpc64-*-freebsd*)
        TARGET=POWERPC; TARGETDIR=powerpc
@@ -333,14 +336,20 @@ AC_CHECK_SIZEOF(long double)
 # Also AC_SUBST this variable for ffi.h.
 if test -z "$HAVE_LONG_DOUBLE"; then
   HAVE_LONG_DOUBLE=0
-  if test $ac_cv_sizeof_double != $ac_cv_sizeof_long_double; then
-    if test $ac_cv_sizeof_long_double != 0; then
+  if test $ac_cv_sizeof_long_double != 0; then
+    if test $HAVE_LONG_DOUBLE_VARIANT != 0; then
+      AC_DEFINE(HAVE_LONG_DOUBLE_VARIANT, 1, [Define if you support more than one size of the long double type])
       HAVE_LONG_DOUBLE=1
-      AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the long double type and it is bigger than a double])
+    else
+      if test $ac_cv_sizeof_double != $ac_cv_sizeof_long_double; then
+        HAVE_LONG_DOUBLE=1
+        AC_DEFINE(HAVE_LONG_DOUBLE, 1, [Define if you have the long double type and it is bigger than a double])
+      fi
     fi
   fi
 fi
 AC_SUBST(HAVE_LONG_DOUBLE)
+AC_SUBST(HAVE_LONG_DOUBLE_VARIANT)
 
 AC_C_BIGENDIAN
 
index ddd28fa..0d0a8dc 100644 (file)
@@ -205,6 +205,7 @@ FFI_EXEC_TRAMPOLINE_TABLE = @FFI_EXEC_TRAMPOLINE_TABLE@
 FGREP = @FGREP@
 GREP = @GREP@
 HAVE_LONG_DOUBLE = @HAVE_LONG_DOUBLE@
+HAVE_LONG_DOUBLE_VARIANT = @HAVE_LONG_DOUBLE_VARIANT@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index a51583b..93c776f 100644 (file)
@@ -221,6 +221,11 @@ typedef struct {
 #endif
 } ffi_cif;
 
+#if HAVE_LONG_DOUBLE_VARIANT
+/* Used to adjust size/alignment of ffi types.  */
+void ffi_prep_types (ffi_abi abi);
+# endif
+
 /* Used internally, but overridden by some architectures */
 ffi_status ffi_prep_cif_core(ffi_cif *cif,
                             ffi_abi abi,
index 8e786d3..fb60d20 100644 (file)
@@ -187,6 +187,7 @@ FFI_EXEC_TRAMPOLINE_TABLE = @FFI_EXEC_TRAMPOLINE_TABLE@
 FGREP = @FGREP@
 GREP = @GREP@
 HAVE_LONG_DOUBLE = @HAVE_LONG_DOUBLE@
+HAVE_LONG_DOUBLE_VARIANT = @HAVE_LONG_DOUBLE_VARIANT@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@
index feb2144..efb441b 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;
 }
diff --git a/src/powerpc/ffi_linux64.c b/src/powerpc/ffi_linux64.c
new file mode 100644 (file)
index 0000000..33f24b3
--- /dev/null
@@ -0,0 +1,942 @@
+/* -----------------------------------------------------------------------
+   ffi_linux64.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
+                   Copyright (c) 1998 Geoffrey Keating
+
+   PowerPC 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 AUTHOR 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"
+
+#ifdef POWERPC64
+#include "ffi_common.h"
+#include "ffi_powerpc.h"
+
+
+/* About the LINUX64 ABI.  */
+enum {
+  NUM_GPR_ARG_REGISTERS64 = 8,
+  NUM_FPR_ARG_REGISTERS64 = 13
+};
+enum { ASM_NEEDS_REGISTERS64 = 4 };
+
+
+#if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+/* Adjust size of ffi_type_longdouble.  */
+void FFI_HIDDEN
+ffi_prep_types_linux64 (ffi_abi abi)
+{
+  if ((abi & (FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128)) == FFI_LINUX)
+    {
+      ffi_type_longdouble.size = 8;
+      ffi_type_longdouble.alignment = 8;
+    }
+  else
+    {
+      ffi_type_longdouble.size = 16;
+      ffi_type_longdouble.alignment = 16;
+    }
+}
+#endif
+
+
+#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
+
+
+/* Perform machine dependent cif processing */
+static ffi_status
+ffi_prep_cif_linux64_core (ffi_cif *cif)
+{
+  ffi_type **ptr;
+  unsigned bytes;
+  unsigned i, fparg_count = 0, intarg_count = 0;
+  unsigned flags = cif->flags;
+#if _CALL_ELF == 2
+  unsigned int elt, elnum;
+#endif
+
+#if FFI_TYPE_LONGDOUBLE == FFI_TYPE_DOUBLE
+  /* If compiled without long double support..  */
+  if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0)
+    return FFI_BAD_ABI;
+#endif
+
+  /* The machine-independent calculation of cif->bytes doesn't work
+     for us.  Redo the calculation.  */
+#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.  */
+  switch (cif->rtype->type)
+    {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+    case FFI_TYPE_LONGDOUBLE:
+      if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0)
+       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;
+
+    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:
+#if _CALL_ELF == 2
+      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 (cif->rtype->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;
+    }
+
+  for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)
+    {
+      unsigned int align;
+
+      switch ((*ptr)->type)
+       {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+       case FFI_TYPE_LONGDOUBLE:
+         if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0)
+           {
+             fparg_count++;
+             intarg_count++;
+           }
+         /* Fall through.  */
+#endif
+       case FFI_TYPE_DOUBLE:
+       case FFI_TYPE_FLOAT:
+         fparg_count++;
+         intarg_count++;
+         if (fparg_count > NUM_FPR_ARG_REGISTERS64)
+           flags |= FLAG_ARG_NEEDS_PSAVE;
+         break;
+
+       case FFI_TYPE_STRUCT:
+         if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0)
+           {
+             align = (*ptr)->alignment;
+             if (align > 16)
+               align = 16;
+             align = align / 8;
+             if (align > 1)
+               intarg_count = ALIGN (intarg_count, align);
+           }
+         intarg_count += ((*ptr)->size + 7) / 8;
+#if _CALL_ELF == 2
+         elt = discover_homogeneous_aggregate (*ptr, &elnum);
+         if (elt)
+           {
+             fparg_count += elnum;
+             if (fparg_count > NUM_FPR_ARG_REGISTERS64)
+               flags |= FLAG_ARG_NEEDS_PSAVE;
+           }
+         else
+#endif
+           {
+             if (intarg_count > NUM_GPR_ARG_REGISTERS64)
+               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_REGISTERS64)
+           flags |= FLAG_ARG_NEEDS_PSAVE;
+         break;
+       default:
+         FFI_ASSERT (0);
+       }
+    }
+
+  if (fparg_count != 0)
+    flags |= FLAG_FP_ARGUMENTS;
+  if (intarg_count > 4)
+    flags |= FLAG_4_GPR_ARGUMENTS;
+
+  /* Space for the FPR registers, if needed.  */
+  if (fparg_count != 0)
+    bytes += NUM_FPR_ARG_REGISTERS64 * sizeof (double);
+
+  /* Stack space.  */
+#if _CALL_ELF == 2
+  if ((flags & FLAG_ARG_NEEDS_PSAVE) != 0)
+    bytes += intarg_count * sizeof (long);
+#else
+  if (intarg_count > NUM_GPR_ARG_REGISTERS64)
+    bytes += (intarg_count - NUM_GPR_ARG_REGISTERS64) * sizeof (long);
+#endif
+
+  /* The stack space allocated needs to be a multiple of 16 bytes.  */
+  bytes = (bytes + 15) & ~0xF;
+
+  cif->flags = flags;
+  cif->bytes = bytes;
+
+  return FFI_OK;
+}
+
+ffi_status FFI_HIDDEN
+ffi_prep_cif_linux64 (ffi_cif *cif)
+{
+  if ((cif->abi & FFI_LINUX) != 0)
+    cif->nfixedargs = cif->nargs;
+#if _CALL_ELF != 2
+  else if (cif->abi == FFI_COMPAT_LINUX64)
+    {
+      /* This call is from old code.  Don't touch cif->nfixedargs
+        since old code will be using a smaller cif.  */
+      cif->flags |= FLAG_COMPAT;
+      /* Translate to new abi value.  */
+      cif->abi = FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128;
+    }
+#endif
+  else
+    return FFI_BAD_ABI;
+  return ffi_prep_cif_linux64_core (cif);
+}
+
+ffi_status FFI_HIDDEN
+ffi_prep_cif_linux64_var (ffi_cif *cif,
+                         unsigned int nfixedargs,
+                         unsigned int ntotalargs MAYBE_UNUSED)
+{
+  if ((cif->abi & FFI_LINUX) != 0)
+    cif->nfixedargs = nfixedargs;
+#if _CALL_ELF != 2
+  else if (cif->abi == FFI_COMPAT_LINUX64)
+    {
+      /* This call is from old code.  Don't touch cif->nfixedargs
+        since old code will be using a smaller cif.  */
+      cif->flags |= FLAG_COMPAT;
+      /* Translate to new abi value.  */
+      cif->abi = FFI_LINUX | FFI_LINUX_LONG_DOUBLE_128;
+    }
+#endif
+  else
+    return FFI_BAD_ABI;
+#if _CALL_ELF == 2
+  cif->flags |= FLAG_ARG_NEEDS_PSAVE;
+#endif
+  return ffi_prep_cif_linux64_core (cif);
+}
+
+
+/* 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
+
+*/
+
+void FFI_HIDDEN
+ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
+{
+  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;
+  unsigned long align;
+
+  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;
+#if _CALL_ELF != 2
+  nfixedargs = (unsigned) -1;
+  if ((flags & FLAG_COMPAT) == 0)
+#endif
+    nfixedargs = ecif->cif->nfixedargs;
+  for (ptr = ecif->cif->arg_types, i = 0;
+       i < nargs;
+       i++, ptr++, p_argv.v++)
+    {
+#if _CALL_ELF == 2
+      unsigned int elt, elnum;
+#endif
+
+      switch ((*ptr)->type)
+       {
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+       case FFI_TYPE_LONGDOUBLE:
+         if ((ecif->cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0)
+           {
+             double_tmp = (*p_argv.d)[0];
+             if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+               {
+                 *fpr_base.d++ = double_tmp;
+# if _CALL_ELF != 2
+                 if ((flags & FLAG_COMPAT) != 0)
+                   *next_arg.d = double_tmp;
+# endif
+               }
+             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;
+# if _CALL_ELF != 2
+                 if ((flags & FLAG_COMPAT) != 0)
+                   *next_arg.d = double_tmp;
+# endif
+               }
+             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;
+           }
+         /* Fall through.  */
+#endif
+       case FFI_TYPE_DOUBLE:
+         double_tmp = **p_argv.d;
+         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+           {
+             *fpr_base.d++ = double_tmp;
+#if _CALL_ELF != 2
+             if ((flags & FLAG_COMPAT) != 0)
+               *next_arg.d = double_tmp;
+#endif
+           }
+         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;
+
+       case FFI_TYPE_FLOAT:
+         double_tmp = **p_argv.f;
+         if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+           {
+             *fpr_base.d++ = double_tmp;
+#if _CALL_ELF != 2
+             if ((flags & FLAG_COMPAT) != 0)
+               *next_arg.f = (float) double_tmp;
+#endif
+           }
+         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_STRUCT:
+         if ((ecif->cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0)
+           {
+             align = (*ptr)->alignment;
+             if (align > 16)
+               align = 16;
+             if (align > 1)
+               next_arg.p = ALIGN (next_arg.p, align);
+           }
+#if _CALL_ELF == 2
+         elt = discover_homogeneous_aggregate (*ptr, &elnum);
+         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
+#endif
+           {
+             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 _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_linux64 (ffi_closure *closure,
+                             ffi_cif *cif,
+                             void (*fun) (ffi_cif *, void *, void **, void *),
+                             void *user_data,
+                             void *codeloc)
+{
+#if _CALL_ELF == 2
+  unsigned int *tramp = (unsigned int *) &closure->tramp[0];
+
+  if (cif->abi < FFI_LINUX || cif->abi >= FFI_LAST_ABI)
+    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_LINUX || cif->abi >= FFI_LAST_ABI)
+    return FFI_BAD_ABI;
+
+  /* Copy function address and TOC from ffi_closure_LINUX64.  */
+  memcpy (tramp, (char *) ffi_closure_LINUX64, 16);
+  tramp[2] = codeloc;
+#endif
+
+  closure->cif = cif;
+  closure->fun = fun;
+  closure->user_data = user_data;
+
+  return FFI_OK;
+}
+
+
+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;
+  unsigned long align;
+
+  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;
+#if _CALL_ELF != 2
+  nfixedargs = (unsigned) -1;
+  if ((cif->flags & FLAG_COMPAT) == 0)
+#endif
+    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:
+         if ((cif->abi & FFI_LINUX_STRUCT_ALIGN) != 0)
+           {
+             align = arg_types[i]->alignment;
+             if (align > 16)
+               align = 16;
+             if (align > 1)
+               pst = (unsigned long *) ALIGN ((size_t) pst, align);
+           }
+         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;
+
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+       case FFI_TYPE_LONGDOUBLE:
+         if ((cif->abi & FFI_LINUX_LONG_DOUBLE_128) != 0)
+           {
+             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;
+           }
+         /* Fall through.  */
+#endif
+       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;
+
+       case FFI_TYPE_FLOAT:
+         if (pfr < end_pfr && i < nfixedargs)
+           {
+             /* Float values are stored as doubles in the
+                ffi_closure_LINUX64 code.  Fix them here.  */
+             pfr->f = (float) pfr->d;
+             avalue[i] = pfr;
+             pfr++;
+           }
+         else
+           avalue[i] = pst;
+         pst++;
+         break;
+
+       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;
+}
+#endif
diff --git a/src/powerpc/ffi_powerpc.h b/src/powerpc/ffi_powerpc.h
new file mode 100644 (file)
index 0000000..2e61653
--- /dev/null
@@ -0,0 +1,77 @@
+/* -----------------------------------------------------------------------
+   ffi_powerpc.h - 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
+                   Copyright (c) 1998 Geoffrey Keating
+
+   PowerPC 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 AUTHOR 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.
+   ----------------------------------------------------------------------- */
+
+enum {
+  /* The assembly depends on these exact flags.  */
+  /* These go in cr7 */
+  FLAG_RETURNS_SMST    = 1 << (31-31), /* Used for FFI_SYSV small structs.  */
+  FLAG_RETURNS_NOTHING  = 1 << (31-30),
+  FLAG_RETURNS_FP       = 1 << (31-29),
+  FLAG_RETURNS_64BITS   = 1 << (31-28),
+
+  /* This goes in cr6 */
+  FLAG_RETURNS_128BITS  = 1 << (31-27),
+
+  FLAG_COMPAT          = 1 << (31- 8), /* Not used by assembly */
+
+  /* These go in cr1 */
+  FLAG_ARG_NEEDS_COPY   = 1 << (31- 7), /* Used by sysv code */
+  FLAG_ARG_NEEDS_PSAVE  = FLAG_ARG_NEEDS_COPY, /* Used by linux64 code */
+  FLAG_FP_ARGUMENTS     = 1 << (31- 6), /* cr1.eq; specified by ABI */
+  FLAG_4_GPR_ARGUMENTS  = 1 << (31- 5),
+  FLAG_RETVAL_REFERENCE = 1 << (31- 4)
+};
+
+typedef union
+{
+  float f;
+  double d;
+} ffi_dblfl;
+
+void FFI_HIDDEN ffi_closure_SYSV (void);
+void FFI_HIDDEN ffi_call_SYSV(extended_cif *, unsigned, unsigned, unsigned *,
+                             void (*)(void));
+
+void FFI_HIDDEN ffi_prep_types_sysv (ffi_abi);
+ffi_status FFI_HIDDEN ffi_prep_cif_sysv (ffi_cif *);
+int FFI_HIDDEN ffi_closure_helper_SYSV (ffi_closure *, void *, unsigned long *,
+                                       ffi_dblfl *, unsigned long *);
+
+void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long, unsigned long,
+                                unsigned long *, void (*)(void));
+void FFI_HIDDEN ffi_closure_LINUX64 (void);
+
+void FFI_HIDDEN ffi_prep_types_linux64 (ffi_abi);
+ffi_status FFI_HIDDEN ffi_prep_cif_linux64 (ffi_cif *);
+ffi_status FFI_HIDDEN ffi_prep_cif_linux64_var (ffi_cif *, unsigned int,
+                                               unsigned int);
+void FFI_HIDDEN ffi_prep_args64 (extended_cif *, unsigned long *const);
+int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,
+                                          unsigned long *, ffi_dblfl *);
diff --git a/src/powerpc/ffi_sysv.c b/src/powerpc/ffi_sysv.c
new file mode 100644 (file)
index 0000000..fbe85fe
--- /dev/null
@@ -0,0 +1,931 @@
+/* -----------------------------------------------------------------------
+   ffi_sysv.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
+                Copyright (c) 1998 Geoffrey Keating
+
+   PowerPC 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 AUTHOR 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"
+
+#ifndef POWERPC64
+#include "ffi_common.h"
+#include "ffi_powerpc.h"
+
+
+/* About the SYSV ABI.  */
+#define ASM_NEEDS_REGISTERS 4
+#define NUM_GPR_ARG_REGISTERS 8
+#define NUM_FPR_ARG_REGISTERS 8
+
+
+#if HAVE_LONG_DOUBLE_VARIANT && FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+/* Adjust size of ffi_type_longdouble.  */
+void FFI_HIDDEN
+ffi_prep_types_sysv (ffi_abi abi)
+{
+  if ((abi & (FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128)) == FFI_SYSV)
+    {
+      ffi_type_longdouble.size = 8;
+      ffi_type_longdouble.alignment = 8;
+    }
+  else
+    {
+      ffi_type_longdouble.size = 16;
+      ffi_type_longdouble.alignment = 16;
+    }
+}
+#endif
+
+/* Transform long double, double and float to other types as per abi.  */
+static int
+translate_float (int abi, int type)
+{
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+  if (type == FFI_TYPE_LONGDOUBLE
+      && (abi & FFI_SYSV_LONG_DOUBLE_128) == 0)
+    type = FFI_TYPE_DOUBLE;
+#endif
+  if ((abi & FFI_SYSV_SOFT_FLOAT) != 0)
+    {
+      if (type == FFI_TYPE_FLOAT)
+       type = FFI_TYPE_UINT32;
+      else if (type == FFI_TYPE_DOUBLE)
+       type = FFI_TYPE_UINT64;
+#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+      else if (type == FFI_TYPE_LONGDOUBLE)
+       type = FFI_TYPE_UINT128;
+    }
+  else if ((abi & FFI_SYSV_IBM_LONG_DOUBLE) == 0)
+    {
+      if (type == FFI_TYPE_LONGDOUBLE)
+       type = FFI_TYPE_STRUCT;
+#endif
+    }
+  return type;
+}
+
+/* Perform machine dependent cif processing */
+static ffi_status
+ffi_prep_cif_sysv_core (ffi_cif *cif)
+{
+  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.  */
+
+  /* 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);
+
+  /* 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;
+     - Larger structures are allocated space and a pointer is passed as
+     the first argument.
+     - Single/double FP values are returned in fpr1;
+     - long doubles (if not equivalent to double) are returned in
+     fpr1,fpr2 for Linux and as for large structs for SysV.  */
+
+  type = translate_float (cif->abi, type);
+
+  switch (type)
+    {
+#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;
+#ifdef __NO_FPRS__
+      return FFI_BAD_ABI;
+#endif
+      break;
+
+    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.  A draft ABI used by linux instead
+        returns them in memory.  */
+      if ((cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
+       {
+         flags |= FLAG_RETURNS_SMST;
+         break;
+       }
+      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;
+    }
+
+  /* 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;
+
+      typenum = translate_float (cif->abi, typenum);
+
+      switch (typenum)
+       {
+#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++;
+#ifdef __NO_FPRS__
+         return FFI_BAD_ABI;
+#endif
+         break;
+
+       case FFI_TYPE_FLOAT:
+         fparg_count++;
+#ifdef __NO_FPRS__
+         return FFI_BAD_ABI;
+#endif
+         break;
+
+       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);
+       }
+    }
+
+  if (fparg_count != 0)
+    flags |= FLAG_FP_ARGUMENTS;
+  if (intarg_count > 4)
+    flags |= FLAG_4_GPR_ARGUMENTS;
+  if (struct_copy_size != 0)
+    flags |= FLAG_ARG_NEEDS_COPY;
+
+  /* Space for the FPR registers, if needed.  */
+  if (fparg_count != 0)
+    bytes += NUM_FPR_ARG_REGISTERS * sizeof (double);
+
+  /* Stack space.  */
+  if (intarg_count > NUM_GPR_ARG_REGISTERS)
+    bytes += (intarg_count - NUM_GPR_ARG_REGISTERS) * sizeof (int);
+  if (fparg_count > NUM_FPR_ARG_REGISTERS)
+    bytes += (fparg_count - NUM_FPR_ARG_REGISTERS) * sizeof (double);
+
+  /* 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_HIDDEN
+ffi_prep_cif_sysv (ffi_cif *cif)
+{
+  if ((cif->abi & FFI_SYSV) == 0)
+    {
+      /* This call is from old code.  Translate to new ABI values.  */
+      cif->flags |= FLAG_COMPAT;
+      switch (cif->abi)
+       {
+       default:
+         return FFI_BAD_ABI;
+
+       case FFI_COMPAT_SYSV:
+         cif->abi = FFI_SYSV | FFI_SYSV_STRUCT_RET | FFI_SYSV_LONG_DOUBLE_128;
+         break;
+
+       case FFI_COMPAT_GCC_SYSV:
+         cif->abi = FFI_SYSV | FFI_SYSV_LONG_DOUBLE_128;
+         break;
+
+       case FFI_COMPAT_LINUX:
+         cif->abi = (FFI_SYSV | FFI_SYSV_IBM_LONG_DOUBLE
+                     | FFI_SYSV_LONG_DOUBLE_128);
+         break;
+
+       case FFI_COMPAT_LINUX_SOFT_FLOAT:
+         cif->abi = (FFI_SYSV | FFI_SYSV_SOFT_FLOAT | FFI_SYSV_IBM_LONG_DOUBLE
+                     | FFI_SYSV_LONG_DOUBLE_128);
+         break;
+       }
+    }
+  return ffi_prep_cif_sysv_core (cif);
+}
+
+/* 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_HIDDEN
+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 int typenum = (*ptr)->type;
+
+      typenum = translate_float (ecif->cif->abi, typenum);
+
+      /* Now test the translated value */
+      switch (typenum)
+       {
+#ifndef __NO_FPRS__
+# 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
+       case FFI_TYPE_DOUBLE:
+         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;
+
+       case FFI_TYPE_FLOAT:
+         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;
+#endif /* have FPRs */
+
+       case FFI_TYPE_UINT128:
+         /* 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.  */
+         {
+           unsigned int int_tmp;
+           unsigned int ii;
+           if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3)
+             {
+               if (intarg_count < NUM_GPR_ARG_REGISTERS)
+                 intarg_count = NUM_GPR_ARG_REGISTERS;
+               for (ii = 0; ii < 4; ii++)
+                 {
+                   int_tmp = (*p_argv.ui)[ii];
+                   *next_arg.u++ = int_tmp;
+                 }
+             }
+           else
+             {
+               for (ii = 0; 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
+           {
+             /* The abi states only certain register pairs can be
+                used for passing long long int specifically (r3,r4),
+                (r5,r6), (r7,r8), (r9,r10).  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);
+}
+
+#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");
+}
+
+ffi_status FFI_HIDDEN
+ffi_prep_closure_loc_sysv (ffi_closure *closure,
+                          ffi_cif *cif,
+                          void (*fun) (ffi_cif *, void *, void **, void *),
+                          void *user_data,
+                          void *codeloc)
+{
+  unsigned int *tramp;
+
+  if (cif->abi < FFI_SYSV || cif->abi >= FFI_LAST_ABI)
+    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);
+
+  closure->cif = cif;
+  closure->fun = fun;
+  closure->user_data = user_data;
+
+  return FFI_OK;
+}
+
+/* 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 */
+  rtypenum = translate_float (cif->abi, rtypenum);
+
+  /* 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_STRUCT_RET) != 0 && 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.  */
+    typenum = translate_float (cif->abi, typenum);
+
+    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).  */
+       if (nf < NUM_FPR_ARG_REGISTERS)
+         {
+           /* 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...  */
+           double temp = pfr->d;
+           pfr->f = (float) temp;
+           avalue[i] = pfr;
+           nf++;
+           pfr++;
+         }
+       else
+         {
+           avalue[i] = pst;
+           pst += 1;
+         }
+       break;
+
+      case FFI_TYPE_DOUBLE:
+       if (nf < NUM_FPR_ARG_REGISTERS)
+         {
+           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 < NUM_FPR_ARG_REGISTERS - 1)
+         {
+           avalue[i] = pfr;
+           pfr += 2;
+           nf += 2;
+         }
+       else
+         {
+           if (((long) pst) & 4)
+             pst++;
+           avalue[i] = pst;
+           pst += 4;
+           nf = 8;
+         }
+       break;
+# endif
+#endif
+
+      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 < NUM_GPR_ARG_REGISTERS - 3)
+         {
+           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__
+       if (ng < NUM_GPR_ARG_REGISTERS)
+         {
+           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__
+       if (ng < NUM_GPR_ARG_REGISTERS)
+         {
+           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:
+       if (ng < NUM_GPR_ARG_REGISTERS)
+         {
+           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 < NUM_GPR_ARG_REGISTERS)
+         {
+           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 < NUM_GPR_ARG_REGISTERS - 1)
+         {
+           if (ng & 1)
+             {
+               /* 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 = NUM_GPR_ARG_REGISTERS;
+         }
+       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 with the size of
+     the struct less one.  We never have a struct with size zero.
+     See the comment in ffitarget.h about ordering.  */
+  if (rtypenum == FFI_TYPE_STRUCT
+      && (cif->abi & FFI_SYSV_STRUCT_RET) != 0 && size <= 8)
+    return FFI_SYSV_TYPE_SMALL_STRUCT - 1 + size;
+  return rtypenum;
+}
+#endif
index 2be728e..b47b0f5 100644 (file)
@@ -60,45 +60,76 @@ typedef signed long            ffi_sarg;
 typedef enum ffi_abi {
   FFI_FIRST_ABI = 0,
 
-#ifdef POWERPC
-  FFI_SYSV,
-  FFI_GCC_SYSV,
-  FFI_LINUX64,
-  FFI_LINUX,
-  FFI_LINUX_SOFT_FLOAT,
-# if defined(POWERPC64)
-  FFI_DEFAULT_ABI = FFI_LINUX64,
-# elif defined(__NO_FPRS__)
-  FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT,
-# elif (__LDBL_MANT_DIG__ == 106)
-  FFI_DEFAULT_ABI = FFI_LINUX,
-# else
-  FFI_DEFAULT_ABI = FFI_GCC_SYSV,
-# endif
-#endif
-
-#ifdef POWERPC_AIX
+#if defined (POWERPC_AIX)
   FFI_AIX,
   FFI_DARWIN,
   FFI_DEFAULT_ABI = FFI_AIX,
-#endif
+  FFI_LAST_ABI
 
-#ifdef POWERPC_DARWIN
+#elif defined (POWERPC_DARWIN)
   FFI_AIX,
   FFI_DARWIN,
   FFI_DEFAULT_ABI = FFI_DARWIN,
-#endif
+  FFI_LAST_ABI
 
-#ifdef POWERPC_FREEBSD
-  FFI_SYSV,
-  FFI_GCC_SYSV,
-  FFI_LINUX64,
-  FFI_LINUX,
-  FFI_LINUX_SOFT_FLOAT,
-  FFI_DEFAULT_ABI = FFI_SYSV,
+#else
+  /* The FFI_COMPAT values are used by old code.  Since libffi may be
+     a shared library we have to support old values for backwards
+     compatibility.  */
+  FFI_COMPAT_SYSV,
+  FFI_COMPAT_GCC_SYSV,
+  FFI_COMPAT_LINUX64,
+  FFI_COMPAT_LINUX,
+  FFI_COMPAT_LINUX_SOFT_FLOAT,
+
+# if defined (POWERPC64)
+  /* This bit, always set in new code, must not be set in any of the
+     old FFI_COMPAT values that might be used for 64-bit linux.  We
+     only need worry about FFI_COMPAT_LINUX64, but to be safe avoid
+     all old values.  */
+  FFI_LINUX = 8,
+  /* This and following bits can reuse FFI_COMPAT values.  */
+  FFI_LINUX_STRUCT_ALIGN = 1,
+  FFI_LINUX_LONG_DOUBLE_128 = 2,
+  FFI_DEFAULT_ABI = (FFI_LINUX
+#  ifdef __STRUCT_PARM_ALIGN__
+                    | FFI_LINUX_STRUCT_ALIGN
+#  endif
+#  ifdef __LONG_DOUBLE_128__
+                    | FFI_LINUX_LONG_DOUBLE_128
+#  endif
+                    ),
+  FFI_LAST_ABI = 12
+
+# else
+  /* This bit, always set in new code, must not be set in any of the
+     old FFI_COMPAT values that might be used for 32-bit linux/sysv/bsd.  */
+  FFI_SYSV = 8,
+  /* This and following bits can reuse FFI_COMPAT values.  */
+  FFI_SYSV_SOFT_FLOAT = 1,
+  FFI_SYSV_STRUCT_RET = 2,
+  FFI_SYSV_IBM_LONG_DOUBLE = 4,
+  FFI_SYSV_LONG_DOUBLE_128 = 16,
+
+  FFI_DEFAULT_ABI = (FFI_SYSV
+#  ifdef __NO_FPRS__
+                    | FFI_SYSV_SOFT_FLOAT
+#  endif
+#  if (defined (__SVR4_STRUCT_RETURN)                                  \
+       || defined (POWERPC_FREEBSD) && !defined (__AIX_STRUCT_RETURN))
+                    | FFI_SYSV_STRUCT_RET
+#  endif
+#  if __LDBL_MANT_DIG__ == 106
+                    | FFI_SYSV_IBM_LONG_DOUBLE
+#  endif
+#  ifdef __LONG_DOUBLE_128__
+                    | FFI_SYSV_LONG_DOUBLE_128
+#  endif
+                    ),
+  FFI_LAST_ABI = 32
+# endif
 #endif
 
-  FFI_LAST_ABI
 } ffi_abi;
 #endif
 
@@ -117,9 +148,7 @@ typedef enum ffi_abi {
 /* Needed for soft-float long-double-128 support.  */
 #define FFI_TYPE_UINT128 (FFI_TYPE_LAST + 1)
 
-/* Needed for FFI_SYSV small structure returns.
-   We use two flag bits, (FLAG_SYSV_SMST_R3, FLAG_SYSV_SMST_R4) which are
-   defined in ffi.c, to determine the exact return type and its size.  */
+/* Needed for FFI_SYSV small structure returns.  */
 #define FFI_SYSV_TYPE_SMALL_STRUCT (FFI_TYPE_LAST + 2)
 
 /* Used by ELFv2 for homogenous structure returns.  */
index 85b8aaa..c4d01d8 100644 (file)
@@ -29,7 +29,7 @@
 #include <fficonfig.h>
 #include <ffi.h>
 
-#ifdef __powerpc64__
+#ifdef POWERPC64
        .hidden ffi_call_LINUX64
        .globl  ffi_call_LINUX64
 # if _CALL_ELF == 2
index 9b6b5f3..46a9ddf 100644 (file)
@@ -30,7 +30,7 @@
 
        .file   "linux64_closure.S"
 
-#ifdef __powerpc64__
+#ifdef POWERPC64
        FFI_HIDDEN (ffi_closure_LINUX64)
        .globl  ffi_closure_LINUX64
 # if _CALL_ELF == 2
@@ -60,13 +60,11 @@ ffi_closure_LINUX64:
 # endif
 
 # if _CALL_ELF == 2
-#  32 byte special reg save area + 64 byte parm save area and retval
-#  + 13*8 fpr save area + round to 16
-#  define STACKFRAME 208
+#  32 byte special reg save area + 64 byte parm save area
+#  + 64 byte retval area + 13*8 fpr save area + round to 16
+#  define STACKFRAME 272
 #  define PARMSAVE 32
-#  No parameter save area is needed for the call to ffi_closure_helper_LINUX64,
-#  so return value can start there.
-#  define RETVAL PARMSAVE
+#  define RETVAL PARMSAVE+64
 # else
 #  48 bytes special reg save area + 64 bytes parm save area
 #  + 16 bytes retval area + 13*8 bytes fpr save area + round to 16
@@ -85,8 +83,8 @@ ffi_closure_LINUX64:
        bt      7, .Lparmsave
        # Our caller has not allocated a parameter save area.
        # We need to allocate one here and use it to pass gprs to
-       # ffi_closure_helper_LINUX64.  The return value area will do.
-       addi    %r12, %r1, -STACKFRAME+RETVAL
+       # ffi_closure_helper_LINUX64.
+       addi    %r12, %r1, -STACKFRAME+PARMSAVE
 .Lparmsave:
        std     %r0, 16(%r1)
        # Save general regs into parm save area
index 1f72340..075922c 100644 (file)
@@ -31,7 +31,7 @@
 
        .file   "ppc_closure.S"
 
-#ifndef __powerpc64__
+#ifndef POWERPC64
 
 ENTRY(ffi_closure_SYSV)
 .LFB1:
@@ -378,8 +378,7 @@ END(ffi_closure_SYSV)
        .align 2
 .LEFDE1:
 
-#endif
-
 #if defined __ELF__ && defined __linux__
        .section        .note.GNU-stack,"",@progbits
 #endif
+#endif
index 675ed03..fed2380 100644 (file)
@@ -30,7 +30,7 @@
 #include <ffi.h>
 #include <powerpc/asm.h>
 
-#ifndef __powerpc64__
+#ifndef POWERPC64
        .globl ffi_prep_args_SYSV
 ENTRY(ffi_call_SYSV)
 .LFB1:
@@ -213,8 +213,8 @@ END(ffi_call_SYSV)
       .uleb128  0x1c
       .align 2
 .LEFDE1:
-#endif
 
 #if defined __ELF__ && defined __linux__
        .section        .note.GNU-stack,"",@progbits
 #endif
+#endif
index e373cbd..a66ee23 100644 (file)
@@ -133,6 +133,10 @@ ffi_status FFI_HIDDEN ffi_prep_cif_core(ffi_cif *cif, ffi_abi abi,
 
   cif->flags = 0;
 
+#if HAVE_LONG_DOUBLE_VARIANT
+  ffi_prep_types (abi);
+#endif
+
   /* Initialize the return type if necessary */
   if ((cif->rtype->size == 0) && (initialize_aggregate(cif->rtype) != FFI_OK))
     return FFI_BAD_TYPEDEF;
index 0a11eb0..0de5994 100644 (file)
@@ -44,6 +44,17 @@ const ffi_type ffi_type_##name = {           \
   id, NULL                                     \
 }
 
+#define FFI_NONCONST_TYPEDEF(name, type, id)   \
+struct struct_align_##name {                   \
+  char c;                                      \
+  type x;                                      \
+};                                             \
+ffi_type ffi_type_##name = {                   \
+  sizeof(type),                                        \
+  offsetof(struct struct_align_##name, x),     \
+  id, NULL                                     \
+}
+
 /* Size and alignment are fake here. They must not be 0. */
 const ffi_type ffi_type_void = {
   1, 1, FFI_TYPE_VOID, NULL
@@ -73,5 +84,9 @@ FFI_TYPEDEF(double, double, FFI_TYPE_DOUBLE);
 # endif
 const ffi_type ffi_type_longdouble = { 16, 16, 4, NULL };
 #elif FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
+# if HAVE_LONG_DOUBLE_VARIANT
+FFI_NONCONST_TYPEDEF(longdouble, long double, FFI_TYPE_LONGDOUBLE);
+# else
 FFI_TYPEDEF(longdouble, long double, FFI_TYPE_LONGDOUBLE);
+# endif
 #endif
index 3afacd2..dc12469 100644 (file)
@@ -158,6 +158,7 @@ FFI_EXEC_TRAMPOLINE_TABLE = @FFI_EXEC_TRAMPOLINE_TABLE@
 FGREP = @FGREP@
 GREP = @GREP@
 HAVE_LONG_DOUBLE = @HAVE_LONG_DOUBLE@
+HAVE_LONG_DOUBLE_VARIANT = @HAVE_LONG_DOUBLE_VARIANT@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
 INSTALL_PROGRAM = @INSTALL_PROGRAM@