On Thu, Sep 8, 2022 at 9:27 AM Richard Biener
<richard.guent...@gmail.com> wrote:
>
>
>
> > Am 08.09.2022 um 08:28 schrieb Aldy Hernandez <al...@redhat.com>:
> >
> > This is what I have in mind for the fpclassify-like methods on the
> > current implementation.  I'll get to the removal of the tristates after
> > Cauldron.
> >
> > As you mentioned, isnormal is kinda tricky, and likely to be confusing
> > for the user.  We can revisit it if it's important.
>
> Yeah.
>
> > ?? I assume maybe_inf() does not return true for NAN ??
>
> Only maybe_nan and known_nan return true for NaN.
>
> >
> > Also, what are your thoughts on signbit?  Perhaps a method returning
> > true if we're sure, and the actual signbit result as a reference?
> >
> >    bool known_signbit (const frange &r, int &signbit);
>
> That works for me.  You could look at

ok.  settled on:
+  bool known_signbit (bool &signbit) const;

It even cleaned up the __builtin_signbit folding kludge because now we
can just bail on NANs from known_signbit until they're properly
handled.

> Tree_expr_nonnegative_p to see if that’s good enough to be used there.  Not 
> sure if -0. is negative or if only x for which x < 0. counts as such..  But 
> yes, access to the sign bit is useful.

We're definitely keeping track of signed zeros.  That was the whole
point of the sign bit.  I'll be removing it also, as it's a tristate.

tree_expr_nonnegative_p could definitely be made to make use of
it...especially because we have global ranges.  I'll look into it.

>
> > How does this look?  I must say, the uses look much cleaner.
>
> Yes, I like it.

Attached is the patch I'm pushing.

Tested on x86-64 Linux.

Thanks.
Aldy

>
> Richard
>
> > Aldy
> >
> > 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.
> > ---
> > gcc/gimple-range-fold.cc |  2 +-
> > gcc/range-op-float.cc    | 26 ++++++++-----------
> > gcc/value-range.cc       | 37 ++++++++++++++-------------
> > gcc/value-range.h        | 54 +++++++++++++++++++++++++++++++++++++++-
> > 4 files changed, 83 insertions(+), 36 deletions(-)
> >
> > diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
> > index c9c7a2ccc70..47a1f49eb36 100644
> > --- a/gcc/gimple-range-fold.cc
> > +++ b/gcc/gimple-range-fold.cc
> > @@ -1031,7 +1031,7 @@ fold_using_range::range_of_builtin_int_call (irange 
> > &r, gcall *call,
> >      {
> >        if (tmp.get_signbit ().varying_p ()
> >        // FIXME: We don't support signed NANs yet.
> > -        || !tmp.get_nan ().no_p ())
> > +        || tmp.maybe_nan ())
> >          return false;
> >        if (tmp.get_signbit ().yes_p ())
> >          r.set_nonzero (type);
> > diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
> > index 5fbbaa1fb36..0f928b6c098 100644
> > --- a/gcc/range-op-float.cc
> > +++ b/gcc/range-op-float.cc
> > @@ -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);
> > diff --git a/gcc/value-range.cc b/gcc/value-range.cc
> > index c3f668a811a..364919ca5c6 100644
> > --- a/gcc/value-range.cc
> > +++ b/gcc/value-range.cc
> > @@ -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,7 +308,7 @@ 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;
> > @@ -467,7 +467,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 +478,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 +525,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 +534,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 +590,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)
> > @@ -644,7 +643,7 @@ 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.
> > @@ -701,7 +700,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 +709,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));
> > @@ -3637,7 +3636,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 +3645,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 +3653,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 +3665,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 ()));
> > }
> > @@ -3795,7 +3794,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);
> > diff --git a/gcc/value-range.h b/gcc/value-range.h
> > index 645dc76c33a..e426225eabf 100644
> > --- a/gcc/value-range.h
> > +++ b/gcc/value-range.h
> > @@ -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,19 @@ 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;
> > +
> >   // 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 (); }
> >   void verify_range ();
> >   bool normalize_kind ();
> >
> > @@ -1187,4 +1195,48 @@ 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);
> > +}
> > +
> > +inline bool
> > +frange::known_inf () const
> > +{
> > +  return (m_kind == VR_RANGE
> > +      && real_identical (&m_min, &m_max)
> > +      && real_isinf (&m_min));
> > +}
> > +
> > +inline bool
> > +frange::maybe_nan () const
> > +{
> > +  return !get_nan ().no_p ();
> > +}
> > +
> > +inline bool
> > +frange::known_nan () const
> > +{
> > +  return get_nan ().yes_p ();
> > +}
> > +
> > #endif // GCC_VALUE_RANGE_H
> > --
> > 2.37.1
> >
>
From 795baa0b044953e9f198f49e379374d633f43b47 Mon Sep 17 00:00:00 2001
From: Aldy Hernandez <al...@redhat.com>
Date: Thu, 8 Sep 2022 08:11:43 +0200
Subject: [PATCH] Implement known/maybe fpclassify like API for frange.

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 |  19 +++---
 gcc/range-op-float.cc    |  26 ++++----
 gcc/value-range.cc       | 126 +++++++++++++++++++++++----------------
 gcc/value-range.h        |  78 +++++++++++++++++++++++-
 4 files changed, 170 insertions(+), 79 deletions(-)

diff --git a/gcc/gimple-range-fold.cc b/gcc/gimple-range-fold.cc
index c9c7a2ccc70..85ed6f9d47e 100644
--- a/gcc/gimple-range-fold.cc
+++ b/gcc/gimple-range-fold.cc
@@ -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;
       }
diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 5fbbaa1fb36..0f928b6c098 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -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);
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index c3f668a811a..adcaaa2a69a 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -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);
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 645dc76c33a..f9a01ee7a05 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -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
-- 
2.37.1

Reply via email to