Blob Blame History Raw
commit cc0e89c519912e0e4e75a2fc0d836f715cdc6806
Author: Ulrich Weigand <ulrich.weigand@de.ibm.com>
Date:   Tue Feb 4 18:42:35 2014 +0100

    PowerPC64 ELFv2 ABI: structure passing / return
    
    Another significant difference in the ELFv2 ABI is that "homogeneous"
    floating-point and vector aggregates, i.e. aggregates the consist
    (recursively) only of members of the same floating-point or vector type,
    are passed in a series of floating-point / vector registers, as if they
    were seperate parameters.  (This is similar to the ARM ABI.)  This
    applies to both calls and returns.
    
    In addition when returning any aggregate of up to 16 bytes, ELFv2 now
    used general-purpose registers.
    
    This patch adds support for these aspects of the ABI, which is relatively
    straightforward after the refactoring patch to ppc-sysv-tdep.c.
    
    gdb/ChangeLog:
    
    	* ppc-sysv-tdep.c (ppc64_aggregate_candidate): New routine.
    	(ppc64_elfv2_abi_homogeneous_aggregate): Likewise.
    	(ppc64_sysv_abi_push_param): Handle ELFv2 homogeneous structs.
    	(ppc64_sysv_abi_return_value): Likewise.  Also, handle small
    	structures returned in GPRs.

Index: gdb-7.6.1/gdb/ppc-sysv-tdep.c
===================================================================
--- gdb-7.6.1.orig/gdb/ppc-sysv-tdep.c
+++ gdb-7.6.1/gdb/ppc-sysv-tdep.c
@@ -1100,6 +1100,160 @@ convert_code_addr_to_desc_addr (CORE_ADD
   return 1;
 }
 
+/* Walk down the type tree of TYPE counting consecutive base elements.
+   If *FIELD_TYPE is NULL, then set it to the first valid floating point
+   or vector type.  If a non-floating point or vector type is found, or
+   if a floating point or vector type that doesn't match a non-NULL
+   *FIELD_TYPE is found, then return -1, otherwise return the count in the
+   sub-tree.  */
+
+static LONGEST
+ppc64_aggregate_candidate (struct type *type,
+			   struct type **field_type)
+{
+  type = check_typedef (type);
+
+  switch (TYPE_CODE (type))
+    {
+    case TYPE_CODE_FLT:
+    case TYPE_CODE_DECFLOAT:
+      if (!*field_type)
+	*field_type = type;
+      if (TYPE_CODE (*field_type) == TYPE_CODE (type)
+	  && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
+	return 1;
+      break;
+
+    case TYPE_CODE_COMPLEX:
+      type = TYPE_TARGET_TYPE (type);
+      if (TYPE_CODE (type) == TYPE_CODE_FLT
+	  || TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
+	{
+	  if (!*field_type)
+	    *field_type = type;
+	  if (TYPE_CODE (*field_type) == TYPE_CODE (type)
+	      && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
+	    return 2;
+	}
+      break;
+
+    case TYPE_CODE_ARRAY:
+      if (TYPE_VECTOR (type))
+	{
+	  if (!*field_type)
+	    *field_type = type;
+	  if (TYPE_CODE (*field_type) == TYPE_CODE (type)
+	      && TYPE_LENGTH (*field_type) == TYPE_LENGTH (type))
+	    return 1;
+	}
+      else
+	{
+	  LONGEST count, low_bound, high_bound;
+
+	  count = ppc64_aggregate_candidate
+		   (TYPE_TARGET_TYPE (type), field_type);
+	  if (count == -1)
+	    return -1;
+
+	  if (!get_array_bounds (type, &low_bound, &high_bound))
+	    return -1;
+	  count *= high_bound - low_bound;
+
+	  /* There must be no padding.  */
+	  if (count == 0)
+	    return TYPE_LENGTH (type) == 0 ? 0 : -1;
+	  else if (TYPE_LENGTH (type) != count * TYPE_LENGTH (*field_type))
+	    return -1;
+
+	  return count;
+	}
+      break;
+
+    case TYPE_CODE_STRUCT:
+    case TYPE_CODE_UNION:
+	{
+	  LONGEST count = 0;
+	  int i;
+
+	  for (i = 0; i < TYPE_NFIELDS (type); i++)
+	    {
+	      LONGEST sub_count;
+
+	      if (field_is_static (&TYPE_FIELD (type, i)))
+		continue;
+
+	      sub_count = ppc64_aggregate_candidate
+			   (TYPE_FIELD_TYPE (type, i), field_type);
+	      if (sub_count == -1)
+		return -1;
+
+	      if (TYPE_CODE (type) == TYPE_CODE_STRUCT)
+		count += sub_count;
+	      else
+		count = max (count, sub_count);
+	    }
+
+	  /* There must be no padding.  */
+	  if (count == 0)
+	    return TYPE_LENGTH (type) == 0 ? 0 : -1;
+	  else if (TYPE_LENGTH (type) != count * TYPE_LENGTH (*field_type))
+	    return -1;
+
+	  return count;
+	}
+      break;
+
+    default:
+      break;
+    }
+
+  return -1;
+}
+
+/* If an argument of type TYPE is a homogeneous float or vector aggregate
+   that shall be passed in FP/vector registers according to the ELFv2 ABI,
+   return the homogeneous element type in *ELT_TYPE and the number of
+   elements in *N_ELTS, and return non-zero.  Otherwise, return zero.  */
+
+static int
+ppc64_elfv2_abi_homogeneous_aggregate (struct type *type,
+				       struct type **elt_type, int *n_elts)
+{
+  /* Complex types at the top level are treated separately.  However,
+     complex types can be elements of homogeneous aggregates.  */
+  if (TYPE_CODE (type) == TYPE_CODE_STRUCT
+      || TYPE_CODE (type) == TYPE_CODE_UNION
+      || (TYPE_CODE (type) == TYPE_CODE_ARRAY && !TYPE_VECTOR (type)))
+    {
+      struct type *field_type = NULL;
+      LONGEST field_count = ppc64_aggregate_candidate (type, &field_type);
+
+      if (field_count > 0)
+	{
+	  int n_regs = ((TYPE_CODE (field_type) == TYPE_CODE_FLT
+			 || TYPE_CODE (field_type) == TYPE_CODE_DECFLOAT)?
+			(TYPE_LENGTH (field_type) + 7) >> 3 : 1);
+
+	  /* The ELFv2 ABI allows homogeneous aggregates to occupy
+	     up to 8 registers.  */
+	  if (field_count * n_regs <= 8)
+	    {
+	      if (elt_type)
+		*elt_type = field_type;
+	      if (n_elts)
+		*n_elts = (int) field_count;
+	      /* Note that field_count is LONGEST since it may hold the size
+		 of an array, while *n_elts is int since its value is bounded
+		 by the number of registers used for argument passing.  The
+		 cast cannot overflow due to the bounds checking above.  */
+	      return 1;
+	    }
+	}
+    }
+
+  return 0;
+}
+
 /* Structure holding the next argument position.  */
 struct ppc64_sysv_argpos
   {
@@ -1388,6 +1542,29 @@ ppc64_sysv_abi_push_param (struct gdbarc
 	  if (TYPE_CODE (type) == TYPE_CODE_FLT)
 	    ppc64_sysv_abi_push_freg (gdbarch, type, val, argpos);
 	}
+
+      /* In the ELFv2 ABI, homogeneous floating-point or vector
+	 aggregates are passed in a series of registers.  */
+      if (tdep->elf_abi == POWERPC_ELF_V2)
+	{
+	  struct type *eltype;
+	  int i, nelt;
+
+	  if (ppc64_elfv2_abi_homogeneous_aggregate (type, &eltype, &nelt))
+	    for (i = 0; i < nelt; i++)
+	      {
+		const gdb_byte *elval = val + i * TYPE_LENGTH (eltype);
+
+		if (TYPE_CODE (eltype) == TYPE_CODE_FLT
+		    || TYPE_CODE (eltype) == TYPE_CODE_DECFLOAT)
+		  ppc64_sysv_abi_push_freg (gdbarch, eltype, elval, argpos);
+		else if (TYPE_CODE (eltype) == TYPE_CODE_ARRAY
+			 && TYPE_VECTOR (eltype)
+			 && tdep->vector_abi == POWERPC_VEC_ALTIVEC
+			 && TYPE_LENGTH (eltype) == 16)
+		  ppc64_sysv_abi_push_vreg (gdbarch, elval, argpos);
+	      }
+	}
     }
 }
 
@@ -1833,6 +2010,72 @@ ppc64_sysv_abi_return_value (struct gdba
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
 
+  /* In the ELFv2 ABI, homogeneous floating-point or vector
+     aggregates are returned in registers.  */
+  if (tdep->elf_abi == POWERPC_ELF_V2
+      && ppc64_elfv2_abi_homogeneous_aggregate (valtype, &eltype, &nelt))
+    {
+      for (i = 0; i < nelt; i++)
+	{
+	  ok = ppc64_sysv_abi_return_value_base (gdbarch, eltype, regcache,
+						 readbuf, writebuf, i);
+	  gdb_assert (ok);
+
+	  if (readbuf)
+	    readbuf += TYPE_LENGTH (eltype);
+	  if (writebuf)
+	    writebuf += TYPE_LENGTH (eltype);
+	}
+
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+
+  /* In the ELFv2 ABI, aggregate types of up to 16 bytes are
+     returned in registers r3:r4.  */
+  if (tdep->elf_abi == POWERPC_ELF_V2
+      && TYPE_LENGTH (valtype) <= 16
+      && (TYPE_CODE (valtype) == TYPE_CODE_STRUCT
+	  || TYPE_CODE (valtype) == TYPE_CODE_UNION
+	  || (TYPE_CODE (valtype) == TYPE_CODE_ARRAY
+	      && !TYPE_VECTOR (valtype))))
+    {
+      int n_regs = ((TYPE_LENGTH (valtype) + tdep->wordsize - 1)
+		    / tdep->wordsize);
+      int i;
+
+      for (i = 0; i < n_regs; i++)
+	{
+	  gdb_byte regval[MAX_REGISTER_SIZE];
+	  int regnum = tdep->ppc_gp0_regnum + 3 + i;
+	  int offset = i * tdep->wordsize;
+	  int len = TYPE_LENGTH (valtype) - offset;
+
+	  if (len > tdep->wordsize)
+	    len = tdep->wordsize;
+
+	  if (writebuf != NULL)
+	    {
+	      memset (regval, 0, sizeof regval);
+	      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
+		  && offset == 0)
+		memcpy (regval + tdep->wordsize - len, writebuf, len);
+	      else
+		memcpy (regval, writebuf + offset, len);
+	      regcache_cooked_write (regcache, regnum, regval);
+	    }
+	  if (readbuf != NULL)
+	    {
+	      regcache_cooked_read (regcache, regnum, regval);
+	      if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG
+		  && offset == 0)
+		memcpy (readbuf, regval + tdep->wordsize - len, len);
+	      else
+		memcpy (readbuf + offset, regval, len);
+	    }
+	}
+      return RETURN_VALUE_REGISTER_CONVENTION;
+    }
+
   /* Handle plain base types.  */
   if (ppc64_sysv_abi_return_value_base (gdbarch, valtype, regcache,
 					readbuf, writebuf, 0))