diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index b795e92d8fb..485612fe67c 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -109,3 +109,44 @@ vrange_printer::print_irange_bitmasks (const irange &r) const
   print_hex (nz, buf);
   pp_string (pp, buf);
 }
+
+// Print an frange.
+
+void
+vrange_printer::visit (const frange &r) const
+{
+  pp_string (pp, "[frange] ");
+  if (r.undefined_p ())
+    {
+      pp_string (pp, "UNDEFINED");
+      return;
+    }
+  dump_generic_node (pp, r.type (), 0, TDF_NONE, false);
+  pp_string (pp, " ");
+  if (r.varying_p ())
+    {
+      pp_string (pp, "VARYING");
+      return;
+    }
+  print_frange_prop ("NAN", r.get_nan ());
+  print_frange_prop ("INF", r.get_inf ());
+  print_frange_prop ("NINF", r.get_ninf ());
+}
+
+// Print the FP properties in an frange.
+
+void
+vrange_printer::print_frange_prop (const char *str, const fp_prop &prop) const
+{
+  if (prop.varying_p ())
+    return;
+
+  if (prop.yes_p ())
+    pp_string (pp, str);
+  else if (prop.no_p ())
+    {
+      pp_character (pp, '!');
+      pp_string (pp, str);
+    }
+  pp_character (pp, ' ');
+}
diff --git a/gcc/value-range-pretty-print.h b/gcc/value-range-pretty-print.h
index 6d2fb74cc7a..c1c7c4244cc 100644
--- a/gcc/value-range-pretty-print.h
+++ b/gcc/value-range-pretty-print.h
@@ -27,9 +27,11 @@ public:
   vrange_printer (pretty_printer *pp_) : pp (pp_) { }
   void visit (const unsupported_range &) const override;
   void visit (const irange &) const override;
+  void visit (const frange &) const override;
 private:
   void print_irange_bound (tree bound) const;
   void print_irange_bitmasks (const irange &) const;
+  void print_frange_prop (const char *str, const fp_prop &) const;
 
   pretty_printer *pp;
 };
diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h
index 7e005e4db56..5a3336b673b 100644
--- a/gcc/value-range-storage.h
+++ b/gcc/value-range-storage.h
@@ -39,6 +39,7 @@ public:
   template <typename T> T *clone (const T &src);
 private:
   irange *alloc_irange (unsigned pairs);
+  frange *alloc_frange ();
   void operator= (const vrange_allocator &) = delete;
 };
 
@@ -142,7 +143,9 @@ vrange_allocator::alloc_vrange (tree type)
 {
   if (irange::supports_p (type))
     return alloc_irange (2);
-
+  if (frange::supports_p (type))
+    return alloc_frange ();
+  return NULL;
   gcc_unreachable ();
 }
 
@@ -164,6 +167,13 @@ vrange_allocator::alloc_irange (unsigned num_pairs)
   return new (r) irange (mem, num_pairs);
 }
 
+inline frange *
+vrange_allocator::alloc_frange ()
+{
+  void *r = alloc (sizeof (frange));
+  return new (r) frange ();
+}
+
 // Return a clone of an irange.
 
 template <>
@@ -175,6 +185,17 @@ vrange_allocator::clone <irange> (const irange &src)
   return r;
 }
 
+// Return a clone of an frange.
+
+template <>
+inline frange *
+vrange_allocator::clone <frange> (const frange &src)
+{
+  frange *r = alloc_frange ();
+  *r = src;
+  return r;
+}
+
 // Return a clone of a vrange.
 
 template <>
@@ -183,7 +204,9 @@ vrange_allocator::clone <vrange> (const vrange &src)
 {
   if (is_a <irange> (src))
     return clone <irange> (as_a <irange> (src));
-
+  if (is_a <frange> (src))
+    return clone <frange> (as_a <frange> (src));
+  return NULL;
   gcc_unreachable ();
 }
 
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 525e1924057..e49b06d1038 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -195,12 +195,12 @@ vrange &
 vrange::operator= (const vrange &src)
 {
   if (is_a <irange> (src))
-    {
-      as_a <irange> (*this) = as_a <irange> (src);
-      return *this;
-    }
+    as_a <irange> (*this) = as_a <irange> (src);
+  else if (is_a <frange> (src))
+    as_a <frange> (*this) = as_a <frange> (src);
   else
     gcc_unreachable ();
+  return *this;
 }
 
 // Equality operator for generic ranges.
@@ -210,6 +210,8 @@ vrange::operator== (const vrange &src) const
 {
   if (is_a <irange> (src))
     return as_a <irange> (*this) == as_a <irange> (src);
+  if (is_a <frange> (src))
+    return as_a <frange> (*this) == as_a <frange> (src);
   gcc_unreachable ();
 }
 
@@ -252,6 +254,191 @@ unsupported_range::unsupported_range ()
   set_undefined ();
 }
 
+void
+frange::accept (const vrange_visitor &v) const
+{
+  v.visit (*this);
+}
+
+// Setter for franges.  Currently only singletons are supported.
+
+void
+frange::set (tree min, tree max, value_range_kind kind)
+{
+  gcc_checking_assert (kind == VR_RANGE);
+  gcc_checking_assert (operand_equal_p (min, max));
+  gcc_checking_assert (TREE_CODE (min) == REAL_CST);
+
+  m_kind = kind;
+  m_type = TREE_TYPE (min);
+
+  REAL_VALUE_TYPE *const cst = TREE_REAL_CST_PTR (min);
+  if (real_isnan (cst))
+    m_props.nan_set_yes ();
+  else
+    m_props.nan_set_no ();
+
+  if (real_isinf (cst))
+    {
+      if (real_isneg (cst))
+	{
+	  m_props.inf_set_no ();
+	  m_props.ninf_set_yes ();
+	}
+      else
+	{
+	  m_props.inf_set_yes ();
+	  m_props.ninf_set_no ();
+	}
+    }
+  else
+    {
+      m_props.inf_set_no ();
+      m_props.ninf_set_no ();
+    }
+
+  if (flag_checking)
+    verify_range ();
+}
+
+// Normalize range to VARYING or UNDEFINED, or vice versa.
+//
+// A range with no known properties can be dropped to VARYING.
+// Similarly, a VARYING with any properties should be dropped to a
+// VR_RANGE.  Normalizing ranges upon changing them ensures there is
+// only one representation for a given range.
+
+void
+frange::normalize_kind ()
+{
+  if (m_kind == VR_RANGE)
+    {
+      // No FP properties set means varying.
+      if (m_props.nan_varying_p ()
+	  && m_props.inf_varying_p ()
+	  && m_props.ninf_varying_p ())
+	{
+	  set_varying (m_type);
+	  return;
+	}
+      // Undefined is viral.
+      if (m_props.nan_undefined_p ()
+	  || m_props.inf_undefined_p ()
+	  || m_props.ninf_undefined_p ())
+	{
+	  set_undefined ();
+	  return;
+	}
+    }
+  else if (m_kind == VR_VARYING)
+    {
+      // If a VARYING has any FP properties, it's no longer VARYING.
+      if (!m_props.nan_varying_p ()
+	  || !m_props.inf_varying_p ()
+	  || !m_props.ninf_varying_p ())
+	m_kind = VR_RANGE;
+    }
+}
+
+bool
+frange::union_ (const vrange &v)
+{
+  const frange &r = as_a <frange> (v);
+
+  if (r.undefined_p () || varying_p ())
+    return false;
+  if (undefined_p () || r.varying_p ())
+    {
+      *this = r;
+      return true;
+    }
+
+  bool ret = m_props.union_ (r.m_props);
+  normalize_kind ();
+
+  if (flag_checking)
+    verify_range ();
+  return ret;
+}
+
+bool
+frange::intersect (const vrange &v)
+{
+  const frange &r = as_a <frange> (v);
+
+  if (undefined_p () || r.varying_p ())
+    return false;
+  if (r.undefined_p ())
+    {
+      set_undefined ();
+      return true;
+    }
+  if (varying_p ())
+    {
+      *this = r;
+      return true;
+    }
+
+  bool ret = m_props.intersect (r.m_props);
+  normalize_kind ();
+
+  if (flag_checking)
+    verify_range ();
+  return ret;
+}
+
+frange &
+frange::operator= (const frange &src)
+{
+  m_kind = src.m_kind;
+  m_type = src.m_type;
+  m_props = src.m_props;
+
+  if (flag_checking)
+    verify_range ();
+  return *this;
+}
+
+bool
+frange::operator== (const frange &src) const
+{
+  if (m_kind == src.m_kind)
+    {
+      if (undefined_p ())
+	return true;
+
+      if (varying_p ())
+	return types_compatible_p (m_type, src.m_type);
+
+      return m_props == src.m_props;
+    }
+  return false;
+}
+
+bool
+frange::supports_type_p (tree type) const
+{
+  return supports_p (type);
+}
+
+void
+frange::verify_range ()
+{
+  if (undefined_p ())
+    {
+      gcc_checking_assert (m_props.undefined_p ());
+      return;
+    }
+  else if (varying_p ())
+    {
+      gcc_checking_assert (m_props.varying_p ());
+      return;
+    }
+
+  gcc_checking_assert (m_kind == VR_RANGE);
+  gcc_checking_assert (!m_props.varying_p () && !m_props.undefined_p ());
+}
+
 // Here we copy between any two irange's.  The ranges can be legacy or
 // multi-ranges, and copying between any combination works correctly.
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 4af92fd65d9..e43fbe30f27 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -45,6 +45,8 @@ enum value_range_discriminator
 {
   // Range holds an integer or pointer.
   VR_IRANGE,
+  // Floating point range.
+  VR_FRANGE,
   // Range holds an unsupported type.
   VR_UNKNOWN
 };
@@ -252,6 +254,117 @@ public:
   virtual void accept (const vrange_visitor &v) const override;
 };
 
+// Floating point property to represent possible values of a NAN, INF, etc.
+
+class fp_prop
+{
+public:
+  enum kind {
+    UNDEFINED	= 0x0,		// Prop is impossible.
+    YES		= 0x1,		// Prop is definitely set.
+    NO		= 0x2,		// Prop is definitely not set.
+    VARYING	= (YES | NO)	// Prop may hold.
+  };
+  fp_prop (kind f) : m_kind (f) { }
+  bool varying_p () const { return m_kind == VARYING; }
+  bool undefined_p () const { return m_kind == UNDEFINED; }
+  bool yes_p () const { return m_kind == YES; }
+  bool no_p () const { return m_kind == NO; }
+private:
+  unsigned char m_kind : 2;
+};
+
+// Accessors for individual FP properties.
+
+#define FP_PROP_ACCESSOR(NAME) \
+  void NAME##_set_varying () { u.bits.NAME = fp_prop::VARYING; }	\
+  void NAME##_set_yes () { u.bits.NAME = fp_prop::YES; }	\
+  void NAME##_set_no () { u.bits.NAME = fp_prop::NO; }	\
+  bool NAME##_varying_p () const { return u.bits.NAME == fp_prop::VARYING; } \
+  bool NAME##_undefined_p () const { return u.bits.NAME == fp_prop::UNDEFINED; } \
+  bool NAME##_yes_p () const { return u.bits.NAME == fp_prop::YES; }	\
+  bool NAME##_no_p () const { return u.bits.NAME == fp_prop::NO; } \
+  fp_prop get_##NAME () const				   \
+  { return fp_prop ((fp_prop::kind) u.bits.NAME); } \
+  void set_##NAME (fp_prop::kind f) { u.bits.NAME = f; }
+
+// Aggregate of all the FP properties in an frange packed into one
+// structure to save space.  Using explicit fp_prop's in the frange,
+// would take one byte per property because of padding.  Instead, we
+// can save all properties into one byte.
+
+class frange_props
+{
+public:
+  frange_props () { set_varying (); }
+  void set_varying () { u.bytes = 0xff; }
+  void set_undefined () { u.bytes = 0; }
+  bool varying_p () { return u.bytes == 0xff; }
+  bool undefined_p () { return u.bytes == 0; }
+  bool union_ (const frange_props &other);
+  bool intersect (const frange_props &other);
+  bool operator== (const frange_props &other) const;
+  FP_PROP_ACCESSOR(nan)
+  FP_PROP_ACCESSOR(inf)
+  FP_PROP_ACCESSOR(ninf)
+private:
+  union {
+    struct {
+      unsigned char nan : 2;
+      unsigned char inf : 2;
+      unsigned char ninf : 2;
+    } bits;
+    unsigned char bytes;
+  } u;
+};
+
+// Accessors for getting/setting all FP properties at once.
+
+#define FRANGE_PROP_ACCESSOR(NAME)				\
+  fp_prop get_##NAME () const { return m_props.get_##NAME (); }	\
+  void set_##NAME (fp_prop::kind f)				\
+  {								\
+    m_props.set_##NAME (f);					\
+    normalize_kind ();						\
+  }
+
+// A floating point range.
+
+class frange : public vrange
+{
+  friend class frange_storage_slot;
+public:
+  frange ();
+  frange (const frange &);
+  static bool supports_p (tree type)
+  {
+    // Disabled until floating point range-ops come live.
+    return 0 && SCALAR_FLOAT_TYPE_P (type);
+  }
+  virtual tree type () const override;
+  virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+  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 supports_type_p (tree type) const override;
+  virtual void accept (const vrange_visitor &v) const override;
+  frange& operator= (const frange &);
+  bool operator== (const frange &) const;
+  bool operator!= (const frange &r) const { return !(*this == r); }
+
+  // Each fp_prop can be accessed with get_PROP() and set_PROP().
+  FRANGE_PROP_ACCESSOR(nan)
+  FRANGE_PROP_ACCESSOR(inf)
+  FRANGE_PROP_ACCESSOR(ninf)
+private:
+  void verify_range ();
+  void normalize_kind ();
+
+  frange_props m_props;
+  tree m_type;
+};
+
 // is_a<> and as_a<> implementation for vrange.
 
 // Anything we haven't specialized is a hard fail.
@@ -297,10 +410,18 @@ is_a <irange> (vrange &v)
   return v.m_discriminator == VR_IRANGE;
 }
 
+template <>
+inline bool
+is_a <frange> (vrange &v)
+{
+  return v.m_discriminator == VR_FRANGE;
+}
+
 class vrange_visitor
 {
 public:
   virtual void visit (const irange &) const { }
+  virtual void visit (const frange &) const { }
   virtual void visit (const unsupported_range &) const { }
 };
 
@@ -360,6 +481,7 @@ private:
   unsupported_range m_unsupported;
   vrange *m_vrange;
   int_range_max m_irange;
+  frange m_frange;
 };
 
 inline
@@ -401,6 +523,8 @@ Value_Range::init (tree type)
 
   if (irange::supports_p (type))
     m_vrange = &m_irange;
+  else if (frange::supports_p (type))
+    m_vrange = &m_frange;
   else
     m_vrange = &m_unsupported;
 }
@@ -426,6 +550,11 @@ Value_Range::operator= (const vrange &r)
       m_irange = as_a <irange> (r);
       m_vrange = &m_irange;
     }
+  else if (is_a <frange> (r))
+    {
+      m_frange = as_a <frange> (r);
+      m_vrange = &m_frange;
+    }
   else
     gcc_unreachable ();
 
@@ -461,7 +590,7 @@ Value_Range::operator const vrange &() const
 inline bool
 Value_Range::supports_type_p (tree type)
 {
-  return irange::supports_p (type);
+  return irange::supports_p (type) || frange::supports_p (type);
 }
 
 // Returns true for an old-school value_range as described above.
@@ -881,6 +1010,69 @@ irange::normalize_kind ()
     }
 }
 
+
+// Supporting methods for frange.
+
+inline bool
+frange_props::operator== (const frange_props &other) const
+{
+  return u.bytes == other.u.bytes;
+}
+
+inline bool
+frange_props::union_ (const frange_props &other)
+{
+  unsigned char saved = u.bytes;
+  u.bytes |= other.u.bytes;
+  return u.bytes != saved;
+}
+
+inline bool
+frange_props::intersect (const frange_props &other)
+{
+  unsigned char saved = u.bytes;
+  u.bytes &= other.u.bytes;
+  return u.bytes != saved;
+}
+
+inline
+frange::frange ()
+{
+  m_discriminator = VR_FRANGE;
+  m_type = nullptr;
+  set_undefined ();
+}
+
+inline
+frange::frange (const frange &src)
+{
+  m_discriminator = VR_FRANGE;
+  *this = src;
+}
+
+inline tree
+frange::type () const
+{
+  return m_type;
+}
+
+inline void
+frange::set_varying (tree type)
+{
+  m_kind = VR_VARYING;
+  m_type = type;
+  m_props.set_varying ();
+}
+
+inline void
+frange::set_undefined ()
+{
+  m_kind = VR_UNDEFINED;
+  m_type = NULL;
+  m_props.set_undefined ();
+}
+
+
 // Return the maximum value for TYPE.
 
 inline tree
