diff --git a/gdb/ChangeLog b/gdb/ChangeLog index aeb3f99f67..81acf041f0 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,24 @@ +2017-10-24 Ulrich Weigand + + * 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 * doublest.c (floatformat_precision): New routine. diff --git a/gdb/common/format.c b/gdb/common/format.c index d68579cedc..8cb15511fa 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 e1482fa6e8..33afc3a3f4 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 fa2c6db59c..1cdb35cd91 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 020b9ac411..2f76043cda 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 cafa7d3441..27d1c12f7f 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 ""; + /* Unless we need to adhere to a specific format, provide special + output for certain cases. */ + if (format == nullptr) + { + /* Detect invalid representations. */ + if (!floatformat_is_valid (fmt, in)) + return ""; - /* 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); + /* 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++; - std::string host_format - = string_printf ("%%.%d" DOUBLEST_PRINT_FORMAT, 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--; + + 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 7fdd0abb32..f249352921 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 b2b7994b88..4323475939 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: - dfp_value = value_from_decfloat (dfp_type, dec); + - if the parameter has floating-point type, it's value + is converted to the target type; - dfp_ptr = (gdb_byte *) value_contents (dfp_value); + - 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; + + - otherwise, an error is raised. + + 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]);