X-Git-Url: http://git.efficios.com/?a=blobdiff_plain;f=gdb%2Ftarget-float.c;h=1bdd3efb69f27b4a0e9e50cf9d7d4ce1a5a96a91;hb=40c75bc8b07abc5d5774ea1c439b69c96e7fd485;hp=8bc027e78ea608c23c710760865dee10f610f250;hpb=d723696126beb185dc1f8f3196be7cb29948148d;p=deliverable%2Fbinutils-gdb.git diff --git a/gdb/target-float.c b/gdb/target-float.c index 8bc027e78e..1bdd3efb69 100644 --- a/gdb/target-float.c +++ b/gdb/target-float.c @@ -1,6 +1,6 @@ /* Floating point routines for GDB, the GNU debugger. - Copyright (C) 2017 Free Software Foundation, Inc. + Copyright (C) 2017-2020 Free Software Foundation, Inc. This file is part of GDB. @@ -21,23 +21,50 @@ #include "gdbtypes.h" #include "floatformat.h" #include "target-float.h" +#include "gdbarch.h" +/* Target floating-point operations. -/* Helper routines operating on binary floating-point data. */ + We provide multiple implementations of those operations, which differ + by the host-side intermediate format they perform computations in. -#include + Those multiple implementations all derive from the following abstract + base class, which specifies the set of operations to be implemented. */ -#if (defined HAVE_LONG_DOUBLE && defined PRINTF_HAS_LONG_DOUBLE \ - && defined SCANF_HAS_LONG_DOUBLE) -typedef long double DOUBLEST; -#else -typedef double DOUBLEST; -/* If we can't scan or print long double, we don't want to use it - anywhere. */ -# undef HAVE_LONG_DOUBLE -# undef PRINTF_HAS_LONG_DOUBLE -# undef SCANF_HAS_LONG_DOUBLE -#endif +class target_float_ops +{ +public: + virtual std::string to_string (const gdb_byte *addr, const struct type *type, + const char *format) const = 0; + virtual bool from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const = 0; + + virtual LONGEST to_longest (const gdb_byte *addr, + const struct type *type) const = 0; + virtual void from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const = 0; + virtual void from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const = 0; + virtual double to_host_double (const gdb_byte *addr, + const struct type *type) const = 0; + virtual void from_host_double (gdb_byte *addr, const struct type *type, + double val) const = 0; + virtual void convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const = 0; + + virtual void binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const = 0; + virtual int compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const = 0; +}; + + +/* Helper routines operating on binary floating-point data. */ + +#include +#include /* Different kinds of floatformat numbers recognized by floatformat_classify. To avoid portability issues, we use local @@ -447,7 +474,106 @@ floatformat_mantissa (const struct floatformat *fmt, return res; } -/* Convert TO/FROM target to the hosts DOUBLEST floating-point format. +/* Convert printf format string FORMAT to the otherwise equivalent string + which may be used to print a host floating-point number using the length + modifier LENGTH (which may be 0 if none is needed). If FORMAT is null, + return a format appropriate to print the full precision of a target + floating-point number of format FMT. */ +static std::string +floatformat_printf_format (const struct floatformat *fmt, + const char *format, char length) +{ + 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++; + + 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 appropriate host floating-point type. */ + if (length) + host_format += length; + host_format += conversion; + + return host_format; +} + +/* Implementation of target_float_ops using the host floating-point type T + as intermediate type. */ + +template class host_float_ops : public target_float_ops +{ +public: + std::string to_string (const gdb_byte *addr, const struct type *type, + const char *format) const override; + bool from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const override; + + LONGEST to_longest (const gdb_byte *addr, + const struct type *type) const override; + void from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const override; + void from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const override; + double to_host_double (const gdb_byte *addr, + const struct type *type) const override; + void from_host_double (gdb_byte *addr, const struct type *type, + double val) const override; + void convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const override; + + void binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const override; + int compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const override; + +private: + void from_target (const struct floatformat *fmt, + const gdb_byte *from, T *to) const; + void from_target (const struct type *type, + const gdb_byte *from, T *to) const; + + void to_target (const struct type *type, + const T *from, gdb_byte *to) const; + void to_target (const struct floatformat *fmt, + const T *from, gdb_byte *to) const; +}; + + +/* Convert TO/FROM target to the host floating-point format T. If the host and target formats agree, we just copy the raw data into the appropriate type of variable and return, letting the host @@ -467,11 +593,11 @@ static const struct floatformat *host_double_format = GDB_HOST_DOUBLE_FORMAT; static const struct floatformat *host_long_double_format = GDB_HOST_LONG_DOUBLE_FORMAT; -/* Convert from FMT to a DOUBLEST. FROM is the address of the extended float. - Store the DOUBLEST in *TO. */ -static void -floatformat_to_doublest (const struct floatformat *fmt, - const void *from, DOUBLEST *to) +/* Convert target floating-point value at FROM in format FMT to host + floating-point format of type T. */ +template void +host_float_ops::from_target (const struct floatformat *fmt, + const gdb_byte *from, T *to) const { gdb_assert (fmt != NULL); @@ -501,7 +627,6 @@ floatformat_to_doublest (const struct floatformat *fmt, } unsigned char *ufrom = (unsigned char *) from; - DOUBLEST dto; long exponent; unsigned long mant; unsigned int mant_bits, mant_off; @@ -522,9 +647,9 @@ floatformat_to_doublest (const struct floatformat *fmt, { double dto; - floatformat_to_double (fmt->split_half ? fmt->split_half : fmt, - from, &dto); - *to = (DOUBLEST) dto; + floatformat_to_double /* ARI: floatformat_to_double */ + (fmt->split_half ? fmt->split_half : fmt, from, &dto); + *to = (T) dto; return; } @@ -535,9 +660,9 @@ floatformat_to_doublest (const struct floatformat *fmt, if (fmt->split_half) { - DOUBLEST dtop, dbot; + T dtop, dbot; - floatformat_to_doublest (fmt->split_half, ufrom, &dtop); + from_target (fmt->split_half, ufrom, &dtop); /* Preserve the sign of 0, which is the sign of the top half. */ if (dtop == 0.0) @@ -545,9 +670,8 @@ floatformat_to_doublest (const struct floatformat *fmt, *to = dtop; return; } - floatformat_to_doublest (fmt->split_half, - ufrom + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, - &dbot); + from_target (fmt->split_half, + ufrom + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, &dbot); *to = dtop + dbot; return; } @@ -560,7 +684,7 @@ floatformat_to_doublest (const struct floatformat *fmt, mant_bits_left = fmt->man_len; mant_off = fmt->man_start; - dto = 0.0; + T dto = 0.0; special_exponent = exponent == 0 || exponent == fmt->exp_nan; @@ -593,7 +717,7 @@ floatformat_to_doublest (const struct floatformat *fmt, mant = get_field (ufrom, order, fmt->totalsize, mant_off, mant_bits); - dto += ldexp ((double) mant, exponent - mant_bits); + dto += ldexp ((T) mant, exponent - mant_bits); exponent -= mant_bits; mant_off += mant_bits; mant_bits_left -= mant_bits; @@ -605,11 +729,18 @@ floatformat_to_doublest (const struct floatformat *fmt, *to = dto; } -/* Convert the DOUBLEST *FROM to an extended float in format FMT and - store where TO points. */ -static void -floatformat_from_doublest (const struct floatformat *fmt, - const DOUBLEST *from, void *to) +template void +host_float_ops::from_target (const struct type *type, + const gdb_byte *from, T *to) const +{ + from_target (floatformat_from_type (type), from, to); +} + +/* Convert host floating-point value of type T to target floating-point + value in format FMT and store at TO. */ +template void +host_float_ops::to_target (const struct floatformat *fmt, + const T *from, gdb_byte *to) const { gdb_assert (fmt != NULL); @@ -635,9 +766,9 @@ floatformat_from_doublest (const struct floatformat *fmt, return; } - DOUBLEST dfrom; + T dfrom; int exponent; - DOUBLEST mant; + T mant; unsigned int mant_bits, mant_off; int mant_bits_left; unsigned char *uto = (unsigned char *) to; @@ -659,7 +790,7 @@ floatformat_from_doublest (const struct floatformat *fmt, removed via storing in memory, and so the top half really is the result of converting to double. */ static volatile double dtop, dbot; - DOUBLEST dtopnv, dbotnv; + T dtopnv, dbotnv; dtop = (double) dfrom; /* If the rounded top half is Inf, the bottom must be 0 not NaN @@ -667,13 +798,12 @@ floatformat_from_doublest (const struct floatformat *fmt, if (dtop + dtop == dtop && dtop != 0.0) dbot = 0.0; else - dbot = (double) (dfrom - (DOUBLEST) dtop); + dbot = (double) (dfrom - (T) dtop); dtopnv = dtop; dbotnv = dbot; - floatformat_from_doublest (fmt->split_half, &dtopnv, uto); - floatformat_from_doublest (fmt->split_half, &dbotnv, - (uto - + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2)); + to_target (fmt->split_half, &dtopnv, uto); + to_target (fmt->split_half, &dbotnv, + uto + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2); return; } @@ -708,11 +838,7 @@ floatformat_from_doublest (const struct floatformat *fmt, goto finalize_byteorder; } -#ifdef HAVE_LONG_DOUBLE - mant = frexpl (dfrom, &exponent); -#else mant = frexp (dfrom, &exponent); -#endif if (exponent + fmt->exp_bias <= 0) { @@ -790,99 +916,74 @@ floatformat_from_doublest (const struct floatformat *fmt, floatformat_normalize_byteorder (fmt, newto, to); } -/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, - to a string, optionally using the print format FORMAT. */ -static std::string -floatformat_to_string (const struct floatformat *fmt, - const gdb_byte *in, const char *format) +template void +host_float_ops::to_target (const struct type *type, + const T *from, gdb_byte *to) const { - /* 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 ""; + /* Ensure possible padding bytes in the target buffer are zeroed out. */ + memset (to, 0, TYPE_LENGTH (type)); - /* 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); - } - } + to_target (floatformat_from_type (type), from, to); +} +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a string, optionally using the print format FORMAT. */ +template struct printf_length_modifier +{ + static constexpr char value = 0; +}; +template<> struct printf_length_modifier +{ + static constexpr char value = 'L'; +}; +template std::string +host_float_ops::to_string (const gdb_byte *addr, const struct type *type, + const char *format) const +{ /* 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++; - - 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); - } + constexpr char length = printf_length_modifier::value; + const struct floatformat *fmt = floatformat_from_type (type); + std::string host_format = floatformat_printf_format (fmt, format, length); - /* 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; + T host_float; + from_target (type, addr, &host_float); - DOUBLEST doub; - floatformat_to_doublest (fmt, in, &doub); - return string_printf (host_format.c_str (), doub); + DIAGNOSTIC_PUSH + DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL + return string_printf (host_format.c_str (), host_float); + DIAGNOSTIC_POP } -/* Parse string STRING into a target floating-number of format FMT and +/* Parse string IN into a target floating-number of type TYPE and store it as byte-stream ADDR. Return whether parsing succeeded. */ -static bool -floatformat_from_string (const struct floatformat *fmt, gdb_byte *out, - const std::string &in) +template struct scanf_length_modifier +{ + static constexpr char value = 0; +}; +template<> struct scanf_length_modifier +{ + static constexpr char value = 'l'; +}; +template<> struct scanf_length_modifier { - DOUBLEST doub; + static constexpr char value = 'L'; +}; +template bool +host_float_ops::from_string (gdb_byte *addr, const struct type *type, + const std::string &in) const +{ + T host_float; int n, num; -#ifdef HAVE_LONG_DOUBLE - const char *scan_format = "%Lg%n"; -#else - const char *scan_format = "%lg%n"; -#endif - num = sscanf (in.c_str (), scan_format, &doub, &n); + + std::string scan_format = "%"; + if (scanf_length_modifier::value) + scan_format += scanf_length_modifier::value; + scan_format += "g%n"; + + DIAGNOSTIC_PUSH + DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL + num = sscanf (in.c_str (), scan_format.c_str(), &host_float, &n); + DIAGNOSTIC_POP /* The sscanf man page suggests not making any assumptions on the effect of %n on the result, so we don't. @@ -894,100 +995,101 @@ floatformat_from_string (const struct floatformat *fmt, gdb_byte *out, if (in[n]) return false; - floatformat_from_doublest (fmt, &doub, out); + to_target (type, &host_float, addr); return true; } -/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, to an integer value (rounding towards zero). */ -static LONGEST -floatformat_to_longest (const struct floatformat *fmt, const gdb_byte *addr) +template LONGEST +host_float_ops::to_longest (const gdb_byte *addr, + const struct type *type) const { - DOUBLEST d; - floatformat_to_doublest (fmt, addr, &d); - return (LONGEST) d; + T host_float; + from_target (type, addr, &host_float); + T min_possible_range = static_cast(std::numeric_limits::min()); + T max_possible_range = -min_possible_range; + /* host_float can be converted to an integer as long as it's in + the range [min_possible_range, max_possible_range). If not, it is either + too large, or too small, or is NaN; in this case return the maximum or + minimum possible value. */ + if (host_float < max_possible_range && host_float >= min_possible_range) + return static_cast (host_float); + if (host_float < min_possible_range) + return std::numeric_limits::min(); + /* This line will be executed if host_float is NaN. */ + return std::numeric_limits::max(); } -/* Convert signed integer VAL to a target floating-number of format FMT +/* Convert signed integer VAL to a target floating-number of type TYPE and store it as byte-stream ADDR. */ -static void -floatformat_from_longest (const struct floatformat *fmt, gdb_byte *addr, - LONGEST val) +template void +host_float_ops::from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const { - DOUBLEST d = (DOUBLEST) val; - floatformat_from_doublest (fmt, &d, addr); + T host_float = (T) val; + to_target (type, &host_float, addr); } -/* Convert unsigned integer VAL to a target floating-number of format FMT +/* Convert unsigned integer VAL to a target floating-number of type TYPE and store it as byte-stream ADDR. */ -static void -floatformat_from_ulongest (const struct floatformat *fmt, gdb_byte *addr, - ULONGEST val) +template void +host_float_ops::from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const { - DOUBLEST d = (DOUBLEST) val; - floatformat_from_doublest (fmt, &d, addr); + T host_float = (T) val; + to_target (type, &host_float, addr); } -/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, to a floating-point value in the host "double" format. */ -static double -floatformat_to_host_double (const struct floatformat *fmt, - const gdb_byte *addr) +template double +host_float_ops::to_host_double (const gdb_byte *addr, + const struct type *type) const { - DOUBLEST d; - floatformat_to_doublest (fmt, addr, &d); - return (double) d; + T host_float; + from_target (type, addr, &host_float); + return (double) host_float; } /* Convert floating-point value VAL in the host "double" format to a target - floating-number of format FMT and store it as byte-stream ADDR. */ -static void -floatformat_from_host_double (const struct floatformat *fmt, gdb_byte *addr, - double val) + floating-number of type TYPE and store it as byte-stream ADDR. */ +template void +host_float_ops::from_host_double (gdb_byte *addr, const struct type *type, + double val) const { - DOUBLEST d = (DOUBLEST) val; - floatformat_from_doublest (fmt, &d, addr); + T host_float = (T) val; + to_target (type, &host_float, addr); } -/* Convert a floating-point number of format FROM_FMT from the target - byte-stream FROM to a floating-point number of format TO_FMT, and +/* Convert a floating-point number of type FROM_TYPE from the target + byte-stream FROM to a floating-point number of type TO_TYPE, and store it to the target byte-stream TO. */ -static void -floatformat_convert (const gdb_byte *from, const struct floatformat *from_fmt, - gdb_byte *to, const struct floatformat *to_fmt) +template void +host_float_ops::convert (const gdb_byte *from, + const struct type *from_type, + gdb_byte *to, + const struct type *to_type) const { - if (from_fmt == to_fmt) - { - /* The floating-point formats match, so we simply copy the data. */ - memcpy (to, from, floatformat_totalsize_bytes (to_fmt)); - } - else - { - /* The floating-point formats don't match. The best we can do - (apart from simulating the target FPU) is converting to the - widest floating-point type supported by the host, and then - again to the desired type. */ - DOUBLEST d; - - floatformat_to_doublest (from_fmt, from, &d); - floatformat_from_doublest (to_fmt, &d, to); - } + T host_float; + from_target (from_type, from, &host_float); + to_target (to_type, &host_float, to); } /* Perform the binary operation indicated by OPCODE, using as operands the target byte streams X and Y, interpreted as floating-point numbers of - formats FMT_X and FMT_Y, respectively. Convert the result to format - FMT_RES and store it into the byte-stream RES. */ -static void -floatformat_binop (enum exp_opcode op, - const struct floatformat *fmt_x, const gdb_byte *x, - const struct floatformat *fmt_y, const gdb_byte *y, - const struct floatformat *fmt_result, gdb_byte *result) + types TYPE_X and TYPE_Y, respectively. Convert the result to format + TYPE_RES and store it into the byte-stream RES. */ +template void +host_float_ops::binop (enum exp_opcode op, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const { - DOUBLEST v1, v2, v = 0; + T v1, v2, v = 0; - floatformat_to_doublest (fmt_x, x, &v1); - floatformat_to_doublest (fmt_y, y, &v2); + from_target (type_x, x, &v1); + from_target (type_y, y, &v2); switch (op) { @@ -1028,20 +1130,20 @@ floatformat_binop (enum exp_opcode op, break; } - floatformat_from_doublest (fmt_result, &v, result); + to_target (type_res, &v, res); } /* Compare the two target byte streams X and Y, interpreted as floating-point - numbers of formats FMT_X and FMT_Y, respectively. Return zero if X and Y + numbers of types TYPE_X and TYPE_Y, respectively. Return zero if X and Y are equal, -1 if X is less than Y, and 1 otherwise. */ -static int -floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, - const struct floatformat *fmt_y, const gdb_byte *y) +template int +host_float_ops::compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const { - DOUBLEST v1, v2; + T v1, v2; - floatformat_to_doublest (fmt_x, x, &v1); - floatformat_to_doublest (fmt_y, y, &v2); + from_target (type_x, x, &v1); + from_target (type_y, y, &v2); if (v1 == v2) return 0; @@ -1051,101 +1153,670 @@ floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, } -/* Helper routines operating on decimal floating-point data. */ - -/* Decimal floating point is one of the extension to IEEE 754, which is - described in http://grouper.ieee.org/groups/754/revision.html and - http://www2.hursley.ibm.com/decimal/. It completes binary floating - point by representing floating point more exactly. */ +/* Implementation of target_float_ops using the MPFR library + mpfr_t as intermediate type. */ -/* The order of the following headers is important for making sure - decNumber structure is large enough to hold decimal128 digits. */ +#ifdef HAVE_LIBMPFR -#include "dpd/decimal128.h" -#include "dpd/decimal64.h" -#include "dpd/decimal32.h" +#define MPFR_USE_INTMAX_T -/* When using decimal128, this is the maximum string length + 1 - (value comes from libdecnumber's DECIMAL128_String constant). */ -#define MAX_DECIMAL_STRING 43 +#include -/* In GDB, we are using an array of gdb_byte to represent decimal values. - They are stored in host byte order. This routine does the conversion if - the target byte order is different. */ -static void -match_endianness (const gdb_byte *from, int len, enum bfd_endian byte_order, - gdb_byte *to) +class mpfr_float_ops : public target_float_ops { - int i; - -#if WORDS_BIGENDIAN -#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE -#else -#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG -#endif - - if (byte_order == OPPOSITE_BYTE_ORDER) - for (i = 0; i < len; i++) - to[i] = from[len - i - 1]; - else - for (i = 0; i < len; i++) - to[i] = from[i]; +public: + std::string to_string (const gdb_byte *addr, const struct type *type, + const char *format) const override; + bool from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const override; + + LONGEST to_longest (const gdb_byte *addr, + const struct type *type) const override; + void from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const override; + void from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const override; + double to_host_double (const gdb_byte *addr, + const struct type *type) const override; + void from_host_double (gdb_byte *addr, const struct type *type, + double val) const override; + void convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const override; + + void binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const override; + int compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const override; + +private: + /* Local wrapper class to handle mpfr_t initalization and cleanup. */ + class gdb_mpfr + { + public: + mpfr_t val; + + gdb_mpfr (const struct type *type) + { + const struct floatformat *fmt = floatformat_from_type (type); + mpfr_init2 (val, floatformat_precision (fmt)); + } - return; -} + gdb_mpfr (const gdb_mpfr &source) + { + mpfr_init2 (val, mpfr_get_prec (source.val)); + } -/* Helper function to get the appropriate libdecnumber context for each size - of decimal float. */ -static void -set_decnumber_context (decContext *ctx, int len) -{ - switch (len) + ~gdb_mpfr () { - case 4: - decContextDefault (ctx, DEC_INIT_DECIMAL32); - break; - case 8: - decContextDefault (ctx, DEC_INIT_DECIMAL64); - break; - case 16: - decContextDefault (ctx, DEC_INIT_DECIMAL128); - break; + mpfr_clear (val); } + }; - ctx->traps = 0; -} + void from_target (const struct floatformat *fmt, + const gdb_byte *from, gdb_mpfr &to) const; + void from_target (const struct type *type, + const gdb_byte *from, gdb_mpfr &to) const; -/* Check for errors signaled in the decimal context structure. */ -static void -decimal_check_errors (decContext *ctx) + void to_target (const struct type *type, + const gdb_mpfr &from, gdb_byte *to) const; + void to_target (const struct floatformat *fmt, + const gdb_mpfr &from, gdb_byte *to) const; +}; + + +/* Convert TO/FROM target floating-point format to mpfr_t. */ + +void +mpfr_float_ops::from_target (const struct floatformat *fmt, + const gdb_byte *orig_from, gdb_mpfr &to) const { - /* An error here could be a division by zero, an overflow, an underflow or - an invalid operation (from the DEC_Errors constant in decContext.h). - Since GDB doesn't complain about division by zero, overflow or underflow - errors for binary floating, we won't complain about them for decimal - floating either. */ - if (ctx->status & DEC_IEEE_854_Invalid_operation) + const gdb_byte *from = orig_from; + mpfr_exp_t exponent; + unsigned long mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + int special_exponent; /* It's a NaN, denorm or zero. */ + enum floatformat_byteorders order; + unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES]; + enum float_kind kind; + + gdb_assert (fmt->totalsize + <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT); + + /* Handle non-numbers. */ + kind = floatformat_classify (fmt, from); + if (kind == float_infinite) { - /* Leave only the error bits in the status flags. */ - ctx->status &= DEC_IEEE_854_Invalid_operation; - error (_("Cannot perform operation: %s"), - decContextStatusToString (ctx)); + mpfr_set_inf (to.val, floatformat_is_negative (fmt, from) ? -1 : 1); + return; + } + if (kind == float_nan) + { + mpfr_set_nan (to.val); + return; } -} -/* Helper function to convert from libdecnumber's appropriate representation - for computation to each size of decimal float. */ -static void -decimal_from_number (const decNumber *from, - gdb_byte *to, int len, enum bfd_endian byte_order) -{ - gdb_byte dec[16]; + order = floatformat_normalize_byteorder (fmt, from, newfrom); - decContext set; + if (order != fmt->byteorder) + from = newfrom; - set_decnumber_context (&set, len); + if (fmt->split_half) + { + gdb_mpfr top (to), bot (to); + + from_target (fmt->split_half, from, top); + /* Preserve the sign of 0, which is the sign of the top half. */ + if (mpfr_zero_p (top.val)) + { + mpfr_set (to.val, top.val, MPFR_RNDN); + return; + } + from_target (fmt->split_half, + from + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, bot); + mpfr_add (to.val, top.val, bot.val, MPFR_RNDN); + return; + } + + exponent = get_field (from, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len); + /* Note that if exponent indicates a NaN, we can't really do anything useful + (not knowing if the host has NaN's, or how to build one). So it will + end up as an infinity or something close; that is OK. */ + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + mpfr_set_zero (to.val, 0); + + special_exponent = exponent == 0 || exponent == fmt->exp_nan; + + /* Don't bias NaNs. Use minimum exponent for denorms. For + simplicity, we don't check for zero as the exponent doesn't matter. + Note the cast to int; exp_bias is unsigned, so it's important to + make sure the operation is done in signed arithmetic. */ + if (!special_exponent) + exponent -= fmt->exp_bias; + else if (exponent == 0) + exponent = 1 - fmt->exp_bias; + + /* Build the result algebraically. Might go infinite, underflow, etc; + who cares. */ + + /* If this format uses a hidden bit, explicitly add it in now. Otherwise, + increment the exponent by one to account for the integer bit. */ + + if (!special_exponent) + { + if (fmt->intbit == floatformat_intbit_no) + mpfr_set_ui_2exp (to.val, 1, exponent, MPFR_RNDN); + else + exponent++; + } + + gdb_mpfr tmp (to); + + while (mant_bits_left > 0) + { + mant_bits = std::min (mant_bits_left, 32); + + mant = get_field (from, order, fmt->totalsize, mant_off, mant_bits); + + mpfr_set_ui (tmp.val, mant, MPFR_RNDN); + mpfr_mul_2si (tmp.val, tmp.val, exponent - mant_bits, MPFR_RNDN); + mpfr_add (to.val, to.val, tmp.val, MPFR_RNDN); + exponent -= mant_bits; + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* Negate it if negative. */ + if (get_field (from, order, fmt->totalsize, fmt->sign_start, 1)) + mpfr_neg (to.val, to.val, MPFR_RNDN); +} + +void +mpfr_float_ops::from_target (const struct type *type, + const gdb_byte *from, gdb_mpfr &to) const +{ + from_target (floatformat_from_type (type), from, to); +} + +void +mpfr_float_ops::to_target (const struct floatformat *fmt, + const gdb_mpfr &from, gdb_byte *orig_to) const +{ + unsigned char *to = orig_to; + mpfr_exp_t exponent; + unsigned int mant_bits, mant_off; + int mant_bits_left; + enum floatformat_byteorders order = fmt->byteorder; + unsigned char newto[FLOATFORMAT_LARGEST_BYTES]; + + if (order != floatformat_little) + order = floatformat_big; + + if (order != fmt->byteorder) + to = newto; + + memset (to, 0, floatformat_totalsize_bytes (fmt)); + + if (fmt->split_half) + { + gdb_mpfr top (from), bot (from); + + mpfr_set (top.val, from.val, MPFR_RNDN); + /* If the rounded top half is Inf, the bottom must be 0 not NaN + or Inf. */ + if (mpfr_inf_p (top.val)) + mpfr_set_zero (bot.val, 0); + else + mpfr_sub (bot.val, from.val, top.val, MPFR_RNDN); + + to_target (fmt->split_half, top, to); + to_target (fmt->split_half, bot, + to + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2); + return; + } + + gdb_mpfr tmp (from); + + if (mpfr_zero_p (from.val)) + goto finalize_byteorder; /* Result is zero */ + + mpfr_set (tmp.val, from.val, MPFR_RNDN); + + if (mpfr_nan_p (tmp.val)) /* Result is NaN */ + { + /* From is NaN */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Be sure it's not infinity, but NaN value is irrel. */ + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 1); + goto finalize_byteorder; + } + + /* If negative, set the sign bit. */ + if (mpfr_sgn (tmp.val) < 0) + { + put_field (to, order, fmt->totalsize, fmt->sign_start, 1, 1); + mpfr_neg (tmp.val, tmp.val, MPFR_RNDN); + } + + if (mpfr_inf_p (tmp.val)) /* Result is Infinity. */ + { + /* Infinity exponent is same as NaN's. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Infinity mantissa is all zeroes. */ + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + mpfr_frexp (&exponent, tmp.val, tmp.val, MPFR_RNDN); + + if (exponent + fmt->exp_bias <= 0) + { + /* The value is too small to be expressed in the destination + type (not enough bits in the exponent. Treat as 0. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, 0); + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + if (exponent + fmt->exp_bias >= (1 << fmt->exp_len)) + { + /* The value is too large to fit into the destination. + Treat as infinity. */ + put_field (to, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + put_field (to, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + put_field (to, order, fmt->totalsize, fmt->exp_start, fmt->exp_len, + exponent + fmt->exp_bias - 1); + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + while (mant_bits_left > 0) + { + unsigned long mant_long; + + mant_bits = mant_bits_left < 32 ? mant_bits_left : 32; + + mpfr_mul_2ui (tmp.val, tmp.val, 32, MPFR_RNDN); + mant_long = mpfr_get_ui (tmp.val, MPFR_RNDZ) & 0xffffffffL; + mpfr_sub_ui (tmp.val, tmp.val, mant_long, MPFR_RNDZ); + + /* If the integer bit is implicit, then we need to discard it. + If we are discarding a zero, we should be (but are not) creating + a denormalized number which means adjusting the exponent + (I think). */ + if (mant_bits_left == fmt->man_len + && fmt->intbit == floatformat_intbit_no) + { + mant_long <<= 1; + mant_long &= 0xffffffffL; + /* If we are processing the top 32 mantissa bits of a doublest + so as to convert to a float value with implied integer bit, + we will only be putting 31 of those 32 bits into the + final value due to the discarding of the top bit. In the + case of a small float value where the number of mantissa + bits is less than 32, discarding the top bit does not alter + the number of bits we will be adding to the result. */ + if (mant_bits == 32) + mant_bits -= 1; + } + + if (mant_bits < 32) + { + /* The bits we want are in the most significant MANT_BITS bits of + mant_long. Move them to the least significant. */ + mant_long >>= 32 - mant_bits; + } + + put_field (to, order, fmt->totalsize, + mant_off, mant_bits, mant_long); + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + finalize_byteorder: + /* Do we need to byte-swap the words in the result? */ + if (order != fmt->byteorder) + floatformat_normalize_byteorder (fmt, newto, orig_to); +} + +void +mpfr_float_ops::to_target (const struct type *type, + const gdb_mpfr &from, gdb_byte *to) const +{ + /* Ensure possible padding bytes in the target buffer are zeroed out. */ + memset (to, 0, TYPE_LENGTH (type)); + + to_target (floatformat_from_type (type), from, to); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a string, optionally using the print format FORMAT. */ +std::string +mpfr_float_ops::to_string (const gdb_byte *addr, + const struct type *type, + const char *format) const +{ + const struct floatformat *fmt = floatformat_from_type (type); + + /* 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, addr)) + return ""; + + /* Handle NaN and Inf. */ + enum float_kind kind = floatformat_classify (fmt, addr); + if (kind == float_nan) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + const char *mantissa = floatformat_mantissa (fmt, addr); + return string_printf ("%snan(0x%s)", sign, mantissa); + } + else if (kind == float_infinite) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + return string_printf ("%sinf", sign); + } + } + + /* Determine the format string to use on the host side. */ + std::string host_format = floatformat_printf_format (fmt, format, 'R'); + + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + + int size = mpfr_snprintf (NULL, 0, host_format.c_str (), tmp.val); + std::string str (size, '\0'); + mpfr_sprintf (&str[0], host_format.c_str (), tmp.val); + + return str; +} + +/* Parse string STRING into a target floating-number of type TYPE and + store it as byte-stream ADDR. Return whether parsing succeeded. */ +bool +mpfr_float_ops::from_string (gdb_byte *addr, + const struct type *type, + const std::string &in) const +{ + gdb_mpfr tmp (type); + + char *endptr; + mpfr_strtofr (tmp.val, in.c_str (), &endptr, 0, MPFR_RNDN); + + /* We only accept the whole string. */ + if (*endptr) + return false; + + to_target (type, tmp, addr); + return true; +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to an integer value (rounding towards zero). */ +LONGEST +mpfr_float_ops::to_longest (const gdb_byte *addr, + const struct type *type) const +{ + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + return mpfr_get_sj (tmp.val, MPFR_RNDZ); +} + +/* Convert signed integer VAL to a target floating-number of type TYPE + and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_longest (gdb_byte *addr, + const struct type *type, + LONGEST val) const +{ + gdb_mpfr tmp (type); + mpfr_set_sj (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert unsigned integer VAL to a target floating-number of type TYPE + and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_ulongest (gdb_byte *addr, + const struct type *type, + ULONGEST val) const +{ + gdb_mpfr tmp (type); + mpfr_set_uj (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a floating-point value in the host "double" format. */ +double +mpfr_float_ops::to_host_double (const gdb_byte *addr, + const struct type *type) const +{ + gdb_mpfr tmp (type); + from_target (type, addr, tmp); + return mpfr_get_d (tmp.val, MPFR_RNDN); +} + +/* Convert floating-point value VAL in the host "double" format to a target + floating-number of type TYPE and store it as byte-stream ADDR. */ +void +mpfr_float_ops::from_host_double (gdb_byte *addr, + const struct type *type, + double val) const +{ + gdb_mpfr tmp (type); + mpfr_set_d (tmp.val, val, MPFR_RNDN); + to_target (type, tmp, addr); +} + +/* Convert a floating-point number of type FROM_TYPE from the target + byte-stream FROM to a floating-point number of type TO_TYPE, and + store it to the target byte-stream TO. */ +void +mpfr_float_ops::convert (const gdb_byte *from, + const struct type *from_type, + gdb_byte *to, + const struct type *to_type) const +{ + gdb_mpfr from_tmp (from_type), to_tmp (to_type); + from_target (from_type, from, from_tmp); + mpfr_set (to_tmp.val, from_tmp.val, MPFR_RNDN); + to_target (to_type, to_tmp, to); +} + +/* Perform the binary operation indicated by OPCODE, using as operands the + target byte streams X and Y, interpreted as floating-point numbers of + types TYPE_X and TYPE_Y, respectively. Convert the result to type + TYPE_RES and store it into the byte-stream RES. */ +void +mpfr_float_ops::binop (enum exp_opcode op, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const +{ + gdb_mpfr x_tmp (type_x), y_tmp (type_y), tmp (type_res); + + from_target (type_x, x, x_tmp); + from_target (type_y, y, y_tmp); + + switch (op) + { + case BINOP_ADD: + mpfr_add (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_SUB: + mpfr_sub (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MUL: + mpfr_mul (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_DIV: + mpfr_div (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_EXP: + mpfr_pow (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MIN: + mpfr_min (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + case BINOP_MAX: + mpfr_max (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); + break; + + default: + error (_("Integer-only operation on floating point number.")); + break; + } + + to_target (type_res, tmp, res); +} + +/* Compare the two target byte streams X and Y, interpreted as floating-point + numbers of types TYPE_X and TYPE_Y, respectively. Return zero if X and Y + are equal, -1 if X is less than Y, and 1 otherwise. */ +int +mpfr_float_ops::compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const +{ + gdb_mpfr x_tmp (type_x), y_tmp (type_y); + + from_target (type_x, x, x_tmp); + from_target (type_y, y, y_tmp); + + if (mpfr_equal_p (x_tmp.val, y_tmp.val)) + return 0; + else if (mpfr_less_p (x_tmp.val, y_tmp.val)) + return -1; + else + return 1; +} + +#endif + + +/* Helper routines operating on decimal floating-point data. */ + +/* Decimal floating point is one of the extension to IEEE 754, which is + described in http://grouper.ieee.org/groups/754/revision.html and + http://www2.hursley.ibm.com/decimal/. It completes binary floating + point by representing floating point more exactly. */ + +/* The order of the following headers is important for making sure + decNumber structure is large enough to hold decimal128 digits. */ + +#include "dpd/decimal128.h" +#include "dpd/decimal64.h" +#include "dpd/decimal32.h" + +/* When using decimal128, this is the maximum string length + 1 + (value comes from libdecnumber's DECIMAL128_String constant). */ +#define MAX_DECIMAL_STRING 43 + +/* In GDB, we are using an array of gdb_byte to represent decimal values. + They are stored in host byte order. This routine does the conversion if + the target byte order is different. */ +static void +match_endianness (const gdb_byte *from, const struct type *type, gdb_byte *to) +{ + gdb_assert (TYPE_CODE (type) == TYPE_CODE_DECFLOAT); + + int len = TYPE_LENGTH (type); + int i; + +#if WORDS_BIGENDIAN +#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE +#else +#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG +#endif + + if (type_byte_order (type) == OPPOSITE_BYTE_ORDER) + for (i = 0; i < len; i++) + to[i] = from[len - i - 1]; + else + for (i = 0; i < len; i++) + to[i] = from[i]; + + return; +} + +/* Helper function to get the appropriate libdecnumber context for each size + of decimal float. */ +static void +set_decnumber_context (decContext *ctx, const struct type *type) +{ + gdb_assert (TYPE_CODE (type) == TYPE_CODE_DECFLOAT); + + switch (TYPE_LENGTH (type)) + { + case 4: + decContextDefault (ctx, DEC_INIT_DECIMAL32); + break; + case 8: + decContextDefault (ctx, DEC_INIT_DECIMAL64); + break; + case 16: + decContextDefault (ctx, DEC_INIT_DECIMAL128); + break; + } + + ctx->traps = 0; +} + +/* Check for errors signaled in the decimal context structure. */ +static void +decimal_check_errors (decContext *ctx) +{ + /* An error here could be a division by zero, an overflow, an underflow or + an invalid operation (from the DEC_Errors constant in decContext.h). + Since GDB doesn't complain about division by zero, overflow or underflow + errors for binary floating, we won't complain about them for decimal + floating either. */ + if (ctx->status & DEC_IEEE_854_Invalid_operation) + { + /* Leave only the error bits in the status flags. */ + ctx->status &= DEC_IEEE_854_Invalid_operation; + error (_("Cannot perform operation: %s"), + decContextStatusToString (ctx)); + } +} + +/* Helper function to convert from libdecnumber's appropriate representation + for computation to each size of decimal float. */ +static void +decimal_from_number (const decNumber *from, + gdb_byte *to, const struct type *type) +{ + gdb_byte dec[16]; + + decContext set; + + set_decnumber_context (&set, type); - switch (len) + switch (TYPE_LENGTH (type)) { case 4: decimal32FromNumber ((decimal32 *) dec, from, &set); @@ -1161,19 +1832,19 @@ decimal_from_number (const decNumber *from, break; } - match_endianness (dec, len, byte_order, to); + match_endianness (dec, type, to); } /* Helper function to convert each size of decimal float to libdecnumber's appropriate representation for computation. */ static void -decimal_to_number (const gdb_byte *from, int len, enum bfd_endian byte_order, +decimal_to_number (const gdb_byte *addr, const struct type *type, decNumber *to) { gdb_byte dec[16]; - match_endianness (from, len, byte_order, dec); + match_endianness (addr, type, dec); - switch (len) + switch (TYPE_LENGTH (type)) { case 4: decimal32ToNumber ((decimal32 *) dec, to); @@ -1190,16 +1861,70 @@ decimal_to_number (const gdb_byte *from, int len, enum bfd_endian byte_order, } } +/* Returns true if ADDR (which is of type TYPE) is the number zero. */ +static bool +decimal_is_zero (const gdb_byte *addr, const struct type *type) +{ + decNumber number; + + decimal_to_number (addr, type, &number); + + return decNumberIsZero (&number); +} + + +/* Implementation of target_float_ops using the libdecnumber decNumber type + as intermediate format. */ + +class decimal_float_ops : public target_float_ops +{ +public: + std::string to_string (const gdb_byte *addr, const struct type *type, + const char *format) const override; + bool from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const override; + + LONGEST to_longest (const gdb_byte *addr, + const struct type *type) const override; + void from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const override; + void from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const override; + double to_host_double (const gdb_byte *addr, + const struct type *type) const override + { + /* We don't support conversions between target decimal floating-point + types and the host double type. */ + gdb_assert_not_reached ("invalid operation on decimal float"); + } + void from_host_double (gdb_byte *addr, const struct type *type, + double val) const override + { + /* We don't support conversions between target decimal floating-point + types and the host double type. */ + gdb_assert_not_reached ("invalid operation on decimal float"); + } + void convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const override; + + void binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const override; + int compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const override; +}; + /* Convert decimal type to its string representation. LEN is the length of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and 16 bytes for decimal128. */ -static std::string -decimal_to_string (const gdb_byte *decbytes, int len, - enum bfd_endian byte_order, const char *format = nullptr) +std::string +decimal_float_ops::to_string (const gdb_byte *addr, const struct type *type, + const char *format = nullptr) const { gdb_byte dec[16]; - match_endianness (decbytes, len, byte_order, dec); + match_endianness (addr, type, dec); if (format != nullptr) { @@ -1215,7 +1940,7 @@ decimal_to_string (const gdb_byte *decbytes, int len, std::string result; result.resize (MAX_DECIMAL_STRING); - switch (len) + switch (TYPE_LENGTH (type)) { case 4: decimal32ToString ((decimal32 *) dec, &result[0]); @@ -1237,16 +1962,16 @@ decimal_to_string (const gdb_byte *decbytes, int len, /* Convert the string form of a decimal value to its decimal representation. LEN is the length of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and 16 bytes for decimal128. */ -static bool -decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, - const std::string &string) +bool +decimal_float_ops::from_string (gdb_byte *addr, const struct type *type, + const std::string &string) const { decContext set; gdb_byte dec[16]; - set_decnumber_context (&set, len); + set_decnumber_context (&set, type); - switch (len) + switch (TYPE_LENGTH (type)) { case 4: decimal32FromString ((decimal32 *) dec, string.c_str (), &set); @@ -1262,7 +1987,7 @@ decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, break; } - match_endianness (dec, len, byte_order, decbytes); + match_endianness (dec, type, addr); /* Check for errors in the DFP operation. */ decimal_check_errors (&set); @@ -1271,9 +1996,9 @@ decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, } /* Converts a LONGEST to a decimal float of specified LEN bytes. */ -static void -decimal_from_longest (LONGEST from, - gdb_byte *to, int len, enum bfd_endian byte_order) +void +decimal_float_ops::from_longest (gdb_byte *addr, const struct type *type, + LONGEST from) const { decNumber number; @@ -1284,13 +2009,13 @@ decimal_from_longest (LONGEST from, decNumberFromInt32 (&number, (int32_t) from); - decimal_from_number (&number, to, len, byte_order); + decimal_from_number (&number, addr, type); } /* Converts a ULONGEST to a decimal float of specified LEN bytes. */ -static void -decimal_from_ulongest (ULONGEST from, - gdb_byte *to, int len, enum bfd_endian byte_order) +void +decimal_float_ops::from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST from) const { decNumber number; @@ -1301,36 +2026,36 @@ decimal_from_ulongest (ULONGEST from, decNumberFromUInt32 (&number, (uint32_t) from); - decimal_from_number (&number, to, len, byte_order); + decimal_from_number (&number, addr, type); } /* Converts a decimal float of LEN bytes to a LONGEST. */ -static LONGEST -decimal_to_longest (const gdb_byte *from, int len, enum bfd_endian byte_order) +LONGEST +decimal_float_ops::to_longest (const gdb_byte *addr, + const struct type *type) const { /* libdecnumber has a function to convert from decimal to integer, but it doesn't work when the decimal number has a fractional part. */ - std::string str = decimal_to_string (from, len, byte_order); + std::string str = to_string (addr, type); return strtoll (str.c_str (), NULL, 10); } /* Perform operation OP with operands X and Y with sizes LEN_X and LEN_Y and byte orders BYTE_ORDER_X and BYTE_ORDER_Y, and store value in RESULT with size LEN_RESULT and byte order BYTE_ORDER_RESULT. */ -static void -decimal_binop (enum exp_opcode op, - const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, - const gdb_byte *y, int len_y, enum bfd_endian byte_order_y, - gdb_byte *result, int len_result, - enum bfd_endian byte_order_result) +void +decimal_float_ops::binop (enum exp_opcode op, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) const { decContext set; decNumber number1, number2, number3; - decimal_to_number (x, len_x, byte_order_x, &number1); - decimal_to_number (y, len_y, byte_order_y, &number2); + decimal_to_number (x, type_x, &number1); + decimal_to_number (y, type_y, &number2); - set_decnumber_context (&set, len_result); + set_decnumber_context (&set, type_res); switch (op) { @@ -1357,37 +2082,26 @@ decimal_binop (enum exp_opcode op, /* Check for errors in the DFP operation. */ decimal_check_errors (&set); - decimal_from_number (&number3, result, len_result, byte_order_result); -} - -/* Returns true if X (which is LEN bytes wide) is the number zero. */ -static int -decimal_is_zero (const gdb_byte *x, int len, enum bfd_endian byte_order) -{ - decNumber number; - - decimal_to_number (x, len, byte_order, &number); - - return decNumberIsZero (&number); + decimal_from_number (&number3, res, type_res); } /* Compares two numbers numerically. If X is less than Y then the return value will be -1. If they are equal, then the return value will be 0. If X is greater than the Y then the return value will be 1. */ -static int -decimal_compare (const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, - const gdb_byte *y, int len_y, enum bfd_endian byte_order_y) +int +decimal_float_ops::compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) const { decNumber number1, number2, result; decContext set; - int len_result; + const struct type *type_result; - decimal_to_number (x, len_x, byte_order_x, &number1); - decimal_to_number (y, len_y, byte_order_y, &number2); + decimal_to_number (x, type_x, &number1); + decimal_to_number (y, type_y, &number2); /* Perform the comparison in the larger of the two sizes. */ - len_result = len_x > len_y ? len_x : len_y; - set_decnumber_context (&set, len_result); + type_result = TYPE_LENGTH (type_x) > TYPE_LENGTH (type_y) ? type_x : type_y; + set_decnumber_context (&set, type_result); decNumberCompare (&result, &number1, &number2, &set); @@ -1406,15 +2120,14 @@ decimal_compare (const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, /* Convert a decimal value from a decimal type with LEN_FROM bytes to a decimal type with LEN_TO bytes. */ -static void -decimal_convert (const gdb_byte *from, int len_from, - enum bfd_endian byte_order_from, gdb_byte *to, int len_to, - enum bfd_endian byte_order_to) +void +decimal_float_ops::convert (const gdb_byte *from, const struct type *from_type, + gdb_byte *to, const struct type *to_type) const { decNumber number; - decimal_to_number (from, len_from, byte_order_from, &number); - decimal_from_number (&number, to, len_to, byte_order_to); + decimal_to_number (from, from_type, &number); + decimal_from_number (&number, to, to_type); } @@ -1423,6 +2136,180 @@ decimal_convert (const gdb_byte *from, int len_from, "struct type", which may be either a binary or decimal floating-point type (TYPE_CODE_FLT or TYPE_CODE_DECFLOAT). */ +/* Return whether TYPE1 and TYPE2 are of the same category (binary or + decimal floating-point). */ +static bool +target_float_same_category_p (const struct type *type1, + const struct type *type2) +{ + return TYPE_CODE (type1) == TYPE_CODE (type2); +} + +/* Return whether TYPE1 and TYPE2 use the same floating-point format. */ +static bool +target_float_same_format_p (const struct type *type1, + const struct type *type2) +{ + if (!target_float_same_category_p (type1, type2)) + return false; + + switch (TYPE_CODE (type1)) + { + case TYPE_CODE_FLT: + return floatformat_from_type (type1) == floatformat_from_type (type2); + + case TYPE_CODE_DECFLOAT: + return (TYPE_LENGTH (type1) == TYPE_LENGTH (type2) + && (type_byte_order (type1) + == type_byte_order (type2))); + + default: + gdb_assert_not_reached ("unexpected type code"); + } +} + +/* Return the size (without padding) of the target floating-point + format used by TYPE. */ +static int +target_float_format_length (const struct type *type) +{ + switch (TYPE_CODE (type)) + { + case TYPE_CODE_FLT: + return floatformat_totalsize_bytes (floatformat_from_type (type)); + + case TYPE_CODE_DECFLOAT: + return TYPE_LENGTH (type); + + default: + gdb_assert_not_reached ("unexpected type code"); + } +} + +/* Identifiers of available host-side intermediate formats. These must + be sorted so the that the more "general" kinds come later. */ +enum target_float_ops_kind +{ + /* Target binary floating-point formats that match a host format. */ + host_float = 0, + host_double, + host_long_double, + /* Any other target binary floating-point format. */ + binary, + /* Any target decimal floating-point format. */ + decimal +}; + +/* Given a target type TYPE, choose the best host-side intermediate format + to perform operations on TYPE in. */ +static enum target_float_ops_kind +get_target_float_ops_kind (const struct type *type) +{ + switch (TYPE_CODE (type)) + { + case TYPE_CODE_FLT: + { + const struct floatformat *fmt = floatformat_from_type (type); + + /* Binary floating-point formats matching a host format. */ + if (fmt == host_float_format) + return target_float_ops_kind::host_float; + if (fmt == host_double_format) + return target_float_ops_kind::host_double; + if (fmt == host_long_double_format) + return target_float_ops_kind::host_long_double; + + /* Any other binary floating-point format. */ + return target_float_ops_kind::binary; + } + + case TYPE_CODE_DECFLOAT: + { + /* Any decimal floating-point format. */ + return target_float_ops_kind::decimal; + } + + default: + gdb_assert_not_reached ("unexpected type code"); + } +} + +/* Return target_float_ops to peform operations for KIND. */ +static const target_float_ops * +get_target_float_ops (enum target_float_ops_kind kind) +{ + switch (kind) + { + /* If the type format matches one of the host floating-point + types, use that type as intermediate format. */ + case target_float_ops_kind::host_float: + { + static host_float_ops host_float_ops_float; + return &host_float_ops_float; + } + + case target_float_ops_kind::host_double: + { + static host_float_ops host_float_ops_double; + return &host_float_ops_double; + } + + case target_float_ops_kind::host_long_double: + { + static host_float_ops host_float_ops_long_double; + return &host_float_ops_long_double; + } + + /* For binary floating-point formats that do not match any host format, + use mpfr_t as intermediate format to provide precise target-floating + point emulation. However, if the MPFR library is not available, + use the largest host floating-point type as intermediate format. */ + case target_float_ops_kind::binary: + { +#ifdef HAVE_LIBMPFR + static mpfr_float_ops binary_float_ops; +#else + static host_float_ops binary_float_ops; +#endif + return &binary_float_ops; + } + + /* For decimal floating-point types, always use the libdecnumber + decNumber type as intermediate format. */ + case target_float_ops_kind::decimal: + { + static decimal_float_ops decimal_float_ops; + return &decimal_float_ops; + } + + default: + gdb_assert_not_reached ("unexpected target_float_ops_kind"); + } +} + +/* Given a target type TYPE, determine the best host-side intermediate format + to perform operations on TYPE in. */ +static const target_float_ops * +get_target_float_ops (const struct type *type) +{ + enum target_float_ops_kind kind = get_target_float_ops_kind (type); + return get_target_float_ops (kind); +} + +/* The same for operations involving two target types TYPE1 and TYPE2. */ +static const target_float_ops * +get_target_float_ops (const struct type *type1, const struct type *type2) +{ + gdb_assert (TYPE_CODE (type1) == TYPE_CODE (type2)); + + enum target_float_ops_kind kind1 = get_target_float_ops_kind (type1); + enum target_float_ops_kind kind2 = get_target_float_ops_kind (type2); + + /* Given the way the kinds are sorted, we simply choose the larger one; + this will be able to hold values of either type. */ + return get_target_float_ops (std::max (kind1, kind2)); +} + /* Return whether the byte-stream ADDR holds a valid value of floating-point type TYPE. */ bool @@ -1447,8 +2334,7 @@ target_float_is_zero (const gdb_byte *addr, const struct type *type) == float_zero); if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_is_zero (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); + return decimal_is_zero (addr, type); gdb_assert_not_reached ("unexpected type code"); } @@ -1459,15 +2345,33 @@ std::string target_float_to_string (const gdb_byte *addr, const struct type *type, const char *format) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) - return floatformat_to_string (floatformat_from_type (type), addr, format); + /* Unless we need to adhere to a specific format, provide special + output for special cases of binary floating-point numbers. */ + if (format == nullptr && TYPE_CODE (type) == TYPE_CODE_FLT) + { + const struct floatformat *fmt = floatformat_from_type (type); - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_to_string (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type)), - format); + /* Detect invalid representations. */ + if (!floatformat_is_valid (fmt, addr)) + return ""; - gdb_assert_not_reached ("unexpected type code"); + /* Handle NaN and Inf. */ + enum float_kind kind = floatformat_classify (fmt, addr); + if (kind == float_nan) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + const char *mantissa = floatformat_mantissa (fmt, addr); + return string_printf ("%snan(0x%s)", sign, mantissa); + } + else if (kind == float_infinite) + { + const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; + return string_printf ("%sinf", sign); + } + } + + const target_float_ops *ops = get_target_float_ops (type); + return ops->to_string (addr, type, format); } /* Parse string STRING into a target floating-number of type TYPE and @@ -1476,19 +2380,8 @@ bool target_float_from_string (gdb_byte *addr, const struct type *type, const std::string &string) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (addr, 0, TYPE_LENGTH (type)); - - if (TYPE_CODE (type) == TYPE_CODE_FLT) - return floatformat_from_string (floatformat_from_type (type), addr, - string); - - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_from_string (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type)), - string); - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + return ops->from_string (addr, type, string); } /* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, @@ -1496,14 +2389,8 @@ target_float_from_string (gdb_byte *addr, const struct type *type, LONGEST target_float_to_longest (const gdb_byte *addr, const struct type *type) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) - return floatformat_to_longest (floatformat_from_type (type), addr); - - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_to_longest (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + return ops->to_longest (addr, type); } /* Convert signed integer VAL to a target floating-number of type TYPE @@ -1512,23 +2399,8 @@ void target_float_from_longest (gdb_byte *addr, const struct type *type, LONGEST val) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (addr, 0, TYPE_LENGTH (type)); - - if (TYPE_CODE (type) == TYPE_CODE_FLT) - { - floatformat_from_longest (floatformat_from_type (type), addr, val); - return; - } - - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - { - decimal_from_longest (val, addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); - return; - } - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + ops->from_longest (addr, type, val); } /* Convert unsigned integer VAL to a target floating-number of type TYPE @@ -1537,23 +2409,8 @@ void target_float_from_ulongest (gdb_byte *addr, const struct type *type, ULONGEST val) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (addr, 0, TYPE_LENGTH (type)); - - if (TYPE_CODE (type) == TYPE_CODE_FLT) - { - floatformat_from_ulongest (floatformat_from_type (type), addr, val); - return; - } - - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - { - decimal_from_ulongest (val, addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); - return; - } - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + ops->from_ulongest (addr, type, val); } /* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, @@ -1562,13 +2419,8 @@ double target_float_to_host_double (const gdb_byte *addr, const struct type *type) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) - return floatformat_to_host_double (floatformat_from_type (type), addr); - - /* We don't support conversions between target decimal floating-point - types and the host double type here. */ - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + return ops->to_host_double (addr, type); } /* Convert floating-point value VAL in the host "double" format to a target @@ -1577,19 +2429,8 @@ void target_float_from_host_double (gdb_byte *addr, const struct type *type, double val) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (addr, 0, TYPE_LENGTH (type)); - - if (TYPE_CODE (type) == TYPE_CODE_FLT) - { - floatformat_from_host_double (floatformat_from_type (type), addr, val); - return; - } - - /* We don't support conversions between target decimal floating-point - types and the host double type here. */ - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type); + ops->from_host_double (addr, type, val); } /* Convert a floating-point number of type FROM_TYPE from the target @@ -1599,43 +2440,27 @@ void target_float_convert (const gdb_byte *from, const struct type *from_type, gdb_byte *to, const struct type *to_type) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (to, 0, TYPE_LENGTH (to_type)); - - /* Use direct conversion routines if we have them. */ - - if (TYPE_CODE (from_type) == TYPE_CODE_FLT - && TYPE_CODE (to_type) == TYPE_CODE_FLT) - { - floatformat_convert (from, floatformat_from_type (from_type), - to, floatformat_from_type (to_type)); - return; - } - - if (TYPE_CODE (from_type) == TYPE_CODE_DECFLOAT - && TYPE_CODE (to_type) == TYPE_CODE_DECFLOAT) - { - decimal_convert (from, TYPE_LENGTH (from_type), - gdbarch_byte_order (get_type_arch (from_type)), - to, TYPE_LENGTH (to_type), - gdbarch_byte_order (get_type_arch (to_type))); - return; - } - /* We cannot directly convert between binary and decimal floating-point types, so go via an intermediary string. */ - - if ((TYPE_CODE (from_type) == TYPE_CODE_FLT - && TYPE_CODE (to_type) == TYPE_CODE_DECFLOAT) - || (TYPE_CODE (from_type) == TYPE_CODE_DECFLOAT - && TYPE_CODE (to_type) == TYPE_CODE_FLT)) + if (!target_float_same_category_p (from_type, to_type)) { std::string str = target_float_to_string (from, from_type); target_float_from_string (to, to_type, str); return; } - gdb_assert_not_reached ("unexpected type code"); + /* Convert between two different formats in the same category. */ + if (!target_float_same_format_p (from_type, to_type)) + { + const target_float_ops *ops = get_target_float_ops (from_type, to_type); + ops->convert (from, from_type, to, to_type); + return; + } + + /* The floating-point formats match, so we simply copy the data, ensuring + possible padding bytes in the target buffer are zeroed out. */ + memset (to, 0, TYPE_LENGTH (to_type)); + memcpy (to, from, target_float_format_length (to_type)); } /* Perform the binary operation indicated by OPCODE, using as operands the @@ -1652,33 +2477,11 @@ target_float_binop (enum exp_opcode opcode, const gdb_byte *y, const struct type *type_y, gdb_byte *res, const struct type *type_res) { - /* Ensure possible padding bytes in the target buffer are zeroed out. */ - memset (res, 0, TYPE_LENGTH (type_res)); - - if (TYPE_CODE (type_res) == TYPE_CODE_FLT) - { - gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_FLT); - gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT); - return floatformat_binop (opcode, - floatformat_from_type (type_x), x, - floatformat_from_type (type_y), y, - floatformat_from_type (type_res), res); - } - - if (TYPE_CODE (type_res) == TYPE_CODE_DECFLOAT) - { - gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT); - gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT); - return decimal_binop (opcode, - x, TYPE_LENGTH (type_x), - gdbarch_byte_order (get_type_arch (type_x)), - y, TYPE_LENGTH (type_y), - gdbarch_byte_order (get_type_arch (type_y)), - res, TYPE_LENGTH (type_res), - gdbarch_byte_order (get_type_arch (type_res))); - } + gdb_assert (target_float_same_category_p (type_x, type_res)); + gdb_assert (target_float_same_category_p (type_y, type_res)); - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type_x, type_y); + ops->binop (opcode, x, type_x, y, type_y, res, type_res); } /* Compare the two target byte streams X and Y, interpreted as floating-point @@ -1692,22 +2495,9 @@ int target_float_compare (const gdb_byte *x, const struct type *type_x, const gdb_byte *y, const struct type *type_y) { - if (TYPE_CODE (type_x) == TYPE_CODE_FLT) - { - gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT); - return floatformat_compare (floatformat_from_type (type_x), x, - floatformat_from_type (type_y), y); - } + gdb_assert (target_float_same_category_p (type_x, type_y)); - if (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT) - { - gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT); - return decimal_compare (x, TYPE_LENGTH (type_x), - gdbarch_byte_order (get_type_arch (type_x)), - y, TYPE_LENGTH (type_y), - gdbarch_byte_order (get_type_arch (type_y))); - } - - gdb_assert_not_reached ("unexpected type code"); + const target_float_ops *ops = get_target_float_ops (type_x, type_y); + return ops->compare (x, type_x, y, type_y); }