This patch adjusts the weay prange_storage stores values in memory.
(note this isn't prange itself, but the memory storage variant)
Before this, other than UNDEFINED, there were always 4 words allocated.
2 for the lower and upper bound, and 2 for bitmask mask/value pair.
With upcoming changes supporting size and offset, there may be 4 more
variable words required sometimes. it seems reasonable to make this
more efficient now and only allocate what is needed in memory.
This patch breaks the formatting of a prange into common occurrences:
0 extra bytes:
PR_UNDEFINED, VR_UNDEFINED
PR_VARYING, VR_VARYING
PR_ZERO, [0, 0]
PR_NONZERO, [1, +INF]
2 extra bytes for a bitmask
PR_FULL, [0, +INF] (Must have bitmask)
2 or 4 extra bytes depending on whether a bitmask is present.
PR_OTHER [x, y]
THis will become more important when I eventually check in the variant
points-to code which tracks offset and size ranges as well.
This causes a minor slowdown in VRP for the extra work of about 0.5%
across a build of GCC, which amounts to a minor 0.02% across compilation.
bootstraps on x86_64-pc-linux-gnu with no regressions. Pushed.
Andrew
From 35b38198d143bbba5b5b499aa4a070a9c6a0d141 Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <[email protected]>
Date: Wed, 11 Feb 2026 10:57:24 -0500
Subject: [PATCH 2/2] More memory efficient prange_storage.
* value-range-storage.cc (prange_storage::alloc): Use prange_format.
(prange_storage::prange_storage): Likewise.
(prange_format::prange_format): New.
(prange_storage::set_prange): Use prange_foramt and only write
required words to storage.
(prange_storage::get_prange): Only read required words.
(prange_storage::equal_p): Adjust for new enum.
(prange_storage::fits_p): Use prange_format to determine size.
* value-range-storage.h (enum prange_kind): New.
(class prange_format): New.
(get_low, get_high, get_value, get_mask): Replace with get_word.
(set_low, set_high, set_value, set_mask): Replace with set_word.
(get_word): New.
(set_word): New.
---
gcc/value-range-storage.cc | 177 +++++++++++++++++++++++++++++--------
gcc/value-range-storage.h | 34 +++++--
2 files changed, 163 insertions(+), 48 deletions(-)
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index f2f4a5c52f8..7fa104228f7 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -593,12 +593,8 @@ frange_storage::fits_p (const frange &) const
prange_storage *
prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r)
{
- size_t size = sizeof (prange_storage);
- if (!r.undefined_p ())
- {
- unsigned prec = TYPE_PRECISION (r.type ());
- size += trailing_wide_ints<NINTS>::extra_size (prec);
- }
+ prange_format format (r);
+ size_t size = sizeof (prange_storage) + format.extra_size;
prange_storage *p = static_cast <prange_storage *> (allocator.alloc (size));
new (p) prange_storage (r);
return p;
@@ -608,64 +604,163 @@ prange_storage::alloc (vrange_internal_alloc &allocator, const prange &r)
prange_storage::prange_storage (const prange &r)
{
- // It is the caller's responsibility to allocate enough space such
- // that the precision fits.
+ prange_format format (r);
+ m_trailing_ints.set_precision (format.precision, format.num_words);
+ set_prange (r);
+}
+
+// FIll the format structure for range R.
+
+prange_storage::prange_format::prange_format (const prange &r)
+{
+ kind = PR_UNDEFINED;
+ has_bitmask = 0;
+ extra_size = 0;
+ precision = 0;
+ num_words = 0;
+
if (r.undefined_p ())
- // Undefined ranges do not require any extra space for trailing
- // wide ints.
- m_trailing_ints.set_precision (0);
+ return;
+
+ if (r.varying_p ())
+ {
+ kind = PR_VARYING;
+ return;
+ }
+
+ if (r.zero_p ())
+ {
+ kind = PR_ZERO;
+ return;
+ }
+
+ if (r.nonzero_p ())
+ kind = PR_NONZERO;
else
- m_trailing_ints.set_precision (TYPE_PRECISION (r.type ()));
+ {
+ prange tmp (r.type ());
+ if (r.lower_bound () == tmp.lower_bound ()
+ && r.upper_bound () == tmp.upper_bound ())
+ kind = PR_FULL;
+ else
+ {
+ // PR_OTHER requires words of storage for the end points.
+ kind = PR_OTHER;
+ num_words += 2;
+ }
+ }
+ precision = TYPE_PRECISION (r.type ());
+ has_bitmask = !r.get_bitmask ().unknown_p ();
- set_prange (r);
+ // Bitmasks require 2 words of storage.
+ if (has_bitmask)
+ num_words += 2;
+
+ if (num_words != 0)
+ extra_size = trailing_wide_ints<NINTS>::extra_size (precision, num_words);
+
+ // PR_FULL must have a bitmask, or it should be PR_VARYING.
+ gcc_checking_assert (kind != PR_FULL || has_bitmask);
}
void
prange_storage::set_prange (const prange &r)
{
- if (r.undefined_p ())
- m_kind = VR_UNDEFINED;
- else if (r.varying_p ())
- m_kind = VR_VARYING;
- else
+ prange_format format (r);
+ m_kind = format.kind;
+ m_has_bitmask = format.has_bitmask;
+ unsigned index = 0;
+
+ switch (m_kind)
+ {
+ case PR_UNDEFINED:
+ case PR_VARYING:
+ case PR_ZERO:
+ return;
+ case PR_NONZERO:
+ case PR_FULL:
+ break;
+ case PR_OTHER:
+ set_word (index++, r.lower_bound (), r.type ());
+ set_word (index++, r.upper_bound (), r.type ());
+ break;
+ default:
+ gcc_unreachable ();
+ }
+
+ if (m_has_bitmask)
{
- m_kind = VR_RANGE;
- set_low (r.lower_bound ());
- set_high (r.upper_bound ());
irange_bitmask bm = r.m_bitmask;
- set_value (bm.value ());
- set_mask (bm.mask ());
+ set_word (index++, r.m_bitmask.value (), r.type ());
+ set_word (index++, r.m_bitmask.mask (), r.type ());
}
+ gcc_checking_assert (index == format.num_words);
}
void
prange_storage::get_prange (prange &r, tree type) const
{
gcc_checking_assert (r.supports_type_p (type));
+ unsigned index = 0;
+ switch (m_kind)
+ {
+ case PR_UNDEFINED:
+ r.set_undefined ();
+ return;
- if (m_kind == VR_UNDEFINED)
- r.set_undefined ();
- else if (m_kind == VR_VARYING)
- r.set_varying (type);
- else
+ case PR_VARYING:
+ r.set_varying (type);
+ return;
+
+ case PR_ZERO:
+ r.set_zero (type);
+ return;
+
+ case PR_NONZERO:
+ r.set_nonzero (type);
+ break;
+
+ case PR_FULL:
+ {
+ r.m_kind = VR_RANGE;
+ r.m_type = type;
+ prange tmp (type);
+ r.m_min = tmp.lower_bound ();
+ r.m_max = tmp.upper_bound ();
+ break;
+ }
+
+ case PR_OTHER:
+ {
+ gcc_checking_assert (m_kind == PR_OTHER);
+ r.m_kind = VR_RANGE;
+ r.m_type = type;
+ r.m_min = get_word (index++, type);
+ r.m_max = get_word (index++, type);
+ break;
+ }
+ default:
+ gcc_unreachable ();
+ }
+
+ if (m_has_bitmask)
{
- gcc_checking_assert (m_kind == VR_RANGE);
- gcc_checking_assert (TYPE_PRECISION (type) == m_trailing_ints.get_precision ());
- r.m_kind = VR_RANGE;
- r.m_type = type;
- r.m_min = get_low ();
- r.m_max = get_high ();
- r.m_bitmask = irange_bitmask (get_value (), get_mask ());
- if (flag_checking)
- r.verify_range ();
+ wide_int value = get_word (index++, type);
+ wide_int mask = get_word (index++, type);
+ r.m_bitmask = irange_bitmask (value, mask);
}
+ else
+ r.m_bitmask.set_unknown (TYPE_PRECISION (type));
+
+ if (flag_checking)
+ r.verify_range ();
}
bool
prange_storage::equal_p (const prange &r) const
{
if (r.undefined_p ())
- return m_kind == VR_UNDEFINED;
+ return m_kind == PR_UNDEFINED;
prange tmp;
get_prange (tmp, r.type ());
@@ -680,7 +775,11 @@ prange_storage::fits_p (const prange &r) const
if (r.undefined_p ())
return true;
- return TYPE_PRECISION (r.type ()) <= m_trailing_ints.get_precision ();
+ prange_format f (r);
+ unsigned prec = m_trailing_ints.get_precision ();
+ unsigned num = m_trailing_ints.num_elements ();
+ size_t curr = num ? trailing_wide_ints<NINTS>::extra_size (prec, num) : 0;
+ return f.extra_size <= curr;
}
diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h
index e7a5700c142..78a6f4efb62 100644
--- a/gcc/value-range-storage.h
+++ b/gcc/value-range-storage.h
@@ -113,19 +113,35 @@ private:
DISABLE_COPY_AND_ASSIGN (prange_storage);
prange_storage (const prange &r);
- enum value_range_kind m_kind : 3;
+ // A prange_format class summarizes the storage requirements for a prange
+ // which are then used to initialize the prange_storage fields.
+ enum prange_kind { PR_UNDEFINED, // VR_UNDEFINED
+ PR_VARYING, // VR_VARYING
+ PR_ZERO, // [0, 0]
+ PR_NONZERO, // [1, +INF]
+ PR_FULL, // [0, +INF] (Must have bitmask)
+ PR_OTHER }; // [x, y]
+ class prange_format
+ {
+ public:
+ prange_format (const prange &r);
+ enum prange_kind kind;
+ bool has_bitmask;
+ size_t extra_size;
+ unsigned short precision;
+ unsigned num_words;
+ };
+
+ enum prange_kind m_kind;
+ bool m_has_bitmask;
// We don't use TRAILING_WIDE_INT_ACCESSOR because the getters here
// must be const. Perhaps TRAILING_WIDE_INT_ACCESSOR could be made
// const and return wide_int instead of trailing_wide_int.
- wide_int get_low () const { return m_trailing_ints[0]; }
- wide_int get_high () const { return m_trailing_ints[1]; }
- wide_int get_value () const { return m_trailing_ints[2]; }
- wide_int get_mask () const { return m_trailing_ints[3]; }
- template <typename T> void set_low (const T &x) { m_trailing_ints[0] = x; }
- template <typename T> void set_high (const T &x) { m_trailing_ints[1] = x; }
- template <typename T> void set_value (const T &x) { m_trailing_ints[2] = x; }
- template <typename T> void set_mask (const T &x) { m_trailing_ints[3] = x; }
+ wide_int get_word (unsigned i, tree) const
+ { return m_trailing_ints[i]; }
+ template <typename T> void set_word (unsigned i, const T &x, tree)
+ { m_trailing_ints[i] = x; }
static const unsigned int NINTS = 4;
trailing_wide_ints<NINTS> m_trailing_ints;
--
2.45.0