aboutsummaryrefslogtreecommitdiff
path: root/gcc/value-range.cc
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/value-range.cc')
-rw-r--r--gcc/value-range.cc818
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 ();
}