Target FP: Introduce target-float.{c,h}
This patch introduces the new set of target floating-point handling routines
in target-float.{c,h}. In the end, the intention is that this file will
contain support for all operations in target FP format, fully replacing
both the current doublest.{c,h} and dfp.{c,h}.
To begin with, this patch only adds a target_float_is_zero routine,
which handles the equivalent of decimal_is_zero for both binary and
decimal FP. For the binary case, to avoid conversion to DOUBLEST,
this is implemented using the floatformat_classify routine.
However, it turns out that floatformat_classify actually has a bug
(it was not used to check for zero before), so this is fixed as well.
The new routine is used in both value_logical_not and valpy_nonzero.
There is one extra twist: the code previously used value_as_double
to convert to DOUBLEST and then compare against zero. That routine
performs an extra task: it detects invalid floating-point values
and raises an error. In any place where value_as_double is removed
in favor of some target-float.c routine, we need to replace that check.
To keep this check centralized in one place, I've added a new routine
is_floating_value, which returns a boolean determining whether a
value's type is floating point (binary or decimal), and if so, also
performs the validity check. Since we need to check whether a value
is FP before calling any of the target-float routines anyway, this
seems a good place to add the check without much code size overhead.
In some places where we only want to check for floating-point types
and not perform a validity check (e.g. for the *output* of an operation),
we can use the new is_floating_type routine (in gdbarch) instead.
The validity check itself is done by a new target_float_is_valid
routine in target-float, encapsulating floatformat_is_valid.
ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.c (SFILES): Add target-float.c.
(HFILES_NO_SRCDIR): Add target-float.h.
(COMMON_OBS): Add target-float.o.
* target-float.h: New file.
* target-float.c: New file.
* doublest.c (floatformat_classify): Fix detection of float_zero.
* gdbtypes.c (is_floating_type): New function.
* gdbtypes.h (is_floating_type): Add prototype.
* value.c: Do not include "floatformat.h".
(unpack_double): Use target_float_is_valid.
(is_floating_value): New function.
* value.h (is_floating_value): Add prototype-
* valarith.c: Include "target-float.h".
(value_logical_not): Use target_float_is_zero.
* python/py-value.c: Include "target-float.h".
(valpy_nonzero): Use target_float_is_zero.
2017-11-06 15:55:11 +01:00
|
|
|
/* Floating point routines for GDB, the GNU debugger.
|
|
|
|
|
|
|
|
Copyright (C) 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 "dfp.h"
|
|
|
|
#include "doublest.h"
|
|
|
|
#include "gdbtypes.h"
|
|
|
|
#include "floatformat.h"
|
|
|
|
#include "target-float.h"
|
|
|
|
|
|
|
|
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
/* Helper routines operating on binary floating-point data. */
|
|
|
|
|
2017-11-06 15:58:46 +01:00
|
|
|
#include <math.h>
|
|
|
|
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
/* Convert the byte-stream ADDR, interpreted as floating-point format FMT,
|
|
|
|
to an integer value (rounding towards zero). */
|
|
|
|
static LONGEST
|
|
|
|
floatformat_to_longest (const struct floatformat *fmt, const gdb_byte *addr)
|
|
|
|
{
|
|
|
|
DOUBLEST d;
|
|
|
|
floatformat_to_doublest (fmt, addr, &d);
|
|
|
|
return (LONGEST) d;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert signed integer VAL to a target floating-number of format FMT
|
|
|
|
and store it as byte-stream ADDR. */
|
|
|
|
static void
|
|
|
|
floatformat_from_longest (const struct floatformat *fmt, gdb_byte *addr,
|
|
|
|
LONGEST val)
|
|
|
|
{
|
|
|
|
DOUBLEST d = (DOUBLEST) val;
|
|
|
|
floatformat_from_doublest (fmt, &d, addr);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert unsigned integer VAL to a target floating-number of format FMT
|
|
|
|
and store it as byte-stream ADDR. */
|
|
|
|
static void
|
|
|
|
floatformat_from_ulongest (const struct floatformat *fmt, gdb_byte *addr,
|
|
|
|
ULONGEST val)
|
|
|
|
{
|
|
|
|
DOUBLEST d = (DOUBLEST) val;
|
|
|
|
floatformat_from_doublest (fmt, &d, addr);
|
|
|
|
}
|
|
|
|
|
2017-11-06 16:00:47 +01:00
|
|
|
/* Convert the byte-stream ADDR, interpreted as floating-point format FMT,
|
|
|
|
to a floating-point value in the host "double" format. */
|
|
|
|
static double
|
|
|
|
floatformat_to_host_double (const struct floatformat *fmt,
|
|
|
|
const gdb_byte *addr)
|
|
|
|
{
|
|
|
|
DOUBLEST d;
|
|
|
|
floatformat_to_doublest (fmt, addr, &d);
|
|
|
|
return (double) d;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert floating-point value VAL in the host "double" format to a target
|
|
|
|
floating-number of format FMT and store it as byte-stream ADDR. */
|
|
|
|
static void
|
|
|
|
floatformat_from_host_double (const struct floatformat *fmt, gdb_byte *addr,
|
|
|
|
double val)
|
|
|
|
{
|
|
|
|
DOUBLEST d = (DOUBLEST) val;
|
|
|
|
floatformat_from_doublest (fmt, &d, addr);
|
|
|
|
}
|
|
|
|
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
/* Convert a floating-point number of format FROM_FMT from the target
|
|
|
|
byte-stream FROM to a floating-point number of format TO_FMT, and
|
|
|
|
store it to the target byte-stream TO. */
|
|
|
|
static void
|
|
|
|
floatformat_convert (const gdb_byte *from, const struct floatformat *from_fmt,
|
|
|
|
gdb_byte *to, const struct floatformat *to_fmt)
|
|
|
|
{
|
|
|
|
if (from_fmt == to_fmt)
|
|
|
|
{
|
|
|
|
/* The floating-point formats match, so we simply copy the data. */
|
|
|
|
memcpy (to, from, floatformat_totalsize_bytes (to_fmt));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
/* The floating-point formats don't match. The best we can do
|
|
|
|
(apart from simulating the target FPU) is converting to the
|
|
|
|
widest floating-point type supported by the host, and then
|
|
|
|
again to the desired type. */
|
|
|
|
DOUBLEST d;
|
|
|
|
|
|
|
|
floatformat_to_doublest (from_fmt, from, &d);
|
|
|
|
floatformat_from_doublest (to_fmt, &d, to);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-06 15:58:46 +01:00
|
|
|
/* 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;
|
|
|
|
}
|
|
|
|
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
|
Target FP: Introduce target-float.{c,h}
This patch introduces the new set of target floating-point handling routines
in target-float.{c,h}. In the end, the intention is that this file will
contain support for all operations in target FP format, fully replacing
both the current doublest.{c,h} and dfp.{c,h}.
To begin with, this patch only adds a target_float_is_zero routine,
which handles the equivalent of decimal_is_zero for both binary and
decimal FP. For the binary case, to avoid conversion to DOUBLEST,
this is implemented using the floatformat_classify routine.
However, it turns out that floatformat_classify actually has a bug
(it was not used to check for zero before), so this is fixed as well.
The new routine is used in both value_logical_not and valpy_nonzero.
There is one extra twist: the code previously used value_as_double
to convert to DOUBLEST and then compare against zero. That routine
performs an extra task: it detects invalid floating-point values
and raises an error. In any place where value_as_double is removed
in favor of some target-float.c routine, we need to replace that check.
To keep this check centralized in one place, I've added a new routine
is_floating_value, which returns a boolean determining whether a
value's type is floating point (binary or decimal), and if so, also
performs the validity check. Since we need to check whether a value
is FP before calling any of the target-float routines anyway, this
seems a good place to add the check without much code size overhead.
In some places where we only want to check for floating-point types
and not perform a validity check (e.g. for the *output* of an operation),
we can use the new is_floating_type routine (in gdbarch) instead.
The validity check itself is done by a new target_float_is_valid
routine in target-float, encapsulating floatformat_is_valid.
ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* Makefile.c (SFILES): Add target-float.c.
(HFILES_NO_SRCDIR): Add target-float.h.
(COMMON_OBS): Add target-float.o.
* target-float.h: New file.
* target-float.c: New file.
* doublest.c (floatformat_classify): Fix detection of float_zero.
* gdbtypes.c (is_floating_type): New function.
* gdbtypes.h (is_floating_type): Add prototype.
* value.c: Do not include "floatformat.h".
(unpack_double): Use target_float_is_valid.
(is_floating_value): New function.
* value.h (is_floating_value): Add prototype-
* valarith.c: Include "target-float.h".
(value_logical_not): Use target_float_is_zero.
* python/py-value.c: Include "target-float.h".
(valpy_nonzero): Use target_float_is_zero.
2017-11-06 15:55:11 +01:00
|
|
|
/* Typed floating-point routines. These routines operate on floating-point
|
|
|
|
values in target format, represented by a byte buffer interpreted as a
|
|
|
|
"struct type", which may be either a binary or decimal floating-point
|
|
|
|
type (TYPE_CODE_FLT or TYPE_CODE_DECFLOAT). */
|
|
|
|
|
|
|
|
/* Return whether the byte-stream ADDR holds a valid value of
|
|
|
|
floating-point type TYPE. */
|
|
|
|
bool
|
|
|
|
target_float_is_valid (const gdb_byte *addr, const struct type *type)
|
|
|
|
{
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return floatformat_is_valid (floatformat_from_type (type), addr);
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
return true;
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Return whether the byte-stream ADDR, interpreted as floating-point
|
|
|
|
type TYPE, is numerically equal to zero (of either sign). */
|
|
|
|
bool
|
|
|
|
target_float_is_zero (const gdb_byte *addr, const struct type *type)
|
|
|
|
{
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return (floatformat_classify (floatformat_from_type (type), addr)
|
|
|
|
== float_zero);
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
return decimal_is_zero (addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)));
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
2017-11-06 15:56:35 +01:00
|
|
|
/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
|
|
|
|
to a string, optionally using the print format FORMAT. */
|
|
|
|
std::string
|
|
|
|
target_float_to_string (const gdb_byte *addr, const struct type *type,
|
|
|
|
const char *format)
|
|
|
|
{
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return floatformat_to_string (floatformat_from_type (type), addr, format);
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
return decimal_to_string (addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)),
|
|
|
|
format);
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Parse string STRING into a target floating-number of type TYPE and
|
|
|
|
store it as byte-stream ADDR. Return whether parsing succeeded. */
|
|
|
|
bool
|
|
|
|
target_float_from_string (gdb_byte *addr, const struct type *type,
|
|
|
|
const std::string &string)
|
|
|
|
{
|
|
|
|
/* Ensure possible padding bytes in the target buffer are zeroed out. */
|
|
|
|
memset (addr, 0, TYPE_LENGTH (type));
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return floatformat_from_string (floatformat_from_type (type), addr,
|
|
|
|
string);
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
return decimal_from_string (addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)),
|
|
|
|
string);
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
|
|
|
|
/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
|
|
|
|
to an integer value (rounding towards zero). */
|
|
|
|
LONGEST
|
|
|
|
target_float_to_longest (const gdb_byte *addr, const struct type *type)
|
|
|
|
{
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return floatformat_to_longest (floatformat_from_type (type), addr);
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
return decimal_to_longest (addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)));
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert signed integer VAL to a target floating-number of type TYPE
|
|
|
|
and store it as byte-stream ADDR. */
|
|
|
|
void
|
|
|
|
target_float_from_longest (gdb_byte *addr, const struct type *type,
|
|
|
|
LONGEST val)
|
|
|
|
{
|
|
|
|
/* Ensure possible padding bytes in the target buffer are zeroed out. */
|
|
|
|
memset (addr, 0, TYPE_LENGTH (type));
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
{
|
|
|
|
floatformat_from_longest (floatformat_from_type (type), addr, val);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
{
|
|
|
|
decimal_from_longest (val, addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert unsigned integer VAL to a target floating-number of type TYPE
|
|
|
|
and store it as byte-stream ADDR. */
|
|
|
|
void
|
|
|
|
target_float_from_ulongest (gdb_byte *addr, const struct type *type,
|
|
|
|
ULONGEST val)
|
|
|
|
{
|
|
|
|
/* Ensure possible padding bytes in the target buffer are zeroed out. */
|
|
|
|
memset (addr, 0, TYPE_LENGTH (type));
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
{
|
|
|
|
floatformat_from_ulongest (floatformat_from_type (type), addr, val);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_DECFLOAT)
|
|
|
|
{
|
|
|
|
decimal_from_ulongest (val, addr, TYPE_LENGTH (type),
|
|
|
|
gdbarch_byte_order (get_type_arch (type)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
2017-11-06 16:00:47 +01:00
|
|
|
/* Convert the byte-stream ADDR, interpreted as floating-point type TYPE,
|
|
|
|
to a floating-point value in the host "double" format. */
|
|
|
|
double
|
|
|
|
target_float_to_host_double (const gdb_byte *addr,
|
|
|
|
const struct type *type)
|
|
|
|
{
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
return floatformat_to_host_double (floatformat_from_type (type), addr);
|
|
|
|
|
|
|
|
/* We don't support conversions between target decimal floating-point
|
|
|
|
types and the host double type here. */
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Convert floating-point value VAL in the host "double" format to a target
|
|
|
|
floating-number of type TYPE and store it as byte-stream ADDR. */
|
|
|
|
void
|
|
|
|
target_float_from_host_double (gdb_byte *addr, const struct type *type,
|
|
|
|
double val)
|
|
|
|
{
|
|
|
|
/* Ensure possible padding bytes in the target buffer are zeroed out. */
|
|
|
|
memset (addr, 0, TYPE_LENGTH (type));
|
|
|
|
|
|
|
|
if (TYPE_CODE (type) == TYPE_CODE_FLT)
|
|
|
|
{
|
|
|
|
floatformat_from_host_double (floatformat_from_type (type), addr, val);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We don't support conversions between target decimal floating-point
|
|
|
|
types and the host double type here. */
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
|
|
|
|
Target FP: Add conversion routines to target-float.{c,h}
This patch adds the following conversion routines:
- target_float_to_longest
- target_float_from_longest
- target_float_from_ulongest
- target_float_convert
which call the equivalent decimal_ routines to handle decimal FP,
and call helper routines that currently still go via DOUBLEST to
handle binary FP.
The target_float_convert routine not only handles BFP<->BFP and
DFP<->DFP conversions, but also BFP<->DFP, which are implemented
by converting to a string and back.
These helpers are used in particular to implement conversion
from and to FP in value_cast, without going through DOUBLEST there.
In order to implement this for the FP<-integer case, the
pack_long / pack_unsigned_long routines are extended to support
floating-point values as output (thereby allowing use of
value_from_[u]longest with a floating-point target type).
This latter change also allows simplification of value_one.
gdb/ChangeLog:
2017-11-06 Ulrich Weigand <uweigand@de.ibm.com>
* target-float.c (floatformat_to_longest): New function.
(floatformat_from_longest, floatformat_from_ulongest): Likewise.
(floatformat_convert): Likewise.
(target_float_to_longest): Likewise.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* target-float.h (target_float_to_longest): Add prototype.
(target_float_from_longest, target_float_from_ulongest): Likewise.
(target_float_convert): Likewise.
* value.c (unpack_long): Use target_float_to_longest.
(pack_long): Allow FP types. Use target_float_from_longest.
(pack_unsigned_long): Likewise using target_float_from_ulongest.
* valops.c: Include "target-float.h". Do not include "dfp.h".
(value_cast): Handle conversions to FP using target_float_convert,
value_from_ulongest, and value_from_longest.
(value_one): Use value_from_longest for FP types as well.
2017-11-06 15:57:31 +01:00
|
|
|
/* Convert a floating-point number of type FROM_TYPE from the target
|
|
|
|
byte-stream FROM to a floating-point number of type TO_TYPE, and
|
|
|
|
store it to the target byte-stream TO. */
|
|
|
|
void
|
|
|
|
target_float_convert (const gdb_byte *from, const struct type *from_type,
|
|
|
|
gdb_byte *to, const struct type *to_type)
|
|
|
|
{
|
|
|
|
/* Ensure possible padding bytes in the target buffer are zeroed out. */
|
|
|
|
memset (to, 0, TYPE_LENGTH (to_type));
|
|
|
|
|
|
|
|
/* Use direct conversion routines if we have them. */
|
|
|
|
|
|
|
|
if (TYPE_CODE (from_type) == TYPE_CODE_FLT
|
|
|
|
&& TYPE_CODE (to_type) == TYPE_CODE_FLT)
|
|
|
|
{
|
|
|
|
floatformat_convert (from, floatformat_from_type (from_type),
|
|
|
|
to, floatformat_from_type (to_type));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (TYPE_CODE (from_type) == TYPE_CODE_DECFLOAT
|
|
|
|
&& TYPE_CODE (to_type) == TYPE_CODE_DECFLOAT)
|
|
|
|
{
|
|
|
|
decimal_convert (from, TYPE_LENGTH (from_type),
|
|
|
|
gdbarch_byte_order (get_type_arch (from_type)),
|
|
|
|
to, TYPE_LENGTH (to_type),
|
|
|
|
gdbarch_byte_order (get_type_arch (to_type)));
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* We cannot directly convert between binary and decimal floating-point
|
|
|
|
types, so go via an intermediary string. */
|
|
|
|
|
|
|
|
if ((TYPE_CODE (from_type) == TYPE_CODE_FLT
|
|
|
|
&& TYPE_CODE (to_type) == TYPE_CODE_DECFLOAT)
|
|
|
|
|| (TYPE_CODE (from_type) == TYPE_CODE_DECFLOAT
|
|
|
|
&& TYPE_CODE (to_type) == TYPE_CODE_FLT))
|
|
|
|
{
|
|
|
|
std::string str = target_float_to_string (from, from_type);
|
|
|
|
target_float_from_string (to, to_type, str);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
gdb_assert_not_reached ("unexpected type code");
|
|
|
|
}
|
2017-11-06 15:58:46 +01:00
|
|
|
|
|
|
|
/* 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");
|
|
|
|
}
|
|
|
|
|