/* Support routines for value ranges. Copyright (C) 2019-2023 Free Software Foundation, Inc. Major hacks by Aldy Hernandez and Andrew MacLeod . This file is part of GCC. GCC is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3, or (at your option) any later version. GCC is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with GCC; see the file COPYING3. If not see . */ #include "config.h" #include "system.h" #include "coretypes.h" #include "backend.h" #include "tree.h" #include "gimple.h" #include "ssa.h" #include "tree-pretty-print.h" #include "value-range-pretty-print.h" #include "fold-const.h" #include "gimple-range.h" void irange::accept (const vrange_visitor &v) const { v.visit (*this); } void unsupported_range::accept (const vrange_visitor &v) const { v.visit (*this); } // Convenience function only available for integers and pointers. wide_int Value_Range::lower_bound () const { if (is_a (*m_vrange)) return as_a (*m_vrange).lower_bound (); gcc_unreachable (); } // Convenience function only available for integers and pointers. wide_int Value_Range::upper_bound () const { if (is_a (*m_vrange)) return as_a (*m_vrange).upper_bound (); gcc_unreachable (); } void Value_Range::dump (FILE *out) const { if (m_vrange) m_vrange->dump (out); else fprintf (out, "NULL"); } DEBUG_FUNCTION void debug (const Value_Range &r) { r.dump (stderr); fprintf (stderr, "\n"); } // Default vrange definitions. bool vrange::contains_p (tree) const { return varying_p (); } bool vrange::singleton_p (tree *) const { return false; } void vrange::set (tree min, tree, value_range_kind) { set_varying (TREE_TYPE (min)); } tree vrange::type () const { return void_type_node; } bool vrange::supports_type_p (const_tree) const { return false; } void vrange::set_undefined () { m_kind = VR_UNDEFINED; } void vrange::set_varying (tree) { m_kind = VR_VARYING; } bool vrange::union_ (const vrange &r) { if (r.undefined_p () || varying_p ()) return false; if (undefined_p () || r.varying_p ()) { operator= (r); return true; } gcc_unreachable (); return false; } bool vrange::intersect (const vrange &r) { if (undefined_p () || r.varying_p ()) return false; if (r.undefined_p ()) { set_undefined (); return true; } if (varying_p ()) { operator= (r); return true; } gcc_unreachable (); return false; } bool vrange::zero_p () const { return false; } bool vrange::nonzero_p () const { return false; } void vrange::set_nonzero (tree type) { set_varying (type); } void vrange::set_zero (tree type) { set_varying (type); } void vrange::set_nonnegative (tree type) { set_varying (type); } bool vrange::fits_p (const vrange &) const { return true; } // Assignment operator for generic ranges. Copying incompatible types // is not allowed. vrange & vrange::operator= (const vrange &src) { if (is_a (src)) as_a (*this) = as_a (src); else if (is_a (src)) as_a (*this) = as_a (src); else { gcc_checking_assert (is_a (src)); m_kind = src.m_kind; } return *this; } // Equality operator for generic ranges. bool vrange::operator== (const vrange &src) const { if (is_a (src)) return as_a (*this) == as_a (src); if (is_a (src)) return as_a (*this) == as_a (src); gcc_unreachable (); } // Wrapper for vrange_printer to dump a range to a file. void vrange::dump (FILE *file) const { pretty_printer buffer; pp_needs_newline (&buffer) = true; buffer.buffer->stream = file; vrange_printer vrange_pp (&buffer); this->accept (vrange_pp); pp_flush (&buffer); } namespace inchash { void add_vrange (const vrange &v, inchash::hash &hstate, unsigned int) { if (v.undefined_p ()) { hstate.add_int (VR_UNDEFINED); return; } // Types are ignored throughout to inhibit two ranges being equal // but having different hash values. This can happen when two // ranges are equal and their types are different (but // types_compatible_p is true). if (is_a (v)) { const irange &r = as_a (v); if (r.varying_p ()) hstate.add_int (VR_VARYING); else hstate.add_int (VR_RANGE); for (unsigned i = 0; i < r.num_pairs (); ++i) { hstate.add_wide_int (r.lower_bound (i)); hstate.add_wide_int (r.upper_bound (i)); } hstate.add_wide_int (r.get_nonzero_bits ()); return; } if (is_a (v)) { const frange &r = as_a (v); if (r.varying_p ()) hstate.add_int (VR_VARYING); else hstate.add_int (VR_RANGE); hstate.add_real_value (r.lower_bound ()); hstate.add_real_value (r.upper_bound ()); nan_state nan = r.get_nan_state (); hstate.add_int (nan.pos_p ()); hstate.add_int (nan.neg_p ()); return; } gcc_unreachable (); } } //namespace inchash bool irange::supports_type_p (const_tree type) const { return supports_p (type); } // Return TRUE if R fits in THIS. bool irange::fits_p (const vrange &r) const { return m_max_ranges >= as_a (r).num_pairs (); } void irange::set_nonnegative (tree type) { set (type, wi::zero (TYPE_PRECISION (type)), wi::to_wide (TYPE_MAX_VALUE (type))); } void frange::accept (const vrange_visitor &v) const { v.visit (*this); } // Flush denormal endpoints to the appropriate 0.0. void frange::flush_denormals_to_zero () { if (undefined_p () || known_isnan ()) return; machine_mode mode = TYPE_MODE (type ()); // Flush [x, -DENORMAL] to [x, -0.0]. if (real_isdenormal (&m_max, mode) && real_isneg (&m_max)) { if (HONOR_SIGNED_ZEROS (m_type)) m_max = dconstm0; else m_max = dconst0; } // Flush [+DENORMAL, x] to [+0.0, x]. if (real_isdenormal (&m_min, mode) && !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, const nan_state &nan, 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 (); } // Handle NANs. if (real_isnan (&min) || real_isnan (&max)) { gcc_checking_assert (real_identical (&min, &max)); bool sign = real_isneg (&min); set_nan (type, sign); return; } m_kind = kind; m_type = type; m_min = min; m_max = max; if (HONOR_NANS (m_type)) { m_pos_nan = nan.pos_p (); m_neg_nan = nan.neg_p (); } else { m_pos_nan = false; m_neg_nan = false; } if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type))) { if (real_iszero (&m_min, 1)) m_min.sign = 0; if (real_iszero (&m_max, 1)) m_max.sign = 0; } else if (!HONOR_SIGNED_ZEROS (m_type)) { if (real_iszero (&m_max, 1)) m_max.sign = 0; if (real_iszero (&m_min, 0)) m_min.sign = 1; } // For -ffinite-math-only we can drop ranges outside the // representable numbers to min/max for the type. if (!HONOR_INFINITIES (m_type)) { 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; else if (real_less (&max_repr, &m_min)) m_min = max_repr; if (real_less (&max_repr, &m_max)) m_max = max_repr; else if (real_less (&m_max, &min_repr)) m_max = min_repr; } // Check for swapped ranges. gcc_checking_assert (real_compare (LE_EXPR, &min, &max)); normalize_kind (); if (flag_checking) verify_range (); } // Setter for an frange defaulting the NAN possibility to +-NAN when // HONOR_NANS. void frange::set (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max, value_range_kind kind) { set (type, min, max, nan_state (true), kind); } 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. // // A range with no known properties can be dropped to VARYING. // Similarly, a VARYING with any properties should be dropped to a // VR_RANGE. Normalizing ranges upon changing them ensures there is // only one representation for a given range. bool frange::normalize_kind () { if (m_kind == VR_RANGE && frange_val_is_min (m_min, m_type) && frange_val_is_max (m_max, m_type)) { if (!HONOR_NANS (m_type) || (m_pos_nan && m_neg_nan)) { set_varying (m_type); return true; } } else if (m_kind == VR_VARYING) { if (HONOR_NANS (m_type) && (!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) { const frange &r = as_a (v); if (r.undefined_p () || varying_p ()) return false; if (undefined_p () || r.varying_p ()) { *this = r; return true; } // 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 true; } bool frange::intersect (const vrange &v) { const frange &r = as_a (v); if (undefined_p () || r.varying_p ()) return false; if (r.undefined_p ()) { set_undefined (); return true; } if (varying_p ()) { *this = r; return true; } // 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 changed; } frange & frange::operator= (const frange &src) { m_kind = src.m_kind; m_type = src.m_type; 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 (); return *this; } bool frange::operator== (const frange &src) const { if (m_kind == src.m_kind) { if (undefined_p ()) return true; if (varying_p ()) return types_compatible_p (m_type, src.m_type); bool nan1 = known_isnan (); bool nan2 = src.known_isnan (); if (nan1 || nan2) { if (nan1 && nan2) return (m_pos_nan == src.m_pos_nan && m_neg_nan == src.m_neg_nan); 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 R. bool frange::contains_p (const REAL_VALUE_TYPE &r) const { gcc_checking_assert (m_kind != VR_ANTI_RANGE); if (undefined_p ()) return false; if (varying_p ()) return true; if (real_isnan (&r)) { // 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 == r.sign; } if (known_isnan ()) return false; if (real_compare (GE_EXPR, &r, &m_min) && real_compare (LE_EXPR, &r, &m_max)) { // Make sure the signs are equal for signed zeros. if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (&r)) return r.sign == m_min.sign || r.sign == m_max.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::internal_singleton_p (REAL_VALUE_TYPE *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 = m_min; return true; } return false; } bool frange::singleton_p (tree *result) const { if (internal_singleton_p ()) { if (result) *result = build_real (m_type, m_min); return true; } return false; } bool frange::singleton_p (REAL_VALUE_TYPE &r) const { return internal_singleton_p (&r); } bool frange::supports_type_p (const_tree type) const { return supports_p (type); } void frange::verify_range () { if (!undefined_p ()) gcc_checking_assert (HONOR_NANS (m_type) || !maybe_isnan ()); switch (m_kind) { case VR_UNDEFINED: gcc_checking_assert (!m_type); return; case VR_VARYING: gcc_checking_assert (m_type); gcc_checking_assert (frange_val_is_min (m_min, m_type)); gcc_checking_assert (frange_val_is_max (m_max, m_type)); if (HONOR_NANS (m_type)) gcc_checking_assert (m_pos_nan && m_neg_nan); else gcc_checking_assert (!m_pos_nan && !m_neg_nan); 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 (); } // 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)) { set (type, dconstm0, dconst0); clear_nan (); } 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)); } // Set the range to non-negative numbers, that is [+0.0, +INF]. // // The NAN in the resulting range (if HONOR_NANS) has a varying sign // as there are no guarantees in IEEE 754 wrt to the sign of a NAN, // except for copy, abs, and copysign. It is the responsibility of // the caller to set the NAN's sign if desired. void frange::set_nonnegative (tree type) { set (type, dconst0, frange_val_max (type)); } // Here we copy between any two irange's. irange & irange::operator= (const irange &src) { int needed = src.num_pairs (); maybe_resize (needed); unsigned x; unsigned lim = src.m_num_ranges; if (lim > m_max_ranges) lim = m_max_ranges; for (x = 0; x < lim * 2; ++x) m_base[x] = src.m_base[x]; // If the range didn't fit, the last range should cover the rest. if (lim != src.m_num_ranges) m_base[x - 1] = src.m_base[src.m_num_ranges * 2 - 1]; m_num_ranges = lim; m_type = src.m_type; m_kind = src.m_kind; m_nonzero_mask = src.m_nonzero_mask; if (m_max_ranges == 1) normalize_kind (); if (flag_checking) verify_range (); return *this; } value_range_kind get_legacy_range (const irange &r, tree &min, tree &max) { if (r.undefined_p ()) { min = NULL_TREE; max = NULL_TREE; return VR_UNDEFINED; } tree type = r.type (); if (r.varying_p ()) { min = wide_int_to_tree (type, r.lower_bound ()); max = wide_int_to_tree (type, r.upper_bound ()); return VR_VARYING; } unsigned int precision = TYPE_PRECISION (type); signop sign = TYPE_SIGN (type); if (r.num_pairs () > 1 && precision > 1 && r.lower_bound () == wi::min_value (precision, sign) && r.upper_bound () == wi::max_value (precision, sign)) { int_range<3> inv (r); inv.invert (); min = wide_int_to_tree (type, inv.lower_bound (0)); max = wide_int_to_tree (type, inv.upper_bound (0)); return VR_ANTI_RANGE; } min = wide_int_to_tree (type, r.lower_bound ()); max = wide_int_to_tree (type, r.upper_bound ()); return VR_RANGE; } /* Set value range to the canonical form of {VRTYPE, MIN, MAX, EQUIV}. This means adjusting VRTYPE, MIN and MAX representing the case of a wrapping range with MAX < MIN covering [MIN, type_max] U [type_min, MAX] as anti-rage ~[MAX+1, MIN-1]. Likewise for wrapping anti-ranges. In corner cases where MAX+1 or MIN-1 wraps this will fall back to varying. This routine exists to ease canonicalization in the case where we extract ranges from var + CST op limit. */ void irange::set (tree type, const wide_int &min, const wide_int &max, value_range_kind kind) { unsigned prec = TYPE_PRECISION (type); signop sign = TYPE_SIGN (type); wide_int min_value = wi::min_value (prec, sign); wide_int max_value = wi::max_value (prec, sign); m_type = type; m_nonzero_mask = wi::minus_one (prec); if (kind == VR_RANGE) { m_base[0] = min; m_base[1] = max; m_num_ranges = 1; if (min == min_value && max == max_value) m_kind = VR_VARYING; else m_kind = VR_RANGE; } else { gcc_checking_assert (kind == VR_ANTI_RANGE); gcc_checking_assert (m_max_ranges > 1); m_kind = VR_UNDEFINED; m_num_ranges = 0; wi::overflow_type ovf; wide_int lim; if (sign == SIGNED) lim = wi::add (min, -1, sign, &ovf); else lim = wi::sub (min, 1, sign, &ovf); if (!ovf) { m_kind = VR_RANGE; m_base[0] = min_value; m_base[1] = lim; ++m_num_ranges; } if (sign == SIGNED) lim = wi::sub (max, -1, sign, &ovf); else lim = wi::add (max, 1, sign, &ovf); if (!ovf) { m_kind = VR_RANGE; m_base[m_num_ranges * 2] = lim; m_base[m_num_ranges * 2 + 1] = max_value; ++m_num_ranges; } } if (flag_checking) verify_range (); } void irange::set (tree min, tree max, value_range_kind kind) { if (POLY_INT_CST_P (min) || POLY_INT_CST_P (max)) { set_varying (TREE_TYPE (min)); return; } gcc_checking_assert (TREE_CODE (min) == INTEGER_CST); gcc_checking_assert (TREE_CODE (max) == INTEGER_CST); return set (TREE_TYPE (min), wi::to_wide (min), wi::to_wide (max), kind); } // Check the validity of the range. void irange::verify_range () { gcc_checking_assert (m_discriminator == VR_IRANGE); if (m_kind == VR_UNDEFINED) { gcc_checking_assert (m_num_ranges == 0); return; } gcc_checking_assert (m_num_ranges <= m_max_ranges); // Legacy allowed these to represent VARYING for unknown types. // Leave this in for now, until all users are converted. Eventually // we should abort in set_varying. if (m_kind == VR_VARYING && m_type == error_mark_node) return; unsigned prec = TYPE_PRECISION (m_type); if (m_kind == VR_VARYING) { gcc_checking_assert (m_nonzero_mask == -1); gcc_checking_assert (m_num_ranges == 1); gcc_checking_assert (varying_compatible_p ()); gcc_checking_assert (lower_bound ().get_precision () == prec); gcc_checking_assert (upper_bound ().get_precision () == prec); return; } gcc_checking_assert (m_num_ranges != 0); gcc_checking_assert (!varying_compatible_p ()); for (unsigned i = 0; i < m_num_ranges; ++i) { wide_int lb = lower_bound (i); wide_int ub = upper_bound (i); gcc_checking_assert (lb.get_precision () == prec); gcc_checking_assert (ub.get_precision () == prec); int c = wi::cmp (lb, ub, TYPE_SIGN (m_type)); gcc_checking_assert (c == 0 || c == -1); } gcc_checking_assert (m_nonzero_mask.get_precision () == prec); } bool irange::operator== (const irange &other) const { if (m_num_ranges != other.m_num_ranges) return false; if (m_num_ranges == 0) return true; signop sign1 = TYPE_SIGN (type ()); signop sign2 = TYPE_SIGN (other.type ()); for (unsigned i = 0; i < m_num_ranges; ++i) { widest_int lb = widest_int::from (lower_bound (i), sign1); widest_int ub = widest_int::from (upper_bound (i), sign1); widest_int lb_other = widest_int::from (other.lower_bound (i), sign2); widest_int ub_other = widest_int::from (other.upper_bound (i), sign2); if (lb != lb_other || ub != ub_other) return false; } widest_int nz1 = widest_int::from (get_nonzero_bits (), sign1); widest_int nz2 = widest_int::from (other.get_nonzero_bits (), sign2); return nz1 == nz2; } /* If range is a singleton, place it in RESULT and return TRUE. */ bool irange::singleton_p (tree *result) const { if (num_pairs () == 1 && lower_bound () == upper_bound ()) { if (result) *result = wide_int_to_tree (type (), lower_bound ()); return true; } return false; } bool irange::singleton_p (wide_int &w) const { if (num_pairs () == 1 && lower_bound () == upper_bound ()) { w = lower_bound (); return true; } return false; } /* Return 1 if CST is inside value range. 0 if CST is not inside value range. Benchmark compile/20001226-1.c compilation time after changing this function. */ bool irange::contains_p (const wide_int &cst) const { if (undefined_p ()) return false; // See if we can exclude CST based on the nonzero bits. if (m_nonzero_mask != -1 && cst != 0 && wi::bit_and (m_nonzero_mask, cst) == 0) return false; signop sign = TYPE_SIGN (type ()); for (unsigned r = 0; r < m_num_ranges; ++r) { if (wi::lt_p (cst, lower_bound (r), sign)) return false; if (wi::le_p (cst, upper_bound (r), sign)) return true; } return false; } // Perform an efficient union with R when both ranges have only a single pair. // Excluded are VARYING and UNDEFINED ranges. bool irange::irange_single_pair_union (const irange &r) { gcc_checking_assert (!undefined_p () && !varying_p ()); gcc_checking_assert (!r.undefined_p () && !varying_p ()); signop sign = TYPE_SIGN (m_type); // Check if current lower bound is also the new lower bound. if (wi::le_p (m_base[0], r.m_base[0], sign)) { // If current upper bound is new upper bound, we're done. if (wi::le_p (r.m_base[1], m_base[1], sign)) return union_nonzero_bits (r); // Otherwise R has the new upper bound. // Check for overlap/touching ranges, or single target range. if (m_max_ranges == 1 || (widest_int::from (m_base[1], sign) + 1 >= widest_int::from (r.m_base[0], TYPE_SIGN (r.m_type)))) m_base[1] = r.m_base[1]; else { // This is a dual range result. m_base[2] = r.m_base[0]; m_base[3] = r.m_base[1]; m_num_ranges = 2; } union_nonzero_bits (r); return true; } // Set the new lower bound to R's lower bound. wide_int lb = m_base[0]; m_base[0] = r.m_base[0]; // If R fully contains THIS range, just set the upper bound. if (wi::ge_p (r.m_base[1], m_base[1], sign)) m_base[1] = r.m_base[1]; // Check for overlapping ranges, or target limited to a single range. else if (m_max_ranges == 1 || (widest_int::from (r.m_base[1], TYPE_SIGN (r.m_type)) + 1 >= widest_int::from (lb, sign))) ; else { // Left with 2 pairs. m_num_ranges = 2; m_base[2] = lb; m_base[3] = m_base[1]; m_base[1] = r.m_base[1]; } union_nonzero_bits (r); return true; } // Return TRUE if anything changes. bool irange::union_ (const vrange &v) { const irange &r = as_a (v); if (r.undefined_p ()) return false; if (undefined_p ()) { operator= (r); if (flag_checking) verify_range (); return true; } if (varying_p ()) return false; if (r.varying_p ()) { set_varying (type ()); return true; } // Special case one range union one range. if (m_num_ranges == 1 && r.m_num_ranges == 1) return irange_single_pair_union (r); // If this ranges fully contains R, then we need do nothing. if (irange_contains_p (r)) return union_nonzero_bits (r); // Do not worry about merging and such by reserving twice as many // pairs as needed, and then simply sort the 2 ranges into this // intermediate form. // // The intermediate result will have the property that the beginning // of each range is <= the beginning of the next range. There may // be overlapping ranges at this point. I.e. this would be valid // [-20, 10], [-10, 0], [0, 20], [40, 90] as it satisfies this // constraint : -20 < -10 < 0 < 40. When the range is rebuilt into r, // the merge is performed. // // [Xi,Yi]..[Xn,Yn] U [Xj,Yj]..[Xm,Ym] --> [Xk,Yk]..[Xp,Yp] auto_vec res (m_num_ranges * 2 + r.m_num_ranges * 2); unsigned i = 0, j = 0, k = 0; signop sign = TYPE_SIGN (m_type); while (i < m_num_ranges * 2 && j < r.m_num_ranges * 2) { // lower of Xi and Xj is the lowest point. if (widest_int::from (m_base[i], sign) <= widest_int::from (r.m_base[j], sign)) { res.quick_push (m_base[i]); res.quick_push (m_base[i + 1]); k += 2; i += 2; } else { res.quick_push (r.m_base[j]); res.quick_push (r.m_base[j + 1]); k += 2; j += 2; } } for ( ; i < m_num_ranges * 2; i += 2) { res.quick_push (m_base[i]); res.quick_push (m_base[i + 1]); k += 2; } for ( ; j < r.m_num_ranges * 2; j += 2) { res.quick_push (r.m_base[j]); res.quick_push (r.m_base[j + 1]); k += 2; } // Now normalize the vector removing any overlaps. i = 2; for (j = 2; j < k ; j += 2) { // Current upper+1 is >= lower bound next pair, then we merge ranges. if (widest_int::from (res[i - 1], sign) + 1 >= widest_int::from (res[j], sign)) { // New upper bounds is greater of current or the next one. if (widest_int::from (res[j + 1], sign) > widest_int::from (res[i - 1], sign)) res[i - 1] = res[j + 1]; } else { // This is a new distinct range, but no point in copying it // if it is already in the right place. if (i != j) { res[i++] = res[j]; res[i++] = res[j + 1]; } else i += 2; } } // At this point, the vector should have i ranges, none overlapping. // Now it simply needs to be copied, and if there are too many // ranges, merge some. We wont do any analysis as to what the // "best" merges are, simply combine the final ranges into one. maybe_resize (i / 2); if (i > m_max_ranges * 2) { res[m_max_ranges * 2 - 1] = res[i - 1]; i = m_max_ranges * 2; } for (j = 0; j < i ; j++) m_base[j] = res [j]; m_num_ranges = i / 2; m_kind = VR_RANGE; union_nonzero_bits (r); return true; } // Return TRUE if THIS fully contains R. No undefined or varying cases. bool irange::irange_contains_p (const irange &r) const { gcc_checking_assert (!undefined_p () && !varying_p ()); gcc_checking_assert (!r.undefined_p () && !varying_p ()); // In order for THIS to fully contain R, all of the pairs within R must // be fully contained by the pairs in this object. signop sign = TYPE_SIGN (m_type); unsigned ri = 0; unsigned i = 0; wide_int rl = r.m_base[0]; wide_int ru = r.m_base[1]; wide_int l = m_base[0]; wide_int u = m_base[1]; while (1) { // If r is contained within this range, move to the next R if (wi::ge_p (rl, l, sign) && wi::le_p (ru, u, sign)) { // This pair is OK, Either done, or bump to the next. if (++ri >= r.num_pairs ()) return true; rl = r.m_base[ri * 2]; ru = r.m_base[ri * 2 + 1]; continue; } // Otherwise, check if this's pair occurs before R's. if (wi::lt_p (u, rl, sign)) { // There's still at least one pair of R left. if (++i >= num_pairs ()) return false; l = m_base[i * 2]; u = m_base[i * 2 + 1]; continue; } return false; } return false; } // Return TRUE if anything changes. bool irange::intersect (const vrange &v) { const irange &r = as_a (v); gcc_checking_assert (undefined_p () || r.undefined_p () || range_compatible_p (type (), r.type ())); if (undefined_p ()) return false; if (r.undefined_p ()) { set_undefined (); return true; } if (r.varying_p ()) return false; if (varying_p ()) { operator= (r); return true; } if (r.num_pairs () == 1) { bool res = intersect (r.lower_bound (), r.upper_bound ()); if (undefined_p ()) return true; res |= intersect_nonzero_bits (r); return res; } // If R fully contains this, then intersection will change nothing. if (r.irange_contains_p (*this)) return intersect_nonzero_bits (r); // ?? We could probably come up with something smarter than the // worst case scenario here. int needed = num_pairs () + r.num_pairs (); maybe_resize (needed); signop sign = TYPE_SIGN (m_type); unsigned bld_pair = 0; unsigned bld_lim = m_max_ranges; int_range_max r2 (*this); unsigned r2_lim = r2.num_pairs (); unsigned i2 = 0; for (unsigned i = 0; i < r.num_pairs (); ) { // If r1's upper is < r2's lower, we can skip r1's pair. wide_int ru = r.m_base[i * 2 + 1]; wide_int r2l = r2.m_base[i2 * 2]; if (wi::lt_p (ru, r2l, sign)) { i++; continue; } // Likewise, skip r2's pair if its excluded. wide_int r2u = r2.m_base[i2 * 2 + 1]; wide_int rl = r.m_base[i * 2]; if (wi::lt_p (r2u, rl, sign)) { i2++; if (i2 < r2_lim) continue; // No more r2, break. break; } // Must be some overlap. Find the highest of the lower bounds, // and set it, unless the build limits lower bounds is already // set. if (bld_pair < bld_lim) { if (wi::ge_p (rl, r2l, sign)) m_base[bld_pair * 2] = rl; else m_base[bld_pair * 2] = r2l; } else // Decrease and set a new upper. bld_pair--; // ...and choose the lower of the upper bounds. if (wi::le_p (ru, r2u, sign)) { m_base[bld_pair * 2 + 1] = ru; bld_pair++; // Move past the r1 pair and keep trying. i++; continue; } else { m_base[bld_pair * 2 + 1] = r2u; bld_pair++; i2++; if (i2 < r2_lim) continue; // No more r2, break. break; } // r2 has the higher lower bound. } // At the exit of this loop, it is one of 2 things: // ran out of r1, or r2, but either means we are done. m_num_ranges = bld_pair; if (m_num_ranges == 0) { set_undefined (); return true; } m_kind = VR_RANGE; intersect_nonzero_bits (r); return true; } // Multirange intersect for a specified wide_int [lb, ub] range. // Return TRUE if intersect changed anything. // // NOTE: It is the caller's responsibility to intersect the nonzero masks. bool irange::intersect (const wide_int& lb, const wide_int& ub) { // Undefined remains undefined. if (undefined_p ()) return false; tree range_type = type(); signop sign = TYPE_SIGN (range_type); gcc_checking_assert (TYPE_PRECISION (range_type) == wi::get_precision (lb)); gcc_checking_assert (TYPE_PRECISION (range_type) == wi::get_precision (ub)); // If this range is fully contained, then intersection will do nothing. if (wi::ge_p (lower_bound (), lb, sign) && wi::le_p (upper_bound (), ub, sign)) return false; unsigned bld_index = 0; unsigned pair_lim = num_pairs (); for (unsigned i = 0; i < pair_lim; i++) { wide_int pairl = m_base[i * 2]; wide_int pairu = m_base[i * 2 + 1]; // Once UB is less than a pairs lower bound, we're done. if (wi::lt_p (ub, pairl, sign)) break; // if LB is greater than this pairs upper, this pair is excluded. if (wi::lt_p (pairu, lb, sign)) continue; // Must be some overlap. Find the highest of the lower bounds, // and set it if (wi::gt_p (lb, pairl, sign)) m_base[bld_index * 2] = lb; else m_base[bld_index * 2] = pairl; // ...and choose the lower of the upper bounds and if the base pair // has the lower upper bound, need to check next pair too. if (wi::lt_p (ub, pairu, sign)) { m_base[bld_index++ * 2 + 1] = ub; break; } else m_base[bld_index++ * 2 + 1] = pairu; } m_num_ranges = bld_index; if (m_num_ranges == 0) { set_undefined (); return true; } m_kind = VR_RANGE; // No need to call normalize_kind(), as the caller will do this // while intersecting the nonzero mask. if (flag_checking) verify_range (); return true; } // Signed 1-bits are strange. You can't subtract 1, because you can't // represent the number 1. This works around that for the invert routine. static wide_int inline subtract_one (const wide_int &x, tree type, wi::overflow_type &overflow) { if (TYPE_SIGN (type) == SIGNED) return wi::add (x, -1, SIGNED, &overflow); else return wi::sub (x, 1, UNSIGNED, &overflow); } // The analogous function for adding 1. static wide_int inline add_one (const wide_int &x, tree type, wi::overflow_type &overflow) { if (TYPE_SIGN (type) == SIGNED) return wi::sub (x, -1, SIGNED, &overflow); else return wi::add (x, 1, UNSIGNED, &overflow); } // Return the inverse of a range. void irange::invert () { gcc_checking_assert (!undefined_p () && !varying_p ()); // We always need one more set of bounds to represent an inverse, so // if we're at the limit, we can't properly represent things. // // For instance, to represent the inverse of a 2 sub-range set // [5, 10][20, 30], we would need a 3 sub-range set // [-MIN, 4][11, 19][31, MAX]. // // In this case, return the most conservative thing. // // However, if any of the extremes of the range are -MIN/+MAX, we // know we will not need an extra bound. For example: // // INVERT([-MIN,20][30,40]) => [21,29][41,+MAX] // INVERT([-MIN,20][30,MAX]) => [21,29] tree ttype = type (); unsigned prec = TYPE_PRECISION (ttype); signop sign = TYPE_SIGN (ttype); wide_int type_min = wi::min_value (prec, sign); wide_int type_max = wi::max_value (prec, sign); m_nonzero_mask = wi::minus_one (prec); if (m_num_ranges == m_max_ranges && lower_bound () != type_min && upper_bound () != type_max) { m_base[1] = type_max; m_num_ranges = 1; return; } // At this point, we need one extra sub-range to represent the // inverse. maybe_resize (m_num_ranges + 1); // The algorithm is as follows. To calculate INVERT ([a,b][c,d]), we // generate [-MIN, a-1][b+1, c-1][d+1, MAX]. // // If there is an over/underflow in the calculation for any // sub-range, we eliminate that subrange. This allows us to easily // calculate INVERT([-MIN, 5]) with: [-MIN, -MIN-1][6, MAX]. And since // we eliminate the underflow, only [6, MAX] remains. unsigned i = 0; wi::overflow_type ovf; // Construct leftmost range. int_range_max orig_range (*this); unsigned nitems = 0; wide_int tmp; // If this is going to underflow on the MINUS 1, don't even bother // checking. This also handles subtracting one from an unsigned 0, // which doesn't set the underflow bit. if (type_min != orig_range.lower_bound ()) { m_base[nitems++] = type_min; tmp = subtract_one (orig_range.lower_bound (), ttype, ovf); m_base[nitems++] = tmp; if (ovf) nitems = 0; } i++; // Construct middle ranges if applicable. if (orig_range.num_pairs () > 1) { unsigned j = i; for (; j < (orig_range.num_pairs () * 2) - 1; j += 2) { // The middle ranges cannot have MAX/MIN, so there's no need // to check for unsigned overflow on the +1 and -1 here. tmp = wi::add (orig_range.m_base[j], 1, sign, &ovf); m_base[nitems++] = tmp; tmp = subtract_one (orig_range.m_base[j + 1], ttype, ovf); m_base[nitems++] = tmp; if (ovf) nitems -= 2; } i = j; } // Construct rightmost range. // // However, if this will overflow on the PLUS 1, don't even bother. // This also handles adding one to an unsigned MAX, which doesn't // set the overflow bit. if (type_max != orig_range.m_base[i]) { tmp = add_one (orig_range.m_base[i], ttype, ovf); m_base[nitems++] = tmp; m_base[nitems++] = type_max; if (ovf) nitems -= 2; } m_num_ranges = nitems / 2; // We disallow undefined or varying coming in, so the result can // only be a VR_RANGE. gcc_checking_assert (m_kind == VR_RANGE); if (flag_checking) verify_range (); } // Return the nonzero bits inherent in the range. wide_int irange::get_nonzero_bits_from_range () const { wide_int min = lower_bound (); wide_int max = upper_bound (); wide_int xorv = min ^ max; if (xorv != 0) { unsigned prec = TYPE_PRECISION (type ()); xorv = wi::mask (prec - wi::clz (xorv), false, prec); } return min | xorv; } // If the the nonzero mask can be trivially converted to a range, do // so and return TRUE. bool irange::set_range_from_nonzero_bits () { gcc_checking_assert (!undefined_p ()); if (m_nonzero_mask == -1) return false; unsigned popcount = wi::popcount (m_nonzero_mask); // If we have only one bit set in the mask, we can figure out the // range immediately. if (popcount == 1) { // Make sure we don't pessimize the range. if (!contains_p (m_nonzero_mask)) return false; bool has_zero = contains_zero_p (*this); wide_int nz = m_nonzero_mask; set (m_type, nz, nz); m_nonzero_mask = nz; if (has_zero) { int_range<2> zero; zero.set_zero (type ()); union_ (zero); } return true; } else if (popcount == 0) { set_zero (type ()); return true; } return false; } void irange::set_nonzero_bits (const wide_int &bits) { gcc_checking_assert (!undefined_p ()); // Drop VARYINGs with a nonzero mask to a plain range. if (m_kind == VR_VARYING && bits != -1) m_kind = VR_RANGE; m_nonzero_mask = bits; if (set_range_from_nonzero_bits ()) return; normalize_kind (); if (flag_checking) verify_range (); } // Return the nonzero bitmask. This will return the nonzero bits plus // the nonzero bits inherent in the range. wide_int irange::get_nonzero_bits () const { gcc_checking_assert (!undefined_p ()); // The nonzero mask inherent in the range is calculated on-demand. // For example, [0,255] does not have a 0xff nonzero mask by default // (unless manually set). This saves us considerable time, because // setting it at creation incurs a large penalty for irange::set. // At the time of writing there was a 5% slowdown in VRP if we kept // the mask precisely up to date at all times. Instead, we default // to -1 and set it when explicitly requested. However, this // function will always return the correct mask. if (m_nonzero_mask == -1) return get_nonzero_bits_from_range (); else return m_nonzero_mask & get_nonzero_bits_from_range (); } // Intersect the nonzero bits in R into THIS and normalize the range. // Return TRUE if the intersection changed anything. bool irange::intersect_nonzero_bits (const irange &r) { gcc_checking_assert (!undefined_p () && !r.undefined_p ()); if (m_nonzero_mask == -1 && r.m_nonzero_mask == -1) { normalize_kind (); if (flag_checking) verify_range (); return false; } bool changed = false; if (m_nonzero_mask != r.m_nonzero_mask) { wide_int nz = get_nonzero_bits () & r.get_nonzero_bits (); // If the nonzero bits did not change, return false. if (nz == get_nonzero_bits ()) return false; m_nonzero_mask = nz; if (set_range_from_nonzero_bits ()) return true; changed = true; } normalize_kind (); if (flag_checking) verify_range (); return changed; } // Union the nonzero bits in R into THIS and normalize the range. // Return TRUE if the union changed anything. bool irange::union_nonzero_bits (const irange &r) { gcc_checking_assert (!undefined_p () && !r.undefined_p ()); if (m_nonzero_mask == -1 && r.m_nonzero_mask == -1) { normalize_kind (); if (flag_checking) verify_range (); return false; } bool changed = false; if (m_nonzero_mask != r.m_nonzero_mask) { wide_int save = get_nonzero_bits (); m_nonzero_mask = save | r.get_nonzero_bits (); // No need to call set_range_from_nonzero_bits, because we'll // never narrow the range. Besides, it would cause endless // recursion because of the union_ in // set_range_from_nonzero_bits. changed = m_nonzero_mask != save; } normalize_kind (); if (flag_checking) verify_range (); return changed; } void dump_value_range (FILE *file, const vrange *vr) { vr->dump (file); } DEBUG_FUNCTION void debug (const vrange *vr) { dump_value_range (stderr, vr); fprintf (stderr, "\n"); } DEBUG_FUNCTION void debug (const vrange &vr) { debug (&vr); } DEBUG_FUNCTION void debug (const value_range *vr) { dump_value_range (stderr, vr); fprintf (stderr, "\n"); } DEBUG_FUNCTION void debug (const value_range &vr) { dump_value_range (stderr, &vr); fprintf (stderr, "\n"); } /* Return true, if VAL1 and VAL2 are equal values for VRP purposes. */ bool vrp_operand_equal_p (const_tree val1, const_tree val2) { if (val1 == val2) return true; if (!val1 || !val2 || !operand_equal_p (val1, val2, 0)) return false; return true; } void gt_ggc_mx (irange *x) { if (!x->undefined_p ()) gt_ggc_mx (x->m_type); } void gt_pch_nx (irange *x) { if (!x->undefined_p ()) gt_pch_nx (x->m_type); } void gt_pch_nx (irange *x, gt_pointer_operator op, void *cookie) { for (unsigned i = 0; i < x->m_num_ranges; ++i) { op (&x->m_base[i * 2], NULL, cookie); op (&x->m_base[i * 2 + 1], NULL, cookie); } } void gt_ggc_mx (frange *x) { gt_ggc_mx (x->m_type); } void gt_pch_nx (frange *x) { gt_pch_nx (x->m_type); } void gt_pch_nx (frange *x, gt_pointer_operator op, void *cookie) { op (&x->m_type, NULL, cookie); } void gt_ggc_mx (vrange *x) { if (is_a (*x)) return gt_ggc_mx ((irange *) x); if (is_a (*x)) return gt_ggc_mx ((frange *) x); gcc_unreachable (); } void gt_pch_nx (vrange *x) { if (is_a (*x)) return gt_pch_nx ((irange *) x); if (is_a (*x)) return gt_pch_nx ((frange *) x); gcc_unreachable (); } void gt_pch_nx (vrange *x, gt_pointer_operator op, void *cookie) { if (is_a (*x)) gt_pch_nx ((irange *) x, op, cookie); else if (is_a (*x)) gt_pch_nx ((frange *) x, op, cookie); else gcc_unreachable (); } // ?? These stubs are for ipa-prop.cc which use a value_range in a // hash_traits. hash-traits.h defines an extern of gt_ggc_mx (T &) // instead of picking up the gt_ggc_mx (T *) version. void gt_pch_nx (int_range<2> *&x) { return gt_pch_nx ((irange *) x); } void gt_ggc_mx (int_range<2> *&x) { return gt_ggc_mx ((irange *) x); } #define DEFINE_INT_RANGE_INSTANCE(N) \ template int_range::int_range(tree_node *, \ const wide_int &, \ const wide_int &, \ value_range_kind); \ template int_range::int_range(tree); \ template int_range::int_range(const irange &); \ template int_range::int_range(const int_range &); \ template int_range& int_range::operator= (const int_range &); DEFINE_INT_RANGE_INSTANCE(1) DEFINE_INT_RANGE_INSTANCE(2) DEFINE_INT_RANGE_INSTANCE(3) DEFINE_INT_RANGE_INSTANCE(255) #if CHECKING_P #include "selftest.h" #define INT(x) wi::shwi ((x), TYPE_PRECISION (integer_type_node)) #define UINT(x) wi::uhwi ((x), TYPE_PRECISION (unsigned_type_node)) #define SCHAR(x) wi::shwi ((x), TYPE_PRECISION (signed_char_type_node)) namespace selftest { static int_range<2> range (tree type, int a, int b, value_range_kind kind = VR_RANGE) { wide_int w1, w2; if (TYPE_UNSIGNED (type)) { w1 = wi::uhwi (a, TYPE_PRECISION (type)); w2 = wi::uhwi (b, TYPE_PRECISION (type)); } else { w1 = wi::shwi (a, TYPE_PRECISION (type)); w2 = wi::shwi (b, TYPE_PRECISION (type)); } return int_range<2> (type, w1, w2, kind); } static int_range<2> range_int (int a, int b, value_range_kind kind = VR_RANGE) { return range (integer_type_node, a, b, kind); } static int_range<2> range_uint (int a, int b, value_range_kind kind = VR_RANGE) { return range (unsigned_type_node, a, b, kind); } static int_range<2> range_uint128 (int a, int b, value_range_kind kind = VR_RANGE) { tree u128_type_node = build_nonstandard_integer_type (128, 1); return range (u128_type_node, a, b, kind); } static int_range<2> range_uchar (int a, int b, value_range_kind kind = VR_RANGE) { return range (unsigned_char_type_node, a, b, kind); } static int_range<2> range_char (int a, int b, value_range_kind kind = VR_RANGE) { return range (signed_char_type_node, a, b, kind); } static int_range<3> build_range3 (int a, int b, int c, int d, int e, int f) { int_range<3> i1 = range_int (a, b); int_range<3> i2 = range_int (c, d); int_range<3> i3 = range_int (e, f); i1.union_ (i2); i1.union_ (i3); return i1; } static void range_tests_irange3 () { int_range<3> r0, r1, r2; int_range<3> i1, i2, i3; // ([10,20] U [5,8]) U [1,3] ==> [1,3][5,8][10,20]. r0 = range_int (10, 20); r1 = range_int (5, 8); r0.union_ (r1); r1 = range_int (1, 3); r0.union_ (r1); ASSERT_TRUE (r0 == build_range3 (1, 3, 5, 8, 10, 20)); // [1,3][5,8][10,20] U [-5,0] => [-5,3][5,8][10,20]. r1 = range_int (-5, 0); r0.union_ (r1); ASSERT_TRUE (r0 == build_range3 (-5, 3, 5, 8, 10, 20)); // [10,20][30,40] U [50,60] ==> [10,20][30,40][50,60]. r1 = range_int (50, 60); r0 = range_int (10, 20); r0.union_ (range_int (30, 40)); r0.union_ (r1); ASSERT_TRUE (r0 == build_range3 (10, 20, 30, 40, 50, 60)); // [10,20][30,40][50,60] U [70, 80] ==> [10,20][30,40][50,60][70,80]. r1 = range_int (70, 80); r0.union_ (r1); r2 = build_range3 (10, 20, 30, 40, 50, 60); r2.union_ (range_int (70, 80)); ASSERT_TRUE (r0 == r2); // [10,20][30,40][50,60] U [6,35] => [6,40][50,60]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (6, 35); r0.union_ (r1); r1 = range_int (6, 40); r1.union_ (range_int (50, 60)); ASSERT_TRUE (r0 == r1); // [10,20][30,40][50,60] U [6,60] => [6,60]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (6, 60); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (6, 60)); // [10,20][30,40][50,60] U [6,70] => [6,70]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (6, 70); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (6, 70)); // [10,20][30,40][50,60] U [35,70] => [10,20][30,70]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (35, 70); r0.union_ (r1); r1 = range_int (10, 20); r1.union_ (range_int (30, 70)); ASSERT_TRUE (r0 == r1); // [10,20][30,40][50,60] U [15,35] => [10,40][50,60]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (15, 35); r0.union_ (r1); r1 = range_int (10, 40); r1.union_ (range_int (50, 60)); ASSERT_TRUE (r0 == r1); // [10,20][30,40][50,60] U [35,35] => [10,20][30,40][50,60]. r0 = build_range3 (10, 20, 30, 40, 50, 60); r1 = range_int (35, 35); r0.union_ (r1); ASSERT_TRUE (r0 == build_range3 (10, 20, 30, 40, 50, 60)); } static void range_tests_int_range_max () { int_range_max big; unsigned int nrange; // Build a huge multi-range range. for (nrange = 0; nrange < 50; ++nrange) { int_range<1> tmp = range_int (nrange*10, nrange *10 + 5); big.union_ (tmp); } ASSERT_TRUE (big.num_pairs () == nrange); // Verify that we can copy it without loosing precision. int_range_max copy (big); ASSERT_TRUE (copy.num_pairs () == nrange); // Inverting it should produce one more sub-range. big.invert (); ASSERT_TRUE (big.num_pairs () == nrange + 1); int_range<1> tmp = range_int (5, 37); big.intersect (tmp); ASSERT_TRUE (big.num_pairs () == 4); // Test that [10,10][20,20] does NOT contain 15. { int_range_max i1 = range_int (10, 10); int_range_max i2 = range_int (20, 20); i1.union_ (i2); ASSERT_FALSE (i1.contains_p (INT (15))); } } // Simulate -fstrict-enums where the domain of a type is less than the // underlying type. static void range_tests_strict_enum () { // The enum can only hold [0, 3]. tree rtype = copy_node (unsigned_type_node); TYPE_MIN_VALUE (rtype) = build_int_cstu (rtype, 0); TYPE_MAX_VALUE (rtype) = build_int_cstu (rtype, 3); // Test that even though vr1 covers the strict enum domain ([0, 3]), // it does not cover the domain of the underlying type. int_range<1> vr1 = range (rtype, 0, 1); int_range<1> vr2 = range (rtype, 2, 3); vr1.union_ (vr2); ASSERT_TRUE (vr1 == range (rtype, 0, 3)); ASSERT_FALSE (vr1.varying_p ()); // Test that copying to a multi-range does not change things. int_range<2> ir1 (vr1); ASSERT_TRUE (ir1 == vr1); ASSERT_FALSE (ir1.varying_p ()); // The same test as above, but using TYPE_{MIN,MAX}_VALUE instead of [0,3]. vr1 = int_range<2> (rtype, wi::to_wide (TYPE_MIN_VALUE (rtype)), wi::to_wide (TYPE_MAX_VALUE (rtype))); ir1 = vr1; ASSERT_TRUE (ir1 == vr1); ASSERT_FALSE (ir1.varying_p ()); } static void range_tests_misc () { tree u128_type = build_nonstandard_integer_type (128, /*unsigned=*/1); int_range<2> i1, i2, i3; int_range<2> r0, r1, rold; // Test 1-bit signed integer union. // [-1,-1] U [0,0] = VARYING. tree one_bit_type = build_nonstandard_integer_type (1, 0); wide_int one_bit_min = irange_val_min (one_bit_type); wide_int one_bit_max = irange_val_max (one_bit_type); { int_range<2> min = int_range<2> (one_bit_type, one_bit_min, one_bit_min); int_range<2> max = int_range<2> (one_bit_type, one_bit_max, one_bit_max); max.union_ (min); ASSERT_TRUE (max.varying_p ()); } // Test that we can set a range of true+false for a 1-bit signed int. r0 = range_true_and_false (one_bit_type); // Test inversion of 1-bit signed integers. { int_range<2> min = int_range<2> (one_bit_type, one_bit_min, one_bit_min); int_range<2> max = int_range<2> (one_bit_type, one_bit_max, one_bit_max); int_range<2> t; t = min; t.invert (); ASSERT_TRUE (t == max); t = max; t.invert (); ASSERT_TRUE (t == min); } // Test that NOT(255) is [0..254] in 8-bit land. int_range<1> not_255 = range_uchar (255, 255, VR_ANTI_RANGE); ASSERT_TRUE (not_255 == range_uchar (0, 254)); // Test that NOT(0) is [1..255] in 8-bit land. int_range<2> not_zero = range_nonzero (unsigned_char_type_node); ASSERT_TRUE (not_zero == range_uchar (1, 255)); // Check that [0,127][0x..ffffff80,0x..ffffff] // => ~[128, 0x..ffffff7f]. r0 = range_uint128 (0, 127); wide_int high = wi::minus_one (128); // low = -1 - 127 => 0x..ffffff80. wide_int low = wi::sub (high, wi::uhwi (127, 128)); r1 = int_range<1> (u128_type, low, high); // [0x..ffffff80, 0x..ffffffff] // r0 = [0,127][0x..ffffff80,0x..fffffff]. r0.union_ (r1); // r1 = [128, 0x..ffffff7f]. r1 = int_range<1> (u128_type, wi::uhwi (128, 128), wi::sub (wi::minus_one (128), wi::uhwi (128, 128))); r0.invert (); ASSERT_TRUE (r0 == r1); r0.set_varying (integer_type_node); wide_int minint = r0.lower_bound (); wide_int maxint = r0.upper_bound (); r0.set_varying (short_integer_type_node); r0.set_varying (unsigned_type_node); wide_int maxuint = r0.upper_bound (); // Check that ~[0,5] => [6,MAX] for unsigned int. r0 = range_uint (0, 5); r0.invert (); ASSERT_TRUE (r0 == int_range<1> (unsigned_type_node, wi::uhwi (6, TYPE_PRECISION (unsigned_type_node)), maxuint)); // Check that ~[10,MAX] => [0,9] for unsigned int. r0 = int_range<1> (unsigned_type_node, wi::uhwi (10, TYPE_PRECISION (unsigned_type_node)), maxuint); r0.invert (); ASSERT_TRUE (r0 == range_uint (0, 9)); // Check that ~[0,5] => [6,MAX] for unsigned 128-bit numbers. r0 = range_uint128 (0, 5, VR_ANTI_RANGE); r1 = int_range<1> (u128_type, wi::uhwi (6, 128), wi::minus_one (128)); ASSERT_TRUE (r0 == r1); // Check that [~5] is really [-MIN,4][6,MAX]. r0 = range_int (5, 5, VR_ANTI_RANGE); r1 = int_range<1> (integer_type_node, minint, INT (4)); r1.union_ (int_range<1> (integer_type_node, INT (6), maxint)); ASSERT_FALSE (r1.undefined_p ()); ASSERT_TRUE (r0 == r1); r1 = range_int (5, 5); int_range<2> r2 (r1); ASSERT_TRUE (r1 == r2); r1 = range_int (5, 10); r1 = range_int (5, 10); ASSERT_TRUE (r1.contains_p (INT (7))); r1 = range_char (0, 20); ASSERT_TRUE (r1.contains_p (SCHAR(15))); ASSERT_FALSE (r1.contains_p (SCHAR(300))); // NOT([10,20]) ==> [-MIN,9][21,MAX]. r0 = r1 = range_int (10, 20); r2 = int_range<1> (integer_type_node, minint, INT(9)); r2.union_ (int_range<1> (integer_type_node, INT(21), maxint)); ASSERT_FALSE (r2.undefined_p ()); r1.invert (); ASSERT_TRUE (r1 == r2); // Test that NOT(NOT(x)) == x. r2.invert (); ASSERT_TRUE (r0 == r2); // Test that booleans and their inverse work as expected. r0 = range_zero (boolean_type_node); ASSERT_TRUE (r0 == range_false ()); r0.invert (); ASSERT_TRUE (r0 == range_true ()); // Make sure NULL and non-NULL of pointer types work, and that // inverses of them are consistent. tree voidp = build_pointer_type (void_type_node); r0 = range_zero (voidp); r1 = r0; r0.invert (); r0.invert (); ASSERT_TRUE (r0 == r1); // [10,20] U [15, 30] => [10, 30]. r0 = range_int (10, 20); r1 = range_int (15, 30); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (10, 30)); // [15,40] U [] => [15,40]. r0 = range_int (15, 40); r1.set_undefined (); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (15, 40)); // [10,20] U [10,10] => [10,20]. r0 = range_int (10, 20); r1 = range_int (10, 10); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (10, 20)); // [10,20] U [9,9] => [9,20]. r0 = range_int (10, 20); r1 = range_int (9, 9); r0.union_ (r1); ASSERT_TRUE (r0 == range_int (9, 20)); // [10,20] ^ [15,30] => [15,20]. r0 = range_int (10, 20); r1 = range_int (15, 30); r0.intersect (r1); ASSERT_TRUE (r0 == range_int (15, 20)); // Test the internal sanity of wide_int's wrt HWIs. ASSERT_TRUE (wi::max_value (TYPE_PRECISION (boolean_type_node), TYPE_SIGN (boolean_type_node)) == wi::uhwi (1, TYPE_PRECISION (boolean_type_node))); // Test zero_p(). r0 = range_int (0, 0); ASSERT_TRUE (r0.zero_p ()); // Test nonzero_p(). r0 = range_int (0, 0); r0.invert (); ASSERT_TRUE (r0.nonzero_p ()); // r0 = ~[1,1] r0 = range_int (1, 1, VR_ANTI_RANGE); // r1 = ~[3,3] r1 = range_int (3, 3, VR_ANTI_RANGE); // vv = [0,0][2,2][4, MAX] int_range<3> vv = r0; vv.intersect (r1); ASSERT_TRUE (vv.contains_p (UINT (2))); ASSERT_TRUE (vv.num_pairs () == 3); r0 = range_int (1, 1); // And union it with [0,0][2,2][4,MAX] multi range r0.union_ (vv); // The result should be [0,2][4,MAX], or ~[3,3] but it must contain 2 ASSERT_TRUE (r0.contains_p (INT (2))); } static void range_tests_nonzero_bits () { int_range<2> r0, r1; // Adding nonzero bits to a varying drops the varying. r0.set_varying (integer_type_node); r0.set_nonzero_bits (INT (255)); ASSERT_TRUE (!r0.varying_p ()); // Dropping the nonzero bits brings us back to varying. r0.set_nonzero_bits (INT (-1)); ASSERT_TRUE (r0.varying_p ()); // Test contains_p with nonzero bits. r0.set_zero (integer_type_node); ASSERT_TRUE (r0.contains_p (INT (0))); ASSERT_FALSE (r0.contains_p (INT (1))); r0.set_nonzero_bits (INT (0xfe)); ASSERT_FALSE (r0.contains_p (INT (0x100))); ASSERT_FALSE (r0.contains_p (INT (0x3))); // Union of nonzero bits. r0.set_varying (integer_type_node); r0.set_nonzero_bits (INT (0xf0)); r1.set_varying (integer_type_node); r1.set_nonzero_bits (INT (0xf)); r0.union_ (r1); ASSERT_TRUE (r0.get_nonzero_bits () == 0xff); // Intersect of nonzero bits. r0 = range_int (0, 255); r0.set_nonzero_bits (INT (0xfe)); r1.set_varying (integer_type_node); r1.set_nonzero_bits (INT (0xf0)); r0.intersect (r1); ASSERT_TRUE (r0.get_nonzero_bits () == 0xf0); // Intersect where the mask of nonzero bits is implicit from the range. r0.set_varying (integer_type_node); r1 = range_int (0, 255); r0.intersect (r1); ASSERT_TRUE (r0.get_nonzero_bits () == 0xff); // The union of a mask of 0xff..ffff00 with a mask of 0xff spans the // entire domain, and makes the range a varying. r0.set_varying (integer_type_node); wide_int x = wi::shwi (0xff, TYPE_PRECISION (integer_type_node)); x = wi::bit_not (x); r0.set_nonzero_bits (x); // 0xff..ff00 r1.set_varying (integer_type_node); r1.set_nonzero_bits (INT (0xff)); r0.union_ (r1); ASSERT_TRUE (r0.varying_p ()); // Test that setting a nonzero bit of 1 does not pessimize the range. r0.set_zero (integer_type_node); r0.set_nonzero_bits (INT (1)); ASSERT_TRUE (r0.zero_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; bool signbit; // 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 ()); } // [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)); REAL_VALUE_TYPE nan = r; ASSERT_TRUE (r0.contains_p (nan)); // -NAN is in a VARYING. r0.set_varying (float_type_node); q = real_value_negate (&r); REAL_VALUE_TYPE neg_nan = 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 ()); // [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 ()); // 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 ()); } static void range_tests_signed_zeros () { REAL_VALUE_TYPE zero = dconst0; REAL_VALUE_TYPE neg_zero = zero; neg_zero.sign = 1; frange r0, r1; bool signbit; // [0,0] contains [0,0] but not [-0,-0] and vice versa. r0 = frange_float ("0.0", "0.0"); r1 = frange_float ("-0.0", "-0.0"); 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_float ("0.0", "0.0"); ASSERT_TRUE (r0.contains_p (zero)); ASSERT_FALSE (r0.contains_p (neg_zero)); r0 = frange_float ("-0.0", "-0.0"); ASSERT_TRUE (r0.contains_p (neg_zero)); ASSERT_FALSE (r0.contains_p (zero)); r0 = frange_float ("-0.0", "0.0"); ASSERT_TRUE (r0.contains_p (neg_zero)); ASSERT_TRUE (r0.contains_p (zero)); r0 = frange_float ("-3", "5"); ASSERT_TRUE (r0.contains_p (neg_zero)); ASSERT_TRUE (r0.contains_p (zero)); // The intersection of zeros that differ in sign is a NAN (or // undefined if not honoring NANs). r0 = frange_float ("-0.0", "-0.0"); r1 = frange_float ("0.0", "0.0"); 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_float ("0.0", "0.0"); r1 = frange_float ("-0.0", "-0.0"); r0.union_ (r1); ASSERT_TRUE (r0.zero_p () && !r0.signbit_p (signbit)); // [-0, +0] has an unknown sign. r0 = frange_float ("-0.0", "0.0"); ASSERT_TRUE (r0.zero_p () && !r0.signbit_p (signbit)); // [-0, +0] ^ [0, 0] is [0, 0] r0 = frange_float ("-0.0", "0.0"); r1 = frange_float ("0.0", "0.0"); r0.intersect (r1); ASSERT_TRUE (r0.zero_p ()); 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); if (HONOR_NANS (float_type_node)) ASSERT_TRUE (r0.known_isnan ()); else ASSERT_TRUE (r0.undefined_p ()); r0.set_nonnegative (float_type_node); if (HONOR_NANS (float_type_node)) ASSERT_TRUE (r0.maybe_isnan ()); // Numbers containing zero should have an unknown SIGNBIT. r0 = frange_float ("0", "10"); r0.clear_nan (); ASSERT_TRUE (r0.signbit_p (signbit) && !signbit); } 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 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; if (HONOR_NANS (float_type_node)) 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"); ASSERT_TRUE (r0.varying_p ()); // ...unless it has some special property... if (HONOR_NANS (r0.type ())) { 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] ^ [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 ()); if (HONOR_INFINITIES (float_type_node)) { // Make sure [-Inf, -Inf] doesn't get normalized. r0 = frange_float ("-Inf", "-Inf"); ASSERT_TRUE (real_isinf (&r0.lower_bound (), true)); ASSERT_TRUE (real_isinf (&r0.upper_bound (), true)); } // Test that reading back a global range yields the same result as // what we wrote into it. tree ssa = make_temp_ssa_name (float_type_node, NULL, "blah"); r0.set_varying (float_type_node); r0.clear_nan (); set_range_info (ssa, r0); get_global_range_query ()->range_of_expr (r1, ssa); ASSERT_EQ (r0, r1); } // Run floating range tests for various combinations of NAN and INF // support. static void range_tests_floats_various () { int save_finite_math_only = flag_finite_math_only; // Test -ffinite-math-only. flag_finite_math_only = 1; range_tests_floats (); // Test -fno-finite-math-only. flag_finite_math_only = 0; range_tests_floats (); flag_finite_math_only = save_finite_math_only; } void range_tests () { range_tests_irange3 (); range_tests_int_range_max (); range_tests_strict_enum (); range_tests_nonzero_bits (); range_tests_floats_various (); range_tests_misc (); } } // namespace selftest #endif // CHECKING_P