[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Bug#644338: Fwd: libffi: Build errors on PowerPC e500, test-suite failures on PowerPC soft-float



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.Moffett@boeing.com>
> Date: October 04, 2011 18:12:27 EDT
> To: Debian Bug Tracking System <submit@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-powerpcspe@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.Moffett@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.Moffett@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 to: