diff options
author | Aldy Hernandez <aldyh@redhat.com> | 2022-09-14 08:32:34 +0200 |
---|---|---|
committer | Aldy Hernandez <aldyh@redhat.com> | 2022-09-18 09:03:17 +0200 |
commit | 917461478d3bb733a64bc21876811d017c555b3c (patch) | |
tree | c07d32f70967fcd5e4f78a469b18d7b879460cf8 /gcc/range-op-float.cc | |
parent | 205a6fb2a0c5285e77a4f25da36d0a1af7ab104a (diff) | |
download | gcc-917461478d3bb733a64bc21876811d017c555b3c.zip gcc-917461478d3bb733a64bc21876811d017c555b3c.tar.gz gcc-917461478d3bb733a64bc21876811d017c555b3c.tar.bz2 |
Rewrite NAN and sign handling in frange
The attatched patch rewrites the NAN and sign handling, dropping both
tristates in favor of a pair of boolean flags for NANs, and nothing at
all for signs. The signs are tracked in the range itself, so now it's
possible to describe things like [-0.0, +0.0] +NAN, [+0, +0], [-5, +0],
[+0, 3] -NAN, etc.
Here is an example of the various ranges and how they are displayed:
[frange] float VARYING NAN ;; Varying includes NAN
[frange] UNDEFINED ;; Empty set as always
[frange] float [] +-NAN ;; Unknown sign NAN
[frange] float [] -NAN ;; -NAN
[frange] float [] +NAN ;; +NAN
[frange] float [-0.0, 0.0] ;; All zeros.
[frange] float [-0.0, -0.0] +-NAN ;; -0 or NAN.
[frange] float [-5.0e+0, -1.0e+0] +NAN ;; [-5, -1] or +NAN
[frange] float [-5.0e+0, -0.0] +-NAN ;; [-5, -0] or NAN
[frange] float [-5.0e+0, -0.0] ;; [-5, -0]
[frange] float [5.0e+0, 1.0e+1] ;; [5, 10]
Notice the NAN signs are decoupled from the range, so we can represent
a negative range with a positive NAN. For this range,
frange::signbit_p() would return false, as only when the signs of the
NANs and range agree can we be certain.
There is no longer any pessimization of ranges for intersects
involving NANs. Also, union and intersect work with signed zeros:
// [-0, x] U [+0, x] => [-0, x]
// [ x, -0] U [ x, +0] => [ x, +0]
// [-0, x] ^ [+0, x] => [+0, x]
// [ x, -0] ^ [ x, +0] => [ x, -0]
The special casing for signed zeros in the singleton code is gone in
favor of just making sure the signs in the range agree, that is
[-0, -0] for example.
I have removed the idea that a known NAN is a "range", so a NAN is no
longer in the endpoints itself. Requesting the bound of a known NAN
is a hard fail. For that matter, we don't store the actual NAN in the
range. The only information we have are the set of boolean flags.
This way we make sure nothing seeps into the frange. This also means
it's explicit that we don't track anything but the sign in NANs. We
can revisit this if we desire to track signalling or whatever
concoction y'all can imagine.
Regstrapped with mpfr tests on x86-64 and ppc64le Linux. Selftests
were also run with -ffinite-math-only on x86-64.
At Jakub's suggestion, I built lapack with associated tests. They
pass on x86-64 and ppc64le Linux with no regressions from mainline.
As a sanity check, I also ran them for -ffinite-math-only on x86 which
(as expected) returned:
NaN arithmetic did not perform per the ieee spec
Otherwise, all tests pass for -ffinite-math-only.
gcc/ChangeLog:
* range-op-float.cc (frange_add_zeros): Replace set_signbit with
union of zero.
* value-query.cc (range_query::get_tree_range): Remove set_signbit
use.
* value-range-pretty-print.cc (vrange_printer::print_frange_prop):
Remove.
(vrange_printer::print_frange_nan): New.
* value-range-pretty-print.h (print_frange_prop): Remove.
(print_frange_nan): New.
* value-range-storage.cc (frange_storage_slot::set_frange): Set
kind and NAN fields.
(frange_storage_slot::get_frange): Restore kind and NAN fields.
* value-range-storage.h (class frange_storage_slot): Add kind and
NAN fields.
* value-range.cc (frange::update_nan): Remove.
(frange::set_signbit): Remove.
(frange::set): Adjust for NAN fields.
(frange::normalize_kind): Remove m_props.
(frange::combine_zeros): New.
(frange::union_nans): New.
(frange::union_): Handle new NAN fields.
(frange::intersect_nans): New.
(frange::intersect): Handle new NAN fields.
(frange::operator=): Same.
(frange::operator==): Same.
(frange::contains_p): Same.
(frange::singleton_p): Remove special case for signed zeros.
(frange::verify_range): Adjust for new NAN fields.
(frange::set_zero): Handle signed zeros.
(frange::set_nonnegative): Same.
(range_tests_nan): Adjust tests.
(range_tests_signed_zeros): Same.
(range_tests_signbit): Same.
(range_tests_floats): Same.
* value-range.h (class fp_prop): Remove.
(FP_PROP_ACCESSOR): Remove.
(class frange_props): Remove
(frange::lower_bound): NANs don't have endpoints.
(frange::upper_bound): Same.
(frange_props::operator==): Remove.
(frange_props::union_): Remove.
(frange_props::intersect): Remove.
(frange::update_nan): New.
(frange::clear_nan): New.
(frange::undefined_p): New.
(frange::set_nan): New.
(frange::known_finite): Adjust for new NAN representation.
(frange::maybe_isnan): Same.
(frange::known_isnan): Same.
(frange::signbit_p): Same.
* gimple-range-fold.cc (range_of_builtin_int_call): Rename
known_signbit_p into signbit_p.
Diffstat (limited to 'gcc/range-op-float.cc')
-rw-r--r-- | gcc/range-op-float.cc | 38 |
1 files changed, 21 insertions, 17 deletions
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc index fbc14a7..1e39a07 100644 --- a/gcc/range-op-float.cc +++ b/gcc/range-op-float.cc @@ -155,7 +155,7 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons static inline bool finite_operand_p (const frange &op1) { - return flag_finite_math_only || !op1.maybe_nan (); + return flag_finite_math_only || !op1.maybe_isnan (); } // Return TRUE if OP1 and OP2 are known to be free of NANs. @@ -163,7 +163,7 @@ finite_operand_p (const frange &op1) static inline bool finite_operands_p (const frange &op1, const frange &op2) { - return flag_finite_math_only || (!op1.maybe_nan () && !op2.maybe_nan ()); + return flag_finite_math_only || (!op1.maybe_isnan () && !op2.maybe_isnan ()); } // Floating version of relop_early_resolve that takes into account NAN @@ -213,12 +213,16 @@ frange_drop_ninf (frange &r, tree type) static inline void frange_add_zeros (frange &r, tree type) { - if (r.undefined_p () || r.known_nan ()) + if (r.undefined_p () || r.known_isnan ()) return; if (HONOR_SIGNED_ZEROS (type) && (real_iszero (&r.lower_bound ()) || real_iszero (&r.upper_bound ()))) - r.set_signbit (fp_prop::VARYING); + { + frange zero; + zero.set_zero (type); + r.union_ (zero); + } } // Build a range that is <= VAL and store it in R. @@ -226,7 +230,7 @@ frange_add_zeros (frange &r, tree type) static bool build_le (frange &r, tree type, const frange &val) { - if (val.known_nan ()) + if (val.known_isnan ()) { r.set_undefined (); return false; @@ -244,7 +248,7 @@ build_le (frange &r, tree type, const frange &val) static bool build_lt (frange &r, tree type, const frange &val) { - if (val.known_nan ()) + if (val.known_isnan ()) { r.set_undefined (); return false; @@ -268,7 +272,7 @@ build_lt (frange &r, tree type, const frange &val) static bool build_ge (frange &r, tree type, const frange &val) { - if (val.known_nan ()) + if (val.known_isnan ()) { r.set_undefined (); return false; @@ -286,7 +290,7 @@ build_ge (frange &r, tree type, const frange &val) static bool build_gt (frange &r, tree type, const frange &val) { - if (val.known_nan ()) + if (val.known_isnan ()) { r.set_undefined (); return false; @@ -551,7 +555,7 @@ foperator_lt::fold_range (irange &r, tree type, else r = range_true_and_false (type); } - else if (op1.known_nan () || op2.known_nan ()) + else if (op1.known_isnan () || op2.known_isnan ()) r = range_false (type); else r = range_true_and_false (type); @@ -653,7 +657,7 @@ foperator_le::fold_range (irange &r, tree type, else r = range_true_and_false (type); } - else if (op1.known_nan () || op2.known_nan ()) + else if (op1.known_isnan () || op2.known_isnan ()) r = range_false (type); else r = range_true_and_false (type); @@ -747,7 +751,7 @@ foperator_gt::fold_range (irange &r, tree type, else r = range_true_and_false (type); } - else if (op1.known_nan () || op2.known_nan ()) + else if (op1.known_isnan () || op2.known_isnan ()) r = range_false (type); else r = range_true_and_false (type); @@ -849,7 +853,7 @@ foperator_ge::fold_range (irange &r, tree type, else r = range_true_and_false (type); } - else if (op1.known_nan () || op2.known_nan ()) + else if (op1.known_isnan () || op2.known_isnan ()) r = range_false (type); else r = range_true_and_false (type); @@ -932,10 +936,10 @@ foperator_unordered::fold_range (irange &r, tree type, relation_kind) const { // UNORDERED is TRUE if either operand is a NAN. - if (op1.known_nan () || op2.known_nan ()) + if (op1.known_isnan () || op2.known_isnan ()) r = range_true (type); // UNORDERED is FALSE if neither operand is a NAN. - else if (!op1.maybe_nan () && !op2.maybe_nan ()) + else if (!op1.maybe_isnan () && !op2.maybe_isnan ()) r = range_false (type); else r = range_true_and_false (type); @@ -954,7 +958,7 @@ foperator_unordered::op1_range (frange &r, tree type, r.set_varying (type); // Since at least one operand must be NAN, if one of them is // not, the other must be. - if (!op2.maybe_nan ()) + if (!op2.maybe_isnan ()) r.set_nan (type); break; @@ -998,9 +1002,9 @@ foperator_ordered::fold_range (irange &r, tree type, const frange &op1, const frange &op2, relation_kind) const { - if (!op1.maybe_nan () && !op2.maybe_nan ()) + if (!op1.maybe_isnan () && !op2.maybe_isnan ()) r = range_true (type); - else if (op1.known_nan () || op2.known_nan ()) + else if (op1.known_isnan () || op2.known_isnan ()) r = range_false (type); else r = range_true_and_false (type); |