diff options
Diffstat (limited to 'gcc')
-rw-r--r-- | gcc/gimple-range-fold.cc | 2 | ||||
-rw-r--r-- | gcc/range-op-float.cc | 38 | ||||
-rw-r--r-- | gcc/value-query.cc | 11 | ||||
-rw-r--r-- | gcc/value-range-pretty-print.cc | 45 | ||||
-rw-r--r-- | gcc/value-range-pretty-print.h | 2 | ||||
-rw-r--r-- | gcc/value-range-storage.cc | 9 | ||||
-rw-r--r-- | gcc/value-range-storage.h | 7 | ||||
-rw-r--r-- | gcc/value-range.cc | 621 | ||||
-rw-r--r-- | gcc/value-range.h | 231 |
9 files changed, 481 insertions, 485 deletions
diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc index 85ed6f9..a45fc7a 100644 --- a/gcc/gimple-range-fold.cc +++ b/gcc/gimple-range-fold.cc @@ -1030,7 +1030,7 @@ fold_using_range::range_of_builtin_int_call (irange &r, gcall *call, if (src.get_operand (tmp, arg)) { bool signbit; - if (tmp.known_signbit (signbit)) + if (tmp.signbit_p (signbit)) { if (signbit) r.set_nonzero (type); 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); diff --git a/gcc/value-query.cc b/gcc/value-query.cc index ea6e4b9..0bdd670 100644 --- a/gcc/value-query.cc +++ b/gcc/value-query.cc @@ -219,17 +219,8 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt) { frange &f = as_a <frange> (r); f.set (expr, expr); - - // Singletons from the tree world have known properties. - REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (expr); - if (real_isnan (rv)) - f.update_nan (fp_prop::YES); - else + if (!real_isnan (TREE_REAL_CST_PTR (expr))) f.clear_nan (); - if (real_isneg (rv)) - f.set_signbit (fp_prop::YES); - else - f.set_signbit (fp_prop::NO); return true; } diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc index b124e46..eb74422 100644 --- a/gcc/value-range-pretty-print.cc +++ b/gcc/value-range-pretty-print.cc @@ -134,34 +134,39 @@ vrange_printer::visit (const frange &r) const if (r.varying_p ()) { pp_string (pp, "VARYING"); + print_frange_nan (r); return; } pp_character (pp, '['); - dump_generic_node (pp, - build_real (type, r.lower_bound ()), 0, TDF_NONE, false); - pp_string (pp, ", "); - dump_generic_node (pp, - build_real (type, r.upper_bound ()), 0, TDF_NONE, false); - pp_string (pp, "] "); - - print_frange_prop ("NAN", r.get_nan ()); - print_frange_prop ("SIGN", r.get_signbit ()); + bool has_endpoints = !r.known_isnan (); + if (has_endpoints) + { + dump_generic_node (pp, + build_real (type, r.lower_bound ()), 0, TDF_NONE, false); + pp_string (pp, ", "); + dump_generic_node (pp, + build_real (type, r.upper_bound ()), 0, TDF_NONE, false); + } + pp_character (pp, ']'); + print_frange_nan (r); } -// Print the FP properties in an frange. +// Print the NAN info for an frange. void -vrange_printer::print_frange_prop (const char *str, const fp_prop &prop) const +vrange_printer::print_frange_nan (const frange &r) const { - if (prop.varying_p ()) - return; - - if (prop.yes_p ()) - pp_string (pp, str); - else if (prop.no_p ()) + if (r.maybe_isnan ()) { - pp_character (pp, '!'); - pp_string (pp, str); + if (r.m_pos_nan && r.m_neg_nan) + { + pp_string (pp, " +-NAN"); + return; + } + bool nan_sign = r.m_neg_nan; + if (nan_sign) + pp_string (pp, " -NAN"); + else + pp_string (pp, " +NAN"); } - pp_character (pp, ' '); } diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h index ad06c93..20c2659 100644 --- a/gcc/value-range-pretty-print.h +++ b/gcc/value-range-pretty-print.h @@ -31,7 +31,7 @@ public: private: void print_irange_bound (const wide_int &w, tree type) const; void print_irange_bitmasks (const irange &) const; - void print_frange_prop (const char *str, const fp_prop &) const; + void print_frange_nan (const frange &) const; pretty_printer *pp; }; diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc index b7a23fa..de7575e 100644 --- a/gcc/value-range-storage.cc +++ b/gcc/value-range-storage.cc @@ -253,9 +253,11 @@ frange_storage_slot::set_frange (const frange &r) gcc_checking_assert (fits_p (r)); gcc_checking_assert (!r.undefined_p ()); + m_kind = r.m_kind; m_min = r.m_min; m_max = r.m_max; - m_props = r.m_props; + m_pos_nan = r.m_pos_nan; + m_neg_nan = r.m_neg_nan; } void @@ -264,11 +266,12 @@ frange_storage_slot::get_frange (frange &r, tree type) const gcc_checking_assert (r.supports_type_p (type)); r.set_undefined (); - r.m_kind = VR_RANGE; - r.m_props = m_props; + r.m_kind = m_kind; r.m_type = type; r.m_min = m_min; r.m_max = m_max; + r.m_pos_nan = m_pos_nan; + r.m_neg_nan = m_neg_nan; r.normalize_kind (); if (flag_checking) diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h index f506789..0cf95eb 100644 --- a/gcc/value-range-storage.h +++ b/gcc/value-range-storage.h @@ -113,12 +113,11 @@ class GTY (()) frange_storage_slot frange_storage_slot (const frange &r) { set_frange (r); } DISABLE_COPY_AND_ASSIGN (frange_storage_slot); - // We can get away with just storing the properties and the - // endpoints because the type can be gotten from the SSA, and - // UNDEFINED is unsupported, so it can only be a VR_RANGE. + enum value_range_kind m_kind; REAL_VALUE_TYPE m_min; REAL_VALUE_TYPE m_max; - frange_props m_props; + bool m_pos_nan; + bool m_neg_nan; }; class obstack_vrange_allocator final: public vrange_allocator diff --git a/gcc/value-range.cc b/gcc/value-range.cc index d759fcf..55a216e 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -267,106 +267,24 @@ tree_compare (tree_code code, tree op1, tree op2) return !integer_zerop (fold_build2 (code, integer_type_node, op1, op2)); } -// Set the NAN property. Adjust the range if appopriate. - -void -frange::update_nan (fp_prop::kind k) -{ - if (k == fp_prop::YES) - { - if (!maybe_nan ()) - { - set_undefined (); - return; - } - gcc_checking_assert (!undefined_p ()); - set_nan (m_type); - return; - } - - if (k == fp_prop::NO && known_nan ()) - { - set_undefined (); - return; - } - - // Setting VARYING on an obviously NAN range is a no-op. - if (k == fp_prop::VARYING && real_isnan (&m_min)) - return; - - m_props.set_nan (k); - normalize_kind (); - if (flag_checking) - verify_range (); -} - -// Set the SIGNBIT property. Adjust the range if appropriate. - -void -frange::set_signbit (fp_prop::kind k) -{ - gcc_checking_assert (m_type); - - // No additional adjustments are needed for a NAN. - if (known_nan ()) - { - m_props.set_signbit (k); - return; - } - // Ignore sign changes when they're set correctly. - if (!maybe_nan ()) - { - // It's negative and we're trying to make it negative or varying. - if (real_less (&m_max, &dconst0) && (k == fp_prop::YES - || k == fp_prop::VARYING)) - return; - // It's positive and we're trying to make it positive or varying. - if (real_less (&dconst0, &m_min) && (k == fp_prop::NO - || k == fp_prop::VARYING)) - return; - } - // Adjust the range depending on the sign bit. - if (k == fp_prop::YES) - { - // Crop the range to [-INF, 0]. - frange crop (m_type, dconstninf, dconst0); - intersect (crop); - if (!undefined_p ()) - m_props.set_signbit (fp_prop::YES); - } - else if (k == fp_prop::NO) - { - // Crop the range to [0, +INF]. - frange crop (m_type, dconst0, dconstinf); - intersect (crop); - if (!undefined_p ()) - m_props.set_signbit (fp_prop::NO); - } - else - { - m_props.set_signbit (fp_prop::VARYING); - normalize_kind (); - } - - if (flag_checking) - verify_range (); -} - // Setter for franges. void frange::set (tree min, tree max, value_range_kind kind) { - if (kind == VR_UNDEFINED) + switch (kind) { + case VR_UNDEFINED: set_undefined (); return; - } - // Treat VR_ANTI_RANGE and VR_VARYING as varying. - if (kind != VR_RANGE) - { + case VR_VARYING: + case VR_ANTI_RANGE: set_varying (TREE_TYPE (min)); return; + case VR_RANGE: + break; + default: + gcc_unreachable (); } // Handle NANs. @@ -375,24 +293,25 @@ frange::set (tree min, tree max, value_range_kind kind) gcc_checking_assert (real_identical (TREE_REAL_CST_PTR (min), TREE_REAL_CST_PTR (max))); tree type = TREE_TYPE (min); - set_nan (type); + bool sign = real_isneg (TREE_REAL_CST_PTR (min)); + set_nan (type, sign); return; } m_kind = kind; m_type = TREE_TYPE (min); - m_props.set_varying (); m_min = *TREE_REAL_CST_PTR (min); m_max = *TREE_REAL_CST_PTR (max); - - // Set SIGNBIT property for positive and negative ranges. - if (real_less (&m_max, &dconst0)) - m_props.signbit_set_yes (); - else if (real_less (&dconst0, &m_min)) - m_props.signbit_set_no (); - - if (!HONOR_NANS (m_type)) - m_props.nan_set_no (); + if (HONOR_NANS (m_type)) + { + m_pos_nan = true; + m_neg_nan = true; + } + else + { + m_pos_nan = false; + m_neg_nan = false; + } // Check for swapped ranges. gcc_checking_assert (tree_compare (LE_EXPR, min, max)); @@ -423,18 +342,11 @@ frange::set (tree type, bool frange::normalize_kind () { - // Undefined is viral. - if (m_props.nan_undefined_p () || m_props.signbit_undefined_p ()) - { - set_undefined (); - return true; - } if (m_kind == VR_RANGE && real_isinf (&m_min, 1) && real_isinf (&m_max, 0)) { - // No FP properties set means varying. - if (m_props.varying_p ()) + if (m_pos_nan && m_neg_nan) { set_varying (m_type); return true; @@ -442,8 +354,7 @@ frange::normalize_kind () } else if (m_kind == VR_VARYING) { - // If a VARYING has any FP properties, it's no longer VARYING. - if (!m_props.varying_p ()) + if (!m_pos_nan || !m_neg_nan) { m_kind = VR_RANGE; m_min = dconstninf; @@ -451,9 +362,70 @@ frange::normalize_kind () return true; } } + else if (m_kind == VR_NAN && !m_pos_nan && !m_neg_nan) + set_undefined (); return false; } +// Union or intersect the zero endpoints of two ranges. For example: +// [-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] +// +// UNION_P is true when performing a union, or false when intersecting. + +bool +frange::combine_zeros (const frange &r, bool union_p) +{ + gcc_checking_assert (!undefined_p () && !known_isnan ()); + + bool changed = false; + if (real_iszero (&m_min) && real_iszero (&r.m_min) + && real_isneg (&m_min) != real_isneg (&r.m_min)) + { + m_min.sign = union_p; + changed = true; + } + if (real_iszero (&m_max) && real_iszero (&r.m_max) + && real_isneg (&m_max) != real_isneg (&r.m_max)) + { + m_max.sign = !union_p; + changed = true; + } + // If the signs are swapped, the resulting range is empty. + if (m_min.sign == 0 && m_max.sign == 1) + { + if (maybe_isnan ()) + m_kind = VR_NAN; + else + m_kind = VR_UNDEFINED; + changed = true; + } + return changed; +} + +// Union two ranges when one is known to be a NAN. + +bool +frange::union_nans (const frange &r) +{ + gcc_checking_assert (known_isnan () || r.known_isnan ()); + + if (known_isnan ()) + { + m_kind = r.m_kind; + m_min = r.m_min; + m_max = r.m_max; + } + m_pos_nan |= r.m_pos_nan; + m_neg_nan |= r.m_neg_nan; + normalize_kind (); + if (flag_checking) + verify_range (); + return true; +} + bool frange::union_ (const vrange &v) { @@ -467,29 +439,18 @@ frange::union_ (const vrange &v) return true; } - // If one side has a NAN, the union is the other side, plus the union - // of the properties and the possibility of a NAN. - if (known_nan ()) + // Combine NAN info. + if (known_isnan () || r.known_isnan ()) + return union_nans (r); + bool changed = false; + if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan) { - frange_props save = m_props; - *this = r; - m_props = save; - m_props.union_ (r.m_props); - update_nan (fp_prop::VARYING); - if (flag_checking) - verify_range (); - return true; - } - if (r.known_nan ()) - { - m_props.union_ (r.m_props); - update_nan (fp_prop::VARYING); - if (flag_checking) - verify_range (); - return true; + m_pos_nan |= r.m_pos_nan; + m_neg_nan |= r.m_neg_nan; + changed = true; } - bool changed = m_props.union_ (r.m_props); + // Combine endpoints. if (real_less (&r.m_min, &m_min)) { m_min = r.m_min; @@ -500,13 +461,34 @@ frange::union_ (const vrange &v) m_max = r.m_max; changed = true; } - changed |= normalize_kind (); + if (HONOR_SIGNED_ZEROS (m_type)) + changed |= combine_zeros (r, true); + + changed |= normalize_kind (); if (flag_checking) verify_range (); return changed; } +// Intersect two ranges when one is known to be a NAN. + +bool +frange::intersect_nans (const frange &r) +{ + gcc_checking_assert (known_isnan () || r.known_isnan ()); + + m_pos_nan &= r.m_pos_nan; + m_neg_nan &= r.m_neg_nan; + if (maybe_isnan ()) + m_kind = VR_NAN; + else + m_kind = VR_UNDEFINED; + if (flag_checking) + verify_range (); + return true; +} + bool frange::intersect (const vrange &v) { @@ -525,25 +507,18 @@ frange::intersect (const vrange &v) return true; } - // If two NANs are not exactly the same, drop to an unknown NAN, - // otherwise there's nothing to do. - if (known_nan () && r.known_nan ()) - { - if (m_props == r.m_props) - return false; - - set_nan (m_type); - return true; - } - // ?? Perhaps the intersection of a NAN and anything is a NAN ??. - if (known_nan () || r.known_nan ()) + // Combine NAN info. + if (known_isnan () || r.known_isnan ()) + return intersect_nans (r); + bool changed = false; + if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan) { - set_varying (m_type); - return true; + m_pos_nan &= r.m_pos_nan; + m_neg_nan &= r.m_neg_nan; + changed = true; } - bool changed = m_props.intersect (r.m_props); - + // Combine endpoints. if (real_less (&m_min, &r.m_min)) { m_min = r.m_min; @@ -554,14 +529,22 @@ frange::intersect (const vrange &v) m_max = r.m_max; changed = true; } - // If the endpoints are swapped, the ranges are disjoint. + // If the endpoints are swapped, the resulting range is empty. if (real_less (&m_max, &m_min)) { - set_undefined (); + if (maybe_isnan ()) + m_kind = VR_NAN; + else + m_kind = VR_UNDEFINED; + if (flag_checking) + verify_range (); return true; } - changed |= normalize_kind (); + if (HONOR_SIGNED_ZEROS (m_type)) + changed |= combine_zeros (r, false); + + changed |= normalize_kind (); if (flag_checking) verify_range (); return changed; @@ -574,7 +557,8 @@ frange::operator= (const frange &src) m_type = src.m_type; m_min = src.m_min; m_max = src.m_max; - m_props = src.m_props; + m_pos_nan = src.m_pos_nan; + m_neg_nan = src.m_neg_nan; if (flag_checking) verify_range (); @@ -592,12 +576,13 @@ frange::operator== (const frange &src) const if (varying_p ()) return types_compatible_p (m_type, src.m_type); - if (known_nan () || src.known_nan ()) + if (known_isnan () || src.known_isnan ()) return false; return (real_identical (&m_min, &src.m_min) && real_identical (&m_max, &src.m_max) - && m_props == src.m_props + && m_pos_nan == src.m_pos_nan + && m_neg_nan == src.m_neg_nan && types_compatible_p (m_type, src.m_type)); } return false; @@ -617,21 +602,24 @@ frange::contains_p (tree cst) const if (varying_p ()) return true; + if (real_isnan (rv)) + { + // No NAN in range. + if (!m_pos_nan && !m_neg_nan) + return false; + // Both +NAN and -NAN are present. + if (m_pos_nan && m_neg_nan) + return true; + return m_neg_nan == rv->sign; + } + if (known_isnan ()) + return false; if (real_compare (GE_EXPR, rv, &m_min) && real_compare (LE_EXPR, rv, &m_max)) { + // Make sure the signs are equal for signed zeros. if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (rv)) - { - // FIXME: This is still using get_signbit() instead of - // known_signbit() because the latter bails on possible NANs - // (for now). - if (get_signbit ().yes_p ()) - return real_isneg (rv); - else if (get_signbit ().no_p ()) - return !real_isneg (rv); - else - return true; - } + return m_min.sign == m_max.sign && m_min.sign == rv->sign; return true; } return false; @@ -648,29 +636,9 @@ frange::singleton_p (tree *result) const if (m_kind == VR_RANGE && real_identical (&m_min, &m_max)) { // Return false for any singleton that may be a NAN. - if (HONOR_NANS (m_type) && maybe_nan ()) + if (HONOR_NANS (m_type) && maybe_isnan ()) return false; - // Return the appropriate zero if known. - if (HONOR_SIGNED_ZEROS (m_type) && zero_p ()) - { - bool signbit; - if (known_signbit (signbit)) - { - if (signbit) - { - if (result) - *result = build_real (m_type, real_value_negate (&dconst0)); - } - else - { - if (result) - *result = build_real (m_type, dconst0); - } - return true; - } - return false; - } if (result) *result = build_real (m_type, m_min); return true; @@ -687,57 +655,40 @@ frange::supports_type_p (const_tree type) const void frange::verify_range () { - if (undefined_p ()) + switch (m_kind) { - gcc_checking_assert (m_props.undefined_p ()); + case VR_UNDEFINED: + // m_type is ignored. return; - } - gcc_checking_assert (!m_props.undefined_p ()); - - if (varying_p ()) - { - gcc_checking_assert (m_props.varying_p ()); + case VR_VARYING: + gcc_checking_assert (m_type); + gcc_checking_assert (m_pos_nan && m_neg_nan); + gcc_checking_assert (real_isinf (&m_min, 1)); + gcc_checking_assert (real_isinf (&m_max, 0)); return; + case VR_RANGE: + gcc_checking_assert (m_type); + break; + case VR_NAN: + gcc_checking_assert (m_type); + gcc_checking_assert (m_pos_nan || m_neg_nan); + return; + default: + gcc_unreachable (); } - // We don't support the inverse of an frange (yet). - gcc_checking_assert (m_kind == VR_RANGE); + // NANs cannot appear in the endpoints of a range. + gcc_checking_assert (!real_isnan (&m_min) && !real_isnan (&m_max)); - bool is_nan = real_isnan (&m_min) || real_isnan (&m_max); - if (is_nan) - { - // If either is a NAN, both must be a NAN. - gcc_checking_assert (real_identical (&m_min, &m_max)); - gcc_checking_assert (known_nan ()); - } - else - // Make sure we don't have swapped ranges. - gcc_checking_assert (!real_less (&m_max, &m_min)); + // Make sure we don't have swapped ranges. + gcc_checking_assert (!real_less (&m_max, &m_min)); - // If we're absolutely sure we have a NAN, the endpoints should - // reflect this, otherwise we'd have more than one way to represent - // a NAN. - if (known_nan ()) - { - gcc_checking_assert (real_isnan (&m_min)); - gcc_checking_assert (real_isnan (&m_max)); - } - else - { - // Make sure the signbit and range agree. - bool signbit; - if (known_signbit (signbit)) - { - if (signbit) - gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0)); - else - gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0)); - } - } + // [ +0.0, -0.0 ] is nonsensical. + gcc_checking_assert (!(real_iszero (&m_min, 0) && real_iszero (&m_max, 1))); // If all the properties are clear, we better not span the entire // domain, because that would make us varying. - if (m_props.varying_p ()) + if (m_pos_nan && m_neg_nan) gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0)); } @@ -755,16 +706,24 @@ frange::nonzero_p () const return false; } -// Set range to [+0.0, +0.0]. +// Set range to [+0.0, +0.0] if honoring signed zeros, or [0.0, 0.0] +// otherwise. void frange::set_zero (tree type) { - tree zero = build_zero_cst (type); - set (zero, zero); + if (HONOR_SIGNED_ZEROS (type)) + { + REAL_VALUE_TYPE dconstm0 = dconst0; + dconstm0.sign = 1; + set (type, dconstm0, dconst0); + clear_nan (); + } + else + set (type, dconst0, dconst0); } -// Return TRUE for any [0.0, 0.0] regardless of sign. +// Return TRUE for any zero regardless of sign. bool frange::zero_p () const @@ -777,9 +736,7 @@ frange::zero_p () const void frange::set_nonnegative (tree type) { - tree zero = build_zero_cst (type); - tree inf = vrp_val_max (type); - set (zero, inf); + set (type, dconst0, dconstinf); } // Here we copy between any two irange's. The ranges can be legacy or @@ -3637,8 +3594,21 @@ range_tests_nan () ASSERT_EQ (r0, r1); r0.clear_nan (); ASSERT_NE (r0, r1); + r0.update_nan (); + ASSERT_EQ (r0, r1); + + // [10, 20] NAN ^ [30, 40] NAN = NAN. + r0 = frange_float ("10", "20"); + r1 = frange_float ("30", "40"); + r0.intersect (r1); + ASSERT_TRUE (r0.known_isnan ()); + + // [3,5] U [5,10] NAN = ... NAN + r0 = frange_float ("3", "5"); r0.clear_nan (); - ASSERT_NE (r0, r1); + r1 = frange_float ("5", "10"); + r0.union_ (r1); + ASSERT_TRUE (r0.maybe_isnan ()); } // NAN ranges are not equal to each other. @@ -3657,40 +3627,75 @@ range_tests_nan () real_from_string (&r, "6"); ASSERT_TRUE (real_identical (&q, &r0.lower_bound ())); ASSERT_TRUE (real_identical (&r, &r0.upper_bound ())); - ASSERT_TRUE (r0.maybe_nan ()); + ASSERT_TRUE (r0.maybe_isnan ()); // NAN U NAN = NAN r0.set_nan (float_type_node); r1.set_nan (float_type_node); r0.union_ (r1); - ASSERT_TRUE (real_isnan (&r0.lower_bound ())); - ASSERT_TRUE (real_isnan (&r1.upper_bound ())); - ASSERT_TRUE (r0.known_nan ()); + ASSERT_TRUE (r0.known_isnan ()); - // [INF, INF] ^ NAN = VARYING + // [INF, INF] NAN ^ NAN = NAN r0.set_nan (float_type_node); r1 = frange_float ("+Inf", "+Inf"); + if (!HONOR_NANS (float_type_node)) + r1.update_nan (); r0.intersect (r1); - ASSERT_TRUE (r0.varying_p ()); + ASSERT_TRUE (r0.known_isnan ()); // NAN ^ NAN = NAN r0.set_nan (float_type_node); r1.set_nan (float_type_node); r0.intersect (r1); - ASSERT_TRUE (r0.known_nan ()); + ASSERT_TRUE (r0.known_isnan ()); + + // +NAN ^ -NAN = UNDEFINED + r0.set_nan (float_type_node, false); + r1.set_nan (float_type_node, true); + r0.intersect (r1); + ASSERT_TRUE (r0.undefined_p ()); // VARYING ^ NAN = NAN. r0.set_nan (float_type_node); r1.set_varying (float_type_node); r0.intersect (r1); - ASSERT_TRUE (r0.known_nan ()); + ASSERT_TRUE (r0.known_isnan ()); + + // [3,4] ^ NAN = UNDEFINED. + r0 = frange_float ("3", "4"); + r0.clear_nan (); + r1.set_nan (float_type_node); + r0.intersect (r1); + ASSERT_TRUE (r0.undefined_p ()); + + // [-3, 5] ^ NAN = UNDEFINED + r0 = frange_float ("-3", "5"); + r0.clear_nan (); + r1.set_nan (float_type_node); + r0.intersect (r1); + ASSERT_TRUE (r0.undefined_p ()); + + // Setting the NAN bit to yes does not make us a known NAN. + r0.set_varying (float_type_node); + r0.update_nan (); + ASSERT_FALSE (r0.known_isnan ()); - // Setting the NAN bit to yes, forces to range to [NAN, NAN]. + // NAN is in a VARYING. r0.set_varying (float_type_node); - r0.update_nan (fp_prop::YES); - ASSERT_TRUE (r0.known_nan ()); - ASSERT_TRUE (real_isnan (&r0.lower_bound ())); - ASSERT_TRUE (real_isnan (&r0.upper_bound ())); + real_nan (&r, "", 1, TYPE_MODE (float_type_node)); + tree nan = build_real (float_type_node, r); + ASSERT_TRUE (r0.contains_p (nan)); + + // -NAN is in a VARYING. + r0.set_varying (float_type_node); + q = real_value_negate (&r); + tree neg_nan = build_real (float_type_node, q); + ASSERT_TRUE (r0.contains_p (neg_nan)); + + // Clearing the NAN on a [] NAN is the empty set. + r0.set_nan (float_type_node); + r0.clear_nan (); + ASSERT_TRUE (r0.undefined_p ()); } static void @@ -3702,49 +3707,84 @@ range_tests_signed_zeros () frange r0, r1; bool signbit; - // Since -0.0 == +0.0, a range of [-0.0, -0.0] should contain +0.0 - // and vice versa. + // [0,0] contains [0,0] but not [-0,-0] and vice versa. r0 = frange (zero, zero); r1 = frange (neg_zero, neg_zero); ASSERT_TRUE (r0.contains_p (zero)); - ASSERT_TRUE (r0.contains_p (neg_zero)); - ASSERT_TRUE (r1.contains_p (zero)); + ASSERT_TRUE (!r0.contains_p (neg_zero)); ASSERT_TRUE (r1.contains_p (neg_zero)); + ASSERT_TRUE (!r1.contains_p (zero)); // Test contains_p() when we know the sign of the zero. - r0 = frange(zero, zero); - r0.set_signbit (fp_prop::NO); + r0 = frange (zero, zero); ASSERT_TRUE (r0.contains_p (zero)); ASSERT_FALSE (r0.contains_p (neg_zero)); - r0.set_signbit (fp_prop::YES); + r0 = frange (neg_zero, neg_zero); ASSERT_TRUE (r0.contains_p (neg_zero)); ASSERT_FALSE (r0.contains_p (zero)); - // The intersection of zeros that differ in sign is the empty set. - r0 = frange (zero, zero); - r0.set_signbit (fp_prop::YES); + // The intersection of zeros that differ in sign is a NAN (or + // undefined if not honoring NANs). + r0 = frange (neg_zero, neg_zero); r1 = frange (zero, zero); - r1.set_signbit (fp_prop::NO); r0.intersect (r1); - ASSERT_TRUE (r0.undefined_p ()); + if (HONOR_NANS (float_type_node)) + ASSERT_TRUE (r0.known_isnan ()); + else + ASSERT_TRUE (r0.undefined_p ()); // The union of zeros that differ in sign is a zero with unknown sign. r0 = frange (zero, zero); - r0.set_signbit (fp_prop::NO); - r1 = frange (zero, zero); - r1.set_signbit (fp_prop::YES); + r1 = frange (neg_zero, neg_zero); r0.union_ (r1); - ASSERT_TRUE (r0.zero_p () && !r0.known_signbit (signbit)); + ASSERT_TRUE (r0.zero_p () && !r0.signbit_p (signbit)); - // NAN U [5,6] should be [5,6] with no sign info. + // [-0, +0] has an unknown sign. + r0 = frange (neg_zero, zero); + ASSERT_TRUE (r0.zero_p () && !r0.signbit_p (signbit)); + + // [-0, +0] ^ [0, 0] is [0, 0] + r0 = frange (neg_zero, zero); + r1 = frange (zero, zero); + r0.intersect (r1); + ASSERT_TRUE (r0.zero_p ()); + + // NAN U [5,6] should be [5,6] NAN. r0.set_nan (float_type_node); r1 = frange_float ("5", "6"); + r1.clear_nan (); r0.union_ (r1); real_from_string (&q, "5"); real_from_string (&r, "6"); ASSERT_TRUE (real_identical (&q, &r0.lower_bound ())); ASSERT_TRUE (real_identical (&r, &r0.upper_bound ())); - ASSERT_TRUE (!r0.known_signbit (signbit)); + ASSERT_TRUE (!r0.signbit_p (signbit)); + ASSERT_TRUE (r0.maybe_isnan ()); + + r0 = frange_float ("+0", "5"); + r0.clear_nan (); + ASSERT_TRUE (r0.signbit_p (signbit) && !signbit); + + r0 = frange_float ("-0", "5"); + r0.clear_nan (); + ASSERT_TRUE (!r0.signbit_p (signbit)); + + r0 = frange_float ("-0", "10"); + r1 = frange_float ("0", "5"); + r0.intersect (r1); + ASSERT_TRUE (real_iszero (&r0.lower_bound (), false)); + + r0 = frange_float ("-0", "5"); + r1 = frange_float ("0", "5"); + r0.union_ (r1); + ASSERT_TRUE (real_iszero (&r0.lower_bound (), true)); + + r0 = frange_float ("-5", "-0"); + r0.update_nan (); + r1 = frange_float ("0", "0"); + r1.update_nan (); + r0.intersect (r1); + ASSERT_TRUE (r0.known_isnan ()); } static void @@ -3753,52 +3793,25 @@ range_tests_signbit () frange r0, r1; bool signbit; - // Setting the signbit drops the range to [-INF, 0]. - r0.set_varying (float_type_node); - r0.set_signbit (fp_prop::YES); - ASSERT_TRUE (real_isinf (&r0.lower_bound (), 1)); - ASSERT_TRUE (real_iszero (&r0.upper_bound ())); - - // Setting the signbit for [-5, 10] crops the range to [-5, 0] with - // the signbit property set. - r0 = frange_float ("-5", "10"); - r0.set_signbit (fp_prop::YES); - r0.clear_nan (); - ASSERT_TRUE (r0.known_signbit (signbit) && signbit); - r1 = frange_float ("-5", "0"); - ASSERT_TRUE (real_identical (&r0.lower_bound (), &r1.lower_bound ())); - ASSERT_TRUE (real_identical (&r0.upper_bound (), &r1.upper_bound ())); - // Negative numbers should have the SIGNBIT set. r0 = frange_float ("-5", "-1"); r0.clear_nan (); - ASSERT_TRUE (r0.known_signbit (signbit) && signbit); + ASSERT_TRUE (r0.signbit_p (signbit) && signbit); // Positive numbers should have the SIGNBIT clear. r0 = frange_float ("1", "10"); r0.clear_nan (); - ASSERT_TRUE (r0.known_signbit (signbit) && !signbit); + ASSERT_TRUE (r0.signbit_p (signbit) && !signbit); // Numbers containing zero should have an unknown SIGNBIT. r0 = frange_float ("0", "10"); r0.clear_nan (); - ASSERT_TRUE (!r0.known_signbit (signbit)); + ASSERT_TRUE (r0.signbit_p (signbit) && !signbit); // Numbers spanning both positive and negative should have an // unknown SIGNBIT. r0 = frange_float ("-10", "10"); r0.clear_nan (); - ASSERT_TRUE (!r0.known_signbit (signbit)); + ASSERT_TRUE (!r0.signbit_p (signbit)); r0.set_varying (float_type_node); - ASSERT_TRUE (!r0.known_signbit (signbit)); - - // Ignore signbit changes when the sign bit is obviously known from - // the range. - r0 = frange_float ("5", "10"); - r0.clear_nan (); - r0.set_signbit (fp_prop::VARYING); - ASSERT_TRUE (r0.known_signbit (signbit) && !signbit); - r0 = frange_float ("-5", "-1"); - r0.set_signbit (fp_prop::NO); - r0.clear_nan (); - ASSERT_TRUE (r0.undefined_p ()); + ASSERT_TRUE (!r0.signbit_p (signbit)); } static void @@ -3815,7 +3828,7 @@ range_tests_floats () // A range of [-INF,+INF] is actually VARYING if no other properties // are set. r0 = frange_float ("-Inf", "+Inf"); - if (r0.maybe_nan ()) + if (r0.maybe_isnan ()) ASSERT_TRUE (r0.varying_p ()); // ...unless it has some special property... r0.clear_nan (); @@ -3896,9 +3909,19 @@ range_tests_floats () r0.intersect (r1); ASSERT_EQ (r0, frange_float ("15", "20")); + // [10,20] NAN ^ [21,25] NAN = [NAN] + r0 = frange_float ("10", "20"); + r0.update_nan (); + r1 = frange_float ("21", "25"); + r1.update_nan (); + r0.intersect (r1); + ASSERT_TRUE (r0.known_isnan ()); + // [10,20] ^ [21,25] = [] r0 = frange_float ("10", "20"); + r0.clear_nan (); r1 = frange_float ("21", "25"); + r1.clear_nan (); r0.intersect (r1); ASSERT_TRUE (r0.undefined_p ()); } diff --git a/gcc/value-range.h b/gcc/value-range.h index 4392de8..3a401f3 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -35,6 +35,8 @@ enum value_range_kind VR_RANGE, /* Range is ~[MIN, MAX]. */ VR_ANTI_RANGE, + /* Range is a NAN. */ + VR_NAN, /* Range is a nice guy. */ VR_LAST }; @@ -263,69 +265,10 @@ public: virtual void accept (const vrange_visitor &v) const override; }; -// Floating point property to represent possible values of a NAN, INF, etc. - -class fp_prop -{ -public: - enum kind { - UNDEFINED = 0x0, // Prop is impossible. - YES = 0x1, // Prop is definitely set. - NO = 0x2, // Prop is definitely not set. - VARYING = (YES | NO) // Prop may hold. - }; - fp_prop (kind f) : m_kind (f) { } - bool varying_p () const { return m_kind == VARYING; } - bool undefined_p () const { return m_kind == UNDEFINED; } - bool yes_p () const { return m_kind == YES; } - bool no_p () const { return m_kind == NO; } -private: - unsigned char m_kind : 2; -}; - -// Accessors for individual FP properties. - -#define FP_PROP_ACCESSOR(NAME) \ - void NAME##_set_varying () { u.bits.NAME = fp_prop::VARYING; } \ - void NAME##_set_yes () { u.bits.NAME = fp_prop::YES; } \ - void NAME##_set_no () { u.bits.NAME = fp_prop::NO; } \ - bool NAME##_varying_p () const { return u.bits.NAME == fp_prop::VARYING; } \ - bool NAME##_undefined_p () const { return u.bits.NAME == fp_prop::UNDEFINED; } \ - bool NAME##_yes_p () const { return u.bits.NAME == fp_prop::YES; } \ - bool NAME##_no_p () const { return u.bits.NAME == fp_prop::NO; } \ - fp_prop get_##NAME () const \ - { return fp_prop ((fp_prop::kind) u.bits.NAME); } \ - void set_##NAME (fp_prop::kind f) { u.bits.NAME = f; } - -// Aggregate of all the FP properties in an frange packed into one -// structure to save space. Using explicit fp_prop's in the frange, -// would take one byte per property because of padding. Instead, we -// can save all properties into one byte. - -class frange_props -{ -public: - frange_props () { set_varying (); } - void set_varying () { u.bytes = 0xff; } - void set_undefined () { u.bytes = 0; } - bool varying_p () { return u.bytes == 0xff; } - bool undefined_p () { return u.bytes == 0; } - bool union_ (const frange_props &other); - bool intersect (const frange_props &other); - bool operator== (const frange_props &other) const; - FP_PROP_ACCESSOR(nan) - FP_PROP_ACCESSOR(signbit) -private: - union { - struct { - unsigned char nan : 2; - unsigned char signbit : 2; - } bits; - unsigned char bytes; - } u; -}; - // A floating point range. +// +// The representation is a type with a couple of endpoints, unioned +// with the set of { -NAN, +Nan }. class frange : public vrange { @@ -349,6 +292,7 @@ public: void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &, value_range_kind = VR_RANGE); void set_nan (tree type); + void set_nan (tree type, bool sign); virtual void set_varying (tree type) override; virtual void set_undefined () override; virtual bool union_ (const vrange &) override; @@ -367,42 +311,41 @@ public: bool operator!= (const frange &r) const { return !(*this == r); } const REAL_VALUE_TYPE &lower_bound () const; const REAL_VALUE_TYPE &upper_bound () const; + void update_nan (); + void clear_nan (); // fpclassify like API - bool known_finite () const; - bool maybe_inf () const; - bool known_inf () const; - bool maybe_nan () const; - bool known_nan () const; - bool known_signbit (bool &signbit) const; - - // Accessors for FP properties. - void update_nan (fp_prop::kind f); - void clear_nan () { update_nan (fp_prop::NO); } - void set_signbit (fp_prop::kind); + bool known_isfinite () const; + bool known_isnan () const; + bool known_isinf () const; + bool maybe_isnan () const; + bool maybe_isinf () const; + bool signbit_p (bool &signbit) const; private: - fp_prop get_nan () const { return m_props.get_nan (); } - fp_prop get_signbit () const { return m_props.get_signbit (); } void verify_range (); bool normalize_kind (); + bool union_nans (const frange &); + bool intersect_nans (const frange &); + bool combine_zeros (const frange &, bool union_p); - frange_props m_props; tree m_type; REAL_VALUE_TYPE m_min; REAL_VALUE_TYPE m_max; + bool m_pos_nan; + bool m_neg_nan; }; inline const REAL_VALUE_TYPE & frange::lower_bound () const { - gcc_checking_assert (!undefined_p ()); + gcc_checking_assert (!undefined_p () && !known_isnan ()); return m_min; } inline const REAL_VALUE_TYPE & frange::upper_bound () const { - gcc_checking_assert (!undefined_p ()); + gcc_checking_assert (!undefined_p () && !known_isnan ()); return m_max; } @@ -1082,30 +1025,6 @@ vrp_val_min (const_tree type) return NULL_TREE; } -// Supporting methods for frange. - -inline bool -frange_props::operator== (const frange_props &other) const -{ - return u.bytes == other.u.bytes; -} - -inline bool -frange_props::union_ (const frange_props &other) -{ - unsigned char saved = u.bytes; - u.bytes |= other.u.bytes; - return u.bytes != saved; -} - -inline bool -frange_props::intersect (const frange_props &other) -{ - unsigned char saved = u.bytes; - u.bytes &= other.u.bytes; - return u.bytes != saved; -} - inline frange::frange () { @@ -1154,15 +1073,42 @@ frange::set_varying (tree type) m_type = type; m_min = dconstninf; m_max = dconstinf; - m_props.set_varying (); + m_pos_nan = true; + m_neg_nan = true; } inline void frange::set_undefined () { m_kind = VR_UNDEFINED; - m_type = NULL; - m_props.set_undefined (); + if (flag_checking) + verify_range (); +} + +// Set the NAN bit and adjust the range. + +inline void +frange::update_nan () +{ + gcc_checking_assert (!undefined_p ()); + m_pos_nan = true; + m_neg_nan = true; + normalize_kind (); + if (flag_checking) + verify_range (); +} + +// Clear the NAN bit and adjust the range. + +inline void +frange::clear_nan () +{ + gcc_checking_assert (!undefined_p ()); + m_pos_nan = false; + m_neg_nan = false; + normalize_kind (); + if (flag_checking) + verify_range (); } // Set R to maximum representable value for TYPE. @@ -1186,19 +1132,28 @@ real_min_representable (REAL_VALUE_TYPE *r, tree type) *r = real_value_negate (r); } -// Build a NAN of type TYPE. +// Build a signless NAN of type TYPE. inline void frange::set_nan (tree type) { - REAL_VALUE_TYPE r; - gcc_assert (real_nan (&r, "", 1, TYPE_MODE (type))); - m_kind = VR_RANGE; + m_kind = VR_NAN; m_type = type; - m_min = r; - m_max = r; - m_props.set_varying (); - m_props.nan_set_yes (); + m_pos_nan = true; + m_neg_nan = true; + if (flag_checking) + verify_range (); +} + +// Build a NAN of type TYPE with SIGN. + +inline void +frange::set_nan (tree type, bool sign) +{ + m_kind = VR_NAN; + m_type = type; + m_neg_nan = sign; + m_pos_nan = !sign; if (flag_checking) verify_range (); } @@ -1206,21 +1161,19 @@ frange::set_nan (tree type) // Return TRUE if range is known to be finite. inline bool -frange::known_finite () const +frange::known_isfinite () const { if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE) return false; - return (!real_isnan (&m_min) - && !real_isinf (&m_min) - && !real_isinf (&m_max)); + return (!maybe_isnan () && !real_isinf (&m_min) && !real_isinf (&m_max)); } // Return TRUE if range may be infinite. inline bool -frange::maybe_inf () const +frange::maybe_isinf () const { - if (undefined_p () || m_kind == VR_ANTI_RANGE) + if (undefined_p () || m_kind == VR_ANTI_RANGE || m_kind == VR_NAN) return false; if (varying_p ()) return true; @@ -1230,7 +1183,7 @@ frange::maybe_inf () const // Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF]. inline bool -frange::known_inf () const +frange::known_isinf () const { return (m_kind == VR_RANGE && real_identical (&m_min, &m_max) @@ -1240,32 +1193,50 @@ frange::known_inf () const // Return TRUE if range is possibly a NAN. inline bool -frange::maybe_nan () const +frange::maybe_isnan () const { - return !get_nan ().no_p (); + return m_pos_nan || m_neg_nan; } // Return TRUE if range is a +NAN or -NAN. inline bool -frange::known_nan () const +frange::known_isnan () const { - return get_nan ().yes_p (); + return m_kind == VR_NAN; } // If the signbit for the range is known, set it in SIGNBIT and return // TRUE. inline bool -frange::known_signbit (bool &signbit) const +frange::signbit_p (bool &signbit) const { - // FIXME: Signed NANs are not supported yet. - if (maybe_nan ()) + if (undefined_p ()) return false; - if (get_signbit ().varying_p ()) + + // NAN with unknown sign. + if (m_pos_nan && m_neg_nan) return false; - signbit = get_signbit ().yes_p (); - return true; + // No NAN. + if (!m_pos_nan && !m_neg_nan) + { + if (m_min.sign == m_max.sign) + { + signbit = m_min.sign; + return true; + } + return false; + } + // NAN with known sign. + bool nan_sign = m_neg_nan; + if (known_isnan () + || (nan_sign == m_min.sign && nan_sign == m_max.sign)) + { + signbit = nan_sign; + return true; + } + return false; } #endif // GCC_VALUE_RANGE_H |