Message ID | 20220530132751.1752112-1-aldyh@redhat.com |
---|---|
State | New |
Headers | show |
Series | [1/5] Implement abstract vrange class. | expand |
Final patch committed. Includes support for class unsupported_range. Re-tested on x86-64 Linux. On Mon, May 30, 2022 at 3:28 PM Aldy Hernandez <aldyh@redhat.com> wrote: > > This is a series of patches making ranger type agnostic in preparation > for contributing support for other types of ranges (pointers and > floats initially). > > The first step in this process is to implement vrange, an abstract > class that will be exclusively used by ranger, and from which all > ranges will inherit. Vrange provides the minimum operations for > ranger to work. The current virtual methods are what we've used to > implement frange (floats) and prange (pointers), but we may restrict > the virtual methods further as other ranges come about > (i.e. set_nonnegative() has no meaning for a future string range). > > This patchset also provides a mechanism for declaring local type > agnostic ranges that can transparently hold an irange, frange, > prange's, etc, and a dispatch mechanism for range-ops to work with > various range types. More details in the relevant patches. > > FUTURE PLAN > =========== > > The plan after this is to contribute a bare bones implementation for > floats (frange) that will provide relationals, followed by a > separation of integers and pointers (irange and prange). Once this is > in place, we can further enhance both floats and pointers. For > example, pointer tracking, pointer plus optimizations, and keeping > track of NaN's, etc. > > Once frange and prange come live, all ranger clients will immediately > benefit from these enhancements. For instance, in our local branch, > the threader is already float aware with regards to relationals. > > We expect to wait a few weeks before starting to contribute further > enhancements to give the tree a time to stabilize, and Andrew time to > rebase his upcoming patches :-P. > > NOTES > ===== > > In discussions with Andrew, it has become clear that with vrange > coming about, supports_type_p() is somewhat ambiguous. Prior to > vrange it has been used to (a) determine if a type is supported by > ranger, (b) as a short-cut for checking if a type is pointer or integer, > as well as (c) to see if a given range can hold a type. These things > have had the same meaning in irange, but are slightly different with > vrange. I will address this in a follow-up patch. > > Speaking of supported types, we now provide an unsupported_range > for passing around ranges for unsupported types. We've been silently > doing this for a while, in both vr-values by creating VARYING for > unsupported types with error_mark_node end points, and in ranger when > we pass an unsupported range before we realize in range_of_expr that > it's unsupported. This class just formalizes what we've already been > doing in an irange, but making it explicit that you can't do anything > with these ranges except pass them. Any other operation traps. > > There is no GTY support for vrange yet, as we don't store it long > term. When we contribute support for global ranges (think > SSA_NAME_RANGE_INFO but for generic ranges), we will include it. There > was just no need to pollute this patchset with it. > > TESTING > ======= > > The patchset has been tested on x86-64 Linux as well as ppc64 Linux. > I have also verified that we fold the same number of conditionals in > evrp as well as thread the same number of paths. There should be no > user visible changes. > > We have also benchmarked the work, with the final numbers being an > *improvement* of 1.92% for evrp, and 0.82% for VRP. Overall > compilation has a miniscule improvement. This is despite the extra > indirection level. > > The improvements are mostly because of small cleanups required for the > generalization of ranges. As a sanity check, I stuck kcachegrind on a > few sample .ii files to see where the time was being gained. Most of > the gain came from gimple_range_global() being 19% faster. This > function is called a lot, and it was constructing a legacy > value_range, then returning it by value, which the caller then had to > convert to an irange. This is in line with other pending work: > anytime we get rid of legacy, we gain time. > > I will wait a few days before committing to welcome any comments. > > gcc/ChangeLog: > > * value-range-equiv.cc (value_range_equiv::set): New. > * value-range-equiv.h (class value_range_equiv): Make set method > virtual. > Remove default bitmap argument from set method. > * value-range.cc (vrange::contains_p): New. > (vrange::singleton_p): New. > (vrange::operator=): New. > (vrange::operator==): New. > (irange::fits_p): Move to .cc file. > (irange::set_nonnegative): New. > (unsupported_range::unsupported_range): New. > (unsupported_range::set): New. > (unsupported_range::type): New. > (unsupported_range::set_undefined): New. > (unsupported_range::set_varying): New. > (unsupported_range::dump): New. > (unsupported_range::union_): New. > (unsupported_range::intersect): New. > (unsupported_range::zero_p): New. > (unsupported_range::nonzero_p): New. > (unsupported_range::set_nonzero): New. > (unsupported_range::set_zero): New. > (unsupported_range::set_nonnegative): New. > (unsupported_range::fits_p): New. > (irange::set): Call irange::set_undefined. > (irange::verify_range): Check discriminator field. > (irange::dump): Dump [irange] marker. > (irange::debug): Move to... > (vrange::debug): ...here. > (dump_value_range): Accept vrange. > (debug): Same. > * value-range.h (enum value_range_discriminator): New. > (class vrange): New. > (class unsupported_range): New. > (struct vrange_traits): New. > (is_a): New. > (as_a): New. > (class irange): Inherit from vrange. > (dump_value_range): Adjust for vrange. > (irange::kind): Rename to... > (vrange::kind): ...this. > (irange::varying_p): Rename to... > (vrange::varying_p): ...this. > (irange::undefined_p): Rename to... > (vrange::undefined_p): ...this. > (irange::irange): Set discriminator. > (irange::union_): Convert to irange before passing to irange > method. > (irange::intersect): Same. > (vrange::supports_type_p): New. > * vr-values.cc (vr_values::extract_range_from_binary_expr): Pass > NULL bitmap argument to value_range_equiv::set. > (vr_values::extract_range_basic): Same. > --- > gcc/value-range-equiv.cc | 6 ++ > gcc/value-range-equiv.h | 3 +- > gcc/value-range.cc | 168 ++++++++++++++++++++++++++++++++- > gcc/value-range.h | 195 ++++++++++++++++++++++++++++++++------- > gcc/vr-values.cc | 6 +- > 5 files changed, 338 insertions(+), 40 deletions(-) > > diff --git a/gcc/value-range-equiv.cc b/gcc/value-range-equiv.cc > index 77c6f5ca99d..b0ae1288a09 100644 > --- a/gcc/value-range-equiv.cc > +++ b/gcc/value-range-equiv.cc > @@ -50,6 +50,12 @@ value_range_equiv::set (tree min, tree max, bitmap equiv, > check (); > } > > +void > +value_range_equiv::set (tree min, tree max, value_range_kind kind) > +{ > + set (min, max, m_equiv, kind); > +} > + > void > value_range_equiv::set (tree val) > { > diff --git a/gcc/value-range-equiv.h b/gcc/value-range-equiv.h > index 0aa1069cb61..743ceb2b227 100644 > --- a/gcc/value-range-equiv.h > +++ b/gcc/value-range-equiv.h > @@ -41,9 +41,10 @@ class GTY((user)) value_range_equiv : public value_range > void move (value_range_equiv *); > > /* Leaves equiv bitmap alone. */ > + virtual void set (tree, tree, value_range_kind = VR_RANGE) override; > void update (tree, tree, value_range_kind = VR_RANGE); > /* Deep-copies equiv bitmap argument. */ > - void set (tree, tree, bitmap = NULL, value_range_kind = VR_RANGE); > + void set (tree, tree, bitmap, value_range_kind = VR_RANGE); > void set (tree); > > bool operator== (const value_range_equiv &) const /* = delete */; > diff --git a/gcc/value-range.cc b/gcc/value-range.cc > index 2e7385aecc2..97ff0614f48 100644 > --- a/gcc/value-range.cc > +++ b/gcc/value-range.cc > @@ -30,6 +30,162 @@ along with GCC; see the file COPYING3. If not see > #include "fold-const.h" > #include "gimple-range.h" > > +// Default implementation when none has been defined. > + > +bool > +vrange::contains_p (tree) const > +{ > + return false; > +} > + > +// Default implementation when none has been defined. > + > +bool > +vrange::singleton_p (tree *) const > +{ > + return false; > +} > + > +// Assignment operator for generic ranges. Copying incompatible types > +// is not allowed. > + > +vrange & > +vrange::operator= (const vrange &src) > +{ > + if (is_a <irange> (src)) > + { > + as_a <irange> (*this) = as_a <irange> (src); > + return *this; > + } > + else > + gcc_unreachable (); > +} > + > +// Equality operator for generic ranges. > + > +bool > +vrange::operator== (const vrange &src) const > +{ > + if (is_a <irange> (src)) > + return as_a <irange> (*this) == as_a <irange> (src); > + gcc_unreachable (); > +} > + > +// Return TRUE if R fits in THIS. > + > +bool > +irange::fits_p (const vrange &r) const > +{ > + return m_max_ranges >= as_a <irange> (r).num_pairs (); > +} > + > +void > +irange::set_nonnegative (tree type) > +{ > + set (build_int_cst (type, 0), TYPE_MAX_VALUE (type)); > +} > + > +unsupported_range::unsupported_range () > +{ > + m_discriminator = VR_UNKNOWN; > + set_undefined (); > +} > + > +void > +unsupported_range::set (tree, tree, value_range_kind) > +{ > + gcc_unreachable (); > +} > + > +tree > +unsupported_range::type () const > +{ > + gcc_unreachable (); > + return nullptr; > +} > + > +void > +unsupported_range::set_undefined () > +{ > + m_kind = VR_UNDEFINED; > +} > + > +void > +unsupported_range::set_varying (tree) > +{ > + gcc_unreachable (); > +} > + > +void > +unsupported_range::dump (FILE *file) const > +{ > + fprintf (file, "[unsupported_range] "); > + if (undefined_p ()) > + { > + fprintf (file, "UNDEFINED"); > + return; > + } > + if (varying_p ()) > + { > + fprintf (file, "VARYING"); > + return; > + } > + gcc_unreachable (); > +} > + > +bool > +unsupported_range::union_ (const vrange &) > +{ > + gcc_unreachable (); > + return false; > +} > + > +bool > +unsupported_range::intersect (const vrange &) > +{ > + gcc_unreachable (); > + return false; > +} > + > +bool > +unsupported_range::zero_p () const > +{ > + gcc_unreachable (); > + return false; > +} > + > +bool > +unsupported_range::nonzero_p () const > +{ > + gcc_unreachable (); > + return false; > +} > + > +void > +unsupported_range::set_nonzero (tree) > +{ > + gcc_unreachable (); > +} > + > +void > +unsupported_range::set_zero (tree) > +{ > + gcc_unreachable (); > +} > + > +void > +unsupported_range::set_nonnegative (tree) > +{ > + gcc_unreachable (); > +} > + > +bool > +unsupported_range::fits_p (const vrange &) const > +{ > + gcc_unreachable (); > + return false; > +} > + > // Here we copy between any two irange's. The ranges can be legacy or > // multi-ranges, and copying between any combination works correctly. > > @@ -291,7 +447,7 @@ irange::set (tree min, tree max, value_range_kind kind) > } > if (kind == VR_UNDEFINED) > { > - set_undefined (); > + irange::set_undefined (); > return; > } > > @@ -370,6 +526,7 @@ irange::set (tree min, tree max, value_range_kind kind) > void > irange::verify_range () > { > + gcc_checking_assert (m_discriminator == VR_IRANGE); > if (m_kind == VR_UNDEFINED) > { > gcc_checking_assert (m_num_ranges == 0); > @@ -2087,6 +2244,7 @@ dump_bound_with_infinite_markers (FILE *file, tree bound) > void > irange::dump (FILE *file) const > { > + fprintf (file, "[irange] "); > if (undefined_p ()) > { > fprintf (file, "UNDEFINED"); > @@ -2121,27 +2279,27 @@ irange::dump (FILE *file) const > } > > void > -irange::debug () const > +vrange::debug () const > { > dump (stderr); > fprintf (stderr, "\n"); > } > > void > -dump_value_range (FILE *file, const irange *vr) > +dump_value_range (FILE *file, const vrange *vr) > { > vr->dump (file); > } > > DEBUG_FUNCTION void > -debug (const irange *vr) > +debug (const vrange *vr) > { > dump_value_range (stderr, vr); > fprintf (stderr, "\n"); > } > > DEBUG_FUNCTION void > -debug (const irange &vr) > +debug (const vrange &vr) > { > debug (&vr); > } > diff --git a/gcc/value-range.h b/gcc/value-range.h > index ec59d2e4f23..0061f667092 100644 > --- a/gcc/value-range.h > +++ b/gcc/value-range.h > @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see > #ifndef GCC_VALUE_RANGE_H > #define GCC_VALUE_RANGE_H > > +class irange; > + > // Types of value ranges. > enum value_range_kind > { > @@ -37,24 +39,71 @@ enum value_range_kind > VR_LAST > }; > > -// Range of values that can be associated with an SSA_NAME. > -// > -// This is the base class without any storage. > +// Discriminator between different vrange types. > + > +enum value_range_discriminator > +{ > + // Range holds an integer or pointer. > + VR_IRANGE, > + // Range holds an unsupported type. > + VR_UNKNOWN > +}; > + > +// Abstract class for ranges of any of the supported types. > + > +class vrange > +{ > + template <typename T> friend bool is_a (vrange &); > +public: > + virtual void set (tree, tree, value_range_kind = VR_RANGE) = 0; > + virtual tree type () const = 0; > + virtual void set_varying (tree type) = 0; > + virtual void set_undefined () = 0; > + virtual void dump (FILE * = stderr) const = 0; > + virtual bool union_ (const vrange &) = 0; > + virtual bool intersect (const vrange &) = 0; > + virtual bool singleton_p (tree *result = NULL) const; > + virtual bool contains_p (tree cst) const; > + virtual bool zero_p () const = 0; > + virtual bool nonzero_p () const = 0; > + virtual void set_nonzero (tree type) = 0; > + virtual void set_zero (tree type) = 0; > + virtual void set_nonnegative (tree type) = 0; > + virtual bool fits_p (const vrange &r) const = 0; > + > + static bool supports_type_p (tree); > + > + bool varying_p () const; > + bool undefined_p () const; > + vrange& operator= (const vrange &); > + bool operator== (const vrange &) const; > + bool operator!= (const vrange &r) const { return !(*this == r); } > + > + enum value_range_kind kind () const; // DEPRECATED > + void debug () const; > + > +protected: > + ENUM_BITFIELD(value_range_kind) m_kind : 8; > + ENUM_BITFIELD(value_range_discriminator) m_discriminator : 4; > +}; > + > +// An integer range without any storage. > > -class GTY((user)) irange > +class GTY((user)) irange : public vrange > { > friend class irange_allocator; > public: > // In-place setters. > - void set (tree, tree, value_range_kind = VR_RANGE); > - void set_nonzero (tree); > - void set_zero (tree); > - void set_varying (tree type); > - void set_undefined (); > + virtual void set (tree, tree, value_range_kind = VR_RANGE) override; > + virtual void set_nonzero (tree type) override; > + virtual void set_zero (tree type) override; > + virtual void set_nonnegative (tree type) override; > + virtual void set_varying (tree type) override; > + virtual void set_undefined () override; > > // Range types. > static bool supports_type_p (tree); > - tree type () const; > + virtual tree type () const override; > > // Iteration over sub-ranges. > unsigned num_pairs () const; > @@ -63,16 +112,14 @@ public: > wide_int upper_bound () const; > > // Predicates. > - bool zero_p () const; > - bool nonzero_p () const; > - bool undefined_p () const; > - bool varying_p () const; > - bool singleton_p (tree *result = NULL) const; > - bool contains_p (tree) const; > + virtual bool zero_p () const override; > + virtual bool nonzero_p () const override; > + virtual bool singleton_p (tree *result = NULL) const override; > + virtual bool contains_p (tree cst) const override; > > // In-place operators. > - bool union_ (const irange &); > - bool intersect (const irange &); > + virtual bool union_ (const vrange &) override; > + virtual bool intersect (const vrange &) override; > void invert (); > > // Operator overloads. > @@ -81,12 +128,10 @@ public: > bool operator!= (const irange &r) const { return !(*this == r); } > > // Misc methods. > - bool fits_p (const irange &r) { return m_max_ranges >= r.num_pairs (); } > - void dump (FILE * = stderr) const; > - void debug () const; > + virtual bool fits_p (const vrange &r) const override; > + virtual void dump (FILE * = stderr) const override; > > // Deprecated legacy public methods. > - enum value_range_kind kind () const; // DEPRECATED > tree min () const; // DEPRECATED > tree max () const; // DEPRECATED > bool symbolic_p () const; // DEPRECATED > @@ -139,7 +184,6 @@ private: > bool intersect (const wide_int& lb, const wide_int& ub); > unsigned char m_num_ranges; > unsigned char m_max_ranges; > - ENUM_BITFIELD(value_range_kind) m_kind : 8; > tree *m_base; > }; > > @@ -173,6 +217,88 @@ private: > tree m_ranges[N*2]; > }; > > +// Unsupported temporaries may be created by ranger before it's known > +// they're unsupported, or by vr_values::get_value_range. All > +// operations except construction cause a trap. > + > +class unsupported_range : public vrange > +{ > +public: > + unsupported_range (); > + virtual void set (tree, tree, value_range_kind) override; > + virtual tree type () const override; > + virtual void set_varying (tree type) override; > + virtual void set_undefined () override; > + virtual void dump (FILE *) const override; > + virtual bool union_ (const vrange &) override; > + virtual bool intersect (const vrange &) override; > + virtual bool zero_p () const override; > + virtual bool nonzero_p () const override; > + virtual void set_nonzero (tree) override; > + virtual void set_zero (tree) override; > + virtual void set_nonnegative (tree) override; > + virtual bool fits_p (const vrange &) const override; > +}; > + > +// Traits to implement vrange is_a<> and as_a<>. > + > +template<typename T> > +struct vrange_traits > +{ > + // Default to something unusable. > + typedef void range_type; > +}; > + > +template<> > +struct vrange_traits<irange> > +{ > + typedef irange range_type; > +}; > + > +template <typename T> > +inline bool > +is_a (vrange &v) > +{ > + gcc_unreachable (); > + return false; > +} > + > +template <typename T> > +inline bool > +is_a (const vrange &v) > +{ > + // Reuse is_a <vrange> to implement the const version. > + const T &derived = static_cast<const T &> (v); > + return is_a <T> (const_cast<T &> (derived)); > +} > + > +template <typename T> > +inline T & > +as_a (vrange &v) > +{ > + typedef typename vrange_traits<T>::range_type range_type; > + gcc_checking_assert (is_a <range_type> (v)); > + return static_cast <range_type &> (v); > +} > + > +template <typename T> > +inline const T & > +as_a (const vrange &v) > +{ > + typedef typename vrange_traits<T>::range_type range_type; > + gcc_checking_assert (is_a <range_type> (v)); > + return static_cast <const range_type &> (v); > +} > + > +// Specializations for the different range types. > + > +template <> > +inline bool > +is_a <irange> (vrange &v) > +{ > + return v.m_discriminator == VR_IRANGE; > +} > + > // This is a special int_range<1> with only one pair, plus > // VR_ANTI_RANGE magic to describe slightly more than can be described > // in one pair. It is described in the code as a "legacy range" (as > @@ -197,13 +323,13 @@ irange::legacy_mode_p () const > extern bool range_has_numeric_bounds_p (const irange *); > extern bool ranges_from_anti_range (const value_range *, > value_range *, value_range *); > -extern void dump_value_range (FILE *, const irange *); > +extern void dump_value_range (FILE *, const vrange *); > extern bool vrp_val_is_min (const_tree); > extern bool vrp_val_is_max (const_tree); > extern bool vrp_operand_equal_p (const_tree, const_tree); > > inline value_range_kind > -irange::kind () const > +vrange::kind () const > { > return m_kind; > } > @@ -293,13 +419,13 @@ irange::varying_compatible_p () const > } > > inline bool > -irange::varying_p () const > +vrange::varying_p () const > { > return m_kind == VR_VARYING; > } > > inline bool > -irange::undefined_p () const > +vrange::undefined_p () const > { > return m_kind == VR_UNDEFINED; > } > @@ -398,6 +524,7 @@ gt_pch_nx (int_range<N> *x, gt_pointer_operator op, void *cookie) > inline > irange::irange (tree *base, unsigned nranges) > { > + m_discriminator = VR_IRANGE; > m_base = base; > m_max_ranges = nranges; > set_undefined (); > @@ -547,21 +674,21 @@ irange::upper_bound () const > } > > inline bool > -irange::union_ (const irange &r) > +irange::union_ (const vrange &r) > { > dump_flags_t m_flags = dump_flags; > dump_flags &= ~TDF_DETAILS; > - bool ret = irange::legacy_verbose_union_ (&r); > + bool ret = irange::legacy_verbose_union_ (&as_a <irange> (r)); > dump_flags = m_flags; > return ret; > } > > inline bool > -irange::intersect (const irange &r) > +irange::intersect (const vrange &r) > { > dump_flags_t m_flags = dump_flags; > dump_flags &= ~TDF_DETAILS; > - bool ret = irange::legacy_verbose_intersect (&r); > + bool ret = irange::legacy_verbose_intersect (&as_a <irange> (r)); > dump_flags = m_flags; > return ret; > } > @@ -608,6 +735,12 @@ irange::normalize_kind () > } > } > > +inline bool > +vrange::supports_type_p (tree type) > +{ > + return irange::supports_type_p (type); > +} > + > // Return the maximum value for TYPE. > > inline tree > diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc > index 47faa4ff938..6f8583c8d01 100644 > --- a/gcc/vr-values.cc > +++ b/gcc/vr-values.cc > @@ -883,7 +883,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr, > wide_int wmax = wi::to_wide (max, TYPE_PRECISION (TREE_TYPE (max))); > tree range_min = build_zero_cst (expr_type); > tree range_max = wide_int_to_tree (expr_type, wmax - 1); > - vr->set (range_min, range_max); > + vr->set (range_min, range_max, NULL); > return; > } > } > @@ -1275,7 +1275,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt) > /* This is the boolean return value whether compare and > exchange changed anything or not. */ > vr->set (build_int_cst (type, 0), > - build_int_cst (type, 1)); > + build_int_cst (type, 1), NULL); > return; > } > break; > @@ -1297,7 +1297,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt) > vr->set_varying (type); > else > vr->set (build_int_cst (type, 0), > - build_int_cst (type, 1)); > + build_int_cst (type, 1), NULL); > } > else if (types_compatible_p (type, TREE_TYPE (op0)) > && types_compatible_p (type, TREE_TYPE (op1))) > -- > 2.36.1 >
diff --git a/gcc/value-range-equiv.cc b/gcc/value-range-equiv.cc index 77c6f5ca99d..b0ae1288a09 100644 --- a/gcc/value-range-equiv.cc +++ b/gcc/value-range-equiv.cc @@ -50,6 +50,12 @@ value_range_equiv::set (tree min, tree max, bitmap equiv, check (); } +void +value_range_equiv::set (tree min, tree max, value_range_kind kind) +{ + set (min, max, m_equiv, kind); +} + void value_range_equiv::set (tree val) { diff --git a/gcc/value-range-equiv.h b/gcc/value-range-equiv.h index 0aa1069cb61..743ceb2b227 100644 --- a/gcc/value-range-equiv.h +++ b/gcc/value-range-equiv.h @@ -41,9 +41,10 @@ class GTY((user)) value_range_equiv : public value_range void move (value_range_equiv *); /* Leaves equiv bitmap alone. */ + virtual void set (tree, tree, value_range_kind = VR_RANGE) override; void update (tree, tree, value_range_kind = VR_RANGE); /* Deep-copies equiv bitmap argument. */ - void set (tree, tree, bitmap = NULL, value_range_kind = VR_RANGE); + void set (tree, tree, bitmap, value_range_kind = VR_RANGE); void set (tree); bool operator== (const value_range_equiv &) const /* = delete */; diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 2e7385aecc2..97ff0614f48 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -30,6 +30,162 @@ along with GCC; see the file COPYING3. If not see #include "fold-const.h" #include "gimple-range.h" +// Default implementation when none has been defined. + +bool +vrange::contains_p (tree) const +{ + return false; +} + +// Default implementation when none has been defined. + +bool +vrange::singleton_p (tree *) const +{ + return false; +} + +// Assignment operator for generic ranges. Copying incompatible types +// is not allowed. + +vrange & +vrange::operator= (const vrange &src) +{ + if (is_a <irange> (src)) + { + as_a <irange> (*this) = as_a <irange> (src); + return *this; + } + else + gcc_unreachable (); +} + +// Equality operator for generic ranges. + +bool +vrange::operator== (const vrange &src) const +{ + if (is_a <irange> (src)) + return as_a <irange> (*this) == as_a <irange> (src); + gcc_unreachable (); +} + +// Return TRUE if R fits in THIS. + +bool +irange::fits_p (const vrange &r) const +{ + return m_max_ranges >= as_a <irange> (r).num_pairs (); +} + +void +irange::set_nonnegative (tree type) +{ + set (build_int_cst (type, 0), TYPE_MAX_VALUE (type)); +} + +unsupported_range::unsupported_range () +{ + m_discriminator = VR_UNKNOWN; + set_undefined (); +} + +void +unsupported_range::set (tree, tree, value_range_kind) +{ + gcc_unreachable (); +} + +tree +unsupported_range::type () const +{ + gcc_unreachable (); + return nullptr; +} + +void +unsupported_range::set_undefined () +{ + m_kind = VR_UNDEFINED; +} + +void +unsupported_range::set_varying (tree) +{ + gcc_unreachable (); +} + +void +unsupported_range::dump (FILE *file) const +{ + fprintf (file, "[unsupported_range] "); + if (undefined_p ()) + { + fprintf (file, "UNDEFINED"); + return; + } + if (varying_p ()) + { + fprintf (file, "VARYING"); + return; + } + gcc_unreachable (); +} + +bool +unsupported_range::union_ (const vrange &) +{ + gcc_unreachable (); + return false; +} + +bool +unsupported_range::intersect (const vrange &) +{ + gcc_unreachable (); + return false; +} + +bool +unsupported_range::zero_p () const +{ + gcc_unreachable (); + return false; +} + +bool +unsupported_range::nonzero_p () const +{ + gcc_unreachable (); + return false; +} + +void +unsupported_range::set_nonzero (tree) +{ + gcc_unreachable (); +} + +void +unsupported_range::set_zero (tree) +{ + gcc_unreachable (); +} + +void +unsupported_range::set_nonnegative (tree) +{ + gcc_unreachable (); +} + +bool +unsupported_range::fits_p (const vrange &) const +{ + gcc_unreachable (); + return false; +} + // Here we copy between any two irange's. The ranges can be legacy or // multi-ranges, and copying between any combination works correctly. @@ -291,7 +447,7 @@ irange::set (tree min, tree max, value_range_kind kind) } if (kind == VR_UNDEFINED) { - set_undefined (); + irange::set_undefined (); return; } @@ -370,6 +526,7 @@ irange::set (tree min, tree max, value_range_kind kind) void irange::verify_range () { + gcc_checking_assert (m_discriminator == VR_IRANGE); if (m_kind == VR_UNDEFINED) { gcc_checking_assert (m_num_ranges == 0); @@ -2087,6 +2244,7 @@ dump_bound_with_infinite_markers (FILE *file, tree bound) void irange::dump (FILE *file) const { + fprintf (file, "[irange] "); if (undefined_p ()) { fprintf (file, "UNDEFINED"); @@ -2121,27 +2279,27 @@ irange::dump (FILE *file) const } void -irange::debug () const +vrange::debug () const { dump (stderr); fprintf (stderr, "\n"); } void -dump_value_range (FILE *file, const irange *vr) +dump_value_range (FILE *file, const vrange *vr) { vr->dump (file); } DEBUG_FUNCTION void -debug (const irange *vr) +debug (const vrange *vr) { dump_value_range (stderr, vr); fprintf (stderr, "\n"); } DEBUG_FUNCTION void -debug (const irange &vr) +debug (const vrange &vr) { debug (&vr); } diff --git a/gcc/value-range.h b/gcc/value-range.h index ec59d2e4f23..0061f667092 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -22,6 +22,8 @@ along with GCC; see the file COPYING3. If not see #ifndef GCC_VALUE_RANGE_H #define GCC_VALUE_RANGE_H +class irange; + // Types of value ranges. enum value_range_kind { @@ -37,24 +39,71 @@ enum value_range_kind VR_LAST }; -// Range of values that can be associated with an SSA_NAME. -// -// This is the base class without any storage. +// Discriminator between different vrange types. + +enum value_range_discriminator +{ + // Range holds an integer or pointer. + VR_IRANGE, + // Range holds an unsupported type. + VR_UNKNOWN +}; + +// Abstract class for ranges of any of the supported types. + +class vrange +{ + template <typename T> friend bool is_a (vrange &); +public: + virtual void set (tree, tree, value_range_kind = VR_RANGE) = 0; + virtual tree type () const = 0; + virtual void set_varying (tree type) = 0; + virtual void set_undefined () = 0; + virtual void dump (FILE * = stderr) const = 0; + virtual bool union_ (const vrange &) = 0; + virtual bool intersect (const vrange &) = 0; + virtual bool singleton_p (tree *result = NULL) const; + virtual bool contains_p (tree cst) const; + virtual bool zero_p () const = 0; + virtual bool nonzero_p () const = 0; + virtual void set_nonzero (tree type) = 0; + virtual void set_zero (tree type) = 0; + virtual void set_nonnegative (tree type) = 0; + virtual bool fits_p (const vrange &r) const = 0; + + static bool supports_type_p (tree); + + bool varying_p () const; + bool undefined_p () const; + vrange& operator= (const vrange &); + bool operator== (const vrange &) const; + bool operator!= (const vrange &r) const { return !(*this == r); } + + enum value_range_kind kind () const; // DEPRECATED + void debug () const; + +protected: + ENUM_BITFIELD(value_range_kind) m_kind : 8; + ENUM_BITFIELD(value_range_discriminator) m_discriminator : 4; +}; + +// An integer range without any storage. -class GTY((user)) irange +class GTY((user)) irange : public vrange { friend class irange_allocator; public: // In-place setters. - void set (tree, tree, value_range_kind = VR_RANGE); - void set_nonzero (tree); - void set_zero (tree); - void set_varying (tree type); - void set_undefined (); + virtual void set (tree, tree, value_range_kind = VR_RANGE) override; + virtual void set_nonzero (tree type) override; + virtual void set_zero (tree type) override; + virtual void set_nonnegative (tree type) override; + virtual void set_varying (tree type) override; + virtual void set_undefined () override; // Range types. static bool supports_type_p (tree); - tree type () const; + virtual tree type () const override; // Iteration over sub-ranges. unsigned num_pairs () const; @@ -63,16 +112,14 @@ public: wide_int upper_bound () const; // Predicates. - bool zero_p () const; - bool nonzero_p () const; - bool undefined_p () const; - bool varying_p () const; - bool singleton_p (tree *result = NULL) const; - bool contains_p (tree) const; + virtual bool zero_p () const override; + virtual bool nonzero_p () const override; + virtual bool singleton_p (tree *result = NULL) const override; + virtual bool contains_p (tree cst) const override; // In-place operators. - bool union_ (const irange &); - bool intersect (const irange &); + virtual bool union_ (const vrange &) override; + virtual bool intersect (const vrange &) override; void invert (); // Operator overloads. @@ -81,12 +128,10 @@ public: bool operator!= (const irange &r) const { return !(*this == r); } // Misc methods. - bool fits_p (const irange &r) { return m_max_ranges >= r.num_pairs (); } - void dump (FILE * = stderr) const; - void debug () const; + virtual bool fits_p (const vrange &r) const override; + virtual void dump (FILE * = stderr) const override; // Deprecated legacy public methods. - enum value_range_kind kind () const; // DEPRECATED tree min () const; // DEPRECATED tree max () const; // DEPRECATED bool symbolic_p () const; // DEPRECATED @@ -139,7 +184,6 @@ private: bool intersect (const wide_int& lb, const wide_int& ub); unsigned char m_num_ranges; unsigned char m_max_ranges; - ENUM_BITFIELD(value_range_kind) m_kind : 8; tree *m_base; }; @@ -173,6 +217,88 @@ private: tree m_ranges[N*2]; }; +// Unsupported temporaries may be created by ranger before it's known +// they're unsupported, or by vr_values::get_value_range. All +// operations except construction cause a trap. + +class unsupported_range : public vrange +{ +public: + unsupported_range (); + virtual void set (tree, tree, value_range_kind) override; + virtual tree type () const override; + virtual void set_varying (tree type) override; + virtual void set_undefined () override; + virtual void dump (FILE *) const override; + virtual bool union_ (const vrange &) override; + virtual bool intersect (const vrange &) override; + virtual bool zero_p () const override; + virtual bool nonzero_p () const override; + virtual void set_nonzero (tree) override; + virtual void set_zero (tree) override; + virtual void set_nonnegative (tree) override; + virtual bool fits_p (const vrange &) const override; +}; + +// Traits to implement vrange is_a<> and as_a<>. + +template<typename T> +struct vrange_traits +{ + // Default to something unusable. + typedef void range_type; +}; + +template<> +struct vrange_traits<irange> +{ + typedef irange range_type; +}; + +template <typename T> +inline bool +is_a (vrange &v) +{ + gcc_unreachable (); + return false; +} + +template <typename T> +inline bool +is_a (const vrange &v) +{ + // Reuse is_a <vrange> to implement the const version. + const T &derived = static_cast<const T &> (v); + return is_a <T> (const_cast<T &> (derived)); +} + +template <typename T> +inline T & +as_a (vrange &v) +{ + typedef typename vrange_traits<T>::range_type range_type; + gcc_checking_assert (is_a <range_type> (v)); + return static_cast <range_type &> (v); +} + +template <typename T> +inline const T & +as_a (const vrange &v) +{ + typedef typename vrange_traits<T>::range_type range_type; + gcc_checking_assert (is_a <range_type> (v)); + return static_cast <const range_type &> (v); +} + +// Specializations for the different range types. + +template <> +inline bool +is_a <irange> (vrange &v) +{ + return v.m_discriminator == VR_IRANGE; +} + // This is a special int_range<1> with only one pair, plus // VR_ANTI_RANGE magic to describe slightly more than can be described // in one pair. It is described in the code as a "legacy range" (as @@ -197,13 +323,13 @@ irange::legacy_mode_p () const extern bool range_has_numeric_bounds_p (const irange *); extern bool ranges_from_anti_range (const value_range *, value_range *, value_range *); -extern void dump_value_range (FILE *, const irange *); +extern void dump_value_range (FILE *, const vrange *); extern bool vrp_val_is_min (const_tree); extern bool vrp_val_is_max (const_tree); extern bool vrp_operand_equal_p (const_tree, const_tree); inline value_range_kind -irange::kind () const +vrange::kind () const { return m_kind; } @@ -293,13 +419,13 @@ irange::varying_compatible_p () const } inline bool -irange::varying_p () const +vrange::varying_p () const { return m_kind == VR_VARYING; } inline bool -irange::undefined_p () const +vrange::undefined_p () const { return m_kind == VR_UNDEFINED; } @@ -398,6 +524,7 @@ gt_pch_nx (int_range<N> *x, gt_pointer_operator op, void *cookie) inline irange::irange (tree *base, unsigned nranges) { + m_discriminator = VR_IRANGE; m_base = base; m_max_ranges = nranges; set_undefined (); @@ -547,21 +674,21 @@ irange::upper_bound () const } inline bool -irange::union_ (const irange &r) +irange::union_ (const vrange &r) { dump_flags_t m_flags = dump_flags; dump_flags &= ~TDF_DETAILS; - bool ret = irange::legacy_verbose_union_ (&r); + bool ret = irange::legacy_verbose_union_ (&as_a <irange> (r)); dump_flags = m_flags; return ret; } inline bool -irange::intersect (const irange &r) +irange::intersect (const vrange &r) { dump_flags_t m_flags = dump_flags; dump_flags &= ~TDF_DETAILS; - bool ret = irange::legacy_verbose_intersect (&r); + bool ret = irange::legacy_verbose_intersect (&as_a <irange> (r)); dump_flags = m_flags; return ret; } @@ -608,6 +735,12 @@ irange::normalize_kind () } } +inline bool +vrange::supports_type_p (tree type) +{ + return irange::supports_type_p (type); +} + // Return the maximum value for TYPE. inline tree diff --git a/gcc/vr-values.cc b/gcc/vr-values.cc index 47faa4ff938..6f8583c8d01 100644 --- a/gcc/vr-values.cc +++ b/gcc/vr-values.cc @@ -883,7 +883,7 @@ vr_values::extract_range_from_binary_expr (value_range_equiv *vr, wide_int wmax = wi::to_wide (max, TYPE_PRECISION (TREE_TYPE (max))); tree range_min = build_zero_cst (expr_type); tree range_max = wide_int_to_tree (expr_type, wmax - 1); - vr->set (range_min, range_max); + vr->set (range_min, range_max, NULL); return; } } @@ -1275,7 +1275,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt) /* This is the boolean return value whether compare and exchange changed anything or not. */ vr->set (build_int_cst (type, 0), - build_int_cst (type, 1)); + build_int_cst (type, 1), NULL); return; } break; @@ -1297,7 +1297,7 @@ vr_values::extract_range_basic (value_range_equiv *vr, gimple *stmt) vr->set_varying (type); else vr->set (build_int_cst (type, 0), - build_int_cst (type, 1)); + build_int_cst (type, 1), NULL); } else if (types_compatible_p (type, TREE_TYPE (op0)) && types_compatible_p (type, TREE_TYPE (op1)))