https://gcc.gnu.org/g:9244ea4bf556381d3f7fb66154dc8944ebeb005c
commit r16-1550-g9244ea4bf556381d3f7fb66154dc8944ebeb005c Author: Andrew MacLeod <amacl...@redhat.com> Date: Mon Jun 16 15:41:47 2025 -0400 Snap subrange boundries to bitmask constraints. Ensure all subrange endpoints conform to the bitmask. PR tree-optimization/120661 gcc/ * value-range.cc (irange::snap): New. (irange::snap_subranges): New. (irange::set_range_from_bitmask): Call snap_subranges. * value-range.h (snap, snap_subranges): New prototypes. gcc/testsuite/ * gcc.dg/pr120661-1.c: New. * gcc.dg/pr120661-2.c: New. Diff: --- gcc/testsuite/gcc.dg/pr120661-1.c | 51 ++++++++++++++++++++++ gcc/testsuite/gcc.dg/pr120661-2.c | 39 +++++++++++++++++ gcc/value-range.cc | 91 +++++++++++++++++++++++++++++++++++++++ gcc/value-range.h | 2 + 4 files changed, 183 insertions(+) diff --git a/gcc/testsuite/gcc.dg/pr120661-1.c b/gcc/testsuite/gcc.dg/pr120661-1.c new file mode 100644 index 000000000000..abf9210050d4 --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr120661-1.c @@ -0,0 +1,51 @@ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -Os" } */ + +typedef __builtin_va_list __gnuc_va_list; +typedef __gnuc_va_list va_list; + +int e, a, b; +int f(int b, ...) { + va_list args; + __builtin_c23_va_start(args, b); + unsigned c = 1; + for (int d; d < b; ++d) + c = c ^ 1; + return c; +} +static int fn3(int l, int i, int n) { + int j; + goto k; +r: + j = (f(e) + 1641730381) * l + 1189664732 * n + 1064 * i - 1545337304; + if (903562339 * j + n >= 0) + goto m; + goto ac; +k: + if (0) + goto ad; + goto t; +ad: + if (b) + goto s; + goto r; +m: + goto ad; +t: + j = l; + l = 800794 * j; + goto ad; +s: + b = 2 * b + 1; + if (a + (long)j) + goto t; + i = n; + goto s; +ac: +} +int main() { + if (a) + if (fn3(-1, 1, -1)) + fn3(1, 0, 3); + return 0; +} diff --git a/gcc/testsuite/gcc.dg/pr120661-2.c b/gcc/testsuite/gcc.dg/pr120661-2.c new file mode 100644 index 000000000000..05d976db450b --- /dev/null +++ b/gcc/testsuite/gcc.dg/pr120661-2.c @@ -0,0 +1,39 @@ +/* { dg-do compile } */ +/* { dg-options "-std=c23 -O2" } */ + +typedef __builtin_va_list __gnuc_va_list; +typedef __gnuc_va_list va_list; + +int a, c, d; +int e(int b, ...) { + va_list args; + __builtin_c23_va_start(args, b); + + int r = 0; + for (int i = 0; i < b; i++) { + int v = __builtin_va_arg(args, int); + r += v; + } + __builtin_va_end (args); + return r; +} +int f() { e(0); } +int main() { + int h = 0, g = 0; + goto l; +i: + if (f() * h) + goto k; +j: + h = h - 2; +k: + d = 1200000000 * h + 10; + g = (long)g + -1000000000 * d + 1; + if (a * h >= 0) { + if (g + (c - (long)1) >= 0) + goto i; + return 0; + } +l: + goto j; +} diff --git a/gcc/value-range.cc b/gcc/value-range.cc index e2d75f59c2e0..5e97fdb76919 100644 --- a/gcc/value-range.cc +++ b/gcc/value-range.cc @@ -2254,6 +2254,94 @@ irange::invert () verify_range (); } +// This routine will take the bounds [LB, UB], and apply the bitmask to those +// values such that both bounds satisfy the bitmask. TRUE is returned +// if either bound changes, and they are retuirned as [NEW_LB, NEW_UB]. +// if NEW_UB < NEW_LB, then the entire bound is to be removed as none of +// the values are valid. +// ie, [4, 14] MASK 0xFFFE VALUE 0x1 +// means all values must be odd, the new bounds returned will be [5, 13]. +// ie, [4, 4] MASK 0xFFFE VALUE 0x1 +// would return [1, 0] and as the LB < UB, the entire subrange is invalid +// and should be removed. + +bool +irange::snap (const wide_int &lb, const wide_int &ub, + wide_int &new_lb, wide_int &new_ub) +{ + uint z = wi::ctz (m_bitmask.mask ()); + if (z == 0) + return false; + const wide_int &wild_mask = m_bitmask.mask (); + + const wide_int step = (wi::one (TYPE_PRECISION (type ())) << z); + const wide_int match_mask = step - 1; + const wide_int value = m_bitmask.value () & match_mask; + + wide_int rem_lb = lb & match_mask; + + wi::overflow_type ov_sub; + wide_int diff = wi::sub(value, rem_lb, UNSIGNED, &ov_sub); + wide_int offset = diff & match_mask; + + wi::overflow_type ov1; + new_lb = wi::add (lb, offset, UNSIGNED, &ov1); + + wide_int rem_ub = ub & match_mask; + wide_int offset_ub = (rem_ub - value) & match_mask; + + wi::overflow_type ov2; + new_ub = wi::sub (ub, offset_ub, UNSIGNED, &ov2); + + // Overflow or inverted range = invalid + if (ov1 != wi::OVF_NONE || ov2 != wi::OVF_NONE + || wi::lt_p (new_ub, new_lb, TYPE_SIGN (type ()))) + { + new_lb = wi::one (lb.get_precision ()); + new_ub = wi::zero (ub.get_precision ()); + return true; + } + return (new_lb != lb) || (new_ub != ub); +} + +// This method loops through the subranges in THIS, and adjusts any bounds +// to satisfy the contraints of the BITMASK. If a subrange is invalid, +// it is removed. TRUE is returned if there were any changes. + +bool +irange::snap_subranges () +{ + bool changed = false; + int_range_max invalid; + unsigned x; + wide_int lb, ub; + for (x = 0; x < m_num_ranges; x++) + { + if (snap (lower_bound (x), upper_bound (x), lb, ub)) + { + changed = true; + // This subrange is to be completely removed. + if (wi::lt_p (ub, lb, TYPE_SIGN (type ()))) + { + int_range<1> tmp (type (), lower_bound (x), upper_bound (x)); + invalid.union_ (tmp); + continue; + } + if (lower_bound (x) != lb) + m_base[x * 2] = lb; + if (upper_bound (x) != ub) + m_base[x * 2 + 1] = ub; + } + } + // Remove any subranges which are no invalid. + if (!invalid.undefined_p ()) + { + invalid.invert (); + intersect (invalid); + } + return changed; +} + // If the mask can be trivially converted to a range, do so. // Otherwise attempt to remove the lower bits from the range. // Return true if the range changed in any way. @@ -2353,6 +2441,9 @@ irange::set_range_from_bitmask () // Make sure we call intersect, so do it first. changed = intersect (mask_range) | changed; + // Npw make sure each subrange endpoint matches the bitmask. + changed |= snap_subranges (); + return changed; } diff --git a/gcc/value-range.h b/gcc/value-range.h index 74cdf29ddcb3..c32c5076b63a 100644 --- a/gcc/value-range.h +++ b/gcc/value-range.h @@ -344,6 +344,8 @@ private: bool intersect_bitmask (const irange &r); bool union_bitmask (const irange &r); bool set_range_from_bitmask (); + bool snap_subranges (); + bool snap (const wide_int &, const wide_int &, wide_int &, wide_int &); bool intersect (const wide_int& lb, const wide_int& ub); bool union_append (const irange &r);