X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Ftarget-float.c;h=d077ca2e8e888fd1554ddbbaf01dfdc54c3d76fc;hb=0d02e70b197c786f26175b9a73f94e01d14abdab;hp=ad04f8fb4a195f77572ba2702f6868d3451be11f;hpb=66c02b9ed1eabf1d7981c0c27ec9fd3c17fc5d35;p=binutils-gdb.git diff --git a/gdb/target-float.c b/gdb/target-float.c index ad04f8fb4a1..d077ca2e8e8 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-2022 Free Software Foundation, Inc. This file is part of GDB. @@ -18,146 +18,2116 @@ along with this program. If not, see . */ #include "defs.h" -#include "dfp.h" -#include "doublest.h" #include "gdbtypes.h" #include "floatformat.h" #include "target-float.h" +#include "gdbarch.h" + +/* Target floating-point operations. + + We provide multiple implementations of those operations, which differ + by the host-side intermediate format they perform computations in. + + 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 +#include +#include + +/* Different kinds of floatformat numbers recognized by + floatformat_classify. To avoid portability issues, we use local + values instead of the C99 macros (FP_NAN et cetera). */ +enum float_kind { + float_nan, + float_infinite, + float_zero, + float_normal, + float_subnormal +}; + +/* The odds that CHAR_BIT will be anything but 8 are low enough that I'm not + going to bother with trying to muck around with whether it is defined in + a system header, what we do if not, etc. */ +#define FLOATFORMAT_CHAR_BIT 8 + +/* The number of bytes that the largest floating-point type that we + can convert to doublest will need. */ +#define FLOATFORMAT_LARGEST_BYTES 16 + +/* Return the floatformat's total size in host bytes. */ +static size_t +floatformat_totalsize_bytes (const struct floatformat *fmt) +{ + return ((fmt->totalsize + FLOATFORMAT_CHAR_BIT - 1) + / FLOATFORMAT_CHAR_BIT); +} + +/* Return the precision of the floating point format FMT. */ +static int +floatformat_precision (const struct floatformat *fmt) +{ + /* Assume the precision of and IBM long double is twice the precision + of the underlying double. This matches what GCC does. */ + if (fmt->split_half) + return 2 * floatformat_precision (fmt->split_half); + + /* Otherwise, the precision is the size of mantissa in bits, + including the implicit bit if present. */ + int prec = fmt->man_len; + if (fmt->intbit == floatformat_intbit_no) + prec++; + + return prec; +} + +/* Normalize the byte order of FROM into TO. If no normalization is + needed then FMT->byteorder is returned and TO is not changed; + otherwise the format of the normalized form in TO is returned. */ +static enum floatformat_byteorders +floatformat_normalize_byteorder (const struct floatformat *fmt, + const void *from, void *to) +{ + const unsigned char *swapin; + unsigned char *swapout; + int words; + + if (fmt->byteorder == floatformat_little + || fmt->byteorder == floatformat_big) + return fmt->byteorder; + + words = fmt->totalsize / FLOATFORMAT_CHAR_BIT; + words >>= 2; + + swapout = (unsigned char *)to; + swapin = (const unsigned char *)from; + + if (fmt->byteorder == floatformat_vax) + { + while (words-- > 0) + { + *swapout++ = swapin[1]; + *swapout++ = swapin[0]; + *swapout++ = swapin[3]; + *swapout++ = swapin[2]; + swapin += 4; + } + /* This may look weird, since VAX is little-endian, but it is + easier to translate to big-endian than to little-endian. */ + return floatformat_big; + } + else + { + gdb_assert (fmt->byteorder == floatformat_littlebyte_bigword); + + while (words-- > 0) + { + *swapout++ = swapin[3]; + *swapout++ = swapin[2]; + *swapout++ = swapin[1]; + *swapout++ = swapin[0]; + swapin += 4; + } + return floatformat_big; + } +} + +/* Extract a field which starts at START and is LEN bytes long. DATA and + TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */ +static unsigned long +get_field (const bfd_byte *data, enum floatformat_byteorders order, + unsigned int total_len, unsigned int start, unsigned int len) +{ + unsigned long result; + unsigned int cur_byte; + int cur_bitshift; + + /* Caller must byte-swap words before calling this routine. */ + gdb_assert (order == floatformat_little || order == floatformat_big); + + /* Start at the least significant part of the field. */ + if (order == floatformat_little) + { + /* We start counting from the other end (i.e, from the high bytes + rather than the low bytes). As such, we need to be concerned + with what happens if bit 0 doesn't start on a byte boundary. + I.e, we need to properly handle the case where total_len is + not evenly divisible by 8. So we compute ``excess'' which + represents the number of bits from the end of our starting + byte needed to get to bit 0. */ + int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT); + + cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) + - ((start + len + excess) / FLOATFORMAT_CHAR_BIT); + cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT) + - FLOATFORMAT_CHAR_BIT; + } + else + { + cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT; + cur_bitshift = + ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT; + } + if (cur_bitshift > -FLOATFORMAT_CHAR_BIT) + result = *(data + cur_byte) >> (-cur_bitshift); + else + result = 0; + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + + /* Move towards the most significant part of the field. */ + while (cur_bitshift < len) + { + result |= (unsigned long)*(data + cur_byte) << cur_bitshift; + cur_bitshift += FLOATFORMAT_CHAR_BIT; + switch (order) + { + case floatformat_little: + ++cur_byte; + break; + case floatformat_big: + --cur_byte; + break; + } + } + if (len < sizeof(result) * FLOATFORMAT_CHAR_BIT) + /* Mask out bits which are not part of the field. */ + result &= ((1UL << len) - 1); + return result; +} + +/* Set a field which starts at START and is LEN bytes long. DATA and + TOTAL_LEN are the thing we are extracting it from, in byteorder ORDER. */ +static void +put_field (unsigned char *data, enum floatformat_byteorders order, + unsigned int total_len, unsigned int start, unsigned int len, + unsigned long stuff_to_put) +{ + unsigned int cur_byte; + int cur_bitshift; + + /* Caller must byte-swap words before calling this routine. */ + gdb_assert (order == floatformat_little || order == floatformat_big); + + /* Start at the least significant part of the field. */ + if (order == floatformat_little) + { + int excess = FLOATFORMAT_CHAR_BIT - (total_len % FLOATFORMAT_CHAR_BIT); + + cur_byte = (total_len / FLOATFORMAT_CHAR_BIT) + - ((start + len + excess) / FLOATFORMAT_CHAR_BIT); + cur_bitshift = ((start + len + excess) % FLOATFORMAT_CHAR_BIT) + - FLOATFORMAT_CHAR_BIT; + } + else + { + cur_byte = (start + len) / FLOATFORMAT_CHAR_BIT; + cur_bitshift = + ((start + len) % FLOATFORMAT_CHAR_BIT) - FLOATFORMAT_CHAR_BIT; + } + if (cur_bitshift > -FLOATFORMAT_CHAR_BIT) + { + *(data + cur_byte) &= + ~(((1 << ((start + len) % FLOATFORMAT_CHAR_BIT)) - 1) + << (-cur_bitshift)); + *(data + cur_byte) |= + (stuff_to_put & ((1 << FLOATFORMAT_CHAR_BIT) - 1)) << (-cur_bitshift); + } + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + + /* Move towards the most significant part of the field. */ + while (cur_bitshift < len) + { + if (len - cur_bitshift < FLOATFORMAT_CHAR_BIT) + { + /* This is the last byte. */ + *(data + cur_byte) &= + ~((1 << (len - cur_bitshift)) - 1); + *(data + cur_byte) |= (stuff_to_put >> cur_bitshift); + } + else + *(data + cur_byte) = ((stuff_to_put >> cur_bitshift) + & ((1 << FLOATFORMAT_CHAR_BIT) - 1)); + cur_bitshift += FLOATFORMAT_CHAR_BIT; + if (order == floatformat_little) + ++cur_byte; + else + --cur_byte; + } +} + +/* Check if VAL (which is assumed to be a floating point number whose + format is described by FMT) is negative. */ +static int +floatformat_is_negative (const struct floatformat *fmt, + const bfd_byte *uval) +{ + enum floatformat_byteorders order; + unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES]; + + gdb_assert (fmt != NULL); + gdb_assert (fmt->totalsize + <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT); + + /* An IBM long double (a two element array of double) always takes the + sign of the first double. */ + if (fmt->split_half) + fmt = fmt->split_half; + + order = floatformat_normalize_byteorder (fmt, uval, newfrom); + + if (order != fmt->byteorder) + uval = newfrom; + + return get_field (uval, order, fmt->totalsize, fmt->sign_start, 1); +} + +/* Check if VAL is "not a number" (NaN) for FMT. */ +static enum float_kind +floatformat_classify (const struct floatformat *fmt, + const bfd_byte *uval) +{ + long exponent; + unsigned long mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + enum floatformat_byteorders order; + unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES]; + int mant_zero; + + gdb_assert (fmt != NULL); + gdb_assert (fmt->totalsize + <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT); + + /* An IBM long double (a two element array of double) can be classified + by looking at the first double. inf and nan are specified as + ignoring the second double. zero and subnormal will always have + the second double 0.0 if the long double is correctly rounded. */ + if (fmt->split_half) + fmt = fmt->split_half; + + order = floatformat_normalize_byteorder (fmt, uval, newfrom); + + if (order != fmt->byteorder) + uval = newfrom; + + exponent = get_field (uval, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len); + + mant_bits_left = fmt->man_len; + mant_off = fmt->man_start; + + mant_zero = 1; + while (mant_bits_left > 0) + { + mant_bits = std::min (mant_bits_left, 32); + + mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits); + + /* If there is an explicit integer bit, mask it off. */ + if (mant_off == fmt->man_start + && fmt->intbit == floatformat_intbit_yes) + mant &= ~(1 << (mant_bits - 1)); + + if (mant) + { + mant_zero = 0; + break; + } + + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* If exp_nan is not set, assume that inf, NaN, and subnormals are not + supported. */ + if (! fmt->exp_nan) + { + if (mant_zero) + return float_zero; + else + return float_normal; + } + + if (exponent == 0) + { + if (mant_zero) + return float_zero; + else + return float_subnormal; + } + + if (exponent == fmt->exp_nan) + { + if (mant_zero) + return float_infinite; + else + return float_nan; + } + + return float_normal; +} + +/* Convert the mantissa of VAL (which is assumed to be a floating + point number whose format is described by FMT) into a hexadecimal + and store it in a static string. Return a pointer to that string. */ +static const char * +floatformat_mantissa (const struct floatformat *fmt, + const bfd_byte *val) +{ + unsigned char *uval = (unsigned char *) val; + unsigned long mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + static char res[50]; + char buf[9]; + int len; + enum floatformat_byteorders order; + unsigned char newfrom[FLOATFORMAT_LARGEST_BYTES]; + + gdb_assert (fmt != NULL); + gdb_assert (fmt->totalsize + <= FLOATFORMAT_LARGEST_BYTES * FLOATFORMAT_CHAR_BIT); + + /* For IBM long double (a two element array of double), return the + mantissa of the first double. The problem with returning the + actual mantissa from both doubles is that there can be an + arbitrary number of implied 0's or 1's between the mantissas + of the first and second double. In any case, this function + is only used for dumping out nans, and a nan is specified to + ignore the value in the second double. */ + if (fmt->split_half) + fmt = fmt->split_half; + + order = floatformat_normalize_byteorder (fmt, uval, newfrom); + + if (order != fmt->byteorder) + uval = newfrom; + + if (! fmt->exp_nan) + return 0; + + /* Make sure we have enough room to store the mantissa. */ + gdb_assert (sizeof res > ((fmt->man_len + 7) / 8) * 2); + + mant_off = fmt->man_start; + mant_bits_left = fmt->man_len; + mant_bits = (mant_bits_left % 32) > 0 ? mant_bits_left % 32 : 32; + + mant = get_field (uval, order, fmt->totalsize, mant_off, mant_bits); + + len = xsnprintf (res, sizeof res, "%lx", mant); + + mant_off += mant_bits; + mant_bits_left -= mant_bits; + + while (mant_bits_left > 0) + { + mant = get_field (uval, order, fmt->totalsize, mant_off, 32); + + xsnprintf (buf, sizeof buf, "%08lx", mant); + gdb_assert (len + strlen (buf) <= sizeof res); + strcat (res, buf); + + mant_off += 32; + mant_bits_left -= 32; + } + + return res; +} + +/* 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 + increase precision as necessary. Otherwise, we call the conversion + routine and let it do the dirty work. Note that even if the target + and host floating-point formats match, the length of the types + might still be different, so the conversion routines must make sure + to not overrun any buffers. For example, on x86, long double is + the 80-bit extended precision type on both 32-bit and 64-bit ABIs, + but by default it is stored as 12 bytes on 32-bit, and 16 bytes on + 64-bit, for alignment reasons. See comment in store_typed_floating + for a discussion about zeroing out remaining bytes in the target + buffer. */ + +static const struct floatformat *host_float_format = GDB_HOST_FLOAT_FORMAT; +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 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); + + if (fmt == host_float_format) + { + float val = 0; + + memcpy (&val, from, floatformat_totalsize_bytes (fmt)); + *to = val; + return; + } + else if (fmt == host_double_format) + { + double val = 0; + + memcpy (&val, from, floatformat_totalsize_bytes (fmt)); + *to = val; + return; + } + else if (fmt == host_long_double_format) + { + long double val = 0; + + memcpy (&val, from, floatformat_totalsize_bytes (fmt)); + *to = val; + return; + } + + unsigned char *ufrom = (unsigned char *) from; + long 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); + + /* For non-numbers, reuse libiberty's logic to find the correct + format. We do not lose any precision in this case by passing + through a double. */ + kind = floatformat_classify (fmt, (const bfd_byte *) from); + if (kind == float_infinite || kind == float_nan) + { + double dto; + + floatformat_to_double /* ARI: floatformat_to_double */ + (fmt->split_half ? fmt->split_half : fmt, from, &dto); + *to = (T) dto; + return; + } + + order = floatformat_normalize_byteorder (fmt, ufrom, newfrom); + + if (order != fmt->byteorder) + ufrom = newfrom; + + if (fmt->split_half) + { + T dtop, dbot; + + from_target (fmt->split_half, ufrom, &dtop); + /* Preserve the sign of 0, which is the sign of the top + half. */ + if (dtop == 0.0) + { + *to = dtop; + return; + } + from_target (fmt->split_half, + ufrom + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2, &dbot); + *to = dtop + dbot; + return; + } + + exponent = get_field (ufrom, 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; + T dto = 0.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) + dto = ldexp (1.0, exponent); + else + exponent++; + } + + while (mant_bits_left > 0) + { + mant_bits = std::min (mant_bits_left, 32); + + mant = get_field (ufrom, order, fmt->totalsize, mant_off, mant_bits); + + dto += ldexp ((T) mant, exponent - mant_bits); + exponent -= mant_bits; + mant_off += mant_bits; + mant_bits_left -= mant_bits; + } + + /* Negate it if negative. */ + if (get_field (ufrom, order, fmt->totalsize, fmt->sign_start, 1)) + dto = -dto; + *to = dto; +} + +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); + + if (fmt == host_float_format) + { + float val = *from; + + memcpy (to, &val, floatformat_totalsize_bytes (fmt)); + return; + } + else if (fmt == host_double_format) + { + double val = *from; + + memcpy (to, &val, floatformat_totalsize_bytes (fmt)); + return; + } + else if (fmt == host_long_double_format) + { + long double val = *from; + + memcpy (to, &val, floatformat_totalsize_bytes (fmt)); + return; + } + + T dfrom; + int exponent; + T mant; + unsigned int mant_bits, mant_off; + int mant_bits_left; + unsigned char *uto = (unsigned char *) to; + enum floatformat_byteorders order = fmt->byteorder; + unsigned char newto[FLOATFORMAT_LARGEST_BYTES]; + + if (order != floatformat_little) + order = floatformat_big; + + if (order != fmt->byteorder) + uto = newto; + + memcpy (&dfrom, from, sizeof (dfrom)); + memset (uto, 0, floatformat_totalsize_bytes (fmt)); + + if (fmt->split_half) + { + /* Use static volatile to ensure that any excess precision is + removed via storing in memory, and so the top half really is + the result of converting to double. */ + static volatile double dtop, dbot; + T dtopnv, dbotnv; + + dtop = (double) dfrom; + /* If the rounded top half is Inf, the bottom must be 0 not NaN + or Inf. */ + if (dtop + dtop == dtop && dtop != 0.0) + dbot = 0.0; + else + dbot = (double) (dfrom - (T) dtop); + dtopnv = dtop; + dbotnv = dbot; + to_target (fmt->split_half, &dtopnv, uto); + to_target (fmt->split_half, &dbotnv, + uto + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2); + return; + } + + if (dfrom == 0) + goto finalize_byteorder; /* Result is zero */ + if (dfrom != dfrom) /* Result is NaN */ + { + /* From is NaN */ + put_field (uto, 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 (uto, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 1); + goto finalize_byteorder; + } + + /* If negative, set the sign bit. */ + if (dfrom < 0) + { + put_field (uto, order, fmt->totalsize, fmt->sign_start, 1, 1); + dfrom = -dfrom; + } + + if (dfrom + dfrom == dfrom && dfrom != 0.0) /* Result is Infinity. */ + { + /* Infinity exponent is same as NaN's. */ + put_field (uto, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + /* Infinity mantissa is all zeroes. */ + put_field (uto, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + mant = frexp (dfrom, &exponent); + + 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 (uto, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, 0); + put_field (uto, 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 (uto, order, fmt->totalsize, fmt->exp_start, + fmt->exp_len, fmt->exp_nan); + put_field (uto, order, fmt->totalsize, fmt->man_start, + fmt->man_len, 0); + goto finalize_byteorder; + } + + put_field (uto, 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; + + mant *= 4294967296.0; + mant_long = ((unsigned long) mant) & 0xffffffffL; + mant -= mant_long; + + /* 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 (uto, 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, to); +} + +template void +host_float_ops::to_target (const struct type *type, + const T *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. */ +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. */ + 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); + + T host_float; + from_target (type, addr, &host_float); + + DIAGNOSTIC_PUSH + DIAGNOSTIC_IGNORE_FORMAT_NONLITERAL + return string_printf (host_format.c_str (), host_float); + DIAGNOSTIC_POP +} + +/* Parse string IN into a target floating-number of type TYPE and + store it as byte-stream ADDR. Return whether parsing succeeded. */ +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 +{ + 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; + + 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. + That is why we simply test num == 0. */ + if (num == 0) + return false; + + /* We only accept the whole string. */ + if (in[n]) + return false; + + to_target (type, &host_float, addr); + return true; +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to an integer value (rounding towards zero). */ +template LONGEST +host_float_ops::to_longest (const gdb_byte *addr, + const struct type *type) const +{ + 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 type TYPE + and store it as byte-stream ADDR. */ +template void +host_float_ops::from_longest (gdb_byte *addr, const struct type *type, + LONGEST val) const +{ + T host_float = (T) val; + to_target (type, &host_float, addr); +} + +/* Convert unsigned integer VAL to a target floating-number of type TYPE + and store it as byte-stream ADDR. */ +template void +host_float_ops::from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST val) const +{ + T host_float = (T) val; + to_target (type, &host_float, addr); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a floating-point value in the host "double" format. */ +template double +host_float_ops::to_host_double (const gdb_byte *addr, + const struct type *type) const +{ + 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 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 +{ + T host_float = (T) val; + to_target (type, &host_float, 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. */ +template void +host_float_ops::convert (const gdb_byte *from, + const struct type *from_type, + gdb_byte *to, + const struct type *to_type) const +{ + 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 + 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 +{ + T v1, v2, v = 0; + + from_target (type_x, x, &v1); + from_target (type_y, y, &v2); + + switch (op) + { + case BINOP_ADD: + v = v1 + v2; + break; + + case BINOP_SUB: + v = v1 - v2; + break; + + case BINOP_MUL: + v = v1 * v2; + break; + + case BINOP_DIV: + v = v1 / v2; + break; + + case BINOP_EXP: + errno = 0; + v = pow (v1, v2); + if (errno) + error (_("Cannot perform exponentiation: %s"), + safe_strerror (errno)); + break; + + case BINOP_MIN: + v = v1 < v2 ? v1 : v2; + break; + + case BINOP_MAX: + v = v1 > v2 ? v1 : v2; + break; + + default: + error (_("Integer-only operation on floating point number.")); + break; + } + + to_target (type_res, &v, 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. */ +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 +{ + T v1, v2; + + from_target (type_x, x, &v1); + from_target (type_y, y, &v2); + + if (v1 == v2) + return 0; + if (v1 < v2) + return -1; + return 1; +} + + +/* Implementation of target_float_ops using the MPFR library + mpfr_t as intermediate type. */ + +#ifdef HAVE_LIBMPFR + +#define MPFR_USE_INTMAX_T + +#include + +class mpfr_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: + /* Local wrapper class to handle mpfr_t initialization 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)); + } + + gdb_mpfr (const gdb_mpfr &source) + { + mpfr_init2 (val, mpfr_get_prec (source.val)); + } + + ~gdb_mpfr () + { + mpfr_clear (val); + } + }; + + 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; + + 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 +{ + 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) + { + mpfr_set_inf (to.val, floatformat_is_negative (fmt, from) ? -1 : 1); + return; + } + if (kind == float_nan) + { + mpfr_set_nan (to.val); + return; + } + + order = floatformat_normalize_byteorder (fmt, from, newfrom); + + if (order != fmt->byteorder) + from = newfrom; + + 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_CODE_DECFLOAT); + + int len = TYPE_LENGTH (type); + int i; -/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, - to an integer value (rounding towards zero). */ -static LONGEST -floatformat_to_longest (const struct floatformat *fmt, const gdb_byte *addr) +#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) { - DOUBLEST d; - floatformat_to_doublest (fmt, addr, &d); - return (LONGEST) d; + gdb_assert (type->code () == 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; } -/* Convert signed integer VAL to a target floating-number of format FMT - and store it as byte-stream ADDR. */ +/* Check for errors signaled in the decimal context structure. */ static void -floatformat_from_longest (const struct floatformat *fmt, gdb_byte *addr, - LONGEST val) +decimal_check_errors (decContext *ctx) { - DOUBLEST d = (DOUBLEST) val; - floatformat_from_doublest (fmt, &d, addr); + /* 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)); + } } -/* Convert unsigned integer VAL to a target floating-number of format FMT - and store it as byte-stream ADDR. */ +/* Helper function to convert from libdecnumber's appropriate representation + for computation to each size of decimal float. */ static void -floatformat_from_ulongest (const struct floatformat *fmt, gdb_byte *addr, - ULONGEST val) +decimal_from_number (const decNumber *from, + gdb_byte *to, const struct type *type) { - DOUBLEST d = (DOUBLEST) val; - floatformat_from_doublest (fmt, &d, addr); + gdb_byte dec[16]; + + decContext set; + + set_decnumber_context (&set, type); + + switch (TYPE_LENGTH (type)) + { + case 4: + decimal32FromNumber ((decimal32 *) dec, from, &set); + break; + case 8: + decimal64FromNumber ((decimal64 *) dec, from, &set); + break; + case 16: + decimal128FromNumber ((decimal128 *) dec, from, &set); + break; + default: + error (_("Unknown decimal floating point type.")); + break; + } + + match_endianness (dec, type, to); } -/* 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 - store it to the target byte-stream TO. */ +/* Helper function to convert each size of decimal float to libdecnumber's + appropriate representation for computation. */ static void -floatformat_convert (const gdb_byte *from, const struct floatformat *from_fmt, - gdb_byte *to, const struct floatformat *to_fmt) +decimal_to_number (const gdb_byte *addr, const struct type *type, + decNumber *to) { - if (from_fmt == to_fmt) + gdb_byte dec[16]; + match_endianness (addr, type, dec); + + switch (TYPE_LENGTH (type)) { - /* The floating-point formats match, so we simply copy the data. */ - memcpy (to, from, floatformat_totalsize_bytes (to_fmt)); + case 4: + decimal32ToNumber ((decimal32 *) dec, to); + break; + case 8: + decimal64ToNumber ((decimal64 *) dec, to); + break; + case 16: + decimal128ToNumber ((decimal128 *) dec, to); + break; + default: + error (_("Unknown decimal floating point type.")); + break; } - else +} + +/* 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. */ +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 (addr, type, dec); + + if (format != nullptr) { - /* 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; + /* We don't handle format strings (yet). If the host printf supports + decimal floating point types, just use this. Otherwise, fall back + to printing the number while ignoring the format string. */ +#if defined (PRINTF_HAS_DECFLOAT) + /* FIXME: This makes unwarranted assumptions about the host ABI! */ + return string_printf (format, dec); +#endif + } + + std::string result; + result.resize (MAX_DECIMAL_STRING); - floatformat_to_doublest (from_fmt, from, &d); - floatformat_from_doublest (to_fmt, &d, to); + switch (TYPE_LENGTH (type)) + { + case 4: + decimal32ToString ((decimal32 *) dec, &result[0]); + break; + case 8: + decimal64ToString ((decimal64 *) dec, &result[0]); + break; + case 16: + decimal128ToString ((decimal128 *) dec, &result[0]); + break; + default: + error (_("Unknown decimal floating point type.")); + break; } + + return result; } -/* 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) +/* 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. */ +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, type); + + switch (TYPE_LENGTH (type)) + { + case 4: + decimal32FromString ((decimal32 *) dec, string.c_str (), &set); + break; + case 8: + decimal64FromString ((decimal64 *) dec, string.c_str (), &set); + break; + case 16: + decimal128FromString ((decimal128 *) dec, string.c_str (), &set); + break; + default: + error (_("Unknown decimal floating point type.")); + break; + } + + match_endianness (dec, type, addr); + + /* Check for errors in the DFP operation. */ + decimal_check_errors (&set); + + return true; +} + +/* Converts a LONGEST to a decimal float of specified LEN bytes. */ +void +decimal_float_ops::from_longest (gdb_byte *addr, const struct type *type, + LONGEST from) const +{ + decNumber number; + + if ((int32_t) from != from) + /* libdecnumber can convert only 32-bit integers. */ + error (_("Conversion of large integer to a " + "decimal floating type is not supported.")); + + decNumberFromInt32 (&number, (int32_t) from); + + decimal_from_number (&number, addr, type); +} + +/* Converts a ULONGEST to a decimal float of specified LEN bytes. */ +void +decimal_float_ops::from_ulongest (gdb_byte *addr, const struct type *type, + ULONGEST from) const +{ + decNumber number; + + if ((uint32_t) from != from) + /* libdecnumber can convert only 32-bit integers. */ + error (_("Conversion of large integer to a " + "decimal floating type is not supported.")); + + decNumberFromUInt32 (&number, (uint32_t) from); + + decimal_from_number (&number, addr, type); +} + +/* Converts a decimal float of LEN bytes to a LONGEST. */ +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 = 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. */ +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 { - DOUBLEST v1, v2, v = 0; + decContext set; + decNumber number1, number2, number3; + + decimal_to_number (x, type_x, &number1); + decimal_to_number (y, type_y, &number2); - floatformat_to_doublest (fmt_x, x, &v1); - floatformat_to_doublest (fmt_y, y, &v2); + set_decnumber_context (&set, type_res); switch (op) { case BINOP_ADD: - v = v1 + v2; + decNumberAdd (&number3, &number1, &number2, &set); break; - case BINOP_SUB: - v = v1 - v2; + decNumberSubtract (&number3, &number1, &number2, &set); break; - case BINOP_MUL: - v = v1 * v2; + decNumberMultiply (&number3, &number1, &number2, &set); break; - case BINOP_DIV: - v = v1 / v2; + decNumberDivide (&number3, &number1, &number2, &set); break; - case BINOP_EXP: - errno = 0; - v = pow (v1, v2); - if (errno) - error (_("Cannot perform exponentiation: %s"), - safe_strerror (errno)); - break; - - case BINOP_MIN: - v = v1 < v2 ? v1 : v2; - break; - - case BINOP_MAX: - v = v1 > v2 ? v1 : v2; + decNumberPower (&number3, &number1, &number2, &set); break; - - default: - error (_("Integer-only operation on floating point number.")); + default: + error (_("Operation not valid for decimal floating point number.")); break; } - floatformat_from_doublest (fmt_result, &v, result); + /* Check for errors in the DFP operation. */ + decimal_check_errors (&set); + + decimal_from_number (&number3, res, type_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 - 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) +/* 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. */ +int +decimal_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; + decNumber number1, number2, result; + decContext set; + const struct type *type_result; - floatformat_to_doublest (fmt_x, x, &v1); - floatformat_to_doublest (fmt_y, y, &v2); + decimal_to_number (x, type_x, &number1); + decimal_to_number (y, type_y, &number2); - if (v1 == v2) + /* Perform the comparison in the larger of the two sizes. */ + 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); + + /* Check for errors in the DFP operation. */ + decimal_check_errors (&set); + + if (decNumberIsNaN (&result)) + error (_("Comparison with an invalid number (NaN).")); + else if (decNumberIsZero (&result)) return 0; - if (v1 < v2) + else if (decNumberIsNegative (&result)) return -1; - return 1; + else + return 1; +} + +/* Convert a decimal value from a decimal type with LEN_FROM bytes to a + decimal type with LEN_TO bytes. */ +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, from_type, &number); + decimal_from_number (&number, to, to_type); } @@ -166,15 +2136,189 @@ floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, "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 type1->code () == type2->code (); +} + +/* 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 (type1->code ()) + { + 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 ()) + { + 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 ()) + { + 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 perform 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 (type1->code () == type2->code ()); + + 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 target_float_is_valid (const gdb_byte *addr, const struct type *type) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) + if (type->code () == TYPE_CODE_FLT) return floatformat_is_valid (floatformat_from_type (type), addr); - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) + if (type->code () == TYPE_CODE_DECFLOAT) return true; gdb_assert_not_reached ("unexpected type code"); @@ -185,13 +2329,12 @@ target_float_is_valid (const gdb_byte *addr, const struct type *type) bool target_float_is_zero (const gdb_byte *addr, const struct type *type) { - if (TYPE_CODE (type) == TYPE_CODE_FLT) + if (type->code () == TYPE_CODE_FLT) return (floatformat_classify (floatformat_from_type (type), addr) == float_zero); - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_is_zero (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); + if (type->code () == TYPE_CODE_DECFLOAT) + return decimal_is_zero (addr, type); gdb_assert_not_reached ("unexpected type code"); } @@ -202,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); - - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - return decimal_to_string (addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type)), - 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_CODE_FLT) + { + const struct floatformat *fmt = floatformat_from_type (type); + + /* 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); + } + } - gdb_assert_not_reached ("unexpected type code"); + 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 @@ -219,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, @@ -239,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 @@ -255,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 @@ -280,23 +2409,28 @@ 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; - } + const target_float_ops *ops = get_target_float_ops (type); + ops->from_ulongest (addr, type, val); +} - if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) - { - decimal_from_ulongest (val, addr, TYPE_LENGTH (type), - gdbarch_byte_order (get_type_arch (type))); - return; - } +/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, + to a floating-point value in the host "double" format. */ +double +target_float_to_host_double (const gdb_byte *addr, + const struct type *type) +{ + const target_float_ops *ops = get_target_float_ops (type); + return ops->to_host_double (addr, type); +} - gdb_assert_not_reached ("unexpected type code"); +/* 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 +target_float_from_host_double (gdb_byte *addr, const struct type *type, + double val) +{ + 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 @@ -306,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 @@ -359,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 @@ -399,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); }