diff options
author | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2017-11-06 16:04:03 +0100 |
---|---|---|
committer | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2017-11-06 16:04:03 +0100 |
commit | 1cfb73dbb7503d1cfb088c14d1125a5030a1f386 (patch) | |
tree | a3446d08987ce758763ba739f3b8cb09d0f260db /gdb | |
parent | b07e9c466ed24af614090ac42d6730a291608f69 (diff) | |
download | fsf-binutils-gdb-1cfb73dbb7503d1cfb088c14d1125a5030a1f386.zip fsf-binutils-gdb-1cfb73dbb7503d1cfb088c14d1125a5030a1f386.tar.gz fsf-binutils-gdb-1cfb73dbb7503d1cfb088c14d1125a5030a1f386.tar.bz2 |
Target FP: Merge doublest.c and dfp.c into target-float.c
Now that all target FP operations are performed via target-float.c,
this file remains the sole caller of functions in doublest.c and dfp.c.
Therefore, this patch merges the latter files into the former and
makes all their function static there.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.in (SFILES): Remove doublest.c and dfp.c.
(HFILES_NO_SRCDIR): Remove doublest.h and dfp.h.
(COMMON_OBS): Remove doublest.o and dfp.o.
Do not build target-float.c (instead of doublest.c)
with -Wformat-nonliteral.
* doublest.c: Remove file.
* doublest.h: Remove file.
* dfp.c: Remove file.
* dfp.h: Remove file.
* target-float.c: Do not include "doublest.h" and "dfp.h".
(DOUBLEST): Move here from doublest.h.
(enum float_kind): Likewise.
(FLOATFORMAT_CHAR_BIT): Likewise.
(FLOATFORMAT_LARGEST_BYTES): Likewise.
(floatformat_totalsize_bytes): Move here from doublest.c. Make static.
(floatformat_precision): Likewise.
(floatformat_normalize_byteorder, get_field, put_field): Likewise.
(floatformat_is_negative, floatformat_classify, floatformat_mantissa):
Likewise.
(host_float_format, host_double_format, host_long_double_format):
Likewise.
(floatformat_to_string, floatformat_from_string): Likewise.
(floatformat_to_doublest): Likewise. Also, inline the original
convert_floatformat_to_doublest.
(floatformat_from_doublest): Likewise. Also, inline the original
convert_floatformat_from_doublest.
Include "dpd/decimal128.h", "dpd/decimal64.h", and "dpd/decimal32.h".
(MAX_DECIMAL_STRING): Move here from dfp.c.
(match_endianness): Likewise.
(set_decnumber_context, decimal_check_errors): Likewise.
(decimal_from_number, decimal_to_number): Likewise.
(decimal_to_string, decimal_from_string): Likewise. Make static.
(decimal_from_longest, decimal_from_ulongest): Likewise.
(decimal_to_longest): Likewise.
(decimal_binop, decimal_is_zero, decimal_compare): Likewise.
(decimal_convert): Likewise.
Diffstat (limited to 'gdb')
-rw-r--r-- | gdb/ChangeLog | 42 | ||||
-rw-r--r-- | gdb/Makefile.in | 12 | ||||
-rw-r--r-- | gdb/dfp.c | 389 | ||||
-rw-r--r-- | gdb/dfp.h | 50 | ||||
-rw-r--r-- | gdb/doublest.c | 898 | ||||
-rw-r--r-- | gdb/doublest.h | 79 | ||||
-rw-r--r-- | gdb/target-float.c | 1248 |
7 files changed, 1291 insertions, 1427 deletions
diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 2691367..58cb4eb 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,5 +1,47 @@ 2017-11-06 Ulrich Weigand <uweigand@de.ibm.com> + * Makefile.in (SFILES): Remove doublest.c and dfp.c. + (HFILES_NO_SRCDIR): Remove doublest.h and dfp.h. + (COMMON_OBS): Remove doublest.o and dfp.o. + Do not build target-float.c (instead of doublest.c) + with -Wformat-nonliteral. + + * doublest.c: Remove file. + * doublest.h: Remove file. + * dfp.c: Remove file. + * dfp.h: Remove file. + + * target-float.c: Do not include "doublest.h" and "dfp.h". + (DOUBLEST): Move here from doublest.h. + (enum float_kind): Likewise. + (FLOATFORMAT_CHAR_BIT): Likewise. + (FLOATFORMAT_LARGEST_BYTES): Likewise. + (floatformat_totalsize_bytes): Move here from doublest.c. Make static. + (floatformat_precision): Likewise. + (floatformat_normalize_byteorder, get_field, put_field): Likewise. + (floatformat_is_negative, floatformat_classify, floatformat_mantissa): + Likewise. + (host_float_format, host_double_format, host_long_double_format): + Likewise. + (floatformat_to_string, floatformat_from_string): Likewise. + (floatformat_to_doublest): Likewise. Also, inline the original + convert_floatformat_to_doublest. + (floatformat_from_doublest): Likewise. Also, inline the original + convert_floatformat_from_doublest. + + Include "dpd/decimal128.h", "dpd/decimal64.h", and "dpd/decimal32.h". + (MAX_DECIMAL_STRING): Move here from dfp.c. + (match_endianness): Likewise. + (set_decnumber_context, decimal_check_errors): Likewise. + (decimal_from_number, decimal_to_number): Likewise. + (decimal_to_string, decimal_from_string): Likewise. Make static. + (decimal_from_longest, decimal_from_ulongest): Likewise. + (decimal_to_longest): Likewise. + (decimal_binop, decimal_is_zero, decimal_compare): Likewise. + (decimal_convert): Likewise. + +2017-11-06 Ulrich Weigand <uweigand@de.ibm.com> + * doublest.c: Do not include "gdbtypes.h". (extract_typed_floating): Remove. (store_typed_floating): Remove. diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 9e8cdf7..84d3493 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -1086,11 +1086,9 @@ SFILES = \ d-valprint.c \ dbxread.c \ demangle.c \ - dfp.c \ dictionary.c \ disasm.c \ disasm-selftests.c \ - doublest.c \ dtrace-probe.c \ dummy-frame.c \ dwarf2-frame.c \ @@ -1320,11 +1318,9 @@ HFILES_NO_SRCDIR = \ darwin-nat.h \ dcache.h \ defs.h \ - dfp.h \ dicos-tdep.h \ dictionary.h \ disasm.h \ - doublest.h \ dummy-frame.h \ dwarf2-frame.h \ dwarf2-frame-tailcall.h \ @@ -1709,11 +1705,9 @@ COMMON_OBS = $(DEPFILES) $(CONFIG_OBS) $(YYOBJ) \ dbxread.o \ debug.o \ demangle.o \ - dfp.o \ dictionary.o \ disasm.o \ disasm-selftests.o \ - doublest.o \ dummy-frame.o \ dwarf2-frame.o \ dwarf2-frame-tailcall.o \ @@ -2735,10 +2729,10 @@ printcmd.o: $(srcdir)/printcmd.c $(COMPILE.post) $(srcdir)/printcmd.c $(POSTCOMPILE) -# Same for "doublest.c". -doublest.o: $(srcdir)/doublest.c +# Same for "target-float.c". +target-float.o: $(srcdir)/target-float.c $(COMPILE.pre) $(INTERNAL_CFLAGS) $(GDB_WARN_CFLAGS_NO_FORMAT) \ - $(COMPILE.post) $(srcdir)/doublest.c + $(COMPILE.post) $(srcdir)/target-float.c # ada-exp.c can appear in srcdir, for releases; or in ., for # development builds. diff --git a/gdb/dfp.c b/gdb/dfp.c deleted file mode 100644 index ec05134..0000000 --- a/gdb/dfp.c +++ /dev/null @@ -1,389 +0,0 @@ -/* Decimal floating point support for GDB. - - Copyright (C) 2007-2017 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 "expression.h" -#include "dfp.h" - -/* 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, int len, enum bfd_endian byte_order, - gdb_byte *to) -{ - int i; - -#if WORDS_BIGENDIAN -#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE -#else -#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG -#endif - - if (byte_order == OPPOSITE_BYTE_ORDER) - for (i = 0; i < len; i++) - to[i] = from[len - i - 1]; - else - for (i = 0; i < len; i++) - to[i] = from[i]; - - return; -} - -/* Helper function to get the appropriate libdecnumber context for each size - of decimal float. */ -static void -set_decnumber_context (decContext *ctx, int len) -{ - switch (len) - { - 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, int len) -{ - decContext set; - - set_decnumber_context (&set, len); - - switch (len) - { - case 4: - decimal32FromNumber ((decimal32 *) to, from, &set); - break; - case 8: - decimal64FromNumber ((decimal64 *) to, from, &set); - break; - case 16: - decimal128FromNumber ((decimal128 *) to, from, &set); - break; - } -} - -/* Helper function to convert each size of decimal float to libdecnumber's - appropriate representation for computation. */ -static void -decimal_to_number (const gdb_byte *from, int len, decNumber *to) -{ - switch (len) - { - case 4: - decimal32ToNumber ((decimal32 *) from, to); - break; - case 8: - decimal64ToNumber ((decimal64 *) from, to); - break; - case 16: - decimal128ToNumber ((decimal128 *) from, to); - break; - default: - error (_("Unknown decimal floating point type.")); - break; - } -} - -/* 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_to_string (const gdb_byte *decbytes, int len, - enum bfd_endian byte_order, const char *format) -{ - gdb_byte dec[16]; - - match_endianness (decbytes, len, byte_order, dec); - - if (format != nullptr) - { - /* We don't handle format strings (yet). If the host printf supports - decimal floating point types, just use this. Otherwise, fall back - to printing the number while ignoring the format string. */ -#if defined (PRINTF_HAS_DECFLOAT) - /* FIXME: This makes unwarranted assumptions about the host ABI! */ - return string_printf (format, dec); -#endif - } - - std::string result; - result.resize (MAX_DECIMAL_STRING); - - switch (len) - { - 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_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, - const std::string &string) -{ - decContext set; - gdb_byte dec[16]; - - set_decnumber_context (&set, len); - - switch (len) - { - 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, len, byte_order, decbytes); - - /* 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_from_longest (LONGEST from, - gdb_byte *to, int len, enum bfd_endian byte_order) -{ - gdb_byte dec[16]; - 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, dec, len); - match_endianness (dec, len, byte_order, to); -} - -/* Converts a ULONGEST to a decimal float of specified LEN bytes. */ -void -decimal_from_ulongest (ULONGEST from, - gdb_byte *to, int len, enum bfd_endian byte_order) -{ - gdb_byte dec[16]; - 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, dec, len); - match_endianness (dec, len, byte_order, to); -} - -/* Converts a decimal float of LEN bytes to a LONGEST. */ -LONGEST -decimal_to_longest (const gdb_byte *from, int len, enum bfd_endian byte_order) -{ - /* libdecnumber has a function to convert from decimal to integer, but - it doesn't work when the decimal number has a fractional part. */ - std::string str = decimal_to_string (from, len, byte_order); - 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_binop (enum exp_opcode op, - const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, - const gdb_byte *y, int len_y, enum bfd_endian byte_order_y, - gdb_byte *result, int len_result, - enum bfd_endian byte_order_result) -{ - decContext set; - decNumber number1, number2, number3; - gdb_byte dec1[16], dec2[16], dec3[16]; - - match_endianness (x, len_x, byte_order_x, dec1); - match_endianness (y, len_y, byte_order_y, dec2); - - decimal_to_number (dec1, len_x, &number1); - decimal_to_number (dec2, len_y, &number2); - - set_decnumber_context (&set, len_result); - - 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, dec3, len_result); - - match_endianness (dec3, len_result, byte_order_result, result); -} - -/* Returns true if X (which is LEN bytes wide) is the number zero. */ -int -decimal_is_zero (const gdb_byte *x, int len, enum bfd_endian byte_order) -{ - decNumber number; - gdb_byte dec[16]; - - match_endianness (x, len, byte_order, dec); - decimal_to_number (dec, len, &number); - - return decNumberIsZero (&number); -} - -/* 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_compare (const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, - const gdb_byte *y, int len_y, enum bfd_endian byte_order_y) -{ - decNumber number1, number2, result; - decContext set; - gdb_byte dec1[16], dec2[16]; - int len_result; - - match_endianness (x, len_x, byte_order_x, dec1); - match_endianness (y, len_y, byte_order_y, dec2); - - decimal_to_number (dec1, len_x, &number1); - decimal_to_number (dec2, len_y, &number2); - - /* Perform the comparison in the larger of the two sizes. */ - len_result = len_x > len_y ? len_x : len_y; - set_decnumber_context (&set, len_result); - - 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_convert (const gdb_byte *from, int len_from, - enum bfd_endian byte_order_from, gdb_byte *to, int len_to, - enum bfd_endian byte_order_to) -{ - decNumber number; - gdb_byte dec[16]; - - match_endianness (from, len_from, byte_order_from, dec); - - decimal_to_number (dec, len_from, &number); - decimal_from_number (&number, dec, len_to); - - match_endianness (dec, len_to, byte_order_to, to); -} diff --git a/gdb/dfp.h b/gdb/dfp.h deleted file mode 100644 index af52fa2..0000000 --- a/gdb/dfp.h +++ /dev/null @@ -1,50 +0,0 @@ -/* Decimal floating point support for GDB. - - Copyright (C) 2007-2017 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/>. */ - -/* 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. */ - -#ifndef DFP_H -#define DFP_H - -#include "expression.h" /* For enum exp_opcode. */ - -extern std::string decimal_to_string (const gdb_byte *, int, enum bfd_endian, - const char *format = nullptr); -extern bool decimal_from_string (gdb_byte *, int, enum bfd_endian, - const std::string &string); -extern void decimal_from_longest (LONGEST from, gdb_byte *to, - int len, enum bfd_endian byte_order); -extern void decimal_from_ulongest (ULONGEST from, gdb_byte *to, - int len, enum bfd_endian byte_order); -extern LONGEST decimal_to_longest (const gdb_byte *from, int len, - enum bfd_endian byte_order); -extern void decimal_binop (enum exp_opcode, - const gdb_byte *, int, enum bfd_endian, - const gdb_byte *, int, enum bfd_endian, - gdb_byte *, int, enum bfd_endian); -extern int decimal_is_zero (const gdb_byte *, int, enum bfd_endian); -extern int decimal_compare (const gdb_byte *, int, enum bfd_endian, - const gdb_byte *, int, enum bfd_endian); -extern void decimal_convert (const gdb_byte *, int, enum bfd_endian, - gdb_byte *, int, enum bfd_endian); - -#endif diff --git a/gdb/doublest.c b/gdb/doublest.c deleted file mode 100644 index 87d3b1f..0000000 --- a/gdb/doublest.c +++ /dev/null @@ -1,898 +0,0 @@ -/* Floating point routines for GDB, the GNU debugger. - - Copyright (C) 1986-2017 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/>. */ - -/* Support for converting target fp numbers into host DOUBLEST format. */ - -/* XXX - This code should really be in libiberty/floatformat.c, - however configuration issues with libiberty made this very - difficult to do in the available time. */ - -#include "defs.h" -#include "doublest.h" -#include "floatformat.h" -#include <math.h> /* ldexp */ -#include <algorithm> - -/* 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 - -/* 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; -} - -/* 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; - } -} - -/* Convert from FMT to a DOUBLEST. - FROM is the address of the extended float. - Store the DOUBLEST in *TO. */ - -static void -convert_floatformat_to_doublest (const struct floatformat *fmt, - const void *from, - DOUBLEST *to) -{ - unsigned char *ufrom = (unsigned char *) from; - DOUBLEST 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 = (DOUBLEST) dto; - return; - } - - order = floatformat_normalize_byteorder (fmt, ufrom, newfrom); - - if (order != fmt->byteorder) - ufrom = newfrom; - - if (fmt->split_half) - { - DOUBLEST dtop, dbot; - - floatformat_to_doublest (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; - } - floatformat_to_doublest (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 ((double) 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; -} - -/* 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; - } -} - -/* The converse: convert the DOUBLEST *FROM to an extended float and - store where TO points. Neither FROM nor TO have any alignment - restrictions. */ - -static void -convert_doublest_to_floatformat (const struct floatformat *fmt, - const DOUBLEST *from, void *to) -{ - DOUBLEST dfrom; - int exponent; - DOUBLEST 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; - DOUBLEST 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 - (DOUBLEST) dtop); - dtopnv = dtop; - dbotnv = dbot; - floatformat_from_doublest (fmt->split_half, &dtopnv, uto); - floatformat_from_doublest (fmt->split_half, &dbotnv, - (uto - + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2)); - 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; - } - -#ifdef HAVE_LONG_DOUBLE - mant = frexpl (dfrom, &exponent); -#else - mant = frexp (dfrom, &exponent); -#endif - - 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); -} - -/* Check if VAL (which is assumed to be a floating point number whose - format is described by FMT) is negative. */ - -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. */ - -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. */ - -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; -} - -/* 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; -} - - -/* Convert TO/FROM target to the hosts DOUBLEST floating-point format. - - 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; - -/* See doublest.h. */ - -size_t -floatformat_totalsize_bytes (const struct floatformat *fmt) -{ - return ((fmt->totalsize + FLOATFORMAT_CHAR_BIT - 1) - / FLOATFORMAT_CHAR_BIT); -} - -void -floatformat_to_doublest (const struct floatformat *fmt, - const void *in, DOUBLEST *out) -{ - gdb_assert (fmt != NULL); - - if (fmt == host_float_format) - { - float val = 0; - - memcpy (&val, in, floatformat_totalsize_bytes (fmt)); - *out = val; - } - else if (fmt == host_double_format) - { - double val = 0; - - memcpy (&val, in, floatformat_totalsize_bytes (fmt)); - *out = val; - } - else if (fmt == host_long_double_format) - { - long double val = 0; - - memcpy (&val, in, floatformat_totalsize_bytes (fmt)); - *out = val; - } - else - convert_floatformat_to_doublest (fmt, in, out); -} - -void -floatformat_from_doublest (const struct floatformat *fmt, - const DOUBLEST *in, void *out) -{ - gdb_assert (fmt != NULL); - - if (fmt == host_float_format) - { - float val = *in; - - memcpy (out, &val, floatformat_totalsize_bytes (fmt)); - } - else if (fmt == host_double_format) - { - double val = *in; - - memcpy (out, &val, floatformat_totalsize_bytes (fmt)); - } - else if (fmt == host_long_double_format) - { - long double val = *in; - - memcpy (out, &val, floatformat_totalsize_bytes (fmt)); - } - else - convert_doublest_to_floatformat (fmt, in, out); -} - -/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, - to a string, optionally using the print format FORMAT. */ -std::string -floatformat_to_string (const struct floatformat *fmt, - const gdb_byte *in, const char *format) -{ - /* Unless we need to adhere to a specific format, provide special - output for certain cases. */ - if (format == nullptr) - { - /* Detect invalid representations. */ - if (!floatformat_is_valid (fmt, in)) - return "<invalid float value>"; - - /* Handle NaN and Inf. */ - enum float_kind kind = floatformat_classify (fmt, in); - if (kind == float_nan) - { - const char *sign = floatformat_is_negative (fmt, in)? "-" : ""; - const char *mantissa = floatformat_mantissa (fmt, in); - return string_printf ("%snan(0x%s)", sign, mantissa); - } - else if (kind == float_infinite) - { - const char *sign = floatformat_is_negative (fmt, in)? "-" : ""; - return string_printf ("%sinf", sign); - } - } - - /* Determine the format string to use on the host side. */ - std::string host_format; - char conversion; - - if (format == nullptr) - { - /* If no format was specified, print the number using a format string - where the precision is set to the DECIMAL_DIG value for the given - floating-point format. This value is computed as - - ceil(1 + p * log10(b)), - - where p is the precision of the floating-point format in bits, and - b is the base (which is always 2 for the formats we support). */ - const double log10_2 = .30102999566398119521; - double d_decimal_dig = 1 + floatformat_precision (fmt) * log10_2; - int decimal_dig = d_decimal_dig; - if (decimal_dig < d_decimal_dig) - decimal_dig++; - - host_format = string_printf ("%%.%d", decimal_dig); - conversion = 'g'; - } - else - { - /* Use the specified format, stripping out the conversion character - and length modifier, if present. */ - size_t len = strlen (format); - gdb_assert (len > 1); - conversion = format[--len]; - gdb_assert (conversion == 'e' || conversion == 'f' || conversion == 'g' - || conversion == 'E' || conversion == 'G'); - if (format[len - 1] == 'L') - len--; - - host_format = std::string (format, len); - } - - /* Add the length modifier and conversion character appropriate for - handling the host DOUBLEST type. */ -#ifdef HAVE_LONG_DOUBLE - host_format += 'L'; -#endif - host_format += conversion; - - DOUBLEST doub; - floatformat_to_doublest (fmt, in, &doub); - return string_printf (host_format.c_str (), doub); -} - -/* Parse string STRING into a target floating-number of format FMT and - store it as byte-stream ADDR. Return whether parsing succeeded. */ -bool -floatformat_from_string (const struct floatformat *fmt, gdb_byte *out, - const std::string &in) -{ - DOUBLEST doub; - int n, num; -#ifdef HAVE_LONG_DOUBLE - const char *scan_format = "%Lg%n"; -#else - const char *scan_format = "%lg%n"; -#endif - num = sscanf (in.c_str (), scan_format, &doub, &n); - - /* 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; - - floatformat_from_doublest (fmt, &doub, out); - return true; -} diff --git a/gdb/doublest.h b/gdb/doublest.h deleted file mode 100644 index fe3f897..0000000 --- a/gdb/doublest.h +++ /dev/null @@ -1,79 +0,0 @@ -/* Floating point definitions for GDB. - - Copyright (C) 1986-2017 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/>. */ - -#ifndef DOUBLEST_H -#define DOUBLEST_H - -struct floatformat; - -/* Use `long double' if the host compiler supports it. (Note that this is not - necessarily any longer than `double'. On SunOS/gcc, it's the same as - double.) This is necessary because GDB internally converts all floating - point values to the widest type supported by the host. - - There are problems however, when the target `long double' is longer than the - host's `long double'. In general, we'll probably reduce the precision of - any such values and print a warning. */ - -#if (defined HAVE_LONG_DOUBLE && defined PRINTF_HAS_LONG_DOUBLE \ - && defined SCANF_HAS_LONG_DOUBLE) -typedef long double DOUBLEST; -#else -typedef double DOUBLEST; -/* If we can't scan or print long double, we don't want to use it - anywhere. */ -# undef HAVE_LONG_DOUBLE -# undef PRINTF_HAS_LONG_DOUBLE -# undef SCANF_HAS_LONG_DOUBLE -#endif - -/* 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 -}; - -extern void floatformat_to_doublest (const struct floatformat *, - const void *in, DOUBLEST *out); -extern void floatformat_from_doublest (const struct floatformat *, - const DOUBLEST *in, void *out); - -extern int floatformat_is_negative (const struct floatformat *, - const bfd_byte *); -extern enum float_kind floatformat_classify (const struct floatformat *, - const bfd_byte *); -extern const char *floatformat_mantissa (const struct floatformat *, - const bfd_byte *); - -extern std::string floatformat_to_string (const struct floatformat *fmt, - const gdb_byte *in, - const char *format = nullptr); -extern bool floatformat_from_string (const struct floatformat *fmt, - gdb_byte *out, const std::string &in); - -/* Return the floatformat's total size in host bytes. */ - -extern size_t floatformat_totalsize_bytes (const struct floatformat *fmt); - -#endif diff --git a/gdb/target-float.c b/gdb/target-float.c index d2c1064..a5f6af5 100644 --- a/gdb/target-float.c +++ b/gdb/target-float.c @@ -18,8 +18,6 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */ #include "defs.h" -#include "dfp.h" -#include "doublest.h" #include "gdbtypes.h" #include "floatformat.h" #include "target-float.h" @@ -29,6 +27,877 @@ #include <math.h> +#if (defined HAVE_LONG_DOUBLE && defined PRINTF_HAS_LONG_DOUBLE \ + && defined SCANF_HAS_LONG_DOUBLE) +typedef long double DOUBLEST; +#else +typedef double DOUBLEST; +/* If we can't scan or print long double, we don't want to use it + anywhere. */ +# undef HAVE_LONG_DOUBLE +# undef PRINTF_HAS_LONG_DOUBLE +# undef SCANF_HAS_LONG_DOUBLE +#endif + +/* 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 TO/FROM target to the hosts DOUBLEST floating-point format. + + 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 from FMT to a DOUBLEST. FROM is the address of the extended float. + Store the DOUBLEST in *TO. */ +static void +floatformat_to_doublest (const struct floatformat *fmt, + const void *from, DOUBLEST *to) +{ + 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; + DOUBLEST 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 = (DOUBLEST) dto; + return; + } + + order = floatformat_normalize_byteorder (fmt, ufrom, newfrom); + + if (order != fmt->byteorder) + ufrom = newfrom; + + if (fmt->split_half) + { + DOUBLEST dtop, dbot; + + floatformat_to_doublest (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; + } + floatformat_to_doublest (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 ((double) 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; +} + +/* Convert the DOUBLEST *FROM to an extended float in format FMT and + store where TO points. */ +static void +floatformat_from_doublest (const struct floatformat *fmt, + const DOUBLEST *from, void *to) +{ + 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; + } + + DOUBLEST dfrom; + int exponent; + DOUBLEST 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; + DOUBLEST 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 - (DOUBLEST) dtop); + dtopnv = dtop; + dbotnv = dbot; + floatformat_from_doublest (fmt->split_half, &dtopnv, uto); + floatformat_from_doublest (fmt->split_half, &dbotnv, + (uto + + fmt->totalsize / FLOATFORMAT_CHAR_BIT / 2)); + 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; + } + +#ifdef HAVE_LONG_DOUBLE + mant = frexpl (dfrom, &exponent); +#else + mant = frexp (dfrom, &exponent); +#endif + + 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); +} + +/* Convert the byte-stream ADDR, interpreted as floating-point format FMT, + to a string, optionally using the print format FORMAT. */ +static std::string +floatformat_to_string (const struct floatformat *fmt, + const gdb_byte *in, const char *format) +{ + /* Unless we need to adhere to a specific format, provide special + output for certain cases. */ + if (format == nullptr) + { + /* Detect invalid representations. */ + if (!floatformat_is_valid (fmt, in)) + return "<invalid float value>"; + + /* Handle NaN and Inf. */ + enum float_kind kind = floatformat_classify (fmt, in); + if (kind == float_nan) + { + const char *sign = floatformat_is_negative (fmt, in)? "-" : ""; + const char *mantissa = floatformat_mantissa (fmt, in); + return string_printf ("%snan(0x%s)", sign, mantissa); + } + else if (kind == float_infinite) + { + const char *sign = floatformat_is_negative (fmt, in)? "-" : ""; + return string_printf ("%sinf", sign); + } + } + + /* Determine the format string to use on the host side. */ + std::string host_format; + char conversion; + + if (format == nullptr) + { + /* If no format was specified, print the number using a format string + where the precision is set to the DECIMAL_DIG value for the given + floating-point format. This value is computed as + + ceil(1 + p * log10(b)), + + where p is the precision of the floating-point format in bits, and + b is the base (which is always 2 for the formats we support). */ + const double log10_2 = .30102999566398119521; + double d_decimal_dig = 1 + floatformat_precision (fmt) * log10_2; + int decimal_dig = d_decimal_dig; + if (decimal_dig < d_decimal_dig) + decimal_dig++; + + host_format = string_printf ("%%.%d", decimal_dig); + conversion = 'g'; + } + else + { + /* Use the specified format, stripping out the conversion character + and length modifier, if present. */ + size_t len = strlen (format); + gdb_assert (len > 1); + conversion = format[--len]; + gdb_assert (conversion == 'e' || conversion == 'f' || conversion == 'g' + || conversion == 'E' || conversion == 'G'); + if (format[len - 1] == 'L') + len--; + + host_format = std::string (format, len); + } + + /* Add the length modifier and conversion character appropriate for + handling the host DOUBLEST type. */ +#ifdef HAVE_LONG_DOUBLE + host_format += 'L'; +#endif + host_format += conversion; + + DOUBLEST doub; + floatformat_to_doublest (fmt, in, &doub); + return string_printf (host_format.c_str (), doub); +} + +/* Parse string STRING into a target floating-number of format FMT and + store it as byte-stream ADDR. Return whether parsing succeeded. */ +static bool +floatformat_from_string (const struct floatformat *fmt, gdb_byte *out, + const std::string &in) +{ + DOUBLEST doub; + int n, num; +#ifdef HAVE_LONG_DOUBLE + const char *scan_format = "%Lg%n"; +#else + const char *scan_format = "%lg%n"; +#endif + num = sscanf (in.c_str (), scan_format, &doub, &n); + + /* 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; + + floatformat_from_doublest (fmt, &doub, out); + return true; +} + /* Convert the byte-stream ADDR, interpreted as floating-point format FMT, to an integer value (rounding towards zero). */ static LONGEST @@ -182,6 +1051,381 @@ floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, } +/* Helper routines operating on decimal floating-point data. */ + +/* Decimal floating point is one of the extension to IEEE 754, which is + described in http://grouper.ieee.org/groups/754/revision.html and + http://www2.hursley.ibm.com/decimal/. It completes binary floating + point by representing floating point more exactly. */ + +/* 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, int len, enum bfd_endian byte_order, + gdb_byte *to) +{ + int i; + +#if WORDS_BIGENDIAN +#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_LITTLE +#else +#define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG +#endif + + if (byte_order == OPPOSITE_BYTE_ORDER) + for (i = 0; i < len; i++) + to[i] = from[len - i - 1]; + else + for (i = 0; i < len; i++) + to[i] = from[i]; + + return; +} + +/* Helper function to get the appropriate libdecnumber context for each size + of decimal float. */ +static void +set_decnumber_context (decContext *ctx, int len) +{ + switch (len) + { + 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, int len) +{ + decContext set; + + set_decnumber_context (&set, len); + + switch (len) + { + case 4: + decimal32FromNumber ((decimal32 *) to, from, &set); + break; + case 8: + decimal64FromNumber ((decimal64 *) to, from, &set); + break; + case 16: + decimal128FromNumber ((decimal128 *) to, from, &set); + break; + } +} + +/* Helper function to convert each size of decimal float to libdecnumber's + appropriate representation for computation. */ +static void +decimal_to_number (const gdb_byte *from, int len, decNumber *to) +{ + switch (len) + { + case 4: + decimal32ToNumber ((decimal32 *) from, to); + break; + case 8: + decimal64ToNumber ((decimal64 *) from, to); + break; + case 16: + decimal128ToNumber ((decimal128 *) from, to); + break; + default: + error (_("Unknown decimal floating point type.")); + break; + } +} + +/* Convert decimal type to its string representation. LEN is the length + of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and + 16 bytes for decimal128. */ +static std::string +decimal_to_string (const gdb_byte *decbytes, int len, + enum bfd_endian byte_order, const char *format = nullptr) +{ + gdb_byte dec[16]; + + match_endianness (decbytes, len, byte_order, dec); + + if (format != nullptr) + { + /* We don't handle format strings (yet). If the host printf supports + decimal floating point types, just use this. Otherwise, fall back + to printing the number while ignoring the format string. */ +#if defined (PRINTF_HAS_DECFLOAT) + /* FIXME: This makes unwarranted assumptions about the host ABI! */ + return string_printf (format, dec); +#endif + } + + std::string result; + result.resize (MAX_DECIMAL_STRING); + + switch (len) + { + 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. */ +static bool +decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, + const std::string &string) +{ + decContext set; + gdb_byte dec[16]; + + set_decnumber_context (&set, len); + + switch (len) + { + 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, len, byte_order, decbytes); + + /* Check for errors in the DFP operation. */ + decimal_check_errors (&set); + + return true; +} + +/* Converts a LONGEST to a decimal float of specified LEN bytes. */ +static void +decimal_from_longest (LONGEST from, + gdb_byte *to, int len, enum bfd_endian byte_order) +{ + gdb_byte dec[16]; + 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, dec, len); + match_endianness (dec, len, byte_order, to); +} + +/* Converts a ULONGEST to a decimal float of specified LEN bytes. */ +static void +decimal_from_ulongest (ULONGEST from, + gdb_byte *to, int len, enum bfd_endian byte_order) +{ + gdb_byte dec[16]; + 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, dec, len); + match_endianness (dec, len, byte_order, to); +} + +/* Converts a decimal float of LEN bytes to a LONGEST. */ +static LONGEST +decimal_to_longest (const gdb_byte *from, int len, enum bfd_endian byte_order) +{ + /* libdecnumber has a function to convert from decimal to integer, but + it doesn't work when the decimal number has a fractional part. */ + std::string str = decimal_to_string (from, len, byte_order); + return strtoll (str.c_str (), NULL, 10); +} + +/* Perform operation OP with operands X and Y with sizes LEN_X and LEN_Y + and byte orders BYTE_ORDER_X and BYTE_ORDER_Y, and store value in + RESULT with size LEN_RESULT and byte order BYTE_ORDER_RESULT. */ +static void +decimal_binop (enum exp_opcode op, + const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, + const gdb_byte *y, int len_y, enum bfd_endian byte_order_y, + gdb_byte *result, int len_result, + enum bfd_endian byte_order_result) +{ + decContext set; + decNumber number1, number2, number3; + gdb_byte dec1[16], dec2[16], dec3[16]; + + match_endianness (x, len_x, byte_order_x, dec1); + match_endianness (y, len_y, byte_order_y, dec2); + + decimal_to_number (dec1, len_x, &number1); + decimal_to_number (dec2, len_y, &number2); + + set_decnumber_context (&set, len_result); + + 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, dec3, len_result); + + match_endianness (dec3, len_result, byte_order_result, result); +} + +/* Returns true if X (which is LEN bytes wide) is the number zero. */ +static int +decimal_is_zero (const gdb_byte *x, int len, enum bfd_endian byte_order) +{ + decNumber number; + gdb_byte dec[16]; + + match_endianness (x, len, byte_order, dec); + decimal_to_number (dec, len, &number); + + return decNumberIsZero (&number); +} + +/* Compares two numbers numerically. If X is less than Y then the return value + will be -1. If they are equal, then the return value will be 0. If X is + greater than the Y then the return value will be 1. */ +static int +decimal_compare (const gdb_byte *x, int len_x, enum bfd_endian byte_order_x, + const gdb_byte *y, int len_y, enum bfd_endian byte_order_y) +{ + decNumber number1, number2, result; + decContext set; + gdb_byte dec1[16], dec2[16]; + int len_result; + + match_endianness (x, len_x, byte_order_x, dec1); + match_endianness (y, len_y, byte_order_y, dec2); + + decimal_to_number (dec1, len_x, &number1); + decimal_to_number (dec2, len_y, &number2); + + /* Perform the comparison in the larger of the two sizes. */ + len_result = len_x > len_y ? len_x : len_y; + set_decnumber_context (&set, len_result); + + 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. */ +static void +decimal_convert (const gdb_byte *from, int len_from, + enum bfd_endian byte_order_from, gdb_byte *to, int len_to, + enum bfd_endian byte_order_to) +{ + decNumber number; + gdb_byte dec[16]; + + match_endianness (from, len_from, byte_order_from, dec); + + decimal_to_number (dec, len_from, &number); + decimal_from_number (&number, dec, len_to); + + match_endianness (dec, len_to, byte_order_to, to); +} + + /* 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 |