I am forwarding this upstream from Debian bug 644338:
  http://bugs.debian.org/644338

Please see the following URL for more information about the port:
  http://wiki.debian.org/PowerPCSPEPort

Your comments, suggestions, and critiques are highly appreciated.

Cheers,
Kyle Moffett

> From: "Moffett, Kyle D" <kyle.d.moff...@boeing.com>
> Date: October 04, 2011 18:12:27 EDT
> To: Debian Bug Tracking System <sub...@bugs.debian.org>
> Subject: libffi: Build errors on PowerPC e500, test-suite failures on PowerPC 
> soft-float
>
> Package: libffi
> Severity: normal
> Tags: patch upstream
> User: debian-powerpc...@breakpoint.cc
> Usertags: powerpcspe
>
> The Debian-Ports "powerpcspe" architecture can't currently build the
> libffi package for a couple reasons:
>
>  (1) The package contains lots of FP assembly instructions even when
>      built on a soft-float target, resulting in compile errors on the
>      Debian powerpcspe architecture (totally different FPU ops).
>
>  (2) The existing soft-float support code has buggy handling of 128-bit
>      values and results in testsuite failures on soft-float and e500
>      (powerpcspe) platforms even when it can be made to compile.
>
> The attached patch resolves both issues.
>
> Cheers,
> Kyle Moffett
>

>From 95d80e11f6d14da32c9e117321658c27155e313a Mon Sep 17 00:00:00 2001

From: Kyle Moffett <kyle.d.moff...@boeing.com>

Date: Tue, 16 Aug 2011 14:46:50 -0400

Subject: [PATCH] PowerPC: Debug and fix soft-floating-point support



There were a wide range of bugs in this code, including long-double

register alignment issues, assignments to global constants (which were

actually stored as non-constant integers).



This passes the testsuite on soft-floating-point PowerPC, and it builds

and passes the testsuite on PowerPC e500 systems which cannot even

assemble the regular floating-point instruction set.



Signed-off-by: Kyle Moffett <kyle.d.moff...@boeing.com>

---

 src/powerpc/ffi.c         |  533 ++++++++++++++++++++++++---------------------

 src/powerpc/ffitarget.h   |   14 +-

 src/powerpc/ppc_closure.S |   19 ++

 src/powerpc/sysv.S        |    6 +

 4 files changed, 310 insertions(+), 262 deletions(-)



diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c

index fb2a39f..e5ec1c5 100644

--- a/src/powerpc/ffi.c

+++ b/src/powerpc/ffi.c

@@ -40,7 +40,9 @@ 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  */

@@ -51,21 +53,20 @@ enum {

   /* Bits (31-24) through (31-19) store shift value for SMST */

 

   FLAG_ARG_NEEDS_COPY   = 1 << (31- 7),

+#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.  */

-unsigned int NUM_GPR_ARG_REGISTERS = 8;

+#define ASM_NEEDS_REGISTERS 4

+#define NUM_GPR_ARG_REGISTERS 8

 #ifndef __NO_FPRS__

-unsigned int NUM_FPR_ARG_REGISTERS = 8;

-#else

-unsigned int NUM_FPR_ARG_REGISTERS = 0;

+# define NUM_FPR_ARG_REGISTERS 8

 #endif

 

-enum { ASM_NEEDS_REGISTERS = 4 };

-

 /* ffi_prep_args_SYSV is called by the assembly routine once stack space

    has been allocated for the function's arguments.

 

@@ -114,10 +115,12 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

   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.  */

@@ -126,9 +129,8 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

   /* 'next_arg' grows up as we put parameters in it.  */

   valp next_arg;

 

-  int i, ii MAYBE_UNUSED;

+  int i;

   ffi_type **ptr;

-  double double_tmp;

   union {

     void **v;

     char **c;

@@ -144,15 +146,16 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

   size_t struct_copy_size;

   unsigned gprvalue;

 

-  if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)

-    NUM_FPR_ARG_REGISTERS = 0;

-

   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.  */

@@ -175,12 +178,28 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

        i > 0;

        i--, ptr++, p_argv.v++)

     {

-      switch ((*ptr)->type)

-       {

+      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.  */

-         if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)

-           goto soft_float_prep;

          double_tmp = **p_argv.f;

          if (fparg_count >= NUM_FPR_ARG_REGISTERS)

            {

@@ -219,43 +238,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

 

 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

        case FFI_TYPE_LONGDOUBLE:

-         if ((ecif->cif->abi != FFI_LINUX)

-               && (ecif->cif->abi != FFI_LINUX_SOFT_FLOAT))

-           goto do_struct;

-         /* 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.  */

-         if (ecif->cif->abi == FFI_LINUX_SOFT_FLOAT)

-           {

-             unsigned int int_tmp = (*p_argv.ui)[0];

-             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;

-                 next_arg.u++;

-                 for (ii = 1; ii < 4; ii++)

-                   {

-                     int_tmp = (*p_argv.ui)[ii];

-                     *next_arg.u = int_tmp;

-                     next_arg.u++;

-                   }

-               }

-             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;

-           }

-         else

-           {

              double_tmp = (*p_argv.d)[0];

 

              if (fparg_count >= NUM_FPR_ARG_REGISTERS - 1)

@@ -281,13 +263,40 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

 

              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:

-       soft_double_prep:

          if (intarg_count == NUM_GPR_ARG_REGISTERS-1)

            intarg_count++;

          if (intarg_count >= NUM_GPR_ARG_REGISTERS)

@@ -320,9 +329,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

          break;

 

        case FFI_TYPE_STRUCT:

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-       do_struct:

-#endif

          struct_copy_size = ((*ptr)->size + 15) & ~0xF;

          copy_space.c -= struct_copy_size;

          memcpy (copy_space.c, *p_argv.c, (*ptr)->size);

@@ -350,7 +356,6 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

        case FFI_TYPE_UINT32:

        case FFI_TYPE_SINT32:

        case FFI_TYPE_POINTER:

-       soft_float_prep:

 

          gprvalue = **p_argv.ui;

 

@@ -367,8 +372,10 @@ ffi_prep_args_SYSV (extended_cif *ecif, unsigned *const 
stack)

   /* 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);

+#ifndef __NO_FPRS__

   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);

 }

 

@@ -605,9 +612,6 @@ ffi_prep_cif_machdep (ffi_cif *cif)

   unsigned type = cif->rtype->type;

   unsigned size = cif->rtype->size;

 

-  if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-    NUM_FPR_ARG_REGISTERS = 0;

-

   if (cif->abi != FFI_LINUX64)

     {

       /* All the machine-independent calculation of cif->bytes will be wrong.

@@ -647,25 +651,38 @@ ffi_prep_cif_machdep (ffi_cif *cif)

      - 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)

     {

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

+#ifndef __NO_FPRS__

     case FFI_TYPE_LONGDOUBLE:

-      if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX64

-       && cif->abi != FFI_LINUX_SOFT_FLOAT)

-       goto byref;

       flags |= FLAG_RETURNS_128BITS;

       /* Fall through.  */

-#endif

     case FFI_TYPE_DOUBLE:

       flags |= FLAG_RETURNS_64BITS;

       /* Fall through.  */

     case FFI_TYPE_FLOAT:

-      /* With FFI_LINUX_SOFT_FLOAT no fp registers are used.  */

-      if (cif->abi != FFI_LINUX_SOFT_FLOAT)

-       flags |= FLAG_RETURNS_FP;

+      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;

@@ -700,9 +717,6 @@ ffi_prep_cif_machdep (ffi_cif *cif)

                }

            }

        }

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-    byref:

-#endif

       intarg_count++;

       flags |= FLAG_RETVAL_REFERENCE;

       /* Fall through.  */

@@ -723,39 +737,36 @@ ffi_prep_cif_machdep (ffi_cif *cif)

        Stuff on the stack needs to keep proper alignment.  */

     for (ptr = cif->arg_types, i = cif->nargs; i > 0; i--, ptr++)

       {

-       switch ((*ptr)->type)

-         {

+       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:

-           /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */

-           if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-             goto soft_float_cif;

            fparg_count++;

            /* floating singles are not 8-aligned on stack */

            break;

 

 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

          case FFI_TYPE_LONGDOUBLE:

-           if (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)

-             goto do_struct;

-           if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-             {

-               if (intarg_count >= NUM_GPR_ARG_REGISTERS - 3

-                 || intarg_count < NUM_GPR_ARG_REGISTERS)

-                 /* A long double in FFI_LINUX_SOFT_FLOAT can use only

-                    a set of four consecutive gprs. If we have not enough,

-                    we have to adjust the intarg_count value.  */

-                 intarg_count += NUM_GPR_ARG_REGISTERS - intarg_count;

-               intarg_count += 4;

-               break;

-             }

-           else

-             fparg_count++;

+           fparg_count++;

            /* Fall thru */

 #endif

          case FFI_TYPE_DOUBLE:

-           /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */

-           if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-             goto soft_double_cif;

            fparg_count++;

            /* If this FP arg is going on the stack, it must be

               8-byte-aligned.  */

@@ -764,10 +775,21 @@ ffi_prep_cif_machdep (ffi_cif *cif)

                && 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:

-         soft_double_cif:

            /* '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

@@ -784,9 +806,6 @@ ffi_prep_cif_machdep (ffi_cif *cif)

            break;

 

          case FFI_TYPE_STRUCT:

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-         do_struct:

-#endif

            /* 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

@@ -794,12 +813,20 @@ ffi_prep_cif_machdep (ffi_cif *cif)

            struct_copy_size += ((*ptr)->size + 15) & ~0xF;

            /* Fall through (allocate space for the pointer).  */

 

-         default:

-         soft_float_cif:

+         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

@@ -828,16 +855,29 @@ ffi_prep_cif_machdep (ffi_cif *cif)

            intarg_count += ((*ptr)->size + 7) / 8;

            break;

 

-         default:

+         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++;

            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)

@@ -845,21 +885,27 @@ ffi_prep_cif_machdep (ffi_cif *cif)

 

   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 (intarg_count > NUM_GPR_ARG_REGISTERS64)

@@ -906,9 +952,11 @@ ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, 
void **avalue)

   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;

@@ -1017,32 +1065,38 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

   void **          avalue;

   ffi_type **      arg_types;

   long             i, avn;

-  long             nf;   /* number of floating registers already used */

-  long             ng;   /* number of general registers already used */

-  ffi_cif *        cif;

-  double           temp;

-  unsigned         size;

+#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;

 

-  cif = closure->cif;

   avalue = alloca (cif->nargs * sizeof (void *));

-  size = cif->rtype->size;

 

-  nf = 0;

-  ng = 0;

+  /* 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 ((cif->rtype->type == FFI_TYPE_STRUCT

-       && !((cif->abi == FFI_SYSV) && (size <= 8)))

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-      || (cif->rtype->type == FFI_TYPE_LONGDOUBLE

-         && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)

-#endif

-      )

-    {

+  if (rtypenum == FFI_TYPE_STRUCT && ((cif->abi != FFI_SYSV) || (size > 8))) {

       rvalue = (void *) *pgr;

       ng++;

       pgr++;

@@ -1053,10 +1107,109 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

   arg_types = cif->arg_types;

 

   /* Grab the addresses of the arguments from the stack frame.  */

-  while (i < avn)

-    {

-      switch (arg_types[i]->type)

-       {

+  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;

+#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)

+           {

+             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:

          /* there are 8 gpr registers used to pass values */

@@ -1092,7 +1245,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

        case FFI_TYPE_SINT32:

        case FFI_TYPE_UINT32:

        case FFI_TYPE_POINTER:

-       soft_float_closure:

          /* there are 8 gpr registers used to pass values */

          if (ng < 8)

            {

@@ -1108,9 +1260,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

          break;

 

        case FFI_TYPE_STRUCT:

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-       do_struct:

-#endif

          /* Structs are passed by reference. The address will appear in a

             gpr if it is one of the first 8 arguments.  */

          if (ng < 8)

@@ -1128,7 +1277,6 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

 

        case FFI_TYPE_SINT64:

        case FFI_TYPE_UINT64:

-       soft_double_closure:

          /* 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)

@@ -1160,99 +1308,8 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

            }

          break;

 

-       case FFI_TYPE_FLOAT:

-         /* With FFI_LINUX_SOFT_FLOAT floats are handled like UINT32.  */

-         if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-           goto soft_float_closure;

-         /* 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)

-           {

-             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:

-         /* With FFI_LINUX_SOFT_FLOAT doubles are handled like UINT64.  */

-         if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-           goto soft_double_closure;

-         /* 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 (cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)

-           goto do_struct;

-         if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-           { /* 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;

-               }

-             break;

-           }

-         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

-

        default:

-         FFI_ASSERT (0);

+               FFI_ASSERT (0);

        }

 

       i++;

@@ -1269,39 +1326,9 @@ ffi_closure_helper_SYSV (ffi_closure *closure, void 
*rvalue,

      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 && cif->rtype->type == FFI_TYPE_STRUCT

-      && size <= 8)

+  if (cif->abi == FFI_SYSV && rtypenum == FFI_TYPE_STRUCT && size <= 8)

     return (FFI_SYSV_TYPE_SMALL_STRUCT - 1) + size;

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-  else if (cif->rtype->type == FFI_TYPE_LONGDOUBLE

-          && cif->abi != FFI_LINUX && cif->abi != FFI_LINUX_SOFT_FLOAT)

-    return FFI_TYPE_STRUCT;

-#endif

-  /* With FFI_LINUX_SOFT_FLOAT floats and doubles are handled like UINT32

-     respectivley UINT64.  */

-  if (cif->abi == FFI_LINUX_SOFT_FLOAT)

-    {

-      switch (cif->rtype->type)

-       {

-       case FFI_TYPE_FLOAT:

-         return FFI_TYPE_UINT32;

-         break;

-       case FFI_TYPE_DOUBLE:

-         return FFI_TYPE_UINT64;

-         break;

-#if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE

-       case FFI_TYPE_LONGDOUBLE:

-         return FFI_TYPE_UINT128;

-         break;

-#endif

-       default:

-         return cif->rtype->type;

-       }

-    }

-  else

-    {

-      return cif->rtype->type;

-    }

+  return rtypenum;

 }

 

 int FFI_HIDDEN ffi_closure_helper_LINUX64 (ffi_closure *, void *,

diff --git a/src/powerpc/ffitarget.h b/src/powerpc/ffitarget.h

index d17f731..820c482 100644

--- a/src/powerpc/ffitarget.h

+++ b/src/powerpc/ffitarget.h

@@ -60,18 +60,14 @@ typedef enum ffi_abi {

   FFI_LINUX64,

   FFI_LINUX,

   FFI_LINUX_SOFT_FLOAT,

-# ifdef POWERPC64

+# if defined(POWERPC64)

   FFI_DEFAULT_ABI = FFI_LINUX64,

-# else

-#  if (!defined(__NO_FPRS__) && (__LDBL_MANT_DIG__ == 106))

-  FFI_DEFAULT_ABI = FFI_LINUX,

-#  else

-#   ifdef __NO_FPRS__

+# elif defined(__NO_FPRS__)

   FFI_DEFAULT_ABI = FFI_LINUX_SOFT_FLOAT,

-#   else

+# elif (__LDBL_MANT_DIG__ == 106)

+  FFI_DEFAULT_ABI = FFI_LINUX,

+# else

   FFI_DEFAULT_ABI = FFI_GCC_SYSV,

-#   endif

-#  endif

 # endif

 #endif

 

diff --git a/src/powerpc/ppc_closure.S b/src/powerpc/ppc_closure.S

index 56f7d1a..41fb885 100644

--- a/src/powerpc/ppc_closure.S

+++ b/src/powerpc/ppc_closure.S

@@ -122,22 +122,41 @@ ENTRY(ffi_closure_SYSV)

        blr

 

 # case FFI_TYPE_FLOAT

+#ifndef __NO_FPRS__

        lfs %f1,112+0(%r1)

        mtlr %r0

        addi %r1,%r1,144

+#else

+       nop

+       nop

+       nop

+#endif

        blr

 

 # case FFI_TYPE_DOUBLE

+#ifndef __NO_FPRS__

        lfd %f1,112+0(%r1)

        mtlr %r0

        addi %r1,%r1,144

+#else

+       nop

+       nop

+       nop

+#endif

        blr

 

 # case FFI_TYPE_LONGDOUBLE

+#ifndef __NO_FPRS__

        lfd %f1,112+0(%r1)

        lfd %f2,112+8(%r1)

        mtlr %r0

        b .Lfinish

+#else

+       nop

+       nop

+       nop

+       blr

+#endif

 

 # case FFI_TYPE_UINT8

        lbz %r3,112+3(%r1)

diff --git a/src/powerpc/sysv.S b/src/powerpc/sysv.S

index 96ea22b..5ee3a19 100644

--- a/src/powerpc/sysv.S

+++ b/src/powerpc/sysv.S

@@ -83,6 +83,7 @@ ENTRY(ffi_call_SYSV)

        nop

 1:

 

+#ifndef __NO_FPRS__

        /* Load all the FP registers.  */

        bf-     6,2f

        lfd     %f1,-16-(8*4)-(8*8)(%r28)

@@ -94,6 +95,7 @@ ENTRY(ffi_call_SYSV)

        lfd     %f6,-16-(8*4)-(3*8)(%r28)

        lfd     %f7,-16-(8*4)-(2*8)(%r28)

        lfd     %f8,-16-(8*4)-(1*8)(%r28)

+#endif

 2:

 

        /* Make the call.  */

@@ -103,7 +105,9 @@ ENTRY(ffi_call_SYSV)

        mtcrf   0x01,%r31 /* cr7  */

        bt-     31,L(small_struct_return_value)

        bt-     30,L(done_return_value)

+#ifndef __NO_FPRS__

        bt-     29,L(fp_return_value)

+#endif

        stw     %r3,0(%r30)

        bf+     28,L(done_return_value)

        stw     %r4,4(%r30)

@@ -124,6 +128,7 @@ L(done_return_value):

        lwz     %r1,0(%r1)

        blr

 

+#ifndef __NO_FPRS__

 L(fp_return_value):

        bf      28,L(float_return_value)

        stfd    %f1,0(%r30)

@@ -134,6 +139,7 @@ L(fp_return_value):

 L(float_return_value):

        stfs    %f1,0(%r30)

        b       L(done_return_value)

+#endif

 

 L(small_struct_return_value):

        extrwi  %r6,%r31,2,19         /* number of bytes padding = shift/8 */

-- 

1.7.2.5



Reply via email to