@@ -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, ' ');
+}
@@ -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;
};
@@ -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 ();
}
@@ -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.
@@ -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