diff options
author | Aldy Hernandez <aldyh@redhat.com> | 2022-07-24 19:42:11 +0200 |
---|---|---|
committer | Aldy Hernandez <aldyh@redhat.com> | 2022-07-25 08:45:21 +0200 |
commit | 1a10bd84a5d103f6e1fdcd960f8377e3d099d773 (patch) | |
tree | 718038b157e964efcc5a502cdfcea6f636783a52 /gcc/value-range.cc | |
parent | 75d20d6c84c12bedd65a904e462f02f0b9eb3f77 (diff) | |
download | gcc-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.cc | 195 |
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. |