public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
* [PATCH] Add support for floating point endpoints to frange.
@ 2022-08-23 11:42 Aldy Hernandez
  2022-08-26 15:55 ` Aldy Hernandez
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-23 11:42 UTC (permalink / raw)
  To: GCC patches

The current implementation of frange is just a type with some bits to
represent NAN and INF.  We can do better and represent endpoints to
ultimately solve longstanding PRs such as PR24021.  This patch adds
these endpoints.  In follow-up patches I will add support for
relational operators using them, as well as a bare bones PLUS_EXPR
range-op-float entry to solve the PR.

I have chosen to use REAL_VALUE_TYPEs for the endpoints, since that's
what we use underneath the trees.  This will be somewhat analogous to
our eventual use of wide-ints in the irange.  No sense going through
added levels of indirection if we can avoid it.  That, plus real.*
already has a nice API for dealing with floats.

With this patch, ranges will be closed float point intervals, which
make the implementation simpler, since we don't have to keep track of
open/closed intervals.  This is conservative enough for use in the
ranger world, as we'd rather err on the side of more elements in a
range, than less.

For example, even though we cannot precisely represent the open
interval (3.0, 5.0) with this approach, it is perfectably reasonable
to represent it as [3.0, 5.0] since the closed interval is a super set
of the open one.  In the VRP/ranger world, it is always better to
err on the side of more information in a range, than not.  After all,
when we don't know anything about a range, we just use VARYING which
is a fancy term for a range spanning the entire domain.

Since REAL_VALUE_TYPEs have properly defined infinity and NAN
semantics, all the math can be made to work:

[-INF, 3.0] !NAN	=> Numbers <= 3.0 (NAN cannot happen)
[3.0, +INF]		=> Numbers >= 3.0 (NAN is possible)
[-INF, +INF]		=> VARYING (NAN is possible)
[-INF, +INF] !NAN	=> Entire domain.  NAN cannot happen.

Also, since REAL_VALUE_TYPEs can represent the minimum and maximum
representable values of a TYPE_MODE, we can disambiguate between them
and negative and positive infinity (see get_max_float in real.cc).

This also makes the math all work.  For example, suppose we know
nothing about x and y (VARYING).  On the TRUE side of x > y, we can
deduce that:

	(a) x cannot be NAN
	(b) y cannot be NAN
	(c) y cannot be +INF.

(c) means that we can drop the upper bound of "y" from +INF to the
maximum representable value for its type.

Having endpoints with different representation for infinity and the
maximum representable values, means we can drop the +-INF properties
we currently have in the frange.  I will do this as a follow-up patch,
as well as contributing more detailed relational operators.

I am still tweaking some regressed tests because of the usual premature
threading and VRP in the presence of smarter ranges.  I figured I'd post
now to give others a chance to comment in the meantime.

gcc/ChangeLog:

	* value-range-pretty-print.cc (vrange_printer::visit): Adapt for
	endpoints.
	* value-range.cc (frange::set): Same.
	(frange::normalize_kind): Same.
	(frange::union_): Same.
	(frange::intersect): Same.
	(frange::operator=): Same.
	(frange::verify_range): Same.
	(frange::contains_p): New.
	(frange::singleton_p): New.
	(frange_float): New.
	(real_max_representable): New.
	(real_min_representable): New.
	(range_tests_floats): New.
	(range_tests): Call range_tests_floats.
	* value-range.h (frange::lower_bound): New.
	(frange::upper_bound): New.
	(vrp_val_min): Use real_inf with a sign argument.
	(frange::frange): New.
	(frange::set_varying): Adapt for endpoints.
	(frange::set_undefined): Adapt for endpoints.
---
 gcc/value-range-pretty-print.cc |  12 +-
 gcc/value-range.cc              | 259 ++++++++++++++++++++++++++++++--
 gcc/value-range.h               |  53 ++++++-
 3 files changed, 312 insertions(+), 12 deletions(-)

diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index cbf50d3d854..1a026c22f99 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -122,19 +122,29 @@ vrange_printer::print_irange_bitmasks (const irange &r) const
 void
 vrange_printer::visit (const frange &r) const
 {
+  tree type = r.type ();
+
   pp_string (pp, "[frange] ");
   if (r.undefined_p ())
     {
       pp_string (pp, "UNDEFINED");
       return;
     }
-  dump_generic_node (pp, r.type (), 0, TDF_NONE, false);
+  dump_generic_node (pp, type, 0, TDF_NONE, false);
   pp_string (pp, " ");
   if (r.varying_p ())
     {
       pp_string (pp, "VARYING");
       return;
     }
+  pp_character (pp, '[');
+  dump_generic_node (pp,
+		     build_real (type, r.lower_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, ", ");
+  dump_generic_node (pp,
+		     build_real (type, r.upper_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, "] ");
+
   print_frange_prop ("NAN", r.get_nan ());
   print_frange_prop ("INF", r.get_inf ());
   print_frange_prop ("NINF", r.get_ninf ());
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index d056f7356e1..3d81a884192 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -287,6 +287,8 @@ frange::set (tree min, tree max, value_range_kind kind)
   m_kind = kind;
   m_type = TREE_TYPE (min);
   m_props.set_varying ();
+  m_min = *TREE_REAL_CST_PTR (min);
+  m_max = *TREE_REAL_CST_PTR (max);
 
   bool is_min = vrp_val_is_min (min);
   bool is_max = vrp_val_is_max (max);
@@ -332,6 +334,16 @@ frange::set (tree min, tree max, value_range_kind kind)
     verify_range ();
 }
 
+// Setter for frange from REAL_VALUE_TYPE endpoints.
+
+void
+frange::set (tree type,
+	     const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	     value_range_kind kind)
+{
+  set (build_real (type, min), build_real (type, max), kind);
+}
+
 // Normalize range to VARYING or UNDEFINED, or vice versa.  Return
 // TRUE if anything changed.
 //
@@ -343,7 +355,9 @@ frange::set (tree min, tree max, value_range_kind kind)
 bool
 frange::normalize_kind ()
 {
-  if (m_kind == VR_RANGE)
+  if (m_kind == VR_RANGE
+      && real_isinf (&m_min, 1)
+      && real_isinf (&m_max, 0))
     {
       // No FP properties set means varying.
       if (m_props.varying_p ())
@@ -366,6 +380,8 @@ frange::normalize_kind ()
       if (!m_props.varying_p ())
 	{
 	  m_kind = VR_RANGE;
+	  real_inf (&m_min, 1);
+	  real_inf (&m_max, 0);
 	  return true;
 	}
     }
@@ -385,12 +401,22 @@ frange::union_ (const vrange &v)
       return true;
     }
 
-  bool ret = m_props.union_ (r.m_props);
-  ret |= normalize_kind ();
+  bool changed = m_props.union_ (r.m_props);
+  if (real_less (&r.m_min, &m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&m_max, &r.m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 bool
@@ -411,12 +437,28 @@ frange::intersect (const vrange &v)
       return true;
     }
 
-  bool ret = m_props.intersect (r.m_props);
-  ret |= normalize_kind ();
+  bool changed = m_props.intersect (r.m_props);
+  if (real_less (&m_min, &r.m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&r.m_max, &m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  // If the endpoints are swapped, the ranges are disjoint.
+  if (real_less (&m_max, &m_min))
+    {
+      set_undefined ();
+      return true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 frange &
@@ -424,6 +466,8 @@ 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_props = src.m_props;
 
   if (flag_checking)
@@ -442,7 +486,44 @@ frange::operator== (const frange &src) const
       if (varying_p ())
 	return types_compatible_p (m_type, src.m_type);
 
-      return m_props == src.m_props;
+      return (real_identical (&m_min, &src.m_min)
+	      && real_identical (&m_max, &src.m_max)
+	      && m_props == src.m_props
+	      && types_compatible_p (m_type, src.m_type));
+    }
+  return false;
+}
+
+// Return TRUE if range contains the TREE_REAL_CST_PTR in CST.
+
+bool
+frange::contains_p (tree cst) const
+{
+  if (undefined_p ())
+    return false;
+
+  if (varying_p ())
+    return true;
+
+  gcc_checking_assert (m_kind == VR_RANGE);
+
+  return (real_compare (GE_EXPR, TREE_REAL_CST_PTR (cst), &m_min)
+	  && real_compare (LE_EXPR, TREE_REAL_CST_PTR (cst), &m_max));
+}
+
+// If range is a singleton, place it in RESULT and return TRUE.  If
+// RESULT is NULL, just return TRUE.
+
+bool
+frange::singleton_p (tree *result) const
+{
+  if (m_kind == VR_RANGE
+      && real_identical (&m_min, &m_max)
+      && !real_isnan (&m_min))
+    {
+      if (result)
+	*result = build_real (m_type, m_min);
+      return true;
     }
   return false;
 }
@@ -461,13 +542,32 @@ frange::verify_range ()
       gcc_checking_assert (m_props.undefined_p ());
       return;
     }
+  gcc_checking_assert (!m_props.undefined_p ());
+
   if (varying_p ())
     {
       gcc_checking_assert (m_props.varying_p ());
       return;
     }
+
+  // We don't support the inverse of an frange (yet).
   gcc_checking_assert (m_kind == VR_RANGE);
-  gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ());
+
+  bool is_nan = real_isnan (&m_min) || real_isnan (&m_max);
+  if (is_nan)
+    {
+      // 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 ());
+    }
+  else
+    // Make sure we don't have swapped ranges.
+    gcc_checking_assert (!real_less (&m_max, &m_min));
+
+  // If all the properties are clear, we better not span the entire
+  // domain, because that would make us varying.
+  if (m_props.varying_p ())
+    gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0));
 }
 
 // Here we copy between any two irange's.  The ranges can be legacy or
@@ -3300,6 +3400,146 @@ range_tests_nonzero_bits ()
   ASSERT_TRUE (r0.varying_p ());
 }
 
+// Build an frange from string endpoints.
+
+static inline frange
+frange_float (const char *lb, const char *ub, tree type = float_type_node)
+{
+  REAL_VALUE_TYPE min, max;
+  real_from_string (&min, lb);
+  real_from_string (&max, ub);
+  return frange (type, min, max);
+}
+
+// Set R to maximum representable value for TYPE.
+
+static inline void
+real_max_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  char buf[128];
+  get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
+		 buf, sizeof (buf), false);
+  real_from_string (r, buf);
+}
+
+// Set R to minimum representable value for TYPE.
+
+static inline void
+real_min_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  real_max_representable (r, type);
+  real_value_negate (r);
+}
+
+static void
+range_tests_floats ()
+{
+  frange r0, r1;
+
+
+  // Equal ranges but with differing NAN bits are not equal.
+  r1 = frange_float ("10", "12");
+  r0 = r1;
+  ASSERT_EQ (r0, r1);
+  r0.set_nan (fp_prop::NO);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::YES);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::VARYING);
+  ASSERT_EQ (r0, r1);
+
+  // A range of [-INF,+INF] is actually VARYING...
+  r0 = frange_float ("-Inf", "+Inf");
+  ASSERT_TRUE (r0.varying_p ());
+  // ...unless it has some special property...
+  r0.set_nan (fp_prop::NO);
+  ASSERT_FALSE (r0.varying_p ());
+
+  // The endpoints of a VARYING are +-INF.
+  REAL_VALUE_TYPE inf, ninf;
+  real_inf (&inf, 0);
+  real_inf (&ninf, 1);
+  r0.set_varying (float_type_node);
+  ASSERT_TRUE (real_identical (&r0.lower_bound (), &ninf));
+  ASSERT_TRUE (real_identical (&r0.upper_bound (), &inf));
+
+  // The maximum representable range for a type is still a subset of VARYING.
+  REAL_VALUE_TYPE q, r;
+  real_min_representable (&q, float_type_node);
+  real_max_representable (&r, float_type_node);
+  r0 = frange (float_type_node, q, r);
+  // r0 is not a varying, because it does not include -INF/+INF.
+  ASSERT_FALSE (r0.varying_p ());
+  // The upper bound of r0 must be less than +INF.
+  ASSERT_TRUE (real_less (&r0.upper_bound (), &inf));
+  // The lower bound of r0 must be greater than -INF.
+  ASSERT_TRUE (real_less (&ninf, &r0.lower_bound ()));
+
+  // For most architectures, where float and double are different
+  // sizes, having the same endpoints does not necessarily mean the
+  // ranges are equal.
+  if (!types_compatible_p (float_type_node, double_type_node))
+    {
+      r0 = frange_float ("3.0", "3.0", float_type_node);
+      r1 = frange_float ("3.0", "3.0", double_type_node);
+      ASSERT_NE (r0, r1);
+    }
+
+  // [3,5] U [10,12] = [3,12].
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("10", "12");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "12"));
+
+  // [5,10] U [4,8] = [4,10]
+  r0 = frange_float ("5", "10");
+  r1 = frange_float ("4", "8");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "10"));
+
+  // [3,5] U [4,10] = [3,10]
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("4", "10");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "10"));
+
+  // [4,10] U [5,11] = [4,11]
+  r0 = frange_float ("4", "10");
+  r1 = frange_float ("5", "11");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "11"));
+
+  // [3,12] ^ [10,12] = [10,12].
+  r0 = frange_float ("3", "12");
+  r1 = frange_float ("10", "12");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "12"));
+
+  // [10,12] ^ [11,11] = [11,11]
+  r0 = frange_float ("10", "12");
+  r1 = frange_float ("11", "11");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("11", "11"));
+
+  // [10,20] ^ [5,15] = [10,15]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("5",  "15");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "15"));
+
+  // [10,20] ^ [15,25] = [15,20]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("15", "25");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("15", "20"));
+
+  // [10,20] ^ [21,25] = []
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("21", "25");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+}
+
 void
 range_tests ()
 {
@@ -3308,6 +3548,7 @@ range_tests ()
   range_tests_int_range_max ();
   range_tests_strict_enum ();
   range_tests_nonzero_bits ();
+  range_tests_floats ();
   range_tests_misc ();
 }
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index f0075d0fb1a..7be9e84e13a 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -345,21 +345,30 @@ class frange : public vrange
 public:
   frange ();
   frange (const frange &);
+  frange (tree, tree, value_range_kind = VR_RANGE);
+  frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	  value_range_kind = VR_RANGE);
   static bool supports_p (const_tree type)
   {
     return SCALAR_FLOAT_TYPE_P (type);
   }
   virtual tree type () const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
+	    value_range_kind = VR_RANGE);
   virtual void set_varying (tree type) override;
   virtual void set_undefined () override;
   virtual bool union_ (const vrange &) override;
   virtual bool intersect (const vrange &) override;
+  virtual bool contains_p (tree) const override;
+  virtual bool singleton_p (tree *result = NULL) const override;
   virtual bool supports_type_p (const_tree type) const override;
   virtual void accept (const vrange_visitor &v) const override;
   frange& operator= (const frange &);
   bool operator== (const frange &) const;
   bool operator!= (const frange &r) const { return !(*this == r); }
+  const REAL_VALUE_TYPE &lower_bound () const;
+  const REAL_VALUE_TYPE &upper_bound () const;
 
   // Each fp_prop can be accessed with get_PROP() and set_PROP().
   FRANGE_PROP_ACCESSOR(nan)
@@ -371,8 +380,24 @@ private:
 
   frange_props m_props;
   tree m_type;
+  REAL_VALUE_TYPE m_min;
+  REAL_VALUE_TYPE m_max;
 };
 
+inline const REAL_VALUE_TYPE &
+frange::lower_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_max;
+}
+
 // is_a<> and as_a<> implementation for vrange.
 
 // Anything we haven't specialized is a hard fail.
@@ -1051,8 +1076,8 @@ vrp_val_min (const_tree type)
   if (frange::supports_p (type))
     {
       REAL_VALUE_TYPE real, real_ninf;
-      real_inf (&real);
-      real_ninf = real_value_negate (&real);
+      real_inf (&real, 0);
+      real_inf (&real_ninf, 1);
       return build_real (const_cast <tree> (type), real_ninf);
     }
   return NULL_TREE;
@@ -1096,6 +1121,26 @@ frange::frange (const frange &src)
   *this = src;
 }
 
+// frange constructor from REAL_VALUE_TYPE endpoints.
+
+inline
+frange::frange (tree type,
+		const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+		value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (type, min, max, kind);
+}
+
+// frange constructor from trees.
+
+inline
+frange::frange (tree min, tree max, value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (min, max, kind);
+}
+
 inline tree
 frange::type () const
 {
@@ -1107,6 +1152,8 @@ frange::set_varying (tree type)
 {
   m_kind = VR_VARYING;
   m_type = type;
+  real_inf (&m_min, 1);
+  real_inf (&m_max, 0);
   m_props.set_varying ();
 }
 
@@ -1116,6 +1163,8 @@ frange::set_undefined ()
   m_kind = VR_UNDEFINED;
   m_type = NULL;
   m_props.set_undefined ();
+  memset (&m_min, 0, sizeof (m_min));
+  memset (&m_max, 0, sizeof (m_max));
 }
 
 #endif // GCC_VALUE_RANGE_H
-- 
2.37.1


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-23 11:42 [PATCH] Add support for floating point endpoints to frange Aldy Hernandez
@ 2022-08-26 15:55 ` Aldy Hernandez
  2022-08-26 17:40   ` Andrew Pinski
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-26 15:55 UTC (permalink / raw)
  To: GCC patches; +Cc: Andrew MacLeod, Jakub Jelinek, Andrew Pinski

[-- Attachment #1: Type: text/plain, Size: 1322 bytes --]

[pinskia: I'm CCing you as the author of the match.pd pattern.]

So, as I wrap up the work here (latest patch attached), I see there's
another phiopt regression (not spaceship related).  I was hoping
someone could either give me a hand, or offer some guidance.

The failure is in gcc.dg/tree-ssa/phi-opt-24.c.

We fail to transform the following into -A:

/* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */

float f0(float A)
{
  //     A == 0? A : -A    same as -A
  if (A == 0)  return A;
  return -A;
}

This is because the abs/negative match.pd pattern here:

/* abs/negative simplifications moved from fold_cond_expr_with_comparison,
   Need to handle (A - B) case as fold_cond_expr_with_comparison does.
   Need to handle UN* comparisons.
   ...
   ...

Sees IL that has the 0.0 propagated.

Instead of:

  <bb 2> [local count: 1073741824]:
  if (A_2(D) == 0.0)
    goto <bb 4>; [34.00%]
  else
    goto <bb 3>; [66.00%]

  <bb 3> [local count: 708669601]:
  _3 = -A_2(D);

  <bb 4> [local count: 1073741824]:
  # _1 = PHI <A_2(D)(2), _3(3)>

It now sees:

  <bb 4> [local count: 1073741824]:
  # _1 = PHI <0.0(2), _3(3)>

which it leaves untouched, causing the if conditional to survive.

Is this something that can be done by improving the match.pd pattern,
or should be done elsewhere?

Thanks.
Aldy

[-- Attachment #2: 0006-Add-support-for-floating-point-endpoints-to-frange.patch --]
[-- Type: text/x-patch, Size: 25335 bytes --]

From a1f64017defaf6d5841b54e8bc867d63738a9f5c Mon Sep 17 00:00:00 2001
From: Aldy Hernandez <aldyh@redhat.com>
Date: Tue, 23 Aug 2022 13:36:33 +0200
Subject: [PATCH] Add support for floating point endpoints to frange.

The current implementation of frange is just a type with some bits to
represent NAN and INF.  We can do better and represent endpoints to
ultimately solve longstanding PRs such as PR24021.  This patch adds
these endpoints.  In follow-up patches I will add support for
relational operators using them, as well as a bare bones PLUS_EXPR
range-op-float entry to solve the PR.

I have chosen to use REAL_VALUE_TYPEs for the endpoints, since that's
what we use underneath the trees.  This will be somewhat analogous to
our eventual use of wide-ints in the irange.  No sense going through
added levels of indirection if we can avoid it.  That, plus real.*
already has a nice API for dealing with floats.

With this patch, ranges will be closed float point intervals, which
make the implementation simpler, since we don't have to keep track of
open/closed intervals.  This is conservative enough for use in the
ranger world, as we'd rather err on the side of more elements in a
range, than less.

For example, even though we cannot precisely represent the open
interval (3.0, 5.0) with this approach, it is perfectably reasonable
to represent it as [3.0, 5.0] since the closed interval is a super set
of the open one.  In the VRP/ranger world, it is always better to
err on the side of more information in a range, than not.  After all,
when we don't know anything about a range, we just use VARYING which
is a fancy term for a range spanning the entire domain.

Since REAL_VALUE_TYPEs have properly defined infinity and NAN
semantics, all the math can be made to work:

[-INF, 3.0] !NAN	=> Numbers <= 3.0 (NAN cannot happen)
[3.0, +INF]		=> Numbers >= 3.0 (NAN is possible)
[-INF, +INF]		=> VARYING (NAN is possible)
[-INF, +INF] !NAN	=> Entire domain.  NAN cannot happen.

Also, since REAL_VALUE_TYPEs can represent the minimum and maximum
representable values of a TYPE_MODE, we can disambiguate between them
and negative and positive infinity (see get_max_float in real.cc).

This also makes the math all work.  For example, suppose we know
nothing about x and y (VARYING).  On the TRUE side of x > y, we can
deduce that:

	(a) x cannot be NAN
	(b) y cannot be NAN
	(c) y cannot be +INF.

(c) means that we can drop the upper bound of "y" from +INF to the
maximum representable value for its type.

Having endpoints with different representation for infinity and the
maximum representable values, means we can drop the +-INF properties
we currently have in the frange.  I will do this as a follow-up patch,
as well as contributing more detailed relational operators.

gcc/ChangeLog:

	* value-range-pretty-print.cc (vrange_printer::visit): Adapt for
	endpoints.
	* value-range.cc (frange::set): Same.
	(frange::normalize_kind): Same.
	(frange::operator=): Same.
	(frange::verify_range): Same.
	(frange::union_): Adapt for endpoints and special case NAN
	ranges.
	(frange::intersect): Same.
	(frange::operator==): Special case NAN ranges.
	(frange::contains_p): New.
	(frange::singleton_p): New.
	(frange_float): New.
	(frange_nan): New.
	(early_nan_resolve): New.
	(real_max_representable): New.
	(real_min_representable): New.
	(range_tests_floats): New.
	(range_tests): Call range_tests_floats.
	* value-range.h (frange::lower_bound): New.
	(frange::upper_bound): New.
	(vrp_val_min): Use real_inf with a sign argument.
	(frange::frange): New.
	(frange::set_varying): Adapt for endpoints.
	(frange::set_undefined): Adapt for endpoints.
---
 gcc/range-op-float.cc           |  16 +-
 gcc/value-range-pretty-print.cc |  12 +-
 gcc/value-range-storage.cc      |  12 +
 gcc/value-range.cc              | 455 +++++++++++++++++++++++++++++++-
 gcc/value-range.h               |  58 +++-
 5 files changed, 539 insertions(+), 14 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ff9fe312acf..4578066a78b 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -150,6 +150,18 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons
   return VREL_VARYING;
 }
 
+// Set R to [NAN, NAN].
+
+static inline void
+frange_set_nan (frange &r, tree type)
+{
+  REAL_VALUE_TYPE rv;
+  bool res = real_nan (&rv, "", 1, TYPE_MODE (type));
+  if (flag_checking)
+    gcc_assert (res);
+  r.set (type, rv, rv);
+}
+
 // Return TRUE if OP1 and OP2 are known to be free of NANs.
 
 static inline bool
@@ -275,7 +287,7 @@ foperator_equal::op1_range (frange &r, tree type,
       r.set_varying (type);
       // The FALSE side of op1 == op1 implies op1 is a NAN.
       if (rel == VREL_EQ)
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
       break;
 
     default:
@@ -636,7 +648,7 @@ foperator_unordered::op1_range (frange &r, tree 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 ())
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
       break;
 
     case BRS_FALSE:
diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index cbf50d3d854..1a026c22f99 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -122,19 +122,29 @@ vrange_printer::print_irange_bitmasks (const irange &r) const
 void
 vrange_printer::visit (const frange &r) const
 {
+  tree type = r.type ();
+
   pp_string (pp, "[frange] ");
   if (r.undefined_p ())
     {
       pp_string (pp, "UNDEFINED");
       return;
     }
-  dump_generic_node (pp, r.type (), 0, TDF_NONE, false);
+  dump_generic_node (pp, type, 0, TDF_NONE, false);
   pp_string (pp, " ");
   if (r.varying_p ())
     {
       pp_string (pp, "VARYING");
       return;
     }
+  pp_character (pp, '[');
+  dump_generic_node (pp,
+		     build_real (type, r.lower_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, ", ");
+  dump_generic_node (pp,
+		     build_real (type, r.upper_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, "] ");
+
   print_frange_prop ("NAN", r.get_nan ());
   print_frange_prop ("INF", r.get_inf ());
   print_frange_prop ("NINF", r.get_ninf ());
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index ea3b83ca641..adf23c39f0d 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -261,6 +261,18 @@ frange_storage_slot::get_frange (frange &r, tree type) const
 {
   gcc_checking_assert (r.supports_type_p (type));
 
+  // FIXME: NANs get special treatment, because we need [NAN, NAN] in
+  // the range to properly represent it, not just the NAN flag in the
+  // property bits.  This will go away when we stream out the
+  // endpoints.
+  if (m_props.get_nan ().yes_p ())
+    {
+      REAL_VALUE_TYPE rv;
+      real_nan (&rv, "", 1, TYPE_MODE (type));
+      r.set (type, rv, rv);
+      return;
+    }
+
   r.set_varying (type);
   r.m_props = m_props;
   r.normalize_kind ();
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index edd10bf5794..2c1aedece37 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -291,6 +291,8 @@ frange::set (tree min, tree max, value_range_kind kind)
   m_kind = kind;
   m_type = TREE_TYPE (min);
   m_props.set_varying ();
+  m_min = *TREE_REAL_CST_PTR (min);
+  m_max = *TREE_REAL_CST_PTR (max);
 
   bool is_min = vrp_val_is_min (min);
   bool is_max = vrp_val_is_max (max);
@@ -336,6 +338,16 @@ frange::set (tree min, tree max, value_range_kind kind)
     verify_range ();
 }
 
+// Setter for frange from REAL_VALUE_TYPE endpoints.
+
+void
+frange::set (tree type,
+	     const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	     value_range_kind kind)
+{
+  set (build_real (type, min), build_real (type, max), kind);
+}
+
 // Normalize range to VARYING or UNDEFINED, or vice versa.  Return
 // TRUE if anything changed.
 //
@@ -347,7 +359,9 @@ frange::set (tree min, tree max, value_range_kind kind)
 bool
 frange::normalize_kind ()
 {
-  if (m_kind == VR_RANGE)
+  if (m_kind == VR_RANGE
+      && real_isinf (&m_min, 1)
+      && real_isinf (&m_max, 0))
     {
       // No FP properties set means varying.
       if (m_props.varying_p ())
@@ -370,12 +384,32 @@ frange::normalize_kind ()
       if (!m_props.varying_p ())
 	{
 	  m_kind = VR_RANGE;
+	  real_inf (&m_min, 1);
+	  real_inf (&m_max, 0);
 	  return true;
 	}
     }
   return false;
 }
 
+// If both operands are definitely NAN, do nothing as they combine
+// perfectly.  If OTOH, only one is a NAN, set R to VARYING as they
+// can't be neither unioned nor intersected.  Return TRUE if we
+// changed anything.
+
+static inline bool
+early_nan_resolve (frange &r, const frange &other)
+{
+  gcc_checking_assert (r.get_nan ().yes_p () || other.get_nan ().yes_p ());
+
+  // There's nothing to do for both NANs.
+  if (r.get_nan ().yes_p () == other.get_nan ().yes_p ())
+    return false;
+  // But only one NAN complicates things.
+  r.set_varying (r.type ());
+  return true;
+}
+
 bool
 frange::union_ (const vrange &v)
 {
@@ -388,13 +422,38 @@ frange::union_ (const vrange &v)
       *this = r;
       return true;
     }
+  // ?? We could do better here.  [5,6] U NAN should be [5,6] with the
+  // NAN maybe bits set.  For now, this is conservatively correct.
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
+
+  bool changed = m_props.union_ (r.m_props);
+
+  // The union of zeros differing in sign are [-0.0, +0.0].
+  if (HONOR_SIGNED_ZEROS (m_type)
+      && zero_p () && r.zero_p ()
+      && *this != r)
+    {
+      m_min = real_value_negate (&dconst0);
+      m_max = dconst0;
+      return true;
+    }
 
-  bool ret = m_props.union_ (r.m_props);
-  ret |= normalize_kind ();
+  if (real_less (&r.m_min, &m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&m_max, &r.m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 bool
@@ -414,13 +473,43 @@ frange::intersect (const vrange &v)
       *this = r;
       return true;
     }
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
+
+  bool changed = m_props.intersect (r.m_props);
+
+  // ?? No idea how to handle the intersection of zeros that differ in
+  // sign.  Setting [-0.0, +0.0] is at least conservative correct.
+  if (HONOR_SIGNED_ZEROS (m_type)
+      && zero_p () && r.zero_p ()
+      && *this != r)
+    {
+      m_min = real_value_negate (&dconst0);
+      m_max = dconst0;
+      return true;
+    }
 
-  bool ret = m_props.intersect (r.m_props);
-  ret |= normalize_kind ();
+  if (real_less (&m_min, &r.m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&r.m_max, &m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  // If the endpoints are swapped, the ranges are disjoint.
+  if (real_less (&m_max, &m_min))
+    {
+      set_undefined ();
+      return true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 frange &
@@ -428,6 +517,8 @@ 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_props = src.m_props;
 
   if (flag_checking)
@@ -446,7 +537,48 @@ frange::operator== (const frange &src) const
       if (varying_p ())
 	return types_compatible_p (m_type, src.m_type);
 
-      return m_props == src.m_props;
+      if (m_props.get_nan ().yes_p ()
+	  || src.m_props.get_nan ().yes_p ())
+	return false;
+
+      return (real_identical (&m_min, &src.m_min)
+	      && real_identical (&m_max, &src.m_max)
+	      && m_props == src.m_props
+	      && types_compatible_p (m_type, src.m_type));
+    }
+  return false;
+}
+
+// Return TRUE if range contains the TREE_REAL_CST_PTR in CST.
+
+bool
+frange::contains_p (tree cst) const
+{
+  if (undefined_p ())
+    return false;
+
+  if (varying_p ())
+    return true;
+
+  gcc_checking_assert (m_kind == VR_RANGE);
+
+  return (real_compare (GE_EXPR, TREE_REAL_CST_PTR (cst), &m_min)
+	  && real_compare (LE_EXPR, TREE_REAL_CST_PTR (cst), &m_max));
+}
+
+// If range is a singleton, place it in RESULT and return TRUE.  If
+// RESULT is NULL, just return TRUE.
+
+bool
+frange::singleton_p (tree *result) const
+{
+  if (m_kind == VR_RANGE
+      && real_identical (&m_min, &m_max)
+      && !real_isnan (&m_min))
+    {
+      if (result)
+	*result = build_real (m_type, m_min);
+      return true;
     }
   return false;
 }
@@ -465,13 +597,82 @@ frange::verify_range ()
       gcc_checking_assert (m_props.undefined_p ());
       return;
     }
+  gcc_checking_assert (!m_props.undefined_p ());
+
   if (varying_p ())
     {
       gcc_checking_assert (m_props.varying_p ());
       return;
     }
+
+  // We don't support the inverse of an frange (yet).
   gcc_checking_assert (m_kind == VR_RANGE);
-  gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ());
+
+  bool is_nan = real_isnan (&m_min) || real_isnan (&m_max);
+  if (is_nan)
+    {
+      // 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 ());
+    }
+  else
+    // Make sure we don't have swapped ranges.
+    gcc_checking_assert (!real_less (&m_max, &m_min));
+
+  // 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 ())
+    {
+      gcc_checking_assert (real_isnan (&m_min));
+      gcc_checking_assert (real_isnan (&m_max));
+    }
+
+  // If all the properties are clear, we better not span the entire
+  // domain, because that would make us varying.
+  if (m_props.varying_p ())
+    gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0));
+}
+
+// We can't do much with nonzeros yet.
+void
+frange::set_nonzero (tree type)
+{
+  set_varying (type);
+}
+
+// We can't do much with nonzeros yet.
+bool
+frange::nonzero_p () const
+{
+  return false;
+}
+
+// Set range to [+0.0, +0.0].
+
+void
+frange::set_zero (tree type)
+{
+  tree zero = build_zero_cst (type);
+  set (zero, zero);
+}
+
+// Note that this returns TRUE for any combination of a +-0.0 range.
+
+bool
+frange::zero_p () const
+{
+  return (m_kind == VR_RANGE
+	  && real_iszero (&m_min)
+	  && real_iszero (&m_max));
+}
+
+void
+frange::set_nonnegative (tree type)
+{
+  tree zero = build_zero_cst (type);
+  tree inf = vrp_val_max (type);
+  set (zero, inf);
 }
 
 // Here we copy between any two irange's.  The ranges can be legacy or
@@ -3304,6 +3505,241 @@ range_tests_nonzero_bits ()
   ASSERT_TRUE (r0.varying_p ());
 }
 
+// Build an frange from string endpoints.
+
+static inline frange
+frange_float (const char *lb, const char *ub, tree type = float_type_node)
+{
+  REAL_VALUE_TYPE min, max;
+  gcc_assert (real_from_string (&min, lb) == 0);
+  gcc_assert (real_from_string (&max, ub) == 0);
+  return frange (type, min, max);
+}
+
+// Build a NAN of type TYPE.
+
+static inline frange
+frange_nan (tree type = float_type_node)
+{
+  REAL_VALUE_TYPE r;
+
+  gcc_assert (real_nan (&r, "", 1, TYPE_MODE (type)));
+  return frange (type, r, r);
+}
+
+// Set R to maximum representable value for TYPE.
+
+static inline void
+real_max_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  char buf[128];
+  get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
+		 buf, sizeof (buf), false);
+  real_from_string (r, buf);
+}
+
+// Set R to minimum representable value for TYPE.
+
+static inline void
+real_min_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  real_max_representable (r, type);
+  real_value_negate (r);
+}
+
+static void
+range_tests_nan ()
+{
+  frange r0, r1;
+
+  // Equal ranges but with differing NAN bits are not equal.
+  r1 = frange_float ("10", "12");
+  r0 = r1;
+  ASSERT_EQ (r0, r1);
+  r0.set_nan (fp_prop::NO);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::YES);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::VARYING);
+  ASSERT_EQ (r0, r1);
+
+  // NAN ranges are not equal to each other.
+  r0 = frange_nan ();
+  r1 = r0;
+  ASSERT_FALSE (r0 == r1);
+  ASSERT_FALSE (r0 == r0);
+  ASSERT_TRUE (r0 != r0);
+
+  // Make sure NAN and INF have the right properties set, and that
+  // combining them doesn't give any crazy results.
+  r0 = frange_nan ();
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  r1 = frange_float ("+Inf", "+Inf");
+  ASSERT_TRUE (r1.get_inf ().yes_p ());
+  r0.union_ (r1);
+  // [INF, INF] U NAN = VARYING
+  ASSERT_TRUE (r0.varying_p ());
+
+  // [INF, INF] ^ NAN = VARYING
+  r0 = frange_nan ();
+  r1 = frange_float ("+Inf", "+Inf");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.varying_p ());
+
+  // NAN ^ NAN = NAN
+  r0 = frange_nan ();
+  r1 = frange_nan ();
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+
+  // VARYING ^ NAN = NAN.
+  r0 = frange_nan ();
+  r1.set_varying (float_type_node);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+}
+
+static void
+range_tests_signed_zeros ()
+{
+  tree zero = build_zero_cst (float_type_node);
+  tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero);
+  frange r0, r1;
+  REAL_VALUE_TYPE q, r;
+
+  // ?? If -0.0 == +0.0, then a range of [-0.0, -0.0] should
+  // contain +0.0 and vice versa.  We probably need a property to
+  // track signed zeros in the frange like we do for NAN, to
+  // properly model all this.
+  r0 = frange (zero, zero);
+  r1 = frange (neg_zero, neg_zero);
+  ASSERT_TRUE (r0.contains_p (zero));
+  ASSERT_TRUE (r0.contains_p (neg_zero));
+  ASSERT_TRUE (r1.contains_p (zero));
+  ASSERT_TRUE (r1.contains_p (neg_zero));
+
+  // [0,0] U [-0,-0] = [-0,0]
+  r0 = frange (zero, zero);
+  r1 = frange (neg_zero, neg_zero);
+  r0.union_ (r1);
+  q = r0.lower_bound ();
+  r = r0.upper_bound ();
+  ASSERT_TRUE (real_iszero (&q, 1));
+  ASSERT_TRUE (real_iszero (&r, 0));
+
+  // [0,0] U [-0,-0] = [-0,0]
+  //
+  // ?? Ultimately this could be [0.0, 0.0] with the signed zero bit
+  // set to VARYING.
+  r0 = frange (zero, zero);
+  r1 = frange (neg_zero, neg_zero);
+  r0.intersect (r1);
+  ASSERT_TRUE (real_iszero (&r0.lower_bound (), 1));
+  ASSERT_TRUE (real_iszero (&r0.upper_bound (), 0));
+}
+
+static void
+range_tests_floats ()
+{
+  frange r0, r1;
+
+  range_tests_nan ();
+
+  if (HONOR_SIGNED_ZEROS (float_type_node))
+    range_tests_signed_zeros ();
+
+  // A range of [-INF,+INF] is actually VARYING...
+  r0 = frange_float ("-Inf", "+Inf");
+  ASSERT_TRUE (r0.varying_p ());
+  // ...unless it has some special property...
+  r0.set_nan (fp_prop::NO);
+  ASSERT_FALSE (r0.varying_p ());
+
+  // The endpoints of a VARYING are +-INF.
+  REAL_VALUE_TYPE inf, ninf;
+  real_inf (&inf, 0);
+  real_inf (&ninf, 1);
+  r0.set_varying (float_type_node);
+  ASSERT_TRUE (real_identical (&r0.lower_bound (), &ninf));
+  ASSERT_TRUE (real_identical (&r0.upper_bound (), &inf));
+
+  // The maximum representable range for a type is still a subset of VARYING.
+  REAL_VALUE_TYPE q, r;
+  real_min_representable (&q, float_type_node);
+  real_max_representable (&r, float_type_node);
+  r0 = frange (float_type_node, q, r);
+  // r0 is not a varying, because it does not include -INF/+INF.
+  ASSERT_FALSE (r0.varying_p ());
+  // The upper bound of r0 must be less than +INF.
+  ASSERT_TRUE (real_less (&r0.upper_bound (), &inf));
+  // The lower bound of r0 must be greater than -INF.
+  ASSERT_TRUE (real_less (&ninf, &r0.lower_bound ()));
+
+  // For most architectures, where float and double are different
+  // sizes, having the same endpoints does not necessarily mean the
+  // ranges are equal.
+  if (!types_compatible_p (float_type_node, double_type_node))
+    {
+      r0 = frange_float ("3.0", "3.0", float_type_node);
+      r1 = frange_float ("3.0", "3.0", double_type_node);
+      ASSERT_NE (r0, r1);
+    }
+
+  // [3,5] U [10,12] = [3,12].
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("10", "12");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "12"));
+
+  // [5,10] U [4,8] = [4,10]
+  r0 = frange_float ("5", "10");
+  r1 = frange_float ("4", "8");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "10"));
+
+  // [3,5] U [4,10] = [3,10]
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("4", "10");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "10"));
+
+  // [4,10] U [5,11] = [4,11]
+  r0 = frange_float ("4", "10");
+  r1 = frange_float ("5", "11");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "11"));
+
+  // [3,12] ^ [10,12] = [10,12].
+  r0 = frange_float ("3", "12");
+  r1 = frange_float ("10", "12");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "12"));
+
+  // [10,12] ^ [11,11] = [11,11]
+  r0 = frange_float ("10", "12");
+  r1 = frange_float ("11", "11");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("11", "11"));
+
+  // [10,20] ^ [5,15] = [10,15]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("5",  "15");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "15"));
+
+  // [10,20] ^ [15,25] = [15,20]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("15", "25");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("15", "20"));
+
+  // [10,20] ^ [21,25] = []
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("21", "25");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+}
+
 void
 range_tests ()
 {
@@ -3312,6 +3748,7 @@ range_tests ()
   range_tests_int_range_max ();
   range_tests_strict_enum ();
   range_tests_nonzero_bits ();
+  range_tests_floats ();
   range_tests_misc ();
 }
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index f0075d0fb1a..1e458e14466 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -345,21 +345,35 @@ class frange : public vrange
 public:
   frange ();
   frange (const frange &);
+  frange (tree, tree, value_range_kind = VR_RANGE);
+  frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	  value_range_kind = VR_RANGE);
   static bool supports_p (const_tree type)
   {
     return SCALAR_FLOAT_TYPE_P (type);
   }
   virtual tree type () const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
+	    value_range_kind = VR_RANGE);
   virtual void set_varying (tree type) override;
   virtual void set_undefined () override;
   virtual bool union_ (const vrange &) override;
   virtual bool intersect (const vrange &) override;
+  virtual bool contains_p (tree) const override;
+  virtual bool singleton_p (tree *result = NULL) const override;
   virtual bool supports_type_p (const_tree type) const override;
   virtual void accept (const vrange_visitor &v) const override;
+  virtual bool zero_p () const;
+  virtual bool nonzero_p () const;
+  virtual void set_nonzero (tree type);
+  virtual void set_zero (tree type);
+  virtual void set_nonnegative (tree type);
   frange& operator= (const frange &);
   bool operator== (const frange &) const;
   bool operator!= (const frange &r) const { return !(*this == r); }
+  const REAL_VALUE_TYPE &lower_bound () const;
+  const REAL_VALUE_TYPE &upper_bound () const;
 
   // Each fp_prop can be accessed with get_PROP() and set_PROP().
   FRANGE_PROP_ACCESSOR(nan)
@@ -371,8 +385,24 @@ private:
 
   frange_props m_props;
   tree m_type;
+  REAL_VALUE_TYPE m_min;
+  REAL_VALUE_TYPE m_max;
 };
 
+inline const REAL_VALUE_TYPE &
+frange::lower_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_max;
+}
+
 // is_a<> and as_a<> implementation for vrange.
 
 // Anything we haven't specialized is a hard fail.
@@ -1051,8 +1081,8 @@ vrp_val_min (const_tree type)
   if (frange::supports_p (type))
     {
       REAL_VALUE_TYPE real, real_ninf;
-      real_inf (&real);
-      real_ninf = real_value_negate (&real);
+      real_inf (&real, 0);
+      real_inf (&real_ninf, 1);
       return build_real (const_cast <tree> (type), real_ninf);
     }
   return NULL_TREE;
@@ -1096,6 +1126,26 @@ frange::frange (const frange &src)
   *this = src;
 }
 
+// frange constructor from REAL_VALUE_TYPE endpoints.
+
+inline
+frange::frange (tree type,
+		const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+		value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (type, min, max, kind);
+}
+
+// frange constructor from trees.
+
+inline
+frange::frange (tree min, tree max, value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (min, max, kind);
+}
+
 inline tree
 frange::type () const
 {
@@ -1107,6 +1157,8 @@ frange::set_varying (tree type)
 {
   m_kind = VR_VARYING;
   m_type = type;
+  real_inf (&m_min, 1);
+  real_inf (&m_max, 0);
   m_props.set_varying ();
 }
 
@@ -1116,6 +1168,8 @@ frange::set_undefined ()
   m_kind = VR_UNDEFINED;
   m_type = NULL;
   m_props.set_undefined ();
+  memset (&m_min, 0, sizeof (m_min));
+  memset (&m_max, 0, sizeof (m_max));
 }
 
 #endif // GCC_VALUE_RANGE_H
-- 
2.37.1


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 15:55 ` Aldy Hernandez
@ 2022-08-26 17:40   ` Andrew Pinski
  2022-08-26 18:11     ` Aldy Hernandez
  2022-08-26 19:16     ` Aldy Hernandez
  0 siblings, 2 replies; 25+ messages in thread
From: Andrew Pinski @ 2022-08-26 17:40 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: GCC patches, Andrew MacLeod, Jakub Jelinek

On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> [pinskia: I'm CCing you as the author of the match.pd pattern.]
>
> So, as I wrap up the work here (latest patch attached), I see there's
> another phiopt regression (not spaceship related).  I was hoping
> someone could either give me a hand, or offer some guidance.
>
> The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
>
> We fail to transform the following into -A:
>
> /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
>
> float f0(float A)
> {
>   //     A == 0? A : -A    same as -A
>   if (A == 0)  return A;
>   return -A;
> }
>
> This is because the abs/negative match.pd pattern here:
>
> /* abs/negative simplifications moved from fold_cond_expr_with_comparison,
>    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
>    Need to handle UN* comparisons.
>    ...
>    ...
>
> Sees IL that has the 0.0 propagated.
>
> Instead of:
>
>   <bb 2> [local count: 1073741824]:
>   if (A_2(D) == 0.0)
>     goto <bb 4>; [34.00%]
>   else
>     goto <bb 3>; [66.00%]
>
>   <bb 3> [local count: 708669601]:
>   _3 = -A_2(D);
>
>   <bb 4> [local count: 1073741824]:
>   # _1 = PHI <A_2(D)(2), _3(3)>
>
> It now sees:
>
>   <bb 4> [local count: 1073741824]:
>   # _1 = PHI <0.0(2), _3(3)>
>
> which it leaves untouched, causing the if conditional to survive.
>
> Is this something that can be done by improving the match.pd pattern,
> or should be done elsewhere?

Oh the pattern which is supposed to catch this does:
  (simplify
   (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
    (if (!HONOR_SIGNED_ZEROS (type))
     @1))

Notice the integer_zerop here.
fold_cond_expr_with_comparison has integer_zerop there too.
I am not 100% sure you can replace A_2 with 0.0 where you are doing it
as mentioned in another thread.

Thanks,
Andrew Pinski


>
> Thanks.
> Aldy

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 17:40   ` Andrew Pinski
@ 2022-08-26 18:11     ` Aldy Hernandez
  2022-08-26 18:11       ` Aldy Hernandez
  2022-08-26 19:16     ` Aldy Hernandez
  1 sibling, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-26 18:11 UTC (permalink / raw)
  To: Andrew Pinski; +Cc: GCC patches, Andrew MacLeod, Jakub Jelinek

On Fri, Aug 26, 2022, 19:40 Andrew Pinski <pinskia@gmail.com> wrote:

> On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> >
> > [pinskia: I'm CCing you as the author of the match.pd pattern.]
> >
> > So, as I wrap up the work here (latest patch attached), I see there's
> > another phiopt regression (not spaceship related).  I was hoping
> > someone could either give me a hand, or offer some guidance.
> >
> > The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
> >
> > We fail to transform the following into -A:
> >
> > /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
> >
> > float f0(float A)
> > {
> >   //     A == 0? A : -A    same as -A
> >   if (A == 0)  return A;
> >   return -A;
> > }
> >
> > This is because the abs/negative match.pd pattern here:
> >
> > /* abs/negative simplifications moved from
> fold_cond_expr_with_comparison,
> >    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
> >    Need to handle UN* comparisons.
> >    ...
> >    ...
> >
> > Sees IL that has the 0.0 propagated.
> >
> > Instead of:
> >
> >   <bb 2> [local count: 1073741824]:
> >   if (A_2(D) == 0.0)
> >     goto <bb 4>; [34.00%]
> >   else
> >     goto <bb 3>; [66.00%]
> >
> >   <bb 3> [local count: 708669601]:
> >   _3 = -A_2(D);
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <A_2(D)(2), _3(3)>
> >
> > It now sees:
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <0.0(2), _3(3)>
> >
> > which it leaves untouched, causing the if conditional to survive.
> >
> > Is this something that can be done by improving the match.pd pattern,
> > or should be done elsewhere?
>
> Oh the pattern which is supposed to catch this does:
>   (simplify
>    (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
>     (if (!HONOR_SIGNED_ZEROS (type))
>      @1))
>
> Notice the integer_zerop here.
> fold_cond_expr_with_comparison has integer_zerop there too.
> I am not 100% sure you can replace A_2 with 0.0 where you are doing it
> as mentioned in another thread.
>

Are you sure we can't make the replacement, cause the test runs with
-fno-signed-zeros?

Aldy


> Thanks,
> Andrew Pinski
>
>
> >
> > Thanks.
> > Aldy
>
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 18:11     ` Aldy Hernandez
@ 2022-08-26 18:11       ` Aldy Hernandez
  0 siblings, 0 replies; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-26 18:11 UTC (permalink / raw)
  To: Andrew Pinski; +Cc: GCC patches, Andrew MacLeod, Jakub Jelinek

[-- Attachment #1: Type: text/plain, Size: 2189 bytes --]

On Fri, Aug 26, 2022, 19:40 Andrew Pinski <pinskia@gmail.com> wrote:

> On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> >
> > [pinskia: I'm CCing you as the author of the match.pd pattern.]
> >
> > So, as I wrap up the work here (latest patch attached), I see there's
> > another phiopt regression (not spaceship related).  I was hoping
> > someone could either give me a hand, or offer some guidance.
> >
> > The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
> >
> > We fail to transform the following into -A:
> >
> > /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
> >
> > float f0(float A)
> > {
> >   //     A == 0? A : -A    same as -A
> >   if (A == 0)  return A;
> >   return -A;
> > }
> >
> > This is because the abs/negative match.pd pattern here:
> >
> > /* abs/negative simplifications moved from
> fold_cond_expr_with_comparison,
> >    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
> >    Need to handle UN* comparisons.
> >    ...
> >    ...
> >
> > Sees IL that has the 0.0 propagated.
> >
> > Instead of:
> >
> >   <bb 2> [local count: 1073741824]:
> >   if (A_2(D) == 0.0)
> >     goto <bb 4>; [34.00%]
> >   else
> >     goto <bb 3>; [66.00%]
> >
> >   <bb 3> [local count: 708669601]:
> >   _3 = -A_2(D);
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <A_2(D)(2), _3(3)>
> >
> > It now sees:
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <0.0(2), _3(3)>
> >
> > which it leaves untouched, causing the if conditional to survive.
> >
> > Is this something that can be done by improving the match.pd pattern,
> > or should be done elsewhere?
>
> Oh the pattern which is supposed to catch this does:
>   (simplify
>    (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
>     (if (!HONOR_SIGNED_ZEROS (type))
>      @1))
>
> Notice the integer_zerop here.
> fold_cond_expr_with_comparison has integer_zerop there too.
> I am not 100% sure you can replace A_2 with 0.0 where you are doing it
> as mentioned in another thread.
>

Are you sure we can't make the replacement, cause the test runs with
-fno-signed-zeros?

Aldy


> Thanks,
> Andrew Pinski
>
>
> >
> > Thanks.
> > Aldy
>
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 17:40   ` Andrew Pinski
  2022-08-26 18:11     ` Aldy Hernandez
@ 2022-08-26 19:16     ` Aldy Hernandez
  2022-08-26 19:44       ` Andrew Pinski
  1 sibling, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-26 19:16 UTC (permalink / raw)
  To: Andrew Pinski; +Cc: GCC patches, Andrew MacLeod, Jakub Jelinek

On Fri, Aug 26, 2022 at 7:40 PM Andrew Pinski <pinskia@gmail.com> wrote:
>
> On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> >
> > [pinskia: I'm CCing you as the author of the match.pd pattern.]
> >
> > So, as I wrap up the work here (latest patch attached), I see there's
> > another phiopt regression (not spaceship related).  I was hoping
> > someone could either give me a hand, or offer some guidance.
> >
> > The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
> >
> > We fail to transform the following into -A:
> >
> > /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
> >
> > float f0(float A)
> > {
> >   //     A == 0? A : -A    same as -A
> >   if (A == 0)  return A;
> >   return -A;
> > }
> >
> > This is because the abs/negative match.pd pattern here:
> >
> > /* abs/negative simplifications moved from fold_cond_expr_with_comparison,
> >    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
> >    Need to handle UN* comparisons.
> >    ...
> >    ...
> >
> > Sees IL that has the 0.0 propagated.
> >
> > Instead of:
> >
> >   <bb 2> [local count: 1073741824]:
> >   if (A_2(D) == 0.0)
> >     goto <bb 4>; [34.00%]
> >   else
> >     goto <bb 3>; [66.00%]
> >
> >   <bb 3> [local count: 708669601]:
> >   _3 = -A_2(D);
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <A_2(D)(2), _3(3)>
> >
> > It now sees:
> >
> >   <bb 4> [local count: 1073741824]:
> >   # _1 = PHI <0.0(2), _3(3)>
> >
> > which it leaves untouched, causing the if conditional to survive.
> >
> > Is this something that can be done by improving the match.pd pattern,
> > or should be done elsewhere?
>
> Oh the pattern which is supposed to catch this does:
>   (simplify
>    (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
>     (if (!HONOR_SIGNED_ZEROS (type))
>      @1))

On trunk without any patches, for the following snippet with -O2
-fno-signed-zeros -fdump-tree-phiopt-folding...

float f0(float A)
{
  //     A == 0? A : -A    same as -A
  if (A == 0)  return A;
  return -A;
}

...the phiopt2 dump file has:

Applying pattern match.pd:4805, gimple-match.cc:69291, which
corresponds to the aforementioned pattern.  So it looks like that was
the pattern that was matching that isn't any more?

Are you saying this pattern should only work with integers?

Aldy


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 19:16     ` Aldy Hernandez
@ 2022-08-26 19:44       ` Andrew Pinski
  2022-08-29 13:45         ` Aldy Hernandez
  0 siblings, 1 reply; 25+ messages in thread
From: Andrew Pinski @ 2022-08-26 19:44 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: GCC patches, Andrew MacLeod, Jakub Jelinek

On Fri, Aug 26, 2022 at 12:16 PM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> On Fri, Aug 26, 2022 at 7:40 PM Andrew Pinski <pinskia@gmail.com> wrote:
> >
> > On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> > >
> > > [pinskia: I'm CCing you as the author of the match.pd pattern.]
> > >
> > > So, as I wrap up the work here (latest patch attached), I see there's
> > > another phiopt regression (not spaceship related).  I was hoping
> > > someone could either give me a hand, or offer some guidance.
> > >
> > > The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
> > >
> > > We fail to transform the following into -A:
> > >
> > > /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
> > >
> > > float f0(float A)
> > > {
> > >   //     A == 0? A : -A    same as -A
> > >   if (A == 0)  return A;
> > >   return -A;
> > > }
> > >
> > > This is because the abs/negative match.pd pattern here:
> > >
> > > /* abs/negative simplifications moved from fold_cond_expr_with_comparison,
> > >    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
> > >    Need to handle UN* comparisons.
> > >    ...
> > >    ...
> > >
> > > Sees IL that has the 0.0 propagated.
> > >
> > > Instead of:
> > >
> > >   <bb 2> [local count: 1073741824]:
> > >   if (A_2(D) == 0.0)
> > >     goto <bb 4>; [34.00%]
> > >   else
> > >     goto <bb 3>; [66.00%]
> > >
> > >   <bb 3> [local count: 708669601]:
> > >   _3 = -A_2(D);
> > >
> > >   <bb 4> [local count: 1073741824]:
> > >   # _1 = PHI <A_2(D)(2), _3(3)>
> > >
> > > It now sees:
> > >
> > >   <bb 4> [local count: 1073741824]:
> > >   # _1 = PHI <0.0(2), _3(3)>
> > >
> > > which it leaves untouched, causing the if conditional to survive.
> > >
> > > Is this something that can be done by improving the match.pd pattern,
> > > or should be done elsewhere?
> >
> > Oh the pattern which is supposed to catch this does:
> >   (simplify
> >    (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
> >     (if (!HONOR_SIGNED_ZEROS (type))
> >      @1))
>
> On trunk without any patches, for the following snippet with -O2
> -fno-signed-zeros -fdump-tree-phiopt-folding...
>
> float f0(float A)
> {
>   //     A == 0? A : -A    same as -A
>   if (A == 0)  return A;
>   return -A;
> }
>
> ...the phiopt2 dump file has:
>
> Applying pattern match.pd:4805, gimple-match.cc:69291, which
> corresponds to the aforementioned pattern.  So it looks like that was
> the pattern that was matching that isn't any more?
>
> Are you saying this pattern should only work with integers?

I am saying the pattern which is right after the one that matches
(without your patch) currrently works for integer only.
You could change integer_zerop to zerop in that pattern but I am not
100% sure that is valid thing to do.
Note there are a few other patterns in that for loop that does
integer_zerop which might need to be zerop too.

Thanks,
Andrew Pinski

>
> Aldy
>

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-26 19:44       ` Andrew Pinski
@ 2022-08-29 13:45         ` Aldy Hernandez
  2022-08-29 13:54           ` Jakub Jelinek
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 13:45 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

[-- Attachment #1: Type: text/plain, Size: 4444 bytes --]

Jakub, et al... here is the latest version of the frange endpoints
patch addressing the signed zero problem (well treating +-0.0
ambiguously), as well as implementing all the relational operators.

[Andrew M: I mostly copied our relop code from irange, while keeping
track NANs, etc.  It should be pleasantly familiar.]

A few notes...

We can now represent things like [5.0, 5.0], which is the singleton
5.0 *or* a NAN.  OTOH, we could also have [5.0, 5.0] !NAN, which is
5.0 without the possibility of a NAN.  This allows us to track
appropriate ranges on both sides of an if, but keep track of which
side (true side) we definitely know we won't have a NAN.

As mentioned, frange::singleton_p([0,0]) returns false for any version
of 0.0 (unless !HONOR_SIGNED_ZEROS).  I'll work on zero tracking at a
later time.  The patch is getting pretty large as is.

For convenience, singleton_p() returns false for a NAN.  IMO, it makes
the implementation cleaner, but I'm not wed to the idea if someone
objects.

This patch passes all tests for all languages, including go and ada,
with the aforementioned exception of gcc.dg/tree-ssa/phi-opt-24.c
which is getting confused because we have propagated (correctly) a 0.0
into the PHI.  Hints?  Takers? ;-).

Aldy

On Fri, Aug 26, 2022 at 9:44 PM Andrew Pinski <pinskia@gmail.com> wrote:
>
> On Fri, Aug 26, 2022 at 12:16 PM Aldy Hernandez <aldyh@redhat.com> wrote:
> >
> > On Fri, Aug 26, 2022 at 7:40 PM Andrew Pinski <pinskia@gmail.com> wrote:
> > >
> > > On Fri, Aug 26, 2022 at 8:55 AM Aldy Hernandez <aldyh@redhat.com> wrote:
> > > >
> > > > [pinskia: I'm CCing you as the author of the match.pd pattern.]
> > > >
> > > > So, as I wrap up the work here (latest patch attached), I see there's
> > > > another phiopt regression (not spaceship related).  I was hoping
> > > > someone could either give me a hand, or offer some guidance.
> > > >
> > > > The failure is in gcc.dg/tree-ssa/phi-opt-24.c.
> > > >
> > > > We fail to transform the following into -A:
> > > >
> > > > /* { dg-options "-O2 -fno-signed-zeros -fdump-tree-phiopt" } */
> > > >
> > > > float f0(float A)
> > > > {
> > > >   //     A == 0? A : -A    same as -A
> > > >   if (A == 0)  return A;
> > > >   return -A;
> > > > }
> > > >
> > > > This is because the abs/negative match.pd pattern here:
> > > >
> > > > /* abs/negative simplifications moved from fold_cond_expr_with_comparison,
> > > >    Need to handle (A - B) case as fold_cond_expr_with_comparison does.
> > > >    Need to handle UN* comparisons.
> > > >    ...
> > > >    ...
> > > >
> > > > Sees IL that has the 0.0 propagated.
> > > >
> > > > Instead of:
> > > >
> > > >   <bb 2> [local count: 1073741824]:
> > > >   if (A_2(D) == 0.0)
> > > >     goto <bb 4>; [34.00%]
> > > >   else
> > > >     goto <bb 3>; [66.00%]
> > > >
> > > >   <bb 3> [local count: 708669601]:
> > > >   _3 = -A_2(D);
> > > >
> > > >   <bb 4> [local count: 1073741824]:
> > > >   # _1 = PHI <A_2(D)(2), _3(3)>
> > > >
> > > > It now sees:
> > > >
> > > >   <bb 4> [local count: 1073741824]:
> > > >   # _1 = PHI <0.0(2), _3(3)>
> > > >
> > > > which it leaves untouched, causing the if conditional to survive.
> > > >
> > > > Is this something that can be done by improving the match.pd pattern,
> > > > or should be done elsewhere?
> > >
> > > Oh the pattern which is supposed to catch this does:
> > >   (simplify
> > >    (cnd (cmp @0 zerop) integer_zerop (negate@1 @0))
> > >     (if (!HONOR_SIGNED_ZEROS (type))
> > >      @1))
> >
> > On trunk without any patches, for the following snippet with -O2
> > -fno-signed-zeros -fdump-tree-phiopt-folding...
> >
> > float f0(float A)
> > {
> >   //     A == 0? A : -A    same as -A
> >   if (A == 0)  return A;
> >   return -A;
> > }
> >
> > ...the phiopt2 dump file has:
> >
> > Applying pattern match.pd:4805, gimple-match.cc:69291, which
> > corresponds to the aforementioned pattern.  So it looks like that was
> > the pattern that was matching that isn't any more?
> >
> > Are you saying this pattern should only work with integers?
>
> I am saying the pattern which is right after the one that matches
> (without your patch) currrently works for integer only.
> You could change integer_zerop to zerop in that pattern but I am not
> 100% sure that is valid thing to do.
> Note there are a few other patterns in that for loop that does
> integer_zerop which might need to be zerop too.
>
> Thanks,
> Andrew Pinski
>
> >
> > Aldy
> >
>

[-- Attachment #2: 0001-Add-support-for-floating-point-endpoints-to-frange.patch --]
[-- Type: text/x-patch, Size: 46106 bytes --]

From 67e61693dfda7fa6dadb0bddc62b2d70a88d9dce Mon Sep 17 00:00:00 2001
From: Aldy Hernandez <aldyh@redhat.com>
Date: Tue, 23 Aug 2022 13:36:33 +0200
Subject: [PATCH] Add support for floating point endpoints to frange.

The current implementation of frange is just a type with some bits to
represent NAN and INF.  We can do better and represent endpoints to
ultimately solve longstanding PRs such as PR24021.  This patch adds
these endpoints.  In follow-up patches I will add support for
a bare bones PLUS_EXPR range-op-float entry to solve the PR.

I have chosen to use REAL_VALUE_TYPEs for the endpoints, since that's
what we use underneath the trees.  This will be somewhat analogous to
our eventual use of wide-ints in the irange.  No sense going through
added levels of indirection if we can avoid it.  That, plus real.*
already has a nice API for dealing with floats.

With this patch, ranges will be closed float point intervals, which
make the implementation simpler, since we don't have to keep track of
open/closed intervals.  This is conservative enough for use in the
ranger world, as we'd rather err on the side of more elements in a
range, than less.

For example, even though we cannot precisely represent the open
interval (3.0, 5.0) with this approach, it is perfectably reasonable
to represent it as [3.0, 5.0] since the closed interval is a super set
of the open one.  In the VRP/ranger world, it is always better to
err on the side of more information in a range, than not.  After all,
when we don't know anything about a range, we just use VARYING which
is a fancy term for a range spanning the entire domain.

Since REAL_VALUE_TYPEs have properly defined infinity and NAN
semantics, all the math can be made to work:

[-INF, 3.0] !NAN	=> Numbers <= 3.0 (NAN cannot happen)
[3.0, +INF]		=> Numbers >= 3.0 (NAN is possible)
[-INF, +INF]		=> VARYING (NAN is possible)
[-INF, +INF] !NAN	=> Entire domain.  NAN cannot happen.

Also, since REAL_VALUE_TYPEs can represent the minimum and maximum
representable values of a TYPE_MODE, we can disambiguate between them
and negative and positive infinity (see get_max_float in real.cc).

This also makes the math all work.  For example, suppose we know
nothing about x and y (VARYING).  On the TRUE side of x > y, we can
deduce that:

	(a) x cannot be NAN
	(b) y cannot be NAN
	(c) y cannot be +INF.

(c) means that we can drop the upper bound of "y" from +INF to the
maximum representable value for its type.

Having endpoints with different representation for infinity and the
maximum representable values, means we can drop the +-INF properties
we currently have in the frange.

gcc/ChangeLog:

	* range-op-float.cc (frange_set_nan): New.
	(finite_operand_p): New.
	(build_le): New.
	(build_lt): New.
	(build_ge): New.
	(build_gt): New.
	(foperator_equal::fold_range): Adapt for endpoints.
	(foperator_equal::op1_range): Same.
	(foperator_not_equal::fold_range): Same.
	(foperator_not_equal::op1_range): Same.
	(foperator_lt::fold_range): Same.
	(foperator_lt::op1_range): Same.
	(foperator_lt::op2_range): Same.
	(foperator_le::fold_range): Same.
	(foperator_le::op1_range): Same.
	(foperator_le::op2_range): Same.
	(foperator_gt::fold_range): Same.
	(foperator_gt::op1_range): Same.
	(foperator_gt::op2_range): Same.
	(foperator_ge::fold_range): Same.
	(foperator_ge::op1_range): Same.
	(foperator_ge::op2_range): Same.
	(foperator_unordered::op1_range): Same.
	* value-query.cc (range_query::get_tree_range): Set NAN property
	if appropriate.
	* value-range-pretty-print.cc (vrange_printer::visit): Adapt for endpoints.
	* value-range-storage.cc (frange_storage_slot::get_frange): Same.
	* value-range.cc (frange::set): Same.
	(frange::normalize_kind): Same.
	(early_nan_resolve): New.
	(frange::union_): Adapt for endpoints.
	(frange::intersect): Same.
	(frange::operator=): Same.
	(frange::contains_p): New.
	(frange::singleton_p): New.
	(frange::set_nonzero): New.
	(frange::nonzero_p): New.
	(frange::set_zero): New.
	(frange::zero_p): New.
	(frange::set_nonnegative): New.
	(frange_float): New.
	(frange_nan): New.
	(real_max_representable): New.
	(real_min_representable): New.
	(range_tests_nan): New.
	(range_tests_signed_zeros): New.
	(range_tests_floats): New.
	(range_tests): Call range_tests_floats.
	* value-range.h (class frange_props): Remove inf and ninf
	properties.
	(class frange): Add new constructors.
	Add contains_p, singleton_p, zero_p, nonzero_p, set_nonzero,
	set_zero, set_nonnegative, lower_bound, upper_bound.
	(frange::lower_bound): New.
	(frange::upper_bound): New.
	(vrp_val_min): Use real_inf with a sign argument.
	(frange::set_varying): Adapt for endpoints.
	(frange::set_undefined): Same.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/recip-3.c: Disable DOM and jump threading.
---
 gcc/range-op-float.cc                   | 395 +++++++++++++++----
 gcc/testsuite/gcc.dg/tree-ssa/recip-3.c |   5 +
 gcc/value-query.cc                      |   9 +
 gcc/value-range-pretty-print.cc         |  14 +-
 gcc/value-range-storage.cc              |  12 +
 gcc/value-range.cc                      | 484 +++++++++++++++++++++---
 gcc/value-range.h                       |  64 +++-
 7 files changed, 852 insertions(+), 131 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index ff9fe312acf..c76a43c6141 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -150,6 +150,18 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons
   return VREL_VARYING;
 }
 
+// Set R to [NAN, NAN].
+
+static inline void
+frange_set_nan (frange &r, tree type)
+{
+  REAL_VALUE_TYPE rv;
+  bool res = real_nan (&rv, "", 1, TYPE_MODE (type));
+  if (flag_checking)
+    gcc_assert (res);
+  r.set (type, rv, rv);
+}
+
 // Return TRUE if OP1 and OP2 are known to be free of NANs.
 
 static inline bool
@@ -160,6 +172,14 @@ finite_operands_p (const frange &op1, const frange &op2)
 	      && op2.get_nan ().no_p ()));
 }
 
+// Return TRUE if OP1 is known to be free of NANs.
+
+static inline bool
+finite_operand_p (const frange &op1)
+{
+  return flag_finite_math_only || op1.get_nan ().no_p ();
+}
+
 // Floating version of relop_early_resolve that takes into account NAN
 // and -ffinite-math-only.
 
@@ -194,6 +214,45 @@ default_frelop_fold_range (irange &r, tree type,
   return true;
 }
 
+// (X <= VAL) produces the range of [MIN, VAL].
+
+static void
+build_le (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+  REAL_VALUE_TYPE min;
+  real_inf (&min, 1);
+  r.set (type, min, val);
+}
+
+// (X < VAL) produces the range of [MIN, VAL).
+
+static void
+build_lt (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+  // Hijack LE because we only support closed intervals.
+  build_le (r, type, val);
+}
+
+// (X >= VAL) produces the range of [VAL, MAX].
+
+static void
+build_ge (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+  REAL_VALUE_TYPE max;
+  real_inf (&max, 0);
+  r.set (type, val, max);
+}
+
+// (X > VAL) produces the range of (VAL, MAX].
+
+static void
+build_gt (frange &r, tree type, const REAL_VALUE_TYPE &val)
+{
+  // Hijack GE because we only support closed intervals.
+  build_ge (r, type, val);
+}
+
+
 class foperator_identity : public range_operator_float
 {
   using range_operator_float::fold_range;
@@ -224,10 +283,7 @@ class foperator_equal : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_EQ);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return equal_op1_op2_relation (lhs);
@@ -243,6 +299,39 @@ class foperator_equal : public range_operator_float
   }
 } fop_equal;
 
+bool
+foperator_equal::fold_range (irange &r, tree type,
+			     const frange &op1, const frange &op2,
+			     relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_EQ))
+    return true;
+
+  // We can be sure the values are always equal or not if both ranges
+  // consist of a single value, and then compare them.
+  if (op1.singleton_p () && op2.singleton_p ())
+    {
+      if (op1 == op2)
+	r = range_true (type);
+      else
+	r = range_false (type);
+    }
+  else if (finite_operands_p (op1, op2))
+    {
+      // If ranges do not intersect, we know the range is not equal,
+      // otherwise we don't know anything for sure.
+      frange tmp = op1;
+      tmp.intersect (op2);
+      if (tmp.undefined_p ())
+	r = range_false (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_equal::op1_range (frange &r, tree type,
 			    const irange &lhs,
@@ -252,21 +341,8 @@ foperator_equal::op1_range (frange &r, tree type,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      if (HONOR_SIGNED_ZEROS (type)
-	  && op2.contains_p (build_zero_cst (type)))
-	{
-	  // With signed zeros, x == -0.0 does not mean we can replace
-	  // x with -0.0, because x may be either +0.0 or -0.0.
-	  r.set_varying (type);
-	}
-      else
-	{
-	  // If it's true, the result is the same as OP2.
-	  //
-	  // If the range does not actually contain zeros, this should
-	  // always be OK.
-	  r = op2;
-	}
+      // If it's true, the result is the same as OP2.
+      r = op2;
       // The TRUE side of op1 == op2 implies op1 is !NAN.
       r.set_nan (fp_prop::NO);
       break;
@@ -275,7 +351,15 @@ foperator_equal::op1_range (frange &r, tree type,
       r.set_varying (type);
       // The FALSE side of op1 == op1 implies op1 is a NAN.
       if (rel == VREL_EQ)
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
+      // If the result is false, the only time we know anything is
+      // if OP2 is a constant.
+      else if (op2.singleton_p ()
+	       || (finite_operand_p (op2) && op2.zero_p ()))
+	{
+	  REAL_VALUE_TYPE tmp = op2.lower_bound ();
+	  r.set (type, tmp, tmp, VR_ANTI_RANGE);
+	}
       break;
 
     default:
@@ -291,10 +375,7 @@ class foperator_not_equal : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_NE);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return not_equal_op1_op2_relation (lhs);
@@ -304,6 +385,39 @@ class foperator_not_equal : public range_operator_float
 		  relation_kind rel) const final override;
 } fop_not_equal;
 
+bool
+foperator_not_equal::fold_range (irange &r, tree type,
+				 const frange &op1, const frange &op2,
+				 relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_NE))
+    return true;
+
+  // We can be sure the values are always equal or not if both ranges
+  // consist of a single value, and then compare them.
+  if (op1.singleton_p () && op2.singleton_p ())
+    {
+      if (op1 != op2)
+	r = range_true (type);
+      else
+	r = range_false (type);
+    }
+  else if (finite_operands_p (op1, op2))
+    {
+      // If ranges do not intersect, we know the range is not equal,
+      // otherwise we don't know anything for sure.
+      frange tmp = op1;
+      tmp.intersect (op2);
+      if (tmp.undefined_p ())
+	r = range_true (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_not_equal::op1_range (frange &r, tree type,
 				const irange &lhs,
@@ -313,11 +427,23 @@ foperator_not_equal::op1_range (frange &r, tree type,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
+      // If the result is true, the only time we know anything is if
+      // OP2 is a constant.
+      if (op2.singleton_p ())
+	{
+	  // This is correct even if op1 is NAN, because the following
+	  // range would be ~[tmp, tmp] with the NAN property set to
+	  // maybe (VARYING).
+	  REAL_VALUE_TYPE tmp = op2.lower_bound ();
+	  r.set (type, tmp, tmp, VR_ANTI_RANGE);
+	}
+      else
+	r.set_varying (type);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      // If it's false, the result is the same as OP2.
+      r = op2;
       // The FALSE side of op1 != op2 implies op1 is !NAN.
       r.set_nan (fp_prop::NO);
       break;
@@ -336,10 +462,7 @@ class foperator_lt : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LT);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return lt_op1_op2_relation (lhs);
@@ -352,6 +475,31 @@ class foperator_lt : public range_operator_float
 		  relation_kind rel) const final override;
 } fop_lt;
 
+bool
+foperator_lt::fold_range (irange &r, tree type,
+			  const frange &op1, const frange &op2,
+			  relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LT))
+    return true;
+
+  if (finite_operands_p (op1, op2))
+    {
+      if (real_less (&op1.upper_bound (), &op2.lower_bound ()))
+	r = range_true (type);
+      else if (finite_operands_p (op1, op2)
+	       && !real_less (&op1.lower_bound (), &op2.upper_bound ()))
+	r = range_false (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_lt::op1_range (frange &r,
 			 tree type,
@@ -362,14 +510,12 @@ foperator_lt::op1_range (frange &r,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 < op2 implies op1 is !NAN and !INF.
+      build_lt (r, type, op2.upper_bound ());
       r.set_nan (fp_prop::NO);
-      r.set_inf (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_ge (r, type, op2.lower_bound ());
       break;
 
     default:
@@ -388,14 +534,12 @@ foperator_lt::op2_range (frange &r,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 < op2 implies op2 is !NAN and !NINF.
+      build_gt (r, type, op1.lower_bound ());
       r.set_nan (fp_prop::NO);
-      r.set_ninf (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_le (r, type, op1.upper_bound ());
       break;
 
     default:
@@ -412,10 +556,7 @@ class foperator_le : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_LE);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return le_op1_op2_relation (lhs);
@@ -425,29 +566,74 @@ class foperator_le : public range_operator_float
 		  relation_kind rel) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel) const final override
-  {
-    return op1_range (r, type, lhs, op1, rel);
-  }
+		  relation_kind rel) const final override;
 } fop_le;
 
+bool
+foperator_le::fold_range (irange &r, tree type,
+			  const frange &op1, const frange &op2,
+			  relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_LE))
+    return true;
+
+  if (finite_operands_p (op1, op2))
+    {
+      if (real_compare (LE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+	r = range_true (type);
+      else if (finite_operands_p (op1, op2)
+	       && !real_compare (LE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+	r = range_false (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_le::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
-			 const frange &op2 ATTRIBUTE_UNUSED,
+			 const frange &op2,
 			 relation_kind) const
 {
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 <= op2 implies op1 is !NAN.
+      build_le (r, type, op2.upper_bound ());
       r.set_nan (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_gt (r, type, op2.lower_bound ());
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+bool
+foperator_le::op2_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op1,
+			 relation_kind) const
+{
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_TRUE:
+      build_ge (r, type, op1.lower_bound ());
+      r.set_nan (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      build_lt (r, type, op1.upper_bound ());
       break;
 
     default:
@@ -464,10 +650,7 @@ class foperator_gt : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GT);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return gt_op1_op2_relation (lhs);
@@ -480,6 +663,31 @@ class foperator_gt : public range_operator_float
 		  relation_kind rel) const final override;
 } fop_gt;
 
+bool
+foperator_gt::fold_range (irange &r, tree type,
+			  const frange &op1, const frange &op2,
+			  relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GT))
+    return true;
+
+  if (finite_operands_p (op1, op2))
+    {
+      if (real_compare (GT_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+	r = range_true (type);
+      else if (finite_operands_p (op1, op2)
+	       && !real_compare (GT_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+	r = range_false (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_gt::op1_range (frange &r,
 			 tree type,
@@ -490,14 +698,12 @@ foperator_gt::op1_range (frange &r,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 > op2 implies op1 is !NAN and !NINF.
+      build_gt (r, type, op2.lower_bound ());
       r.set_nan (fp_prop::NO);
-      r.set_ninf (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_le (r, type, op2.upper_bound ());
       break;
 
     default:
@@ -516,14 +722,12 @@ foperator_gt::op2_range (frange &r,
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 > op2 implies op2 is !NAN and !INF.
+      build_lt (r, type, op1.upper_bound ());
       r.set_nan (fp_prop::NO);
-      r.set_inf (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_ge (r, type, op1.lower_bound ());
       break;
 
     default:
@@ -540,10 +744,7 @@ class foperator_ge : public range_operator_float
 
   bool fold_range (irange &r, tree type,
 		   const frange &op1, const frange &op2,
-		   relation_kind rel) const final override
-  {
-    return default_frelop_fold_range (r, type, op1, op2, rel, VREL_GE);
-  }
+		   relation_kind rel) const final override;
   relation_kind op1_op2_relation (const irange &lhs) const final override
   {
     return ge_op1_op2_relation (lhs);
@@ -553,29 +754,73 @@ class foperator_ge : public range_operator_float
 		  relation_kind rel) const final override;
   bool op2_range (frange &r, tree type,
 		  const irange &lhs, const frange &op1,
-		  relation_kind rel) const final override
-  {
-    return op1_range (r, type, lhs, op1, rel);
-  }
+		  relation_kind rel) const final override;
 } fop_ge;
 
+bool
+foperator_ge::fold_range (irange &r, tree type,
+			  const frange &op1, const frange &op2,
+			  relation_kind rel) const
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, VREL_GE))
+    return true;
+
+  if (finite_operands_p (op1, op2))
+    {
+      if (real_compare (GE_EXPR, &op1.lower_bound (), &op2.upper_bound ()))
+	r = range_true (type);
+      else if (finite_operands_p (op1, op2)
+	       && !real_compare (GE_EXPR, &op1.upper_bound (), &op2.lower_bound ()))
+	r = range_false (type);
+      else
+	r = range_true_and_false (type);
+    }
+  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
 bool
 foperator_ge::op1_range (frange &r,
 			 tree type,
 			 const irange &lhs,
-			 const frange &op2 ATTRIBUTE_UNUSED,
+			 const frange &op2,
 			 relation_kind) const
 {
   switch (get_bool_state (r, lhs, type))
     {
     case BRS_TRUE:
-      r.set_varying (type);
-      // The TRUE side of op1 >= op2 implies op1 is !NAN.
+      build_ge (r, type, op2.lower_bound ());
       r.set_nan (fp_prop::NO);
       break;
 
     case BRS_FALSE:
-      r.set_varying (type);
+      build_lt (r, type, op2.upper_bound ());
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+bool
+foperator_ge::op2_range (frange &r, tree type,
+			 const irange &lhs,
+			 const frange &op1,
+			 relation_kind) const
+{
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_FALSE:
+      build_gt (r, type, op1.lower_bound ());
+      break;
+
+    case BRS_TRUE:
+      build_le (r, type, op1.upper_bound ());
+      r.set_nan (fp_prop::NO);
       break;
 
     default:
@@ -636,7 +881,7 @@ foperator_unordered::op1_range (frange &r, tree 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 ())
-	r.set_nan (fp_prop::YES);
+	frange_set_nan (r, type);
       break;
 
     case BRS_FALSE:
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
index 410b28044b4..036f32a9c33 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/recip-3.c
@@ -1,6 +1,11 @@
 /* { dg-do compile } */
 /* { dg-options "-O1 -fno-trapping-math -funsafe-math-optimizations -fdump-tree-recip" } */
 
+/* The recip pass has a threshold of 3 reciprocal operations before it attempts
+   to optimize a sequence.  With a FP enabled ranger, we eliminate one of them
+   earlier, causing the pass to skip this optimization.  */
+/* { dg-additional-options "-fno-thread-jumps -fno-tree-dominator-opts" } */
+
 double F[5] = { 0.0, 0.0 }, e;
 
 /* In this case the optimization is interesting.  */
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 4af8eca0172..4637fb409e5 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -211,10 +211,19 @@ range_query::get_tree_range (vrange &r, tree expr, gimple *stmt)
   switch (TREE_CODE (expr))
     {
     case INTEGER_CST:
+      if (TREE_OVERFLOW_P (expr))
+	expr = drop_tree_overflow (expr);
+      r.set (expr, expr);
+      return true;
+
     case REAL_CST:
       if (TREE_OVERFLOW_P (expr))
 	expr = drop_tree_overflow (expr);
       r.set (expr, expr);
+      if (real_isnan (TREE_REAL_CST_PTR (expr)))
+	as_a <frange> (r).set_nan (fp_prop::YES);
+      else
+	as_a <frange> (r).set_nan (fp_prop::NO);
       return true;
 
     case SSA_NAME:
diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index cbf50d3d854..e66d56da29d 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -122,22 +122,30 @@ vrange_printer::print_irange_bitmasks (const irange &r) const
 void
 vrange_printer::visit (const frange &r) const
 {
+  tree type = r.type ();
+
   pp_string (pp, "[frange] ");
   if (r.undefined_p ())
     {
       pp_string (pp, "UNDEFINED");
       return;
     }
-  dump_generic_node (pp, r.type (), 0, TDF_NONE, false);
+  dump_generic_node (pp, type, 0, TDF_NONE, false);
   pp_string (pp, " ");
   if (r.varying_p ())
     {
       pp_string (pp, "VARYING");
       return;
     }
+  pp_character (pp, '[');
+  dump_generic_node (pp,
+		     build_real (type, r.lower_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, ", ");
+  dump_generic_node (pp,
+		     build_real (type, r.upper_bound ()), 0, TDF_NONE, false);
+  pp_string (pp, "] ");
+
   print_frange_prop ("NAN", r.get_nan ());
-  print_frange_prop ("INF", r.get_inf ());
-  print_frange_prop ("NINF", r.get_ninf ());
 }
 
 // Print the FP properties in an frange.
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index ea3b83ca641..adf23c39f0d 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -261,6 +261,18 @@ frange_storage_slot::get_frange (frange &r, tree type) const
 {
   gcc_checking_assert (r.supports_type_p (type));
 
+  // FIXME: NANs get special treatment, because we need [NAN, NAN] in
+  // the range to properly represent it, not just the NAN flag in the
+  // property bits.  This will go away when we stream out the
+  // endpoints.
+  if (m_props.get_nan ().yes_p ())
+    {
+      REAL_VALUE_TYPE rv;
+      real_nan (&rv, "", 1, TYPE_MODE (type));
+      r.set (type, rv, rv);
+      return;
+    }
+
   r.set_varying (type);
   r.m_props = m_props;
   r.normalize_kind ();
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index edd10bf5794..8922d563b92 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -291,41 +291,18 @@ frange::set (tree min, tree max, value_range_kind kind)
   m_kind = kind;
   m_type = TREE_TYPE (min);
   m_props.set_varying ();
+  m_min = *TREE_REAL_CST_PTR (min);
+  m_max = *TREE_REAL_CST_PTR (max);
 
-  bool is_min = vrp_val_is_min (min);
-  bool is_max = vrp_val_is_max (max);
   bool is_nan = (real_isnan (TREE_REAL_CST_PTR (min))
 		 || real_isnan (TREE_REAL_CST_PTR (max)));
 
   // Ranges with a NAN and a non-NAN endpoint are nonsensical.
   gcc_checking_assert (!is_nan || operand_equal_p (min, max));
 
-  // The properties for singletons can be all set ahead of time.
-  if (operand_equal_p (min, max))
-    {
-      // Set INF properties.
-      if (is_min)
-	m_props.ninf_set_yes ();
-      else
-	m_props.ninf_set_no ();
-      if (is_max)
-	m_props.inf_set_yes ();
-      else
-	m_props.inf_set_no ();
-      // Set NAN property.
-      if (is_nan)
-	m_props.nan_set_yes ();
-      else
-	m_props.nan_set_no ();
-    }
-  else
-    {
-      // Mark when the endpoints can't be +-INF.
-      if (!is_min)
-	m_props.ninf_set_no ();
-      if (!is_max)
-	m_props.inf_set_no ();
-    }
+  // Set NAN property if we're absolutely sure.
+  if (is_nan && operand_equal_p (min, max))
+    m_props.nan_set_yes ();
 
   // Check for swapped ranges.
   gcc_checking_assert (is_nan || tree_compare (LE_EXPR, min, max));
@@ -336,6 +313,16 @@ frange::set (tree min, tree max, value_range_kind kind)
     verify_range ();
 }
 
+// Setter for frange from REAL_VALUE_TYPE endpoints.
+
+void
+frange::set (tree type,
+	     const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	     value_range_kind kind)
+{
+  set (build_real (type, min), build_real (type, max), kind);
+}
+
 // Normalize range to VARYING or UNDEFINED, or vice versa.  Return
 // TRUE if anything changed.
 //
@@ -347,7 +334,15 @@ frange::set (tree min, tree max, value_range_kind kind)
 bool
 frange::normalize_kind ()
 {
-  if (m_kind == VR_RANGE)
+  // Undefined is viral.
+  if (m_props.nan_undefined_p ())
+    {
+      set_undefined ();
+      return true;
+    }
+  if (m_kind == VR_RANGE
+      && real_isinf (&m_min, 1)
+      && real_isinf (&m_max, 0))
     {
       // No FP properties set means varying.
       if (m_props.varying_p ())
@@ -355,14 +350,6 @@ frange::normalize_kind ()
 	  set_varying (m_type);
 	  return true;
 	}
-      // Undefined is viral.
-      if (m_props.nan_undefined_p ()
-	  || m_props.inf_undefined_p ()
-	  || m_props.ninf_undefined_p ())
-	{
-	  set_undefined ();
-	  return true;
-	}
     }
   else if (m_kind == VR_VARYING)
     {
@@ -370,12 +357,32 @@ frange::normalize_kind ()
       if (!m_props.varying_p ())
 	{
 	  m_kind = VR_RANGE;
+	  real_inf (&m_min, 1);
+	  real_inf (&m_max, 0);
 	  return true;
 	}
     }
   return false;
 }
 
+// If both operands are definitely NAN, do nothing as they combine
+// perfectly.  If OTOH, only one is a NAN, set R to VARYING as they
+// can't be neither unioned nor intersected.  Return TRUE if we
+// changed anything.
+
+static inline bool
+early_nan_resolve (frange &r, const frange &other)
+{
+  gcc_checking_assert (r.get_nan ().yes_p () || other.get_nan ().yes_p ());
+
+  // There's nothing to do for both NANs.
+  if (r.get_nan ().yes_p () == other.get_nan ().yes_p ())
+    return false;
+  // But only one NAN complicates things.
+  r.set_varying (r.type ());
+  return true;
+}
+
 bool
 frange::union_ (const vrange &v)
 {
@@ -388,13 +395,34 @@ frange::union_ (const vrange &v)
       *this = r;
       return true;
     }
+  // ?? We could do better here.  [5,6] U NAN should be [5,6] with the
+  // NAN maybe bits set.  For now, this is conservatively correct.
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
+
+  bool changed = m_props.union_ (r.m_props);
+
+  // Note: for the combination of zeros that differ in sign we keep
+  // the endpoints untouched.  This means that for -0.0 U +0.0, the
+  // result will be -0.0.  Ultimately this doesn't matter, because we
+  // keep singleton zeros ambiguous throughout to avoid propagating
+  // them.  See frange::singleton_p().
 
-  bool ret = m_props.union_ (r.m_props);
-  ret |= normalize_kind ();
+  if (real_less (&r.m_min, &m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&m_max, &r.m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 bool
@@ -414,13 +442,38 @@ frange::intersect (const vrange &v)
       *this = r;
       return true;
     }
+  if (get_nan ().yes_p () || r.get_nan ().yes_p ())
+    return early_nan_resolve (*this, r);
+
+  bool changed = m_props.intersect (r.m_props);
 
-  bool ret = m_props.intersect (r.m_props);
-  ret |= normalize_kind ();
+  // Note: for the combination of zeros that differ in sign we keep
+  // the endpoints untouched.  This means that for -0.0 ^ +0.0, the
+  // result will be -0.0.  Ultimately this doesn't matter, because we
+  // keep singleton zeros ambiguous throughout to avoid propagating
+  // them.  See frange::singleton_p().
+
+  if (real_less (&m_min, &r.m_min))
+    {
+      m_min = r.m_min;
+      changed = true;
+    }
+  if (real_less (&r.m_max, &m_max))
+    {
+      m_max = r.m_max;
+      changed = true;
+    }
+  // If the endpoints are swapped, the ranges are disjoint.
+  if (real_less (&m_max, &m_min))
+    {
+      set_undefined ();
+      return true;
+    }
+  changed |= normalize_kind ();
 
   if (flag_checking)
     verify_range ();
-  return ret;
+  return changed;
 }
 
 frange &
@@ -428,6 +481,8 @@ 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_props = src.m_props;
 
   if (flag_checking)
@@ -446,7 +501,61 @@ frange::operator== (const frange &src) const
       if (varying_p ())
 	return types_compatible_p (m_type, src.m_type);
 
-      return m_props == src.m_props;
+      if (m_props.get_nan ().yes_p ()
+	  || src.m_props.get_nan ().yes_p ())
+	return false;
+
+      return (real_identical (&m_min, &src.m_min)
+	      && real_identical (&m_max, &src.m_max)
+	      && m_props == src.m_props
+	      && types_compatible_p (m_type, src.m_type));
+    }
+  return false;
+}
+
+// Return TRUE if range contains the TREE_REAL_CST_PTR in CST.
+
+bool
+frange::contains_p (tree cst) const
+{
+  if (undefined_p ())
+    return false;
+
+  if (varying_p ())
+    return true;
+
+  gcc_checking_assert (m_kind == VR_RANGE);
+
+  return (real_compare (GE_EXPR, TREE_REAL_CST_PTR (cst), &m_min)
+	  && real_compare (LE_EXPR, TREE_REAL_CST_PTR (cst), &m_max));
+}
+
+// If range is a singleton, place it in RESULT and return TRUE.  If
+// RESULT is NULL, just return TRUE.
+//
+// A NAN can never be a singleton, and neither can a 0.0 if we're
+// honoring signed zeros because we have no way of telling which zero
+// it is.
+
+bool
+frange::singleton_p (tree *result) const
+{
+  if (m_kind == VR_RANGE
+      && real_identical (&m_min, &m_max)
+      && (!HONOR_SIGNED_ZEROS (m_type) || !zero_p ())
+      && (!HONOR_NANS (m_type) || get_nan ().no_p ()))
+    {
+      // If we're honoring signed zeros, fail because we don't know
+      // which zero we have.  This avoids propagating the wrong zero.
+      if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
+	return false;
+
+      if (!get_nan ().no_p ())
+	return false;
+
+      if (result)
+	*result = build_real (m_type, m_min);
+      return true;
     }
   return false;
 }
@@ -465,13 +574,82 @@ frange::verify_range ()
       gcc_checking_assert (m_props.undefined_p ());
       return;
     }
+  gcc_checking_assert (!m_props.undefined_p ());
+
   if (varying_p ())
     {
       gcc_checking_assert (m_props.varying_p ());
       return;
     }
+
+  // We don't support the inverse of an frange (yet).
   gcc_checking_assert (m_kind == VR_RANGE);
-  gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ());
+
+  bool is_nan = real_isnan (&m_min) || real_isnan (&m_max);
+  if (is_nan)
+    {
+      // 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 ());
+    }
+  else
+    // Make sure we don't have swapped ranges.
+    gcc_checking_assert (!real_less (&m_max, &m_min));
+
+  // 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 ())
+    {
+      gcc_checking_assert (real_isnan (&m_min));
+      gcc_checking_assert (real_isnan (&m_max));
+    }
+
+  // If all the properties are clear, we better not span the entire
+  // domain, because that would make us varying.
+  if (m_props.varying_p ())
+    gcc_checking_assert (!real_isinf (&m_min, 1) || !real_isinf (&m_max, 0));
+}
+
+// We can't do much with nonzeros yet.
+void
+frange::set_nonzero (tree type)
+{
+  set_varying (type);
+}
+
+// We can't do much with nonzeros yet.
+bool
+frange::nonzero_p () const
+{
+  return false;
+}
+
+// Set range to [+0.0, +0.0].
+
+void
+frange::set_zero (tree type)
+{
+  tree zero = build_zero_cst (type);
+  set (zero, zero);
+}
+
+// Return TRUE for any [0.0, 0.0] regardless of sign.
+
+bool
+frange::zero_p () const
+{
+  return (m_kind == VR_RANGE
+	  && real_iszero (&m_min)
+	  && real_iszero (&m_max));
+}
+
+void
+frange::set_nonnegative (tree type)
+{
+  tree zero = build_zero_cst (type);
+  tree inf = vrp_val_max (type);
+  set (zero, inf);
 }
 
 // Here we copy between any two irange's.  The ranges can be legacy or
@@ -3304,6 +3482,221 @@ range_tests_nonzero_bits ()
   ASSERT_TRUE (r0.varying_p ());
 }
 
+// Build an frange from string endpoints.
+
+static inline frange
+frange_float (const char *lb, const char *ub, tree type = float_type_node)
+{
+  REAL_VALUE_TYPE min, max;
+  gcc_assert (real_from_string (&min, lb) == 0);
+  gcc_assert (real_from_string (&max, ub) == 0);
+  return frange (type, min, max);
+}
+
+// Build a NAN of type TYPE.
+
+static inline frange
+frange_nan (tree type = float_type_node)
+{
+  REAL_VALUE_TYPE r;
+
+  gcc_assert (real_nan (&r, "", 1, TYPE_MODE (type)));
+  return frange (type, r, r);
+}
+
+
+// Set R to maximum representable value for TYPE.
+
+static inline void
+real_max_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  char buf[128];
+  get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
+		 buf, sizeof (buf), false);
+  int res = real_from_string (r, buf);
+  gcc_checking_assert (!res);
+}
+
+// Set R to minimum representable value for TYPE.
+
+static inline void
+real_min_representable (REAL_VALUE_TYPE *r, tree type)
+{
+  real_max_representable (r, type);
+  *r = real_value_negate (r);
+}
+
+static void
+range_tests_nan ()
+{
+  frange r0, r1;
+
+  // Equal ranges but with differing NAN bits are not equal.
+  r1 = frange_float ("10", "12");
+  r0 = r1;
+  ASSERT_EQ (r0, r1);
+  r0.set_nan (fp_prop::NO);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::YES);
+  ASSERT_NE (r0, r1);
+  r0.set_nan (fp_prop::VARYING);
+  ASSERT_EQ (r0, r1);
+
+  // NAN ranges are not equal to each other.
+  r0 = frange_nan ();
+  r1 = r0;
+  ASSERT_FALSE (r0 == r1);
+  ASSERT_FALSE (r0 == r0);
+  ASSERT_TRUE (r0 != r0);
+
+  // Make sure that combining NAN and INF doesn't give any crazy results.
+  r0 = frange_nan ();
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+  r1 = frange_float ("+Inf", "+Inf");
+  r0.union_ (r1);
+  // [INF, INF] U NAN = VARYING
+  ASSERT_TRUE (r0.varying_p ());
+
+  // [INF, INF] ^ NAN = VARYING
+  r0 = frange_nan ();
+  r1 = frange_float ("+Inf", "+Inf");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.varying_p ());
+
+  // NAN ^ NAN = NAN
+  r0 = frange_nan ();
+  r1 = frange_nan ();
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+
+  // VARYING ^ NAN = NAN.
+  r0 = frange_nan ();
+  r1.set_varying (float_type_node);
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.get_nan ().yes_p ());
+}
+
+static void
+range_tests_signed_zeros ()
+{
+  tree zero = build_zero_cst (float_type_node);
+  tree neg_zero = fold_build1 (NEGATE_EXPR, float_type_node, zero);
+  frange r0, r1;
+
+  // ?? If -0.0 == +0.0, then a range of [-0.0, -0.0] should
+  // contain +0.0 and vice versa.  We probably need a property to
+  // track signed zeros in the frange like we do for NAN, to
+  // properly model all this.
+  r0 = frange (zero, zero);
+  r1 = frange (neg_zero, neg_zero);
+  ASSERT_TRUE (r0.contains_p (zero));
+  ASSERT_TRUE (r0.contains_p (neg_zero));
+  ASSERT_TRUE (r1.contains_p (zero));
+  ASSERT_TRUE (r1.contains_p (neg_zero));
+}
+
+static void
+range_tests_floats ()
+{
+  frange r0, r1;
+
+  range_tests_nan ();
+
+  if (HONOR_SIGNED_ZEROS (float_type_node))
+    range_tests_signed_zeros ();
+
+  // A range of [-INF,+INF] is actually VARYING...
+  r0 = frange_float ("-Inf", "+Inf");
+  ASSERT_TRUE (r0.varying_p ());
+  // ...unless it has some special property...
+  r0.set_nan (fp_prop::NO);
+  ASSERT_FALSE (r0.varying_p ());
+
+  // The endpoints of a VARYING are +-INF.
+  REAL_VALUE_TYPE inf, ninf;
+  real_inf (&inf, 0);
+  real_inf (&ninf, 1);
+  r0.set_varying (float_type_node);
+  ASSERT_TRUE (real_identical (&r0.lower_bound (), &ninf));
+  ASSERT_TRUE (real_identical (&r0.upper_bound (), &inf));
+
+  // The maximum representable range for a type is still a subset of VARYING.
+  REAL_VALUE_TYPE q, r;
+  real_min_representable (&q, float_type_node);
+  real_max_representable (&r, float_type_node);
+  r0 = frange (float_type_node, q, r);
+  // r0 is not a varying, because it does not include -INF/+INF.
+  ASSERT_FALSE (r0.varying_p ());
+  // The upper bound of r0 must be less than +INF.
+  ASSERT_TRUE (real_less (&r0.upper_bound (), &inf));
+  // The lower bound of r0 must be greater than -INF.
+  ASSERT_TRUE (real_less (&ninf, &r0.lower_bound ()));
+
+  // For most architectures, where float and double are different
+  // sizes, having the same endpoints does not necessarily mean the
+  // ranges are equal.
+  if (!types_compatible_p (float_type_node, double_type_node))
+    {
+      r0 = frange_float ("3.0", "3.0", float_type_node);
+      r1 = frange_float ("3.0", "3.0", double_type_node);
+      ASSERT_NE (r0, r1);
+    }
+
+  // [3,5] U [10,12] = [3,12].
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("10", "12");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "12"));
+
+  // [5,10] U [4,8] = [4,10]
+  r0 = frange_float ("5", "10");
+  r1 = frange_float ("4", "8");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "10"));
+
+  // [3,5] U [4,10] = [3,10]
+  r0 = frange_float ("3", "5");
+  r1 = frange_float ("4", "10");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("3", "10"));
+
+  // [4,10] U [5,11] = [4,11]
+  r0 = frange_float ("4", "10");
+  r1 = frange_float ("5", "11");
+  r0.union_ (r1);
+  ASSERT_EQ (r0, frange_float ("4", "11"));
+
+  // [3,12] ^ [10,12] = [10,12].
+  r0 = frange_float ("3", "12");
+  r1 = frange_float ("10", "12");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "12"));
+
+  // [10,12] ^ [11,11] = [11,11]
+  r0 = frange_float ("10", "12");
+  r1 = frange_float ("11", "11");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("11", "11"));
+
+  // [10,20] ^ [5,15] = [10,15]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("5",  "15");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("10", "15"));
+
+  // [10,20] ^ [15,25] = [15,20]
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("15", "25");
+  r0.intersect (r1);
+  ASSERT_EQ (r0, frange_float ("15", "20"));
+
+  // [10,20] ^ [21,25] = []
+  r0 = frange_float ("10", "20");
+  r1 = frange_float ("21", "25");
+  r0.intersect (r1);
+  ASSERT_TRUE (r0.undefined_p ());
+}
+
 void
 range_tests ()
 {
@@ -3312,6 +3705,7 @@ range_tests ()
   range_tests_int_range_max ();
   range_tests_strict_enum ();
   range_tests_nonzero_bits ();
+  range_tests_floats ();
   range_tests_misc ();
 }
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index f0075d0fb1a..1d93634a6c6 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -314,14 +314,10 @@ public:
   bool intersect (const frange_props &other);
   bool operator== (const frange_props &other) const;
   FP_PROP_ACCESSOR(nan)
-  FP_PROP_ACCESSOR(inf)
-  FP_PROP_ACCESSOR(ninf)
 private:
   union {
     struct {
       unsigned char nan : 2;
-      unsigned char inf : 2;
-      unsigned char ninf : 2;
     } bits;
     unsigned char bytes;
   } u;
@@ -345,34 +341,62 @@ class frange : public vrange
 public:
   frange ();
   frange (const frange &);
+  frange (tree, tree, value_range_kind = VR_RANGE);
+  frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+	  value_range_kind = VR_RANGE);
   static bool supports_p (const_tree type)
   {
     return SCALAR_FLOAT_TYPE_P (type);
   }
   virtual tree type () const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
+	    value_range_kind = VR_RANGE);
   virtual void set_varying (tree type) override;
   virtual void set_undefined () override;
   virtual bool union_ (const vrange &) override;
   virtual bool intersect (const vrange &) override;
+  virtual bool contains_p (tree) const override;
+  virtual bool singleton_p (tree *result = NULL) const override;
   virtual bool supports_type_p (const_tree type) const override;
   virtual void accept (const vrange_visitor &v) const override;
+  virtual bool zero_p () const;
+  virtual bool nonzero_p () const;
+  virtual void set_nonzero (tree type);
+  virtual void set_zero (tree type);
+  virtual void set_nonnegative (tree type);
   frange& operator= (const frange &);
   bool operator== (const frange &) const;
   bool operator!= (const frange &r) const { return !(*this == r); }
+  const REAL_VALUE_TYPE &lower_bound () const;
+  const REAL_VALUE_TYPE &upper_bound () const;
 
   // Each fp_prop can be accessed with get_PROP() and set_PROP().
   FRANGE_PROP_ACCESSOR(nan)
-  FRANGE_PROP_ACCESSOR(inf)
-  FRANGE_PROP_ACCESSOR(ninf)
 private:
   void verify_range ();
   bool normalize_kind ();
 
   frange_props m_props;
   tree m_type;
+  REAL_VALUE_TYPE m_min;
+  REAL_VALUE_TYPE m_max;
 };
 
+inline const REAL_VALUE_TYPE &
+frange::lower_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound () const
+{
+  gcc_checking_assert (!undefined_p ());
+  return m_max;
+}
+
 // is_a<> and as_a<> implementation for vrange.
 
 // Anything we haven't specialized is a hard fail.
@@ -1051,8 +1075,8 @@ vrp_val_min (const_tree type)
   if (frange::supports_p (type))
     {
       REAL_VALUE_TYPE real, real_ninf;
-      real_inf (&real);
-      real_ninf = real_value_negate (&real);
+      real_inf (&real, 0);
+      real_inf (&real_ninf, 1);
       return build_real (const_cast <tree> (type), real_ninf);
     }
   return NULL_TREE;
@@ -1096,6 +1120,26 @@ frange::frange (const frange &src)
   *this = src;
 }
 
+// frange constructor from REAL_VALUE_TYPE endpoints.
+
+inline
+frange::frange (tree type,
+		const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+		value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (type, min, max, kind);
+}
+
+// frange constructor from trees.
+
+inline
+frange::frange (tree min, tree max, value_range_kind kind)
+{
+  m_discriminator = VR_FRANGE;
+  set (min, max, kind);
+}
+
 inline tree
 frange::type () const
 {
@@ -1107,6 +1151,8 @@ frange::set_varying (tree type)
 {
   m_kind = VR_VARYING;
   m_type = type;
+  real_inf (&m_min, 1);
+  real_inf (&m_max, 0);
   m_props.set_varying ();
 }
 
@@ -1116,6 +1162,8 @@ frange::set_undefined ()
   m_kind = VR_UNDEFINED;
   m_type = NULL;
   m_props.set_undefined ();
+  memset (&m_min, 0, sizeof (m_min));
+  memset (&m_max, 0, sizeof (m_max));
 }
 
 #endif // GCC_VALUE_RANGE_H
-- 
2.37.1


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 13:45         ` Aldy Hernandez
@ 2022-08-29 13:54           ` Jakub Jelinek
  2022-08-29 14:07             ` Toon Moene
                               ` (3 more replies)
  0 siblings, 4 replies; 25+ messages in thread
From: Jakub Jelinek @ 2022-08-29 13:54 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
> For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> the implementation cleaner, but I'm not wed to the idea if someone
> objects.

If singleton_p() is used to decide whether one can just replace a variable
with singleton range with a constant, then certainly.
If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
NaNs have lots of different representations (the sign bit is ignored
except for stuff like copysign/signbit, there are qNaNs and sNaNs and
except for the single case how Inf is represented, all other values of the
mantissa mean different representations of NaN).  So, unless we track which
exact form of NaN can appear, NaN or any [x, x] range with NaN property
set can't be a singleton.  There could be programs that propagate something
important in NaN mantissa and would be upset if frange kills that.
Of course, one needs to take into account that when a FPU creates NaN, it
will create the canonical qNaN.

	Jakub


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 13:54           ` Jakub Jelinek
@ 2022-08-29 14:07             ` Toon Moene
  2022-08-29 14:15               ` Aldy Hernandez
  2022-08-29 14:08             ` Aldy Hernandez
                               ` (2 subsequent siblings)
  3 siblings, 1 reply; 25+ messages in thread
From: Toon Moene @ 2022-08-29 14:07 UTC (permalink / raw)
  To: gcc-patches

On 8/29/22 15:54, Jakub Jelinek via Gcc-patches wrote:

> On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:

>> For convenience, singleton_p() returns false for a NAN.  IMO, it makes
>> the implementation cleaner, but I'm not wed to the idea if someone
>> objects.
> 
> If singleton_p() is used to decide whether one can just replace a variable
> with singleton range with a constant, then certainly.
> If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> NaNs have lots of different representations (the sign bit is ignored
> except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> except for the single case how Inf is represented, all other values of the
> mantissa mean different representations of NaN).  So, unless we track which
> exact form of NaN can appear, NaN or any [x, x] range with NaN property
> set can't be a singleton.  There could be programs that propagate something
> important in NaN mantissa and would be upset if frange kills that.
> Of course, one needs to take into account that when a FPU creates NaN, it
> will create the canonical qNaN.
> 
> 	Jakub
> 

But the NaNs are irrelevant with -ffinite-math-only, as are the various 
Infs (I do not know offhand which MODE_ that is) ...

Kind regards,

-- 
Toon Moene - e-mail: toon@moene.org - phone: +31 346 214290
Saturnushof 14, 3738 XG  Maartensdijk, The Netherlands


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 13:54           ` Jakub Jelinek
  2022-08-29 14:07             ` Toon Moene
@ 2022-08-29 14:08             ` Aldy Hernandez
  2022-08-29 14:17               ` Jakub Jelinek
  2022-08-30 22:32             ` Joseph Myers
  2022-08-31 15:16             ` Jeff Law
  3 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 14:08 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 3:55 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
> > For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> > the implementation cleaner, but I'm not wed to the idea if someone
> > objects.
>
> If singleton_p() is used to decide whether one can just replace a variable
> with singleton range with a constant, then certainly.
> If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> NaNs have lots of different representations (the sign bit is ignored
> except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> except for the single case how Inf is represented, all other values of the
> mantissa mean different representations of NaN).  So, unless we track which
> exact form of NaN can appear, NaN or any [x, x] range with NaN property

Ok that was more or less what I was thinking.  And no, we don't keep
track of the type of NANs.

How does this look?

bool
frange::singleton_p (tree *result) const
{
  if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
    {
      // If we're honoring signed zeros, fail because we don't know
      // which zero we have.  This avoids propagating the wrong zero.
      if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
    return false;

      // Return false for any singleton that may be a NAN.
      if (!get_nan ().no_p ())
    return false;

      if (result)
    *result = build_real (m_type, m_min);
      return true;
    }
  return false;
}

Thanks.
Aldy

> set can't be a singleton.  There could be programs that propagate something
> important in NaN mantissa and would be upset if frange kills that.
> Of course, one needs to take into account that when a FPU creates NaN, it
> will create the canonical qNaN.
>
>         Jakub
>


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:07             ` Toon Moene
@ 2022-08-29 14:15               ` Aldy Hernandez
  2022-08-29 14:30                 ` Toon Moene
  2022-08-31 15:19                 ` Jeff Law
  0 siblings, 2 replies; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 14:15 UTC (permalink / raw)
  To: Toon Moene; +Cc: gcc-patches

On Mon, Aug 29, 2022 at 4:08 PM Toon Moene <toon@moene.org> wrote:
>
> On 8/29/22 15:54, Jakub Jelinek via Gcc-patches wrote:
>
> > On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
>
> >> For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> >> the implementation cleaner, but I'm not wed to the idea if someone
> >> objects.
> >
> > If singleton_p() is used to decide whether one can just replace a variable
> > with singleton range with a constant, then certainly.
> > If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> > NaNs have lots of different representations (the sign bit is ignored
> > except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> > except for the single case how Inf is represented, all other values of the
> > mantissa mean different representations of NaN).  So, unless we track which
> > exact form of NaN can appear, NaN or any [x, x] range with NaN property
> > set can't be a singleton.  There could be programs that propagate something
> > important in NaN mantissa and would be upset if frange kills that.
> > Of course, one needs to take into account that when a FPU creates NaN, it
> > will create the canonical qNaN.
> >
> >       Jakub
> >
>
> But the NaNs are irrelevant with -ffinite-math-only, as are the various
> Infs (I do not know offhand which MODE_ that is) ...

But even with -ffinite-math-only, is there any benefit to propagating
a known NAN?  For example:

if (__builtin_isnan (x)) {
  use(x);
}

or perhaps:
if (x != x) {
  use(x);
}

Should we propagate NAN into the use of x?

For that matter, AFAICT we don't even generate the unordered
comparisons needed to distinguish a NAN for -ffinite-math-only.

void foo(float f)
{
  if (__builtin_isnan (f))
    bark();
}

$ cat a.c.*original
;; Function foo (null)
;; enabled by -tree-original

{
  if (0)
    {
      bark ();
    }
}

Cheers.
Aldy


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:08             ` Aldy Hernandez
@ 2022-08-29 14:17               ` Jakub Jelinek
  2022-08-29 14:20                 ` Aldy Hernandez
  0 siblings, 1 reply; 25+ messages in thread
From: Jakub Jelinek @ 2022-08-29 14:17 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 04:08:58PM +0200, Aldy Hernandez wrote:
> On Mon, Aug 29, 2022 at 3:55 PM Jakub Jelinek <jakub@redhat.com> wrote:
> >
> > On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
> > > For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> > > the implementation cleaner, but I'm not wed to the idea if someone
> > > objects.
> >
> > If singleton_p() is used to decide whether one can just replace a variable
> > with singleton range with a constant, then certainly.
> > If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> > NaNs have lots of different representations (the sign bit is ignored
> > except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> > except for the single case how Inf is represented, all other values of the
> > mantissa mean different representations of NaN).  So, unless we track which
> > exact form of NaN can appear, NaN or any [x, x] range with NaN property
> 
> Ok that was more or less what I was thinking.  And no, we don't keep
> track of the type of NANs.
> 
> How does this look?
> 
> bool
> frange::singleton_p (tree *result) const
> {
>   if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
>     {
>       // If we're honoring signed zeros, fail because we don't know
>       // which zero we have.  This avoids propagating the wrong zero.
>       if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
>     return false;
> 
>       // Return false for any singleton that may be a NAN.
>       if (!get_nan ().no_p ())
>     return false;

Perhaps if (HONOR_NANS (m_type) && !get_nan ().no_p ()) instead?
Or do you ensure the nan property is never set for -ffinite-math-only?
> 
>       if (result)
>     *result = build_real (m_type, m_min);
>       return true;
>     }
>   return false;
> }

Otherwise LGTM.

	Jakub


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:17               ` Jakub Jelinek
@ 2022-08-29 14:20                 ` Aldy Hernandez
  2022-08-29 14:27                   ` Jakub Jelinek
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 14:20 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 4:17 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Aug 29, 2022 at 04:08:58PM +0200, Aldy Hernandez wrote:
> > On Mon, Aug 29, 2022 at 3:55 PM Jakub Jelinek <jakub@redhat.com> wrote:
> > >
> > > On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
> > > > For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> > > > the implementation cleaner, but I'm not wed to the idea if someone
> > > > objects.
> > >
> > > If singleton_p() is used to decide whether one can just replace a variable
> > > with singleton range with a constant, then certainly.
> > > If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> > > NaNs have lots of different representations (the sign bit is ignored
> > > except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> > > except for the single case how Inf is represented, all other values of the
> > > mantissa mean different representations of NaN).  So, unless we track which
> > > exact form of NaN can appear, NaN or any [x, x] range with NaN property
> >
> > Ok that was more or less what I was thinking.  And no, we don't keep
> > track of the type of NANs.
> >
> > How does this look?
> >
> > bool
> > frange::singleton_p (tree *result) const
> > {
> >   if (m_kind == VR_RANGE && real_identical (&m_min, &m_max))
> >     {
> >       // If we're honoring signed zeros, fail because we don't know
> >       // which zero we have.  This avoids propagating the wrong zero.
> >       if (HONOR_SIGNED_ZEROS (m_type) && zero_p ())
> >     return false;
> >
> >       // Return false for any singleton that may be a NAN.
> >       if (!get_nan ().no_p ())
> >     return false;
>
> Perhaps if (HONOR_NANS (m_type) && !get_nan ().no_p ()) instead?
> Or do you ensure the nan property is never set for -ffinite-math-only?

See followup with Tom downthread.

Sure, I can add the HONOR_NANS, but can we even "see" a NAN in the IL
for -ffinite-math-only?  I suppose it's cleaner with HONOR_NANS....

Aldy


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:20                 ` Aldy Hernandez
@ 2022-08-29 14:27                   ` Jakub Jelinek
  2022-08-29 14:30                     ` Aldy Hernandez
  0 siblings, 1 reply; 25+ messages in thread
From: Jakub Jelinek @ 2022-08-29 14:27 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 04:20:16PM +0200, Aldy Hernandez wrote:
> Sure, I can add the HONOR_NANS, but can we even "see" a NAN in the IL
> for -ffinite-math-only?

Sure, you can, e.g. __builtin_nan{,s}{,f,l} etc. would do it.
It would be UB to use it at runtime in -ffinite-math-only code though.
Another question is, when making a range VARYING, do you set the NAN
property or not when !HONOR_NANS && MODE_HAS_NANS?

>  I suppose it's cleaner with HONOR_NANS....

	Jakub


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:15               ` Aldy Hernandez
@ 2022-08-29 14:30                 ` Toon Moene
  2022-08-29 14:36                   ` Aldy Hernandez
  2022-08-31 15:19                 ` Jeff Law
  1 sibling, 1 reply; 25+ messages in thread
From: Toon Moene @ 2022-08-29 14:30 UTC (permalink / raw)
  To: Aldy Hernandez; +Cc: gcc-patches

On 8/29/22 16:15, Aldy Hernandez wrote:

> But even with -ffinite-math-only, is there any benefit to propagating
> a known NAN?  For example:

The original intent (in 2002) for the option -ffinite-math-only was for 
the optimizers to ignore all the various exceptions to common 
optimizations because they might not work correctly when presented with 
a NaN or an Inf.

I do not know what the effect for floating point range information would 
be - offhand.

But in the *spirit* of this option would be to ignore that the range 
[5.0, 5.0] would "also" contain NaN, for instance.

Kind regards,

-- 
Toon Moene - e-mail: toon@moene.org - phone: +31 346 214290
Saturnushof 14, 3738 XG  Maartensdijk, The Netherlands


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:27                   ` Jakub Jelinek
@ 2022-08-29 14:30                     ` Aldy Hernandez
  2022-08-31 15:24                       ` Jeff Law
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 14:30 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: GCC patches, Andrew MacLeod, Andrew Pinski

On Mon, Aug 29, 2022 at 4:27 PM Jakub Jelinek <jakub@redhat.com> wrote:
>
> On Mon, Aug 29, 2022 at 04:20:16PM +0200, Aldy Hernandez wrote:
> > Sure, I can add the HONOR_NANS, but can we even "see" a NAN in the IL
> > for -ffinite-math-only?
>
> Sure, you can, e.g. __builtin_nan{,s}{,f,l} etc. would do it.
> It would be UB to use it at runtime in -ffinite-math-only code though.
> Another question is, when making a range VARYING, do you set the NAN
> property or not when !HONOR_NANS && MODE_HAS_NANS?

A range of VARYING sets the NAN property to unknown
(fp_prop::VARYING).  If you prefer we can set the property to
fp_prop::NO for !HONOR_NANS && MODE_HAS_NANS.

??

Aldy


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:30                 ` Toon Moene
@ 2022-08-29 14:36                   ` Aldy Hernandez
  2022-08-29 14:42                     ` Toon Moene
  0 siblings, 1 reply; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-29 14:36 UTC (permalink / raw)
  To: Toon Moene, Jakub Jelinek; +Cc: gcc-patches

On Mon, Aug 29, 2022 at 4:30 PM Toon Moene <toon@moene.org> wrote:
>
> On 8/29/22 16:15, Aldy Hernandez wrote:
>
> > But even with -ffinite-math-only, is there any benefit to propagating
> > a known NAN?  For example:
>
> The original intent (in 2002) for the option -ffinite-math-only was for
> the optimizers to ignore all the various exceptions to common
> optimizations because they might not work correctly when presented with
> a NaN or an Inf.
>
> I do not know what the effect for floating point range information would
> be - offhand.
>
> But in the *spirit* of this option would be to ignore that the range
> [5.0, 5.0] would "also" contain NaN, for instance.

Hmm, this is somewhat similar to what Jakub suggested.  Perhaps we
could categorically set !NAN for !HONOR_NANS at frange construction
time?

For reference:
bool
HONOR_NANS (machine_mode m)
{
  return MODE_HAS_NANS (m) && !flag_finite_math_only;
}

Thanks.
Aldy


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:36                   ` Aldy Hernandez
@ 2022-08-29 14:42                     ` Toon Moene
  2022-08-30  9:27                       ` Aldy Hernandez
  2022-08-31 15:22                       ` Jeff Law
  0 siblings, 2 replies; 25+ messages in thread
From: Toon Moene @ 2022-08-29 14:42 UTC (permalink / raw)
  To: Aldy Hernandez, Jakub Jelinek; +Cc: gcc-patches

On 8/29/22 16:36, Aldy Hernandez wrote:

> On Mon, Aug 29, 2022 at 4:30 PM Toon Moene <toon@moene.org> wrote:
>>
>> On 8/29/22 16:15, Aldy Hernandez wrote:
>>
>>> But even with -ffinite-math-only, is there any benefit to propagating
>>> a known NAN?  For example:
>>
>> The original intent (in 2002) for the option -ffinite-math-only was for
>> the optimizers to ignore all the various exceptions to common
>> optimizations because they might not work correctly when presented with
>> a NaN or an Inf.
>>
>> I do not know what the effect for floating point range information would
>> be - offhand.
>>
>> But in the *spirit* of this option would be to ignore that the range
>> [5.0, 5.0] would "also" contain NaN, for instance.
> 
> Hmm, this is somewhat similar to what Jakub suggested.  Perhaps we
> could categorically set !NAN for !HONOR_NANS at frange construction
> time?
> 
> For reference:
> bool
> HONOR_NANS (machine_mode m)
> {
>    return MODE_HAS_NANS (m) && !flag_finite_math_only;
> }
> 
> Thanks.
> Aldy
> 

Yep, I think that would do it.

Thanks,

-- 
Toon Moene - e-mail: toon@moene.org - phone: +31 346 214290
Saturnushof 14, 3738 XG  Maartensdijk, The Netherlands


^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:42                     ` Toon Moene
@ 2022-08-30  9:27                       ` Aldy Hernandez
  2022-08-31 15:22                       ` Jeff Law
  1 sibling, 0 replies; 25+ messages in thread
From: Aldy Hernandez @ 2022-08-30  9:27 UTC (permalink / raw)
  To: Toon Moene; +Cc: Jakub Jelinek, gcc-patches, MacLeod, Andrew

[-- Attachment #1: Type: text/plain, Size: 1779 bytes --]

OK, I'm good to go.

As the patch was getting rather large, I have split it into two parts.
The first is the core endpoints support to frange along with removal
of the +-INF markers (since they are no longer needed).  The second
part is the FP relational operators.  Splitting it up should help in
reviewing/improving the code for readers.  Also, it always helps
regression hunting to have smaller pieces ;-).

Retested on x86-64 Linux.

Pushed.




On Mon, Aug 29, 2022 at 4:42 PM Toon Moene <toon@moene.org> wrote:
>
> On 8/29/22 16:36, Aldy Hernandez wrote:
>
> > On Mon, Aug 29, 2022 at 4:30 PM Toon Moene <toon@moene.org> wrote:
> >>
> >> On 8/29/22 16:15, Aldy Hernandez wrote:
> >>
> >>> But even with -ffinite-math-only, is there any benefit to propagating
> >>> a known NAN?  For example:
> >>
> >> The original intent (in 2002) for the option -ffinite-math-only was for
> >> the optimizers to ignore all the various exceptions to common
> >> optimizations because they might not work correctly when presented with
> >> a NaN or an Inf.
> >>
> >> I do not know what the effect for floating point range information would
> >> be - offhand.
> >>
> >> But in the *spirit* of this option would be to ignore that the range
> >> [5.0, 5.0] would "also" contain NaN, for instance.
> >
> > Hmm, this is somewhat similar to what Jakub suggested.  Perhaps we
> > could categorically set !NAN for !HONOR_NANS at frange construction
> > time?
> >
> > For reference:
> > bool
> > HONOR_NANS (machine_mode m)
> > {
> >    return MODE_HAS_NANS (m) && !flag_finite_math_only;
> > }
> >
> > Thanks.
> > Aldy
> >
>
> Yep, I think that would do it.
>
> Thanks,
>
> --
> Toon Moene - e-mail: toon@moene.org - phone: +31 346 214290
> Saturnushof 14, 3738 XG  Maartensdijk, The Netherlands
>

[-- Attachment #2: 0002-Add-support-for-floating-point-endpoints-to-frange.patch --]
[-- Type: application/x-patch, Size: 31817 bytes --]

[-- Attachment #3: 0003-Implement-relational-operators-for-frange-with-endpo.patch --]
[-- Type: application/x-patch, Size: 18049 bytes --]

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 13:54           ` Jakub Jelinek
  2022-08-29 14:07             ` Toon Moene
  2022-08-29 14:08             ` Aldy Hernandez
@ 2022-08-30 22:32             ` Joseph Myers
  2022-08-31 15:16             ` Jeff Law
  3 siblings, 0 replies; 25+ messages in thread
From: Joseph Myers @ 2022-08-30 22:32 UTC (permalink / raw)
  To: Jakub Jelinek; +Cc: Aldy Hernandez, GCC patches

On Mon, 29 Aug 2022, Jakub Jelinek via Gcc-patches wrote:

> On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
> > For convenience, singleton_p() returns false for a NAN.  IMO, it makes
> > the implementation cleaner, but I'm not wed to the idea if someone
> > objects.
> 
> If singleton_p() is used to decide whether one can just replace a variable
> with singleton range with a constant, then certainly.
> If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and

Note also for decimal floating point that many real numbers - if they 
don't require the full precision of the type to represent the number - can 
be represented with different quantum exponents (so compare equal but 
can't be used to replace each other).

-- 
Joseph S. Myers
joseph@codesourcery.com

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 13:54           ` Jakub Jelinek
                               ` (2 preceding siblings ...)
  2022-08-30 22:32             ` Joseph Myers
@ 2022-08-31 15:16             ` Jeff Law
  3 siblings, 0 replies; 25+ messages in thread
From: Jeff Law @ 2022-08-31 15:16 UTC (permalink / raw)
  To: gcc-patches



On 8/29/2022 7:54 AM, Jakub Jelinek via Gcc-patches wrote:
> On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
>> For convenience, singleton_p() returns false for a NAN.  IMO, it makes
>> the implementation cleaner, but I'm not wed to the idea if someone
>> objects.
> If singleton_p() is used to decide whether one can just replace a variable
> with singleton range with a constant, then certainly.
> If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
> NaNs have lots of different representations (the sign bit is ignored
> except for stuff like copysign/signbit, there are qNaNs and sNaNs and
> except for the single case how Inf is represented, all other values of the
> mantissa mean different representations of NaN).  So, unless we track which
> exact form of NaN can appear, NaN or any [x, x] range with NaN property
> set can't be a singleton.  There could be programs that propagate something
> important in NaN mantissa and would be upset if frange kills that.
> Of course, one needs to take into account that when a FPU creates NaN, it
> will create the canonical qNaN.
I've always thought of singleton_p as having that purpose -- but in the 
integer world we never really had to think about multiple 
representations of the same value.  So it's entirely possible we're 
using singleton_p for multiple purposes.

Clearly if the representation has multiple representations for the same 
value, then we have to be more careful with propagation.  So we may need 
to separate the concept of "this has a value we can propagate" from 
"this has a constant value, but the value may have multiple 
represenatations".

I don't think it's worth trying to track the various NaN 
representations, but I do think it's worth tracking +-Inf and +-0.0.

jeff

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:15               ` Aldy Hernandez
  2022-08-29 14:30                 ` Toon Moene
@ 2022-08-31 15:19                 ` Jeff Law
  1 sibling, 0 replies; 25+ messages in thread
From: Jeff Law @ 2022-08-31 15:19 UTC (permalink / raw)
  To: gcc-patches



On 8/29/2022 8:15 AM, Aldy Hernandez via Gcc-patches wrote:
> On Mon, Aug 29, 2022 at 4:08 PM Toon Moene <toon@moene.org> wrote:
>> On 8/29/22 15:54, Jakub Jelinek via Gcc-patches wrote:
>>
>>> On Mon, Aug 29, 2022 at 03:45:33PM +0200, Aldy Hernandez wrote:
>>>> For convenience, singleton_p() returns false for a NAN.  IMO, it makes
>>>> the implementation cleaner, but I'm not wed to the idea if someone
>>>> objects.
>>> If singleton_p() is used to decide whether one can just replace a variable
>>> with singleton range with a constant, then certainly.
>>> If MODE_HAS_SIGNED_ZEROS, zero has 2 representations (-0.0 and 0.0) and
>>> NaNs have lots of different representations (the sign bit is ignored
>>> except for stuff like copysign/signbit, there are qNaNs and sNaNs and
>>> except for the single case how Inf is represented, all other values of the
>>> mantissa mean different representations of NaN).  So, unless we track which
>>> exact form of NaN can appear, NaN or any [x, x] range with NaN property
>>> set can't be a singleton.  There could be programs that propagate something
>>> important in NaN mantissa and would be upset if frange kills that.
>>> Of course, one needs to take into account that when a FPU creates NaN, it
>>> will create the canonical qNaN.
>>>
>>>        Jakub
>>>
>> But the NaNs are irrelevant with -ffinite-math-only, as are the various
>> Infs (I do not know offhand which MODE_ that is) ...
> But even with -ffinite-math-only, is there any benefit to propagating
> a known NAN?  For example:
In theory, yes, but I don't know if it's worth the headache in practice 
for NaNs, particularly given they can have many representations.

Jeff

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:42                     ` Toon Moene
  2022-08-30  9:27                       ` Aldy Hernandez
@ 2022-08-31 15:22                       ` Jeff Law
  1 sibling, 0 replies; 25+ messages in thread
From: Jeff Law @ 2022-08-31 15:22 UTC (permalink / raw)
  To: gcc-patches



On 8/29/2022 8:42 AM, Toon Moene wrote:
> On 8/29/22 16:36, Aldy Hernandez wrote:
>
>> On Mon, Aug 29, 2022 at 4:30 PM Toon Moene <toon@moene.org> wrote:
>>>
>>> On 8/29/22 16:15, Aldy Hernandez wrote:
>>>
>>>> But even with -ffinite-math-only, is there any benefit to propagating
>>>> a known NAN?  For example:
>>>
>>> The original intent (in 2002) for the option -ffinite-math-only was for
>>> the optimizers to ignore all the various exceptions to common
>>> optimizations because they might not work correctly when presented with
>>> a NaN or an Inf.
>>>
>>> I do not know what the effect for floating point range information 
>>> would
>>> be - offhand.
>>>
>>> But in the *spirit* of this option would be to ignore that the range
>>> [5.0, 5.0] would "also" contain NaN, for instance.
>>
>> Hmm, this is somewhat similar to what Jakub suggested.  Perhaps we
>> could categorically set !NAN for !HONOR_NANS at frange construction
>> time?
>>
>> For reference:
>> bool
>> HONOR_NANS (machine_mode m)
>> {
>>    return MODE_HAS_NANS (m) && !flag_finite_math_only;
>> }
>>
>> Thanks.
>> Aldy
>>
>
> Yep, I think that would do it.
Agreed.
Jeff

^ permalink raw reply	[flat|nested] 25+ messages in thread

* Re: [PATCH] Add support for floating point endpoints to frange.
  2022-08-29 14:30                     ` Aldy Hernandez
@ 2022-08-31 15:24                       ` Jeff Law
  0 siblings, 0 replies; 25+ messages in thread
From: Jeff Law @ 2022-08-31 15:24 UTC (permalink / raw)
  To: gcc-patches



On 8/29/2022 8:30 AM, Aldy Hernandez via Gcc-patches wrote:
> On Mon, Aug 29, 2022 at 4:27 PM Jakub Jelinek <jakub@redhat.com> wrote:
>> On Mon, Aug 29, 2022 at 04:20:16PM +0200, Aldy Hernandez wrote:
>>> Sure, I can add the HONOR_NANS, but can we even "see" a NAN in the IL
>>> for -ffinite-math-only?
>> Sure, you can, e.g. __builtin_nan{,s}{,f,l} etc. would do it.
>> It would be UB to use it at runtime in -ffinite-math-only code though.
>> Another question is, when making a range VARYING, do you set the NAN
>> property or not when !HONOR_NANS && MODE_HAS_NANS?
> A range of VARYING sets the NAN property to unknown
> (fp_prop::VARYING).  If you prefer we can set the property to
> fp_prop::NO for !HONOR_NANS && MODE_HAS_NANS.

If the format doesn't have NaNs or the user explicitly disables them, 
then the state should be NO, otherwise YES.

Jeff


^ permalink raw reply	[flat|nested] 25+ messages in thread

end of thread, other threads:[~2022-08-31 15:24 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-08-23 11:42 [PATCH] Add support for floating point endpoints to frange Aldy Hernandez
2022-08-26 15:55 ` Aldy Hernandez
2022-08-26 17:40   ` Andrew Pinski
2022-08-26 18:11     ` Aldy Hernandez
2022-08-26 18:11       ` Aldy Hernandez
2022-08-26 19:16     ` Aldy Hernandez
2022-08-26 19:44       ` Andrew Pinski
2022-08-29 13:45         ` Aldy Hernandez
2022-08-29 13:54           ` Jakub Jelinek
2022-08-29 14:07             ` Toon Moene
2022-08-29 14:15               ` Aldy Hernandez
2022-08-29 14:30                 ` Toon Moene
2022-08-29 14:36                   ` Aldy Hernandez
2022-08-29 14:42                     ` Toon Moene
2022-08-30  9:27                       ` Aldy Hernandez
2022-08-31 15:22                       ` Jeff Law
2022-08-31 15:19                 ` Jeff Law
2022-08-29 14:08             ` Aldy Hernandez
2022-08-29 14:17               ` Jakub Jelinek
2022-08-29 14:20                 ` Aldy Hernandez
2022-08-29 14:27                   ` Jakub Jelinek
2022-08-29 14:30                     ` Aldy Hernandez
2022-08-31 15:24                       ` Jeff Law
2022-08-30 22:32             ` Joseph Myers
2022-08-31 15:16             ` Jeff Law

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).