[3/3] MIPS: Provide FPU info and decode FCSR in `info float'

Message ID 1418798765-23198-4-git-send-email-yao@codesourcery.com
State New, archived
Headers

Commit Message

Yao Qi Dec. 17, 2014, 6:46 a.m. UTC
  This patch is the V2.  V1 can be found in
https://sourceware.org/ml/gdb-patches/2012-05/msg00938.html
V2 is to address Joel's comment
<https://sourceware.org/ml/gdb-patches/2012-06/msg00289.html> about
keeping dumping floating point registers.  Additionally, command
'info float' prints bits on nan2008 and abs2008.

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

 The change below provides a MIPS-specific handler for the:

(gdb) info float

command.  It provides information about the FPU type available (if any),
the FPU register width, and decodes the CP1 Floating Point Control and
Status Register (FCSR):

(gdb) print /x $fsr
$1 = 0xff83ffff
(gdb) info float
fpu type: double-precision
reg size: 32 bits
cond    : 0 1 2 3 4 5 6 7
cause   : inexact uflow oflow div0 inval unimp
mask    : inexact uflow oflow div0 inval
flags   : inexact uflow oflow div0 inval
rounding: -inf
flush   : zero

 One point to note about CP1.FCSR are the non-standard Flush-to-Nearest
and Flush-Override bits.  They are not a part of the MIPS architecture and
take two positions reserved for an implementation-dependent use in the
architecture.  They are present in all the FPU implementations made by
MIPS Technologies since the spin-off from SGI.

 I haven't been able to track down a single other MIPS FPU implementation
that would make any use of these bits and they are required to be
hardwired to zero by the architecture specification if unimplemented.
Therefore I think it makes sense to report them in the current way.

 GDB has no guaranteed access to the CP0 Processor Identification (PRId)
register to validate this feature properly and the ID information stored
in the CP1 Floating Point Implementation Register (FIR) is from my
experience not reliable enough (there's no Company ID available there for
once unlike in CP0.PRId and Processor ID is not guaranteed to be unique).

 As a side note we should probably dump CP1.FIR information as well, as
there's useful stuff indicating some FPU features there.  That's material
for another change however.

gdb/

2014-12-17  Nigel Stephens  <nigel@mips.com>
            Maciej W. Rozycki  <macro@codesourcery.com>

	* mips-tdep.c (print_fpu_flags): New function.
	(mips_print_float_info): Likewise.
	(mips_gdbarch_init): Install mips_print_float_info as gdbarch
	print_float_info routine.

gdb/testsuite/

2014-12-17  Nigel Stephens  <nigel@mips.com>
            Maciej W. Rozycki  <macro@codesourcery.com>

	* gdb.base/float.exp: Handle the new output from "info float" on
	MIPS targets.
---
 gdb/mips-tdep.c                  | 95 ++++++++++++++++++++++++++++++++++++++++
 gdb/testsuite/gdb.base/float.exp |  9 +++-
 2 files changed, 103 insertions(+), 1 deletion(-)
  

Comments

Pedro Alves Dec. 17, 2014, 10:53 a.m. UTC | #1
On 12/17/2014 06:46 AM, Yao Qi wrote:

> +static void
> +mips_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
> +		      struct frame_info *frame, const char *args)
> +{
> +  int fcsr = mips_regnum (gdbarch)->fp_control_status;
> +  enum mips_fpu_type type = MIPS_FPU_TYPE (gdbarch);
> +  ULONGEST fcs = 0;
> +  int i;
> +
> +  if (fcsr == -1 || !deprecated_frame_register_read (frame, fcsr, NULL))
> +    type = MIPS_FPU_NONE;

"deprecated" method usage alert.  It's better to use methods that return
values and then print "<unsaved>", "<unavailable>" as appropriate,
though in this case you may be able to just use read_frame_register_unsigned
instead.

> +
> +  fprintf_filtered (file, "fpu type: %s\n",
> +		    type == MIPS_FPU_DOUBLE ? "double-precision"
> +		    : type == MIPS_FPU_SINGLE ? "single-precision"
> +		    : "none / unused");
> +
> +  if (type == MIPS_FPU_NONE)
> +    return;
> +

Thanks,
Pedro Alves
  
Maciej W. Rozycki Dec. 21, 2014, 12:13 a.m. UTC | #2
On Wed, 17 Dec 2014, Pedro Alves wrote:

> > +static void
> > +mips_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
> > +		      struct frame_info *frame, const char *args)
> > +{
> > +  int fcsr = mips_regnum (gdbarch)->fp_control_status;
> > +  enum mips_fpu_type type = MIPS_FPU_TYPE (gdbarch);
> > +  ULONGEST fcs = 0;
> > +  int i;
> > +
> > +  if (fcsr == -1 || !deprecated_frame_register_read (frame, fcsr, NULL))
> > +    type = MIPS_FPU_NONE;
> 
> "deprecated" method usage alert.  It's better to use methods that return
> values and then print "<unsaved>", "<unavailable>" as appropriate,
> though in this case you may be able to just use read_frame_register_unsigned
> instead.

 For the record, we use `get_frame_register_unsigned' in such cases 
elsewhere, e.g. `micromips_bc1_pc' and we don't expect it to fail on the 
assumption that `fp_control_status' will have been set correctly.  I see 
this change went in already, but I'd like to see it updated with a 
follow-up change for consistency.

 Specifically `get_frame_register_unsigned' is unsuitable here (as we want 
to poke at the actual register file, not the frame state), but I think a 
failure from `read_frame_register_unsigned' should be signalled as an 
error (exception being thrown, just as from the former function) rather 
than being silently treated as if there were no FPU.

 Also I think the registers should be dumped earlier on, with only generic 
FPU parameters preceding, as the extra decoded FCSR information is an 
elaboration of raw register contents, so I find it logical (as far as the 
reading flow of English text is considered) to be presented later, i.e.:

fpu type: ...
reg size: ...
[registers]
cond    : ...
[...]

Or so I think.  It would help reviewing it if the updated version of the 
patch was presented with corresponding output produced accompanying, just 
as I did with the original version.  Otherwise it requires drawing the 
output in one's head which is not necessarily straightforward.

  Maciej
  

Patch

diff --git a/gdb/mips-tdep.c b/gdb/mips-tdep.c
index eb99910..d528969 100644
--- a/gdb/mips-tdep.c
+++ b/gdb/mips-tdep.c
@@ -73,6 +73,9 @@  static int micromips_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
 static int mips16_insn_at_pc_has_delay_slot (struct gdbarch *gdbarch,
 					     CORE_ADDR addr, int mustbe32);
 
+static void mips_print_float_info (struct gdbarch *, struct ui_file *,
+				   struct frame_info *, const char *);
+
 /* A useful bit in the CP0 status register (MIPS_PS_REGNUM).  */
 /* This bit is set if we are emulating 32-bit FPRs on a 64-bit chip.  */
 #define ST0_FR (1 << 26)
@@ -6387,6 +6390,96 @@  mips_print_register (struct ui_file *file, struct frame_info *frame,
 			      &opts, 0, file);
 }
 
+/* Print IEEE exception condition bits in FLAGS.  */
+
+static void
+print_fpu_flags (struct ui_file *file, int flags)
+{
+  if (flags & (1 << 0))
+    fputs_filtered (" inexact", file);
+  if (flags & (1 << 1))
+    fputs_filtered (" uflow", file);
+  if (flags & (1 << 2))
+    fputs_filtered (" oflow", file);
+  if (flags & (1 << 3))
+    fputs_filtered (" div0", file);
+  if (flags & (1 << 4))
+    fputs_filtered (" inval", file);
+  if (flags & (1 << 5))
+    fputs_filtered (" unimp", file);
+  fputc_filtered ('\n', file);
+}
+
+/* Print interesting information about the floating point processor
+   (if present) or emulator.  */
+
+static void
+mips_print_float_info (struct gdbarch *gdbarch, struct ui_file *file,
+		      struct frame_info *frame, const char *args)
+{
+  int fcsr = mips_regnum (gdbarch)->fp_control_status;
+  enum mips_fpu_type type = MIPS_FPU_TYPE (gdbarch);
+  ULONGEST fcs = 0;
+  int i;
+
+  if (fcsr == -1 || !deprecated_frame_register_read (frame, fcsr, NULL))
+    type = MIPS_FPU_NONE;
+
+  fprintf_filtered (file, "fpu type: %s\n",
+		    type == MIPS_FPU_DOUBLE ? "double-precision"
+		    : type == MIPS_FPU_SINGLE ? "single-precision"
+		    : "none / unused");
+
+  if (type == MIPS_FPU_NONE)
+    return;
+
+  fprintf_filtered (file, "reg size: %d bits\n",
+		    register_size (gdbarch, mips_regnum (gdbarch)->fp0) * 8);
+
+  fcs = get_frame_register_unsigned (frame, fcsr);
+
+  fputs_filtered ("cond    :", file);
+  if (fcs & (1 << 23))
+    fputs_filtered (" 0", file);
+  for (i = 1; i <= 7; i++)
+    if (fcs & (1 << (24 + i)))
+      fprintf_filtered (file, " %d", i);
+  fputc_filtered ('\n', file);
+
+  fputs_filtered ("cause   :", file);
+  print_fpu_flags (file, (fcs >> 12) & 0x3f);
+  fputs ("mask    :", stdout);
+  print_fpu_flags (file, (fcs >> 7) & 0x1f);
+  fputs ("flags   :", stdout);
+  print_fpu_flags (file, (fcs >> 2) & 0x1f);
+
+  fputs_filtered ("rounding: ", file);
+  switch (fcs & 3)
+    {
+    case 0: fputs_filtered ("nearest\n", file); break;
+    case 1: fputs_filtered ("zero\n", file); break;
+    case 2: fputs_filtered ("+inf\n", file); break;
+    case 3: fputs_filtered ("-inf\n", file); break;
+    }
+
+  fputs_filtered ("flush   :", file);
+  if (fcs & (1 << 21))
+    fputs_filtered (" nearest", file);
+  if (fcs & (1 << 22))
+    fputs_filtered (" override", file);
+  if (fcs & (1 << 24))
+    fputs_filtered (" zero", file);
+  if ((fcs & (0xb << 21)) == 0)
+    fputs_filtered (" no", file);
+  fputc_filtered ('\n', file);
+
+  fprintf_filtered (file, "nan2008 : %s\n", fcs & (1 << 18) ? "yes" : "no");
+  fprintf_filtered (file, "abs2008 : %s\n", fcs & (1 << 19) ? "yes" : "no");
+  fputc_filtered ('\n', file);
+
+  default_print_float_info (gdbarch, file, frame, args);
+}
+
 /* Replacement for generic do_registers_info.
    Print regs in pretty columns.  */
 
@@ -8737,6 +8830,8 @@  mips_gdbarch_init (struct gdbarch_info info, struct gdbarch_list *arches)
   set_gdbarch_push_dummy_code (gdbarch, mips_push_dummy_code);
   set_gdbarch_frame_align (gdbarch, mips_frame_align);
 
+  set_gdbarch_print_float_info (gdbarch, mips_print_float_info);
+
   set_gdbarch_convert_register_p (gdbarch, mips_convert_register_p);
   set_gdbarch_register_to_value (gdbarch, mips_register_to_value);
   set_gdbarch_value_to_register (gdbarch, mips_value_to_register);
diff --git a/gdb/testsuite/gdb.base/float.exp b/gdb/testsuite/gdb.base/float.exp
index 5da9630..3ecb01b 100644
--- a/gdb/testsuite/gdb.base/float.exp
+++ b/gdb/testsuite/gdb.base/float.exp
@@ -68,7 +68,14 @@  if { [istarget "aarch64*-*-*"] } then {
 	}
     }
 } elseif [istarget "mips*-*-*"] then {
-    gdb_test "info float" "f0:.*flt:.*dbl:.*" "info float"
+    gdb_test_multiple "info float" "info float" {
+	-re "fpu type: none*" {
+	      pass "info float (without FPU)"
+	  }
+	-re "fpu type:.*cause.*mask.*flags.*round.*flush.*" {
+	      pass "info float (with FPU)"
+	  }
+    }
 } elseif [istarget "powerpc*-*-*"] then {
     gdb_test_multiple "info float" "info_float" {
         -re "f0.*f1.*f31.*fpscr.*$gdb_prompt $" {