aboutsummaryrefslogtreecommitdiff
path: root/gcc/value-range.h
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2024-04-30 19:39:00 +0200
committerAldy Hernandez <aldyh@redhat.com>2024-05-01 09:37:05 +0200
commitc60b3e211c555706cdc2dc8bfcdd540152cff350 (patch)
tree12baf1a49ab7271194679a4e68936e5d65c78641 /gcc/value-range.h
parent1b5732de7e3980aa5197b1ac818f48f1ce9f87ab (diff)
downloadgcc-upstream-c60b3e211c555706cdc2dc8bfcdd540152cff350.tar.gz
Reduce startup costs for Value_Range.
Value_Range is our polymorphic temporary that can hold any range. It is used for type agnostic code where it isn't known ahead of time, what the type of the range will be (irange, france, etc). Currently, there is a temporary of each type in the object, which means we need to construct each range for every temporary. This isn't scaling well now that prange is about to add yet another range type. This patch removes each range, opting to use in-place new for a byte buffer sufficiently large to hold ranges of any type. It reduces the memory footprint by 14% for every Value_Range temporary (from 792 to 680 bytes), and we are guaranteed it will never again grow as we add more range types (strings, complex numbers, etc). Surprisingly, it improves VRP performance by 6.61% and overall compilation by 0.44%, which is a lot more than we bargained for when we started working on prange performance. There is a slight change in semantics for Value_Range. The default constructor does not initialize the object at all. It must be manually initialized with either Value_Range::set_type(), or by assigning a range to it. This means that IPA's m_known_value_ranges must be initialized at allocation, instead of depending on the empty constructor to initialize it to VR_UNDEFINED for unsupported_range. I have taken the time to properly document both the class, and each method. If anything isn't clear, please let me know so I can adjust it accordingly. gcc/ChangeLog: * ipa-fnsummary.cc (evaluate_properties_for_edge): Initialize Value_Range's. * value-range.h (class Value_Range): Add a buffer and remove m_irange and m_frange. (Value_Range::Value_Range): Call init. (Value_Range::set_type): Same. (Value_Range::init): Use in place new to initialize buffer. (Value_Range::operator=): Tidy.
Diffstat (limited to 'gcc/value-range.h')
-rw-r--r--gcc/value-range.h127
1 files changed, 70 insertions, 57 deletions
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 471f362f388..f1c638f8cd0 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -684,6 +684,16 @@ typedef int_range<2> value_range;
// 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.
+//
+// Using any of the various constructors initializes the object
+// appropriately, but the default constructor is uninitialized and
+// must be initialized either with set_type() or by assigning into it.
+//
+// Assigning between incompatible types is allowed. For example if a
+// temporary holds an irange, you can assign an frange into it, and
+// all the right things will happen. However, before passing this
+// object to a function accepting a vrange, the correct type must be
+// set. If it isn't, you can do so with set_type().
class Value_Range
{
@@ -693,6 +703,7 @@ public:
Value_Range (tree type);
Value_Range (tree, tree, value_range_kind kind = VR_RANGE);
Value_Range (const Value_Range &);
+ ~Value_Range ();
void set_type (tree type);
vrange& operator= (const vrange &);
Value_Range& operator= (const Value_Range &);
@@ -726,16 +737,29 @@ public:
void accept (const vrange_visitor &v) const { m_vrange->accept (v); }
private:
void init (tree type);
- unsupported_range m_unsupported;
+ void init (const vrange &);
+
vrange *m_vrange;
- int_range_max m_irange;
- frange m_frange;
+ // The buffer must be at least the size of the largest range.
+ static_assert (sizeof (int_range_max) > sizeof (frange));
+ char m_buffer[sizeof (int_range_max)];
};
+// The default constructor is uninitialized and must be initialized
+// with either set_type() or with an assignment into it.
+
inline
Value_Range::Value_Range ()
{
- m_vrange = &m_unsupported;
+ m_vrange = NULL;
+}
+
+// Copy constructor.
+
+inline
+Value_Range::Value_Range (const Value_Range &r)
+{
+ init (*r.m_vrange);
}
// Copy constructor from a vrange.
@@ -743,11 +767,11 @@ Value_Range::Value_Range ()
inline
Value_Range::Value_Range (const vrange &r)
{
- *this = r;
+ init (r);
}
-// Copy constructor from a TYPE. The range of the temporary is set to
-// UNDEFINED.
+// Construct an UNDEFINED range that can hold ranges of TYPE. If TYPE
+// is not supported, default to unsupported_range.
inline
Value_Range::Value_Range (tree type)
@@ -755,6 +779,9 @@ Value_Range::Value_Range (tree type)
init (type);
}
+// Construct a range that can hold a range of [MIN, MAX], where MIN
+// and MAX are trees.
+
inline
Value_Range::Value_Range (tree min, tree max, value_range_kind kind)
{
@@ -763,13 +790,25 @@ Value_Range::Value_Range (tree min, tree max, value_range_kind kind)
}
inline
-Value_Range::Value_Range (const Value_Range &r)
+Value_Range::~Value_Range ()
{
- *this = *r.m_vrange;
+ if (m_vrange)
+ m_vrange->~vrange ();
}
-// Initialize object so it is possible to store temporaries of TYPE
-// into it.
+// Initialize object to an UNDEFINED range that can hold ranges of
+// TYPE. Clean-up memory if there was a previous object.
+
+inline void
+Value_Range::set_type (tree type)
+{
+ if (m_vrange)
+ m_vrange->~vrange ();
+ init (type);
+}
+
+// Initialize object to an UNDEFINED range that can hold ranges of
+// TYPE.
inline void
Value_Range::init (tree type)
@@ -777,71 +816,45 @@ Value_Range::init (tree type)
gcc_checking_assert (TYPE_P (type));
if (irange::supports_p (type))
- m_vrange = &m_irange;
+ m_vrange = new (&m_buffer) int_range_max ();
else if (frange::supports_p (type))
- m_vrange = &m_frange;
+ m_vrange = new (&m_buffer) frange ();
else
- m_vrange = &m_unsupported;
+ m_vrange = new (&m_buffer) unsupported_range ();
}
-// Set the temporary to allow storing temporaries of TYPE. The range
-// of the temporary is set to UNDEFINED.
+// Initialize object with a copy of R.
inline void
-Value_Range::set_type (tree type)
+Value_Range::init (const vrange &r)
{
- init (type);
- m_vrange->set_undefined ();
+ if (is_a <irange> (r))
+ m_vrange = new (&m_buffer) int_range_max (as_a <irange> (r));
+ else if (is_a <frange> (r))
+ m_vrange = new (&m_buffer) frange (as_a <frange> (r));
+ else
+ m_vrange = new (&m_buffer) unsupported_range (as_a <unsupported_range> (r));
}
-// Assignment operator for temporaries. Copying incompatible types is
-// allowed.
+// Assignment operator. Copying incompatible types is allowed. That
+// is, assigning an frange to an object holding an irange does the
+// right thing.
inline vrange &
Value_Range::operator= (const vrange &r)
{
- if (is_a <irange> (r))
- {
- m_irange = as_a <irange> (r);
- m_vrange = &m_irange;
- }
- else if (is_a <frange> (r))
- {
- m_frange = as_a <frange> (r);
- m_vrange = &m_frange;
- }
- else if (is_a <unsupported_range> (r))
- {
- m_unsupported = as_a <unsupported_range> (r);
- m_vrange = &m_unsupported;
- }
- else
- gcc_unreachable ();
-
+ if (m_vrange)
+ m_vrange->~vrange ();
+ init (r);
return *m_vrange;
}
inline Value_Range &
Value_Range::operator= (const Value_Range &r)
{
- if (r.m_vrange == &r.m_irange)
- {
- m_irange = r.m_irange;
- m_vrange = &m_irange;
- }
- else if (r.m_vrange == &r.m_frange)
- {
- m_frange = r.m_frange;
- m_vrange = &m_frange;
- }
- else if (r.m_vrange == &r.m_unsupported)
- {
- m_unsupported = r.m_unsupported;
- m_vrange = &m_unsupported;
- }
- else
- gcc_unreachable ();
-
+ // No need to call the m_vrange destructor here, as we will do so in
+ // the assignment below.
+ *this = *r.m_vrange;
return *this;
}