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