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

Reply via email to