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

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

Reply via email to