aboutsummaryrefslogtreecommitdiff
path: root/gcc/value-range.cc
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2022-07-24 19:42:11 +0200
committerAldy Hernandez <aldyh@redhat.com>2022-07-25 08:45:21 +0200
commit1a10bd84a5d103f6e1fdcd960f8377e3d099d773 (patch)
tree718038b157e964efcc5a502cdfcea6f636783a52 /gcc/value-range.cc
parent75d20d6c84c12bedd65a904e462f02f0b9eb3f77 (diff)
downloadgcc-1a10bd84a5d103f6e1fdcd960f8377e3d099d773.zip
gcc-1a10bd84a5d103f6e1fdcd960f8377e3d099d773.tar.gz
gcc-1a10bd84a5d103f6e1fdcd960f8377e3d099d773.tar.bz2
frange class to represent floating point ranges
This implements a basic frange class to represent floating point ranges. Although it is meant to be a base for further development, it is enough to handle relations and propagate NAN and other properties. For ranger clients to become floating point aware, we still need the range-op entries, which I will submit later this week. Since those entries require specialized FP knowledge, I will ask for a review from the FP experts before committing. Once range-op entries come live, all ranger clients that have been converted to the type agnostic vrange API will become FP aware: evrp, DOM, the threaders, loop-ch, etc. (Still missing is loop unswitching, as a lot of the int_range* temporaries should be Value_Range. I don't have enough cycles to convert loop unswitching, but could gladly give guidance. It should be straightforward for those familiar with the code ;-)). Samples things we handle: * We can set the FP properties (!NAN, !INF, etc) at assignment from constants (and propagate them throughout the CFG): float z = 0.0; if (__builtin_isnan (z)) link_error (); * The relation oracle works in tandem with the FP ranges: 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 (); } * We know the true side of all ordered conditionals (except !=) implies !NAN: if (x > y) { if (__builtin_isnan (x) || __builtin_isnan (y)) link_error (); } Range-ops also works correctly with -ffinite-math-only, and avoids checking for NANs, etc. I believe this is enough to get a fully fleshed out floating point support for evrp and friends, but doing so is beyond my limited FP knowledge. For example, frange could be enhanced to track constant endpoints, and we could track other FP properties aside from NAN. Further discussion is gladly welcome. Tested on x86-64 Linux. gcc/ChangeLog: * value-range-pretty-print.cc (vrange_printer::visit): New. (vrange_printer::print_frange_prop): New. * value-range-pretty-print.h (class vrange_printer): Add visit and print_frange_prop. * value-range-storage.h (vrange_allocator::alloc_vrange): Handle frange. (vrange_allocator::alloc_frange): New. * value-range.cc (vrange::operator=): Handle frange. (vrange::operator==): Same. (frange::accept): New. (frange::set): New. (frange::normalize_kind): New. (frange::union_): New. (frange::intersect): New. (frange::operator=): New. (frange::operator==): New. (frange::supports_type_p): New. (frange::verify_range): New. * value-range.h (enum value_range_discriminator): Handle frange. (class fp_prop): New. (FP_PROP_ACCESSOR): New. (class frange_props): New. (FRANGE_PROP_ACCESSOR): New. (class frange): New. (Value_Range::init): Handle frange. (Value_Range::operator=): Same. (Value_Range::supports_type_p): Same. (frange_props::operator==): New. (frange_props::union_): New. (frange_props::intersect): New (frange::frange): New. (frange::type): New. (frange::set_varying): New. (frange::set_undefined): New.
Diffstat (limited to 'gcc/value-range.cc')
-rw-r--r--gcc/value-range.cc195
1 files changed, 191 insertions, 4 deletions
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 525e192..e49b06d 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.