[2/5] Implement generic range temporaries.

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

Commit Message

Aldy Hernandez May 30, 2022, 1:27 p.m. UTC
  Now that we have generic ranges, we need a way to define generic local
temporaries on the stack for intermediate calculations in the ranger
and elsewhere.  We need temporaries analogous to int_range_max, but
for any of the supported types (currently just integers, but soon
integers, pointers, and floats).

The tmp_range object is such a temporary.  It is designed to be
transparently used as a vrange.  It shares vrange's abstract API, and
implicitly casts itself to a vrange when passed around.

The ultimate name will be value_range, but we need to remove legacy
first for that to happen.  Until then, tmp_range will do.

Sample usage is as follows.  Instead of:

	extern void foo (vrange &);

	int_range_max t;
	t.set_nonzero (type);
	foo (t);

one does:

	tmp_range t (type);
	t.set_nonzero (type);
	foo (t);

You can also delay initialization, for use in loops for example:

	tmp_range t;
	...
	t.init (type);
	t.set_varying (type);

Creating an supported range type, will result in an unsupported_range
object being created, which will trap if anything but set_undefined()
and undefined_p() are called on it.  There's no size penalty for the
unsupported_range, since its immutable and can be shared across
instances.

Since supports_type_p() is called at construction time for each
temporary, I've removed the non-zero check from this function, which
was mostly unneeded.  I fixed the handful of callers that were
passing null types, and in the process sped things up a bit.

As more range types come about, the tmp_range class will be augmented
to support them by adding the relevant bits in the initialization
code.

Tested on x86-64 & ppc64le Linux.

gcc/ChangeLog:

	* gimple-range-fold.h (gimple_range_type): Check type before
	calling supports_type_p.
	* gimple-range-path.cc (path_range_query::range_of_stmt): Same.
	* value-query.cc (range_query::get_tree_range): Same.
	* value-range.cc (tmp_range::lower_bound): New.
	(tmp_range::upper_bound): New.
	(tmp_range::dump): New.
	* value-range.h (class tmp_range): New.
	(irange::supports_type_p): Do not check if type is non-zero.
---
 gcc/gimple-range-fold.h  |   2 +-
 gcc/gimple-range-path.cc |   2 +-
 gcc/value-query.cc       |   3 +-
 gcc/value-range.cc       |  38 ++++++++++++
 gcc/value-range.h        | 130 ++++++++++++++++++++++++++++++++++++++-
 5 files changed, 169 insertions(+), 6 deletions(-)
  

Comments

Andrew MacLeod May 30, 2022, 2:56 p.m. UTC | #1
On 5/30/22 09:27, Aldy Hernandez wrote:
> Now that we have generic ranges, we need a way to define generic local
> temporaries on the stack for intermediate calculations in the ranger
> and elsewhere.  We need temporaries analogous to int_range_max, but
> for any of the supported types (currently just integers, but soon
> integers, pointers, and floats).
>
> The tmp_range object is such a temporary.  It is designed to be
> transparently used as a vrange.  It shares vrange's abstract API, and
> implicitly casts itself to a vrange when passed around.
>
> The ultimate name will be value_range, but we need to remove legacy
> first for that to happen.  Until then, tmp_range will do.
>
I was going to suggest maybe renaming value_range to legacy_range or 
something, and then start using value_range for ranges of any time.  
Then it occurred to me that numerous places which use value_range 
will/can continue to use value_range going forward.. ie

value_range vr;
  if (!rvals->range_of_expr (vr, name, stmt))
    return -1;

would be unaffected, to it would be pointless turmoil to rename that and 
then rename it back to value_range.

I also notice there are already a few instance of local variable named 
tmp_range, which make name renames annoying.   Perhaps we should use 
Value_Range or something like that in the interim for the multi-type 
ranges?   Then the rename is trivial down the road, formatting will be 
unaffected, and then we're kinda sorta using the end_goal name?

Andrew
  
Aldy Hernandez May 31, 2022, 6:21 a.m. UTC | #2
On Mon, May 30, 2022 at 4:56 PM Andrew MacLeod <amacleod@redhat.com> wrote:
>
> On 5/30/22 09:27, Aldy Hernandez wrote:
> > Now that we have generic ranges, we need a way to define generic local
> > temporaries on the stack for intermediate calculations in the ranger
> > and elsewhere.  We need temporaries analogous to int_range_max, but
> > for any of the supported types (currently just integers, but soon
> > integers, pointers, and floats).
> >
> > The tmp_range object is such a temporary.  It is designed to be
> > transparently used as a vrange.  It shares vrange's abstract API, and
> > implicitly casts itself to a vrange when passed around.
> >
> > The ultimate name will be value_range, but we need to remove legacy
> > first for that to happen.  Until then, tmp_range will do.
> >
> I was going to suggest maybe renaming value_range to legacy_range or
> something, and then start using value_range for ranges of any time.
> Then it occurred to me that numerous places which use value_range
> will/can continue to use value_range going forward.. ie
>
> value_range vr;
>   if (!rvals->range_of_expr (vr, name, stmt))
>     return -1;
>
> would be unaffected, to it would be pointless turmoil to rename that and
> then rename it back to value_range.
>
> I also notice there are already a few instance of local variable named
> tmp_range, which make name renames annoying.   Perhaps we should use
> Value_Range or something like that in the interim for the multi-type
> ranges?   Then the rename is trivial down the road, formatting will be
> unaffected, and then we're kinda sorta using the end_goal name?

OMG that is so ugly!  Although I guess it would be temporary.

Speaking of which, how far away are we from enabling ranger in VRP1?
Because once we do that, we can start nuking legacy and cleaning all
this up.

Aldy
  
Andrew MacLeod May 31, 2022, 4:40 p.m. UTC | #3
On 5/31/22 02:21, Aldy Hernandez wrote:
> On Mon, May 30, 2022 at 4:56 PM Andrew MacLeod <amacleod@redhat.com> wrote:
>> On 5/30/22 09:27, Aldy Hernandez wrote:
>>> Now that we have generic ranges, we need a way to define generic local
>>> temporaries on the stack for intermediate calculations in the ranger
>>> and elsewhere.  We need temporaries analogous to int_range_max, but
>>> for any of the supported types (currently just integers, but soon
>>> integers, pointers, and floats).
>>>
>>> The tmp_range object is such a temporary.  It is designed to be
>>> transparently used as a vrange.  It shares vrange's abstract API, and
>>> implicitly casts itself to a vrange when passed around.
>>>
>>> The ultimate name will be value_range, but we need to remove legacy
>>> first for that to happen.  Until then, tmp_range will do.
>>>
>> I was going to suggest maybe renaming value_range to legacy_range or
>> something, and then start using value_range for ranges of any time.
>> Then it occurred to me that numerous places which use value_range
>> will/can continue to use value_range going forward.. ie
>>
>> value_range vr;
>>    if (!rvals->range_of_expr (vr, name, stmt))
>>      return -1;
>>
>> would be unaffected, to it would be pointless turmoil to rename that and
>> then rename it back to value_range.
>>
>> I also notice there are already a few instance of local variable named
>> tmp_range, which make name renames annoying.   Perhaps we should use
>> Value_Range or something like that in the interim for the multi-type
>> ranges?   Then the rename is trivial down the road, formatting will be
>> unaffected, and then we're kinda sorta using the end_goal name?
> OMG that is so ugly!  Although I guess it would be temporary.
>
> Speaking of which, how far away are we from enabling ranger in VRP1?
> Because once we do that, we can start nuking legacy and cleaning all
> this up.
>
> Aldy
>
Im thinking about making the switch mid juneish...   i still have a few 
verifications to make.  We want to leave legacy for at least a while so 
we can manually switch back to it for investigation of any issues that 
come up during the transition.   so id expect early august time frame 
before trying to remove legacy....    at least legacy VRP.

thats my thoughts anyway.

Andrew

Andrew
  
Aldy Hernandez June 1, 2022, 9:01 a.m. UTC | #4
Final patch committed.

tmp_range has been renamed to Value_Range.

Value_Range::init() has been renamed to set_type() which is more obvious.

Default constructor for Value_Range now points the vrange to the
unsupported_range object, so we always have an object available.

Re-tested on x86-64 Linux.

On Tue, May 31, 2022 at 8:21 AM Aldy Hernandez <aldyh@redhat.com> wrote:
>
> On Mon, May 30, 2022 at 4:56 PM Andrew MacLeod <amacleod@redhat.com> wrote:
> >
> > On 5/30/22 09:27, Aldy Hernandez wrote:
> > > Now that we have generic ranges, we need a way to define generic local
> > > temporaries on the stack for intermediate calculations in the ranger
> > > and elsewhere.  We need temporaries analogous to int_range_max, but
> > > for any of the supported types (currently just integers, but soon
> > > integers, pointers, and floats).
> > >
> > > The tmp_range object is such a temporary.  It is designed to be
> > > transparently used as a vrange.  It shares vrange's abstract API, and
> > > implicitly casts itself to a vrange when passed around.
> > >
> > > The ultimate name will be value_range, but we need to remove legacy
> > > first for that to happen.  Until then, tmp_range will do.
> > >
> > I was going to suggest maybe renaming value_range to legacy_range or
> > something, and then start using value_range for ranges of any time.
> > Then it occurred to me that numerous places which use value_range
> > will/can continue to use value_range going forward.. ie
> >
> > value_range vr;
> >   if (!rvals->range_of_expr (vr, name, stmt))
> >     return -1;
> >
> > would be unaffected, to it would be pointless turmoil to rename that and
> > then rename it back to value_range.
> >
> > I also notice there are already a few instance of local variable named
> > tmp_range, which make name renames annoying.   Perhaps we should use
> > Value_Range or something like that in the interim for the multi-type
> > ranges?   Then the rename is trivial down the road, formatting will be
> > unaffected, and then we're kinda sorta using the end_goal name?
>
> OMG that is so ugly!  Although I guess it would be temporary.
>
> Speaking of which, how far away are we from enabling ranger in VRP1?
> Because once we do that, we can start nuking legacy and cleaning all
> this up.
>
> Aldy
  

Patch

diff --git a/gcc/gimple-range-fold.h b/gcc/gimple-range-fold.h
index 53a5bf85dd4..20cb73dabb9 100644
--- a/gcc/gimple-range-fold.h
+++ b/gcc/gimple-range-fold.h
@@ -81,7 +81,7 @@  gimple_range_type (const gimple *s)
 	    type = TREE_TYPE (type);
 	}
     }
-  if (irange::supports_type_p (type))
+  if (type && irange::supports_type_p (type))
     return type;
   return NULL_TREE;
 }
diff --git a/gcc/gimple-range-path.cc b/gcc/gimple-range-path.cc
index 459d3797da7..66f433dd1d5 100644
--- a/gcc/gimple-range-path.cc
+++ b/gcc/gimple-range-path.cc
@@ -755,7 +755,7 @@  path_range_query::range_of_stmt (irange &r, gimple *stmt, tree)
 {
   tree type = gimple_range_type (stmt);
 
-  if (!irange::supports_type_p (type))
+  if (!type || !irange::supports_type_p (type))
     return false;
 
   // If resolving unknowns, fold the statement making use of any
diff --git a/gcc/value-query.cc b/gcc/value-query.cc
index 9ccd802457b..26e3858103b 100644
--- a/gcc/value-query.cc
+++ b/gcc/value-query.cc
@@ -249,7 +249,8 @@  range_query::get_tree_range (irange &r, tree expr, gimple *stmt)
   if (UNARY_CLASS_P (expr))
     {
       range_operator *op = range_op_handler (TREE_CODE (expr), type);
-      if (op)
+      tree op0_type = TREE_TYPE (TREE_OPERAND (expr, 0));
+      if (op && irange::supports_type_p (op0_type))
 	{
 	  int_range_max r0;
 	  range_of_expr (r0, TREE_OPERAND (expr, 0), stmt);
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 97ff0614f48..c5f326ab479 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -30,6 +30,42 @@  along with GCC; see the file COPYING3.  If not see
 #include "fold-const.h"
 #include "gimple-range.h"
 
+// Convenience function only available for integers and pointers.
+
+wide_int
+tmp_range::lower_bound () const
+{
+  if (is_a <irange> (*m_vrange))
+    return as_a <irange> (*m_vrange).lower_bound ();
+  gcc_unreachable ();
+}
+
+// Convenience function only available for integers and pointers.
+
+wide_int
+tmp_range::upper_bound () const
+{
+  if (is_a <irange> (*m_vrange))
+    return as_a <irange> (*m_vrange).upper_bound ();
+  gcc_unreachable ();
+}
+
+void
+tmp_range::dump (FILE *out) const
+{
+  if (m_vrange)
+    m_vrange->dump (out);
+  else
+    fprintf (out, "NULL");
+}
+
+DEBUG_FUNCTION void
+debug (const tmp_range &r)
+{
+  r.dump (stderr);
+  fprintf (stderr, "\n");
+}
+
 // Default implementation when none has been defined.
 
 bool
@@ -186,6 +222,8 @@  unsupported_range::fits_p (const vrange &) const
   return false;
 }
 
+unsupported_range tmp_range::m_unsupported;
+
 // Here we copy between any two irange's.  The ranges can be legacy or
 // multi-ranges, and copying between any combination works correctly.
 
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 0061f667092..d51998145da 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -54,6 +54,7 @@  enum value_range_discriminator
 class vrange
 {
   template <typename T> friend bool is_a (vrange &);
+  friend class tmp_range;
 public:
   virtual void set (tree, tree, value_range_kind = VR_RANGE) = 0;
   virtual tree type () const = 0;
@@ -313,6 +314,131 @@  typedef int_range<1> value_range;
 // calculations.
 typedef int_range<255> int_range_max;
 
+// This is an "infinite" precision range object for use in temporary
+// calculations for any of the handled types.  The object can be
+// transparently used as a vrange.
+
+class tmp_range
+{
+public:
+  tmp_range ();
+  tmp_range (const vrange &r);
+  tmp_range (tree type);
+  bool init (tree type);
+  vrange& operator= (const vrange &);
+  bool operator== (const tmp_range &r) const;
+  bool operator!= (const tmp_range &r) const;
+  operator vrange &();
+  operator const vrange &() const;
+  void dump (FILE *out = stderr) const;
+
+  // Convenience methods for vrange compatability.
+  void set (tree min, tree max, value_range_kind kind = VR_RANGE)
+    { return m_vrange->set (min, max, kind); }
+  tree type () { return m_vrange->type (); }
+  enum value_range_kind kind () { return m_vrange->kind (); }
+  bool varying_p () const { return m_vrange->varying_p (); }
+  bool undefined_p () const { return m_vrange->undefined_p (); }
+  void set_varying (tree type) { m_vrange->set_varying (type); }
+  void set_undefined () { m_vrange->set_undefined (); }
+  bool union_ (const vrange &r) { return m_vrange->union_ (r); }
+  bool intersect (const vrange &r) { return m_vrange->intersect (r); }
+  bool singleton_p (tree *result = NULL) const
+    { return m_vrange->singleton_p (result); }
+  bool zero_p () const { return m_vrange->zero_p (); }
+  wide_int lower_bound () const; // For irange/prange compatability.
+  wide_int upper_bound () const; // For irange/prange compatability.
+private:
+  static unsupported_range m_unsupported;
+  vrange *m_vrange;
+  int_range_max m_irange;
+  DISABLE_COPY_AND_ASSIGN (tmp_range);
+};
+
+// This default constructor leaves the temporary uninitialized.  Use
+// init() to initialize.
+
+inline
+tmp_range::tmp_range ()
+{
+  m_vrange = nullptr;
+}
+
+// Copy constructor from a vrange.
+
+inline
+tmp_range::tmp_range (const vrange &r)
+{
+  *this = r;
+}
+
+// Copy constructor from a tree TYPE.
+//
+// Note that unlike the similarly named int_range<> constructor which
+// defaults to VARYING, this constructor defaults to UNDEFINED.
+// Defaulting to VARYING had an unfortunate performance penalty.  We
+// could remove the int_range<> (type) constructor if this is
+// confusing.
+
+inline
+tmp_range::tmp_range (tree type)
+{
+  init (type);
+}
+
+// Initialize object so it is possible to store temporaries of TYPE
+// into it.  The range of the temporary is undefined after this call,
+// and must be explicitly set (set_varying, set_undefined, etc).
+
+inline bool
+tmp_range::init (tree type)
+{
+  gcc_checking_assert (TYPE_P (type));
+
+  if (irange::supports_type_p (type))
+    m_vrange = &m_irange;
+  else
+    m_vrange = &m_unsupported;
+  return true;
+}
+
+inline vrange &
+tmp_range::operator= (const vrange &r)
+{
+  if (is_a <irange> (r))
+    {
+      m_irange = as_a <irange> (r);
+      m_vrange = &m_irange;
+      return *m_vrange;
+    }
+  else
+    gcc_unreachable ();
+}
+
+inline bool
+tmp_range::operator== (const tmp_range &r) const
+{
+  return *m_vrange == *r.m_vrange;
+}
+
+inline bool
+tmp_range::operator!= (const tmp_range &r) const
+{
+  return *m_vrange != *r.m_vrange;
+}
+
+inline
+tmp_range::operator vrange &()
+{
+  return *m_vrange;
+}
+
+inline
+tmp_range::operator const vrange &() const
+{
+  return *m_vrange;
+}
+
 // Returns true for an old-school value_range as described above.
 inline bool
 irange::legacy_mode_p () const
@@ -451,9 +577,7 @@  irange::nonzero_p () const
 inline bool
 irange::supports_type_p (tree type)
 {
-  if (type && (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type)))
-    return type;
-  return false;
+  return INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type);
 }
 
 inline bool