From: Aldy Hernandez <[email protected]>

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.

Pushed.

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.
---
 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 9e689ffbc0e..4e1be3b0ed6 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 5d547bd7727..3226e53cb71 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;
-- 
2.47.3

Reply via email to