aboutsummaryrefslogtreecommitdiff
path: root/gcc/value-range.h
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2022-08-30 08:23:33 +0200
committerAldy Hernandez <aldyh@redhat.com>2022-08-30 11:26:24 +0200
commit8bb1df032cc080b751e00c0d7d86c31a630105f9 (patch)
tree109c86b64c96335dbdc87a78fa15028ba97eccd6 /gcc/value-range.h
parentdf8fe4adb0721ab0e4486bc58482b501fe06287d (diff)
downloadgcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.zip
gcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.tar.gz
gcc-8bb1df032cc080b751e00c0d7d86c31a630105f9.tar.bz2
Add support for floating point endpoints to frange.
The current implementation of frange is just a type with some bits to represent NAN and INF. We can do better and represent endpoints to ultimately solve longstanding PRs such as PR24021. This patch adds these endpoints. In follow-up patches I will add support for a bare bones PLUS_EXPR range-op-float entry to solve the PR. I have chosen to use REAL_VALUE_TYPEs for the endpoints, since that's what we use underneath the trees. This will be somewhat analogous to our eventual use of wide-ints in the irange. No sense going through added levels of indirection if we can avoid it. That, plus real.* already has a nice API for dealing with floats. With this patch, ranges will be closed float point intervals, which make the implementation simpler, since we don't have to keep track of open/closed intervals. This is conservative enough for use in the ranger world, as we'd rather err on the side of more elements in a range, than less. For example, even though we cannot precisely represent the open interval (3.0, 5.0) with this approach, it is perfectably reasonable to represent it as [3.0, 5.0] since the closed interval is a super set of the open one. In the VRP/ranger world, it is always better to err on the side of more information in a range, than not. After all, when we don't know anything about a range, we just use VARYING which is a fancy term for a range spanning the entire domain. Since REAL_VALUE_TYPEs have properly defined infinity and NAN semantics, all the math can be made to work: [-INF, 3.0] !NAN => Numbers <= 3.0 (NAN cannot happen) [3.0, 3.0] => 3.0 or NAN. [3.0, +INF] => Numbers >= 3.0 (NAN is possible) [-INF, +INF] => VARYING (NAN is possible) [-INF, +INF] !NAN => Entire domain. NAN cannot happen. Also, since REAL_VALUE_TYPEs can represent the minimum and maximum representable values of a TYPE_MODE, we can disambiguate between them and negative and positive infinity (see get_max_float in real.cc). This also makes the math all work. For example, suppose we know nothing about x and y (VARYING). On the TRUE side of x > y, we can deduce that: (a) x cannot be NAN (b) y cannot be NAN (c) y cannot be +INF. (c) means that we can drop the upper bound of "y" from +INF to the maximum representable value for its type. Having endpoints with different representation for infinity and the maximum representable values, means we can drop the +-INF properties we currently have in the frange. gcc/ChangeLog: * range-op-float.cc (frange_set_nan): New. (frange_drop_inf): New. (frange_drop_ninf): New. (foperator_equal::op1_range): Adjust for endpoints. (foperator_lt::op1_range): Same. (foperator_lt::op2_range): Same. (foperator_gt::op1_range): Same. (foperator_gt::op2_range): Same. (foperator_unordered::op1_range): Same. * value-query.cc (range_query::get_tree_range): Same. * value-range-pretty-print.cc (vrange_printer::visit): Same. * value-range-storage.cc (frange_storage_slot::get_frange): Same. * value-range.cc (frange::set): Same. (frange::normalize_kind): Same. (frange::union_): Same. (frange::intersect): Same. (frange::operator=): Same. (early_nan_resolve): New. (frange::contains_p): New. (frange::singleton_p): New. (frange::set_nonzero): New. (frange::nonzero_p): New. (frange::set_zero): New. (frange::zero_p): New. (frange::set_nonnegative): New. (frange_float): New. (frange_nan): New. (range_tests_nan): New. (range_tests_signed_zeros): New. (range_tests_floats): New. (range_tests): New. * value-range.h (frange::lower_bound): New. (frange::upper_bound): New. (vrp_val_min): Use real_inf with a sign instead of negating inf. (frange::frange): New. (frange::set_varying): Adjust for endpoints. (real_max_representable): New. (real_min_representable): New.
Diffstat (limited to 'gcc/value-range.h')
-rw-r--r--gcc/value-range.h88
1 files changed, 78 insertions, 10 deletions
diff --git a/gcc/value-range.h b/gcc/value-range.h
index f0075d0..43b231b 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -314,14 +314,10 @@ public:
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;
@@ -345,34 +341,62 @@ class frange : public vrange
public:
frange ();
frange (const frange &);
+ frange (tree, tree, value_range_kind = VR_RANGE);
+ frange (tree type, const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+ value_range_kind = VR_RANGE);
static bool supports_p (const_tree type)
{
return SCALAR_FLOAT_TYPE_P (type);
}
virtual tree type () const override;
virtual void set (tree, tree, value_range_kind = VR_RANGE) override;
+ void set (tree type, const REAL_VALUE_TYPE &, const REAL_VALUE_TYPE &,
+ value_range_kind = VR_RANGE);
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 contains_p (tree) const override;
+ virtual bool singleton_p (tree *result = NULL) const override;
virtual bool supports_type_p (const_tree type) const override;
virtual void accept (const vrange_visitor &v) const override;
+ virtual bool zero_p () const;
+ virtual bool nonzero_p () const;
+ virtual void set_nonzero (tree type);
+ virtual void set_zero (tree type);
+ virtual void set_nonnegative (tree type);
frange& operator= (const frange &);
bool operator== (const frange &) const;
bool operator!= (const frange &r) const { return !(*this == r); }
+ const REAL_VALUE_TYPE &lower_bound () const;
+ const REAL_VALUE_TYPE &upper_bound () const;
// 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 ();
bool normalize_kind ();
frange_props m_props;
tree m_type;
+ REAL_VALUE_TYPE m_min;
+ REAL_VALUE_TYPE m_max;
};
+inline const REAL_VALUE_TYPE &
+frange::lower_bound () const
+{
+ gcc_checking_assert (!undefined_p ());
+ return m_min;
+}
+
+inline const REAL_VALUE_TYPE &
+frange::upper_bound () const
+{
+ gcc_checking_assert (!undefined_p ());
+ return m_max;
+}
+
// is_a<> and as_a<> implementation for vrange.
// Anything we haven't specialized is a hard fail.
@@ -1050,10 +1074,9 @@ vrp_val_min (const_tree type)
return build_zero_cst (const_cast<tree> (type));
if (frange::supports_p (type))
{
- REAL_VALUE_TYPE real, real_ninf;
- real_inf (&real);
- real_ninf = real_value_negate (&real);
- return build_real (const_cast <tree> (type), real_ninf);
+ REAL_VALUE_TYPE ninf;
+ real_inf (&ninf, 1);
+ return build_real (const_cast <tree> (type), ninf);
}
return NULL_TREE;
}
@@ -1096,6 +1119,26 @@ frange::frange (const frange &src)
*this = src;
}
+// frange constructor from REAL_VALUE_TYPE endpoints.
+
+inline
+frange::frange (tree type,
+ const REAL_VALUE_TYPE &min, const REAL_VALUE_TYPE &max,
+ value_range_kind kind)
+{
+ m_discriminator = VR_FRANGE;
+ set (type, min, max, kind);
+}
+
+// frange constructor from trees.
+
+inline
+frange::frange (tree min, tree max, value_range_kind kind)
+{
+ m_discriminator = VR_FRANGE;
+ set (min, max, kind);
+}
+
inline tree
frange::type () const
{
@@ -1107,6 +1150,8 @@ frange::set_varying (tree type)
{
m_kind = VR_VARYING;
m_type = type;
+ real_inf (&m_min, 1);
+ real_inf (&m_max, 0);
m_props.set_varying ();
}
@@ -1116,6 +1161,29 @@ frange::set_undefined ()
m_kind = VR_UNDEFINED;
m_type = NULL;
m_props.set_undefined ();
+ memset (&m_min, 0, sizeof (m_min));
+ memset (&m_max, 0, sizeof (m_max));
+}
+
+// Set R to maximum representable value for TYPE.
+
+inline void
+real_max_representable (REAL_VALUE_TYPE *r, tree type)
+{
+ char buf[128];
+ get_max_float (REAL_MODE_FORMAT (TYPE_MODE (type)),
+ buf, sizeof (buf), false);
+ int res = real_from_string (r, buf);
+ gcc_checking_assert (!res);
+}
+
+// Set R to minimum representable value for TYPE.
+
+inline void
+real_min_representable (REAL_VALUE_TYPE *r, tree type)
+{
+ real_max_representable (r, type);
+ *r = real_value_negate (r);
}
#endif // GCC_VALUE_RANGE_H