public inbox for gcc-patches@gcc.gnu.org
 help / color / mirror / Atom feed
From: Aldy Hernandez <aldyh@redhat.com>
To: GCC patches <gcc-patches@gcc.gnu.org>
Cc: Andrew MacLeod <amacleod@redhat.com>,
	Jakub Jelinek <jakub@redhat.com>,
	 Andrew Pinski <pinskia@gmail.com>
Subject: Re: [PATCH] Add support for floating point endpoints to frange.
Date: Fri, 26 Aug 2022 17:55:36 +0200	[thread overview]
Message-ID: <CAGm3qMUsXLbCGnxfKW8_oh9+xf_7FN4TnOVa8rM8-vptMMiwJQ@mail.gmail.com> (raw)
In-Reply-To: <20220823114224.904934-1-aldyh@redhat.com>

[-- 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


  reply	other threads:[~2022-08-26 15:55 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-08-23 11:42 Aldy Hernandez
2022-08-26 15:55 ` Aldy Hernandez [this message]
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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=CAGm3qMUsXLbCGnxfKW8_oh9+xf_7FN4TnOVa8rM8-vptMMiwJQ@mail.gmail.com \
    --to=aldyh@redhat.com \
    --cc=amacleod@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=pinskia@gmail.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).