+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.
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
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
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);
#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,
}
/* 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);
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. */
}
/* 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.
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
{
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]);