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>,
	Jeff Law <jeffreyalaw@gmail.com>,
	Richard Biener <richard.guenther@gmail.com>,
	Jakub Jelinek <jakub@redhat.com>,
	roger@nextmovesoftware.com, Aldy Hernandez <aldyh@redhat.com>
Subject: [RFA] Implement basic range operators to enable floating point VRP.
Date: Mon, 25 Jul 2022 20:49:51 +0200	[thread overview]
Message-ID: <20220725184951.2202379-1-aldyh@redhat.com> (raw)

Without further ado, here is the implementation for floating point
range operators, plus the switch to enable all ranger clients to
handle floats.

These are bare bone implementations good enough for relation operators
to work, while keeping the NAN bits up to date in the frange.  There
is also minimal support for keeping track of +-INF when it is obvious.

I have included some basic tests to help get a feel of what is
ultimately handled.

Since range-ops is the domain specific core of ranger, I think its
best if a global maintainer or an FP expert could review this.

OK for trunk?

Tested on x86-64 Linux.

p.s. I haven't done extensive testing in DOM, but with this we're mighty
close for the forward threader there to be replaceable with the backward
threader, thus removing the last use of the forward threader.

gcc/ChangeLog:

	* range-op-float.cc (finite_operands_p): New.
	(frelop_early_resolve): New.
	(default_frelop_fold_range): New.
	(class foperator_equal): New.
	(class foperator_not_equal): New.
	(class foperator_lt): New.
	(class foperator_le): New.
	(class foperator_gt): New.
	(class foperator_ge): New.
	(class foperator_unordered): New.
	(class foperator_ordered): New.
	(class foperator_relop_unknown): New.
	(floating_op_table::floating_op_table): Add above classes to
	floating op table.
	* value-range.h (frange::supports_p): Enable.

gcc/testsuite/ChangeLog:

	* g++.dg/opt/pr94589-2.C: Add notes.
	* gcc.dg/tree-ssa/vrp-float-1.c: New test.
	* gcc.dg/tree-ssa/vrp-float-11.c: New test.
	* gcc.dg/tree-ssa/vrp-float-3.c: New test.
	* gcc.dg/tree-ssa/vrp-float-4.c: New test.
	* gcc.dg/tree-ssa/vrp-float-6.c: New test.
	* gcc.dg/tree-ssa/vrp-float-7.c: New test.
	* gcc.dg/tree-ssa/vrp-float-8.c: New test.
---
 gcc/range-op-float.cc                        | 564 +++++++++++++++++++
 gcc/testsuite/g++.dg/opt/pr94589-2.C         |  25 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c  |  19 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c |  26 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c  |  18 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c  |  16 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c  |  20 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c  |  14 +
 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c  |  26 +
 gcc/value-range.h                            |   3 +-
 10 files changed, 729 insertions(+), 2 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 8e9d83e3827..d94ff6f915a 100644
--- a/gcc/range-op-float.cc
+++ b/gcc/range-op-float.cc
@@ -150,6 +150,50 @@ range_operator_float::op1_op2_relation (const irange &lhs ATTRIBUTE_UNUSED) cons
   return VREL_VARYING;
 }
 
+// Return TRUE if OP1 and OP2 are known to be free of NANs.
+
+static inline bool
+finite_operands_p (const frange &op1, const frange &op2)
+{
+  return (flag_finite_math_only
+	  || (op1.get_nan ().no_p ()
+	      && op2.get_nan ().no_p ()));
+}
+
+// Floating version of relop_early_resolve that takes into account NAN
+// and -ffinite-math-only.
+
+inline bool
+frelop_early_resolve (irange &r, tree type,
+		      const frange &op1, const frange &op2,
+		      relation_kind rel, relation_kind my_rel)
+{
+  // If either operand is undefined, return VARYING.
+  if (empty_range_varying (r, type, op1, op2))
+    return true;
+
+  // We can fold relations from the oracle when we know both operands
+  // are free of NANs, or when -ffinite-math-only.
+  return (finite_operands_p (op1, op2)
+	  && relop_early_resolve (r, type, op1, op2, rel, my_rel));
+}
+
+// Default implementation of fold_range for relational operators.
+// This amounts to passing on any known relations from the oracle, iff
+// we know the operands are not NAN or -ffinite-math-only holds.
+
+static inline bool
+default_frelop_fold_range (irange &r, tree type,
+			  const frange &op1, const frange &op2,
+			  relation_kind rel, relation_kind my_rel)
+{
+  if (frelop_early_resolve (r, type, op1, op2, rel, my_rel))
+    return true;
+
+  r.set_varying (type);
+  return true;
+}
+
 class foperator_identity : public range_operator_float
 {
   using range_operator_float::fold_range;
@@ -172,6 +216,509 @@ class foperator_identity : public range_operator_float
 public:
 } fop_identity;
 
+class foperator_equal : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return equal_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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);
+  }
+} fop_equal;
+
+bool
+foperator_equal::op1_range (frange &r, tree type,
+			    const irange &lhs,
+			    const frange &op2 ATTRIBUTE_UNUSED,
+			    relation_kind rel) 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.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      // The FALSE side of op1 ORDERED op1 implies op1 is a NAN.
+      if (rel == VREL_EQ)
+	r.set_nan (fp_prop::YES);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+class foperator_not_equal : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return not_equal_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  relation_kind rel) const final override;
+} fop_not_equal;
+
+bool
+foperator_not_equal::op1_range (frange &r, tree type,
+				const irange &lhs,
+				const frange &op2 ATTRIBUTE_UNUSED,
+				relation_kind) const
+{
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_TRUE:
+      r.set_varying (type);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      // The FALSE side of op1 != op2 implies op1 is !NAN.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+class foperator_lt : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return lt_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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;
+} fop_lt;
+
+bool
+foperator_lt::op1_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op2 ATTRIBUTE_UNUSED,
+			 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 and !INF.
+      r.set_nan (fp_prop::NO);
+      r.set_inf (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+bool
+foperator_lt::op2_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op1 ATTRIBUTE_UNUSED,
+			 relation_kind) const
+{
+  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.
+      r.set_nan (fp_prop::NO);
+      r.set_ninf (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+class foperator_le : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return le_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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);
+  }
+} fop_le;
+
+bool
+foperator_le::op1_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op2 ATTRIBUTE_UNUSED,
+			 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.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+class foperator_gt : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return gt_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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;
+} fop_gt;
+
+bool
+foperator_gt::op1_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op2 ATTRIBUTE_UNUSED,
+			 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 and !NINF.
+      r.set_nan (fp_prop::NO);
+      r.set_ninf (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+bool
+foperator_gt::op2_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op1 ATTRIBUTE_UNUSED,
+			 relation_kind) const
+{
+  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.
+      r.set_nan (fp_prop::NO);
+      r.set_inf (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+class foperator_ge : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+  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 op1_op2_relation (const irange &lhs) const final override
+  {
+    return ge_op1_op2_relation (lhs);
+  }
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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);
+  }
+} fop_ge;
+
+bool
+foperator_ge::op1_range (frange &r,
+			 tree type,
+			 const irange &lhs,
+			 const frange &op2 ATTRIBUTE_UNUSED,
+			 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.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+// UNORDERED_EXPR comparison.
+
+class foperator_unordered : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+public:
+  bool fold_range (irange &r, tree type,
+		   const frange &op1, const frange &op2,
+		   relation_kind rel) const final override;
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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);
+  }
+} fop_unordered;
+
+bool
+foperator_unordered::fold_range (irange &r, tree type,
+				 const frange &op1, const frange &op2,
+				 relation_kind) const
+{
+  // UNORDERED is TRUE if either operand is a NAN.
+  if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_true (type);
+  // UNORDERED is FALSE if neither operand is a NAN.
+  else if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
+bool
+foperator_unordered::op1_range (frange &r, tree type,
+				const irange &lhs,
+				const frange &op2 ATTRIBUTE_UNUSED,
+				relation_kind) const
+{
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_TRUE:
+      r.set_varying (type);
+      // Since at least one operand must be NAN, if one of them is
+      // not, the other must be.
+      if (op2.get_nan ().no_p ())
+	r.set_nan (fp_prop::YES);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      // A false UNORDERED means both operands are !NAN.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+// ORDERED_EXPR comparison.
+
+class foperator_ordered : public range_operator_float
+{
+  using range_operator_float::fold_range;
+  using range_operator_float::op1_range;
+  using range_operator_float::op2_range;
+
+public:
+  bool fold_range (irange &r, tree type,
+		   const frange &op1, const frange &op2,
+		   relation_kind rel) const final override;
+  bool op1_range (frange &r, tree type,
+		  const irange &lhs, const frange &op2,
+		  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);
+  }
+} fop_ordered;
+
+bool
+foperator_ordered::fold_range (irange &r, tree type,
+			       const frange &op1, const frange &op2,
+			       relation_kind) const
+{
+  // ORDERED is TRUE if neither operand is a NAN.
+  if (op1.get_nan ().no_p () && op2.get_nan ().no_p ())
+    r = range_true (type);
+  // ORDERED is FALSE if either operand is a NAN.
+  else if (op1.get_nan ().yes_p () || op2.get_nan ().yes_p ())
+    r = range_false (type);
+  else
+    r = range_true_and_false (type);
+  return true;
+}
+
+bool
+foperator_ordered::op1_range (frange &r, tree type,
+			      const irange &lhs,
+			      const frange &op2 ATTRIBUTE_UNUSED,
+			      relation_kind rel) const
+{
+  switch (get_bool_state (r, lhs, type))
+    {
+    case BRS_TRUE:
+      r.set_varying (type);
+      // The TRUE side of op1 UNORDERED op2 implies op1 is !NAN.
+      r.set_nan (fp_prop::NO);
+      break;
+
+    case BRS_FALSE:
+      r.set_varying (type);
+      // The FALSE side of op1 UNORDERED op1 implies op1 is !NAN.
+      if (rel == VREL_EQ)
+	r.set_nan (fp_prop::NO);
+      break;
+
+    default:
+      break;
+    }
+  return true;
+}
+
+// Placeholder for unimplemented relational operators.
+
+class foperator_relop_unknown : public range_operator_float
+{
+  using range_operator_float::fold_range;
+
+public:
+  bool fold_range (irange &r, tree type,
+		   const frange &, const frange &,
+		   relation_kind) const final override
+  {
+    r.set_varying (type);
+    return true;
+  }
+} fop_relop_unknown;
+
 
 // Instantiate a range_op_table for floating point operations.
 static floating_op_table global_floating_table;
@@ -185,6 +732,23 @@ floating_op_table::floating_op_table ()
   set (PAREN_EXPR, fop_identity);
   set (OBJ_TYPE_REF, fop_identity);
   set (REAL_CST, fop_identity);
+
+  // All the relational operators are expected to work, because the
+  // calculation of ranges on outgoing edges expect the handlers to be
+  // present.
+  set (EQ_EXPR, fop_equal);
+  set (NE_EXPR, fop_not_equal);
+  set (LT_EXPR, fop_lt);
+  set (LE_EXPR, fop_le);
+  set (GT_EXPR, fop_gt);
+  set (GE_EXPR, fop_ge);
+  set (UNLE_EXPR, fop_relop_unknown);
+  set (UNLT_EXPR, fop_relop_unknown);
+  set (UNGE_EXPR, fop_relop_unknown);
+  set (UNGT_EXPR, fop_relop_unknown);
+  set (UNEQ_EXPR, fop_relop_unknown);
+  set (ORDERED_EXPR, fop_ordered);
+  set (UNORDERED_EXPR, fop_unordered);
 }
 
 // Return a pointer to the range_operator_float instance, if there is
diff --git a/gcc/testsuite/g++.dg/opt/pr94589-2.C b/gcc/testsuite/g++.dg/opt/pr94589-2.C
index e9ef84b1912..1caa725061e 100644
--- a/gcc/testsuite/g++.dg/opt/pr94589-2.C
+++ b/gcc/testsuite/g++.dg/opt/pr94589-2.C
@@ -4,6 +4,31 @@
 // { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" } }
 // { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) 5\\.0" 12 "optimized" } }
 
+/* This is failing on f5() because the spaceship operator is no longer
+   folded.  What happens is that evrp folds away the final condition
+   here as always true.
+
+  <bb 2> :
+  if (i_2(D) != j_4(D))
+    goto <bb 3>; [INV]
+  else
+    goto <bb 6>; [INV]
+
+  <bb 3> :
+  if (i_2(D) >= j_4(D))
+    goto <bb 4>; [INV]
+  else
+    goto <bb 6>; [INV]
+
+  <bb 4> :
+  if (i_2(D) > j_4(D))   <<== ALWAYS TRUE
+    goto <bb 6>; [INV]
+  else
+    goto <bb 5>; [INV]
+
+  This causes phiopt to no longer be able to fold the total sequence
+  into i_2 >= j_4.  */
+
 #include <compare>
 
 #define A __attribute__((noipa))
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c
new file mode 100644
index 00000000000..88faf72ac42
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-1.c
@@ -0,0 +1,19 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdisable-tree-ethread -fdisable-tree-fre1 -fdump-tree-evrp" }
+
+void bar ();
+void george ();
+
+float
+foo (float x, float y)
+{
+  if (x == x)
+    {
+      if (x > y)
+        bar();
+      if (x == x)
+        george();
+    }
+}
+
+// { dg-final { scan-tree-dump-times "Folding predicate x_*to 1" "evrp" 1 } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c
new file mode 100644
index 00000000000..2f4dc8757a3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-11.c
@@ -0,0 +1,26 @@
+// { dg-do compile }
+// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" }
+
+extern void link_error ();
+
+void fast_sqrt (float);
+
+float test (float x)
+{
+  float y = x*x;
+  if (y >= 0.f)
+    { 
+      if (__builtin_isnan (y))
+	link_error ();
+      else
+	fast_sqrt (y);
+
+      if (!__builtin_isnan (y))
+	fast_sqrt (y);
+      else
+	link_error ();
+    }
+}
+
+// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } }
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c
new file mode 100644
index 00000000000..c659abb6cc0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-3.c
@@ -0,0 +1,18 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" }
+
+void link_error ();
+
+void
+foo (double x, double y)
+{
+  if (x == y)
+    {
+      if (__builtin_isnan (x))
+        link_error ();
+      if (__builtin_isnan (y))
+        link_error ();
+    }
+}
+
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c
new file mode 100644
index 00000000000..86436742e0a
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-4.c
@@ -0,0 +1,16 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" }
+
+void link_error ();
+
+void
+foo (double x, double y)
+{
+  if (x > y)
+    {
+      if (__builtin_isnan (x) || __builtin_isnan (y))
+        link_error ();
+    }
+}
+
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c
new file mode 100644
index 00000000000..145d1861804
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-6.c
@@ -0,0 +1,20 @@
+// { dg-do compile }
+// { dg-options "-O2 -fdisable-tree-ethread -fdump-tree-evrp" }
+
+void bar ();
+
+void
+foo (double x, double y)
+{
+      if (x > y)
+	;
+      else if (!__builtin_isnan (x) && !__builtin_isnan (y))
+	{
+	  // If x and y are not NAN, the x <= y relationship holds, and the
+	  // following conditional can be folded away.
+	  if (x <= y)
+	    bar ();
+	}
+}
+
+// { dg-final { scan-tree-dump-times "Folding predicate x_.* <= y_.* to 1" 1 "evrp" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c
new file mode 100644
index 00000000000..92af87091a8
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-7.c
@@ -0,0 +1,14 @@
+// { dg-do compile }
+// { dg-options "-O2 -fno-tree-forwprop -fno-tree-ccp -fno-tree-fre -fdump-tree-evrp" }
+
+extern void link_error ();
+
+void
+foo ()
+{
+  float z = 0.0;
+  if (__builtin_isnan (z))
+    link_error ();
+}
+
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c
new file mode 100644
index 00000000000..9170150d453
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-float-8.c
@@ -0,0 +1,26 @@
+// { dg-do compile }
+// { dg-options "-O2 -fno-thread-jumps -fdump-tree-evrp" }
+
+extern void link_error ();
+
+void fast_sqrt (float);
+
+float test (float x)
+{
+    float y = x*x;
+    if (y >= 0.f)
+      { 
+        if (__builtin_isnan (y))
+         link_error ();
+        else
+          fast_sqrt (y);
+
+       if (!__builtin_isnan (y))
+         fast_sqrt (y);
+       else
+         link_error ();
+      }
+}
+
+// { dg-final { scan-tree-dump-times "fast_sqrt" 2 "evrp" } }
+// { dg-final { scan-tree-dump-not "link_error" "evrp" } }
diff --git a/gcc/value-range.h b/gcc/value-range.h
index e43fbe30f27..478f165e02b 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -338,8 +338,7 @@ public:
   frange (const frange &);
   static bool supports_p (tree type)
   {
-    // Disabled until floating point range-ops come live.
-    return 0 && SCALAR_FLOAT_TYPE_P (type);
+    return SCALAR_FLOAT_TYPE_P (type);
   }
   virtual tree type () const override;
   virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
-- 
2.36.1


             reply	other threads:[~2022-07-25 18:50 UTC|newest]

Thread overview: 4+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-07-25 18:49 Aldy Hernandez [this message]
2022-07-25 18:53 ` Aldy Hernandez
2022-07-31 18:11 ` Aldy Hernandez
2022-08-02 12:57   ` Aldy Hernandez

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=20220725184951.2202379-1-aldyh@redhat.com \
    --to=aldyh@redhat.com \
    --cc=amacleod@redhat.com \
    --cc=gcc-patches@gcc.gnu.org \
    --cc=jakub@redhat.com \
    --cc=jeffreyalaw@gmail.com \
    --cc=richard.guenther@gmail.com \
    --cc=roger@nextmovesoftware.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).