Commit 164283f4ac5972ce2ab5e015cc2ab1014c23276c

Alan Modra 2013-11-16T06:38:55

The powerpc64 support opted to pass floating point values both in the fpr area and the parameter save area, necessary when the backend doesn't know if a function argument corresponds to the ellipsis arguments of a variadic function. This patch adds powerpc support for variadic functions, and changes the code to only pass fp in the ABI mandated area. ELFv2 needs this change since the parameter save area may not exist there. This also fixes two faulty tests that used a non-variadic function cast to call a variadic function, and spuriously reasoned that this is somehow necessary for static functions..

diff --git a/ChangeLog b/ChangeLog
index d6bbd59..e7216c9 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,20 @@
+2013-11-16  Alan Modra  <amodra@gmail.com>
+
+	* src/powerpc/ffitarget.h (FFI_TARGET_SPECIFIC_VARIADIC): Define.
+	(FFI_EXTRA_CIF_FIELDS): Define.
+	* src/powerpc/ffi.c (ffi_prep_args64): Save fprs as per the
+	ABI, not to both fpr and param save area.
+	(ffi_prep_cif_machdep_core): Renamed from ffi_prep_cif_machdep.
+	Keep initial flags.  Formatting.  Remove dead FFI_LINUX_SOFT_FLOAT
+	code.
+	(ffi_prep_cif_machdep, ffi_prep_cif_machdep_var): New functions.
+	(ffi_closure_helper_LINUX64): Pass floating point as per ABI,
+	not to both fpr and parameter save areas.
+
+	* libffi/testsuite/libffi.call/cls_double_va.c (main): Correct
+	function cast and don't call ffi_prep_cif.
+	* libffi/testsuite/libffi.call/cls_longdouble_va.c (main): Likewise.
+
 2013-11-15  Andrew Haley  <aph@redhat.com>
 
 	* doc/libffi.texi (Closure Example): Fix the sample code.
diff --git a/src/powerpc/ffi.c b/src/powerpc/ffi.c
index f2c133b..a22ac27 100644
--- a/src/powerpc/ffi.c
+++ b/src/powerpc/ffi.c
@@ -454,9 +454,9 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
   /* 'fpr_base' points at the space for fpr3, and grows upwards as
      we use FPR registers.  */
   valp fpr_base;
-  int fparg_count;
+  unsigned int fparg_count;
 
-  int i, words;
+  unsigned int i, words, nargs, nfixedargs;
   ffi_type **ptr;
   double double_tmp;
   union {
@@ -493,30 +493,34 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
 
   /* Now for the arguments.  */
   p_argv.v = ecif->avalue;
-  for (ptr = ecif->cif->arg_types, i = ecif->cif->nargs;
-       i > 0;
-       i--, ptr++, p_argv.v++)
+  nargs = ecif->cif->nargs;
+  nfixedargs = ecif->cif->nfixedargs;
+  for (ptr = ecif->cif->arg_types, i = 0;
+       i < nargs;
+       i++, ptr++, p_argv.v++)
     {
       switch ((*ptr)->type)
 	{
 	case FFI_TYPE_FLOAT:
 	  double_tmp = **p_argv.f;
-	  *next_arg.f = (float) double_tmp;
+	  if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+	    *fpr_base.d++ = double_tmp;
+	  else
+	    *next_arg.f = (float) double_tmp;
 	  if (++next_arg.ul == gpr_end.ul)
 	    next_arg.ul = rest.ul;
-	  if (fparg_count < NUM_FPR_ARG_REGISTERS64)
-	    *fpr_base.d++ = double_tmp;
 	  fparg_count++;
 	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	  break;
 
 	case FFI_TYPE_DOUBLE:
 	  double_tmp = **p_argv.d;
-	  *next_arg.d = double_tmp;
+	  if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+	    *fpr_base.d++ = double_tmp;
+	  else
+	    *next_arg.d = double_tmp;
 	  if (++next_arg.ul == gpr_end.ul)
 	    next_arg.ul = rest.ul;
-	  if (fparg_count < NUM_FPR_ARG_REGISTERS64)
-	    *fpr_base.d++ = double_tmp;
 	  fparg_count++;
 	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
 	  break;
@@ -524,18 +528,20 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	case FFI_TYPE_LONGDOUBLE:
 	  double_tmp = (*p_argv.d)[0];
-	  *next_arg.d = double_tmp;
+	  if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+	    *fpr_base.d++ = double_tmp;
+	  else
+	    *next_arg.d = double_tmp;
 	  if (++next_arg.ul == gpr_end.ul)
 	    next_arg.ul = rest.ul;
-	  if (fparg_count < NUM_FPR_ARG_REGISTERS64)
-	    *fpr_base.d++ = double_tmp;
 	  fparg_count++;
 	  double_tmp = (*p_argv.d)[1];
-	  *next_arg.d = double_tmp;
+	  if (fparg_count < NUM_FPR_ARG_REGISTERS64 && i < nfixedargs)
+	    *fpr_base.d++ = double_tmp;
+	  else
+	    *next_arg.d = double_tmp;
 	  if (++next_arg.ul == gpr_end.ul)
 	    next_arg.ul = rest.ul;
-	  if (fparg_count < NUM_FPR_ARG_REGISTERS64)
-	    *fpr_base.d++ = double_tmp;
 	  fparg_count++;
 	  FFI_ASSERT (__LDBL_MANT_DIG__ == 106);
 	  FFI_ASSERT (flags & FLAG_FP_ARGUMENTS);
@@ -608,15 +614,14 @@ ffi_prep_args64 (extended_cif *ecif, unsigned long *const stack)
 
 
 /* Perform machine dependent cif processing */
-ffi_status
-ffi_prep_cif_machdep (ffi_cif *cif)
+static ffi_status
+ffi_prep_cif_machdep_core (ffi_cif *cif)
 {
   /* All this is for the SYSV and LINUX64 ABI.  */
-  int i;
   ffi_type **ptr;
   unsigned bytes;
-  int fparg_count = 0, intarg_count = 0;
-  unsigned flags = 0;
+  unsigned i, fparg_count = 0, intarg_count = 0;
+  unsigned flags = cif->flags;
   unsigned struct_copy_size = 0;
   unsigned type = cif->rtype->type;
   unsigned size = cif->rtype->size;
@@ -661,19 +666,23 @@ ffi_prep_cif_machdep (ffi_cif *cif)
      - 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 (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;
+      if (type == FFI_TYPE_LONGDOUBLE)
+	type = FFI_TYPE_STRUCT;
 #endif
-  }
+    }
 
   switch (type)
     {
@@ -848,13 +857,8 @@ ffi_prep_cif_machdep (ffi_cif *cif)
 	  {
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	  case FFI_TYPE_LONGDOUBLE:
-	    if (cif->abi == FFI_LINUX_SOFT_FLOAT)
-	      intarg_count += 4;
-	    else
-	      {
-		fparg_count += 2;
-		intarg_count += 2;
-	      }
+	    fparg_count += 2;
+	    intarg_count += 2;
 	    break;
 #endif
 	  case FFI_TYPE_FLOAT:
@@ -936,6 +940,22 @@ ffi_prep_cif_machdep (ffi_cif *cif)
   return FFI_OK;
 }
 
+ffi_status
+ffi_prep_cif_machdep (ffi_cif *cif)
+{
+  cif->nfixedargs = cif->nargs;
+  return ffi_prep_cif_machdep_core (cif);
+}
+
+ffi_status
+ffi_prep_cif_machdep_var (ffi_cif *cif,
+			  unsigned int nfixedargs,
+			  unsigned int ntotalargs MAYBE_UNUSED)
+{
+  cif->nfixedargs = nfixedargs;
+  return ffi_prep_cif_machdep_core (cif);
+}
+
 extern void ffi_call_SYSV(extended_cif *, unsigned, unsigned, unsigned *,
 			  void (*fn)(void));
 extern void FFI_HIDDEN ffi_call_LINUX64(extended_cif *, unsigned long,
@@ -1376,7 +1396,7 @@ ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
 
   void **avalue;
   ffi_type **arg_types;
-  long i, avn;
+  unsigned long i, avn, nfixedargs;
   ffi_cif *cif;
   ffi_dblfl *end_pfr = pfr + NUM_FPR_ARG_REGISTERS64;
 
@@ -1393,6 +1413,7 @@ ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
 
   i = 0;
   avn = cif->nargs;
+  nfixedargs = cif->nfixedargs;
   arg_types = cif->arg_types;
 
   /* Grab the addresses of the arguments from the stack frame.  */
@@ -1451,7 +1472,7 @@ ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
 
 	  /* there are 13 64bit floating point registers */
 
-	  if (pfr < end_pfr)
+	  if (pfr < end_pfr && i < nfixedargs)
 	    {
 	      double temp = pfr->d;
 	      pfr->f = (float) temp;
@@ -1467,7 +1488,7 @@ ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
 	  /* On the outgoing stack all values are aligned to 8 */
 	  /* there are 13 64bit floating point registers */
 
-	  if (pfr < end_pfr)
+	  if (pfr < end_pfr && i < nfixedargs)
 	    {
 	      avalue[i] = pfr;
 	      pfr++;
@@ -1479,14 +1500,14 @@ ffi_closure_helper_LINUX64 (ffi_closure *closure, void *rvalue,
 
 #if FFI_TYPE_LONGDOUBLE != FFI_TYPE_DOUBLE
 	case FFI_TYPE_LONGDOUBLE:
-	  if (pfr + 1 < end_pfr)
+	  if (pfr + 1 < end_pfr && i + 1 < nfixedargs)
 	    {
 	      avalue[i] = pfr;
 	      pfr += 2;
 	    }
 	  else
 	    {
-	      if (pfr < end_pfr)
+	      if (pfr < end_pfr && i < nfixedargs)
 		{
 		  /* Passed partly in f13 and partly on the stack.
 		     Move it all to the stack.  */
diff --git a/src/powerpc/ffitarget.h b/src/powerpc/ffitarget.h
index 3c9db49..2a7e9a1 100644
--- a/src/powerpc/ffitarget.h
+++ b/src/powerpc/ffitarget.h
@@ -106,6 +106,10 @@ typedef enum ffi_abi {
 
 #define FFI_CLOSURES 1
 #define FFI_NATIVE_RAW_API 0
+#if defined (POWERPC) || defined (POWERPC_FREEBSD)
+# define FFI_TARGET_SPECIFIC_VARIADIC 1
+# define FFI_EXTRA_CIF_FIELDS unsigned nfixedargs
+#endif
 
 /* For additional types like the below, take care about the order in
    ppc_closures.S. They must follow after the FFI_TYPE_LAST.  */
diff --git a/testsuite/libffi.call/cls_double_va.c b/testsuite/libffi.call/cls_double_va.c
index 43167b6..e077f92 100644
--- a/testsuite/libffi.call/cls_double_va.c
+++ b/testsuite/libffi.call/cls_double_va.c
@@ -38,7 +38,7 @@ int main (void)
 
 	/* This printf call is variadic */
 	CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint,
-		arg_types) == FFI_OK);
+			       arg_types) == FFI_OK);
 
 	args[0] = &format;
 	args[1] = &doubleArg;
@@ -49,12 +49,10 @@ int main (void)
 	printf("res: %d\n", (int) res);
 	/* { dg-output "\nres: 4" } */
 
-	/* The call to cls_double_va_fn is static, so have to use a normal prep_cif */
-	CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint, arg_types) == FFI_OK);
+	CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL,
+				   code) == FFI_OK);
 
-	CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL, code) == FFI_OK);
-
-	res	= ((int(*)(char*, double))(code))(format, doubleArg);
+	res = ((int(*)(char*, ...))(code))(format, doubleArg);
 	/* { dg-output "\n7.0" } */
 	printf("res: %d\n", (int) res);
 	/* { dg-output "\nres: 4" } */
diff --git a/testsuite/libffi.call/cls_longdouble_va.c b/testsuite/libffi.call/cls_longdouble_va.c
index 7126b13..39b438b 100644
--- a/testsuite/libffi.call/cls_longdouble_va.c
+++ b/testsuite/libffi.call/cls_longdouble_va.c
@@ -38,7 +38,7 @@ int main (void)
 
 	/* This printf call is variadic */
 	CHECK(ffi_prep_cif_var(&cif, FFI_DEFAULT_ABI, 1, 2, &ffi_type_sint,
-		arg_types) == FFI_OK);
+			       arg_types) == FFI_OK);
 
 	args[0] = &format;
 	args[1] = &ldArg;
@@ -49,13 +49,10 @@ int main (void)
 	printf("res: %d\n", (int) res);
 	/* { dg-output "\nres: 4" } */
 
-	/* The call to cls_longdouble_va_fn is static, so have to use a normal prep_cif */
-	CHECK(ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 2, &ffi_type_sint,
-		arg_types) == FFI_OK);
+	CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL,
+				   code) == FFI_OK);
 
-	CHECK(ffi_prep_closure_loc(pcl, &cif, cls_longdouble_va_fn, NULL, code) == FFI_OK);
-
-	res	= ((int(*)(char*, long double))(code))(format, ldArg);
+	res = ((int(*)(char*, ...))(code))(format, ldArg);
 	/* { dg-output "\n7.0" } */
 	printf("res: %d\n", (int) res);
 	/* { dg-output "\nres: 4" } */