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]

[commit] MIPS n32 / n64 fixups


This patch fixes several bugs in the n32 / n64 push_dummy_call
implementation.  On o32, foo (int, double) puts the arguments in
$a0 and $f12; on n32 they go in $a0 and $f13.  The integer register
bumps up the position within the floating point registers.  Also
when struct { int x; double y; } is passed by value, the part that
contains 'y' is passed in a floating point register.

It occured to me after getting this right that a simpler
implementation would be to always put each argument word in
both a GPR and an FPR.  But I didn't think of it until I'd
implemented the accurate solution, so unless bugs turn up with
this version I'll leave it be.

Tested on MIPS (soft float, hard float, n32, n64) and checked in.

-- 
Daniel Jacobowitz
CodeSourcery

2007-08-22  Daniel Jacobowitz  <dan@codesourcery.com>

	gdb/testsuite/
	* gdb.base/callfuncs.exp (do_function_calls): Use t_double_int
	and t_int_double.
	* gdb.base/callfuncs.c (t_double_int, t_int_double): New.

	gdb/
	* mips-tdep.c (mips_n32n64_fp_arg_chunk_p): New.
	(mips_n32n64_push_dummy_call): Always increment float_argreg along
	with argreg.  Use mips_n32n64_fp_arg_chunk_p.

Index: gdb/testsuite/gdb.base/callfuncs.exp
===================================================================
--- gdb/testsuite/gdb.base/callfuncs.exp	(revision 179591)
+++ gdb/testsuite/gdb.base/callfuncs.exp	(working copy)
@@ -163,6 +163,11 @@ proc do_function_calls {} {
 	gdb_test "p t_double_values(double_val1,double_val2)" " = 1"
 	gdb_test "p t_double_values(45.654,double_val2)" " = 1"
 	gdb_test "p t_double_values(double_val1,-67.66)" " = 1"
+
+	gdb_test "p t_double_int(99.0, 1)" " = 0"
+	gdb_test "p t_double_int(99.0, 99)" " = 1"
+	gdb_test "p t_int_double(99, 1.0)" " = 0"
+	gdb_test "p t_int_double(99, 99.0)" " = 1"
     }
 
     gdb_test "p t_string_values(string_val2,string_val1)" " = 0"
Index: gdb/testsuite/gdb.base/callfuncs.c
===================================================================
--- gdb/testsuite/gdb.base/callfuncs.c	(revision 179591)
+++ gdb/testsuite/gdb.base/callfuncs.c	(working copy)
@@ -334,6 +334,29 @@ char char_array_arg1[], char_array_arg2[
 	  !strcmp (char_array_arg2, char_array_val2));
 }
 
+#ifdef PROTOTYPES
+int t_double_int (double double_arg1, int int_arg2)
+#else
+int t_double_int (double_arg1, int_arg2)
+double double_arg1;
+int int_arg2;
+#endif
+{
+  return ((double_arg1 - int_arg2) < DELTA
+	  && (double_arg1 - int_arg2) > -DELTA);
+}
+
+#ifdef PROTOTYPES
+int t_int_double (int int_arg1, double double_arg2)
+#else
+int t_int_double (int_arg1, double_arg2)
+int int_arg1;
+double double_arg2;
+#endif
+{
+  return ((int_arg1 - double_arg2) < DELTA
+	  && (int_arg1 - double_arg2) > -DELTA);
+}
 
 /* This used to simply compare the function pointer arguments with
    known values for func_val1 and func_val2.  Doing so is valid ANSI
Index: gdb/mips-tdep.c
===================================================================
--- gdb/mips-tdep.c	(revision 179591)
+++ gdb/mips-tdep.c	(working copy)
@@ -2795,6 +2795,59 @@ mips_eabi_return_value (struct gdbarch *
 
 /* N32/N64 ABI stuff.  */
 
+/* Search for a naturally aligned double at OFFSET inside a struct
+   ARG_TYPE.  The N32 / N64 ABIs pass these in floating point
+   registers.  */
+
+static int
+mips_n32n64_fp_arg_chunk_p (struct type *arg_type, int offset)
+{
+  int i;
+
+  if (TYPE_CODE (arg_type) != TYPE_CODE_STRUCT)
+    return 0;
+
+  if (MIPS_FPU_TYPE != MIPS_FPU_DOUBLE)
+    return 0;
+
+  if (TYPE_LENGTH (arg_type) < offset + MIPS64_REGSIZE)
+    return 0;
+
+  for (i = 0; i < TYPE_NFIELDS (arg_type); i++)
+    {
+      int pos;
+      struct type *field_type;
+
+      /* We're only looking at normal fields.  */
+      if (TYPE_FIELD_STATIC (arg_type, i)
+	  || (TYPE_FIELD_BITPOS (arg_type, i) % 8) != 0)
+	continue;
+
+      /* If we have gone past the offset, there is no double to pass.  */
+      pos = TYPE_FIELD_BITPOS (arg_type, i) / 8;
+      if (pos > offset)
+	return 0;
+
+      field_type = check_typedef (TYPE_FIELD_TYPE (arg_type, i));
+
+      /* If this field is entirely before the requested offset, go
+	 on to the next one.  */
+      if (pos + TYPE_LENGTH (field_type) <= offset)
+	continue;
+
+      /* If this is our special aligned double, we can stop.  */
+      if (TYPE_CODE (field_type) == TYPE_CODE_FLT
+	  && TYPE_LENGTH (field_type) == MIPS64_REGSIZE)
+	return 1;
+
+      /* This field starts at or before the requested offset, and
+	 overlaps it.  If it is a structure, recurse inwards.  */
+      return mips_n32n64_fp_arg_chunk_p (field_type, offset - pos);
+    }
+
+  return 0;
+}
+
 static CORE_ADDR
 mips_n32n64_push_dummy_call (struct gdbarch *gdbarch, struct value *function,
 			     struct regcache *regcache, CORE_ADDR bp_addr,
@@ -2869,23 +2922,22 @@ mips_n32n64_push_dummy_call (struct gdba
       val = value_contents (arg);
 
       if (fp_register_arg_p (typecode, arg_type)
-	  && float_argreg <= MIPS_LAST_FP_ARG_REGNUM)
+	  && argreg <= MIPS_LAST_ARG_REGNUM)
 	{
 	  /* This is a floating point value that fits entirely
 	     in a single register.  */
-	  /* On 32 bit ABI's the float_argreg is further adjusted
-	     above to ensure that it is even register aligned.  */
 	  LONGEST regval = extract_unsigned_integer (val, len);
 	  if (mips_debug)
 	    fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
 				float_argreg, phex (regval, len));
-	  regcache_cooked_write_unsigned (regcache, float_argreg++, regval);
+	  regcache_cooked_write_unsigned (regcache, float_argreg, regval);
 
 	  if (mips_debug)
 	    fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
 				argreg, phex (regval, len));
 	  regcache_cooked_write_unsigned (regcache, argreg, regval);
-	  argreg += 1;
+	  float_argreg++;
+	  argreg++;
 	}
       else
 	{
@@ -2910,10 +2962,12 @@ mips_n32n64_push_dummy_call (struct gdba
 		fprintf_unfiltered (gdb_stdlog, " -- partial=%d",
 				    partial_len);
 
+	      if (fp_register_arg_p (typecode, arg_type))
+		gdb_assert (argreg > MIPS_LAST_ARG_REGNUM);
+
 	      /* Write this portion of the argument to the stack.  */
 	      if (argreg > MIPS_LAST_ARG_REGNUM
-		  || odd_sized_struct
-		  || fp_register_arg_p (typecode, arg_type))
+		  || odd_sized_struct)
 		{
 		  /* Should shorter than int integer values be
 		     promoted to int before being stored? */
@@ -2954,12 +3008,10 @@ mips_n32n64_push_dummy_call (struct gdba
 		}
 
 	      /* Note!!! This is NOT an else clause.  Odd sized
-	         structs may go thru BOTH paths.  Floating point
-	         arguments will not.  */
+	         structs may go thru BOTH paths.  */
 	      /* Write this portion of the argument to a general
 	         purpose register.  */
-	      if (argreg <= MIPS_LAST_ARG_REGNUM
-		  && !fp_register_arg_p (typecode, arg_type))
+	      if (argreg <= MIPS_LAST_ARG_REGNUM)
 		{
 		  LONGEST regval =
 		    extract_unsigned_integer (val, partial_len);
@@ -2985,6 +3037,19 @@ mips_n32n64_push_dummy_call (struct gdba
 				      argreg,
 				      phex (regval, MIPS64_REGSIZE));
 		  regcache_cooked_write_unsigned (regcache, argreg, regval);
+
+		  if (mips_n32n64_fp_arg_chunk_p (arg_type,
+						  TYPE_LENGTH (arg_type) - len))
+		    {
+		      if (mips_debug)
+			fprintf_filtered (gdb_stdlog, " - fpreg=%d val=%s",
+					  float_argreg,
+					  phex (regval, MIPS64_REGSIZE));
+		      regcache_cooked_write_unsigned (regcache, float_argreg,
+						      regval);
+		    }
+
+		  float_argreg++;
 		  argreg++;
 		}
 


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