]> git.ipfire.org Git - thirdparty/gcc.git/commitdiff
[frange] Store a single sub-range in an array slot.
authorAldy Hernandez <aldy@quesejoda.com>
Sun, 19 Jul 2026 09:38:49 +0000 (09:38 +0000)
committerAldy Hernandez <aldy@quesejoda.com>
Tue, 28 Jul 2026 16:47:55 +0000 (18:47 +0200)
To prepare for holding more than one sub-range, move the endpoints
m_min/m_max into a one-element array.

While here, pick the low-hanging multi-range fruit so the follow-up
patches stay small: consumers that walk every sub-range now loop over
m_num_ranges, and the inherently single-range ones (normalize_kind,
singleton_p, etc) gain an m_num_ranges == 1 guard.  The hard cases
(union_/intersect, etc) are left with FIXMEs for later.

No change to functionality as MAX_PAIRS is 1.  I'll bump this to 2 in
a subequent patch, and implement union/intersect and all the other
little things.

Tested on ppc64le Linux.  No change to assembly over the LAPACK
preprocessed-Fortran corpus; LAPACK and GCC tests pass.

gcc/ChangeLog:

* value-range.h (struct frange_pair): New.
(class frange): Replace m_min and m_max with m_pairs[MAX_PAIRS]
and m_num_ranges.  Add MAX_PAIRS, num_pairs, and the indexed
lower_bound and upper_bound.  Have canonicalize_zeros take an
frange_pair.
(frange::lower_bound, frange::upper_bound): Adjust.
(frange::set_varying, frange::set_undefined, frange::set_nan)
(frange::known_isfinite, frange::known_isnormal)
(frange::known_isdenormal_or_zero, frange::maybe_isinf)
(frange::known_isinf, frange::signbit_p): Adjust.
* value-range.cc (frange::flush_denormals_to_zero): Adjust.
(frange::canonicalize_zeros): Take an frange_pair.
(frange::set, frange::normalize_kind, frange::union_nans)
(frange::union_, frange::intersect, frange::operator=)
(frange::operator==, frange::contains_p)
(frange::internal_singleton_p, frange::singleton_p)
(frange::verify_range, frange::zero_p): Adjust.
* value-range-storage.h (class frange_storage): Replace m_min
and m_max with m_pairs and m_num_ranges.
* value-range-storage.cc (frange_storage::set_frange): Adjust.
(frange_storage::get_frange): Adjust.

gcc/value-range-storage.cc
gcc/value-range-storage.h
gcc/value-range.cc
gcc/value-range.h

index 837c40bb72e63b3bc6b0a4432febf94193cf1faf..6f8b53814a8d96466b3606290b1daaf37e3e13b9 100644 (file)
@@ -524,8 +524,9 @@ frange_storage::set_frange (const frange &r)
   gcc_checking_assert (fits_p (r));
 
   m_kind = r.m_kind;
-  m_min = r.m_min;
-  m_max = r.m_max;
+  m_num_ranges = r.m_num_ranges;
+  for (unsigned i = 0; i < r.m_num_ranges; ++i)
+    m_pairs[i] = r.m_pairs[i];
   m_pos_nan = r.m_pos_nan;
   m_neg_nan = r.m_neg_nan;
 }
@@ -555,13 +556,11 @@ frange_storage::get_frange (frange &r, tree type) const
       return;
     }
 
-  // We use the constructor to create the new range instead of writing
-  // out the bits into the frange directly, because the global range
-  // being read may be being inlined into a function with different
-  // restrictions as when it was originally written.  We want to make
-  // sure the resulting range is canonicalized correctly for the new
-  // consumer.
-  r = frange (type, m_min, m_max, m_kind);
+  // FIXME: Rewrite for sub-ranges.  This only reconstructs the first pair.
+  // Eventually do it piecewise like irange_storage::get_irange: start
+  // undefined and union each sub-range built through the constructor (so
+  // every piece is re-canonicalized).
+  r = frange (type, m_pairs[0].min, m_pairs[0].max, m_kind);
 
   // The constructor will set the NAN bits for HONOR_NANS, but we must
   // make sure to set the NAN sign if known.
index e289765cfe016741c96e0267274597d6cad05ea9..a7369d1eb0f709b96591450decbea807801640c5 100644 (file)
@@ -167,8 +167,8 @@ class GTY((tag ("VR_FRANGE"))) frange_storage : public vrange_storage
   DISABLE_COPY_AND_ASSIGN (frange_storage);
 
   enum value_range_kind m_kind;
-  REAL_VALUE_TYPE m_min;
-  REAL_VALUE_TYPE m_max;
+  frange_pair m_pairs[frange::MAX_PAIRS];
+  unsigned char m_num_ranges;
   bool m_pos_nan;
   bool m_neg_nan;
 };
index 4e1be3b0ed6719f9c3dbec6b0a63a26b0a829509..09381e626026ebd7ae524f28aa9ec3d3fd1d39c2 100644 (file)
@@ -1023,6 +1023,7 @@ frange::flush_denormals_to_zero ()
 
   machine_mode mode = TYPE_MODE (type ());
 
+  // FIXME: Rewrite for sub-ranges.
   // Flush a denormal endpoint to a zero of the same sign: a +denormal lower
   // bound to +0.0, and a -denormal upper bound to -0.0.  Then call
   // canonicalize_zeros to rewrite the sign to whatever the flags make
@@ -1033,15 +1034,15 @@ frange::flush_denormals_to_zero ()
   //
   // keeping contains_p (-0.0) true; under HONOR_SIGNED_ZEROS the sign stands and
   // it stays [ +0.0, 5.0 ].
-  if (real_isdenormal (&m_max, mode) && real_isneg (&m_max))
-    m_max = dconstm0;
-  if (real_isdenormal (&m_min, mode) && !real_isneg (&m_min))
-    m_min = dconst0;
-  canonicalize_zeros (m_min, m_max);
+  if (real_isdenormal (&m_pairs[0].max, mode) && real_isneg (&m_pairs[0].max))
+    m_pairs[0].max = dconstm0;
+  if (real_isdenormal (&m_pairs[0].min, mode) && !real_isneg (&m_pairs[0].min))
+    m_pairs[0].min = dconst0;
+  canonicalize_zeros (m_pairs[0]);
 }
 
-// Canonicalize the signed zeros of the endpoints MIN and MAX according with what
-// the target and flags want:
+// Canonicalize the signed zeros of a sub-range according with what the target
+// and flags want:
 //
 //   !MODE_HAS_SIGNED_ZEROS: the mode has no signed zero, so any zero is +0.0.
 //
@@ -1051,21 +1052,21 @@ frange::flush_denormals_to_zero ()
 //   Otherwise the sign is a real distinction, and we keep it.
 
 void
-frange::canonicalize_zeros (REAL_VALUE_TYPE &min, REAL_VALUE_TYPE &max)
+frange::canonicalize_zeros (frange_pair &p)
 {
   if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type)))
     {
-      if (real_iszero (&min, 1))
-       min.sign = 0;
-      if (real_iszero (&max, 1))
-       max.sign = 0;
+      if (real_iszero (&p.min, 1))
+       p.min.sign = 0;
+      if (real_iszero (&p.max, 1))
+       p.max.sign = 0;
     }
   else if (!HONOR_SIGNED_ZEROS (m_type))
     {
-      if (real_iszero (&max, 1))
-       max.sign = 0;
-      if (real_iszero (&min, 0))
-       min.sign = 1;
+      if (real_iszero (&p.max, 1))
+       p.max.sign = 0;
+      if (real_iszero (&p.min, 0))
+       p.min.sign = 1;
     }
 }
 
@@ -1095,8 +1096,9 @@ frange::set (tree type,
 
   m_kind = kind;
   m_type = type;
-  m_min = min;
-  m_max = max;
+  m_num_ranges = 1;
+  m_pairs[0].min = min;
+  m_pairs[0].max = max;
   if (HONOR_NANS (m_type))
     {
       m_pos_nan = nan.pos_p ();
@@ -1108,7 +1110,7 @@ frange::set (tree type,
       m_neg_nan = false;
     }
 
-  canonicalize_zeros (m_min, m_max);
+  canonicalize_zeros (m_pairs[0]);
 
   // For -ffinite-math-only we can drop ranges outside the
   // representable numbers to min/max for the type.
@@ -1116,14 +1118,14 @@ frange::set (tree 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;
+      if (real_less (&m_pairs[0].min, &min_repr))
+       m_pairs[0].min = min_repr;
+      else if (real_less (&max_repr, &m_pairs[0].min))
+       m_pairs[0].min = max_repr;
+      if (real_less (&max_repr, &m_pairs[0].max))
+       m_pairs[0].max = max_repr;
+      else if (real_less (&m_pairs[0].max, &min_repr))
+       m_pairs[0].max = min_repr;
     }
 
   // Check for swapped ranges.
@@ -1162,8 +1164,9 @@ 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))
+      && m_num_ranges == 1
+      && frange_val_is_min (m_pairs[0].min, m_type)
+      && frange_val_is_max (m_pairs[0].max, m_type))
     {
       if (!HONOR_NANS (m_type) || (m_pos_nan && m_neg_nan))
        {
@@ -1176,8 +1179,9 @@ frange::normalize_kind ()
       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);
+         m_num_ranges = 1;
+         m_pairs[0].min = frange_val_min (m_type);
+         m_pairs[0].max = frange_val_max (m_type);
          if (flag_checking)
            verify_range ();
          return true;
@@ -1199,8 +1203,9 @@ frange::union_nans (const frange &r)
   if (known_isnan () && m_kind != r.m_kind)
     {
       m_kind = r.m_kind;
-      m_min = r.m_min;
-      m_max = r.m_max;
+      m_num_ranges = r.m_num_ranges;
+      for (unsigned i = 0; i < r.m_num_ranges; ++i)
+       m_pairs[i] = r.m_pairs[i];
       changed = true;
     }
   if (m_pos_nan != r.m_pos_nan || m_neg_nan != r.m_neg_nan)
@@ -1241,15 +1246,16 @@ frange::union_ (const vrange &v)
       changed = true;
     }
 
-  // Combine endpoints.
-  if (frange_cmp (r.m_min, m_min) < 0)
+  // FIXME: Rewrite for sub-ranges.
+  // Combine endpoints.  This needs to be rewritten for sub-ranges.
+  if (frange_cmp (r.m_pairs[0].min, m_pairs[0].min) < 0)
     {
-      m_min = r.m_min;
+      m_pairs[0].min = r.m_pairs[0].min;
       changed = true;
     }
-  if (frange_cmp (m_max, r.m_max) < 0)
+  if (frange_cmp (m_pairs[0].max, r.m_pairs[0].max) < 0)
     {
-      m_max = r.m_max;
+      m_pairs[0].max = r.m_pairs[0].max;
       changed = true;
     }
 
@@ -1304,21 +1310,23 @@ frange::intersect (const vrange &v)
       changed = true;
     }
 
+  // FIXME: Rewrite for sub-ranges.
   // Combine endpoints.
-  if (frange_cmp (m_min, r.m_min) < 0)
+  if (frange_cmp (m_pairs[0].min, r.m_pairs[0].min) < 0)
     {
-      m_min = r.m_min;
+      m_pairs[0].min = r.m_pairs[0].min;
       changed = true;
     }
-  if (frange_cmp (r.m_max, m_max) < 0)
+  if (frange_cmp (r.m_pairs[0].max, m_pairs[0].max) < 0)
     {
-      m_max = r.m_max;
+      m_pairs[0].max = r.m_pairs[0].max;
       changed = true;
     }
 
+  // FIXME: Rewrite for sub-ranges.
   // 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 (frange_cmp (m_pairs[0].max, m_pairs[0].min) < 0)
     {
       if (maybe_isnan ())
        m_kind = VR_NAN;
@@ -1338,8 +1346,9 @@ 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_num_ranges = src.m_num_ranges;
+  for (unsigned i = 0; i < src.m_num_ranges; ++i)
+    m_pairs[i] = src.m_pairs[i];
   m_pos_nan = src.m_pos_nan;
   m_neg_nan = src.m_neg_nan;
 
@@ -1369,9 +1378,14 @@ frange::operator== (const frange &src) const
          return false;
        }
 
-      return (real_identical (&m_min, &src.m_min)
-             && real_identical (&m_max, &src.m_max)
-             && m_pos_nan == src.m_pos_nan
+      if (m_num_ranges != src.m_num_ranges)
+       return false;
+      for (unsigned i = 0; i < m_num_ranges; ++i)
+       if (!real_identical (&m_pairs[i].min, &src.m_pairs[i].min)
+           || !real_identical (&m_pairs[i].max, &src.m_pairs[i].max))
+         return false;
+
+      return (m_pos_nan == src.m_pos_nan
              && m_neg_nan == src.m_neg_nan
              && types_compatible_p (m_type, src.m_type));
     }
@@ -1404,7 +1418,12 @@ frange::contains_p (const REAL_VALUE_TYPE &r) const
   if (known_isnan ())
     return false;
 
-  return frange_cmp (r, m_min) >= 0 && frange_cmp (r, m_max) <= 0;
+  for (unsigned i = 0; i < m_num_ranges; ++i)
+    if (frange_cmp (r, m_pairs[i].min) >= 0
+       && frange_cmp (r, m_pairs[i].max) <= 0)
+      return true;
+
+  return false;
 }
 
 // If range is a singleton, place it in RESULT and return TRUE.  If
@@ -1415,7 +1434,9 @@ frange::contains_p (const REAL_VALUE_TYPE &r) const
 bool
 frange::internal_singleton_p (REAL_VALUE_TYPE *result) const
 {
-  if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
+  if (m_kind == VR_RANGE
+      && m_num_ranges == 1
+      && real_identical (&m_pairs[0].min, &m_pairs[0].max))
     {
       // Return false for any singleton that may be a NAN.
       if (HONOR_NANS (m_type) && maybe_isnan ())
@@ -1428,16 +1449,16 @@ frange::internal_singleton_p (REAL_VALUE_TYPE *result) const
          // 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))
+         if (real_isinf (&m_pairs[0].min))
            return false;
          REAL_VALUE_TYPE r;
-         real_convert (&r, DFmode, &m_min);
-         if (real_identical (&r, &m_min))
+         real_convert (&r, DFmode, &m_pairs[0].min);
+         if (real_identical (&r, &m_pairs[0].min))
            return false;
        }
 
       if (result)
-       *result = m_min;
+       *result = m_pairs[0].min;
       return true;
     }
   return false;
@@ -1449,7 +1470,7 @@ frange::singleton_p (tree *result) const
   if (internal_singleton_p ())
     {
       if (result)
-       *result = build_real (m_type, m_min);
+       *result = build_real (m_type, m_pairs[0].min);
       return true;
     }
   return false;
@@ -1479,8 +1500,9 @@ frange::verify_range () const
       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));
+      gcc_checking_assert (m_num_ranges == 1);
+      gcc_checking_assert (frange_val_is_min (m_pairs[0].min, m_type));
+      gcc_checking_assert (frange_val_is_max (m_pairs[0].max, m_type));
       if (HONOR_NANS (m_type))
        gcc_checking_assert (m_pos_nan && m_neg_nan);
       else
@@ -1497,24 +1519,32 @@ frange::verify_range () const
       gcc_unreachable ();
     }
 
-  // NANs cannot appear in the endpoints of a range.
-  gcc_checking_assert (!real_isnan (&m_min) && !real_isnan (&m_max));
+  for (unsigned i = 0; i < m_num_ranges; ++i)
+    {
+      // NANs cannot appear in the endpoints of a range.
+      gcc_checking_assert (!real_isnan (&m_pairs[i].min)
+                          && !real_isnan (&m_pairs[i].max));
 
-  // 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);
+      // Make sure we don't have swapped ranges.
+      // This also catches [ +0.0, -0.0].
+      gcc_checking_assert (frange_cmp (m_pairs[i].min, m_pairs[i].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.
-  if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type)))
-    gcc_checking_assert (!real_iszero (&m_min, 1) && !real_iszero (&m_max, 1));
-  else if (!HONOR_SIGNED_ZEROS (m_type))
-    gcc_checking_assert (!real_iszero (&m_min, 0) && !real_iszero (&m_max, 1));
+      // A zero endpoint must carry its canonical sign.  Every producer runs
+      // canonicalize_zeros, so a zero bound can only descend from a canonical
+      // one.
+      if (!MODE_HAS_SIGNED_ZEROS (TYPE_MODE (m_type)))
+       gcc_checking_assert (!real_iszero (&m_pairs[i].min, 1)
+                            && !real_iszero (&m_pairs[i].max, 1));
+      else if (!HONOR_SIGNED_ZEROS (m_type))
+       gcc_checking_assert (!real_iszero (&m_pairs[i].min, 0)
+                            && !real_iszero (&m_pairs[i].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));
+  if (m_num_ranges == 1 && m_pos_nan && m_neg_nan)
+    gcc_checking_assert (!frange_val_is_min (m_pairs[0].min, m_type)
+                        || !frange_val_is_max (m_pairs[0].max, m_type));
 }
 
 // We can't do much with nonzeros yet.
@@ -1552,8 +1582,9 @@ bool
 frange::zero_p () const
 {
   return (m_kind == VR_RANGE
-         && real_iszero (&m_min)
-         && real_iszero (&m_max));
+         && m_num_ranges == 1
+         && real_iszero (&m_pairs[0].min)
+         && real_iszero (&m_pairs[0].max));
 }
 
 // Set the range to non-negative numbers, that is [+0.0, +INF].
index 5bea3cbc9d4cd57742db5dc2e68001740c037e2e..2ea0006ba9fd2c73c9531be3cae6bc71dac3ed7b 100644 (file)
@@ -584,10 +584,18 @@ nan_state::neg_p () const
   return m_neg_nan;
 }
 
+// A sub-range in an frange.
+
+struct frange_pair
+{
+  REAL_VALUE_TYPE min;
+  REAL_VALUE_TYPE max;
+};
+
 // A subset of possible values for a floating point type.
 //
-// The representation is a type with a couple of endpoints, unioned
-// with a subset of { -NaN, +NaN }.
+// The representation is a single interval, unioned with a subset of
+// { -NaN, +NaN }.
 
 class frange final : public vrange
 {
@@ -657,6 +665,11 @@ public:
   bool known_isnormal () const;
   bool known_isdenormal_or_zero () const;
   virtual void verify_range () const override;
+
+  static const unsigned int MAX_PAIRS = 1;
+  unsigned num_pairs () const { return m_num_ranges; }
+  const REAL_VALUE_TYPE &lower_bound (unsigned pair) const;
+  const REAL_VALUE_TYPE &upper_bound (unsigned pair) const;
 protected:
   virtual bool contains_p (tree cst) const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
@@ -666,11 +679,11 @@ private:
   bool normalize_kind ();
   bool union_nans (const frange &);
   bool intersect_nans (const frange &);
-  void canonicalize_zeros (REAL_VALUE_TYPE &, REAL_VALUE_TYPE &);
+  void canonicalize_zeros (frange_pair &);
 
   tree m_type;
-  REAL_VALUE_TYPE m_min;
-  REAL_VALUE_TYPE m_max;
+  frange_pair m_pairs[MAX_PAIRS];
+  unsigned char m_num_ranges;
   bool m_pos_nan;
   bool m_neg_nan;
 };
@@ -679,14 +692,30 @@ inline const REAL_VALUE_TYPE &
 frange::lower_bound () const
 {
   gcc_checking_assert (!undefined_p () && !known_isnan ());
-  return m_min;
+  return m_pairs[0].min;
 }
 
 inline const REAL_VALUE_TYPE &
 frange::upper_bound () const
 {
   gcc_checking_assert (!undefined_p () && !known_isnan ());
-  return m_max;
+  return m_pairs[m_num_ranges - 1].max;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::lower_bound (unsigned pair) const
+{
+  gcc_checking_assert (!undefined_p () && !known_isnan ());
+  gcc_checking_assert (pair < m_num_ranges);
+  return m_pairs[pair].min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound (unsigned pair) const
+{
+  gcc_checking_assert (!undefined_p () && !known_isnan ());
+  gcc_checking_assert (pair < m_num_ranges);
+  return m_pairs[pair].max;
 }
 
 // Return the NAN state.
@@ -1634,8 +1663,9 @@ frange::set_varying (tree type)
 {
   m_kind = VR_VARYING;
   m_type = type;
-  m_min = frange_val_min (type);
-  m_max = frange_val_max (type);
+  m_num_ranges = 1;
+  m_pairs[0].min = frange_val_min (type);
+  m_pairs[0].max = frange_val_max (type);
   if (HONOR_NANS (m_type))
     {
       m_pos_nan = true;
@@ -1653,9 +1683,10 @@ frange::set_undefined ()
 {
   m_kind = VR_UNDEFINED;
   m_type = NULL;
+  m_num_ranges = 1;
   m_pos_nan = false;
   m_neg_nan = false;
-  // m_min and m_min are uninitialized as they are REAL_VALUE_TYPE ??.
+  // Leave the rest undefined; as it speeds up initializing undefined ranges.
   if (flag_checking)
     verify_range ();
 }
@@ -1789,6 +1820,7 @@ frange::set_nan (tree type, const nan_state &nan)
     {
       m_kind = VR_NAN;
       m_type = type;
+      m_num_ranges = 1;
       m_neg_nan = nan.neg_p ();
       m_pos_nan = nan.pos_p ();
       if (flag_checking)
@@ -1823,7 +1855,9 @@ frange::known_isfinite () const
 {
   if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE)
     return false;
-  return (!maybe_isnan () && !real_isinf (&m_min) && !real_isinf (&m_max));
+  return (!maybe_isnan ()
+         && !real_isinf (&lower_bound ())
+         && !real_isinf (&upper_bound ()));
 }
 
 // Return TRUE if range is known to be normal.
@@ -1835,9 +1869,11 @@ frange::known_isnormal () const
     return false;
 
   machine_mode mode = TYPE_MODE (type ());
-  return (!real_isdenormal (&m_min, mode) && !real_isdenormal (&m_max, mode)
-         && !real_iszero (&m_min) && !real_iszero (&m_max)
-         && (!real_isneg (&m_min) || real_isneg (&m_max)));
+  const REAL_VALUE_TYPE &min = lower_bound ();
+  const REAL_VALUE_TYPE &max = upper_bound ();
+  return (!real_isdenormal (&min, mode) && !real_isdenormal (&max, mode)
+         && !real_iszero (&min) && !real_iszero (&max)
+         && (!real_isneg (&min) || real_isneg (&max)));
 }
 
 // Return TRUE if range is known to be denormal.
@@ -1849,8 +1885,10 @@ frange::known_isdenormal_or_zero () const
     return false;
 
   machine_mode mode = TYPE_MODE (type ());
-  return ((real_isdenormal (&m_min, mode) || real_iszero (&m_min))
-         && (real_isdenormal (&m_max, mode) || real_iszero (&m_max)));
+  const REAL_VALUE_TYPE &min = lower_bound ();
+  const REAL_VALUE_TYPE &max = upper_bound ();
+  return ((real_isdenormal (&min, mode) || real_iszero (&min))
+         && (real_isdenormal (&max, mode) || real_iszero (&max)));
 }
 
 // Return TRUE if range may be infinite.
@@ -1862,7 +1900,7 @@ frange::maybe_isinf () const
     return false;
   if (varying_p ())
     return true;
-  return real_isinf (&m_min) || real_isinf (&m_max);
+  return real_isinf (&lower_bound ()) || real_isinf (&upper_bound ());
 }
 
 // Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF].
@@ -1871,9 +1909,10 @@ inline bool
 frange::known_isinf () const
 {
   return (m_kind == VR_RANGE
+         && m_num_ranges == 1
          && !maybe_isnan ()
-         && real_identical (&m_min, &m_max)
-         && real_isinf (&m_min));
+         && real_identical (&m_pairs[0].min, &m_pairs[0].max)
+         && real_isinf (&m_pairs[0].min));
 }
 
 // Return TRUE if range is possibly a NAN.
@@ -1921,9 +1960,9 @@ frange::signbit_p (bool &signbit) const
   // No NAN.
   if (!m_pos_nan && !m_neg_nan)
     {
-      if (m_min.sign == m_max.sign)
+      if (lower_bound ().sign == upper_bound ().sign)
        {
-         signbit = m_min.sign;
+         signbit = lower_bound ().sign;
          return true;
        }
       return false;
@@ -1931,7 +1970,8 @@ frange::signbit_p (bool &signbit) const
   // NAN with known sign.
   bool nan_sign = m_neg_nan;
   if (known_isnan ()
-      || (nan_sign == m_min.sign && nan_sign == m_max.sign))
+      || (nan_sign == lower_bound ().sign
+         && nan_sign == upper_bound ().sign))
     {
       signbit = nan_sign;
       return true;