This is the mail archive of the gdb-cvs@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]

[binutils-gdb] Target FP printing: Simplify and fix ui_printf


https://sourceware.org/git/gitweb.cgi?p=binutils-gdb.git;h=16e812b29e68c4a6fcad73b033716c4f385ce94f

commit 16e812b29e68c4a6fcad73b033716c4f385ce94f
Author: Ulrich Weigand <ulrich.weigand@de.ibm.com>
Date:   Tue Oct 24 18:00:50 2017 +0200

    Target FP printing: Simplify and fix ui_printf
    
    This patch adds support for handling format strings to both
    floatformat_to_string and decimal_to_string, and then uses
    those routines to implement ui_printf formatted printing.
    
    There is already a subroutine printf_decfloat that ui_printf uses to
    handle decimal FP.  This is renamed to printf_floating and updated
    to handle both binary and decimal FP.  This includes the following
    set of changes:
    
    - printf_decfloat currently parses the format string again to determine
      the intended target format.  This seems superfluous since the common
      parsing code in parse_format_string already did this, but then did
      not pass the result on to its users.  Fixed by splitting the decfloat_arg
      argument class into three distinct classes, and passing them through.
    
    - Now we can rename printf_decfloat to printf_floating and also call it
      for the argument classes representing binary FP types.
    
    - The code will now use the argclass to detect the type the value should
      be printed at, and converts the input value to this type if necessary.
      To remain compatible with current behavior, for binary FP the code
      instead tries to re-interpret the input value as a FP type of the
      same size if that exists.  (Maybe this behavior is more confusing
      than useful -- but this can be changed later if we want to ...)
    
    - Finally, we can use floatformat_to_string / decimal_to_string passing
      the format string to perform the formatted output using the desired
      target FP type.
    
    Note that we no longer generate different code depending on whether or not
    the host supports "long double" -- this check is obsolete anyway since C++11
    mandates "long double", and in any case a %lg format string is intended to
    refer to the *target* long double type, not the host version.
    
    Note also that formatted printing of DFP numbers may not work correctly,
    since it attempts to use the host printf to do so (and makes unwarranted
    assumptions about the host ABI while doing so!).  This is no change to
    the current behavior -- I simply moved the code from printf_decfloat to
    the decimal_to_string routine in dfp.c.  If we want to fix it in the
    future, that is a more appropriate place anyway.
    
    ChangeLog:
    2017-10-24  Ulrich Weigand  <uweigand@de.ibm.com>
    
    	* common/format.h (enum argclass): Replace decfloat_arg by
    	dec32float_arg, dec64float_arg, and dec128float_arg.
    	* common/format.c (parse_format_string): Update to return
    	new decimal float argument classes.
    
    	* printcmd.c (printf_decfloat): Rename to ...
    	(printf_floating): ... this.  Add argclass argument, and use it
    	instead of parsing the format string again.  Add support for
    	binary floating-point values, using floatformat_to_string.
    	Convert value to the target format if it doesn't already match.
    	(ui_printf): Call printf_floating instead of printf_decfloat,
    	also for double_arg / long_double_arg.  Pass argclass.
    
    	* dfp.c (decimal_to_string): Add format string argument.
    	* dfp.h (decimal_to_string): Likewise.
    
    	* doublest.c (floatformat_to_string): Add format string argument.
    	* doublest.h (floatformat_to_string): Likewise.

Diff:
---
 gdb/ChangeLog       |  21 +++++++
 gdb/common/format.c |   8 ++-
 gdb/common/format.h |   3 +-
 gdb/dfp.c           |  13 +++-
 gdb/dfp.h           |   3 +-
 gdb/doublest.c      |  91 +++++++++++++++++++---------
 gdb/doublest.h      |   3 +-
 gdb/printcmd.c      | 168 ++++++++++++++++++++--------------------------------
 8 files changed, 171 insertions(+), 139 deletions(-)

diff --git a/gdb/ChangeLog b/gdb/ChangeLog
index aeb3f99..81acf04 100644
--- a/gdb/ChangeLog
+++ b/gdb/ChangeLog
@@ -1,5 +1,26 @@
 2017-10-24  Ulrich Weigand  <uweigand@de.ibm.com>
 
+	* common/format.h (enum argclass): Replace decfloat_arg by
+	dec32float_arg, dec64float_arg, and dec128float_arg.
+	* common/format.c (parse_format_string): Update to return
+	new decimal float argument classes.
+
+	* printcmd.c (printf_decfloat): Rename to ...
+	(printf_floating): ... this.  Add argclass argument, and use it
+	instead of parsing the format string again.  Add support for
+	binary floating-point values, using floatformat_to_string.
+	Convert value to the target format if it doesn't already match.
+	(ui_printf): Call printf_floating instead of printf_decfloat,
+	also for double_arg / long_double_arg.  Pass argclass.
+
+	* dfp.c (decimal_to_string): Add format string argument.
+	* dfp.h (decimal_to_string): Likewise.
+
+	* doublest.c (floatformat_to_string): Add format string argument.
+	* doublest.h (floatformat_to_string): Likewise.
+
+2017-10-24  Ulrich Weigand  <uweigand@de.ibm.com>
+
 	* doublest.c (floatformat_precision): New routine.
 	(floatformat_to_string): Likewise.
 	* doublest.c (floatformat_to_string): Add prototype.
diff --git a/gdb/common/format.c b/gdb/common/format.c
index d68579c..8cb1551 100644
--- a/gdb/common/format.c
+++ b/gdb/common/format.c
@@ -274,8 +274,12 @@ parse_format_string (const char **arg)
 	  case 'g':
 	  case 'E':
 	  case 'G':
-	    if (seen_big_h || seen_big_d || seen_double_big_d)
-	      this_argclass = decfloat_arg;
+	    if (seen_double_big_d)
+	      this_argclass = dec128float_arg;
+	    else if (seen_big_d)
+	      this_argclass = dec64float_arg;
+	    else if (seen_big_h)
+	      this_argclass = dec32float_arg;
 	    else if (seen_big_l)
 	      this_argclass = long_double_arg;
 	    else
diff --git a/gdb/common/format.h b/gdb/common/format.h
index e1482fa..33afc3a 100644
--- a/gdb/common/format.h
+++ b/gdb/common/format.h
@@ -35,7 +35,8 @@ enum argclass
     literal_piece,
     int_arg, long_arg, long_long_arg, ptr_arg,
     string_arg, wide_string_arg, wide_char_arg,
-    double_arg, long_double_arg, decfloat_arg
+    double_arg, long_double_arg,
+    dec32float_arg, dec64float_arg, dec128float_arg
   };
 
 /* A format piece is a section of the format string that may include a
diff --git a/gdb/dfp.c b/gdb/dfp.c
index fa2c6db..1cdb35c 100644
--- a/gdb/dfp.c
+++ b/gdb/dfp.c
@@ -146,12 +146,23 @@ decimal_to_number (const gdb_byte *from, int len, decNumber *to)
    16 bytes for decimal128.  */
 std::string
 decimal_to_string (const gdb_byte *decbytes, int len,
-		   enum bfd_endian byte_order)
+		   enum bfd_endian byte_order, const char *format)
 {
   gdb_byte dec[16];
 
   match_endianness (decbytes, len, byte_order, dec);
 
+  if (format != nullptr)
+    {
+      /* We don't handle format strings (yet).  If the host printf supports
+	 decimal floating point types, just use this.  Otherwise, fall back
+	 to printing the number while ignoring the format string.  */
+#if defined (PRINTF_HAS_DECFLOAT)
+      /* FIXME: This makes unwarranted assumptions about the host ABI!  */
+      return string_printf (format, dec);
+#endif
+    }
+
   std::string result;
   result.resize (MAX_DECIMAL_STRING);
 
diff --git a/gdb/dfp.h b/gdb/dfp.h
index 020b9ac..2f76043 100644
--- a/gdb/dfp.h
+++ b/gdb/dfp.h
@@ -28,7 +28,8 @@
 #include "doublest.h"    /* For DOUBLEST.  */
 #include "expression.h"  /* For enum exp_opcode.  */
 
-extern std::string decimal_to_string (const gdb_byte *, int, enum bfd_endian);
+extern std::string decimal_to_string (const gdb_byte *, int, enum bfd_endian,
+				      const char *format = nullptr);
 extern bool decimal_from_string (gdb_byte *, int, enum bfd_endian,
 				 std::string string);
 extern void decimal_from_longest (LONGEST from, gdb_byte *to,
diff --git a/gdb/doublest.c b/gdb/doublest.c
index cafa7d3..27d1c12 100644
--- a/gdb/doublest.c
+++ b/gdb/doublest.c
@@ -789,45 +789,78 @@ floatformat_from_doublest (const struct floatformat *fmt,
 }
 
 /* Convert the byte-stream ADDR, interpreted as floating-point format FMT,
-   to a string.  */
+   to a string, optionally using the print format FORMAT.  */
 std::string
 floatformat_to_string (const struct floatformat *fmt,
-		       const gdb_byte *in)
+		       const gdb_byte *in, const char *format)
 {
-  /* Detect invalid representations.  */
-  if (!floatformat_is_valid (fmt, in))
-    return "<invalid float value>";
-
-  /* Handle NaN and Inf.  */
-  enum float_kind kind = floatformat_classify (fmt, in);
-  if (kind == float_nan)
-    {
-      const char *sign = floatformat_is_negative (fmt, in)? "-" : "";
-      const char *mantissa = floatformat_mantissa (fmt, in);
-      return string_printf ("%snan(0x%s)", sign, mantissa);
-    }
-  else if (kind == float_infinite)
+  /* Unless we need to adhere to a specific format, provide special
+     output for certain cases.  */
+  if (format == nullptr)
     {
-      const char *sign = floatformat_is_negative (fmt, in)? "-" : "";
-      return string_printf ("%sinf", sign);
+      /* Detect invalid representations.  */
+      if (!floatformat_is_valid (fmt, in))
+	return "<invalid float value>";
+
+      /* Handle NaN and Inf.  */
+      enum float_kind kind = floatformat_classify (fmt, in);
+      if (kind == float_nan)
+	{
+	  const char *sign = floatformat_is_negative (fmt, in)? "-" : "";
+	  const char *mantissa = floatformat_mantissa (fmt, in);
+	  return string_printf ("%snan(0x%s)", sign, mantissa);
+	}
+      else if (kind == float_infinite)
+	{
+	  const char *sign = floatformat_is_negative (fmt, in)? "-" : "";
+	  return string_printf ("%sinf", sign);
+	}
     }
 
-  /* Print the number using a format string where the precision is set
-     to the DECIMAL_DIG value for the given floating-point format.
-     This value is computed as
+  /* Determine the format string to use on the host side.  */
+  std::string host_format;
+  char conversion;
+
+  if (format == nullptr)
+    {
+      /* If no format was specified, print the number using a format string
+	 where the precision is set to the DECIMAL_DIG value for the given
+	 floating-point format.  This value is computed as
 
 		ceil(1 + p * log10(b)),
 
-     where p is the precision of the floating-point format in bits, and
-     b is the base (which is always 2 for the formats we support).  */
-  const double log10_2 = .30102999566398119521;
-  double d_decimal_dig = 1 + floatformat_precision (fmt) * log10_2;
-  int decimal_dig = d_decimal_dig;
-  if (decimal_dig < d_decimal_dig)
-    decimal_dig++;
+	 where p is the precision of the floating-point format in bits, and
+	 b is the base (which is always 2 for the formats we support).  */
+      const double log10_2 = .30102999566398119521;
+      double d_decimal_dig = 1 + floatformat_precision (fmt) * log10_2;
+      int decimal_dig = d_decimal_dig;
+      if (decimal_dig < d_decimal_dig)
+	decimal_dig++;
+
+      host_format = string_printf ("%%.%d", decimal_dig);
+      conversion = 'g';
+    }
+  else
+    {
+      /* Use the specified format, stripping out the conversion character
+         and length modifier, if present.  */
+      size_t len = strlen (format);
+      gdb_assert (len > 1);
+      conversion = format[--len];
+      gdb_assert (conversion == 'e' || conversion == 'f' || conversion == 'g'
+		  || conversion == 'E' || conversion == 'G');
+      if (format[len - 1] == 'L')
+	len--;
 
-  std::string host_format
-    = string_printf ("%%.%d" DOUBLEST_PRINT_FORMAT, decimal_dig);
+      host_format = std::string (format, len);
+    }
+
+  /* Add the length modifier and conversion character appropriate for
+     handling the host DOUBLEST type.  */
+#ifdef HAVE_LONG_DOUBLE
+  host_format += 'L';
+#endif
+  host_format += conversion;
 
   DOUBLEST doub;
   floatformat_to_doublest (fmt, in, &doub);
diff --git a/gdb/doublest.h b/gdb/doublest.h
index 7fdd0ab..f249352 100644
--- a/gdb/doublest.h
+++ b/gdb/doublest.h
@@ -72,7 +72,8 @@ extern const char *floatformat_mantissa (const struct floatformat *,
 					 const bfd_byte *);
 
 extern std::string floatformat_to_string (const struct floatformat *fmt,
-					  const gdb_byte *in);
+					  const gdb_byte *in,
+					  const char *format = nullptr);
 
 /* Return the floatformat's total size in host bytes.  */
 
diff --git a/gdb/printcmd.c b/gdb/printcmd.c
index b2b7994..4323475 100644
--- a/gdb/printcmd.c
+++ b/gdb/printcmd.c
@@ -2296,86 +2296,78 @@ printf_wide_c_string (struct ui_file *stream, const char *format,
 }
 
 /* Subroutine of ui_printf to simplify it.
-   Print VALUE, a decimal floating point value, to STREAM using FORMAT.  */
+   Print VALUE, a floating point value, to STREAM using FORMAT.  */
 
 static void
-printf_decfloat (struct ui_file *stream, const char *format,
-		 struct value *value)
+printf_floating (struct ui_file *stream, const char *format,
+		 struct value *value, enum argclass argclass)
 {
-  const gdb_byte *param_ptr = value_contents (value);
-
-#if defined (PRINTF_HAS_DECFLOAT)
-  /* If we have native support for Decimal floating
-     printing, handle it here.  */
-  fprintf_filtered (stream, format, param_ptr);
-#else
-  /* As a workaround until vasprintf has native support for DFP
-     we convert the DFP values to string and print them using
-     the %s format specifier.  */
-  const char *p;
-
   /* Parameter data.  */
   struct type *param_type = value_type (value);
   struct gdbarch *gdbarch = get_type_arch (param_type);
   enum bfd_endian byte_order = gdbarch_byte_order (gdbarch);
 
-  /* DFP output data.  */
-  struct value *dfp_value = NULL;
-  gdb_byte *dfp_ptr;
-  int dfp_len = 16;
-  gdb_byte dec[16];
-  struct type *dfp_type = NULL;
-
-  /* Points to the end of the string so that we can go back
-     and check for DFP length modifiers.  */
-  p = format + strlen (format);
-
-  /* Look for the float/double format specifier.  */
-  while (*p != 'f' && *p != 'e' && *p != 'E'
-	 && *p != 'g' && *p != 'G')
-    p--;
-
-  /* Search for the '%' char and extract the size and type of
-     the output decimal value based on its modifiers
-     (%Hf, %Df, %DDf).  */
-  while (*--p != '%')
+  /* Determine target type corresponding to the format string.  */
+  struct type *fmt_type;
+  switch (argclass)
     {
-      if (*p == 'H')
-	{
-	  dfp_len = 4;
-	  dfp_type = builtin_type (gdbarch)->builtin_decfloat;
-	}
-      else if (*p == 'D' && *(p - 1) == 'D')
-	{
-	  dfp_len = 16;
-	  dfp_type = builtin_type (gdbarch)->builtin_declong;
-	  p--;
-	}
-      else
-	{
-	  dfp_len = 8;
-	  dfp_type = builtin_type (gdbarch)->builtin_decdouble;
-	}
+      case double_arg:
+	fmt_type = builtin_type (gdbarch)->builtin_double;
+	break;
+      case long_double_arg:
+	fmt_type = builtin_type (gdbarch)->builtin_long_double;
+	break;
+      case dec32float_arg:
+	fmt_type = builtin_type (gdbarch)->builtin_decfloat;
+	break;
+      case dec64float_arg:
+	fmt_type = builtin_type (gdbarch)->builtin_decdouble;
+	break;
+      case dec128float_arg:
+	fmt_type = builtin_type (gdbarch)->builtin_declong;
+	break;
+      default:
+	gdb_assert_not_reached ("unexpected argument class");
     }
 
-  /* Conversion between different DFP types.  */
-  if (TYPE_CODE (param_type) == TYPE_CODE_DECFLOAT)
-    decimal_convert (param_ptr, TYPE_LENGTH (param_type),
-		     byte_order, dec, dfp_len, byte_order);
-  else
-    /* If this is a non-trivial conversion, just output 0.
-       A correct converted value can be displayed by explicitly
-       casting to a DFP type.  */
-    decimal_from_string (dec, dfp_len, byte_order, "0");
+  /* To match the traditional GDB behavior, the conversion is
+     done differently depending on the type of the parameter:
+
+     - if the parameter has floating-point type, it's value
+       is converted to the target type;
+
+     - otherwise, if the parameter has a type that is of the
+       same size as a built-in floating-point type, the value
+       bytes are interpreted as if they were of that type, and
+       then converted to the target type (this is not done for
+       decimal floating-point argument classes);
+
+     - otherwise, if the source value has an integer value,
+       it's value is converted to the target type;
 
-  dfp_value = value_from_decfloat (dfp_type, dec);
+     - otherwise, an error is raised.
 
-  dfp_ptr = (gdb_byte *) value_contents (dfp_value);
+     In either case, the result of the conversion is a byte buffer
+     formatted in the target format for the target type.  */
+
+  if (TYPE_CODE (fmt_type) == TYPE_CODE_FLT)
+    {
+      param_type = float_type_from_length (param_type);
+      if (param_type != value_type (value))
+	value = value_from_contents (param_type, value_contents (value));
+    }
+
+  value = value_cast (fmt_type, value);
 
   /* Convert the value to a string and print it.  */
-  std::string str = decimal_to_string (dfp_ptr, dfp_len, byte_order);
+  std::string str;
+  if (TYPE_CODE (fmt_type) == TYPE_CODE_FLT)
+    str = floatformat_to_string (floatformat_from_type (fmt_type),
+				 value_contents (value), format);
+  else
+    str = decimal_to_string (value_contents (value),
+			     TYPE_LENGTH (fmt_type), byte_order, format);
   fputs_filtered (str.c_str (), stream);
-#endif
 }
 
 /* Subroutine of ui_printf to simplify it.
@@ -2558,43 +2550,6 @@ ui_printf (const char *arg, struct ui_file *stream)
                                 obstack_base (&output));
 	    }
 	    break;
-	  case double_arg:
-	    {
-	      struct type *type = value_type (val_args[i]);
-	      DOUBLEST val;
-	      int inv;
-
-	      /* If format string wants a float, unchecked-convert the value
-		 to floating point of the same size.  */
-	      type = float_type_from_length (type);
-	      val = unpack_double (type, value_contents (val_args[i]), &inv);
-	      if (inv)
-		error (_("Invalid floating value found in program."));
-
-              fprintf_filtered (stream, current_substring, (double) val);
-	      break;
-	    }
-	  case long_double_arg:
-#ifdef HAVE_LONG_DOUBLE
-	    {
-	      struct type *type = value_type (val_args[i]);
-	      DOUBLEST val;
-	      int inv;
-
-	      /* If format string wants a float, unchecked-convert the value
-		 to floating point of the same size.  */
-	      type = float_type_from_length (type);
-	      val = unpack_double (type, value_contents (val_args[i]), &inv);
-	      if (inv)
-		error (_("Invalid floating value found in program."));
-
-	      fprintf_filtered (stream, current_substring,
-                                (long double) val);
-	      break;
-	    }
-#else
-	    error (_("long double not supported in printf"));
-#endif
 	  case long_long_arg:
 #ifdef PRINTF_HAS_LONG_LONG
 	    {
@@ -2620,9 +2575,14 @@ ui_printf (const char *arg, struct ui_file *stream)
               fprintf_filtered (stream, current_substring, val);
 	      break;
 	    }
-	  /* Handles decimal floating values.  */
-	  case decfloat_arg:
-	    printf_decfloat (stream, current_substring, val_args[i]);
+	  /* Handles floating-point values.  */
+	  case double_arg:
+	  case long_double_arg:
+	  case dec32float_arg:
+	  case dec64float_arg:
+	  case dec128float_arg:
+	    printf_floating (stream, current_substring, val_args[i],
+			     fpieces[fr].argclass);
 	    break;
 	  case ptr_arg:
 	    printf_pointer (stream, current_substring, val_args[i]);


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