This patch improves switch handling when the index has a bitmask
irange::contains__p (wide_int&) was checking whether a constant was a member of range with a bitmask by first checking if the bitmask was unknown, and if not, then perform a bitwise-and on the constant.. This overlooks the fact that many ranges defer adding a bitmask until it is needed, so in those cases, we never actually checked anything.
The first thing is to change this to simply ask if the constant is a member of the range or not. This takes care of dealing with default bitmasks and will return the correct result.
Second, the contains_p(const irange &) version in turn was overly complicated for cases wher the current range was a singleton, and could also overlook certain cases, so now we check for singletons and in that case invoke contains_p (wide_int) directly to get an accurate answer.
Finally, intersect was also overly complicated in cases with singletons, and when bitmasks were involved, could overlook certain cases which should have resulted in UNDEFINED, but which did not. This check for cases where at least one of the 2 ranges is a singleton and in those cases, also invoke contains_p () directly to short circuit and come up with the UNDEFINED result.
These are more cases we have not seen in the wild, but would easily be backported I think. They mostly seem to be of the situation where we COULD do better but don't as opposed to being actually wrong. These changes primarily produce more UNDEFINED results than we use to.
Bootstraps on x86_64-pc-linux-gnu with no regressions. Pushed. Andrew
From e65c2537b019ea19f274c09b357b01cc948fdbcb Mon Sep 17 00:00:00 2001 From: Andrew MacLeod <amacl...@redhat.com> Date: Wed, 28 May 2025 16:27:16 -0400 Subject: [PATCH 3/4] Improve contains_p and intersect with bitmasks. Improve the way contains_p (wide_int) and intersect behave wioth singletons and bitmasks. Also fix a buglet in bitmask_intersect when the result is a singleton which is not in the current range. PR tree-optimization/119039 gcc/ * value-range.cc (irange::contains_p): Call wide_int version of contains_p for singleton ranges. (irange::intersect): If either range is a singleton, use contains_p. gcc/testsuite/ * gcc.dg/pr119039-2.c: New. --- gcc/testsuite/gcc.dg/pr119039-2.c | 60 +++++++++++++++++++++++++++++++ gcc/value-range.cc | 23 +++++++++--- 2 files changed, 78 insertions(+), 5 deletions(-) create mode 100644 gcc/testsuite/gcc.dg/pr119039-2.c diff --git a/gcc/testsuite/gcc.dg/pr119039-2.c b/gcc/testsuite/gcc.dg/pr119039-2.c new file mode 100644 index 00000000000..634b4002820 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr119039-2.c @@ -0,0 +1,60 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-evrp" } */ + +extern void good (void); +extern void bad (void); + +/* Switch simplification should remove 'case 2:' because 'i' will always + * have its 0th bit set (odd). */ + +void bitmask_elimination_1(int i) +{ + i = i | 1; + + switch (i) + { + case 1: + good (); + break; + + // This case should be removed; + case 2: + bad (); + break; + + case 3: + good (); + break; + + default: + break; + } +} + +/* Switch simplification should remove 'case 20-28:' because 'i' will always + * be a multiple of 16. */ +void bitmask_elimination_2 (int i) +{ + int masked_val = i & 0xF0; // This zeroes out the lower 4 bits of 'i' + + switch (masked_val) + { + case 0: + good (); // Reachable. + break; + + // This entire cased should be removed; + case 20 ... 28: + bad (); + break; + + case 32: + good (); // Reachable. + break; + + default: + good (); + break; + } +} +/* { dg-final { scan-tree-dump-not "bad" "evrp" } } */ diff --git a/gcc/value-range.cc b/gcc/value-range.cc index 5e97fdb7691..348c68ec902 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -1628,10 +1628,8 @@ irange::contains_p (const wide_int &cst) const if (undefined_p ()) return false; - // See if we can exclude CST based on the known 0 bits. - if (!m_bitmask.unknown_p () - && cst != 0 - && wi::bit_and (m_bitmask.get_nonzero_bits (), cst) == 0) + // Check is the known bits in bitmask exclude CST. + if (!m_bitmask.member_p (cst)) return false; signop sign = TYPE_SIGN (type ()); @@ -1899,12 +1897,17 @@ irange::irange_contains_p (const irange &r) const gcc_checking_assert (!undefined_p () && !varying_p ()); gcc_checking_assert (!r.undefined_p () && !varying_p ()); + // Check singletons directly which will include any bitmasks. + wide_int rl; + if (r.singleton_p (rl)) + return contains_p (rl); + // In order for THIS to fully contain R, all of the pairs within R must // be fully contained by the pairs in this object. signop sign = TYPE_SIGN (m_type); unsigned ri = 0; unsigned i = 0; - wide_int rl = r.m_base[0]; + rl = r.m_base[0]; wide_int ru = r.m_base[1]; wide_int l = m_base[0]; wide_int u = m_base[1]; @@ -1973,6 +1976,16 @@ irange::intersect (const vrange &v) return res; } + // If either range is a singleton and the other range does not contain + // it, the result is undefined. + wide_int val; + if ((singleton_p (val) && !r.contains_p (val)) + || (r.singleton_p (val) && !contains_p (val))) + { + set_undefined (); + return true; + } + // If R fully contains this, then intersection will change nothing. if (r.irange_contains_p (*this)) return intersect_bitmask (r); -- 2.45.0