| /* Floating point routines for GDB, the GNU debugger. | |
| Copyright (C) 2017-2018 Free Software Foundation, Inc. | |
| This file is part of GDB. | |
| This program is free software; you can redistribute it and/or modify | |
| it under the terms of the GNU General Public License as published by | |
| the Free Software Foundation; either version 3 of the License, or | |
| (at your option) any later version. | |
| This program is distributed in the hope that it will be useful, | |
| but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| GNU General Public License for more details. | |
| You should have received a copy of the GNU General Public License | |
| along with this program. If not, see <http://www.gnu.org/licenses/>. */ | |
| #include "defs.h" | |
| #include "gdbtypes.h" | |
| #include "floatformat.h" | |
| #include "target-float.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 <cmath> | |
| #include <limits> | |
| /* 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<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 | |
| 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<typename T> void | |
| host_float_ops<T>::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; | |
| T dto; | |
| 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 (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; | |
| 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<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); | |
| 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<typename T> void | |
| host_float_ops<T>::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<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. */ | |
| 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); | |
| T host_float; | |
| from_target (type, addr, &host_float); | |
| return string_printf (host_format.c_str (), host_float); | |
| } | |
| /* Parse string IN into a target floating-number of type TYPE and | |
| store it as byte-stream ADDR. Return whether parsing succeeded. */ | |
| 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 | |
| { | |
| T host_float; | |
| int n, num; | |
| 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. | |
| 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<typename T> LONGEST | |
| host_float_ops<T>::to_longest (const gdb_byte *addr, | |
| const struct type *type) const | |
| { | |
| 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 type TYPE | |
| and store it as byte-stream ADDR. */ | |
| template<typename T> void | |
| host_float_ops<T>::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<typename T> void | |
| host_float_ops<T>::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<typename T> double | |
| host_float_ops<T>::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<typename T> void | |
| host_float_ops<T>::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<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 | |
| { | |
| 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<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 | |
| { | |
| 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<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 | |
| { | |
| 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 <mpfr.h> | |
| 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 initalization and cleanup. */ | |
| class gdb_mpfr | |
| { | |
| public: | |
| mpfr_t val; | |
| gdb_mpfr (const struct type *type) | |
| { | |
| const struct floatformat *fmt = floatformat_from_type (type); | |
| mpfr_init2 (val, floatformat_precision (fmt)); | |
| } | |
| 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 "<invalid float value>"; | |
| /* Handle NaN and Inf. */ | |
| enum float_kind kind = floatformat_classify (fmt, addr); | |
| if (kind == float_nan) | |
| { | |
| const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; | |
| const char *mantissa = floatformat_mantissa (fmt, addr); | |
| return string_printf ("%snan(0x%s)", sign, mantissa); | |
| } | |
| else if (kind == float_infinite) | |
| { | |
| const char *sign = floatformat_is_negative (fmt, addr)? "-" : ""; | |
| return string_printf ("%sinf", sign); | |
| } | |
| } | |
| /* Determine the format string to use on the host side. */ | |
| std::string host_format = floatformat_printf_format (fmt, format, 'R'); | |
| gdb_mpfr tmp (type); | |
| from_target (type, addr, tmp); | |
| int size = mpfr_snprintf (NULL, 0, host_format.c_str (), tmp.val); | |
| std::string str (size, '\0'); | |
| mpfr_sprintf (&str[0], host_format.c_str (), tmp.val); | |
| return str; | |
| } | |
| /* Parse string STRING into a target floating-number of type TYPE and | |
| store it as byte-stream ADDR. Return whether parsing succeeded. */ | |
| bool | |
| mpfr_float_ops::from_string (gdb_byte *addr, | |
| const struct type *type, | |
| const std::string &in) const | |
| { | |
| gdb_mpfr tmp (type); | |
| char *endptr; | |
| mpfr_strtofr (tmp.val, in.c_str (), &endptr, 0, MPFR_RNDN); | |
| /* We only accept the whole string. */ | |
| if (*endptr) | |
| return false; | |
| to_target (type, tmp, addr); | |
| return true; | |
| } | |
| /* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, | |
| to an integer value (rounding towards zero). */ | |
| LONGEST | |
| mpfr_float_ops::to_longest (const gdb_byte *addr, | |
| const struct type *type) const | |
| { | |
| gdb_mpfr tmp (type); | |
| from_target (type, addr, tmp); | |
| return mpfr_get_sj (tmp.val, MPFR_RNDZ); | |
| } | |
| /* Convert signed integer VAL to a target floating-number of type TYPE | |
| and store it as byte-stream ADDR. */ | |
| void | |
| mpfr_float_ops::from_longest (gdb_byte *addr, | |
| const struct type *type, | |
| LONGEST val) const | |
| { | |
| gdb_mpfr tmp (type); | |
| mpfr_set_sj (tmp.val, val, MPFR_RNDN); | |
| to_target (type, tmp, addr); | |
| } | |
| /* Convert unsigned integer VAL to a target floating-number of type TYPE | |
| and store it as byte-stream ADDR. */ | |
| void | |
| mpfr_float_ops::from_ulongest (gdb_byte *addr, | |
| const struct type *type, | |
| ULONGEST val) const | |
| { | |
| gdb_mpfr tmp (type); | |
| mpfr_set_uj (tmp.val, val, MPFR_RNDN); | |
| to_target (type, tmp, addr); | |
| } | |
| /* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, | |
| to a floating-point value in the host "double" format. */ | |
| double | |
| mpfr_float_ops::to_host_double (const gdb_byte *addr, | |
| const struct type *type) const | |
| { | |
| gdb_mpfr tmp (type); | |
| from_target (type, addr, tmp); | |
| return mpfr_get_d (tmp.val, MPFR_RNDN); | |
| } | |
| /* Convert floating-point value VAL in the host "double" format to a target | |
| floating-number of type TYPE and store it as byte-stream ADDR. */ | |
| void | |
| mpfr_float_ops::from_host_double (gdb_byte *addr, | |
| const struct type *type, | |
| double val) const | |
| { | |
| gdb_mpfr tmp (type); | |
| mpfr_set_d (tmp.val, val, MPFR_RNDN); | |
| to_target (type, tmp, addr); | |
| } | |
| /* Convert a floating-point number of type FROM_TYPE from the target | |
| byte-stream FROM to a floating-point number of type TO_TYPE, and | |
| store it to the target byte-stream TO. */ | |
| void | |
| mpfr_float_ops::convert (const gdb_byte *from, | |
| const struct type *from_type, | |
| gdb_byte *to, | |
| const struct type *to_type) const | |
| { | |
| gdb_mpfr from_tmp (from_type), to_tmp (to_type); | |
| from_target (from_type, from, from_tmp); | |
| mpfr_set (to_tmp.val, from_tmp.val, MPFR_RNDN); | |
| to_target (to_type, to_tmp, to); | |
| } | |
| /* Perform the binary operation indicated by OPCODE, using as operands the | |
| target byte streams X and Y, interpreted as floating-point numbers of | |
| types TYPE_X and TYPE_Y, respectively. Convert the result to type | |
| TYPE_RES and store it into the byte-stream RES. */ | |
| void | |
| mpfr_float_ops::binop (enum exp_opcode op, | |
| const gdb_byte *x, const struct type *type_x, | |
| const gdb_byte *y, const struct type *type_y, | |
| gdb_byte *res, const struct type *type_res) const | |
| { | |
| gdb_mpfr x_tmp (type_x), y_tmp (type_y), tmp (type_res); | |
| from_target (type_x, x, x_tmp); | |
| from_target (type_y, y, y_tmp); | |
| switch (op) | |
| { | |
| case BINOP_ADD: | |
| mpfr_add (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_SUB: | |
| mpfr_sub (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_MUL: | |
| mpfr_mul (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_DIV: | |
| mpfr_div (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_EXP: | |
| mpfr_pow (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_MIN: | |
| mpfr_min (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| case BINOP_MAX: | |
| mpfr_max (tmp.val, x_tmp.val, y_tmp.val, MPFR_RNDN); | |
| break; | |
| default: | |
| error (_("Integer-only operation on floating point number.")); | |
| break; | |
| } | |
| to_target (type_res, tmp, res); | |
| } | |
| /* Compare the two target byte streams X and Y, interpreted as floating-point | |
| numbers of types TYPE_X and TYPE_Y, respectively. Return zero if X and Y | |
| are equal, -1 if X is less than Y, and 1 otherwise. */ | |
| int | |
| mpfr_float_ops::compare (const gdb_byte *x, const struct type *type_x, | |
| const gdb_byte *y, const struct type *type_y) const | |
| { | |
| gdb_mpfr x_tmp (type_x), y_tmp (type_y); | |
| from_target (type_x, x, x_tmp); | |
| from_target (type_y, y, y_tmp); | |
| if (mpfr_equal_p (x_tmp.val, y_tmp.val)) | |
| return 0; | |
| else if (mpfr_less_p (x_tmp.val, y_tmp.val)) | |
| return -1; | |
| else | |
| return 1; | |
| } | |
| #endif | |
| /* Helper routines operating on decimal floating-point data. */ | |
| /* Decimal floating point is one of the extension to IEEE 754, which is | |
| described in http://grouper.ieee.org/groups/754/revision.html and | |
| http://www2.hursley.ibm.com/decimal/. It completes binary floating | |
| point by representing floating point more exactly. */ | |
| /* The order of the following headers is important for making sure | |
| decNumber structure is large enough to hold decimal128 digits. */ | |
| #include "dpd/decimal128.h" | |
| #include "dpd/decimal64.h" | |
| #include "dpd/decimal32.h" | |
| /* When using decimal128, this is the maximum string length + 1 | |
| (value comes from libdecnumber's DECIMAL128_String constant). */ | |
| #define MAX_DECIMAL_STRING 43 | |
| /* In GDB, we are using an array of gdb_byte to represent decimal values. | |
| They are stored in host byte order. This routine does the conversion if | |
| the target byte order is different. */ | |
| static void | |
| match_endianness (const gdb_byte *from, const struct type *type, gdb_byte *to) | |
| { | |
| gdb_assert (TYPE_CODE (type) == TYPE_CODE_DECFLOAT); | |
| int len = TYPE_LENGTH (type); | |
| int i; | |
| #if WORDS_BIGENDIAN | |
| #define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE | |
| #else | |
| #define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG | |
| #endif | |
| if (gdbarch_byte_order (get_type_arch (type)) == OPPOSITE_BYTE_ORDER) | |
| for (i = 0; i < len; i++) | |
| to[i] = from[len - i - 1]; | |
| else | |
| for (i = 0; i < len; i++) | |
| to[i] = from[i]; | |
| return; | |
| } | |
| /* Helper function to get the appropriate libdecnumber context for each size | |
| of decimal float. */ | |
| static void | |
| set_decnumber_context (decContext *ctx, const struct type *type) | |
| { | |
| gdb_assert (TYPE_CODE (type) == TYPE_CODE_DECFLOAT); | |
| switch (TYPE_LENGTH (type)) | |
| { | |
| case 4: | |
| decContextDefault (ctx, DEC_INIT_DECIMAL32); | |
| break; | |
| case 8: | |
| decContextDefault (ctx, DEC_INIT_DECIMAL64); | |
| break; | |
| case 16: | |
| decContextDefault (ctx, DEC_INIT_DECIMAL128); | |
| break; | |
| } | |
| ctx->traps = 0; | |
| } | |
| /* Check for errors signaled in the decimal context structure. */ | |
| static void | |
| decimal_check_errors (decContext *ctx) | |
| { | |
| /* An error here could be a division by zero, an overflow, an underflow or | |
| an invalid operation (from the DEC_Errors constant in decContext.h). | |
| Since GDB doesn't complain about division by zero, overflow or underflow | |
| errors for binary floating, we won't complain about them for decimal | |
| floating either. */ | |
| if (ctx->status & DEC_IEEE_854_Invalid_operation) | |
| { | |
| /* Leave only the error bits in the status flags. */ | |
| ctx->status &= DEC_IEEE_854_Invalid_operation; | |
| error (_("Cannot perform operation: %s"), | |
| decContextStatusToString (ctx)); | |
| } | |
| } | |
| /* Helper function to convert from libdecnumber's appropriate representation | |
| for computation to each size of decimal float. */ | |
| static void | |
| decimal_from_number (const decNumber *from, | |
| gdb_byte *to, const struct type *type) | |
| { | |
| gdb_byte dec[16]; | |
| decContext set; | |
| set_decnumber_context (&set, type); | |
| switch (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); | |
| } | |
| /* Helper function to convert each size of decimal float to libdecnumber's | |
| appropriate representation for computation. */ | |
| static void | |
| decimal_to_number (const gdb_byte *addr, const struct type *type, | |
| decNumber *to) | |
| { | |
| gdb_byte dec[16]; | |
| match_endianness (addr, type, dec); | |
| switch (TYPE_LENGTH (type)) | |
| { | |
| 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; | |
| } | |
| } | |
| /* 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) | |
| { | |
| /* 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); | |
| 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; | |
| } | |
| /* 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 | |
| { | |
| decContext set; | |
| decNumber number1, number2, number3; | |
| decimal_to_number (x, type_x, &number1); | |
| decimal_to_number (y, type_y, &number2); | |
| set_decnumber_context (&set, type_res); | |
| switch (op) | |
| { | |
| case BINOP_ADD: | |
| decNumberAdd (&number3, &number1, &number2, &set); | |
| break; | |
| case BINOP_SUB: | |
| decNumberSubtract (&number3, &number1, &number2, &set); | |
| break; | |
| case BINOP_MUL: | |
| decNumberMultiply (&number3, &number1, &number2, &set); | |
| break; | |
| case BINOP_DIV: | |
| decNumberDivide (&number3, &number1, &number2, &set); | |
| break; | |
| case BINOP_EXP: | |
| decNumberPower (&number3, &number1, &number2, &set); | |
| break; | |
| default: | |
| error (_("Operation not valid for decimal floating point number.")); | |
| break; | |
| } | |
| /* Check for errors in the DFP operation. */ | |
| decimal_check_errors (&set); | |
| 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. */ | |
| 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; | |
| const struct type *type_result; | |
| decimal_to_number (x, type_x, &number1); | |
| decimal_to_number (y, type_y, &number2); | |
| /* 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; | |
| else if (decNumberIsNegative (&result)) | |
| 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); | |
| } | |
| /* Typed floating-point routines. These routines operate on floating-point | |
| values in target format, represented by a byte buffer interpreted as a | |
| "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 mpfr_t as intermediate format to provide precise target-floating | |
| point emulation. However, if the MPFR library is not availabe, | |
| 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<long double> binary_float_ops; | |
| #endif | |
| return &binary_float_ops; | |
| } | |
| /* For decimal floating-point types, always use the libdecnumber | |
| decNumber type as intermediate format. */ | |
| case target_float_ops_kind::decimal: | |
| { | |
| static decimal_float_ops decimal_float_ops; | |
| return &decimal_float_ops; | |
| } | |
| default: | |
| gdb_assert_not_reached ("unexpected target_float_ops_kind"); | |
| } | |
| } | |
| /* Given a target type TYPE, determine the best host-side intermediate format | |
| to perform operations on TYPE in. */ | |
| static const target_float_ops * | |
| get_target_float_ops (const struct type *type) | |
| { | |
| enum target_float_ops_kind kind = get_target_float_ops_kind (type); | |
| return get_target_float_ops (kind); | |
| } | |
| /* The same for operations involving two target types TYPE1 and TYPE2. */ | |
| static const target_float_ops * | |
| get_target_float_ops (const struct type *type1, const struct type *type2) | |
| { | |
| gdb_assert (TYPE_CODE (type1) == TYPE_CODE (type2)); | |
| enum target_float_ops_kind kind1 = get_target_float_ops_kind (type1); | |
| enum target_float_ops_kind kind2 = get_target_float_ops_kind (type2); | |
| /* Given the way the kinds are sorted, we simply choose the larger one; | |
| this will be able to hold values of either type. */ | |
| return get_target_float_ops (std::max (kind1, kind2)); | |
| } | |
| /* Return whether the byte-stream ADDR holds a valid value of | |
| floating-point type TYPE. */ | |
| bool | |
| target_float_is_valid (const gdb_byte *addr, const struct type *type) | |
| { | |
| if (TYPE_CODE (type) == TYPE_CODE_FLT) | |
| return floatformat_is_valid (floatformat_from_type (type), addr); | |
| if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT) | |
| return true; | |
| gdb_assert_not_reached ("unexpected type code"); | |
| } | |
| /* Return whether the byte-stream ADDR, interpreted as floating-point | |
| type TYPE, is numerically equal to zero (of either sign). */ | |
| bool | |
| target_float_is_zero (const gdb_byte *addr, const struct type *type) | |
| { | |
| if (TYPE_CODE (type) == 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); | |
| gdb_assert_not_reached ("unexpected type code"); | |
| } | |
| /* Convert the byte-stream ADDR, interpreted as floating-point type TYPE, | |
| to a string, optionally using the print format FORMAT. */ | |
| std::string | |
| target_float_to_string (const gdb_byte *addr, const struct type *type, | |
| const char *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); | |
| /* Detect invalid representations. */ | |
| if (!floatformat_is_valid (fmt, addr)) | |
| return "<invalid float value>"; | |
| /* 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 | |
| store it as byte-stream ADDR. Return whether parsing succeeded. */ | |
| bool | |
| target_float_from_string (gdb_byte *addr, const struct type *type, | |
| const std::string &string) | |
| { | |
| 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, | |
| to an integer value (rounding towards zero). */ | |
| LONGEST | |
| target_float_to_longest (const gdb_byte *addr, const struct type *type) | |
| { | |
| 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 | |
| and store it as byte-stream ADDR. */ | |
| void | |
| target_float_from_longest (gdb_byte *addr, const struct type *type, | |
| LONGEST val) | |
| { | |
| 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 | |
| and store it as byte-stream ADDR. */ | |
| void | |
| target_float_from_ulongest (gdb_byte *addr, const struct type *type, | |
| ULONGEST val) | |
| { | |
| 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, | |
| 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); | |
| } | |
| /* 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 | |
| byte-stream FROM to a floating-point number of type TO_TYPE, and | |
| store it to the target byte-stream TO. */ | |
| void | |
| target_float_convert (const gdb_byte *from, const struct type *from_type, | |
| gdb_byte *to, const struct type *to_type) | |
| { | |
| /* We cannot directly convert between binary and decimal floating-point | |
| types, so go via an intermediary string. */ | |
| 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; | |
| } | |
| /* 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 | |
| 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. | |
| The three types must either be all binary floating-point types, or else | |
| all decimal floating-point types. Binary and decimal floating-point | |
| types cannot be mixed within a single operation. */ | |
| void | |
| target_float_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) | |
| { | |
| gdb_assert (target_float_same_category_p (type_x, type_res)); | |
| gdb_assert (target_float_same_category_p (type_y, type_res)); | |
| 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 | |
| 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. | |
| The two types must either both be binary floating-point types, or else | |
| both be decimal floating-point types. Binary and decimal floating-point | |
| types cannot compared directly against each other. */ | |
| int | |
| target_float_compare (const gdb_byte *x, const struct type *type_x, | |
| const gdb_byte *y, const struct type *type_y) | |
| { | |
| gdb_assert (target_float_same_category_p (type_x, type_y)); | |
| const target_float_ops *ops = get_target_float_ops (type_x, type_y); | |
| return ops->compare (x, type_x, y, type_y); | |
| } | |