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.
This commit is contained in:
Ulrich Weigand 2017-11-06 15:58:46 +01:00
parent 50637b26f8
commit 66c02b9ed1
5 changed files with 258 additions and 211 deletions

View File

@ -1,3 +1,29 @@
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.
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (target_float_to_string): New function.

View File

@ -330,9 +330,8 @@ decimal_binop (enum exp_opcode op,
case BINOP_EXP:
decNumberPower (&number3, &number1, &number2, &set);
break;
default:
internal_error (__FILE__, __LINE__,
_("Unknown decimal floating point operation."));
default:
error (_("Operation not valid for decimal floating point number."));
break;
}

View File

@ -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");
}

View File

@ -20,6 +20,8 @@
#ifndef TYPED_FLOAT_H
#define TYPED_FLOAT_H
#include "expression.h"
extern bool target_float_is_valid (const gdb_byte *addr,
const struct type *type);
extern bool target_float_is_zero (const gdb_byte *addr,
@ -44,4 +46,11 @@ extern void target_float_convert (const gdb_byte *from,
const struct type *from_type,
gdb_byte *to, const struct type *to_type);
extern 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);
extern int target_float_compare (const gdb_byte *x, const struct type *type_x,
const gdb_byte *y, const struct type *type_y);
#endif

View File

@ -24,11 +24,9 @@
#include "expression.h"
#include "target.h"
#include "language.h"
#include "doublest.h"
#include "dfp.h"
#include "target-float.h"
#include <math.h>
#include "infcall.h"
#include "common/byte-vector.h"
/* Define whether or not the C operator '/' truncates towards zero for
differently signed operands (truncation direction is undefined in C). */
@ -845,69 +843,62 @@ uinteger_pow (ULONGEST v1, LONGEST v2)
}
}
/* Obtain decimal value of arguments for binary operation, converting from
other types if one of them is not decimal floating point. */
/* Obtain argument values for binary operation, converting from
other types if one of them is not floating point. */
static void
value_args_as_decimal (struct value *arg1, struct value *arg2,
gdb_byte *x, int *len_x, enum bfd_endian *byte_order_x,
gdb_byte *y, int *len_y, enum bfd_endian *byte_order_y)
value_args_as_target_float (struct value *arg1, struct value *arg2,
gdb_byte *x, struct type **eff_type_x,
gdb_byte *y, struct type **eff_type_y)
{
struct type *type1, *type2;
type1 = check_typedef (value_type (arg1));
type2 = check_typedef (value_type (arg2));
/* At least one of the arguments must be of decimal float type. */
gdb_assert (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
|| TYPE_CODE (type2) == TYPE_CODE_DECFLOAT);
/* At least one of the arguments must be of floating-point type. */
gdb_assert (is_floating_type (type1) || is_floating_type (type2));
if (TYPE_CODE (type1) == TYPE_CODE_FLT
|| TYPE_CODE (type2) == TYPE_CODE_FLT)
if (is_floating_type (type1) && is_floating_type (type2)
&& TYPE_CODE (type1) != TYPE_CODE (type2))
/* The DFP extension to the C language does not allow mixing of
* decimal float types with other float types in expressions
* (see WDTR 24732, page 12). */
error (_("Mixing decimal floating types with "
"other floating types is not allowed."));
/* Obtain decimal value of arg1, converting from other types
if necessary. */
/* Obtain value of arg1, converting from other types if necessary. */
if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT)
if (is_floating_type (type1))
{
*byte_order_x = gdbarch_byte_order (get_type_arch (type1));
*len_x = TYPE_LENGTH (type1);
memcpy (x, value_contents (arg1), *len_x);
*eff_type_x = type1;
memcpy (x, value_contents (arg1), TYPE_LENGTH (type1));
}
else if (is_integral_type (type1))
{
*byte_order_x = gdbarch_byte_order (get_type_arch (type2));
*len_x = TYPE_LENGTH (type2);
*eff_type_x = type2;
if (TYPE_UNSIGNED (type1))
decimal_from_ulongest (value_as_long (arg1), x, *len_x, *byte_order_x);
target_float_from_ulongest (x, *eff_type_x, value_as_long (arg1));
else
decimal_from_longest (value_as_long (arg1), x, *len_x, *byte_order_x);
target_float_from_longest (x, *eff_type_x, value_as_long (arg1));
}
else
error (_("Don't know how to convert from %s to %s."), TYPE_NAME (type1),
TYPE_NAME (type2));
/* Obtain decimal value of arg2, converting from other types
if necessary. */
/* Obtain value of arg2, converting from other types if necessary. */
if (TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
if (is_floating_type (type2))
{
*byte_order_y = gdbarch_byte_order (get_type_arch (type2));
*len_y = TYPE_LENGTH (type2);
memcpy (y, value_contents (arg2), *len_y);
*eff_type_y = type2;
memcpy (y, value_contents (arg2), TYPE_LENGTH (type2));
}
else if (is_integral_type (type2))
{
*byte_order_y = gdbarch_byte_order (get_type_arch (type1));
*len_y = TYPE_LENGTH (type1);
*eff_type_y = type1;
if (TYPE_UNSIGNED (type2))
decimal_from_ulongest (value_as_long (arg2), y, *len_y, *byte_order_y);
target_float_from_ulongest (y, *eff_type_y, value_as_long (arg2));
else
decimal_from_longest (value_as_long (arg2), y, *len_y, *byte_order_y);
target_float_from_longest (y, *eff_type_y, value_as_long (arg2));
}
else
error (_("Don't know how to convert from %s to %s."), TYPE_NAME (type1),
@ -932,111 +923,17 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
type1 = check_typedef (value_type (arg1));
type2 = check_typedef (value_type (arg2));
if ((TYPE_CODE (type1) != TYPE_CODE_FLT
&& TYPE_CODE (type1) != TYPE_CODE_DECFLOAT
&& !is_integral_type (type1))
|| (TYPE_CODE (type2) != TYPE_CODE_FLT
&& TYPE_CODE (type2) != TYPE_CODE_DECFLOAT
&& !is_integral_type (type2)))
if ((!is_floating_value (arg1) && !is_integral_type (type1))
|| (!is_floating_value (arg2) && !is_integral_type (type2)))
error (_("Argument to arithmetic operation not a number or boolean."));
if (TYPE_CODE (type1) == TYPE_CODE_DECFLOAT
|| TYPE_CODE (type2) == TYPE_CODE_DECFLOAT)
if (is_floating_type (type1) || is_floating_type (type2))
{
int len_v1, len_v2, len_v;
enum bfd_endian byte_order_v1, byte_order_v2, byte_order_v;
gdb_byte v1[16], v2[16];
gdb_byte v[16];
/* If only one type is decimal float, use its type.
/* If only one type is floating-point, use its type.
Otherwise use the bigger type. */
if (TYPE_CODE (type1) != TYPE_CODE_DECFLOAT)
if (!is_floating_type (type1))
result_type = type2;
else if (TYPE_CODE (type2) != TYPE_CODE_DECFLOAT)
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
else
result_type = type1;
len_v = TYPE_LENGTH (result_type);
byte_order_v = gdbarch_byte_order (get_type_arch (result_type));
value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
v2, &len_v2, &byte_order_v2);
switch (op)
{
case BINOP_ADD:
case BINOP_SUB:
case BINOP_MUL:
case BINOP_DIV:
case BINOP_EXP:
decimal_binop (op, v1, len_v1, byte_order_v1,
v2, len_v2, byte_order_v2,
v, len_v, byte_order_v);
break;
default:
error (_("Operation not valid for decimal floating point number."));
}
val = value_from_decfloat (result_type, v);
}
else if (TYPE_CODE (type1) == TYPE_CODE_FLT
|| TYPE_CODE (type2) == TYPE_CODE_FLT)
{
/* FIXME-if-picky-about-floating-accuracy: Should be doing this
in target format. real.c in GCC probably has the necessary
code. */
DOUBLEST v1, v2, v = 0;
v1 = value_as_double (arg1);
v2 = value_as_double (arg2);
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."));
}
/* If only one type is float, use its type.
Otherwise use the bigger type. */
if (TYPE_CODE (type1) != TYPE_CODE_FLT)
result_type = type2;
else if (TYPE_CODE (type2) != TYPE_CODE_FLT)
else if (!is_floating_type (type2))
result_type = type1;
else if (TYPE_LENGTH (type2) > TYPE_LENGTH (type1))
result_type = type2;
@ -1044,7 +941,18 @@ scalar_binop (struct value *arg1, struct value *arg2, enum exp_opcode op)
result_type = type1;
val = allocate_value (result_type);
store_typed_floating (value_contents_raw (val), value_type (val), v);
struct type *eff_type_v1, *eff_type_v2;
gdb::byte_vector v1, v2;
v1.resize (TYPE_LENGTH (result_type));
v2.resize (TYPE_LENGTH (result_type));
value_args_as_target_float (arg1, arg2,
v1.data (), &eff_type_v1,
v2.data (), &eff_type_v2);
target_float_binop (op, v1.data (), eff_type_v1,
v2.data (), eff_type_v2,
value_contents_raw (val), result_type);
}
else if (TYPE_CODE (type1) == TYPE_CODE_BOOL
|| TYPE_CODE (type2) == TYPE_CODE_BOOL)
@ -1587,27 +1495,20 @@ value_equal (struct value *arg1, struct value *arg2)
if (is_int1 && is_int2)
return longest_to_int (value_as_long (value_binop (arg1, arg2,
BINOP_EQUAL)));
else if ((code1 == TYPE_CODE_FLT || is_int1)
&& (code2 == TYPE_CODE_FLT || is_int2))
else if ((is_floating_value (arg1) || is_int1)
&& (is_floating_value (arg2) || is_int2))
{
/* NOTE: kettenis/20050816: Avoid compiler bug on systems where
`long double' values are returned in static storage (m68k). */
DOUBLEST d = value_as_double (arg1);
struct type *eff_type_v1, *eff_type_v2;
gdb::byte_vector v1, v2;
v1.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
v2.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
return d == value_as_double (arg2);
}
else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
&& (code2 == TYPE_CODE_DECFLOAT || is_int2))
{
gdb_byte v1[16], v2[16];
int len_v1, len_v2;
enum bfd_endian byte_order_v1, byte_order_v2;
value_args_as_target_float (arg1, arg2,
v1.data (), &eff_type_v1,
v2.data (), &eff_type_v2);
value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
v2, &len_v2, &byte_order_v2);
return decimal_compare (v1, len_v1, byte_order_v1,
v2, len_v2, byte_order_v2) == 0;
return target_float_compare (v1.data (), eff_type_v1,
v2.data (), eff_type_v2) == 0;
}
/* FIXME: Need to promote to either CORE_ADDR or LONGEST, whichever
@ -1683,27 +1584,20 @@ value_less (struct value *arg1, struct value *arg2)
if (is_int1 && is_int2)
return longest_to_int (value_as_long (value_binop (arg1, arg2,
BINOP_LESS)));
else if ((code1 == TYPE_CODE_FLT || is_int1)
&& (code2 == TYPE_CODE_FLT || is_int2))
else if ((is_floating_value (arg1) || is_int1)
&& (is_floating_value (arg2) || is_int2))
{
/* NOTE: kettenis/20050816: Avoid compiler bug on systems where
`long double' values are returned in static storage (m68k). */
DOUBLEST d = value_as_double (arg1);
struct type *eff_type_v1, *eff_type_v2;
gdb::byte_vector v1, v2;
v1.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
v2.resize (std::max (TYPE_LENGTH (type1), TYPE_LENGTH (type2)));
return d < value_as_double (arg2);
}
else if ((code1 == TYPE_CODE_DECFLOAT || is_int1)
&& (code2 == TYPE_CODE_DECFLOAT || is_int2))
{
gdb_byte v1[16], v2[16];
int len_v1, len_v2;
enum bfd_endian byte_order_v1, byte_order_v2;
value_args_as_target_float (arg1, arg2,
v1.data (), &eff_type_v1,
v2.data (), &eff_type_v2);
value_args_as_decimal (arg1, arg2, v1, &len_v1, &byte_order_v1,
v2, &len_v2, &byte_order_v2);
return decimal_compare (v1, len_v1, byte_order_v1,
v2, len_v2, byte_order_v2) == -1;
return target_float_compare (v1.data (), eff_type_v1,
v2.data (), eff_type_v2) == -1;
}
else if (code1 == TYPE_CODE_PTR && code2 == TYPE_CODE_PTR)
return value_as_address (arg1) < value_as_address (arg2);
@ -1733,22 +1627,9 @@ value_pos (struct value *arg1)
arg1 = coerce_ref (arg1);
type = check_typedef (value_type (arg1));
if (TYPE_CODE (type) == TYPE_CODE_FLT)
return value_from_double (type, value_as_double (arg1));
else if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
return value_from_decfloat (type, value_contents (arg1));
else if (is_integral_type (type))
{
return value_from_longest (type, value_as_long (arg1));
}
else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
{
struct value *val = allocate_value (type);
memcpy (value_contents_raw (val), value_contents (arg1),
TYPE_LENGTH (type));
return val;
}
if (is_integral_type (type) || is_floating_value (arg1)
|| (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type)))
return value_from_contents (type, value_contents (arg1));
else
{
error (_("Argument to positive operation not a number."));
@ -1764,28 +1645,8 @@ value_neg (struct value *arg1)
arg1 = coerce_ref (arg1);
type = check_typedef (value_type (arg1));
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
{
struct value *val = allocate_value (type);
int len = TYPE_LENGTH (type);
gdb_byte decbytes[16]; /* a decfloat is at most 128 bits long. */
memcpy (decbytes, value_contents (arg1), len);
if (gdbarch_byte_order (get_type_arch (type)) == BFD_ENDIAN_LITTLE)
decbytes[len-1] = decbytes[len - 1] | 0x80;
else
decbytes[0] = decbytes[0] | 0x80;
memcpy (value_contents_raw (val), decbytes, len);
return val;
}
else if (TYPE_CODE (type) == TYPE_CODE_FLT)
return value_from_double (type, -value_as_double (arg1));
else if (is_integral_type (type))
{
return value_from_longest (type, -value_as_long (arg1));
}
if (is_integral_type (type) || is_floating_type (type))
return value_binop (value_from_longest (type, 0), arg1, BINOP_SUB);
else if (TYPE_CODE (type) == TYPE_CODE_ARRAY && TYPE_VECTOR (type))
{
struct value *tmp, *val = allocate_value (type);