This is the mail archive of the gdb-patches@sources.redhat.com 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]

Re: [RFA] mips: Fix "info registers" output


On Sat, Mar 09, 2002 at 09:05:13PM -0500, Andrew Cagney wrote:
> >Meanwhile, Andrew, is this trivial fix OK?  It just makes us decode the
> >actual register values instead of two host pointers in our call to
> >unpack_double.
> 
> I suspect the code was more broken then you thought!

I don't know about that.  I pretty much rewrote it the first time I
tried to fix this, last June.

> If my memory is correct, the value is always stored LE in the register 
> pair.  The code manages to do a double swap and consequently doesn't 
> correctly re-order the registers in the dbl_buffer.
> 
> It is also really broken when a 32 bit ABI is on a 64 bit ISA.  (It very 
> nearly corrupts the stack).

I suppose...

> > /* copy the two floats into one double, and unpack both */
> >-      memcpy (dbl_buffer, raw_buffer, 2 * REGISTER_RAW_SIZE (FP0_REGNUM));
> >       flt1 = unpack_double (builtin_type_float, raw_buffer[HI], &inv1);
> >       flt2 = unpack_double (builtin_type_float, raw_buffer[LO], &inv2);
> >+      memcpy (dbl_buffer, raw_buffer[HI], REGISTER_RAW_SIZE (FP0_REGNUM));
> >+      memcpy (dbl_buffer + REGISTER_RAW_SIZE (FP0_REGNUM),
> >+	      raw_buffer[LO], REGISTER_RAW_SIZE (FP0_REGNUM));
> >       doub = unpack_double (builtin_type_double, dbl_buffer, &inv3);
> > 
> 
> I suspect a bigger improvement would look something like (I'm still not 
> 100% sure of what goes where with BE/LE though :-)
> 
> if (!FP_REGISTER_DOUBLE)

FP_REGISTER_DOUBLE describes a property of the ABI.  I don't really
think it's the appropriate check when printing floating-point
registers; we always take care to print the single-precision value even
if FP_REGISTER_DOUBLE, because they might be used in single-precision
anyway.

I sat down with an old patch from Don Howard (approved, but never
committed) and my old patches and thought about this for a bit.  I
fixed all the mentioned nits with both patches, and rethought some of
the design, and here's what I came up with.  The comments in the code
speak for themselves fairly well:

+/* Floating point register management.
+
+   Background: MIPS1 & 2 fp registers are 32 bits wide.  To support
+   64bit operations, these early MIPS cpus treat fp register pairs
+   (f0,f1) as a single register (d0).  Later MIPS cpu's have 64 bit fp
+   registers and offer a compatibility mode that emulates the MIPS2 fp
+   model.  When operating in MIPS2 fp compat mode, later cpu's split
+   double precision floats into two 32-bit chunks and store them in
+   consecutive fp regs.  To display 64-bit floats stored in this
+   fashion, we have to combine 32 bits from f0 and 32 bits from f1.
+   Throw in user-configurable endianness and you have a real mess.
+
+   The way this works is:
+     - If we are in 32-bit mode or on a 32-bit processor, then a 64-bit
+       double-precision value will be split across two logical registers.
+       The lower-numbered logical register will hold the low-order bits,
+       regardless of the processor's endianness.
+     - If we are on a 64-bit processor, and we are looking for a
+       single-precision value, it will be in the low ordered bits
+       of a 64-bit GPR (after mfc1, for example) or a 64-bit register
+       save slot in memory.
+     - If we are in 64-bit mode, everything is straightforward.

I add two functions, mips_read_fp_register_single and
mips_read_fp_register_double, that understand this.  In the process, I
eliminated the next-to-last use of REGISTER_CONVERT_TO_TYPE, and
simplified a lot of code.

Behaves correctly on the only target I have handy, which is a true
32-bit machine.  Andrew, do you like the look of this?

(Don's in the changelog because I cribbed one function and a lot of
comments from him.)

-- 
Daniel Jacobowitz                           Carnegie Mellon University
MontaVista Software                         Debian GNU/Linux Developer

2002-03-10  Daniel Jacobowitz  <drow@mvista.com>
	    Don Howard <dhoward@redhat.com>

	* mips-tdep.c (ST0_FR): Define.
	(mips2_fp_compat): New function.
	(mips_read_fp_register_single): New function.
	(mips_read_fp_register_double): New function.
	(mips_print_register): Use them.
	(do_fp_register_row): Likewise.

Index: mips-tdep.c
===================================================================
RCS file: /cvs/src/src/gdb/mips-tdep.c,v
retrieving revision 1.66
diff -u -r1.66 mips-tdep.c
--- mips-tdep.c	2002/02/20 22:51:41	1.66
+++ mips-tdep.c	2002/03/10 06:43:35
@@ -44,6 +44,10 @@
 #include "elf-bfd.h"
 #include "symcat.h"
 
+/* A useful bit in the CP0 status register (PS_REGNUM).  */
+/* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip.  */
+#define ST0_FR (1 << 26)
+
 /* The sizes of floating point registers.  */
 
 enum
@@ -174,6 +178,27 @@
     return 4;
 }
 
+/* Determine if a MIPS3 or later cpu is operating in MIPS{1,2} FPU
+   compatiblity mode.  A return value of 1 means that we have
+   physical 64-bit registers, but should treat them as 32-bit registers.  */
+
+static int
+mips2_fp_compat (void)
+{
+  /* MIPS1 and MIPS2 have only 32 bit FPRs, and the FR bit is not
+     meaningful.  */
+  if (REGISTER_RAW_SIZE (FP0_REGNUM) == 4)
+    return 0;
+
+  /* Otherwise check the FR bit in the status register - it controls
+     the FP compatiblity mode.  If it is clear we are in compatibility
+     mode.  */
+  if ((read_register (PS_REGNUM) & ST0_FR) == 0)
+    return 1;
+  
+  return 0;
+}
+
 /* Indicate that the ABI makes use of double-precision registers
    provided by the FPU (rather than combining pairs of registers to
    form double-precision values).  Do not use "TARGET_IS_MIPS64" to
@@ -257,6 +282,9 @@
 static CORE_ADDR after_prologue (CORE_ADDR pc,
 				 mips_extra_func_info_t proc_desc);
 
+static void mips_read_fp_register_single (int regno, char *rare_buffer);
+static void mips_read_fp_register_double (int regno, char *rare_buffer);
+
 /* This value is the model of MIPS in use.  It is derived from the value
    of the PrID register.  */
 
@@ -2676,7 +2704,103 @@
     }
 }
 
+/* Floating point register management.
+
+   Background: MIPS1 & 2 fp registers are 32 bits wide.  To support
+   64bit operations, these early MIPS cpus treat fp register pairs
+   (f0,f1) as a single register (d0).  Later MIPS cpu's have 64 bit fp
+   registers and offer a compatibility mode that emulates the MIPS2 fp
+   model.  When operating in MIPS2 fp compat mode, later cpu's split
+   double precision floats into two 32-bit chunks and store them in
+   consecutive fp regs.  To display 64-bit floats stored in this
+   fashion, we have to combine 32 bits from f0 and 32 bits from f1.
+   Throw in user-configurable endianness and you have a real mess.
+
+   The way this works is:
+     - If we are in 32-bit mode or on a 32-bit processor, then a 64-bit
+       double-precision value will be split across two logical registers.
+       The lower-numbered logical register will hold the low-order bits,
+       regardless of the processor's endianness.
+     - If we are on a 64-bit processor, and we are looking for a
+       single-precision value, it will be in the low ordered bits
+       of a 64-bit GPR (after mfc1, for example) or a 64-bit register
+       save slot in memory.
+     - If we are in 64-bit mode, everything is straightforward.
+
+   Note that this code only deals with "live" registers at the top of the
+   stack.  We will attempt to deal with saved registers later, when
+   the raw/cooked register interface is in place. (We need a general
+   interface that can deal with dynamic saved register sizes -- fp
+   regs could be 32 bits wide in one frame and 64 on the frame above
+   and below).  */
+
+/* Copy a 32-bit single-precision value from the current frame
+   into rare_buffer.  */
+
 static void
+mips_read_fp_register_single (int regno, char *rare_buffer)
+{
+  int raw_size = REGISTER_RAW_SIZE (regno);
+  char *raw_buffer = alloca (raw_size);
+
+  if (read_relative_register_raw_bytes (regno, raw_buffer))
+    error ("can't read register %d (%s)", regno, REGISTER_NAME (regno));
+  if (raw_size == 8)
+    {
+      /* We have a 64-bit value for this register.  Find the low-order
+	 32 bits.  */
+      int offset;
+
+      if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+	offset = 4;
+      else
+	offset = 0;
+
+      memcpy (rare_buffer, raw_buffer + offset, 4);
+    }
+  else
+    {
+      memcpy (rare_buffer, raw_buffer, 4);
+    }
+}
+
+/* Copy a 64-bit double-precision value from the current frame into
+   rare_buffer.  This may include getting half of it from the next
+   register.  */
+
+static void
+mips_read_fp_register_double (int regno, char *rare_buffer)
+{
+  int raw_size = REGISTER_RAW_SIZE (regno);
+
+  if (raw_size == 8 && !mips2_fp_compat ())
+    {
+      /* We have a 64-bit value for this register, and we should use
+	 all 64 bits.  */
+      if (read_relative_register_raw_bytes (regno, rare_buffer))
+	error ("can't read register %d (%s)", regno, REGISTER_NAME (regno));
+    }
+  else
+    {
+      if ((regno - FP0_REGNUM) & 1)
+	internal_error (__FILE__, __LINE__,
+			"mips_read_fp_register_double: bad access to "
+			"odd-numbered FP register");
+
+      if (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG)
+	{
+	  mips_read_fp_register_single (regno, rare_buffer + 4);
+	  mips_read_fp_register_single (regno + 1, rare_buffer);
+	}
+      else      
+	{
+	  mips_read_fp_register_single (regno, rare_buffer);
+	  mips_read_fp_register_single (regno + 1, rare_buffer + 4);
+	}
+    }
+}
+
+static void
 mips_print_register (int regnum, int all)
 {
   char raw_buffer[MAX_REGISTER_RAW_SIZE];
@@ -2688,22 +2812,23 @@
       return;
     }
 
-  /* If an even floating point register, also print as double. */
+  /* If we have a actual 32-bit floating point register (or we are in
+     32-bit compatibility mode), and the register is even-numbered,
+     also print it as a double (spanning two registers).  */
   if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (regnum)) == TYPE_CODE_FLT
+      && (REGISTER_RAW_SIZE (regnum) == 4
+	  || mips2_fp_compat ())
       && !((regnum - FP0_REGNUM) & 1))
-    if (REGISTER_RAW_SIZE (regnum) == 4)	/* this would be silly on MIPS64 or N32 (Irix 6) */
-      {
-	char dbuffer[2 * MAX_REGISTER_RAW_SIZE];
+    {
+      char dbuffer[2 * MAX_REGISTER_RAW_SIZE];
 
-	read_relative_register_raw_bytes (regnum, dbuffer);
-	read_relative_register_raw_bytes (regnum + 1, dbuffer + MIPS_REGSIZE);
-	REGISTER_CONVERT_TO_TYPE (regnum, builtin_type_double, dbuffer);
+      mips_read_fp_register_double (regnum, dbuffer);
 
-	printf_filtered ("(d%d: ", regnum - FP0_REGNUM);
-	val_print (builtin_type_double, dbuffer, 0, 0,
-		   gdb_stdout, 0, 1, 0, Val_pretty_default);
-	printf_filtered ("); ");
-      }
+      printf_filtered ("(d%d: ", regnum - FP0_REGNUM);
+      val_print (builtin_type_double, dbuffer, 0, 0,
+		 gdb_stdout, 0, 1, 0, Val_pretty_default);
+      printf_filtered ("); ");
+    }
   fputs_filtered (REGISTER_NAME (regnum), gdb_stdout);
 
   /* The problem with printing numeric register names (r26, etc.) is that
@@ -2717,8 +2842,10 @@
 
   /* If virtual format is floating, print it that way.  */
   if (TYPE_CODE (REGISTER_VIRTUAL_TYPE (regnum)) == TYPE_CODE_FLT)
-    if (FP_REGISTER_DOUBLE)
-      {				/* show 8-byte floats as float AND double: */
+    if (REGISTER_RAW_SIZE (regnum) == 8 && !mips2_fp_compat ())
+      {
+	/* We have a meaningful 64-bit value in this register.  Show
+	   it as a 32-bit float and a 64-bit double.  */
 	int offset = 4 * (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG);
 
 	printf_filtered (" (float) ");
@@ -2753,35 +2880,25 @@
 static int
 do_fp_register_row (int regnum)
 {				/* do values for FP (float) regs */
-  char *raw_buffer[2];
-  char *dbl_buffer;
-  /* use HI and LO to control the order of combining two flt regs */
-  int HI = (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG);
-  int LO = (TARGET_BYTE_ORDER != BFD_ENDIAN_BIG);
+  char *raw_buffer;
   double doub, flt1, flt2;	/* doubles extracted from raw hex data */
   int inv1, inv2, inv3;
 
-  raw_buffer[0] = (char *) alloca (REGISTER_RAW_SIZE (FP0_REGNUM));
-  raw_buffer[1] = (char *) alloca (REGISTER_RAW_SIZE (FP0_REGNUM));
-  dbl_buffer = (char *) alloca (2 * REGISTER_RAW_SIZE (FP0_REGNUM));
+  raw_buffer = (char *) alloca (2 * REGISTER_RAW_SIZE (FP0_REGNUM));
 
-  /* Get the data in raw format.  */
-  if (read_relative_register_raw_bytes (regnum, raw_buffer[HI]))
-    error ("can't read register %d (%s)", regnum, REGISTER_NAME (regnum));
-  if (REGISTER_RAW_SIZE (regnum) == 4)
-    {
-      /* 4-byte registers: we can fit two registers per row. */
-      /* Also print every pair of 4-byte regs as an 8-byte double. */
-      if (read_relative_register_raw_bytes (regnum + 1, raw_buffer[LO]))
-	error ("can't read register %d (%s)",
-	       regnum + 1, REGISTER_NAME (regnum + 1));
-
-      /* copy the two floats into one double, and unpack both */
-      memcpy (dbl_buffer, raw_buffer, 2 * REGISTER_RAW_SIZE (FP0_REGNUM));
-      flt1 = unpack_double (builtin_type_float, raw_buffer[HI], &inv1);
-      flt2 = unpack_double (builtin_type_float, raw_buffer[LO], &inv2);
-      doub = unpack_double (builtin_type_double, dbl_buffer, &inv3);
+  if (REGISTER_RAW_SIZE (regnum) == 4 || mips2_fp_compat ())
+    {
+      /* 4-byte registers: we can fit two registers per row.  */
+      /* Also print every pair of 4-byte regs as an 8-byte double.  */
+      mips_read_fp_register_single (regnum, raw_buffer);
+      flt1 = unpack_double (builtin_type_float, raw_buffer, &inv1);
+
+      mips_read_fp_register_single (regnum + 1, raw_buffer);
+      flt2 = unpack_double (builtin_type_float, raw_buffer, &inv2);
 
+      mips_read_fp_register_double (regnum, raw_buffer);
+      doub = unpack_double (builtin_type_double, raw_buffer, &inv3);
+      
       printf_filtered (" %-5s", REGISTER_NAME (regnum));
       if (inv1)
 	printf_filtered (": <invalid float>");
@@ -2805,14 +2922,14 @@
       regnum += 2;
     }
   else
-    {				/* eight byte registers: print each one as float AND as double. */
-      int offset = 4 * (TARGET_BYTE_ORDER == BFD_ENDIAN_BIG);
-
-      memcpy (dbl_buffer, raw_buffer[HI], 2 * REGISTER_RAW_SIZE (FP0_REGNUM));
-      flt1 = unpack_double (builtin_type_float,
-			    &raw_buffer[HI][offset], &inv1);
-      doub = unpack_double (builtin_type_double, dbl_buffer, &inv3);
-
+    {
+      /* Eight byte registers: print each one as float AND as double.  */
+      mips_read_fp_register_single (regnum, raw_buffer);
+      flt1 = unpack_double (builtin_type_double, raw_buffer, &inv1);
+
+      mips_read_fp_register_double (regnum, raw_buffer);
+      doub = unpack_double (builtin_type_double, raw_buffer, &inv3);
+      
       printf_filtered (" %-5s: ", REGISTER_NAME (regnum));
       if (inv1)
 	printf_filtered ("<invalid float>");


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