Implement known/maybe fpclassify like API for frange.
authorAldy Hernandez <aldyh@redhat.com>
Thu, 8 Sep 2022 06:11:43 +0000 (08:11 +0200)
committerAldy Hernandez <aldyh@redhat.com>
Thu, 8 Sep 2022 10:56:16 +0000 (12:56 +0200)
gcc/ChangeLog:

* gimple-range-fold.cc
(fold_using_range::range_of_builtin_int_call): Use fpclassify like API.
* range-op-float.cc (finite_operand_p): Same.
(finite_operands_p): Same.
(foperator_lt::fold_range): Same.
(foperator_le::fold_range): Same.
(foperator_gt::fold_range): Same.
(foperator_ge::fold_range): Same.
(foperator_unordered::fold_range): Same.
(foperator_unordered::op1_range): Same.
(foperator_ordered::fold_range): Same.
* value-range.cc (frange::set_nan): Same.
(frange::set_signbit): Same.
(frange::union_): Same.
(frange::intersect): Same.
(frange::operator==): Same.
(frange::singleton_p): Same.
(frange::verify_range): Same.
(range_tests_nan): Same.
(range_tests_floats): Same.
* value-range.h(frange::known_finite): New.
(frange::maybe_inf): New.
(frange::known_inf): New.
(frange::maybe_nan): New.
(frange::known_nan): New.
(frange::known_signbit): New.

gcc/gimple-range-fold.cc
gcc/range-op-float.cc
gcc/value-range.cc
gcc/value-range.h

index c9c7a2c..85ed6f9 100644 (file)
@@ -1029,15 +1029,16 @@ fold_using_range::range_of_builtin_int_call (irange &r, gcall *call,
        frange tmp;
        if (src.get_operand (tmp, arg))
          {
-           if (tmp.get_signbit ().varying_p ()
-               // FIXME: We don't support signed NANs yet.
-               || !tmp.get_nan ().no_p ())
-             return false;
-           if (tmp.get_signbit ().yes_p ())
-             r.set_nonzero (type);
-           else
-             r.set_zero (type);
-           return true;
+           bool signbit;
+           if (tmp.known_signbit (signbit))
+             {
+               if (signbit)
+                 r.set_nonzero (type);
+               else
+                 r.set_zero (type);
+               return true;
+             }
+           return false;
          }
        break;
       }
index 5fbbaa1..0f928b6 100644 (file)
@@ -167,7 +167,7 @@ frange_set_nan (frange &r, tree type)
 static inline bool
 finite_operand_p (const frange &op1)
 {
-  return flag_finite_math_only || op1.get_nan ().no_p ();
+  return flag_finite_math_only || !op1.maybe_nan ();
 }
 
 // Return TRUE if OP1 and OP2 are known to be free of NANs.
@@ -175,9 +175,7 @@ finite_operand_p (const frange &op1)
 static inline bool
 finite_operands_p (const frange &op1, const frange &op2)
 {
-  return (flag_finite_math_only
-         || (op1.get_nan ().no_p ()
-             && op2.get_nan ().no_p ()));
+  return flag_finite_math_only || (!op1.maybe_nan () && !op2.maybe_nan ());
 }
 
 // Floating version of relop_early_resolve that takes into account NAN
@@ -546,7 +544,7 @@ foperator_lt::fold_range (irange &r, tree type,
       else
        r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -648,7 +646,7 @@ foperator_le::fold_range (irange &r, tree type,
       else
        r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -742,7 +740,7 @@ foperator_gt::fold_range (irange &r, tree type,
       else
        r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -844,7 +842,7 @@ foperator_ge::fold_range (irange &r, tree type,
       else
        r = range_true_and_false (type);
     }
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -927,10 +925,10 @@ foperator_unordered::fold_range (irange &r, tree type,
                                 relation_kind) const
 {
   // UNORDERED is TRUE if either operand is a NAN.
-  if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  if (op1.known_nan () || op2.known_nan ())
     r = range_true (type);
   // UNORDERED is FALSE if neither operand is a NAN.
-  else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+  else if (!op1.maybe_nan () && !op2.maybe_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
@@ -949,7 +947,7 @@ foperator_unordered::op1_range (frange &r, tree type,
       r.set_varying (type);
       // Since at least one operand must be NAN, if one of them is
       // not, the other must be.
-      if (op2.get_nan ().no_p ())
+      if (!op2.maybe_nan ())
        frange_set_nan (r, type);
       break;
 
@@ -993,11 +991,9 @@ foperator_ordered::fold_range (irange &r, tree type,
                               const frange &op1, const frange &op2,
                               relation_kind) const
 {
-  // ORDERED is TRUE if neither operand is a NAN.
-  if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+  if (!op1.maybe_nan () && !op2.maybe_nan ())
     r = range_true (type);
-  // ORDERED is FALSE if either operand is a NAN.
-  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+  else if (op1.known_nan () || op2.known_nan ())
     r = range_false (type);
   else
     r = range_true_and_false (type);
index c3f668a..adcaaa2 100644 (file)
@@ -274,7 +274,7 @@ frange::set_nan (fp_prop::kind k)
 {
   if (k == fp_prop::YES)
     {
-      if (get_nan ().no_p ())
+      if (!maybe_nan ())
        {
          set_undefined ();
          return;
@@ -284,7 +284,7 @@ frange::set_nan (fp_prop::kind k)
       return;
     }
 
-  if (k == fp_prop::NO && get_nan ().yes_p ())
+  if (k == fp_prop::NO && known_nan ())
     {
       set_undefined ();
       return;
@@ -308,21 +308,18 @@ frange::set_signbit (fp_prop::kind k)
   gcc_checking_assert (m_type);
 
   // No additional adjustments are needed for a NAN.
-  if (get_nan ().yes_p ())
+  if (known_nan ())
     {
       m_props.set_signbit (k);
       return;
     }
   // Ignore sign changes when they're set correctly.
-  if (real_less (&m_max, &dconst0))
-    {
-      gcc_checking_assert (get_signbit ().yes_p ());
-      return;
-    }
-  if (real_less (&dconst0, &m_min))
+  if (!maybe_nan ())
     {
-      gcc_checking_assert (get_signbit ().no_p ());
-      return;
+      if (real_less (&m_max, &dconst0))
+       return;
+      if (real_less (&dconst0, &m_min))
+       return;
     }
   // Adjust the range depending on the sign bit.
   if (k == fp_prop::YES)
@@ -330,17 +327,22 @@ frange::set_signbit (fp_prop::kind k)
       // Crop the range to [-INF, 0].
       frange crop (m_type, dconstninf, dconst0);
       intersect (crop);
-      m_props.set_signbit (fp_prop::YES);
+      if (!undefined_p ())
+       m_props.set_signbit (fp_prop::YES);
     }
   else if (k == fp_prop::NO)
     {
       // Crop the range to [0, +INF].
       frange crop (m_type, dconst0, dconstinf);
       intersect (crop);
-      m_props.set_signbit (fp_prop::NO);
+      if (!undefined_p ())
+       m_props.set_signbit (fp_prop::NO);
     }
   else
-    m_props.set_signbit (fp_prop::VARYING);
+    {
+      m_props.set_signbit (fp_prop::VARYING);
+      normalize_kind ();
+    }
 
   if (flag_checking)
     verify_range ();
@@ -467,7 +469,7 @@ frange::union_ (const vrange &v)
 
   // If one side has a NAN, the union is the other side, plus the union
   // of the properties and the possibility of a NAN.
-  if (get_nan ().yes_p ())
+  if (known_nan ())
     {
       frange_props save = m_props;
       *this = r;
@@ -478,7 +480,7 @@ frange::union_ (const vrange &v)
        verify_range ();
       return true;
     }
-  if (r.get_nan ().yes_p ())
+  if (r.known_nan ())
     {
       m_props.union_ (r.m_props);
       set_nan (fp_prop::VARYING);
@@ -525,7 +527,7 @@ frange::intersect (const vrange &v)
 
   // If two NANs are not exactly the same, drop to an unknown NAN,
   // otherwise there's nothing to do.
-  if (get_nan ().yes_p () && r.get_nan ().yes_p ())
+  if (known_nan () && r.known_nan ())
     {
       if (m_props == r.m_props)
        return false;
@@ -534,7 +536,7 @@ frange::intersect (const vrange &v)
       return true;
     }
   // ?? Perhaps the intersection of a NAN and anything is a NAN ??.
-  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+  if (known_nan () || r.known_nan ())
     {
       set_varying (m_type);
       return true;
@@ -590,8 +592,7 @@ frange::operator== (const frange &src) const
       if (varying_p ())
        return types_compatible_p (m_type, src.m_type);
 
-      if (m_props.get_nan ().yes_p ()
-         || src.m_props.get_nan ().yes_p ())
+      if (known_nan () || src.known_nan ())
        return false;
 
       return (real_identical (&m_min, &src.m_min)
@@ -621,6 +622,9 @@ frange::contains_p (tree cst) const
     {
       if (HONOR_SIGNED_ZEROS (m_type) && real_iszero (rv))
        {
+         // FIXME: This is still using get_signbit() instead of
+         // known_signbit() because the latter bails on possible NANs
+         // (for now).
          if (get_signbit ().yes_p ())
            return real_isneg (rv);
          else if (get_signbit ().no_p ())
@@ -644,22 +648,25 @@ frange::singleton_p (tree *result) const
   if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
     {
       // Return false for any singleton that may be a NAN.
-      if (HONOR_NANS (m_type) && !get_nan ().no_p ())
+      if (HONOR_NANS (m_type) && maybe_nan ())
        return false;
 
       // Return the appropriate zero if known.
       if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
        {
-         if (get_signbit ().no_p ())
+         bool signbit;
+         if (known_signbit (signbit))
            {
-             if (result)
-               *result = build_real (m_type, dconst0);
-             return true;
-           }
-         if (get_signbit ().yes_p ())
-           {
-             if (result)
-               *result = build_real (m_type, real_value_negate (&dconst0));
+             if (signbit)
+               {
+                 if (result)
+                   *result = build_real (m_type, real_value_negate (&dconst0));
+               }
+             else
+               {
+                 if (result)
+                   *result = build_real (m_type, dconst0);
+               }
              return true;
            }
          return false;
@@ -701,7 +708,7 @@ frange::verify_range ()
     {
       // If either is a NAN, both must be a NAN.
       gcc_checking_assert (real_identical (&m_min, &m_max));
-      gcc_checking_assert (get_nan ().yes_p ());
+      gcc_checking_assert (known_nan ());
     }
   else
     // Make sure we don't have swapped ranges.
@@ -710,7 +717,7 @@ frange::verify_range ()
   // If we're absolutely sure we have a NAN, the endpoints should
   // reflect this, otherwise we'd have more than one way to represent
   // a NAN.
-  if (m_props.get_nan ().yes_p ())
+  if (known_nan ())
     {
       gcc_checking_assert (real_isnan (&m_min));
       gcc_checking_assert (real_isnan (&m_max));
@@ -718,10 +725,14 @@ frange::verify_range ()
   else
     {
       // Make sure the signbit and range agree.
-      if (m_props.get_signbit ().yes_p ())
-       gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0));
-      else if (m_props.get_signbit ().no_p ())
-       gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0));
+      bool signbit;
+      if (known_signbit (signbit))
+       {
+         if (signbit)
+           gcc_checking_assert (real_compare (LE_EXPR, &m_max, &dconst0));
+         else
+           gcc_checking_assert (real_compare (GE_EXPR, &m_min, &dconst0));
+       }
     }
 
   // If all the properties are clear, we better not span the entire
@@ -3637,7 +3648,7 @@ range_tests_nan ()
   ASSERT_FALSE (r0 == r0);
   ASSERT_TRUE (r0 != r0);
 
-  // [5,6] U NAN is [5,6] with an unknown NAN bit.
+  // [5,6] U NAN.
   r0 = frange_float ("5", "6");
   r0.set_nan (fp_prop::NO);
   r1 = frange_nan (float_type_node);
@@ -3646,7 +3657,7 @@ range_tests_nan ()
   real_from_string (&r, "6");
   ASSERT_TRUE (real_identical (&q, &r0.lower_bound ()));
   ASSERT_TRUE (real_identical (&r, &r0.upper_bound ()));
-  ASSERT_TRUE (r0.get_nan ().varying_p ());
+  ASSERT_TRUE (r0.maybe_nan ());
 
   // NAN U NAN = NAN
   r0 = frange_nan (float_type_node);
@@ -3654,7 +3665,7 @@ range_tests_nan ()
   r0.union_ (r1);
   ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
   ASSERT_TRUE (real_isnan (&r1.upper_bound ()));
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // [INF, INF] ^ NAN = VARYING
   r0 = frange_nan (float_type_node);
@@ -3666,18 +3677,18 @@ range_tests_nan ()
   r0 = frange_nan (float_type_node);
   r1 = frange_nan (float_type_node);
   r0.intersect (r1);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // VARYING ^ NAN = NAN.
   r0 = frange_nan (float_type_node);
   r1.set_varying (float_type_node);
   r0.intersect (r1);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
 
   // Setting the NAN bit to yes, forces to range to [NAN, NAN].
   r0.set_varying (float_type_node);
   r0.set_nan (fp_prop::YES);
-  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  ASSERT_TRUE (r0.known_nan ());
   ASSERT_TRUE (real_isnan (&r0.lower_bound ()));
   ASSERT_TRUE (real_isnan (&r0.upper_bound ()));
 }
@@ -3689,6 +3700,7 @@ range_tests_signed_zeros ()
   tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero);
   REAL_VALUE_TYPE q, r;
   frange r0, r1;
+  bool signbit;
 
   // Since -0.0 == +0.0, a range of [-0.0, -0.0] should contain +0.0
   // and vice versa.
@@ -3722,7 +3734,7 @@ range_tests_signed_zeros ()
   r1 = frange (zero, zero);
   r1.set_signbit (fp_prop::YES);
   r0.union_ (r1);
-  ASSERT_TRUE (r0.zero_p () && r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (r0.zero_p () && !r0.known_signbit (signbit));
 
   // NAN U [5,6] should be [5,6] with no sign info.
   r0 = frange_nan (float_type_node);
@@ -3732,13 +3744,14 @@ range_tests_signed_zeros ()
   real_from_string (&r, "6");
   ASSERT_TRUE (real_identical (&q, &r0.lower_bound ()));
   ASSERT_TRUE (real_identical (&r, &r0.upper_bound ()));
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (!r0.known_signbit (signbit));
 }
 
 static void
 range_tests_signbit ()
 {
   frange r0, r1;
+  bool signbit;
 
   // Setting the signbit drops the range to [-INF, 0].
   r0.set_varying (float_type_node);
@@ -3750,35 +3763,42 @@ range_tests_signbit ()
   // the signbit property set.
   r0 = frange_float ("-5", "10");
   r0.set_signbit (fp_prop::YES);
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && signbit);
   r1 = frange_float ("-5", "0");
   ASSERT_TRUE (real_identical (&r0.lower_bound (), &r1.lower_bound ()));
   ASSERT_TRUE (real_identical (&r0.upper_bound (), &r1.upper_bound ()));
 
   // Negative numbers should have the SIGNBIT set.
   r0 = frange_float ("-5", "-1");
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && signbit);
   // Positive numbers should have the SIGNBIT clear.
   r0 = frange_float ("1", "10");
-  ASSERT_TRUE (r0.get_signbit ().no_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
   // Numbers containing zero should have an unknown SIGNBIT.
   r0 = frange_float ("0", "10");
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (!r0.known_signbit (signbit));
   // Numbers spanning both positive and negative should have an
   // unknown SIGNBIT.
   r0 = frange_float ("-10", "10");
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (!r0.known_signbit (signbit));
   r0.set_varying (float_type_node);
-  ASSERT_TRUE (r0.get_signbit ().varying_p ());
+  ASSERT_TRUE (!r0.known_signbit (signbit));
 
   // Ignore signbit changes when the sign bit is obviously known from
   // the range.
   r0 = frange_float ("5", "10");
+  r0.set_nan (fp_prop::NO);
   r0.set_signbit (fp_prop::VARYING);
-  ASSERT_TRUE (r0.get_signbit ().no_p ());
+  ASSERT_TRUE (r0.known_signbit (signbit) && !signbit);
   r0 = frange_float ("-5", "-1");
   r0.set_signbit (fp_prop::NO);
-  ASSERT_TRUE (r0.get_signbit ().yes_p ());
+  r0.set_nan (fp_prop::NO);
+  ASSERT_TRUE (r0.undefined_p ());
 }
 
 static void
@@ -3795,7 +3815,7 @@ range_tests_floats ()
   // A range of [-INF,+INF] is actually VARYING if no other properties
   // are set.
   r0 = frange_float ("-Inf", "+Inf");
-  if (r0.get_nan ().varying_p ())
+  if (r0.maybe_nan ())
     ASSERT_TRUE (r0.varying_p ());
   // ...unless it has some special property...
   r0.set_nan (fp_prop::NO);
index 645dc76..f9a01ee 100644 (file)
@@ -330,6 +330,7 @@ private:
 class frange : public vrange
 {
   friend class frange_storage_slot;
+  friend class vrange_printer;
 public:
   frange ();
   frange (const frange &);
@@ -366,12 +367,20 @@ public:
   const REAL_VALUE_TYPE &lower_bound () const;
   const REAL_VALUE_TYPE &upper_bound () const;
 
+  // fpclassify like API
+  bool known_finite () const;
+  bool maybe_inf () const;
+  bool known_inf () const;
+  bool maybe_nan () const;
+  bool known_nan () const;
+  bool known_signbit (bool &signbit) const;
+
   // Accessors for FP properties.
-  fp_prop get_nan () const { return m_props.get_nan (); }
   void set_nan (fp_prop::kind f);
-  fp_prop get_signbit () const { return m_props.get_signbit (); }
   void set_signbit (fp_prop::kind);
 private:
+  fp_prop get_nan () const { return m_props.get_nan (); }
+  fp_prop get_signbit () const { return m_props.get_signbit (); }
   void verify_range ();
   bool normalize_kind ();
 
@@ -1187,4 +1196,69 @@ frange_nan (tree type)
   return frange (type, r, r);
 }
 
+// Return TRUE if range is known to be finite.
+
+inline bool
+frange::known_finite () const
+{
+  if (undefined_p () || varying_p () || m_kind == VR_ANTI_RANGE)
+    return false;
+  return (!real_isnan (&m_min)
+         && !real_isinf (&m_min)
+         && !real_isinf (&m_max));
+}
+
+// Return TRUE if range may be infinite.
+
+inline bool
+frange::maybe_inf () const
+{
+  if (undefined_p () || m_kind == VR_ANTI_RANGE)
+    return false;
+  if (varying_p ())
+    return true;
+  return real_isinf (&m_min) || real_isinf (&m_max);
+}
+
+// Return TRUE if range is known to be the [-INF,-INF] or [+INF,+INF].
+
+inline bool
+frange::known_inf () const
+{
+  return (m_kind == VR_RANGE
+         && real_identical (&m_min, &m_max)
+         && real_isinf (&m_min));
+}
+
+// Return TRUE if range is possibly a NAN.
+
+inline bool
+frange::maybe_nan () const
+{
+  return !get_nan ().no_p ();
+}
+
+// Return TRUE if range is a +NAN or -NAN.
+
+inline bool
+frange::known_nan () const
+{
+  return get_nan ().yes_p ();
+}
+
+// If the signbit for the range is known, set it in SIGNBIT and return
+// TRUE.
+
+inline bool
+frange::known_signbit (bool &signbit) const
+{
+  // FIXME: Signed NANs are not supported yet.
+  if (maybe_nan ())
+    return false;
+  if (get_signbit ().varying_p ())
+    return false;
+  signbit = get_signbit ().yes_p ();
+  return true;
+}
+
 #endif // GCC_VALUE_RANGE_H