This is the mail archive of the gdb-patches@sourceware.cygnus.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]

x86 FPU support: "info float" and `long double'


OK, here is my attempt at implementing the "info float" comment and
prividing proper long double support without any hacks.  Once this is
in we should remove the Linux-specific TARGET_{EXTRACT|STORE}_FLOATING
hooks that were introduced in findvar.c, and try to find a better
solution for TARGET_ANALYZE_FLOATING.

The output for "info float" is almost identical to DJGPP.  The FP
registers are printed in physical order with "=>" indicating the top
of stack.  Printing the opcode mnemonic is not so easy.  I looked at
libopcode, but I could not find an appropriate entry-point.

Anyway, the output looks like:

(gdb) info float
status 0x3800: flags 0000; top 7;
control 0x37f: 64 bit; NEAR; mask INVAL DENOR DIVZ OVERF UNDER LOS;
last FP instruction: opcode 0xdd05; pc 0x23:0x8048495; operand 0x2b:0x8048f04
regno tag     msb              lsb  value
=> 7: valid   4000c90fdaa22168c000  3.141592653589793116
   6: empty   3ff7d100000000000000  0.006378173828125
   5: empty   40098000000000000000  1024
   4: empty   3fff8000000000000000  1
   3: empty   3fff8000000000000000  1
   2: empty   3ffb9d8909ff2e48e8c0  0.076921537494659423997
   1: empty   3ffbdaf3e93361992eb3  0.10691053569844352396
   0: empty   00000000000000000000  0
(gdb)  

Suggestions for improvement are welcome!

I don't know if this stuff is ready for inclusion yet.  Given the size
of the patch a copyright assignment is probably needed.  I'll try to
assign past and future changes as soon as possible.

If Jim is still making changes to the register file layout, I'll keep
track of his changes and resend the patch.

Mark


1999-10-21  Mark Kettenis  <kettenis@gnu.org>

	* i387-tdep.c: Strip PARAMS.
	(i387_float_info): New function.
	(i387_register_virtual_type): New function.
	(i387_register_convert_to_virtual, i387_register_convert_to_raw):
	New functions.
	(i387_extract_floating, i387_store_floating): Removed.

	* config/i386/tm-i386.h (TARGET_LONG_DOUBLE_BIT,
	TARGET_LONG_DOUBLE_FORMAT): New definitions.
	TARGET_ANALYZE_FLOATING: Moved here from config/i386/tm-linux.h.
	(i387_register_virtual_type): New declaration.
	(REGISTER_VIRTUAL_TYPE): Call i387_register_virtual_type to get
	data type of FP data registers.
	(i387_to_double, double_to_i387): Removed declarations.
	(i387_register_convert_to_virtual, i387_register_convert_to_raw):
	New declarations.
	(REGISTER_CONVERT_TO_VIRTUAL): Let
	i387_register_convert_to_virtual handle the conversion.
	(REGISTER_CONVERT_TO_RAW): Let i387_register_convert_to_raw handle
	the conversion.
	[HAVE_I387_REGS] (i387_float_info): New declaration.
	[HAVE_I387_REGS] (FLOAT_INFO): New define.

	* config/i386/tm-linux.h (TARGET_LONG_DOUBLE_BIT,
	TARGET_EXTRACT_FLOATING, TARGET_STORE_FLOATING,
	REGISTER_CONVERT_TO_VIRTUAL, REGISTER_CONVERT_TO_RAW,
	REGISTER_VIRTUAL_TYPE): Remove definitions.
	(TARGET_ANALYZE_FLOATING): Moved to config/i386/tm-i386.h.
	(i387_extract_floating, i387_store_floating): Remove declarations.
	(LD_I387): Do not define.


Index: gdb/gdb/i387-tdep.c
===================================================================
RCS file: /var/cvsroot/gdb/gdb/i387-tdep.c,v
retrieving revision 1.1.1.4
diff -u -r1.1.1.4 i387-tdep.c
--- gdb/gdb/i387-tdep.c	1999/10/02 19:51:09	1.1.1.4
+++ gdb/gdb/i387-tdep.c	1999/10/21 15:57:28
@@ -1,5 +1,5 @@
 /* Intel 387 floating point stuff.
-   Copyright (C) 1988, 1989, 1991, 1998 Free Software Foundation, Inc.
+   Copyright (C) 1988, 1989, 1991, 1998, 1999 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -26,34 +26,31 @@
 #include "gdbcore.h"
 #include "floatformat.h"
 
-void i387_to_double PARAMS ((char *, char *));
-void double_to_i387 PARAMS ((char *, char *));
+static void print_387_control_bits (unsigned int control);
+static void print_387_status_bits  (unsigned int status);
 
-static void print_387_control_bits PARAMS ((unsigned int control));
-static void print_387_status_bits  PARAMS ((unsigned int status));
+
+/* FIXME: Eliminate the next two functions when we have the time to
+   change all the callers.  */
 
-/* FIXME:  Eliminate these routines when we have the time to change all
-   the callers.  */
+void i387_to_double (char *from, char *to);
+void double_to_i387 (char *from, char *to);
 
 void
-i387_to_double (from, to)
-     char *from;
-     char *to;
+i387_to_double (char *from, char *to)
 {
   floatformat_to_double (&floatformat_i387_ext, from, (double *) to);
 }
 
 void
-double_to_i387 (from, to)
-     char *from;
-     char *to;
+double_to_i387 (char *from, char *to)
 {
   floatformat_from_double (&floatformat_i387_ext, (double *) from, to);
 }
 
+
 static void
-print_387_control_bits (control)
-     unsigned int control;
+print_387_control_bits (unsigned int control)
 {
   switch ((control >> 8) & 3)
     {
@@ -108,9 +105,10 @@
 	     local_hex_string (control & 0xe080));
 }
 
+/* FIXME: Make this static once we have converted all i386 targets to
+   use the GDB register file.  */
 void
-print_387_control_word (control)
-     unsigned int control;
+print_387_control_word (unsigned int control)
 {
   printf_filtered ("control %s:", local_hex_string(control & 0xffff));
   print_387_control_bits (control);
@@ -118,8 +116,7 @@
 }
 
 static void
-print_387_status_bits (status)
-     unsigned int status;
+print_387_status_bits (unsigned int status)
 {
   printf_unfiltered (" flags %d%d%d%d; ",
 		     (status & 0x4000) != 0,
@@ -140,53 +137,141 @@
     }
 }
 
+/* FIXME: Make this static once we have converted all i386 targets to
+   use the GDB register file.  */
 void
-print_387_status_word (status)
-     unsigned int status;
+print_387_status_word (unsigned int status)
 {
   printf_filtered ("status %s:", local_hex_string (status & 0xffff));
   print_387_status_bits (status);
   puts_unfiltered ("\n");
 }
 
-#ifdef LD_I387
-int
-i387_extract_floating (PTR addr, int len, DOUBLEST *dretptr)
+/* Print out the i387 floating poin state.  */
+void
+i387_float_info (void)
 {
-  if (len == TARGET_LONG_DOUBLE_BIT / 8)
+  unsigned int fctrl;
+  unsigned int fstat;
+  unsigned int ftag;
+  unsigned int fcs;
+  unsigned int fcoff;
+  unsigned int fds;
+  unsigned int fdoff;
+  unsigned int fop;
+  int fpreg;
+  int top;
+
+  read_register_gen (FCTRL_REGNUM, (char *) &fctrl);
+  read_register_gen (FSTAT_REGNUM, (char *) &fstat);
+  read_register_gen (FTAG_REGNUM,  (char *) &ftag);
+  read_register_gen (FCS_REGNUM,   (char *) &fcs);
+  read_register_gen (FCOFF_REGNUM, (char *) &fcoff);
+  read_register_gen (FDS_REGNUM,   (char *) &fds);
+  read_register_gen (FDOFF_REGNUM, (char *) &fdoff);
+  read_register_gen (FOP_REGNUM,   (char *) &fop);
+  
+  print_387_status_word (fstat);
+  print_387_control_word (fctrl);
+  printf_unfiltered ("last FP instruction: ");
+  printf_unfiltered ("opcode %s; ",
+		     local_hex_string (fop ? (fop | 0xd800) : 0));
+  printf_unfiltered ("pc %s:", local_hex_string (fcs));
+  printf_unfiltered ("%s; ", local_hex_string (fcoff));
+  printf_unfiltered ("operand %s", local_hex_string (fds));
+  printf_unfiltered (":%s\n", local_hex_string (fdoff));
+
+  top = ((fstat >> 11) & 7);
+
+  printf_unfiltered ("regno tag     msb              lsb  value\n");
+  for (fpreg = 7; fpreg >= 0; fpreg--)
     {
-      if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
-	{
-	  DOUBLEST retval;
+      unsigned char raw[FPU_REG_RAW_SIZE];
+      DOUBLEST value;
+      int i;
+
+      printf_unfiltered ("%s %d: ", fpreg == top ? "=>" : "  ", fpreg);
 
-	  memcpy (dretptr, addr, sizeof (retval));
+      switch ((ftag >> (fpreg * 2)) & 3)
+	{
+	case 0:
+	  printf_unfiltered ("valid   ");
+	  break;
+	case 1:
+	  printf_unfiltered ("zero    ");
+	  break;
+	case 2:
+	  printf_unfiltered ("special ");
+	  break;
+	case 3:
+	  printf_unfiltered ("empty   ");
+	  break;
 	}
-      else
-	floatformat_to_doublest (TARGET_LONG_DOUBLE_FORMAT, addr, dretptr);
 
-      return 1;
+      read_register_gen ((fpreg + 8 - top) % 8 + FP0_REGNUM, raw);
+      
+      for (i = 9; i >= 0; i--)
+	printf_unfiltered ("%02x", raw[i]);
+
+      floatformat_to_doublest (&floatformat_i387_ext, raw, &value);
+
+#ifdef PRINTF_HAS_LONG_DOUBLE
+      printf_unfiltered ("  %.20Lg\n", (long double) value);
+#else
+      printf_unfiltered ("  %.20g\n", (double) value);
+#endif
     }
-  else
-    return 0;
 }
 
-int
-i387_store_floating (PTR addr, int len, DOUBLEST val)
+
+/* Return the "standard" data type of data in the FPU data registers.
+   We use `long double' if the host has them in a representation
+   equivalent with the i387 extended format, and `double' otherwise.  */
+struct type *
+i387_register_virtual_type (void)
 {
-  if (len == TARGET_LONG_DOUBLE_BIT / 8)
-    {
-      /* This `if' may be totally stupid.  I just put it in here to be
-	 absolutely sure I'm preserving the semantics of the code I'm
-	 frobbing, while I try to maintain portability boundaries; I
-	 don't actually know exactly what it's doing.  -JimB, May 1999 */
-      if (HOST_LONG_DOUBLE_FORMAT == TARGET_LONG_DOUBLE_FORMAT)
-	memcpy (addr, &val, sizeof (val));
-      else
-	floatformat_from_doublest (TARGET_LONG_DOUBLE_FORMAT, &val, addr);
+#ifdef HAVE_LONG_DOUBLE
+  if (sizeof (long double) == (TARGET_LONG_DOUBLE_BIT / TARGET_CHAR_BIT))
+    return  builtin_type_long_double;
+#endif
+  return builtin_type_double;
+}
 
-      return 1;
+/* Convert REGNUM, which raw contents are stored in FROM to "standard"
+   data type TYPE and store the result into TO.  */
+void
+i387_register_convert_to_virtual (int regnum, struct type *type,
+				  char *from, char *to)
+{
+#ifdef HAVE_LONG_DOUBLE
+  if (type == builtin_type_long_double)
+    {
+      memset (to, 0, REGISTER_VIRTUAL_SIZE (regnum));
+      memcpy (to, from, REGISTER_RAW_SIZE (regnum));
     }
+  else
+#endif
+    {
+      double val;
+      floatformat_to_double (&floatformat_i387_ext, from, &val);
+      floatformat_from_double (&floatformat_ieee_double_little, &val, to);
+    }
+}
+
+/* Convert the "standard" data type TYPE stored in FROM and store the
+   result in REGNUM whose raw contents live at TO.  */
+void
+i387_register_convert_to_raw (struct type *type, int regnum,
+			      char *from, char *to)
+{
+#ifdef HAVE_LONG_DOUBLE
+  if (type == builtin_type_long_double)
+    memcpy (to, from, REGISTER_RAW_SIZE (regnum));
   else
-    return 0;
+#endif
+    {
+      double val;
+      floatformat_to_double (&floatformat_ieee_double_little, from, &val);
+      floatformat_from_double (&floatformat_i387_ext, &val, to);
+    }
 }
-#endif /* LD_I387 */
Index: gdb/gdb/config/i386/tm-i386.h
===================================================================
RCS file: /var/cvsroot/gdb/gdb/config/i386/tm-i386.h,v
retrieving revision 1.1.1.7
diff -u -r1.1.1.7 tm-i386.h
--- gdb/gdb/config/i386/tm-i386.h	1999/10/20 10:44:05	1.1.1.7
+++ gdb/gdb/config/i386/tm-i386.h	1999/10/21 15:08:56
@@ -1,5 +1,5 @@
 /* Macro definitions for GDB on an Intel i[345]86.
-   Copyright (C) 1995, 1996 Free Software Foundation, Inc.
+   Copyright (C) 1995, 1996, 1999 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -222,43 +222,75 @@
 /* Largest value REGISTER_VIRTUAL_SIZE can have.  */
 #define MAX_REGISTER_VIRTUAL_SIZE 16
 
+/* Although the i387 has a 80-bit extended floating point type, almost
+   all systems supported by GCC use 96-bit and extra padding for
+   alignment.  The exception seems to be OSF/1, where `long double' is
+   the same as `double', but there is no OSF/1 support in GDB.  */
+#define TARGET_LONG_DOUBLE_BIT 96
+#define TARGET_LONG_DOUBLE_FORMAT &floatformat_i387_ext
+
+/* Detect NaNs for the i387 extended floating point format.
+   FIXME: This is not very well hought out and should be replaced by
+   NaN classification based on floatformat.h, fully integrated in GDB.  */
+#define TARGET_ANALYZE_FLOATING					\
+  do								\
+    {								\
+      if (len == 10)                                            \
+        {							\
+          unsigned expon;		 			\
+								\
+	  low = extract_unsigned_integer (valaddr, 4);		\
+          high = extract_unsigned_integer (valaddr + 4, 4);	\
+          expon = extract_unsigned_integer (valaddr + 8, 2);	\
+								\
+          nonnegative = ((expon & 0x8000) == 0);		\
+          is_nan = ((expon & 0x7fff) == 0x7fff)			\
+	    && ((high & 0x80000000) == 0x80000000)		\
+	    && (((high & 0x7fffffff) | low) != 0);		\
+        }                                                       \
+      else                                                      \
+        is_nan = 0;                                             \
+    }								\
+  while (0)
+
 /* Return the GDB type object for the "standard" data type of data in 
    register N.  Perhaps si and di should go here, but potentially they
    could be used for things other than address.  */
 
+/* Return the "standard" data type of data in the FPU data registers.
+   We use `long double' if the host has them in a representation
+   equivalent with the i387 extended format, and `double' otherwise.  */
+extern struct type *i387_register_virtual_type (void);
+
 #define REGISTER_VIRTUAL_TYPE(N)				\
   (((N) == PC_REGNUM || (N) == FP_REGNUM || (N) == SP_REGNUM)	\
    ? lookup_pointer_type (builtin_type_void)			\
-   : IS_FP_REGNUM(N) ? builtin_type_double			\
+   : IS_FP_REGNUM(N) ? i387_register_virtual_type ()            \
    : IS_SSE_REGNUM(N) ? builtin_type_v4sf			\
    : builtin_type_int)
+
+/* Convert the 80-bit FPU data registers to the "standard" data type
+   and back.  */
+extern void i387_register_convert_to_virtual (int regnum, struct type *type,
+					      char *from, char *to);
+extern void i387_register_convert_to_raw (struct type *type, int regnum,
+					  char *from, char *to);
+
+#undef REGISTER_CONVERT_TO_VIRTUAL
+#define REGISTER_CONVERT_TO_VIRTUAL(regnum, type, from, to) \
+  i387_register_convert_to_virtual ((regnum), (type), (from), (to))
+
+#undef REGISTER_CONVERT_TO_RAW
+#define REGISTER_CONVERT_TO_RAW(type, regnum, from, to)	\
+  i387_register_convert_to_raw ((type), (regnum), (from), (to))
 
-/* REGISTER_CONVERTIBLE(N) is true iff register N's virtual format is
-   different from its raw format.  Note that this definition assumes
-   that the host supports IEEE 32-bit floats, since it doesn't say
-   that SSE registers need conversion.  Even if we can't find a
-   counterexample, this is still sloppy.  */
-#define REGISTER_CONVERTIBLE(n) (IS_FP_REGNUM (n))
-
-/* Convert data from raw format for register REGNUM in buffer FROM
-   to virtual format with type TYPE in buffer TO.  */
-extern void i387_to_double (char *, char *);
-
-#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,TYPE,FROM,TO)	\
-{								\
-  double val;							\
-  i387_to_double ((FROM), (char *)&val);			\
-  store_floating ((TO), TYPE_LENGTH (TYPE), val);		\
-}
-
-extern void double_to_i387 (char *, char *);
-
-#define REGISTER_CONVERT_TO_RAW(TYPE,REGNUM,FROM,TO)		\
-{								\
-  double val = extract_floating ((FROM), TYPE_LENGTH (TYPE));	\
-  double_to_i387((char *)&val, (TO));				\
-}
+/* Print out the i387 floating point state.  */
+#ifdef HAVE_I387_REGS
+extern void i387_float_info (void);
+#define FLOAT_INFO { i387_float_info (); }
+#endif
 
+
 /* Store the address of the place in which to copy the structure the
    subroutine will return.  This is called from call_function. */
 
Index: gdb/gdb/config/i386/tm-linux.h
===================================================================
RCS file: /var/cvsroot/gdb/gdb/config/i386/tm-linux.h,v
retrieving revision 1.1.1.5
diff -u -r1.1.1.5 tm-linux.h
--- gdb/gdb/config/i386/tm-linux.h	1999/10/20 10:44:05	1.1.1.5
+++ gdb/gdb/config/i386/tm-linux.h	1999/10/21 15:09:22
@@ -1,5 +1,5 @@
 /* Definitions to target GDB to GNU/Linux on 386.
-   Copyright 1992, 1993 Free Software Foundation, Inc.
+   Copyright 1992, 1993, 1999 Free Software Foundation, Inc.
 
    This file is part of GDB.
 
@@ -41,59 +41,6 @@
 
 #define LOW_RETURN_REGNUM 0	/* holds low four bytes of result */
 #define HIGH_RETURN_REGNUM 2	/* holds high four bytes of result */
-
-/* This should probably move to tm-i386.h.  */
-#define TARGET_LONG_DOUBLE_BIT 80
-
-#if defined(HAVE_LONG_DOUBLE) && defined(HOST_I386)
-/* The host and target are i386 machines and the compiler supports
-   long doubles. Long doubles on the host therefore have the same
-   layout as a 387 FPU stack register. */
-#define LD_I387
-
-extern int i387_extract_floating (PTR addr, int len, long double *dretptr);
-extern int i387_store_floating   (PTR addr, int len, long double val);
-
-#define TARGET_EXTRACT_FLOATING i387_extract_floating
-#define TARGET_STORE_FLOATING   i387_store_floating
-
-#define TARGET_ANALYZE_FLOATING					\
-  do								\
-    {								\
-      unsigned expon;						\
-								\
-      low = extract_unsigned_integer (valaddr, 4);		\
-      high = extract_unsigned_integer (valaddr + 4, 4);		\
-      expon = extract_unsigned_integer (valaddr + 8, 2);	\
-								\
-      nonnegative = ((expon & 0x8000) == 0);			\
-      is_nan = ((expon & 0x7fff) == 0x7fff)			\
-	&& ((high & 0x80000000) == 0x80000000)			\
-	&& (((high & 0x7fffffff) | low) != 0);			\
-    }								\
-  while (0)
-
-#undef REGISTER_CONVERT_TO_VIRTUAL
-#define REGISTER_CONVERT_TO_VIRTUAL(REGNUM,TYPE,FROM,TO)	\
-{								\
-  long double val = *((long double *)FROM);			\
-  store_floating ((TO), TYPE_LENGTH (TYPE), val);		\
-}
-
-#undef REGISTER_CONVERT_TO_RAW
-#define REGISTER_CONVERT_TO_RAW(TYPE,REGNUM,FROM,TO)			\
-{									\
-  long double val = extract_floating ((FROM), TYPE_LENGTH (TYPE));	\
-  *((long double *)TO) = val;						\
-}
-
-/* Return the GDB type object for the "standard" data type
-   of data in register N.  */
-#undef REGISTER_VIRTUAL_TYPE
-#define REGISTER_VIRTUAL_TYPE(N)					\
-  (IS_FP_REGNUM (N) ? builtin_type_long_double : builtin_type_int)
-
-#endif
 
 /* The following works around a problem with /usr/include/sys/procfs.h  */
 #define sys_quotactl 1

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