X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gdb%2Fdfp.c;h=ae6204b5629f69a2ee547ace5e5aeb53526fc30c;hb=b7761f11062dc4d4fd554342ac2d2fb235b65b7a;hp=67cee138b730700cb940f4a0f999e722de3d05d3;hpb=9b913628cfe092b3fdd328de4264def1ec1e94cb;p=binutils-gdb.git diff --git a/gdb/dfp.c b/gdb/dfp.c index 67cee138b73..ae6204b5629 100644 --- a/gdb/dfp.c +++ b/gdb/dfp.c @@ -1,6 +1,6 @@ /* Decimal floating point support for GDB. - Copyright 2007 Free Software Foundation, Inc. + Copyright 2007-2012 Free Software Foundation, Inc. This file is part of GDB. @@ -18,6 +18,10 @@ along with this program. If not, see . */ #include "defs.h" +#include "expression.h" +#include "gdbtypes.h" +#include "value.h" +#include "dfp.h" /* The order of the following headers is important for making sure decNumber structure is large enough to hold decimal128 digits. */ @@ -30,7 +34,8 @@ 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, gdb_byte *to) +match_endianness (const gdb_byte *from, int len, enum bfd_endian byte_order, + gdb_byte *to) { int i; @@ -40,7 +45,7 @@ match_endianness (const gdb_byte *from, int len, gdb_byte *to) #define OPPOSITE_BYTE_ORDER BFD_ENDIAN_BIG #endif - if (gdbarch_byte_order (current_gdbarch) == OPPOSITE_BYTE_ORDER) + if (byte_order == OPPOSITE_BYTE_ORDER) for (i = 0; i < len; i++) to[i] = from[len - i - 1]; else @@ -50,15 +55,101 @@ match_endianness (const gdb_byte *from, int len, gdb_byte *to) 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. */ void -decimal_to_string (const gdb_byte *decbytes, int len, char *s) +decimal_to_string (const gdb_byte *decbytes, int len, + enum bfd_endian byte_order, char *s) { gdb_byte dec[16]; - match_endianness (decbytes, len, dec); + match_endianness (decbytes, len, byte_order, dec); + switch (len) { case 4: @@ -71,7 +162,7 @@ decimal_to_string (const gdb_byte *decbytes, int len, char *s) decimal128ToString ((decimal128 *) dec, s); break; default: - error (_("Unknown decimal floating point type.\n")); + error (_("Unknown decimal floating point type.")); break; } } @@ -80,34 +171,214 @@ decimal_to_string (const gdb_byte *decbytes, int len, char *s) LEN is the length of the decimal type, 4 bytes for decimal32, 8 bytes for decimal64 and 16 bytes for decimal128. */ int -decimal_from_string (gdb_byte *decbytes, int len, const char *string) +decimal_from_string (gdb_byte *decbytes, int len, enum bfd_endian byte_order, + const char *string) { decContext set; gdb_byte dec[16]; + set_decnumber_context (&set, len); + switch (len) { case 4: - decContextDefault (&set, DEC_INIT_DECIMAL32); - set.traps = 0; decimal32FromString ((decimal32 *) dec, string, &set); break; case 8: - decContextDefault (&set, DEC_INIT_DECIMAL64); - set.traps = 0; decimal64FromString ((decimal64 *) dec, string, &set); break; case 16: - decContextDefault (&set, DEC_INIT_DECIMAL128); - set.traps = 0; decimal128FromString ((decimal128 *) dec, string, &set); break; default: - error (_("Unknown decimal floating point type.\n")); + error (_("Unknown decimal floating point type.")); break; } - match_endianness (dec, len, decbytes); + match_endianness (dec, len, byte_order, decbytes); + + /* Check for errors in the DFP operation. */ + decimal_check_errors (&set); return 1; } + +/* Converts a value of an integral type to a decimal float of + specified LEN bytes. */ +void +decimal_from_integral (struct value *from, + gdb_byte *to, int len, enum bfd_endian byte_order) +{ + LONGEST l; + gdb_byte dec[16]; + decNumber number; + struct type *type; + + type = check_typedef (value_type (from)); + + if (TYPE_LENGTH (type) > 4) + /* libdecnumber can convert only 32-bit integers. */ + error (_("Conversion of large integer to a " + "decimal floating type is not supported.")); + + l = value_as_long (from); + + if (TYPE_UNSIGNED (type)) + decNumberFromUInt32 (&number, (unsigned int) l); + else + decNumberFromInt32 (&number, (int) l); + + decimal_from_number (&number, dec, len); + match_endianness (dec, len, byte_order, to); +} + +/* Converts a value of a float type to a decimal float of + specified LEN bytes. + + This is an ugly way to do the conversion, but libdecnumber does + not offer a direct way to do it. */ +void +decimal_from_floating (struct value *from, + gdb_byte *to, int len, enum bfd_endian byte_order) +{ + char *buffer; + + buffer = xstrprintf ("%.30" DOUBLEST_PRINT_FORMAT, value_as_double (from)); + + decimal_from_string (to, len, byte_order, buffer); + + xfree (buffer); +} + +/* Converts a decimal float of LEN bytes to a double value. */ +DOUBLEST +decimal_to_doublest (const gdb_byte *from, int len, enum bfd_endian byte_order) +{ + char buffer[MAX_DECIMAL_STRING]; + + /* This is an ugly way to do the conversion, but libdecnumber does + not offer a direct way to do it. */ + decimal_to_string (from, len, byte_order, buffer); + return strtod (buffer, NULL); +} + +/* 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: + internal_error (__FILE__, __LINE__, + _("Unknown decimal floating point operation.")); + 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); +}