Target FP: Refactor use of host floating-point arithmetic
authorUlrich Weigand <ulrich.weigand@de.ibm.com>
Wed, 22 Nov 2017 12:51:49 +0000 (13:51 +0100)
committerUlrich Weigand <ulrich.weigand@de.ibm.com>
Wed, 22 Nov 2017 12:51:49 +0000 (13:51 +0100)
commit7a26362d360ee34de1e8e927bd7159860fe8354b
tree340627144f1083206a7a8af4f060d7cfdb0bcd56
parenta9f26f609e3a1b6ae3aab300b55442e0a81e2bce
Target FP: Refactor use of host floating-point arithmetic

Prepare for using MPFR to implement floating-point arithmetic by
refactoring the way host floating-point arithmetic is currently used.

In particular, fix the following two problems that cause different
(and incorrect) results due to using host arithmetic:

- Current processing always uses host "long double", and then converts
  back to the actual target format.  This may introduce rounding errors.

- Conversion of FP values to LONGEST simply does a host C++ type cast.
  However the result of such a cast is undefined if the source value
  is outside the representable range.  MPFR always has defined behavior
  here (returns the minimum or maximum representable value).

To fix the first issue, I've now created not just one set of routines
using host FP arithmetic (on long double), but instead three different
sets of routines, one each for host float, double, and long double.
Operations can then be performed in the desired type directly, avoiding
the extra rounding step.  Using C++ templates, the three sets can all
share the same source code without duplication.

To fix the second issue, I'm simply enforcing the same conversion rule
(which makes sense anyway) when converting out-of-range values from
FP to LONGEST.

To contain the code complexity with the variety of options now possible,
I've created a new class "target_float_ops".  There are a total of five
separate implementations of this:

  host_float_ops<float>        Implemented via host FP in given type
  host_float_ops<double>
  host_float_ops<long double>
  mpfr_float_ops               Implemented via MPFR if available
  decimal_float_ops            Implemented via libdecnumber

Note instead of using the DOUBLEST define, this always just uses the
"long double" data type.  But since we now require C++11 anyway, this
type must in any case be avaialble unconditionally.

Most target floating-point operations simply dispatch to a (virtual)
member routine of this class.  Which implementation to choose is
determined from the target types involved, and whether they match
some host type or not.  E.g. any operation on a single type that
matches a host type is performed in that type.  Operations involving
two types that both match host types are performed in the larger one
(according to C/C++ implicit conversion rules).  Operations that
involve any type that does not match a host type are performed using
MPFR.  (And of course operations involving decimal FP are performed
using libdecnumber.)

This first patch implements the refactoring of target-float.c as
described above, introduing the host_float_ops and decimal_float_ops
classes, and using them.  Use of MPFR is introduced in the second patch.
A bit of special-case handling code is moved around to as to avoid
code duplication between host_float_ops and mpfr_float_ops.

Note that due to the changes mentioned above, I've had to update (fix)
the floating-point register values tested in the gdb.arch/vsx-regs.exp
test case.  (The new values now work both with host arithmetic and MPFR.)

gdb/ChangeLog:
2017-11-22  Ulrich Weigand  <uweigand@de.ibm.com>

* target-float.c: Do not include <math.h>.
Include <cmath> and <limits>.
(DOUBLEST): Do not define.
(class target_float_ops): New type.
(class host_float_ops): New templated type.
(class decimal_float_ops): New type.

(floatformat_to_doublest): Rename to ...
(host_float_ops<T>::from_target): ... this.  Use template type T
instead of DOUBLEST.  Use C++ math routines.  Update recursive calls.
(host_float_ops<T>::from_target): New overload using a type argument.
(floatformat_from_doublest): Rename to ...
(host_float_ops<T>::to_target): ... this.  Use template type T
instead of DOUBLEST.  Use C++ math routines.  Update recursive calls.
(host_float_ops<T>::to_target): New overload using a type argument.
(floatformat_printf_format): New function.
(struct printf_length_modifier): New templated type.
(floatformat_to_string): Rename to ...
(host_float_ops<T>::to_string): ... this.  Use type instead of
floatformat argument.  Use floatformat_printf_format and
printf_length_modifier.  Remove special handling of invalid numbers,
infinities and NaN (moved to target_float_to_string).
(struct scanf_length_modifier): New templated type.
(floatformat_from_string): Rename to ...
(host_float_ops<T>::from_string): ... this.  Use type instead of
floatformat argument.  Use scanf_length_modifier.
(floatformat_to_longest): Rename to ...
(host_float_ops<T>::to_longest): ... this.  Use type instead of
floatformat argument.  Handle out-of-range values deterministically.
(floatformat_from_longest): Rename to ...
(host_float_ops<T>::from_longest): ... this.  Use type instead of
floatformat argument.
(floatformat_from_ulongest): Rename to ...
(host_float_ops<T>::from_ulongest): ... this.  Use type instead of
floatformat argument.
(floatformat_to_host_double): Rename to ...
(host_float_ops<T>::to_host_double): ... this.  Use type instead of
floatformat argument.
(floatformat_from_host_double): Rename to ...
(host_float_ops<T>::from_host_double): ... this.  Use type instead of
floatformat argument.
(floatformat_convert): Rename to ...
(host_float_ops<T>::convert): ... this.  Use type instead of
floatformat arguments.  Remove handling of no-op conversions.
(floatformat_binop): Rename to ...
(host_float_ops<T>::binop): ... this.  Use type instead of
floatformat arguments.
(floatformat_compare): Rename to ...
(host_float_ops<T>::compare): ... this.  Use type instead of
floatformat arguments.

(match_endianness): Use type instead of length/byte_order arguments.
(set_decnumber_context): Likewise.
(decimal_from_number): Likewise.  Update calls.
(decimal_to_number): Likewise.
(decimal_is_zero): Likewise.  Update calls.  Move to earlier in file.
(decimal_float_ops::to_host_double): New dummy function.
(decimal_float_ops::from_host_double): Likewise.
(decimal_to_string): Rename to ...
(decimal_float_ops::to_string): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_from_string): Rename to ...
(decimal_float_ops::from_string): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_from_longest): Rename to ...
(decimal_float_ops::from_longest): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_from_ulongest): Rename to ...
(decimal_float_ops::from_ulongest): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_to_longest): Rename to ...
(decimal_float_ops::to_longest): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_binop): Rename to ...
(decimal_float_ops::binop): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_compare): Rename to ...
(decimal_float_ops::compare): ... this.  Use type instead of
length/byte_order arguments.  Update calls.
(decimal_convert): Rename to ...
(decimal_float_ops::convert): ... this.  Use type instead of
length/byte_order arguments.  Update calls.

(target_float_same_category_p): New function.
(target_float_same_format_p): Likewise.
(target_float_format_length): Likewise.
(enum target_float_ops_kind): New type.
(get_target_float_ops_kind): New function.
(get_target_float_ops): Three new overloaded functions.

(target_float_is_zero): Update call.
(target_float_to_string): Add special handling of invalid numbers,
infinities and NaN (moved from floatformat_to_string).  Use
target_float_ops callback.
(target_float_from_string): Use target_float_ops callback.
(target_float_to_longest): Likewise.
(target_float_from_longest): Likewise.
(target_float_from_ulongest): Likewise.
(target_float_to_host_double): Likewise.
(target_float_from_host_double): Likewise.
(target_float_convert): Add special case for no-op conversions.
Use target_float_ops callback.
(target_float_binop): Use target_float_ops callback.
(target_float_compare): Likewise.

gdb/testsuite/ChangeLog:
2017-11-22  Ulrich Weigand  <uweigand@de.ibm.com>

* gdb.arch/vsx-regs.exp: Update register content checks.
gdb/ChangeLog
gdb/target-float.c
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.arch/vsx-regs.exp