Commit 11de69ddb788e4d87ef653898878384116ac16c6

Sergei Trofimovich 2018-02-11T11:29:39

ia64: fix variadic function closures with FP arguments libffi test framework already flagged failures as: ``` FAIL: libffi.call/cls_double_va.c -W -Wall -Wno-psabi -O0 output pattern test, is 7.0 res: 4 0.0 res: 4 ? should match 7.0 ?es: 4 ?.0 res: 4 ``` Failure happens here at ```c // testsuite/libffi.call/cls_double_va.c ... char* format = "%.1f\n"; double doubleArg = 7; ... CHECK(ffi_prep_closure_loc(pcl, &cif, cls_double_va_fn, NULL, code) == FFI_OK); res = ((int(*)(char*, ...))(code))(format, doubleArg); ``` libffi expects 'doubleArg' to be located in 'f9' (second FP argument) but gcc placed it to 'r33' (second GR). ia64 software [1] manual described argument passing ABI in "8.5.2 Register Parameters" as: """ If an actual parameter is known to correspond to a floating-point formal parameter, the following rules apply: a) The actual parameter is passed in the next available floating-point parameter register, if one is available. Floating-point parameter registers are allocated as needed from the range f8-f15, starting with f8. b) If all available floating-point parameter registers have been used, the actual parameter is passed in the appropriate general register(s). (This case can occur only as a result of homogeneous floating-point aggregates, described below.) If a floating-point actual parameter is known to correspond to a variable-argument specification in the formal parameter list, the following rule applies: c) The actual parameter is passed in the appropriate general register(s). If the compiler cannot determine, at the point of call, whether the corresponding formal parameter is a varargs parameter, it must generate code that satisfies both of the above conditions. (The compiler’s determination may be based on prototype declarations, language standard assumptions, analysis, or other user options or information.) """ We have [c] case here and gcc uses only GR for parameter passing. The change binds known variadic arguments ro GRs instead of FPs as those are always expected to be initialized for all variadic call types. This fixes all 10 failures on ia64-unknown-linux-gnu: ``` === libffi Summary === -# of expected passes 1945 -# of unexpected failures 10 + +# of expected passes 1955 ``` [1]: https://www.intel.com/content/dam/www/public/us/en/documents/guides/itanium-software-runtime-architecture-guide.pdf Signed-off-by: Sergei Trofimovich <slyfox@gentoo.org>

diff --git a/src/ia64/ffi.c b/src/ia64/ffi.c
index b77a836..b1d04c3 100644
--- a/src/ia64/ffi.c
+++ b/src/ia64/ffi.c
@@ -220,8 +220,8 @@ hfa_element_type (ffi_type *type, int nested)
 
 /* 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)
 {
   int flags;
 
@@ -271,6 +271,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 int ffi_call_unix (struct ia64_args *, PTR64, void (*)(void), UINT64);
 
 void
@@ -454,10 +470,11 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
   ffi_cif *cif;
   void **avalue;
   ffi_type **p_arg;
-  long i, avn, gpcount, fpcount;
+  long i, avn, gpcount, fpcount, nfixedargs;
 
   cif = closure->cif;
   avn = cif->nargs;
+  nfixedargs = cif->nfixedargs;
   avalue = alloca (avn * sizeof (void *));
 
   /* If the structure return value is passed in memory get that location
@@ -468,6 +485,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
   gpcount = fpcount = 0;
   for (i = 0, p_arg = cif->arg_types; i < avn; i++, p_arg++)
     {
+      int named = i < nfixedargs;
       switch ((*p_arg)->type)
 	{
 	case FFI_TYPE_SINT8:
@@ -491,7 +509,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
 	  break;
 
 	case FFI_TYPE_FLOAT:
-	  if (gpcount < 8 && fpcount < 8)
+	  if (named && gpcount < 8 && fpcount < 8)
 	    {
 	      fpreg *addr = &stack->fp_regs[fpcount++];
 	      float result;
@@ -505,7 +523,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
 	  break;
 
 	case FFI_TYPE_DOUBLE:
-	  if (gpcount < 8 && fpcount < 8)
+	  if (named && gpcount < 8 && fpcount < 8)
 	    {
 	      fpreg *addr = &stack->fp_regs[fpcount++];
 	      double result;
@@ -521,7 +539,7 @@ ffi_closure_unix_inner (ffi_closure *closure, struct ia64_args *stack,
 	case FFI_TYPE_LONGDOUBLE:
 	  if (gpcount & 1)
 	    gpcount++;
-	  if (LDBL_MANT_DIG == 64 && gpcount < 8 && fpcount < 8)
+	  if (LDBL_MANT_DIG == 64 && named && gpcount < 8 && fpcount < 8)
 	    {
 	      fpreg *addr = &stack->fp_regs[fpcount++];
 	      __float80 result;
diff --git a/src/ia64/ffitarget.h b/src/ia64/ffitarget.h
index e68cea6..fd5b9a0 100644
--- a/src/ia64/ffitarget.h
+++ b/src/ia64/ffitarget.h
@@ -50,6 +50,7 @@ typedef enum ffi_abi {
 #define FFI_TRAMPOLINE_SIZE 24  /* Really the following struct, which 	*/
 				/* can be interpreted as a C function	*/
 				/* descriptor:				*/
+#define FFI_TARGET_SPECIFIC_VARIADIC 1
+#define FFI_EXTRA_CIF_FIELDS unsigned nfixedargs
 
 #endif
-