This is the mail archive of the gdb-patches@sourceware.org mailing list for the GDB project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

ARM support for the hard-VFP ABI


This patch, from Joseph Myers, implements the hardware VFP variant of
the ARM EABI (-mfloat-abi=hard in GCC, recently implemented).

It's been in our tree for several months, and I retested it on ARM
GNU/Linux before checking it in - it did need a minor update for the
return-nodebug tests.

-- 
Daniel Jacobowitz
CodeSourcery

2009-07-28  Joseph Myers  <joseph@codesourcery.com>

	* arm-tdep.c (enum arm_vfp_cprc_base_type,
	arm_vfp_cprc_unit_length, arm_vfp_cprc_reg_char,
	arm_vfp_cprc_sub_candidate, arm_vfp_call_candidate,
	arm_vfp_abi_for_function): New.
	(arm_push_dummy_call): Handle VFP ABI.
	(arm_extract_return_value, arm_store_return_value): Handle
	ARM_FLOAT_VFP case.
	(arm_return_value): Handle VFP ABI.
	(arm_gdbarch_init): Default fp_model based on Tag_ABI_VFP_args for
	EABI binaries.

---
 gdb/arm-tdep.c |  360 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 354 insertions(+), 6 deletions(-)

Index: gdb/arm-tdep.c
===================================================================
--- gdb/arm-tdep.c.orig	2009-07-23 12:51:57.000000000 -0700
+++ gdb/arm-tdep.c	2009-07-27 10:07:43.000000000 -0700
@@ -1373,8 +1373,222 @@ arm_type_align (struct type *t)
     }
 }
 
-/* We currently only support passing parameters in integer registers.  This
-   conforms with GCC's default model.  Several other variants exist and
+/* Possible base types for a candidate for passing and returning in
+   VFP registers.  */
+
+enum arm_vfp_cprc_base_type
+{
+  VFP_CPRC_UNKNOWN,
+  VFP_CPRC_SINGLE,
+  VFP_CPRC_DOUBLE,
+  VFP_CPRC_VEC64,
+  VFP_CPRC_VEC128
+};
+
+/* The length of one element of base type B.  */
+
+static unsigned
+arm_vfp_cprc_unit_length (enum arm_vfp_cprc_base_type b)
+{
+  switch (b)
+    {
+    case VFP_CPRC_SINGLE:
+      return 4;
+    case VFP_CPRC_DOUBLE:
+      return 8;
+    case VFP_CPRC_VEC64:
+      return 8;
+    case VFP_CPRC_VEC128:
+      return 16;
+    default:
+      internal_error (__FILE__, __LINE__, _("Invalid VFP CPRC type: %d."),
+		      (int) b);
+    }
+}
+
+/* The character ('s', 'd' or 'q') for the type of VFP register used
+   for passing base type B.  */
+
+static int
+arm_vfp_cprc_reg_char (enum arm_vfp_cprc_base_type b)
+{
+  switch (b)
+    {
+    case VFP_CPRC_SINGLE:
+      return 's';
+    case VFP_CPRC_DOUBLE:
+      return 'd';
+    case VFP_CPRC_VEC64:
+      return 'd';
+    case VFP_CPRC_VEC128:
+      return 'q';
+    default:
+      internal_error (__FILE__, __LINE__, _("Invalid VFP CPRC type: %d."),
+		      (int) b);
+    }
+}
+
+/* Determine whether T may be part of a candidate for passing and
+   returning in VFP registers, ignoring the limit on the total number
+   of components.  If *BASE_TYPE is VFP_CPRC_UNKNOWN, set it to the
+   classification of the first valid component found; if it is not
+   VFP_CPRC_UNKNOWN, all components must have the same classification
+   as *BASE_TYPE.  If it is found that T contains a type not permitted
+   for passing and returning in VFP registers, a type differently
+   classified from *BASE_TYPE, or two types differently classified
+   from each other, return -1, otherwise return the total number of
+   base-type elements found (possibly 0 in an empty structure or
+   array).  Vectors and complex types are not currently supported,
+   matching the generic AAPCS support.  */
+
+static int
+arm_vfp_cprc_sub_candidate (struct type *t,
+			    enum arm_vfp_cprc_base_type *base_type)
+{
+  t = check_typedef (t);
+  switch (TYPE_CODE (t))
+    {
+    case TYPE_CODE_FLT:
+      switch (TYPE_LENGTH (t))
+	{
+	case 4:
+	  if (*base_type == VFP_CPRC_UNKNOWN)
+	    *base_type = VFP_CPRC_SINGLE;
+	  else if (*base_type != VFP_CPRC_SINGLE)
+	    return -1;
+	  return 1;
+
+	case 8:
+	  if (*base_type == VFP_CPRC_UNKNOWN)
+	    *base_type = VFP_CPRC_DOUBLE;
+	  else if (*base_type != VFP_CPRC_DOUBLE)
+	    return -1;
+	  return 1;
+
+	default:
+	  return -1;
+	}
+      break;
+
+    case TYPE_CODE_ARRAY:
+      {
+	int count;
+	unsigned unitlen;
+	count = arm_vfp_cprc_sub_candidate (TYPE_TARGET_TYPE (t), base_type);
+	if (count == -1)
+	  return -1;
+	if (TYPE_LENGTH (t) == 0)
+	  {
+	    gdb_assert (count == 0);
+	    return 0;
+	  }
+	else if (count == 0)
+	  return -1;
+	unitlen = arm_vfp_cprc_unit_length (*base_type);
+	gdb_assert ((TYPE_LENGTH (t) % unitlen) == 0);
+	return TYPE_LENGTH (t) / unitlen;
+      }
+      break;
+
+    case TYPE_CODE_STRUCT:
+      {
+	int count = 0;
+	unsigned unitlen;
+	int i;
+	for (i = 0; i < TYPE_NFIELDS (t); i++)
+	  {
+	    int sub_count = arm_vfp_cprc_sub_candidate (TYPE_FIELD_TYPE (t, i),
+							base_type);
+	    if (sub_count == -1)
+	      return -1;
+	    count += sub_count;
+	  }
+	if (TYPE_LENGTH (t) == 0)
+	  {
+	    gdb_assert (count == 0);
+	    return 0;
+	  }
+	else if (count == 0)
+	  return -1;
+	unitlen = arm_vfp_cprc_unit_length (*base_type);
+	if (TYPE_LENGTH (t) != unitlen * count)
+	  return -1;
+	return count;
+      }
+
+    case TYPE_CODE_UNION:
+      {
+	int count = 0;
+	unsigned unitlen;
+	int i;
+	for (i = 0; i < TYPE_NFIELDS (t); i++)
+	  {
+	    int sub_count = arm_vfp_cprc_sub_candidate (TYPE_FIELD_TYPE (t, i),
+							base_type);
+	    if (sub_count == -1)
+	      return -1;
+	    count = (count > sub_count ? count : sub_count);
+	  }
+	if (TYPE_LENGTH (t) == 0)
+	  {
+	    gdb_assert (count == 0);
+	    return 0;
+	  }
+	else if (count == 0)
+	  return -1;
+	unitlen = arm_vfp_cprc_unit_length (*base_type);
+	if (TYPE_LENGTH (t) != unitlen * count)
+	  return -1;
+	return count;
+      }
+
+    default:
+      break;
+    }
+
+  return -1;
+}
+
+/* Determine whether T is a VFP co-processor register candidate (CPRC)
+   if passed to or returned from a non-variadic function with the VFP
+   ABI in effect.  Return 1 if it is, 0 otherwise.  If it is, set
+   *BASE_TYPE to the base type for T and *COUNT to the number of
+   elements of that base type before returning.  */
+
+static int
+arm_vfp_call_candidate (struct type *t, enum arm_vfp_cprc_base_type *base_type,
+			int *count)
+{
+  enum arm_vfp_cprc_base_type b = VFP_CPRC_UNKNOWN;
+  int c = arm_vfp_cprc_sub_candidate (t, &b);
+  if (c <= 0 || c > 4)
+    return 0;
+  *base_type = b;
+  *count = c;
+  return 1;
+}
+
+/* Return 1 if the VFP ABI should be used for passing arguments to and
+   returning values from a function of type FUNC_TYPE, 0
+   otherwise.  */
+
+static int
+arm_vfp_abi_for_function (struct gdbarch *gdbarch, struct type *func_type)
+{
+  struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  /* Variadic functions always use the base ABI.  Assume that functions
+     without debug info are not variadic.  */
+  if (func_type && TYPE_VARARGS (check_typedef (func_type)))
+    return 0;
+  /* The VFP ABI is only supported as a variant of AAPCS.  */
+  if (tdep->arm_abi != ARM_ABI_AAPCS)
+    return 0;
+  return gdbarch_tdep (gdbarch)->fp_model == ARM_FLOAT_VFP;
+}
+
+/* We currently only support passing parameters in integer registers, which
+   conforms with GCC's default model, and VFP argument passing following
+   the VFP variant of AAPCS.  Several other variants exist and
    we should probably support some of them based on the selected ABI.  */
 
 static CORE_ADDR
@@ -1388,6 +1602,16 @@ arm_push_dummy_call (struct gdbarch *gdb
   int argreg;
   int nstack;
   struct stack_item *si = NULL;
+  int use_vfp_abi;
+  struct type *ftype;
+  unsigned vfp_regs_free = (1 << 16) - 1;
+
+  /* Determine the type of this function and whether the VFP ABI
+     applies.  */
+  ftype = check_typedef (value_type (function));
+  if (TYPE_CODE (ftype) == TYPE_CODE_PTR)
+    ftype = check_typedef (TYPE_TARGET_TYPE (ftype));
+  use_vfp_abi = arm_vfp_abi_for_function (gdbarch, ftype);
 
   /* Set the return address.  For the ARM, the return breakpoint is
      always at BP_ADDR.  */
@@ -1423,6 +1647,9 @@ arm_push_dummy_call (struct gdbarch *gdb
       enum type_code typecode;
       bfd_byte *val;
       int align;
+      enum arm_vfp_cprc_base_type vfp_base_type;
+      int vfp_base_count;
+      int may_use_core_reg = 1;
 
       arg_type = check_typedef (value_type (args[argnum]));
       len = TYPE_LENGTH (arg_type);
@@ -1446,6 +1673,59 @@ arm_push_dummy_call (struct gdbarch *gdb
 	    align = INT_REGISTER_SIZE * 2;
 	}
 
+      if (use_vfp_abi
+	  && arm_vfp_call_candidate (arg_type, &vfp_base_type,
+				     &vfp_base_count))
+	{
+	  int regno;
+	  int unit_length;
+	  int shift;
+	  unsigned mask;
+
+	  /* Because this is a CPRC it cannot go in a core register or
+	     cause a core register to be skipped for alignment.
+	     Either it goes in VFP registers and the rest of this loop
+	     iteration is skipped for this argument, or it goes on the
+	     stack (and the stack alignment code is correct for this
+	     case).  */
+	  may_use_core_reg = 0;
+
+	  unit_length = arm_vfp_cprc_unit_length (vfp_base_type);
+	  shift = unit_length / 4;
+	  mask = (1 << (shift * vfp_base_count)) - 1;
+	  for (regno = 0; regno < 16; regno += shift)
+	    if (((vfp_regs_free >> regno) & mask) == mask)
+	      break;
+
+	  if (regno < 16)
+	    {
+	      int reg_char;
+	      int reg_scaled;
+	      int i;
+
+	      vfp_regs_free &= ~(mask << regno);
+	      reg_scaled = regno / shift;
+	      reg_char = arm_vfp_cprc_reg_char (vfp_base_type);
+	      for (i = 0; i < vfp_base_count; i++)
+		{
+		  char name_buf[4];
+		  int regnum;
+		  sprintf (name_buf, "%c%d", reg_char, reg_scaled + i);
+		  regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+							strlen (name_buf));
+		  regcache_cooked_write (regcache, regnum,
+					 val + i * unit_length);
+		}
+	      continue;
+	    }
+	  else
+	    {
+	      /* This CPRC could not go in VFP registers, so all VFP
+		 registers are now marked as used.  */
+	      vfp_regs_free = 0;
+	    }
+	}
+
       /* Push stack padding for dowubleword alignment.  */
       if (nstack & (align - 1))
 	{
@@ -1454,7 +1734,8 @@ arm_push_dummy_call (struct gdbarch *gdb
 	}
       
       /* Doubleword aligned quantities must go in even register pairs.  */
-      if (argreg <= ARM_LAST_ARG_REGNUM
+      if (may_use_core_reg
+	  && argreg <= ARM_LAST_ARG_REGNUM
 	  && align > INT_REGISTER_SIZE
 	  && argreg & 1)
 	argreg++;
@@ -1482,7 +1763,7 @@ arm_push_dummy_call (struct gdbarch *gdb
 	{
 	  int partial_len = len < INT_REGISTER_SIZE ? len : INT_REGISTER_SIZE;
 
-	  if (argreg <= ARM_LAST_ARG_REGNUM)
+	  if (may_use_core_reg && argreg <= ARM_LAST_ARG_REGNUM)
 	    {
 	      /* The argument is being passed in a general purpose
 		 register.  */
@@ -2511,6 +2792,9 @@ arm_extract_return_value (struct type *t
 
 	case ARM_FLOAT_SOFT_FPA:
 	case ARM_FLOAT_SOFT_VFP:
+	  /* ARM_FLOAT_VFP can arise if this is a variadic function so
+	     not using the VFP ABI code.  */
+	case ARM_FLOAT_VFP:
 	  regcache_cooked_read (regs, ARM_A1_REGNUM, valbuf);
 	  if (TYPE_LENGTH (type) > 4)
 	    regcache_cooked_read (regs, ARM_A1_REGNUM + 1,
@@ -2697,6 +2981,9 @@ arm_store_return_value (struct type *typ
 
 	case ARM_FLOAT_SOFT_FPA:
 	case ARM_FLOAT_SOFT_VFP:
+	  /* ARM_FLOAT_VFP can arise if this is a variadic function so
+	     not using the VFP ABI code.  */
+	case ARM_FLOAT_VFP:
 	  regcache_cooked_write (regs, ARM_A1_REGNUM, valbuf);
 	  if (TYPE_LENGTH (type) > 4)
 	    regcache_cooked_write (regs, ARM_A1_REGNUM + 1, 
@@ -2772,6 +3059,31 @@ arm_return_value (struct gdbarch *gdbarc
 		  gdb_byte *readbuf, const gdb_byte *writebuf)
 {
   struct gdbarch_tdep *tdep = gdbarch_tdep (gdbarch);
+  enum arm_vfp_cprc_base_type vfp_base_type;
+  int vfp_base_count;
+
+  if (arm_vfp_abi_for_function (gdbarch, func_type)
+      && arm_vfp_call_candidate (valtype, &vfp_base_type, &vfp_base_count))
+    {
+      int reg_char = arm_vfp_cprc_reg_char (vfp_base_type);
+      int unit_length = arm_vfp_cprc_unit_length (vfp_base_type);
+      int i;
+      for (i = 0; i < vfp_base_count; i++)
+	{
+	  char name_buf[4];
+	  int regnum;
+	  sprintf (name_buf, "%c%d", reg_char, i);
+	  regnum = user_reg_map_name_to_regnum (gdbarch, name_buf,
+						strlen (name_buf));
+	  if (writebuf)
+	    regcache_cooked_write (regcache, regnum,
+				   writebuf + i * unit_length);
+	  if (readbuf)
+	    regcache_cooked_read (regcache, regnum,
+				  readbuf + i * unit_length);
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
 
   if (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
       || TYPE_CODE (valtype) == TYPE_CODE_UNION
@@ -3347,9 +3659,45 @@ arm_gdbarch_init (struct gdbarch_info in
 		case EF_ARM_EABI_VER4:
 		case EF_ARM_EABI_VER5:
 		  arm_abi = ARM_ABI_AAPCS;
-		  /* EABI binaries default to VFP float ordering.  */
+		  /* EABI binaries default to VFP float ordering.
+		     They may also contain build attributes that can
+		     be used to identify if the VFP argument-passing
+		     ABI is in use.  */
 		  if (fp_model == ARM_FLOAT_AUTO)
-		    fp_model = ARM_FLOAT_SOFT_VFP;
+		    {
+#ifdef HAVE_ELF
+		      switch (bfd_elf_get_obj_attr_int (info.abfd,
+							OBJ_ATTR_PROC,
+							Tag_ABI_VFP_args))
+			{
+			case 0:
+			  /* "The user intended FP parameter/result
+			     passing to conform to AAPCS, base
+			     variant".  */
+			  fp_model = ARM_FLOAT_SOFT_VFP;
+			  break;
+			case 1:
+			  /* "The user intended FP parameter/result
+			     passing to conform to AAPCS, VFP
+			     variant".  */
+			  fp_model = ARM_FLOAT_VFP;
+			  break;
+			case 2:
+			  /* "The user intended FP parameter/result
+			     passing to conform to tool chain-specific
+			     conventions" - we don't know any such
+			     conventions, so leave it as "auto".  */
+			  break;
+			default:
+			  /* Attribute value not mentioned in the
+			     October 2008 ABI, so leave it as
+			     "auto".  */
+			  break;
+			}
+#else
+		      fp_model = ARM_FLOAT_SOFT_VFP;
+#endif
+		    }
 		  break;
 
 		default:


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]