https://gcc.gnu.org/g:3b84d180fa840a447c4fd08d655ea0c2d94abd0b

commit r16-1674-g3b84d180fa840a447c4fd08d655ea0c2d94abd0b
Author: Andrew MacLeod <amacl...@redhat.com>
Date:   Tue Jun 24 13:10:56 2025 -0400

    get_bitmask is sometimes less refined.
    
    get_bitmask intersects the current mask with a mask generated from the
    range.  If the 2 masks are incompatible, it currently returns UNKNOWN.
    Instead, ti should return the original mask or information is lost.
    
            * value-range.cc (irange::get_bitmask): Return original mask if
            result is unknown.
            (assert_snap_result): New.
            (test_irange_snap_bounds): New.
            (range_tests_misc): Call test_irange_snap_bounds.

Diff:
---
 gcc/value-range.cc | 117 ++++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 116 insertions(+), 1 deletion(-)

diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index 23a5c66ed5e3..85c1e26287e9 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -2513,7 +2513,13 @@ irange::get_bitmask () const
   // See also the note in irange_bitmask::intersect.
   irange_bitmask bm (type (), lower_bound (), upper_bound ());
   if (!m_bitmask.unknown_p ())
-    bm.intersect (m_bitmask);
+    {
+      bm.intersect (m_bitmask);
+      // If the new intersection is unknown, it means there are inconstent
+      // bits, so simply return the original bitmask.
+      if (bm.unknown_p ())
+       return m_bitmask;
+    }
   return bm;
 }
 
@@ -2879,6 +2885,112 @@ range_tests_strict_enum ()
   ASSERT_FALSE (ir1.varying_p ());
 }
 
+// Test that range bounds are "snapped" to where they are expected to be.
+
+static void
+assert_snap_result (int lb_val, int ub_val,
+                   int expected_lb, int expected_ub,
+                   unsigned mask_val, unsigned value_val,
+                   tree type)
+{
+  wide_int lb = wi::shwi (lb_val, TYPE_PRECISION (type));
+  wide_int ub = wi::shwi (ub_val, TYPE_PRECISION (type));
+  wide_int new_lb, new_ub;
+
+  irange_bitmask bm (wi::uhwi (value_val, TYPE_PRECISION (type)),
+                    wi::uhwi (mask_val, TYPE_PRECISION (type)));
+
+  int_range_max r (type);
+  r.set (type, lb, ub);
+  r.update_bitmask (bm);
+
+  if (TYPE_SIGN (type) == SIGNED && expected_ub < expected_lb)
+    gcc_checking_assert (r.undefined_p ());
+  else if (TYPE_SIGN (type) == UNSIGNED
+          && ((unsigned)expected_ub < (unsigned)expected_lb))
+    gcc_checking_assert (r.undefined_p ());
+  else
+    {
+      gcc_checking_assert (wi::eq_p (r.lower_bound (),
+                                    wi::shwi (expected_lb,
+                                              TYPE_PRECISION (type))));
+      gcc_checking_assert (wi::eq_p (r.upper_bound (),
+                                    wi::shwi (expected_ub,
+                                              TYPE_PRECISION (type))));
+    }
+}
+
+
+// Run a selection of tests that confirm, bounds are snapped as expected.
+// We only test individual pairs, multiple pairs use the same snapping
+// routine as single pairs.
+
+static void
+test_irange_snap_bounds ()
+{
+  tree u32 = unsigned_type_node;
+  tree s32 = integer_type_node;
+  tree s8 = build_nonstandard_integer_type (8, /*unsigned=*/ 0);
+  tree s1 = build_nonstandard_integer_type (1, /*unsigned=*/ 0);
+  tree u1 = build_nonstandard_integer_type (1, /*unsigned=*/ 1);
+
+  // Basic aligned range: even-only
+  assert_snap_result (5, 15, 6, 14, 0xFFFFFFFE, 0x0, u32);
+  // Singleton that doesn't match mask: undefined.
+  assert_snap_result (7, 7, 1, 0, 0xFFFFFFFE, 0x0, u32);
+  // 8-bit signed char, mask 0xF0 (i.e. step of 16).
+  assert_snap_result (-100, 100, -96, 96, 0xF0, 0x00, s8);
+  // Already aligned range: no change.
+  assert_snap_result (0, 240, 0, 240, 0xF0, 0x00, u32);
+  // Negative range, step 16 alignment (s32).
+  assert_snap_result (-123, -17, -112, -32, 0xFFFFFFF0, 0x00, s32);
+  // Negative range, step 16 alignment (trailing-zero aligned mask).
+  assert_snap_result (-123, -17, -112, -32, 0xFFFFFFF0, 0x00, s32);
+  // s8, 16-alignment mask, value = 0 (valid).
+  assert_snap_result (-50, 10, -48, 0, 0xF0, 0x00, s8);
+  // No values in range [-3,2] match alignment except 0.
+  assert_snap_result (-3, 2, 0, 0, 0xF8, 0x00, s8);
+  // No values in range [-3,2] match alignment — undefined.
+  assert_snap_result (-3, 2, 1, 0, 0xF8, 0x04, s8);
+  // Already aligned range: no change.
+  assert_snap_result (0, 240, 0, 240, 0xF0, 0x00, s32);
+  // 1-bit signed: only -1 allowed (0b1).
+  assert_snap_result (-1, 0, -1, -1, 0x00, 0x01, s1);
+  // 1-bit signed: only 0 allowed (0b0).
+  assert_snap_result (-1, 0, 0, 0, 0x00, 0x00, s1);
+  // 1-bit signed: no match (invalid case).
+  assert_snap_result (-1, -1, 1, 0, 0x00, 0x00, s1);
+  // 1-bit signed: no match (invalid case).
+  assert_snap_result (0, 0, 1, 0, 0x00, 0x01, s1);
+  // 1-bit unsigned: only 1 allowed.
+  assert_snap_result (0, 1, 1, 1, 0x00, 0x01, u1);
+  // 1-bit unsigned: only 0 allowed.
+  assert_snap_result (0, 1, 0, 0, 0x00, 0x00, u1);
+  // 1-bit unsigned: no match (invalid case).
+  assert_snap_result (1, 1, 1, 0, 0x00, 0x00, u1);
+  // 1-bit unsigned: no match (invalid case).
+  assert_snap_result (0, 0, 1, 0, 0x00, 0x01, u1);
+  // Unsigned: Near overflow, even alignment.
+  assert_snap_result (UINT_MAX - 6, UINT_MAX, UINT_MAX - 5, UINT_MAX - 1,
+                     0xFFFFFFFE, 0x00, u32);
+  // Unsigned: Wraparound-like range — no valid snapped values.
+  assert_snap_result (UINT_MAX - 5, UINT_MAX, 1, 0, 0xFFFFFFF0, 0x00, u32);
+  // Signed: Near INT_MAX, 8-aligned.
+  assert_snap_result (INT_MAX - 18, INT_MAX, INT_MAX - 15, INT_MAX - 7,
+                     0xFFFFFFF8, 0x00, s32);
+  // Signed: Near INT_MIN, 16-aligned.
+  assert_snap_result (INT_MIN, INT_MIN + 30, INT_MIN, INT_MIN + 16,
+                     0xFFFFFFF0, 0x00, s32);
+  // Signed: Full domain, 4-aligned.
+  assert_snap_result (-128, 127, -128, 124, 0xFC, 0x00, s8);
+  // Singleton at INT_MIN that doesn’t match alignment — undefined
+  assert_snap_result (INT_MIN, INT_MIN, 1, 0, 0xFFFFFFFE, 0x01, s32);
+  // Range at INT_MIN that doesn’t match alignment — undefined.
+  assert_snap_result (INT_MIN, INT_MIN + 10, 1, 0, 0xFFFFFFF0, 0x0F, s32);
+  // Unsigned: Full domain, 256-aligned.
+  assert_snap_result (0, UINT_MAX, 0, UINT_MAX & ~255, 0xFFFFFF00, 0x00, u32);
+}
+
 static void
 range_tests_misc ()
 {
@@ -3150,6 +3262,9 @@ range_tests_nonzero_bits ()
   r0.set_zero (integer_type_node);
   r0.set_nonzero_bits (INT (1));
   ASSERT_TRUE (r0.zero_p ());
+
+  // Now test that range bounds are snapped to match bitmask alignments.
+  test_irange_snap_bounds ();
 }
 
 // Build an frange from string endpoints.

Reply via email to