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.
This commit is contained in:
Aldy Hernandez 2023-06-29 11:27:22 +02:00
parent a069b86626
commit 0c888665df
11 changed files with 351 additions and 122 deletions

View file

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

View file

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

View file

@ -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;
}

View file

@ -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" } }

View file

@ -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" } } */

View file

@ -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" } }

View file

@ -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);
}

View file

@ -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, " [NZ] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
fprintf (stderr, " [VALUE] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
++len;
for (j = 0; j < *len; ++j)
fprintf (stderr, " [MASK] " HOST_WIDE_INT_PRINT_DEC "\n", *val++);
}
DEBUG_FUNCTION void

View file

@ -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.

View file

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

View file

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