diff options
Diffstat (limited to 'gcc/value-range.cc')
-rw-r--r-- | gcc/value-range.cc | 818 |
1 files changed, 745 insertions, 73 deletions
diff --git a/gcc/value-range.cc b/gcc/value-range.cc index d056f73..9ca4424 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -94,8 +94,9 @@ vrange::singleton_p (tree *) const } void -vrange::set (tree, tree, value_range_kind) +vrange::set (tree min, tree, value_range_kind) { + set_varying (TREE_TYPE (min)); } tree @@ -168,18 +169,21 @@ vrange::nonzero_p () const } void -vrange::set_nonzero (tree) +vrange::set_nonzero (tree type) { + set_varying (type); } void -vrange::set_zero (tree) +vrange::set_zero (tree type) { + set_varying (type); } void -vrange::set_nonnegative (tree) +vrange::set_nonnegative (tree type) { + set_varying (type); } bool @@ -263,75 +267,107 @@ tree_compare (tree_code code, tree op1, tree op2) return !integer_zerop (fold_build2 (code, integer_type_node, op1, op2)); } -// Setter for franges. +// Flush denormal endpoints to the appropriate 0.0. void -frange::set (tree min, tree max, value_range_kind kind) +frange::flush_denormals_to_zero () { - gcc_checking_assert (TREE_CODE (min) == REAL_CST); - gcc_checking_assert (TREE_CODE (max) == REAL_CST); + if (undefined_p () || known_isnan ()) + return; - if (kind == VR_UNDEFINED) + // Flush [x, -DENORMAL] to [x, -0.0]. + if (real_isdenormal (&m_max) && real_isneg (&m_max)) + { + m_max = dconst0; + if (HONOR_SIGNED_ZEROS (m_type)) + m_max.sign = 1; + } + // Flush [+DENORMAL, x] to [+0.0, x]. + if (real_isdenormal (&m_min) && !real_isneg (&m_min)) + m_min = dconst0; +} + +// Setter for franges. + +void +frange::set (tree type, + const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max, + value_range_kind kind) +{ + switch (kind) { + case VR_UNDEFINED: set_undefined (); return; + case VR_VARYING: + case VR_ANTI_RANGE: + set_varying (type); + return; + case VR_RANGE: + break; + default: + gcc_unreachable (); } - // Treat VR_ANTI_RANGE and VR_VARYING as varying. - if (kind != VR_RANGE) + // Handle NANs. + if (real_isnan (&min) || real_isnan (&max)) { - set_varying (TREE_TYPE (min)); + gcc_checking_assert (real_identical (&min, &max)); + if (HONOR_NANS (type)) + { + bool sign = real_isneg (&min); + set_nan (type, sign); + } + else + set_undefined (); return; } m_kind = kind; - m_type = TREE_TYPE (min); - m_props.set_varying (); - - bool is_min = vrp_val_is_min (min); - bool is_max = vrp_val_is_max (max); - bool is_nan = (real_isnan (TREE_REAL_CST_PTR (min)) - || real_isnan (TREE_REAL_CST_PTR (max))); - - // Ranges with a NAN and a non-NAN endpoint are nonsensical. - gcc_checking_assert (!is_nan || operand_equal_p (min, max)); - - // The properties for singletons can be all set ahead of time. - if (operand_equal_p (min, max)) + m_type = type; + m_min = min; + m_max = max; + if (HONOR_NANS (m_type)) { - // Set INF properties. - if (is_min) - m_props.ninf_set_yes (); - else - m_props.ninf_set_no (); - if (is_max) - m_props.inf_set_yes (); - else - m_props.inf_set_no (); - // Set NAN property. - if (is_nan) - m_props.nan_set_yes (); - else - m_props.nan_set_no (); + m_pos_nan = true; + m_neg_nan = true; } else { - // Mark when the endpoints can't be +-INF. - if (!is_min) - m_props.ninf_set_no (); - if (!is_max) - m_props.inf_set_no (); + m_pos_nan = false; + m_neg_nan = false; + } + + // For -ffinite-math-only we can drop ranges outside the + // representable numbers to min/max for the type. + if (flag_finite_math_only) + { + REAL_VALUE_TYPE min_repr = frange_val_min (m_type); + REAL_VALUE_TYPE max_repr = frange_val_max (m_type); + if (real_less (&m_min, &min_repr)) + m_min = min_repr; + if (real_less (&max_repr, &m_max)) + m_max = max_repr; } // Check for swapped ranges. - gcc_checking_assert (is_nan || tree_compare (LE_EXPR, min, max)); + gcc_checking_assert (real_compare (LE_EXPR, &min, &max)); normalize_kind (); + flush_denormals_to_zero (); + if (flag_checking) verify_range (); } +void +frange::set (tree min, tree max, value_range_kind kind) +{ + set (TREE_TYPE (min), + *TREE_REAL_CST_PTR (min), *TREE_REAL_CST_PTR (max), kind); +} + // Normalize range to VARYING or UNDEFINED, or vice versa. Return // TRUE if anything changed. // @@ -343,35 +379,90 @@ frange::set (tree min, tree max, value_range_kind kind) bool frange::normalize_kind () { - if (m_kind == VR_RANGE) + if (m_kind == VR_RANGE + && frange_val_is_min (m_min, m_type) + && frange_val_is_max (m_max, m_type)) { - // No FP properties set means varying. - if (m_props.varying_p ()) + if (m_pos_nan && m_neg_nan) { set_varying (m_type); return true; } - // Undefined is viral. - if (m_props.nan_undefined_p () - || m_props.inf_undefined_p () - || m_props.ninf_undefined_p ()) - { - set_undefined (); - return true; - } } 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 = frange_val_min (m_type); + m_max = frange_val_max (m_type); 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 + set_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) { @@ -385,12 +476,54 @@ frange::union_ (const vrange &v) return true; } - bool ret = m_props.union_ (r.m_props); - ret |= normalize_kind (); + // 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) + { + m_pos_nan |= r.m_pos_nan; + m_neg_nan |= r.m_neg_nan; + changed = true; + } + + // Combine endpoints. + if (real_less (&r.m_min, &m_min)) + { + m_min = r.m_min; + changed = true; + } + if (real_less (&m_max, &r.m_max)) + { + m_max = r.m_max; + changed = true; + } + + 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 + set_undefined (); if (flag_checking) verify_range (); - return ret; + return true; } bool @@ -411,12 +544,47 @@ frange::intersect (const vrange &v) return true; } - bool ret = m_props.intersect (r.m_props); - ret |= normalize_kind (); + // 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) + { + m_pos_nan &= r.m_pos_nan; + m_neg_nan &= r.m_neg_nan; + changed = true; + } + // Combine endpoints. + if (real_less (&m_min, &r.m_min)) + { + m_min = r.m_min; + changed = true; + } + if (real_less (&r.m_max, &m_max)) + { + m_max = r.m_max; + changed = true; + } + // If the endpoints are swapped, the resulting range is empty. + if (real_less (&m_max, &m_min)) + { + if (maybe_isnan ()) + m_kind = VR_NAN; + else + set_undefined (); + if (flag_checking) + verify_range (); + return true; + } + + if (HONOR_SIGNED_ZEROS (m_type)) + changed |= combine_zeros (r, false); + + changed |= normalize_kind (); if (flag_checking) verify_range (); - return ret; + return changed; } frange & @@ -424,7 +592,10 @@ frange::operator= (const frange &src) { m_kind = src.m_kind; m_type = src.m_type; - m_props = src.m_props; + m_min = src.m_min; + m_max = src.m_max; + m_pos_nan = src.m_pos_nan; + m_neg_nan = src.m_neg_nan; if (flag_checking) verify_range (); @@ -442,7 +613,87 @@ frange::operator== (const frange &src) const if (varying_p ()) return types_compatible_p (m_type, src.m_type); - return m_props == src.m_props; + if (known_isnan () || src.known_isnan ()) + return false; + + return (real_identical (&m_min, &src.m_min) + && real_identical (&m_max, &src.m_max) + && m_pos_nan == src.m_pos_nan + && m_neg_nan == src.m_neg_nan + && types_compatible_p (m_type, src.m_type)); + } + return false; +} + +// Return TRUE if range contains the TREE_REAL_CST_PTR in CST. + +bool +frange::contains_p (tree cst) const +{ + gcc_checking_assert (m_kind != VR_ANTI_RANGE); + const REAL_VALUE_TYPE *rv = TREE_REAL_CST_PTR (cst); + + if (undefined_p ()) + return false; + + 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)) + return m_min.sign == m_max.sign && m_min.sign == rv->sign; + return true; + } + return false; +} + +// If range is a singleton, place it in RESULT and return TRUE. If +// RESULT is NULL, just return TRUE. +// +// A NAN can never be a singleton. + +bool +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_isnan ()) + return false; + + if (MODE_COMPOSITE_P (TYPE_MODE (m_type))) + { + // For IBM long doubles, if the value is +-Inf or is exactly + // representable in double, the other double could be +0.0 + // or -0.0. Since this means there is more than one way to + // represent a value, return false to avoid propagating it. + // See libgcc/config/rs6000/ibm-ldouble-format for details. + if (real_isinf (&m_min)) + return false; + REAL_VALUE_TYPE r; + real_convert (&r, DFmode, &m_min); + if (real_identical (&r, &m_min)) + return false; + } + + if (result) + *result = build_real (m_type, m_min); + return true; } return false; } @@ -456,18 +707,93 @@ 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: + gcc_checking_assert (!m_type); + return; + case VR_VARYING: + gcc_checking_assert (m_type); + gcc_checking_assert (m_pos_nan && m_neg_nan); + gcc_checking_assert (frange_val_is_min (m_min, m_type)); + gcc_checking_assert (frange_val_is_max (m_max, m_type)); + 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 (); } - if (varying_p ()) + + // NANs cannot appear in the endpoints of a range. + gcc_checking_assert (!real_isnan (&m_min) && !real_isnan (&m_max)); + + // Make sure we don't have swapped ranges. + gcc_checking_assert (!real_less (&m_max, &m_min)); + + // [ +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_pos_nan && m_neg_nan) + gcc_checking_assert (!frange_val_is_min (m_min, m_type) + || !frange_val_is_max (m_max, m_type)); +} + +// We can't do much with nonzeros yet. +void +frange::set_nonzero (tree type) +{ + set_varying (type); +} + +// We can't do much with nonzeros yet. +bool +frange::nonzero_p () const +{ + return false; +} + +// Set range to [+0.0, +0.0] if honoring signed zeros, or [0.0, 0.0] +// otherwise. + +void +frange::set_zero (tree type) +{ + if (HONOR_SIGNED_ZEROS (type)) { - gcc_checking_assert (m_props.varying_p ()); - return; + REAL_VALUE_TYPE dconstm0 = dconst0; + dconstm0.sign = 1; + set (type, dconstm0, dconst0); + clear_nan (); } - gcc_checking_assert (m_kind == VR_RANGE); - gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ()); + else + set (type, dconst0, dconst0); +} + +// Return TRUE for any zero regardless of sign. + +bool +frange::zero_p () const +{ + return (m_kind == VR_RANGE + && real_iszero (&m_min) + && real_iszero (&m_max)); +} + +void +frange::set_nonnegative (tree type) +{ + set (type, dconst0, frange_val_max (type)); + + // Set +NAN as the only possibility. + if (HONOR_NANS (type)) + update_nan (/*sign=*/0); } // Here we copy between any two irange's. The ranges can be legacy or @@ -3300,6 +3626,351 @@ range_tests_nonzero_bits () ASSERT_TRUE (r0.varying_p ()); } +// Build an frange from string endpoints. + +static inline frange +frange_float (const char *lb, const char *ub, tree type = float_type_node) +{ + REAL_VALUE_TYPE min, max; + gcc_assert (real_from_string (&min, lb) == 0); + gcc_assert (real_from_string (&max, ub) == 0); + return frange (type, min, max); +} + +static void +range_tests_nan () +{ + frange r0, r1; + REAL_VALUE_TYPE q, r; + + // Equal ranges but with differing NAN bits are not equal. + if (HONOR_NANS (float_type_node)) + { + r1 = frange_float ("10", "12"); + r0 = r1; + 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 (); + r1 = frange_float ("5", "10"); + r0.union_ (r1); + ASSERT_TRUE (r0.maybe_isnan ()); + } + + // NAN ranges are not equal to each other. + r0.set_nan (float_type_node); + r1 = r0; + ASSERT_FALSE (r0 == r1); + ASSERT_FALSE (r0 == r0); + ASSERT_TRUE (r0 != r0); + + // [5,6] U NAN = [5,6] NAN. + r0 = frange_float ("5", "6"); + r0.clear_nan (); + r1.set_nan (float_type_node); + 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.maybe_isnan ()); + + // NAN U NAN = NAN + r0.set_nan (float_type_node); + r1.set_nan (float_type_node); + r0.union_ (r1); + ASSERT_TRUE (r0.known_isnan ()); + + // [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.known_isnan ()); + + // NAN ^ NAN = NAN + r0.set_nan (float_type_node); + r1.set_nan (float_type_node); + r0.intersect (r1); + 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_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 ()); + + // NAN is in a VARYING. + r0.set_varying (float_type_node); + 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 +range_tests_signed_zeros () +{ + tree zero = build_zero_cst (float_type_node); + tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero); + REAL_VALUE_TYPE q, r; + frange r0, r1; + bool signbit; + + // [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 (neg_zero)); + ASSERT_TRUE (!r1.contains_p (zero)); + + // Test contains_p() when we know the sign of the zero. + r0 = frange (zero, zero); + ASSERT_TRUE (r0.contains_p (zero)); + ASSERT_FALSE (r0.contains_p (neg_zero)); + 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 a NAN (or + // undefined if not honoring NANs). + r0 = frange (neg_zero, neg_zero); + r1 = frange (zero, zero); + r0.intersect (r1); + 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); + r1 = frange (neg_zero, neg_zero); + r0.union_ (r1); + ASSERT_TRUE (r0.zero_p () && !r0.signbit_p (signbit)); + + // [-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.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 ()); + + r0.set_nonnegative (float_type_node); + ASSERT_TRUE (r0.signbit_p (signbit) && !signbit); + if (HONOR_NANS (float_type_node)) + ASSERT_TRUE (r0.maybe_isnan ()); +} + +static void +range_tests_signbit () +{ + frange r0, r1; + bool signbit; + + // Negative numbers should have the SIGNBIT set. + r0 = frange_float ("-5", "-1"); + r0.clear_nan (); + 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.signbit_p (signbit) && !signbit); + // Numbers containing zero should have an unknown SIGNBIT. + r0 = frange_float ("0", "10"); + r0.clear_nan (); + 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.signbit_p (signbit)); + r0.set_varying (float_type_node); + ASSERT_TRUE (!r0.signbit_p (signbit)); +} + +static void +range_tests_floats () +{ + frange r0, r1; + + range_tests_nan (); + range_tests_signbit (); + + if (HONOR_SIGNED_ZEROS (float_type_node)) + range_tests_signed_zeros (); + + // A range of [-INF,+INF] is actually VARYING if no other properties + // are set. + r0 = frange_float ("-Inf", "+Inf"); + if (r0.maybe_isnan ()) + ASSERT_TRUE (r0.varying_p ()); + // ...unless it has some special property... + r0.clear_nan (); + ASSERT_FALSE (r0.varying_p ()); + + // For most architectures, where float and double are different + // sizes, having the same endpoints does not necessarily mean the + // ranges are equal. + if (!types_compatible_p (float_type_node, double_type_node)) + { + r0 = frange_float ("3.0", "3.0", float_type_node); + r1 = frange_float ("3.0", "3.0", double_type_node); + ASSERT_NE (r0, r1); + } + + // [3,5] U [10,12] = [3,12]. + r0 = frange_float ("3", "5"); + r1 = frange_float ("10", "12"); + r0.union_ (r1); + ASSERT_EQ (r0, frange_float ("3", "12")); + + // [5,10] U [4,8] = [4,10] + r0 = frange_float ("5", "10"); + r1 = frange_float ("4", "8"); + r0.union_ (r1); + ASSERT_EQ (r0, frange_float ("4", "10")); + + // [3,5] U [4,10] = [3,10] + r0 = frange_float ("3", "5"); + r1 = frange_float ("4", "10"); + r0.union_ (r1); + ASSERT_EQ (r0, frange_float ("3", "10")); + + // [4,10] U [5,11] = [4,11] + r0 = frange_float ("4", "10"); + r1 = frange_float ("5", "11"); + r0.union_ (r1); + ASSERT_EQ (r0, frange_float ("4", "11")); + + // [3,12] ^ [10,12] = [10,12]. + r0 = frange_float ("3", "12"); + r1 = frange_float ("10", "12"); + r0.intersect (r1); + ASSERT_EQ (r0, frange_float ("10", "12")); + + // [10,12] ^ [11,11] = [11,11] + r0 = frange_float ("10", "12"); + r1 = frange_float ("11", "11"); + r0.intersect (r1); + ASSERT_EQ (r0, frange_float ("11", "11")); + + // [10,20] ^ [5,15] = [10,15] + r0 = frange_float ("10", "20"); + r1 = frange_float ("5", "15"); + r0.intersect (r1); + ASSERT_EQ (r0, frange_float ("10", "15")); + + // [10,20] ^ [15,25] = [15,20] + r0 = frange_float ("10", "20"); + r1 = frange_float ("15", "25"); + 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 ()); +} + void range_tests () { @@ -3308,6 +3979,7 @@ range_tests () range_tests_int_range_max (); range_tests_strict_enum (); range_tests_nonzero_bits (); + range_tests_floats (); range_tests_misc (); } |