diff options
-rw-r--r-- | gdb/ChangeLog | 107 | ||||
-rw-r--r-- | gdb/target-float.c | 1052 | ||||
-rw-r--r-- | gdb/testsuite/ChangeLog | 4 | ||||
-rw-r--r-- | gdb/testsuite/gdb.arch/vsx-regs.exp | 12 |
4 files changed, 747 insertions, 428 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 63ae72e..b330c7b 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,110 @@ +2017-11-22 Ulrich Weigand <uweigand@de.ibm.com> + + * target-float.c: Do not include <math.h>. + Include <cmath> and <limits>. + (DOUBLEST): Do not define. + (class target_float_ops): New type. + (class host_float_ops): New templated type. + (class decimal_float_ops): New type. + + (floatformat_to_doublest): Rename to ... + (host_float_ops<T>::from_target): ... this. Use template type T + instead of DOUBLEST. Use C++ math routines. Update recursive calls. + (host_float_ops<T>::from_target): New overload using a type argument. + (floatformat_from_doublest): Rename to ... + (host_float_ops<T>::to_target): ... this. Use template type T + instead of DOUBLEST. Use C++ math routines. Update recursive calls. + (host_float_ops<T>::to_target): New overload using a type argument. + (floatformat_printf_format): New function. + (struct printf_length_modifier): New templated type. + (floatformat_to_string): Rename to ... + (host_float_ops<T>::to_string): ... this. Use type instead of + floatformat argument. Use floatformat_printf_format and + printf_length_modifier. Remove special handling of invalid numbers, + infinities and NaN (moved to target_float_to_string). + (struct scanf_length_modifier): New templated type. + (floatformat_from_string): Rename to ... + (host_float_ops<T>::from_string): ... this. Use type instead of + floatformat argument. Use scanf_length_modifier. + (floatformat_to_longest): Rename to ... + (host_float_ops<T>::to_longest): ... this. Use type instead of + floatformat argument. Handle out-of-range values deterministically. + (floatformat_from_longest): Rename to ... + (host_float_ops<T>::from_longest): ... this. Use type instead of + floatformat argument. + (floatformat_from_ulongest): Rename to ... + (host_float_ops<T>::from_ulongest): ... this. Use type instead of + floatformat argument. + (floatformat_to_host_double): Rename to ... + (host_float_ops<T>::to_host_double): ... this. Use type instead of + floatformat argument. + (floatformat_from_host_double): Rename to ... + (host_float_ops<T>::from_host_double): ... this. Use type instead of + floatformat argument. + (floatformat_convert): Rename to ... + (host_float_ops<T>::convert): ... this. Use type instead of + floatformat arguments. Remove handling of no-op conversions. + (floatformat_binop): Rename to ... + (host_float_ops<T>::binop): ... this. Use type instead of + floatformat arguments. + (floatformat_compare): Rename to ... + (host_float_ops<T>::compare): ... this. Use type instead of + floatformat arguments. + + (match_endianness): Use type instead of length/byte_order arguments. + (set_decnumber_context): Likewise. + (decimal_from_number): Likewise. Update calls. + (decimal_to_number): Likewise. + (decimal_is_zero): Likewise. Update calls. Move to earlier in file. + (decimal_float_ops::to_host_double): New dummy function. + (decimal_float_ops::from_host_double): Likewise. + (decimal_to_string): Rename to ... + (decimal_float_ops::to_string): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_from_string): Rename to ... + (decimal_float_ops::from_string): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_from_longest): Rename to ... + (decimal_float_ops::from_longest): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_from_ulongest): Rename to ... + (decimal_float_ops::from_ulongest): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_to_longest): Rename to ... + (decimal_float_ops::to_longest): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_binop): Rename to ... + (decimal_float_ops::binop): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_compare): Rename to ... + (decimal_float_ops::compare): ... this. Use type instead of + length/byte_order arguments. Update calls. + (decimal_convert): Rename to ... + (decimal_float_ops::convert): ... this. Use type instead of + length/byte_order arguments. Update calls. + + (target_float_same_category_p): New function. + (target_float_same_format_p): Likewise. + (target_float_format_length): Likewise. + (enum target_float_ops_kind): New type. + (get_target_float_ops_kind): New function. + (get_target_float_ops): Three new overloaded functions. + + (target_float_is_zero): Update call. + (target_float_to_string): Add special handling of invalid numbers, + infinities and NaN (moved from floatformat_to_string). Use + target_float_ops callback. + (target_float_from_string): Use target_float_ops callback. + (target_float_to_longest): Likewise. + (target_float_from_longest): Likewise. + (target_float_from_ulongest): Likewise. + (target_float_to_host_double): Likewise. + (target_float_from_host_double): Likewise. + (target_float_convert): Add special case for no-op conversions. + Use target_float_ops callback. + (target_float_binop): Use target_float_ops callback. + (target_float_compare): Likewise. + 2017-11-22 Yao Qi <yao.qi@linaro.org> * python/py-gdb-readline.c (gdbpy_readline_wrapper): Use strcpy. diff --git a/gdb/target-float.c b/gdb/target-float.c index 8bc027e..8f1b9f6 100644 --- a/gdb/target-float.c +++ b/gdb/target-float.c @@ -23,21 +23,48 @@ #include "target-float.h" -/* Helper routines operating on binary floating-point data. */ +/* Target floating-point operations. -#include <math.h> + We provide multiple implementations of those operations, which differ + by the host-side intermediate format they perform computations in. -#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 + Those multiple implementations all derive from the following abstract + base class, which specifies the set of operations to be implemented. */ + +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 <cmath> +#include <limits> /* 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<typename T> 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<typename T> void +host_float_ops<T>::from_target (const struct floatformat *fmt, + const gdb_byte *from, T *to) const { gdb_assert (fmt != NULL); @@ -501,7 +627,7 @@ floatformat_to_doublest (const struct floatformat *fmt, } unsigned char *ufrom = (unsigned char *) from; - DOUBLEST dto; + T dto; long exponent; unsigned long mant; unsigned int mant_bits, mant_off; @@ -524,7 +650,7 @@ floatformat_to_doublest (const struct floatformat *fmt, floatformat_to_double (fmt->split_half ? fmt->split_half : fmt, from, &dto); - *to = (DOUBLEST) dto; + *to = (T) dto; return; } @@ -535,9 +661,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 +671,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; } @@ -593,7 +718,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 +730,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<typename T> void +host_float_ops<T>::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<typename T> void +host_float_ops<T>::to_target (const struct floatformat *fmt, + const T *from, gdb_byte *to) const { gdb_assert (fmt != NULL); @@ -635,9 +767,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 +791,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 +799,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 +839,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 +917,67 @@ 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<typename T> void +host_float_ops<T>::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 "<invalid float value>"; + /* 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<typename T> struct printf_length_modifier +{ + static constexpr char value = 0; +}; +template<> struct printf_length_modifier<long double> +{ + static constexpr char value = 'L'; +}; +template<typename T> std::string +host_float_ops<T>::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 + constexpr char length = printf_length_modifier<T>::value; + const struct floatformat *fmt = floatformat_from_type (type); + std::string host_format = floatformat_printf_format (fmt, format, length); - 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 host DOUBLEST type. */ -#ifdef HAVE_LONG_DOUBLE - host_format += 'L'; -#endif - host_format += conversion; - - DOUBLEST doub; - floatformat_to_doublest (fmt, in, &doub); - return string_printf (host_format.c_str (), doub); + T host_float; + from_target (type, addr, &host_float); + return string_printf (host_format.c_str (), host_float); } -/* 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<typename T> struct scanf_length_modifier +{ + static constexpr char value = 0; +}; +template<> struct scanf_length_modifier<double> +{ + static constexpr char value = 'l'; +}; +template<> struct scanf_length_modifier<long double> +{ + static constexpr char value = 'L'; +}; +template<typename T> bool +host_float_ops<T>::from_string (gdb_byte *addr, const struct type *type, + const std::string &in) const { - DOUBLEST doub; + 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<T>::value) + scan_format += scanf_length_modifier<T>::value; + scan_format += "g%n"; + + num = sscanf (in.c_str (), scan_format.c_str(), &host_float, &n); /* The sscanf man page suggests not making any assumptions on the effect of %n on the result, so we don't. @@ -894,100 +989,96 @@ 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<typename T> LONGEST +host_float_ops<T>::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); + /* Converting an out-of-range value is undefined behavior in C, but we + prefer to return a defined value here. */ + if (host_float > std::numeric_limits<LONGEST>::max()) + return std::numeric_limits<LONGEST>::max(); + if (host_float < std::numeric_limits<LONGEST>::min()) + return std::numeric_limits<LONGEST>::min(); + return (LONGEST) host_float; } -/* 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<typename T> void +host_float_ops<T>::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<typename T> void +host_float_ops<T>::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<typename T> double +host_float_ops<T>::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<typename T> void +host_float_ops<T>::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<typename T> void +host_float_ops<T>::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<typename T> void +host_float_ops<T>::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 +1119,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<typename T> int +host_float_ops<T>::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; @@ -1073,9 +1164,11 @@ floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, 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) +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 @@ -1084,7 +1177,7 @@ match_endianness (const gdb_byte *from, int len, enum bfd_endian byte_order, #define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG #endif - if (byte_order == OPPOSITE_BYTE_ORDER) + if (gdbarch_byte_order (get_type_arch (type)) == OPPOSITE_BYTE_ORDER) for (i = 0; i < len; i++) to[i] = from[len - i - 1]; else @@ -1097,9 +1190,11 @@ match_endianness (const gdb_byte *from, int len, enum bfd_endian byte_order, /* Helper function to get the appropriate libdecnumber context for each size of decimal float. */ static void -set_decnumber_context (decContext *ctx, int len) +set_decnumber_context (decContext *ctx, const struct type *type) { - switch (len) + gdb_assert (TYPE_CODE (type) == TYPE_CODE_DECFLOAT); + + switch (TYPE_LENGTH (type)) { case 4: decContextDefault (ctx, DEC_INIT_DECIMAL32); @@ -1137,15 +1232,15 @@ decimal_check_errors (decContext *ctx) 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 *to, const struct type *type) { gdb_byte dec[16]; decContext set; - set_decnumber_context (&set, len); + set_decnumber_context (&set, type); - switch (len) + switch (TYPE_LENGTH (type)) { case 4: decimal32FromNumber ((decimal32 *) dec, from, &set); @@ -1161,19 +1256,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 +1285,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 +1364,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 +1386,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 +1411,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 +1420,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 +1433,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 +1450,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 +1506,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 +1544,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 +1560,174 @@ 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) + && (gdbarch_byte_order (get_type_arch (type1)) + == gdbarch_byte_order (get_type_arch (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<float> host_float_ops_float; + return &host_float_ops_float; + } + + case target_float_ops_kind::host_double: + { + static host_float_ops<double> host_float_ops_double; + return &host_float_ops_double; + } + + case target_float_ops_kind::host_long_double: + { + static host_float_ops<long double> host_float_ops_long_double; + return &host_float_ops_long_double; + } + + /* For binary floating-point formats that do not match any host format, + use the largest host floating-point type as intermediate format. */ + case target_float_ops_kind::binary: + { + static host_float_ops<long double> binary_float_ops; + 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 +1752,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 +1763,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 "<invalid float value>"; - 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 +1798,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 +1807,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 +1817,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 +1827,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 +1837,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 +1847,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 +1858,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 +1895,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 +1913,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); } diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 7434105..b916e93 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,7 @@ +2017-11-22 Ulrich Weigand <uweigand@de.ibm.com> + + * gdb.arch/vsx-regs.exp: Update register content checks. + 2017-11-21 Ulrich Weigand <uweigand@de.ibm.com> * gdb.arch/ppc-longdouble.exp: New file. diff --git a/gdb/testsuite/gdb.arch/vsx-regs.exp b/gdb/testsuite/gdb.arch/vsx-regs.exp index e6b8d72..e45aae5 100644 --- a/gdb/testsuite/gdb.arch/vsx-regs.exp +++ b/gdb/testsuite/gdb.arch/vsx-regs.exp @@ -61,11 +61,11 @@ set endianness [get_endianness] # Data sets used throughout the test if {$endianness == "big"} { - set vector_register1 ".uint128 = 0x3ff4cccccccccccc0000000000000000, v2_double = .0x1, 0x0., v4_float = .0x1, 0xf99999a0, 0x0, 0x0., v4_int32 = .0x3ff4cccc, 0xcccccccc, 0x0, 0x0., v8_int16 = .0x3ff4, 0xcccc, 0xcccc, 0xcccc, 0x0, 0x0, 0x0, 0x0., v16_int8 = .0x3f, 0xf4, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0.." + set vector_register1 ".uint128 = 0x3ff4cccccccccccd0000000000000000, v2_double = .0x1, 0x0., v4_float = .0x1, 0xf9999998, 0x0, 0x0., v4_int32 = .0x3ff4cccc, 0xcccccccd, 0x0, 0x0., v8_int16 = .0x3ff4, 0xcccc, 0xcccc, 0xcccd, 0x0, 0x0, 0x0, 0x0., v16_int8 = .0x3f, 0xf4, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0.." - set vector_register1_vr ".uint128 = 0x3ff4cccccccccccc0000000100000001, v4_float = .0x1, 0xf99999a0, 0x0, 0x0., v4_int32 = .0x3ff4cccc, 0xcccccccc, 0x1, 0x1., v8_int16 = .0x3ff4, 0xcccc, 0xcccc, 0xcccc, 0x0, 0x1, 0x0, 0x1., v16_int8 = .0x3f, 0xf4, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1.." + set vector_register1_vr ".uint128 = 0x3ff4cccccccccccd0000000100000001, v4_float = .0x1, 0xf9999998, 0x0, 0x0., v4_int32 = .0x3ff4cccc, 0xcccccccd, 0x1, 0x1., v8_int16 = .0x3ff4, 0xcccc, 0xcccc, 0xcccd, 0x0, 0x1, 0x0, 0x1., v16_int8 = .0x3f, 0xf4, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcd, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1.." - set vector_register2 "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v2_double = .0x1, 0x1., v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef., v16_int8 = .0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef.." + set vector_register2 "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v2_double = .0x8000000000000000, 0x8000000000000000., v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef., v16_int8 = .0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef.." set vector_register2_vr "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef., v16_int8 = .0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef, 0xde, 0xad, 0xbe, 0xef.." @@ -73,11 +73,11 @@ if {$endianness == "big"} { set vector_register3_vr ".uint128 = 0x1000000010000000100000001, v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0x1, 0x1, 0x1, 0x1., v8_int16 = .0x0, 0x1, 0x0, 0x1, 0x0, 0x1, 0x0, 0x1., v16_int8 = .0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0x1.." } else { - set vector_register1 ".uint128 = 0x3ff4cccccccccccc0000000000000000, v2_double = .0x0, 0x1., v4_float = .0x0, 0x0, 0xf99999a0, 0x1., v4_int32 = .0x0, 0x0, 0xcccccccc, 0x3ff4cccc., v8_int16 = .0x0, 0x0, 0x0, 0x0, 0xcccc, 0xcccc, 0xcccc, 0x3ff4., v16_int8 = .0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f.." + set vector_register1 ".uint128 = 0x3ff4cccccccccccd0000000000000000, v2_double = .0x0, 0x1., v4_float = .0x0, 0x0, 0xf9999998, 0x1., v4_int32 = .0x0, 0x0, 0xcccccccd, 0x3ff4cccc., v8_int16 = .0x0, 0x0, 0x0, 0x0, 0xcccd, 0xcccc, 0xcccc, 0x3ff4., v16_int8 = .0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f.." - set vector_register1_vr ".uint128 = 0x3ff4cccccccccccc0000000100000001, v4_float = .0x0, 0x0, 0xf99999a0, 0x1., v4_int32 = .0x1, 0x1, 0xcccccccc, 0x3ff4cccc., v8_int16 = .0x1, 0x0, 0x1, 0x0, 0xcccc, 0xcccc, 0xcccc, 0x3ff4., v16_int8 = .0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f.." + set vector_register1_vr ".uint128 = 0x3ff4cccccccccccd0000000100000001, v4_float = .0x0, 0x0, 0xf9999998, 0x1., v4_int32 = .0x1, 0x1, 0xcccccccd, 0x3ff4cccc., v8_int16 = .0x1, 0x0, 0x1, 0x0, 0xcccd, 0xcccc, 0xcccc, 0x3ff4., v16_int8 = .0x1, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xcd, 0xcc, 0xcc, 0xcc, 0xcc, 0xcc, 0xf4, 0x3f.." - set vector_register2 "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v2_double = .0x1, 0x1., v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead., v16_int8 = .0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde.." + set vector_register2 "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v2_double = .0x8000000000000000, 0x8000000000000000., v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead., v16_int8 = .0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde.." set vector_register2_vr "uint128 = 0xdeadbeefdeadbeefdeadbeefdeadbeef, v4_float = .0x0, 0x0, 0x0, 0x0., v4_int32 = .0xdeadbeef, 0xdeadbeef, 0xdeadbeef, 0xdeadbeef., v8_int16 = .0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead, 0xbeef, 0xdead., v16_int8 = .0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde, 0xef, 0xbe, 0xad, 0xde.." |