[gcc r17-2645] frange: Add a comparison function for range endpoints
Aldy Hernandez via Gcc-cvs <[email protected]>
| Newsgroups | gmane.comp.gcc.cvs |
|---|---|
| Message-ID | <[email protected]> |
https://gcc.gnu.org/g:93a9ff5383a253d5923adc0c8cc21d5eb4afce54 commit r17-2645-g93a9ff5383a253d5923adc0c8cc21d5eb4afce54 Author: Aldy Hernandez <[email protected]> Date: Tue Jul 14 15:06:25 2026 +0000 frange: Add a comparison function for range endpoints IEEE calls -0.0 and +0.0 equal, but in frange they are distinct: [+0.0, x] excludes -0.0 and [-0.0, x] includes it. We have been fixing things on the fly at each modification site, either manually or by calling combine_zeros to fix up the signs. This was hacky at best. This patch removes the signed zero special casing by providing frange_cmp(), a comparison function that orders -0.0 strictly below +0.0. The ranges themselves are unchanged. The only difference is that we are now more exact about whether we actually modified a range containing a zero. Previously combine_zeros flagged a change whenever the two ranges disagreed on the sign of a zero endpoint, even when the canonical result was the sign we already had. This made union_ and intersect return a changed range pessimistically. We are now more accurate, so in theory the ranger cache should be happier. With these cleanups we can start implementing multi-ranges for frange without any special casing for signed zeros. Tested on x86-64 and ppc64le Linux. No changes to assembly for a corpus of preprocessed files from LAPACK. Also, LAPACK and GCC tests pass as usual. gcc/ChangeLog: * value-range.cc (frange_cmp): New. (frange::combine_zeros): Remove. (frange::union_): Use frange_cmp to merge endpoints. (frange::intersect): Likewise. (frange::contains_p): Use frange_cmp. (frange::verify_range): Use frange_cmp. * value-range.h (class frange): Remove combine_zeros. Diff: --- gcc/value-range.cc | 101 ++++++++++++++++++++--------------------------------- gcc/value-range.h | 1 - 2 files changed, 38 insertions(+), 64 deletions(-) diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 9e689ffbc0e0..4e1be3b0ed67 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -986,6 +986,33 @@ frange::fits_p (const vrange &) const return true; } +// Compare two range endpoints. +// +// In IEEE -0.0 and +0.0 equal for comparison purposes, but as endpoints they +// are distinct. Order -0.0 strictly below +0.0 and use this rather than +// real_less/real_compare, and the signed zeros stop needing a special case. + +static int +frange_cmp (const REAL_VALUE_TYPE &a, const REAL_VALUE_TYPE &b) +{ + gcc_checking_assert (!real_isnan (&a) && !real_isnan (&b)); + + if (real_less (&a, &b)) + return -1; + if (real_less (&b, &a)) + return 1; + if (real_iszero (&a) && real_iszero (&b)) + { + bool nega = real_isneg (&a); + bool negb = real_isneg (&b); + if (nega && !negb) + return -1; + if (!nega && negb) + return 1; + } + return 0; +} + // Flush denormal endpoints to the appropriate 0.0. void @@ -1161,44 +1188,6 @@ frange::normalize_kind () 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 @@ -1253,20 +1242,17 @@ frange::union_ (const vrange &v) } // Combine endpoints. - if (real_less (&r.m_min, &m_min)) + if (frange_cmp (r.m_min, m_min) < 0) { m_min = r.m_min; changed = true; } - if (real_less (&m_max, &r.m_max)) + if (frange_cmp (m_max, r.m_max) < 0) { m_max = r.m_max; changed = true; } - if (HONOR_SIGNED_ZEROS (m_type)) - changed |= combine_zeros (r, true); - changed |= normalize_kind (); return changed; } @@ -1319,18 +1305,20 @@ frange::intersect (const vrange &v) } // Combine endpoints. - if (real_less (&m_min, &r.m_min)) + if (frange_cmp (m_min, r.m_min) < 0) { m_min = r.m_min; changed = true; } - if (real_less (&r.m_max, &m_max)) + if (frange_cmp (r.m_max, m_max) < 0) { 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 the endpoints are swapped, the resulting range is empty. This also + // catches [+0.0, -0.0], which is also empty. + if (frange_cmp (m_max, m_min) < 0) { if (maybe_isnan ()) m_kind = VR_NAN; @@ -1341,9 +1329,6 @@ frange::intersect (const vrange &v) return true; } - if (HONOR_SIGNED_ZEROS (m_type)) - changed |= combine_zeros (r, false); - changed |= normalize_kind (); return changed; } @@ -1419,14 +1404,7 @@ frange::contains_p (const REAL_VALUE_TYPE &r) const 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; + return frange_cmp (r, m_min) >= 0 && frange_cmp (r, m_max) <= 0; } // If range is a singleton, place it in RESULT and return TRUE. If @@ -1522,11 +1500,8 @@ frange::verify_range () const // 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))); + // Make sure we don't have swapped ranges. This also catches [ +0.0, -0.0]. + gcc_checking_assert (frange_cmp (m_min, m_max) <= 0); // A zero endpoint must carry its canonical sign. Every producer runs // canonicalize_zeros, so a zero bound can only descend from a canonical one. diff --git a/gcc/value-range.h b/gcc/value-range.h index 5d547bd7727f..3226e53cb710 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -666,7 +666,6 @@ private: bool normalize_kind (); bool union_nans (const frange &); bool intersect_nans (const frange &); - bool combine_zeros (const frange &, bool union_p); void canonicalize_zeros (REAL_VALUE_TYPE &, REAL_VALUE_TYPE &); tree m_type;