[gcc r13-1933] Implement basic range operators to enable floating point VRP.

Aldy Hernandez aldyh@gcc.gnu.org
Tue Aug 2 12:57:08 GMT 2022


https://gcc.gnu.org/g:24012539ae3410174b3e755b580a16de826d56a6

commit r13-1933-g24012539ae3410174b3e755b580a16de826d56a6
Author: Aldy Hernandez <aldyh@redhat.com>
Date:   Mon Jul 25 16:47:48 2022 +0200

    Implement basic range operators to enable floating point VRP.
    
    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.
    
    Tested on x86-64 Linux.
    
    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: XFAIL.
            * 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.

Diff:
---
 gcc/range-op-float.cc                        | 564 +++++++++++++++++++++++++++
 gcc/testsuite/g++.dg/opt/pr94589-2.C         |   2 +-
 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, 705 insertions(+), 3 deletions(-)

diff --git a/gcc/range-op-float.cc b/gcc/range-op-float.cc
index 8e9d83e3827..4fbd96a7479 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 == 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 ORDERED 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 !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..370deea9218 100644
--- a/gcc/testsuite/g++.dg/opt/pr94589-2.C
+++ b/gcc/testsuite/g++.dg/opt/pr94589-2.C
@@ -1,7 +1,7 @@
 // PR tree-optimization/94589
 // { dg-do compile { target c++20 } }
 // { dg-options "-O2 -g0 -ffast-math -fdump-tree-optimized" }
-// { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" } }
+// { dg-final { scan-tree-dump-times "\[ij]_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) \[ij]_\[0-9]+\\(D\\)" 12 "optimized" { xfail *-*-* } } }
 // { dg-final { scan-tree-dump-times "i_\[0-9]+\\(D\\) (?:<|<=|==|!=|>|>=) 5\\.0" 12 "optimized" } }
 
 #include <compare>
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 390fcb8fd99..305e2cae7e6 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -338,8 +338,7 @@ public:
   frange (const frange &);
   static bool supports_p (const_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;


More information about the Gcc-cvs mailing list