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

Reply via email to