Commit a74a3aaddbcc730d9aa0a4787f136c9cc19b82b3

Richard Henderson 2014-10-17T01:21:22

arm: Rewrite vfp_type_p Do not modify the ffi_type. Rearrange the tests so that we quickly eliminate structures that cannot match. Return an encoded value of element count and base type.

diff --git a/src/arm/ffi.c b/src/arm/ffi.c
index 5e5ad0a..d00ed89 100644
--- a/src/arm/ffi.c
+++ b/src/arm/ffi.c
@@ -34,7 +34,7 @@
 #include <stdlib.h>
 
 /* Forward declares. */
-static int vfp_type_p (ffi_type *);
+static int vfp_type_p (const ffi_type *);
 static void layout_vfp_args (ffi_cif *);
 
 int ffi_prep_args_SYSV (char *stack, extended_cif *ecif, float *vfp_space);
@@ -141,7 +141,6 @@ ffi_prep_args_VFP (char *stack, extended_cif * ecif, float *vfp_space)
   register ffi_type **p_arg;
   char stack_used = 0;
   char done_with_regs = 0;
-  char is_vfp_type;
 
   /* Make sure we are using FFI_VFP.  */
   FFI_ASSERT (ecif->cif->abi == FFI_VFP);
@@ -164,7 +163,7 @@ ffi_prep_args_VFP (char *stack, extended_cif * ecif, float *vfp_space)
   for (i = ecif->cif->nargs, p_arg = ecif->cif->arg_types;
        (i != 0); i--, p_arg++, p_argv++)
     {
-      is_vfp_type = vfp_type_p (*p_arg);
+      int is_vfp_type = vfp_type_p (*p_arg);
 
       /* Allocated in VFP registers. */
       if (vi < ecif->cif->vfp_nargs && is_vfp_type)
@@ -214,7 +213,6 @@ ffi_prep_args_VFP (char *stack, extended_cif * ecif, float *vfp_space)
 ffi_status
 ffi_prep_cif_machdep (ffi_cif * cif)
 {
-  int type_code;
   /* Round the stack up to a multiple of 8 bytes.  This isn't needed
      everywhere, but it is on some platforms, and it doesn't harm anything
      when it isn't needed.  */
@@ -235,13 +233,25 @@ ffi_prep_cif_machdep (ffi_cif * cif)
       break;
 
     case FFI_TYPE_STRUCT:
-      if (cif->abi == FFI_VFP && (type_code = vfp_type_p (cif->rtype)) != 0)
+      if (cif->abi == FFI_VFP)
 	{
-	  /* A Composite Type passed in VFP registers, either
-	     FFI_TYPE_STRUCT_VFP_FLOAT or FFI_TYPE_STRUCT_VFP_DOUBLE. */
-	  cif->flags = (unsigned) type_code;
+	  int h = vfp_type_p (cif->rtype);
+	  if (h)
+	    {
+	      int ele_count = h >> 8;
+	      int type_code = h & 0xff;
+	      if (ele_count > 1)
+		{
+		  if (type_code == FFI_TYPE_FLOAT)
+		    type_code = FFI_TYPE_STRUCT_VFP_FLOAT;
+		  else
+		    type_code = FFI_TYPE_STRUCT_VFP_DOUBLE;
+		}
+	      cif->flags = type_code;
+	      break;
+	    }
 	}
-      else if (cif->rtype->size <= 4)
+      if (cif->rtype->size <= 4)
 	{
 	  /* A Composite Type not larger than 4 bytes is returned in r0.  */
 	  cif->flags = (unsigned) FFI_TYPE_INT;
@@ -446,7 +456,6 @@ ffi_prep_incoming_args_VFP (char *stack, void **rvalue,
   register ffi_type **p_arg;
   char done_with_regs = 0;
   char stack_used = 0;
-  char is_vfp_type;
 
   FFI_ASSERT (cif->abi == FFI_VFP);
   regp = stack;
@@ -462,8 +471,8 @@ ffi_prep_incoming_args_VFP (char *stack, void **rvalue,
 
   for (i = cif->nargs, p_arg = cif->arg_types; (i != 0); i--, p_arg++)
     {
+      int is_vfp_type = vfp_type_p (*p_arg);
       size_t z;
-      is_vfp_type = vfp_type_p (*p_arg);
 
       if (vi < cif->vfp_nargs && is_vfp_type)
 	{
@@ -820,81 +829,150 @@ ffi_prep_closure_loc (ffi_closure * closure,
 
 /* Below are routines for VFP hard-float support. */
 
+/* A subroutine of vfp_type_p.  Given a structure type, return the type code
+   of the first non-structure element.  Recurse for structure elements.
+   Return -1 if the structure is in fact empty, i.e. no nested elements.  */
+
 static int
-rec_vfp_type_p (ffi_type * t, int *elt, int *elnum)
+is_hfa0 (const ffi_type *ty)
 {
-  switch (t->type)
-    {
-    case FFI_TYPE_FLOAT:
-    case FFI_TYPE_DOUBLE:
-      *elt = (int) t->type;
-      *elnum = 1;
-      return 1;
+  ffi_type **elements = ty->elements;
+  int i, ret = -1;
 
-    case FFI_TYPE_STRUCT_VFP_FLOAT:
-      *elt = FFI_TYPE_FLOAT;
-      *elnum = t->size / sizeof (float);
-      return 1;
+  if (elements != NULL)
+    for (i = 0; elements[i]; ++i)
+      {
+        ret = elements[i]->type;
+        if (ret == FFI_TYPE_STRUCT)
+          {
+            ret = is_hfa0 (elements[i]);
+            if (ret < 0)
+              continue;
+          }
+        break;
+      }
 
-    case FFI_TYPE_STRUCT_VFP_DOUBLE:
-      *elt = FFI_TYPE_DOUBLE;
-      *elnum = t->size / sizeof (double);
-      return 1;
+  return ret;
+}
+
+/* A subroutine of vfp_type_p.  Given a structure type, return true if all
+   of the non-structure elements are the same as CANDIDATE.  */
+
+static int
+is_hfa1 (const ffi_type *ty, int candidate)
+{
+  ffi_type **elements = ty->elements;
+  int i;
 
-    case FFI_TYPE_STRUCT:;
+  if (elements != NULL)
+    for (i = 0; elements[i]; ++i)
       {
-	int base_elt = 0, total_elnum = 0;
-	ffi_type **el = t->elements;
-	while (*el)
-	  {
-	    int el_elt = 0, el_elnum = 0;
-	    if (!rec_vfp_type_p (*el, &el_elt, &el_elnum)
-		|| (base_elt && base_elt != el_elt)
-		|| total_elnum + el_elnum > 4)
-	      return 0;
-	    base_elt = el_elt;
-	    total_elnum += el_elnum;
-	    el++;
-	  }
-	*elnum = total_elnum;
-	*elt = base_elt;
-	return 1;
+        int t = elements[i]->type;
+        if (t == FFI_TYPE_STRUCT)
+          {
+            if (!is_hfa1 (elements[i], candidate))
+              return 0;
+          }
+        else if (t != candidate)
+          return 0;
       }
-    default:;
-    }
-  return 0;
+
+  return 1;
 }
 
+/* Determine if TY is an homogenous floating point aggregate (HFA).
+   That is, a structure consisting of 1 to 4 members of all the same type,
+   where that type is a floating point scalar.
+
+   Returns non-zero iff TY is an HFA.  The result is an encoded value where
+   bits 0-7 contain the type code, and bits 8-10 contain the element count.  */
+
 static int
-vfp_type_p (ffi_type * t)
+vfp_type_p (const ffi_type *ty)
 {
-  int elt, elnum;
-  if (rec_vfp_type_p (t, &elt, &elnum))
+  ffi_type **elements;
+  int candidate, i;
+  size_t size, ele_count;
+
+  /* Quickest tests first.  */
+  switch (ty->type)
     {
-      if (t->type == FFI_TYPE_STRUCT)
-	{
-	  if (elnum == 1)
-	    t->type = elt;
-	  else
-	    t->type = (elt == FFI_TYPE_FLOAT
-		       ? FFI_TYPE_STRUCT_VFP_FLOAT
-		       : FFI_TYPE_STRUCT_VFP_DOUBLE);
-	}
-      return (int) t->type;
+    default:
+      return 0;
+    case FFI_TYPE_FLOAT:
+    case FFI_TYPE_DOUBLE:
+      return 0x100 + ty->type;
+    case FFI_TYPE_STRUCT:
+      break;
     }
-  return 0;
+
+  /* No HFA types are smaller than 4 bytes, or larger than 32 bytes.  */
+  size = ty->size;
+  if (size < 4 || size > 32)
+    return 0;
+
+  /* Find the type of the first non-structure member.  */
+  elements = ty->elements;
+  candidate = elements[0]->type;
+  if (candidate == FFI_TYPE_STRUCT)
+    {
+      for (i = 0; ; ++i)
+        {
+          candidate = is_hfa0 (elements[i]);
+          if (candidate >= 0)
+            break;
+        }
+    }
+
+  /* If the first member is not a floating point type, it's not an HFA.
+     Also quickly re-check the size of the structure.  */
+  switch (candidate)
+    {
+    case FFI_TYPE_FLOAT:
+      ele_count = size / sizeof(float);
+      if (size != ele_count * sizeof(float))
+        return 0;
+      break;
+    case FFI_TYPE_DOUBLE:
+      ele_count = size / sizeof(double);
+      if (size != ele_count * sizeof(double))
+        return 0;
+      break;
+    default:
+      return 0;
+    }
+  if (ele_count > 4)
+    return 0;
+
+  /* Finally, make sure that all scalar elements are the same type.  */
+  for (i = 0; elements[i]; ++i)
+    {
+      if (elements[i]->type == FFI_TYPE_STRUCT)
+        {
+          if (!is_hfa1 (elements[i], candidate))
+            return 0;
+        }
+      else if (elements[i]->type != candidate)
+        return 0;
+    }
+
+  /* All tests succeeded.  Encode the result.  */
+  return (ele_count << 8) | candidate;
 }
 
 static int
-place_vfp_arg (ffi_cif * cif, ffi_type * t)
+place_vfp_arg (ffi_cif *cif, int h)
 {
-  short reg = cif->vfp_reg_free;
-  int nregs = t->size / sizeof (float);
-  int align = ((t->type == FFI_TYPE_STRUCT_VFP_FLOAT
-		|| t->type == FFI_TYPE_FLOAT) ? 1 : 2);
+  unsigned short reg = cif->vfp_reg_free;
+  int align = 1, nregs = h >> 8;
+
+  if ((h & 0xff) == FFI_TYPE_DOUBLE)
+    align = 2, nregs *= 2;
+
   /* Align register number. */
   if ((reg & 1) && align == 2)
     reg++;
+
   while (reg + nregs <= 16)
     {
       int s, new_used = 0;
@@ -940,10 +1018,8 @@ layout_vfp_args (ffi_cif * cif)
 
   for (i = 0; i < cif->nargs; i++)
     {
-      ffi_type *t = cif->arg_types[i];
-      if (vfp_type_p (t) && place_vfp_arg (cif, t) == 1)
-	{
-	  break;
-	}
+      int h = vfp_type_p (cif->arg_types[i]);
+      if (h && place_vfp_arg (cif, h) == 1)
+	break;
     }
 }