aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAldy Hernandez <aldyh@redhat.com>2023-06-29 11:27:22 +0200
committerAldy Hernandez <aldyh@redhat.com>2023-07-07 09:55:58 +0200
commit0c888665dfbd5175256c674ee82d85bd0f7450f7 (patch)
treec4e03475bd3987a280bbced9005c62fb4ae873a3
parenta069b8662689865178596e2aab46d4dc4eaa051e (diff)
Implement value/mask tracking for irange.
Integer ranges (irange) currently track known 0 bits. We've wanted to track known 1 bits for some time, and instead of tracking known 0 and known 1's separately, it has been suggested we track a value/mask pair similarly to what we do for CCP and RTL. This patch implements such a thing. With this we now track a VALUE integer which are the known values, and a MASK which tells us which bits contain meaningful information. This allows us to fix a handful of enhancement requests, such as PR107043 and PR107053. There is a 4.48% performance penalty for VRP and 0.42% in overall compilation for this entire patchset. It is expected and in line with the loss incurred when we started tracking known 0 bits. This patch just provides the value/mask tracking support. All the nonzero users (range-op, IPA, CCP, etc), are still using the nonzero nomenclature. For that matter, this patch reimplements the nonzero accessors with the value/mask functionality. In follow-up patches I will enhance these passes to use the value/mask information, and fix the aforementioned PRs. gcc/ChangeLog: * data-streamer-in.cc (streamer_read_value_range): Adjust for value/mask. * data-streamer-out.cc (streamer_write_vrange): Same. * range-op.cc (operator_cast::fold_range): Same. * value-range-pretty-print.cc (vrange_printer::print_irange_bitmasks): Same. * value-range-storage.cc (irange_storage::write_lengths_address): Same. (irange_storage::set_irange): Same. (irange_storage::get_irange): Same. (irange_storage::size): Same. (irange_storage::dump): Same. * value-range-storage.h: Same. * value-range.cc (debug): New. (irange_bitmask::dump): New. (add_vrange): Adjust for value/mask. (irange::operator=): Same. (irange::set): Same. (irange::verify_range): Same. (irange::operator==): Same. (irange::contains_p): Same. (irange::irange_single_pair_union): Same. (irange::union_): Same. (irange::intersect): Same. (irange::invert): Same. (irange::get_nonzero_bits_from_range): Rename to... (irange::get_bitmask_from_range): ...this. (irange::set_range_from_nonzero_bits): Rename to... (irange::set_range_from_bitmask): ...this. (irange::set_nonzero_bits): Rename to... (irange::update_bitmask): ...this. (irange::get_nonzero_bits): Rename to... (irange::get_bitmask): ...this. (irange::intersect_nonzero_bits): Rename to... (irange::intersect_bitmask): ...this. (irange::union_nonzero_bits): Rename to... (irange::union_bitmask): ...this. (irange_bitmask::verify_mask): New. * value-range.h (class irange_bitmask): New. (irange_bitmask::set_unknown): New. (irange_bitmask::unknown_p): New. (irange_bitmask::irange_bitmask): New. (irange_bitmask::get_precision): New. (irange_bitmask::get_nonzero_bits): New. (irange_bitmask::set_nonzero_bits): New. (irange_bitmask::operator==): New. (irange_bitmask::union_): New. (irange_bitmask::intersect): New. (class irange): Friend vrange_printer. (irange::varying_compatible_p): Adjust for bitmask. (irange::set_varying): Same. (irange::set_nonzero): Same. gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/pr107009.c: Adjust irange dumping for value/mask changes. * gcc.dg/tree-ssa/vrp-unreachable.c: Same. * gcc.dg/tree-ssa/vrp122.c: Same.
-rw-r--r--gcc/data-streamer-in.cc6
-rw-r--r--gcc/data-streamer-out.cc5
-rw-r--r--gcc/range-op.cc16
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/pr107009.c2
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/vrp-unreachable.c2
-rw-r--r--gcc/testsuite/gcc.dg/tree-ssa/vrp122.c2
-rw-r--r--gcc/value-range-pretty-print.cc11
-rw-r--r--gcc/value-range-storage.cc26
-rw-r--r--gcc/value-range-storage.h2
-rw-r--r--gcc/value-range.cc248
-rw-r--r--gcc/value-range.h153
11 files changed, 351 insertions, 122 deletions
diff --git a/gcc/data-streamer-in.cc b/gcc/data-streamer-in.cc
index 578c328475f..6e36adc73cc 100644
--- a/gcc/data-streamer-in.cc
+++ b/gcc/data-streamer-in.cc
@@ -241,8 +241,10 @@ streamer_read_value_range (class lto_input_block *ib, data_in *data_in,
int_range<2> tmp (type, lb, ub);
r.union_ (tmp);
}
- wide_int nz = streamer_read_wide_int (ib);
- r.set_nonzero_bits (nz);
+ wide_int value = streamer_read_wide_int (ib);
+ wide_int mask = streamer_read_wide_int (ib);
+ irange_bitmask bm (value, mask);
+ r.update_bitmask (bm);
return;
}
if (is_a <frange> (vr))
diff --git a/gcc/data-streamer-out.cc b/gcc/data-streamer-out.cc
index 93dedfcb895..8af8ae0d363 100644
--- a/gcc/data-streamer-out.cc
+++ b/gcc/data-streamer-out.cc
@@ -423,7 +423,10 @@ streamer_write_vrange (struct output_block *ob, const vrange &v)
streamer_write_wide_int (ob, r.lower_bound (i));
streamer_write_wide_int (ob, r.upper_bound (i));
}
- streamer_write_wide_int (ob, r.get_nonzero_bits ());
+ // TODO: We could avoid streaming out the value if the mask is -1.
+ irange_bitmask bm = r.get_bitmask ();
+ streamer_write_wide_int (ob, bm.value ());
+ streamer_write_wide_int (ob, bm.mask ());
return;
}
if (is_a <frange> (v))
diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index f0dff53ec1e..cb584314f4c 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -2876,16 +2876,20 @@ operator_cast::fold_range (irange &r, tree type ATTRIBUTE_UNUSED,
return true;
}
- // Update the nonzero mask. Truncating casts are problematic unless
+ // Update the bitmask. Truncating casts are problematic unless
// the conversion fits in the resulting outer type.
- wide_int nz = inner.get_nonzero_bits ();
+ irange_bitmask bm = inner.get_bitmask ();
if (truncating_cast_p (inner, outer)
- && wi::rshift (nz, wi::uhwi (TYPE_PRECISION (outer.type ()),
- TYPE_PRECISION (inner.type ())),
+ && wi::rshift (bm.mask (),
+ wi::uhwi (TYPE_PRECISION (outer.type ()),
+ TYPE_PRECISION (inner.type ())),
TYPE_SIGN (inner.type ())) != 0)
return true;
- nz = wide_int::from (nz, TYPE_PRECISION (type), TYPE_SIGN (inner.type ()));
- r.set_nonzero_bits (nz);
+ unsigned prec = TYPE_PRECISION (type);
+ signop sign = TYPE_SIGN (inner.type ());
+ bm = irange_bitmask (wide_int::from (bm.value (), prec, sign),
+ wide_int::from (bm.mask (), prec, sign));
+ r.update_bitmask (bm);
return true;
}
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr107009.c b/gcc/testsuite/gcc.dg/tree-ssa/pr107009.c
index 5010aed1723..0450950c3a9 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/pr107009.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/pr107009.c
@@ -12,4 +12,4 @@ void saxpy(size_t n)
foobar (n);
}
-// { dg-final { scan-tree-dump "NONZERO.*fff8" "dom2" } }
+// { dg-final { scan-tree-dump "fff8 VALUE 0x0" "dom2" } }
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp-unreachable.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp-unreachable.c
index cdc57403c6e..5835dfc8dbc 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp-unreachable.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp-unreachable.c
@@ -39,4 +39,4 @@ void func (unsigned n, unsigned m)
/* { dg-final { scan-tree-dump-not "dead" "vrp1" } } */
/* { dg-final { scan-tree-dump-times "builtin_unreachable" 1 "vrp1" } } */
/* { dg-final { scan-tree-dump-not "builtin_unreachable" "vrp2" } } */
-/* { dg-final { scan-tree-dump-times "fff8" 4 "vrp2" } } */
+/* { dg-final { scan-tree-dump-times "fff8 VALUE 0x0" 4 "vrp2" } } */
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp122.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp122.c
index b2ddcda023c..5a4ca850bee 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/vrp122.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp122.c
@@ -16,4 +16,4 @@ int f(unsigned t)
return 0;
}
-// { dg-final { scan-tree-dump "Global Exported: g_.* NONZERO 0x.*fff0" "evrp" } }
+// { dg-final { scan-tree-dump "Global Exported: g_.* MASK 0x1 VALUE 0x0" "evrp" } }
diff --git a/gcc/value-range-pretty-print.cc b/gcc/value-range-pretty-print.cc
index 8d47d8087e8..c95b09d55b8 100644
--- a/gcc/value-range-pretty-print.cc
+++ b/gcc/value-range-pretty-print.cc
@@ -94,13 +94,16 @@ vrange_printer::print_irange_bound (const wide_int &bound, tree type) const
void
vrange_printer::print_irange_bitmasks (const irange &r) const
{
- wide_int nz = r.get_nonzero_bits ();
- if (nz == -1)
+ irange_bitmask bm = r.m_bitmask;
+ if (bm.unknown_p ())
return;
- pp_string (pp, " NONZERO ");
+ pp_string (pp, " MASK ");
char buf[WIDE_INT_PRINT_BUFFER_SIZE];
- print_hex (nz, buf);
+ print_hex (bm.mask (), buf);
+ pp_string (pp, buf);
+ pp_string (pp, " VALUE ");
+ print_hex (bm.value (), buf);
pp_string (pp, buf);
}
diff --git a/gcc/value-range-storage.cc b/gcc/value-range-storage.cc
index 2f82739680c..e94d7f944b8 100644
--- a/gcc/value-range-storage.cc
+++ b/gcc/value-range-storage.cc
@@ -232,7 +232,7 @@ vrange_storage::equal_p (const vrange &r) const
unsigned char *
irange_storage::write_lengths_address ()
{
- return (unsigned char *)&m_val[(m_num_ranges * 2 + 1)
+ return (unsigned char *)&m_val[(m_num_ranges * 2 + 2)
* WIDE_INT_MAX_HWIS (m_precision)];
}
@@ -301,7 +301,11 @@ irange_storage::set_irange (const irange &r)
write_wide_int (val, len, r.lower_bound (i));
write_wide_int (val, len, r.upper_bound (i));
}
- write_wide_int (val, len, r.m_nonzero_mask);
+
+ // TODO: We could avoid streaming out the value if the mask is -1.
+ irange_bitmask bm = r.m_bitmask;
+ write_wide_int (val, len, bm.value ());
+ write_wide_int (val, len, bm.mask ());
if (flag_checking)
{
@@ -367,7 +371,12 @@ irange_storage::get_irange (irange &r, tree type) const
r.union_ (tmp);
}
}
- read_wide_int (r.m_nonzero_mask, val, *len, m_precision);
+
+ wide_int bits_value, bits_mask;
+ read_wide_int (bits_value, val, *len, m_precision);
+ val += *len++;
+ read_wide_int (bits_mask, val, *len, m_precision);
+ r.m_bitmask = irange_bitmask (bits_value, bits_mask);
if (r.m_kind == VR_VARYING)
r.m_kind = VR_RANGE;
@@ -399,7 +408,7 @@ irange_storage::size (const irange &r)
return sizeof (irange_storage);
unsigned prec = TYPE_PRECISION (r.type ());
- unsigned n = r.num_pairs () * 2 + 1;
+ unsigned n = r.num_pairs () * 2 + 2;
unsigned hwi_size = ((n * WIDE_INT_MAX_HWIS (prec) - 1)
* sizeof (HOST_WIDE_INT));
unsigned len_size = n;
@@ -428,7 +437,7 @@ irange_storage::dump () const
int i, j;
fprintf (stderr, " lengths = [ ");
- for (i = 0; i < m_num_ranges * 2 + 1; ++i)
+ for (i = 0; i < m_num_ranges * 2 + 2; ++i)
fprintf (stderr, "%d ", len[i]);
fprintf (stderr, "]\n");
@@ -443,8 +452,13 @@ irange_storage::dump () const
*val++);
++len;
}
+
+ // Dump value/mask pair.
+ for (j = 0; j < *len; ++j)
+ fprintf (stderr, " [VALUE] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
+ ++len;
for (j = 0; j < *len; ++j)
- fprintf (stderr, " [NZ] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
+ fprintf (stderr, " [MASK] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
}
DEBUG_FUNCTION void
diff --git a/gcc/value-range-storage.h b/gcc/value-range-storage.h
index 99fb815cdc2..a91833ca1ef 100644
--- a/gcc/value-range-storage.h
+++ b/gcc/value-range-storage.h
@@ -91,7 +91,7 @@ private:
enum value_range_kind m_kind : 3;
- // The length of this is m_num_ranges * 2 + 1 to accomodate the nonzero bits.
+ // The length of this is m_num_ranges * 2 + 2 to accomodate the bitmask.
HOST_WIDE_INT m_val[1];
// Another variable-length part of the structure following the HWIs.
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index f5d4bf3bb4a..8e5607a7eeb 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -79,6 +79,13 @@ debug (const Value_Range &r)
fprintf (stderr, "\n");
}
+DEBUG_FUNCTION void
+debug (const irange_bitmask &bm)
+{
+ bm.dump (stderr);
+ fprintf (stderr, "\n");
+}
+
// Default vrange definitions.
bool
@@ -235,6 +242,23 @@ vrange::dump (FILE *file) const
pp_flush (&buffer);
}
+void
+irange_bitmask::dump (FILE *file) const
+{
+ char buf[WIDE_INT_PRINT_BUFFER_SIZE];
+ pretty_printer buffer;
+
+ pp_needs_newline (&buffer) = true;
+ buffer.buffer->stream = file;
+ pp_string (&buffer, "MASK ");
+ print_hex (m_mask, buf);
+ pp_string (&buffer, buf);
+ pp_string (&buffer, " VALUE ");
+ print_hex (m_value, buf);
+ pp_string (&buffer, buf);
+ pp_flush (&buffer);
+}
+
namespace inchash
{
@@ -263,7 +287,9 @@ add_vrange (const vrange &v, inchash::hash &hstate,
hstate.add_wide_int (r.lower_bound (i));
hstate.add_wide_int (r.upper_bound (i));
}
- hstate.add_wide_int (r.get_nonzero_bits ());
+ irange_bitmask bm = r.get_bitmask ();
+ hstate.add_wide_int (bm.value ());
+ hstate.add_wide_int (bm.mask ());
return;
}
if (is_a <frange> (v))
@@ -908,7 +934,7 @@ irange::operator= (const irange &src)
m_num_ranges = lim;
m_type = src.m_type;
m_kind = src.m_kind;
- m_nonzero_mask = src.m_nonzero_mask;
+ m_bitmask = src.m_bitmask;
if (m_max_ranges == 1)
normalize_kind ();
if (flag_checking)
@@ -972,7 +998,7 @@ irange::set (tree type, const wide_int &min, const wide_int &max,
wide_int max_value = wi::max_value (prec, sign);
m_type = type;
- m_nonzero_mask = wi::minus_one (prec);
+ m_bitmask.set_unknown (prec);
if (kind == VR_RANGE)
{
@@ -1059,7 +1085,7 @@ irange::verify_range ()
unsigned prec = TYPE_PRECISION (m_type);
if (m_kind == VR_VARYING)
{
- gcc_checking_assert (m_nonzero_mask == -1);
+ gcc_checking_assert (m_bitmask.unknown_p ());
gcc_checking_assert (m_num_ranges == 1);
gcc_checking_assert (varying_compatible_p ());
gcc_checking_assert (lower_bound ().get_precision () == prec);
@@ -1077,7 +1103,7 @@ irange::verify_range ()
int c = wi::cmp (lb, ub, TYPE_SIGN (m_type));
gcc_checking_assert (c == 0 || c == -1);
}
- gcc_checking_assert (m_nonzero_mask.get_precision () == prec);
+ m_bitmask.verify_mask ();
}
bool
@@ -1101,9 +1127,18 @@ irange::operator== (const irange &other) const
if (lb != lb_other || ub != ub_other)
return false;
}
- widest_int nz1 = widest_int::from (get_nonzero_bits (), sign1);
- widest_int nz2 = widest_int::from (other.get_nonzero_bits (), sign2);
- return nz1 == nz2;
+
+ irange_bitmask bm1 = get_bitmask ();
+ irange_bitmask bm2 = other.get_bitmask ();
+ widest_int tmp1 = widest_int::from (bm1.mask (), sign1);
+ widest_int tmp2 = widest_int::from (bm2.mask (), sign2);
+ if (tmp1 != tmp2)
+ return false;
+ if (bm1.unknown_p ())
+ return true;
+ tmp1 = widest_int::from (bm1.value (), sign1);
+ tmp2 = widest_int::from (bm2.value (), sign2);
+ return tmp1 == tmp2;
}
/* If range is a singleton, place it in RESULT and return TRUE. */
@@ -1143,10 +1178,10 @@ irange::contains_p (const wide_int &cst) const
if (undefined_p ())
return false;
- // See if we can exclude CST based on the nonzero bits.
- if (m_nonzero_mask != -1
+ // See if we can exclude CST based on the known 0 bits.
+ if (!m_bitmask.unknown_p ()
&& cst != 0
- && wi::bit_and (m_nonzero_mask, cst) == 0)
+ && wi::bit_and (m_bitmask.get_nonzero_bits (), cst) == 0)
return false;
signop sign = TYPE_SIGN (type ());
@@ -1176,7 +1211,7 @@ irange::irange_single_pair_union (const irange &r)
{
// If current upper bound is new upper bound, we're done.
if (wi::le_p (r.m_base[1], m_base[1], sign))
- return union_nonzero_bits (r);
+ return union_bitmask (r);
// Otherwise R has the new upper bound.
// Check for overlap/touching ranges, or single target range.
if (m_max_ranges == 1
@@ -1192,7 +1227,7 @@ irange::irange_single_pair_union (const irange &r)
}
// The range has been altered, so normalize it even if nothing
// changed in the mask.
- if (!union_nonzero_bits (r))
+ if (!union_bitmask (r))
normalize_kind ();
if (flag_checking)
verify_range ();
@@ -1221,7 +1256,7 @@ irange::irange_single_pair_union (const irange &r)
}
// The range has been altered, so normalize it even if nothing
// changed in the mask.
- if (!union_nonzero_bits (r))
+ if (!union_bitmask (r))
normalize_kind ();
if (flag_checking)
verify_range ();
@@ -1261,7 +1296,7 @@ irange::union_ (const vrange &v)
// If this ranges fully contains R, then we need do nothing.
if (irange_contains_p (r))
- return union_nonzero_bits (r);
+ return union_bitmask (r);
// Do not worry about merging and such by reserving twice as many
// pairs as needed, and then simply sort the 2 ranges into this
@@ -1356,7 +1391,7 @@ irange::union_ (const vrange &v)
m_kind = VR_RANGE;
// The range has been altered, so normalize it even if nothing
// changed in the mask.
- if (!union_nonzero_bits (r))
+ if (!union_bitmask (r))
normalize_kind ();
if (flag_checking)
verify_range ();
@@ -1439,13 +1474,13 @@ irange::intersect (const vrange &v)
if (undefined_p ())
return true;
- res |= intersect_nonzero_bits (r);
+ res |= intersect_bitmask (r);
return res;
}
// If R fully contains this, then intersection will change nothing.
if (r.irange_contains_p (*this))
- return intersect_nonzero_bits (r);
+ return intersect_bitmask (r);
// ?? We could probably come up with something smarter than the
// worst case scenario here.
@@ -1528,7 +1563,7 @@ irange::intersect (const vrange &v)
m_kind = VR_RANGE;
// The range has been altered, so normalize it even if nothing
// changed in the mask.
- if (!intersect_nonzero_bits (r))
+ if (!intersect_bitmask (r))
normalize_kind ();
if (flag_checking)
verify_range ();
@@ -1652,7 +1687,7 @@ irange::invert ()
signop sign = TYPE_SIGN (ttype);
wide_int type_min = wi::min_value (prec, sign);
wide_int type_max = wi::max_value (prec, sign);
- m_nonzero_mask = wi::minus_one (prec);
+ m_bitmask.set_unknown (prec);
// At this point, we need one extra sub-range to represent the
// inverse.
@@ -1723,45 +1758,45 @@ irange::invert ()
verify_range ();
}
-// Return the nonzero bits inherent in the range.
+// Return the bitmask inherent in the range.
-wide_int
-irange::get_nonzero_bits_from_range () const
+irange_bitmask
+irange::get_bitmask_from_range () const
{
wide_int min = lower_bound ();
wide_int max = upper_bound ();
wide_int xorv = min ^ max;
+ unsigned prec = TYPE_PRECISION (type ());
+
if (xorv != 0)
- {
- unsigned prec = TYPE_PRECISION (type ());
- xorv = wi::mask (prec - wi::clz (xorv), false, prec);
- }
- return min | xorv;
+ xorv = wi::mask (prec - wi::clz (xorv), false, prec);
+
+ return irange_bitmask (wi::zero (prec), min | xorv);
}
-// If the the nonzero mask can be trivially converted to a range, do
-// so and return TRUE.
+// If the the mask can be trivially converted to a range, do so and
+// return TRUE.
bool
-irange::set_range_from_nonzero_bits ()
+irange::set_range_from_bitmask ()
{
gcc_checking_assert (!undefined_p ());
- if (m_nonzero_mask == -1)
+ if (m_bitmask.unknown_p ())
return false;
- unsigned popcount = wi::popcount (m_nonzero_mask);
+ unsigned popcount = wi::popcount (m_bitmask.get_nonzero_bits ());
// If we have only one bit set in the mask, we can figure out the
// range immediately.
if (popcount == 1)
{
// Make sure we don't pessimize the range.
- if (!contains_p (m_nonzero_mask))
+ if (!contains_p (m_bitmask.get_nonzero_bits ()))
return false;
bool has_zero = contains_zero_p (*this);
- wide_int nz = m_nonzero_mask;
+ wide_int nz = m_bitmask.get_nonzero_bits ();
set (m_type, nz, nz);
- m_nonzero_mask = nz;
+ m_bitmask.set_nonzero_bits (nz);
if (has_zero)
{
int_range<2> zero;
@@ -1781,95 +1816,126 @@ irange::set_range_from_nonzero_bits ()
}
void
-irange::set_nonzero_bits (const wide_int &bits)
+irange::update_bitmask (const irange_bitmask &bm)
{
gcc_checking_assert (!undefined_p ());
- // Drop VARYINGs with a nonzero mask to a plain range.
- if (m_kind == VR_VARYING && bits != -1)
+ // Drop VARYINGs with known bits to a plain range.
+ if (m_kind == VR_VARYING && !bm.unknown_p ())
m_kind = VR_RANGE;
- m_nonzero_mask = bits;
- if (!set_range_from_nonzero_bits ())
+ m_bitmask = bm;
+ if (!set_range_from_bitmask ())
normalize_kind ();
if (flag_checking)
verify_range ();
}
-// Return the nonzero bitmask. This will return the nonzero bits plus
-// the nonzero bits inherent in the range.
+// Return the bitmask of known bits that includes the bitmask inherent
+// in the range.
+
+irange_bitmask
+irange::get_bitmask () const
+{
+ gcc_checking_assert (!undefined_p ());
+
+ // The mask inherent in the range is calculated on-demand. For
+ // example, [0,255] does not have known bits set by default. This
+ // saves us considerable time, because setting it at creation incurs
+ // a large penalty for irange::set. At the time of writing there
+ // was a 5% slowdown in VRP if we kept the mask precisely up to date
+ // at all times. Instead, we default to -1 and set it when
+ // explicitly requested. However, this function will always return
+ // the correct mask.
+ //
+ // This also means that the mask may have a finer granularity than
+ // the range and thus contradict it. Think of the mask as an
+ // enhancement to the range. For example:
+ //
+ // [3, 1000] MASK 0xfffffffe VALUE 0x0
+ //
+ // 3 is in the range endpoints, but is excluded per the known 0 bits
+ // in the mask.
+ irange_bitmask bm = get_bitmask_from_range ();
+ if (!m_bitmask.unknown_p ())
+ bm.intersect (m_bitmask);
+ return bm;
+}
+
+// Set the nonzero bits in R into THIS. Return TRUE and
+// normalize the range if anything changed.
+
+void
+irange::set_nonzero_bits (const wide_int &bits)
+{
+ gcc_checking_assert (!undefined_p ());
+ irange_bitmask bm (wi::zero (TYPE_PRECISION (type ())), bits);
+ update_bitmask (bm);
+}
+
+// Return the nonzero bits in R.
wide_int
irange::get_nonzero_bits () const
{
gcc_checking_assert (!undefined_p ());
- // The nonzero mask inherent in the range is calculated on-demand.
- // For example, [0,255] does not have a 0xff nonzero mask by default
- // (unless manually set). This saves us considerable time, because
- // setting it at creation incurs a large penalty for irange::set.
- // At the time of writing there was a 5% slowdown in VRP if we kept
- // the mask precisely up to date at all times. Instead, we default
- // to -1 and set it when explicitly requested. However, this
- // function will always return the correct mask.
- if (m_nonzero_mask == -1)
- return get_nonzero_bits_from_range ();
- else
- return m_nonzero_mask & get_nonzero_bits_from_range ();
+ irange_bitmask bm = get_bitmask ();
+ return bm.value () | bm.mask ();
}
-// Intersect the nonzero bits in R into THIS. Return TRUE and
-// normalize the range if anything changed.
+// Intersect the bitmask in R into THIS and normalize the range.
+// Return TRUE if the intersection changed anything.
bool
-irange::intersect_nonzero_bits (const irange &r)
+irange::intersect_bitmask (const irange &r)
{
gcc_checking_assert (!undefined_p () && !r.undefined_p ());
- if (m_nonzero_mask == -1 && r.m_nonzero_mask == -1)
+ if (m_bitmask == r.m_bitmask)
return false;
- if (m_nonzero_mask != r.m_nonzero_mask)
- {
- wide_int nz = get_nonzero_bits () & r.get_nonzero_bits ();
- // If the nonzero bits did not change, return false.
- if (nz == get_nonzero_bits ())
- return false;
+ irange_bitmask bm = get_bitmask ();
+ if (!bm.intersect (r.get_bitmask ()))
+ return false;
- m_nonzero_mask = nz;
- if (!set_range_from_nonzero_bits ())
- normalize_kind ();
- if (flag_checking)
- verify_range ();
- return true;
- }
- return false;
+ m_bitmask = bm;
+ if (!set_range_from_bitmask ())
+ normalize_kind ();
+ if (flag_checking)
+ verify_range ();
+ return true;
}
-// Union the nonzero bits in R into THIS. Return TRUE and normalize
-// the range if anything changed.
+// Union the bitmask in R into THIS. Return TRUE and normalize the
+// range if anything changed.
bool
-irange::union_nonzero_bits (const irange &r)
+irange::union_bitmask (const irange &r)
{
gcc_checking_assert (!undefined_p () && !r.undefined_p ());
- if (m_nonzero_mask == -1 && r.m_nonzero_mask == -1)
+ if (m_bitmask == r.m_bitmask)
return false;
- if (m_nonzero_mask != r.m_nonzero_mask)
- {
- wide_int save = get_nonzero_bits ();
- m_nonzero_mask = save | r.get_nonzero_bits ();
- if (m_nonzero_mask == save)
- return false;
- // No need to call set_range_from_nonzero_bits, because we'll
- // never narrow the range. Besides, it would cause endless
- // recursion because of the union_ in
- // set_range_from_nonzero_bits.
- normalize_kind ();
- return true;
- }
- return false;
+ irange_bitmask bm = get_bitmask ();
+ if (!bm.union_ (r.get_bitmask ()))
+ return false;
+
+ m_bitmask = bm;
+ // No need to call set_range_from_mask, because we'll never
+ // narrow the range. Besides, it would cause endless recursion
+ // because of the union_ in set_range_from_mask.
+ normalize_kind ();
+ return true;
+}
+
+void
+irange_bitmask::verify_mask () const
+{
+ gcc_assert (m_value.get_precision () == m_mask.get_precision ());
+ // Unknown bits must have their corresponding value bits cleared as
+ // it simplifies union and intersect.
+ gcc_assert (wi::bit_and (m_mask, m_value) == 0);
}
void
diff --git a/gcc/value-range.h b/gcc/value-range.h
index 5d4eaf8b625..0170188201b 100644
--- a/gcc/value-range.h
+++ b/gcc/value-range.h
@@ -113,12 +113,146 @@ namespace inchash
extern void add_vrange (const vrange &, hash &, unsigned flags = 0);
}
+// A pair of values representing the known bits in a range. Zero bits
+// in MASK cover constant values. Set bits in MASK cover unknown
+// values. VALUE are the known bits.
+//
+// Set bits in MASK (no meaningful information) must have their
+// corresponding bits in VALUE cleared, as this speeds up union and
+// intersect.
+
+class irange_bitmask
+{
+public:
+ irange_bitmask () { /* uninitialized */ }
+ irange_bitmask (unsigned prec) { set_unknown (prec); }
+ irange_bitmask (const wide_int &value, const wide_int &mask);
+ wide_int value () const { return m_value; }
+ wide_int mask () const { return m_mask; }
+ void set_unknown (unsigned prec);
+ bool unknown_p () const;
+ unsigned get_precision () const;
+ bool union_ (const irange_bitmask &src);
+ bool intersect (const irange_bitmask &src);
+ bool operator== (const irange_bitmask &src) const;
+ bool operator!= (const irange_bitmask &src) const { return !(*this == src); }
+ void verify_mask () const;
+ void dump (FILE *) const;
+
+ // Convenience functions for nonzero bitmask compatibility.
+ wide_int get_nonzero_bits () const;
+ void set_nonzero_bits (const wide_int &bits);
+private:
+ wide_int m_value;
+ wide_int m_mask;
+};
+
+inline void
+irange_bitmask::set_unknown (unsigned prec)
+{
+ m_value = wi::zero (prec);
+ m_mask = wi::minus_one (prec);
+ if (flag_checking)
+ verify_mask ();
+}
+
+// Return TRUE if THIS does not have any meaningful information.
+
+inline bool
+irange_bitmask::unknown_p () const
+{
+ return m_mask == -1;
+}
+
+inline
+irange_bitmask::irange_bitmask (const wide_int &value, const wide_int &mask)
+{
+ m_value = value;
+ m_mask = mask;
+ if (flag_checking)
+ verify_mask ();
+}
+
+inline unsigned
+irange_bitmask::get_precision () const
+{
+ return m_mask.get_precision ();
+}
+
+// The following two functions are meant for backwards compatability
+// with the nonzero bitmask. A cleared bit means the value must be 0.
+// A set bit means we have no information for the bit.
+
+// Return the nonzero bits.
+inline wide_int
+irange_bitmask::get_nonzero_bits () const
+{
+ return m_value | m_mask;
+}
+
+// Set the bitmask to the nonzero bits in BITS.
+inline void
+irange_bitmask::set_nonzero_bits (const wide_int &bits)
+{
+ m_value = wi::zero (bits.get_precision ());
+ m_mask = bits;
+ if (flag_checking)
+ verify_mask ();
+}
+
+inline bool
+irange_bitmask::operator== (const irange_bitmask &src) const
+{
+ bool unknown1 = unknown_p ();
+ bool unknown2 = src.unknown_p ();
+ if (unknown1 || unknown2)
+ return unknown1 == unknown2;
+ return m_value == src.m_value && m_mask == src.m_mask;
+}
+
+inline bool
+irange_bitmask::union_ (const irange_bitmask &src)
+{
+ irange_bitmask save (*this);
+ m_mask = (m_mask | src.m_mask) | (m_value ^ src.m_value);
+ m_value = m_value & src.m_value;
+ if (flag_checking)
+ verify_mask ();
+ return *this != save;
+}
+
+inline bool
+irange_bitmask::intersect (const irange_bitmask &src)
+{
+ irange_bitmask save (*this);
+ // If we have two known bits that are incompatible, the resulting
+ // bit is undefined. It is unclear whether we should set the entire
+ // range to UNDEFINED, or just a subset of it. For now, set the
+ // entire bitmask to unknown (VARYING).
+ if (wi::bit_and (~(m_mask | src.m_mask),
+ m_value ^ src.m_value) != 0)
+ {
+ unsigned prec = m_mask.get_precision ();
+ m_mask = wi::minus_one (prec);
+ m_value = wi::zero (prec);
+ }
+ else
+ {
+ m_mask = m_mask & src.m_mask;
+ m_value = m_value | src.m_value;
+ }
+ if (flag_checking)
+ verify_mask ();
+ return *this != save;
+}
+
// An integer range without any storage.
class GTY((user)) irange : public vrange
{
friend value_range_kind get_legacy_range (const irange &, tree &, tree &);
friend class irange_storage;
+ friend class vrange_printer;
public:
// In-place setters.
void set (tree type, const wide_int &, const wide_int &,
@@ -161,6 +295,8 @@ public:
virtual bool fits_p (const vrange &r) const override;
virtual void accept (const vrange_visitor &v) const override;
+ void update_bitmask (const irange_bitmask &);
+ irange_bitmask get_bitmask () const;
// Nonzero masks.
wide_int get_nonzero_bits () const;
void set_nonzero_bits (const wide_int &bits);
@@ -187,17 +323,17 @@ private:
friend void gt_pch_nx (irange *, gt_pointer_operator, void *);
bool varying_compatible_p () const;
- bool intersect_nonzero_bits (const irange &r);
- bool union_nonzero_bits (const irange &r);
- wide_int get_nonzero_bits_from_range () const;
- bool set_range_from_nonzero_bits ();
+ bool intersect_bitmask (const irange &r);
+ bool union_bitmask (const irange &r);
+ irange_bitmask get_bitmask_from_range () const;
+ bool set_range_from_bitmask ();
bool intersect (const wide_int& lb, const wide_int& ub);
unsigned char m_num_ranges;
bool m_resizable;
unsigned char m_max_ranges;
tree m_type;
- wide_int m_nonzero_mask;
+ irange_bitmask m_bitmask;
protected:
wide_int *m_base;
};
@@ -741,7 +877,7 @@ irange::varying_compatible_p () const
if (INTEGRAL_TYPE_P (t) || POINTER_TYPE_P (t))
return (l == wi::min_value (prec, sign)
&& u == wi::max_value (prec, sign)
- && m_nonzero_mask == -1);
+ && m_bitmask.unknown_p ());
return true;
}
@@ -908,7 +1044,7 @@ irange::set_varying (tree type)
{
m_kind = VR_VARYING;
m_num_ranges = 1;
- m_nonzero_mask = wi::minus_one (TYPE_PRECISION (type));
+ m_bitmask.set_unknown (TYPE_PRECISION (type));
if (INTEGRAL_TYPE_P (type) || POINTER_TYPE_P (type))
{
@@ -966,7 +1102,8 @@ irange::set_nonzero (tree type)
m_type = type;
m_kind = VR_RANGE;
m_base[0] = wi::one (prec);
- m_base[1] = m_nonzero_mask = wi::minus_one (prec);
+ m_base[1] = wi::minus_one (prec);
+ m_bitmask.set_unknown (prec);
m_num_ranges = 1;
if (flag_checking)