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]

Fix MIPS ABI issues


This patch fixes various issues in GDB's handling of MIPS function call 
ABIs shown up by the testsuite:

* For O32, when a float value is passed in an FPR, only one GPR is 
skipped, not two, contradicting the guess given in a comment.  (The psABI 
document, such as it is, has examples confirming this.)

* For (hard-float) N32 and N64, 128-bit long double values are passed in 
even-odd pairs of floating-point registers; GDB was failing to handle 
passing them at all.  Confirmed in the N32 ABI document (I don't have a 
psABI document for N64 that discusses the function call and return 
interface).

* For N32 and N64, float parameters not passed in FPRs are left-justified, 
not right-justified.  The N32 ABI document explicitly says this.

* For N32 and N64, the special return convention for structs containing 
one or two floating-point values also applies to soft-float (using GPRs 
instead of FPRs).  The N32 ABI document does not discuss soft-float; this 
is what GCC does.

* For N32 and N64, that special return convention applies to a struct 
containing a 128-bit long double (which needs two registers).  The N32 ABI 
document is silent about this case; this is what GCC does.  Both GCC and 
GDB already agree that a struct larger than 128 bits is returned in 
memory, so this case only applies for a struct containing a single long 
double.

Tested for mips-linux-gnu (using GCC 4.3), big and little endian, hard and 
soft float, O32, N32 and N64, all combinations except for little-endian 
soft-float N32 and N64 for which I don't have libraries to hand.  No 
regressions, and this fixes

FAIL: gdb.base/callfuncs.exp: Call function with many float arguments.

for O32 hard float both endian and N32/N64 big-endian both hard and soft 
float;

FAIL: gdb.base/call-sc.exp: p/c L; call call-sc-tld
FAIL: gdb.base/structs.exp: finish foo<n>; return 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: return foo<n>; return 1 structs-tld (GDB internal error)
FAIL: gdb.base/structs.exp: value foo<n> finished; return 1 structs-tld
FAIL: gdb.base/structs.exp: value foo<n> returned; return 1 structs-tld

for N32 and N64 hard float both endian; and

FAIL: gdb.base/structs.exp: p/c fun<n>(); call 1 structs-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-td-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-tf
FAIL: gdb.base/structs.exp: p/c fun<n>(); call 2 structs-tf-td
FAIL: gdb.base/structs.exp: value foo<n> finished; return 1 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-td-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> finished; return 2 structs-tf-td
FAIL: gdb.base/structs.exp: value foo<n> returned; return 1 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-td-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-tf
FAIL: gdb.base/structs.exp: value foo<n> returned; return 2 structs-tf-td

for N32 and N64 soft float.  OK to commit?

2008-07-18  Joseph Myers  <joseph@codesourcery.com>

	* mips-tdep.c (mips_n32n64_push_dummy_call): Handle passing
	128-bit long doubles in even-odd pairs of FPRs.  Do not
	right-align float arguments for big-endian.
	(mips_n32n64_return_value): Apply return value convention for
	structs containing one or two floating-point values to soft-float
	as well as hard-float.  Handle 128-bit long doubles in such
	structs.
	(mips_o32_push_dummy_call): Only skip one integer register for a
	float argument passed in an FPR.

Index: gdb/mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.476
diff -u -p -r1.476 mips-tdep.c
--- gdb/mips-tdep.c	14 Jul 2008 11:25:11 -0000	1.476
+++ gdb/mips-tdep.c	18 Jul 2008 13:35:45 -0000
@@ -3142,23 +3142,49 @@ mips_n32n64_push_dummy_call (struct gdba
 
       val = value_contents (arg);
 
+      /* A 128-bit long double value requires an even-odd pair of
+	 floating-point registers.  */
+      if (len == 16
+	  && fp_register_arg_p (gdbarch, typecode, arg_type)
+	  && (float_argreg & 1))
+	{
+	  float_argreg++;
+	  argreg++;
+	}
+
       if (fp_register_arg_p (gdbarch, typecode, arg_type)
 	  && argreg <= MIPS_LAST_ARG_REGNUM (gdbarch))
 	{
 	  /* This is a floating point value that fits entirely
-	     in a single register.  */
-	  LONGEST regval = extract_unsigned_integer (val, len);
+	     in a single register or a pair of registers.  */
+	  int reglen = (len <= MIPS64_REGSIZE ? len : MIPS64_REGSIZE);
+	  LONGEST regval = extract_unsigned_integer (val, reglen);
 	  if (mips_debug)
 	    fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
-				float_argreg, phex (regval, len));
+				float_argreg, phex (regval, reglen));
 	  regcache_cooked_write_unsigned (regcache, float_argreg, regval);
 
 	  if (mips_debug)
 	    fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
-				argreg, phex (regval, len));
+				argreg, phex (regval, reglen));
 	  regcache_cooked_write_unsigned (regcache, argreg, regval);
 	  float_argreg++;
 	  argreg++;
+	  if (len == 16)
+	    {
+	      regval = extract_unsigned_integer (val + reglen, reglen);
+	      if (mips_debug)
+		fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
+				    float_argreg, phex (regval, reglen));
+	      regcache_cooked_write_unsigned (regcache, float_argreg, regval);
+
+	      if (mips_debug)
+		fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
+				    argreg, phex (regval, reglen));
+	      regcache_cooked_write_unsigned (regcache, argreg, regval);
+	      float_argreg++;
+	      argreg++;
+	    }
 	}
       else
 	{
@@ -3199,8 +3225,7 @@ mips_n32n64_push_dummy_call (struct gdba
 		  if (gdbarch_byte_order (gdbarch) == BFD_ENDIAN_BIG)
 		    {
 		      if ((typecode == TYPE_CODE_INT
-			   || typecode == TYPE_CODE_PTR
-			   || typecode == TYPE_CODE_FLT)
+			   || typecode == TYPE_CODE_PTR)
 			  && len <= 4)
 			longword_offset = MIPS64_REGSIZE - len;
 		    }
@@ -3389,15 +3414,16 @@ mips_n32n64_return_value (struct gdbarch
 		   && (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 0)))
 		       == TYPE_CODE_FLT)
 		   && (TYPE_CODE (check_typedef (TYPE_FIELD_TYPE (type, 1)))
-		       == TYPE_CODE_FLT)))
-	   && tdep->mips_fpu_type != MIPS_FPU_NONE)
+		       == TYPE_CODE_FLT))))
     {
       /* A struct that contains one or two floats.  Each value is part
          in the least significant part of their floating point
-         register..  */
+         register (or GPR, for soft float).  */
       int regnum;
       int field;
-      for (field = 0, regnum = mips_regnum (gdbarch)->fp0;
+      for (field = 0, regnum = (tdep->mips_fpu_type != MIPS_FPU_NONE
+				? mips_regnum (gdbarch)->fp0
+				: MIPS_V0_REGNUM);
 	   field < TYPE_NFIELDS (type); field++, regnum += 2)
 	{
 	  int offset = (FIELD_BITPOS (TYPE_FIELDS (type)[field])
@@ -3405,11 +3431,27 @@ mips_n32n64_return_value (struct gdbarch
 	  if (mips_debug)
 	    fprintf_unfiltered (gdb_stderr, "Return float struct+%d\n",
 				offset);
-	  mips_xfer_register (gdbarch, regcache,
-			      gdbarch_num_regs (gdbarch) + regnum,
-			      TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)),
-			      gdbarch_byte_order (gdbarch),
-			      readbuf, writebuf, offset);
+	  if (TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)) == 16)
+	    {
+	      /* A 16-byte long double field goes in two consecutive
+		 registers.  */
+	      mips_xfer_register (gdbarch, regcache,
+				  gdbarch_num_regs (gdbarch) + regnum,
+				  8,
+				  gdbarch_byte_order (gdbarch),
+				  readbuf, writebuf, offset);
+	      mips_xfer_register (gdbarch, regcache,
+				  gdbarch_num_regs (gdbarch) + regnum + 1,
+				  8,
+				  gdbarch_byte_order (gdbarch),
+				  readbuf, writebuf, offset + 8);
+	    }
+	  else
+	    mips_xfer_register (gdbarch, regcache,
+				gdbarch_num_regs (gdbarch) + regnum,
+				TYPE_LENGTH (TYPE_FIELD_TYPE (type, field)),
+				gdbarch_byte_order (gdbarch),
+				readbuf, writebuf, offset);
 	}
       return RETURN_VALUE_REGISTER_CONVENTION;
     }
@@ -3612,15 +3654,13 @@ mips_o32_push_dummy_call (struct gdbarch
 		fprintf_unfiltered (gdb_stdlog, " - fpreg=%d val=%s",
 				    float_argreg, phex (regval, len));
 	      regcache_cooked_write_unsigned (regcache, float_argreg++, regval);
-	      /* CAGNEY: 32 bit MIPS ABI's always reserve two FP
-	         registers for each argument.  The below is (my
-	         guess) to ensure that the corresponding integer
-	         register has reserved the same space.  */
+	      /* Although two FP registers are reserved for each
+		 argument, only one corresponding integer register is
+		 reserved.  */
 	      if (mips_debug)
 		fprintf_unfiltered (gdb_stdlog, " - reg=%d val=%s",
 				    argreg, phex (regval, len));
-	      regcache_cooked_write_unsigned (regcache, argreg, regval);
-	      argreg += 2;
+	      regcache_cooked_write_unsigned (regcache, argreg++, regval);
 	    }
 	  /* Reserve space for the FP register.  */
 	  stack_offset += align_up (len, MIPS32_REGSIZE);

-- 
Joseph S. Myers
joseph@codesourcery.com


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