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;
