diff mbox series

[1/5] Implement abstract vrange class.

Message ID 20220530132751.1752112-1-aldyh@redhat.com
State New
Headers show
Series [1/5] Implement abstract vrange class. | expand

Commit Message

Aldy Hernandez May 30, 2022, 1:27 p.m. UTC
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(-)

Comments

Aldy Hernandez June 1, 2022, 8:57 a.m. UTC | #1
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 mbox series

Patch

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)))