public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] gdb: LoongArch: Handle special floating-point member struct in dummy call
@ 2023-06-28 10:08 Hui Li
  2023-07-06 15:11 ` Tom Tromey
  0 siblings, 1 reply; 2+ messages in thread
From: Hui Li @ 2023-06-28 10:08 UTC (permalink / raw)
  To: gdb-patches; +Cc: Tiezhu Yang

This commit solves some special problems when a struct containing
floating-point members as function argument or return value for a
dummy call. LoongArch GCC have some special processing for struct
containing floating-point members. This is also the main reason for
a large number of test failures about C++ empty structures as function
arguments or return values.

Mainly includes the following cases:

1.struct contains one floating-point member and at least
  one floating-point argument register is available.

2.struct contains two floating-point member and at least
  two floating-point argument register is available.

3.struct containing one floating-point member and one
  integral member and at least one floating-point argument
  register and at least one integer argument register is
  available

These cases will preferentially use the argument register for
argument passing, even if the structure length is greater than
2*regsize.

Execute the following test on LoongArch64:

loongson@bogon:~$ cat test.c

 #include<stdio.h>

struct test {
	long   a;
	double b __attribute__((aligned(16)));
};
struct test val = { 88, 99.99 };
int check_arg_struct (struct test arg)
  {
    printf("arg.a = %ld\n", arg.a);
    printf("arg.b = %f\n", arg.b);
    printf("sizeof(val) = %d\n", sizeof(val));
    return 1;
  }
int main()
{
   check_arg_struct (val);
   return 0;
}
loongson@bogon:~$ gcc -g test.c -o test
loongson@bogon:~$ ./test
arg.a = 88
arg.b = 99.990000
sizeof(val) = 32

before this patch

loongson@bogon:~$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:19
19	   check_arg_struct (val);
(gdb) p check_arg_struct (val)
arg.a = 140737488286128
arg.b = -nan
sizeof(val) = 32
$1 = 1
...

make check-gdb TESTS="gdb.base/infcall-nested-structs-c++.exp"

 === gdb Summary ===

 # of expected passes		5533
 # of unexpected failures	367

------------------------

after this patch

loongson@bogon:~$ gdb test
...
(gdb) start
...
Temporary breakpoint 1, main () at test.c:19
19	   check_arg_struct (val);
(gdb) p check_arg_struct (val)
arg.a = 88
arg.b = 99.990000
sizeof(val) = 32
$1 = 1
...

make check-gdb TESTS="gdb.base/infcall-nested-structs-c++.exp"

 === gdb Summary ===

 # of expected passes		5900

Signed-off-by: Hui Li <lihui@loongson.cn>
---
 gdb/loongarch-tdep.c | 209 +++++++++++++++++++++++++++++++++++++------
 1 file changed, 182 insertions(+), 27 deletions(-)

diff --git a/gdb/loongarch-tdep.c b/gdb/loongarch-tdep.c
index 62c6f9b220e..11ac4f8732c 100644
--- a/gdb/loongarch-tdep.c
+++ b/gdb/loongarch-tdep.c
@@ -516,7 +516,8 @@ static void
 compute_struct_member (struct type *type,
 		       unsigned int *fixed_point_members,
 		       unsigned int *floating_point_members,
-		       bool *first_member_is_fixed_point)
+		       bool *first_member_is_fixed_point,
+		       bool *has_long_double)
 {
   for (int i = 0; i < type->num_fields (); i++)
     {
@@ -526,6 +527,10 @@ compute_struct_member (struct type *type,
 
       struct type *field_type = check_typedef (type->field (i).type ());
 
+      if ((field_type->code () == TYPE_CODE_FLT && field_type->length () == 16)
+	  || (field_type->code () == TYPE_CODE_COMPLEX && field_type->length () == 32))
+	*has_long_double = true;
+
       if (field_type->code () == TYPE_CODE_INT
 	  || field_type->code () == TYPE_CODE_BOOL
 	  || field_type->code () == TYPE_CODE_CHAR
@@ -544,12 +549,77 @@ compute_struct_member (struct type *type,
 	compute_struct_member (field_type,
 			       fixed_point_members,
 			       floating_point_members,
-			       first_member_is_fixed_point);
+			       first_member_is_fixed_point,
+			       has_long_double);
       else if (field_type->code () == TYPE_CODE_COMPLEX)
 	(*floating_point_members) += 2;
     }
 }
 
+/* Compute the lengths and offsets of struct member.  */
+
+static void
+struct_member_info (struct type *type,
+		    unsigned int *m_offsets,
+		    unsigned int *m_lens,
+		    unsigned int offset,
+		    unsigned int *m_fields)
+{
+  unsigned int count = type->num_fields ();
+  unsigned int i;
+
+  for (i = 0; i < count; ++i)
+    {
+      if (type->field (i).loc_kind () != FIELD_LOC_KIND_BITPOS)
+	continue;
+
+      struct type *field_type = check_typedef (type->field (i).type ());
+      int field_offset = offset + type->field (i).loc_bitpos () / TARGET_CHAR_BIT;
+
+      switch (field_type->code ())
+	{
+	case TYPE_CODE_STRUCT:
+	  struct_member_info (field_type, m_offsets, m_lens, field_offset, m_fields);
+	  break;
+
+	case TYPE_CODE_COMPLEX:
+	  if (*m_fields == 0)
+	    {
+	      /* _Complex float */
+	      if (field_type->length () == 8)
+		{
+		  m_offsets[0] = field_offset;
+		  m_offsets[1] = field_offset + 4;
+		  m_lens[0] = m_lens[1] = 4;
+		  *m_fields = 2;
+		}
+	      /* _Complex double */
+	      else if (field_type->length () == 16)
+		{
+		  m_offsets[0] = field_offset;
+		  m_offsets[1] = field_offset + 8;
+		  m_lens[0] = m_lens[1] = 8;
+		  *m_fields = 2;
+		}
+	    }
+	  break;
+
+	default:
+	  if (*m_fields < 2)
+	    {
+	      m_offsets[*m_fields] = field_offset;
+	      m_lens[*m_fields] = field_type->length ();
+	    }
+	  (*m_fields)++;
+	  break;
+	}
+
+      /* only has special handling for structures with 1 or 2 fields. */
+      if (*m_fields > 2)
+	return;
+    }
+}
+
 /* Implement the push_dummy_call gdbarch method.  */
 
 static CORE_ADDR
@@ -566,15 +636,13 @@ loongarch_push_dummy_call (struct gdbarch *gdbarch,
   int regsize = register_size (gdbarch, 0);
   unsigned int gar = LOONGARCH_ARG_REGNUM;
   unsigned int far = LOONGARCH_ARG_REGNUM;
-  unsigned int fixed_point_members;
-  unsigned int floating_point_members;
-  bool first_member_is_fixed_point;
   gdb_byte buf[1024] = { 0 };
   gdb_byte *addr = buf;
 
   if (return_method != return_method_normal)
     pass_in_gar (regcache, gar--, (gdb_byte *) &struct_addr);
 
+
   for (int i = 0; i < nargs; i++)
     {
       struct value *arg = args[i];
@@ -695,15 +763,56 @@ loongarch_push_dummy_call (struct gdbarch *gdbarch,
 	  break;
 	case TYPE_CODE_STRUCT:
 	  {
-	    fixed_point_members = 0;
-	    floating_point_members = 0;
-	    first_member_is_fixed_point = false;
+	    unsigned int fixed_point_members = 0;
+	    unsigned int floating_point_members = 0;
+	    bool first_member_is_fixed_point = false;
+	    bool has_long_double = false;
+	    unsigned int total_members = 0;
+	    unsigned int m_offsets[2] = { 0, 0 };
+	    unsigned int m_lens[2] = { 0, 0 };
+	    unsigned int m_fields = 0;
 	    compute_struct_member (type,
 				   &fixed_point_members,
 				   &floating_point_members,
-				   &first_member_is_fixed_point);
-
-	    if (len > 0 && len <= regsize)
+				   &first_member_is_fixed_point,
+				   &has_long_double);
+	    total_members = fixed_point_members + floating_point_members;
+	    struct_member_info (type, m_offsets, m_lens, 0, &m_fields);
+	    /* struct has one floating_point_member and at least one FAR;
+	       struct has two floating_point_member and at least two FAR;
+	       struct has one floating_point_member and fixed_point_member
+	       and least one FAR and one GAR. */
+	    if (total_members <= 2 && floating_point_members > 0
+		&& has_long_double == false
+		&& ((total_members == 1 && far >= 1)
+		    || (floating_point_members == 2 && far >= 2)
+		    || (fixed_point_members == 1 && floating_point_members == 1
+			&& far >= 1 && gar >=1)))
+	      {
+		if (floating_point_members == 2)
+		  {
+		    pass_in_far (regcache, far--, val + m_offsets[0]);
+		    pass_in_far (regcache, far--, val + m_offsets[1]);
+		  }
+		else if (floating_point_members == 1 && fixed_point_members == 1)
+		  {
+		    if (first_member_is_fixed_point == false)
+		      {
+			pass_in_far (regcache, far--, val + m_offsets[0]);
+			pass_in_gar (regcache, gar--, val + m_offsets[1]);
+		      }
+		    else
+		      {
+			pass_in_gar (regcache, gar--, val + m_offsets[0]);
+			pass_in_far (regcache, far--, val + m_offsets[1]);
+		      }
+		  }
+		else if (floating_point_members == 1 && total_members == 1)
+		  {
+		    pass_in_far (regcache, far--, val + m_offsets[0]);
+		  }
+	      }
+	    else if (len > 0 && len <= regsize)
 	      {
 		/* The structure has only fixed-point members.  */
 		if (fixed_point_members > 0 && floating_point_members == 0)
@@ -1157,17 +1266,19 @@ loongarch_return_value (struct gdbarch *gdbarch, struct value *function,
   int regsize = register_size (gdbarch, 0);
   enum type_code code = type->code ();
   size_t len = type->length ();
-  unsigned int fixed_point_members;
-  unsigned int floating_point_members;
-  bool first_member_is_fixed_point;
+  unsigned int fixed_point_members = 0;
+  unsigned int floating_point_members = 0;
+  bool first_member_is_fixed_point = false;
+  bool has_long_double = false;
+  unsigned int total_members = 0;
+  unsigned int m_offsets[2] = { 0, 0 };
+  unsigned int m_lens[2] = { 0, 0 };
+  unsigned int m_fields = 0;
   int a0 = LOONGARCH_A0_REGNUM;
   int a1 = LOONGARCH_A0_REGNUM + 1;
   int f0 = LOONGARCH_FIRST_FP_REGNUM;
   int f1 = LOONGARCH_FIRST_FP_REGNUM + 1;
 
-  if (len > 2 * regsize)
-    return RETURN_VALUE_STRUCT_CONVENTION;
-
   switch (code)
     {
     case TYPE_CODE_INT:
@@ -1217,15 +1328,52 @@ loongarch_return_value (struct gdbarch *gdbarch, struct value *function,
       break;
     case TYPE_CODE_STRUCT:
       {
-	fixed_point_members = 0;
-	floating_point_members = 0;
-	first_member_is_fixed_point = false;
 	compute_struct_member (type,
 			       &fixed_point_members,
 			       &floating_point_members,
-			       &first_member_is_fixed_point);
+			       &first_member_is_fixed_point,
+			       &has_long_double);
 
-	if (len > 0 && len <= regsize)
+	total_members = fixed_point_members + floating_point_members;
+	struct_member_info (type, m_offsets, m_lens, 0, &m_fields);
+
+	/* struct has one floating_point_member;
+	   struct has two floating_point_member;
+	   struct has one floating_point_member and fixed_point_member. */
+	if (total_members <= 2 && floating_point_members > 0
+	    && has_long_double == false)
+	  {
+	    if (floating_point_members == 2)
+	      {
+		loongarch_xfer_reg (regcache, f0, m_lens[0], readbuf,
+				    writebuf, m_offsets[0]);
+		loongarch_xfer_reg (regcache, f1, m_lens[1], readbuf,
+				    writebuf, m_offsets[1]);
+	      }
+	    else if (floating_point_members == 1 && fixed_point_members == 1)
+	      {
+		if (first_member_is_fixed_point == false)
+		  {
+		    loongarch_xfer_reg (regcache, f0, m_lens[0], readbuf,
+					writebuf, m_offsets[0]);
+		    loongarch_xfer_reg (regcache, a0, m_lens[1], readbuf,
+					writebuf, m_offsets[1]);
+		  }
+		else
+		  {
+		    loongarch_xfer_reg (regcache, a0, m_lens[0], readbuf,
+					writebuf, m_offsets[0]);
+		    loongarch_xfer_reg (regcache, f0, m_lens[1], readbuf,
+					writebuf, m_offsets[1]);
+		  }
+	      }
+	    else if (floating_point_members == 1 && total_members == 1)
+	      {
+		loongarch_xfer_reg (regcache, f0, m_lens[0], readbuf,
+				    writebuf, m_offsets[0]);
+	      }
+	  }
+	else if (len > 0 && len <= regsize)
 	  {
 	    /* The structure has only fixed-point members.  */
 	    if (fixed_point_members > 0 && floating_point_members == 0)
@@ -1338,6 +1486,8 @@ loongarch_return_value (struct gdbarch *gdbarch, struct value *function,
 		  }
 	      }
 	  }
+	else if (len > 2 * regsize)
+	  return RETURN_VALUE_STRUCT_CONVENTION;
       }
       break;
     case TYPE_CODE_UNION:
@@ -1352,13 +1502,18 @@ loongarch_return_value (struct gdbarch *gdbarch, struct value *function,
 	  loongarch_xfer_reg (regcache, a0, regsize, readbuf, writebuf, 0);
 	  loongarch_xfer_reg (regcache, a1, len - regsize, readbuf, writebuf, regsize);
 	}
+      else if (len > 2 * regsize)
+	return RETURN_VALUE_STRUCT_CONVENTION;
       break;
     case TYPE_CODE_COMPLEX:
-      {
-	/* The return value is passed in f0 and f1.  */
-	loongarch_xfer_reg (regcache, f0, len / 2, readbuf, writebuf, 0);
-	loongarch_xfer_reg (regcache, f1, len / 2, readbuf, writebuf, len / 2);
-      }
+      if (len > 2 * regsize)
+	return RETURN_VALUE_STRUCT_CONVENTION;
+      else
+	{
+	  /* The return value is passed in f0 and f1.  */
+	  loongarch_xfer_reg (regcache, f0, len / 2, readbuf, writebuf, 0);
+	  loongarch_xfer_reg (regcache, f1, len / 2, readbuf, writebuf, len / 2);
+	}
       break;
     default:
       break;
-- 
2.38.1


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

* Re: [PATCH] gdb: LoongArch: Handle special floating-point member struct in dummy call
  2023-06-28 10:08 [PATCH] gdb: LoongArch: Handle special floating-point member struct in dummy call Hui Li
@ 2023-07-06 15:11 ` Tom Tromey
  0 siblings, 0 replies; 2+ messages in thread
From: Tom Tromey @ 2023-07-06 15:11 UTC (permalink / raw)
  To: Hui Li; +Cc: gdb-patches, Tiezhu Yang

>>>>> Hui Li <lihui@loongson.cn> writes:

Hi.

> +      if ((field_type->code () == TYPE_CODE_FLT && field_type->length () == 16)
> +	  || (field_type->code () == TYPE_CODE_COMPLEX && field_type->length () == 32))

These lines look over-long.

> +static void
> +struct_member_info (struct type *type,
> +		    unsigned int *m_offsets,
> +		    unsigned int *m_lens,
> +		    unsigned int offset,
> +		    unsigned int *m_fields)

In gdb, the "m_" prefix is used exclusively for member variables.

Tom

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

end of thread, other threads:[~2023-07-06 15:11 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2023-06-28 10:08 [PATCH] gdb: LoongArch: Handle special floating-point member struct in dummy call Hui Li
2023-07-06 15:11 ` Tom Tromey

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