From 6078f50a123e3801db6d548dfcb5131677b3baae Mon Sep 17 00:00:00 2001
From: Uros Bizjak <ubizjak@gmail.com>
Date: Fri, 4 Dec 2009 19:41:59 +0100
Subject: [PATCH] re PR libffi/41908 (closures fail for some structure
 arguments containing floats)

	PR libffi/41908
	* src/x86/ffi64.c (classify_argument): Update from
	gcc/config/i386/i386.c.
	(ffi_closure_unix64_inner): Do not use the address of two consecutive
	SSE registers directly.
	* testsuite/libffi.call/cls_dbls_struct.c (main): Remove xfail
	for x86_64 linux targets.

From-SVN: r154988
---
 libffi/ChangeLog                              | 10 +++
 libffi/src/x86/ffi64.c                        | 88 +++++++++++++++----
 .../testsuite/libffi.call/cls_dbls_struct.c   |  2 +-
 3 files changed, 82 insertions(+), 18 deletions(-)

diff --git a/libffi/ChangeLog b/libffi/ChangeLog
index b2cef85eba80..7c877a1f4cca 100644
--- a/libffi/ChangeLog
+++ b/libffi/ChangeLog
@@ -1,3 +1,13 @@
+2009-12-04  Uros Bizjak  <ubizjak@gmail.com>
+
+	PR libffi/41908
+	* src/x86/ffi64.c (classify_argument): Update from
+	gcc/config/i386/i386.c.
+	(ffi_closure_unix64_inner): Do not use the address of two consecutive
+	SSE registers directly.
+	* testsuite/libffi.call/cls_dbls_struct.c (main): Remove xfail
+	for x86_64 linux targets.
+
 2009-12-04  David Edelsohn  <edelsohn@gnu.org>
 
 	* src/powerpc/ffi_darwin.c (ffi_closure_helper_DARWIN): Increment
diff --git a/libffi/src/x86/ffi64.c b/libffi/src/x86/ffi64.c
index 116c636598d8..51ada0e879fa 100644
--- a/libffi/src/x86/ffi64.c
+++ b/libffi/src/x86/ffi64.c
@@ -145,13 +145,35 @@ classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
     case FFI_TYPE_UINT64:
     case FFI_TYPE_SINT64:
     case FFI_TYPE_POINTER:
-      if (byte_offset + type->size <= 4)
-	classes[0] = X86_64_INTEGERSI_CLASS;
-      else
-	classes[0] = X86_64_INTEGER_CLASS;
-      return 1;
+      {
+	int size = byte_offset + type->size;
+
+	if (size <= 4)
+	  {
+	    classes[0] = X86_64_INTEGERSI_CLASS;
+	    return 1;
+	  }
+	else if (size <= 8)
+	  {
+	    classes[0] = X86_64_INTEGER_CLASS;
+	    return 1;
+	  }
+	else if (size <= 12)
+	  {
+	    classes[0] = X86_64_INTEGER_CLASS;
+	    classes[1] = X86_64_INTEGERSI_CLASS;
+	    return 2;
+	  }
+	else if (size <= 16)
+	  {
+	    classes[0] = classes[1] = X86_64_INTEGERSI_CLASS;
+	    return 2;
+	  }
+	else
+	  FFI_ASSERT (0);
+      }
     case FFI_TYPE_FLOAT:
-      if (byte_offset == 0)
+      if (!(byte_offset % 8))
 	classes[0] = X86_64_SSESF_CLASS;
       else
 	classes[0] = X86_64_SSE_CLASS;
@@ -171,13 +193,21 @@ classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
 	int i;
 	enum x86_64_reg_class subclasses[MAX_CLASSES];
 
-	/* If the struct is larger than 16 bytes, pass it on the stack.  */
-	if (type->size > 16)
+	/* If the struct is larger than 32 bytes, pass it on the stack.  */
+	if (type->size > 32)
 	  return 0;
 
 	for (i = 0; i < words; i++)
 	  classes[i] = X86_64_NO_CLASS;
 
+	/* Zero sized arrays or structures are NO_CLASS.  We return 0 to
+	   signalize memory class, so handle it as special case.  */
+	if (!words)
+	  {
+	    classes[0] = X86_64_NO_CLASS;
+	    return 1;
+	  }
+
 	/* Merge the fields of structure.  */
 	for (ptr = type->elements; *ptr != NULL; ptr++)
 	  {
@@ -198,6 +228,20 @@ classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
 	    byte_offset += (*ptr)->size;
 	  }
 
+	if (words > 2)
+	  {
+	    /* When size > 16 bytes, if the first one isn't
+	       X86_64_SSE_CLASS or any other ones aren't
+	       X86_64_SSEUP_CLASS, everything should be passed in
+	       memory.  */
+	    if (classes[0] != X86_64_SSE_CLASS)
+	      return 0;
+
+	    for (i = 1; i < words; i++)
+	      if (classes[i] != X86_64_SSEUP_CLASS)
+		return 0;
+	  }
+
 	/* Final merger cleanup.  */
 	for (i = 0; i < words; i++)
 	  {
@@ -207,15 +251,25 @@ classify_argument (ffi_type *type, enum x86_64_reg_class classes[],
 	      return 0;
 
 	    /* The X86_64_SSEUP_CLASS should be always preceded by
-	       X86_64_SSE_CLASS.  */
+	       X86_64_SSE_CLASS or X86_64_SSEUP_CLASS.  */
 	    if (classes[i] == X86_64_SSEUP_CLASS
-		&& (i == 0 || classes[i - 1] != X86_64_SSE_CLASS))
-	      classes[i] = X86_64_SSE_CLASS;
+		&& classes[i - 1] != X86_64_SSE_CLASS
+		&& classes[i - 1] != X86_64_SSEUP_CLASS)
+	      {
+		/* The first one should never be X86_64_SSEUP_CLASS.  */
+		FFI_ASSERT (i != 0);
+		classes[i] = X86_64_SSE_CLASS;
+	      }
 
-	    /*  X86_64_X87UP_CLASS should be preceded by X86_64_X87_CLASS.  */
+	    /*  If X86_64_X87UP_CLASS isn't preceded by X86_64_X87_CLASS,
+		everything should be passed in memory.  */
 	    if (classes[i] == X86_64_X87UP_CLASS
-		&& (i == 0 || classes[i - 1] != X86_64_X87_CLASS))
-	      classes[i] = X86_64_SSE_CLASS;
+		&& (classes[i - 1] != X86_64_X87_CLASS))
+	      {
+		/* The first one should never be X86_64_X87UP_CLASS.  */
+		FFI_ASSERT (i != 0);
+		return 0;
+	      }
 	  }
 	return words;
       }
@@ -528,10 +582,10 @@ ffi_closure_unix64_inner(ffi_closure *closure, void *rvalue,
 	  argp += arg_types[i]->size;
 	}
       /* If the argument is in a single register, or two consecutive
-	 registers, then we can use that address directly.  */
+	 integer registers, then we can use that address directly.  */
       else if (n == 1
-	       || (n == 2
-		   && SSE_CLASS_P (classes[0]) == SSE_CLASS_P (classes[1])))
+	       || (n == 2 && !(SSE_CLASS_P (classes[0])
+			       || SSE_CLASS_P (classes[1]))))
 	{
 	  /* The argument is in a single register.  */
 	  if (SSE_CLASS_P (classes[0]))
diff --git a/libffi/testsuite/libffi.call/cls_dbls_struct.c b/libffi/testsuite/libffi.call/cls_dbls_struct.c
index fcf48b79237d..660dabb883b1 100644
--- a/libffi/testsuite/libffi.call/cls_dbls_struct.c
+++ b/libffi/testsuite/libffi.call/cls_dbls_struct.c
@@ -57,7 +57,7 @@ int main(int argc __UNUSED__, char** argv __UNUSED__)
 	CHECK(ffi_prep_closure_loc(pcl, &cif, closure_test_gn, NULL, code) == FFI_OK);
 
 	((void*(*)(Dbls))(code))(arg);
-	/* { dg-output "1.0 2.0\n" { xfail x86_64-*-linux-* } } */
+	/* { dg-output "1.0 2.0\n" } */
 
 	closure_test_fn(arg);
 	/* { dg-output "1.0 2.0\n" } */
-- 
GitLab