public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH 10/11] ELFv2 ABI: structure passing / return
@ 2014-01-28 17:30 Ulrich Weigand
  2014-01-29  5:00 ` Joel Brobecker
  0 siblings, 1 reply; 5+ messages in thread
From: Ulrich Weigand @ 2014-01-28 17:30 UTC (permalink / raw)
  To: gdb-patches

Hello,

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.

Tested (whole series) on powerpc64-linux and powerpc64le-linux.

Bye,
Ulrich

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: binutils-gdb/gdb/ppc-sysv-tdep.c
===================================================================
--- binutils-gdb.orig/gdb/ppc-sysv-tdep.c
+++ binutils-gdb/gdb/ppc-sysv-tdep.c
@@ -1101,6 +1101,156 @@ 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;
+	      return 1;
+	    }
+	}
+    }
+
+  return 0;
+}
+
 /* Structure holding the next argument position.  */
 struct ppc64_sysv_argpos
   {
@@ -1385,6 +1535,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);
+	      }
+	}
     }
 }
 
@@ -1823,6 +1996,69 @@ 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))
-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 10/11] ELFv2 ABI: structure passing / return
  2014-01-28 17:30 [PATCH 10/11] ELFv2 ABI: structure passing / return Ulrich Weigand
@ 2014-01-29  5:00 ` Joel Brobecker
  2014-01-29 18:14   ` Ulrich Weigand
  0 siblings, 1 reply; 5+ messages in thread
From: Joel Brobecker @ 2014-01-29  5:00 UTC (permalink / raw)
  To: Ulrich Weigand; +Cc: gdb-patches

> 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: binutils-gdb/gdb/ppc-sysv-tdep.c
> ===================================================================
> --- binutils-gdb.orig/gdb/ppc-sysv-tdep.c
> +++ binutils-gdb/gdb/ppc-sysv-tdep.c
> @@ -1101,6 +1101,156 @@ 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)

I think we can reasonably change the return type to int, here?
That would avoid the cast you're making at:

> +      struct type *field_type = NULL;
> +      LONGEST field_count = ppc64_aggregate_candidate (type, &field_type);
[...]
> +	      if (n_elts)
> +		*n_elts = (int) field_count;

The other option is to make everything use LONGEST for this count.
That way, everything would be consistent and we'd be avoiding a cast
as well.

> +  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))))

The last line looks too long. Can you fold it?

> +    {
> +      int n_regs = (TYPE_LENGTH (valtype) + tdep->wordsize - 1) / tdep->wordsize;

Same here.

> +      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;

Missing empty line after local variable declarations.

-- 
Joel

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 10/11] ELFv2 ABI: structure passing / return
  2014-01-29  5:00 ` Joel Brobecker
@ 2014-01-29 18:14   ` Ulrich Weigand
  2014-01-31 10:09     ` Joel Brobecker
  0 siblings, 1 reply; 5+ messages in thread
From: Ulrich Weigand @ 2014-01-29 18:14 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

Joel Brobecker wrote:

> > +/* 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)
> 
> I think we can reasonably change the return type to int, here?
> That would avoid the cast you're making at:
> 
> > +      struct type *field_type = NULL;
> > +      LONGEST field_count = ppc64_aggregate_candidate (type, &field_type);
> [...]
> > +	      if (n_elts)
> > +		*n_elts = (int) field_count;
> 
> The other option is to make everything use LONGEST for this count.
> That way, everything would be consistent and we'd be avoiding a cast
> as well.

So I've chosen those types deliberately :-)  In ppc64_aggregate_candidate
we find *any* aggregate as long as all members are of the same type.  It
is theoretically possible that the number of such members exceed 2G, e.g.
if the aggregate is a very large array.  Thus the obvious return type is
the type we use for array indices, i.e. LONGEST.

However, in ppc64_elfv2_abi_homogeneous_aggregate we only ever set n_elts
if the aggregate is one that is to be returned in registers, which means
that the number of members can never exceed 8.  Thus the obvious type of
n_elts is int.

Note that the cast is guaranteed to be safe, since the value is bounds-
checked before:

+      if (field_count > 0)
[...]
+         if (field_count * n_regs <= 8)
[...]
+               *n_elts = (int) field_count;

Given the above, it seems best to me to chose types as I did orignally ...

Bye,
Ulrich

-- 
  Dr. Ulrich Weigand
  GNU/Linux compilers and toolchain
  Ulrich.Weigand@de.ibm.com

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 10/11] ELFv2 ABI: structure passing / return
  2014-01-29 18:14   ` Ulrich Weigand
@ 2014-01-31 10:09     ` Joel Brobecker
  2014-01-31 15:41       ` Ulrich Weigand
  0 siblings, 1 reply; 5+ messages in thread
From: Joel Brobecker @ 2014-01-31 10:09 UTC (permalink / raw)
  To: Ulrich Weigand; +Cc: gdb-patches

> So I've chosen those types deliberately :-)  In ppc64_aggregate_candidate
> we find *any* aggregate as long as all members are of the same type.  It
> is theoretically possible that the number of such members exceed 2G, e.g.
> if the aggregate is a very large array.  Thus the obvious return type is
> the type we use for array indices, i.e. LONGEST.
> 
> However, in ppc64_elfv2_abi_homogeneous_aggregate we only ever set n_elts
> if the aggregate is one that is to be returned in registers, which means
> that the number of members can never exceed 8.  Thus the obvious type of
> n_elts is int.
> 
> Note that the cast is guaranteed to be safe, since the value is bounds-
> checked before:
> 
> +      if (field_count > 0)
> [...]
> +         if (field_count * n_regs <= 8)
> [...]
> +               *n_elts = (int) field_count;
> 
> Given the above, it seems best to me to chose types as I did orignally ...

That is indeed a good explanation. I suggest we re-use your explanation,
or at least the second part of it, to explain why n_elts is an int
and why the cast of field_count down to int is safe. You'll avoid
someone like me, who is tuned to treat all casts as suspicious, from
unmaking your choice :-).

-- 
Joel

^ permalink raw reply	[flat|nested] 5+ messages in thread

* Re: [PATCH 10/11] ELFv2 ABI: structure passing / return
  2014-01-31 10:09     ` Joel Brobecker
@ 2014-01-31 15:41       ` Ulrich Weigand
  0 siblings, 0 replies; 5+ messages in thread
From: Ulrich Weigand @ 2014-01-31 15:41 UTC (permalink / raw)
  To: Joel Brobecker; +Cc: gdb-patches

Joel Brobecker <brobecker@adacore.com> wrote on 31.01.2014 11:09:46:

> That is indeed a good explanation. I suggest we re-use your explanation,
> or at least the second part of it, to explain why n_elts is an int
> and why the cast of field_count down to int is safe. You'll avoid
> someone like me, who is tuned to treat all casts as suspicious, from
> unmaking your choice :-).

OK, makes sense.  I've added the following comment:

              /* 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.  */

Bye,
Ulrich

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2014-01-31 15:41 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-01-28 17:30 [PATCH 10/11] ELFv2 ABI: structure passing / return Ulrich Weigand
2014-01-29  5:00 ` Joel Brobecker
2014-01-29 18:14   ` Ulrich Weigand
2014-01-31 10:09     ` Joel Brobecker
2014-01-31 15:41       ` Ulrich Weigand

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).