diff options
author | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2017-11-06 15:58:46 +0100 |
---|---|---|
committer | Ulrich Weigand <ulrich.weigand@de.ibm.com> | 2017-11-06 15:58:46 +0100 |
commit | 66c02b9ed1eabf1d7981c0c27ec9fd3c17fc5d35 (patch) | |
tree | 004642d484425eddaec7fdc33f68b1501c1ad104 /gdb/target-float.c | |
parent | 50637b26f85dc3e59b616f1b01ca8885cf98f0b4 (diff) | |
download | gdb-66c02b9ed1eabf1d7981c0c27ec9fd3c17fc5d35.zip gdb-66c02b9ed1eabf1d7981c0c27ec9fd3c17fc5d35.tar.gz gdb-66c02b9ed1eabf1d7981c0c27ec9fd3c17fc5d35.tar.bz2 |
Target FP: Add binop and compare routines to target-float.{c,h}
This patch adds the following target floating-point routines:
- target_float_binop
- target_float_compare
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP (derived from current valarith.c code).
These routines are used to handle both binary and decimal FP types
in scalar_binop, value_equal, and value_less, mostly following the
method currently used for decimal FP. The existing value_args_as_decimal
helper is renamed to value_args_as_target_float and extended to handle
both binary and decimal types.
The unary operations value_pos and value_neg are also simplified,
the former by using a simple copy for all scalar types, the latter
by using value_binop (... BINOP_SUB) to implement negation as
subtraction from zero.
ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c: Include <math.h>.
(floatformat_binop): New function.
(floatformat_compare): Likewise.
(target_float_binop): Likewise.
(target_float_compare): Likewise.
* target-float.h: Include "expression.h".
(target_float_binop): Add prototype.
(target_float_compare): Likewise.
* valarith.c: Do not include "doublest.h" and "dfp.h".
Include "common/byte-vector.h".
(value_args_as_decimal): Remove, replace by ...
(value_args_as_target_float): ... this function. Handle both
binary and decimal target floating-point formats.
(scalar_binop): Handle both binary and decimal FP using
value_args_as_target_float and target_float_binop.
(value_equal): Handle both binary and decimal FP using
value_args_as_target_float and target_float_compare.
(value_less): Likewise.
(value_pos): Handle all scalar types as simple copy.
(value_neg): Handle all scalar types via BINOP_SUB from 0.
* dfp.c (decimal_binop): Throw error instead of internal_error
when called with an unsupported operation code.
Diffstat (limited to 'gdb/target-float.c')
-rw-r--r-- | gdb/target-float.c | 152 |
1 files changed, 152 insertions, 0 deletions
diff --git a/gdb/target-float.c b/gdb/target-float.c index 1178921..ad04f8f 100644 --- a/gdb/target-float.c +++ b/gdb/target-float.c @@ -27,6 +27,8 @@ /* Helper routines operating on binary floating-point data. */ +#include <math.h> + /* Convert the byte-stream ADDR, interpreted as floating-point format FMT, to an integer value (rounding towards zero). */ static LONGEST @@ -82,6 +84,82 @@ floatformat_convert (const gdb_byte *from, const struct floatformat *from_fmt, } } +/* Perform the binary operation indicated by OPCODE, using as operands the + target byte streams X and Y, interpreted as floating-point numbers of + formats FMT_X and FMT_Y, respectively. Convert the result to format + FMT_RES and store it into the byte-stream RES. */ +static void +floatformat_binop (enum exp_opcode op, + const struct floatformat *fmt_x, const gdb_byte *x, + const struct floatformat *fmt_y, const gdb_byte *y, + const struct floatformat *fmt_result, gdb_byte *result) +{ + DOUBLEST v1, v2, v = 0; + + floatformat_to_doublest (fmt_x, x, &v1); + floatformat_to_doublest (fmt_y, y, &v2); + + switch (op) + { + case BINOP_ADD: + v = v1 + v2; + break; + + case BINOP_SUB: + v = v1 - v2; + break; + + case BINOP_MUL: + v = v1 * v2; + break; + + case BINOP_DIV: + v = v1 / v2; + break; + + case BINOP_EXP: + errno = 0; + v = pow (v1, v2); + if (errno) + error (_("Cannot perform exponentiation: %s"), + safe_strerror (errno)); + break; + + case BINOP_MIN: + v = v1 < v2 ? v1 : v2; + break; + + case BINOP_MAX: + v = v1 > v2 ? v1 : v2; + break; + + default: + error (_("Integer-only operation on floating point number.")); + break; + } + + floatformat_from_doublest (fmt_result, &v, result); +} + +/* Compare the two target byte streams X and Y, interpreted as floating-point + numbers of formats FMT_X and FMT_Y, respectively. Return zero if X and Y + are equal, -1 if X is less than Y, and 1 otherwise. */ +static int +floatformat_compare (const struct floatformat *fmt_x, const gdb_byte *x, + const struct floatformat *fmt_y, const gdb_byte *y) +{ + DOUBLEST v1, v2; + + floatformat_to_doublest (fmt_x, x, &v1); + floatformat_to_doublest (fmt_y, y, &v2); + + if (v1 == v2) + return 0; + if (v1 < v2) + return -1; + return 1; +} + /* Typed floating-point routines. These routines operate on floating-point values in target format, represented by a byte buffer interpreted as a @@ -266,3 +344,77 @@ target_float_convert (const gdb_byte *from, const struct type *from_type, gdb_assert_not_reached ("unexpected type code"); } + +/* Perform the binary operation indicated by OPCODE, using as operands the + target byte streams X and Y, interpreted as floating-point numbers of + types TYPE_X and TYPE_Y, respectively. Convert the result to type + TYPE_RES and store it into the byte-stream RES. + + The three types must either be all binary floating-point types, or else + all decimal floating-point types. Binary and decimal floating-point + types cannot be mixed within a single operation. */ +void +target_float_binop (enum exp_opcode opcode, + const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y, + gdb_byte *res, const struct type *type_res) +{ + /* Ensure possible padding bytes in the target buffer are zeroed out. */ + memset (res, 0, TYPE_LENGTH (type_res)); + + if (TYPE_CODE (type_res) == TYPE_CODE_FLT) + { + gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_FLT); + gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT); + return floatformat_binop (opcode, + floatformat_from_type (type_x), x, + floatformat_from_type (type_y), y, + floatformat_from_type (type_res), res); + } + + if (TYPE_CODE (type_res) == TYPE_CODE_DECFLOAT) + { + gdb_assert (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT); + gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT); + return decimal_binop (opcode, + x, TYPE_LENGTH (type_x), + gdbarch_byte_order (get_type_arch (type_x)), + y, TYPE_LENGTH (type_y), + gdbarch_byte_order (get_type_arch (type_y)), + res, TYPE_LENGTH (type_res), + gdbarch_byte_order (get_type_arch (type_res))); + } + + gdb_assert_not_reached ("unexpected type code"); +} + +/* Compare the two target byte streams X and Y, interpreted as floating-point + numbers of types TYPE_X and TYPE_Y, respectively. Return zero if X and Y + are equal, -1 if X is less than Y, and 1 otherwise. + + The two types must either both be binary floating-point types, or else + both be decimal floating-point types. Binary and decimal floating-point + types cannot compared directly against each other. */ +int +target_float_compare (const gdb_byte *x, const struct type *type_x, + const gdb_byte *y, const struct type *type_y) +{ + if (TYPE_CODE (type_x) == TYPE_CODE_FLT) + { + gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_FLT); + return floatformat_compare (floatformat_from_type (type_x), x, + floatformat_from_type (type_y), y); + } + + if (TYPE_CODE (type_x) == TYPE_CODE_DECFLOAT) + { + gdb_assert (TYPE_CODE (type_y) == TYPE_CODE_DECFLOAT); + return decimal_compare (x, TYPE_LENGTH (type_x), + gdbarch_byte_order (get_type_arch (type_x)), + y, TYPE_LENGTH (type_y), + gdbarch_byte_order (get_type_arch (type_y))); + } + + gdb_assert_not_reached ("unexpected type code"); +} + |