When there are trailing 0's in the bitmask, I previously modified set_range_from_bitmask () to remove the lower positive ranges which do not match the value to help with some optimizations.   It helps with lower range comparisons and generally seemed to be a good thing.

This reworks it clean it up a bit and also provide the same functionality for the negative ranges of signed types using those bits.

The original version also didn't properly return true or false based on whether the range changed or not. This one does.

ie,  If the lower 4 bits are all 0:
  int [-INF, +INF] MASK 0xfffffff0 VALUE 0x0

currently becomes:
  int [-INF,  0][16, 2147483632] MASK 0xfffffff0 VALUE 0x0

and with this patch it is enhanced to also remove those negative values and is now:
  int [-INF,  -16][0, 0][16, 2147483632] MASK 0xfffffff0 VALUE 0x0

The test case I included with the patch goes all the way through optimization and never removed the call to dead. It might be somewhat artificial, but you never know where these things will show up after masking and casting.

Oh, and the change to tree-ssanames.cc is because, as you can see, a range with masks can require 3 ranges... and set_bitmask was using an int_range<2>, which caused some havoc with one of the existing testcases when we  were expecting more precision, and 2 sub-ranges wasn't cutting it if we happen to choose the wrong pair to combine/form.

bootstraps on x86_64-pc-linux-gnu with no regressions.   Pushed.

Andrew


From c40a4cc2d943d8572a62f21d3eb1d4171e51d5ac Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacl...@redhat.com>
Date: Thu, 8 May 2025 20:28:11 -0400
Subject: [PATCH] Remove negative ranges using trailing zero masks.

When there are trailing 0's in the bitmask, set_range_from_bitmask () removes
the lower positive ranges which do not match the value.  This reworks it to
provide the same functionailty for the negative ranges in signed types.
If the lower 4 bits are all 0:
  int [-INF, +INF] MASK 0xfffffff0 VALUE 0x0
becomes:
  int [-INF,  -16][0, 0][16, 2147483632] MASK 0xfffffff0 VALUE 0x0

	gcc/
	* tree-ssanames.cc (set_bitmask): Use int_range_max for temps.
	* value-range.cc (irange::set_range_from_bitmask): Handle all
	trailing zero values.

	gcc/testsuite/
	* gcc.dg/tree-ssa/vrp124.c: New.
---
 gcc/testsuite/gcc.dg/tree-ssa/vrp124.c | 31 ++++++++++++++
 gcc/tree-ssanames.cc                   |  2 +-
 gcc/value-range.cc                     | 57 +++++++++++++++++++-------
 3 files changed, 74 insertions(+), 16 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vrp124.c

diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vrp124.c b/gcc/testsuite/gcc.dg/tree-ssa/vrp124.c
new file mode 100644
index 00000000000..789b550f88d
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vrp124.c
@@ -0,0 +1,31 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+/* Test removal of trailing zero mask ranges from signed values. */
+/* Mask off the lower 4 bits of an integer. */
+#define MASK 0XF
+
+void dead (int c);
+void keep();
+
+/* A signed character should have a range something like : */
+/* int [-INF, -16][0, 0][16, 2147483632] MASK 0xfffffff0 VALUE 0x0 */
+
+int
+foo2 (int c)
+{
+  c = c & ~MASK;
+  if (c == 0)
+    return 0;
+  if (c > -16)
+    {
+      keep ();
+      if (c < 16)
+	dead (c);
+    }
+  if (c > (__INT_MAX__ & ~MASK))
+    dead (c);
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "dead" "evrp" } } */
diff --git a/gcc/tree-ssanames.cc b/gcc/tree-ssanames.cc
index de7b9b79f94..fd2abfe0745 100644
--- a/gcc/tree-ssanames.cc
+++ b/gcc/tree-ssanames.cc
@@ -488,7 +488,7 @@ set_bitmask (tree name, const wide_int &value, const wide_int &mask)
 {
   gcc_assert (!POINTER_TYPE_P (TREE_TYPE (name)));
 
-  int_range<2> r (TREE_TYPE (name));
+  int_range_max r (TREE_TYPE (name));
   r.update_bitmask (irange_bitmask (value, mask));
   set_range_info (name, r);
 }
diff --git a/gcc/value-range.cc b/gcc/value-range.cc
index a770b41b474..d2c14e7900d 100644
--- a/gcc/value-range.cc
+++ b/gcc/value-range.cc
@@ -2286,7 +2286,7 @@ irange::set_range_from_bitmask ()
       if (has_zero)
 	{
 	  int_range<2> zero;
-	  zero.set_zero (type ());
+	  zero.set_zero (m_type);
 	  union_ (zero);
 	}
       if (flag_checking)
@@ -2295,31 +2295,58 @@ irange::set_range_from_bitmask ()
     }
   else if (popcount == 0)
     {
-      set_zero (type ());
+      set_zero (m_type);
       return true;
     }
 
-  // If the mask doesn't have any trailing zero, return.
+  // If the mask doesn't have a trailing zero, theres nothing to filter.
   int z = wi::ctz (m_bitmask.mask ());
   if (!z)
     return false;
 
-  // Remove trailing ranges that this bitmask indicates can't exist.
-  int_range_max mask_range;
-  int prec = TYPE_PRECISION (type ());
-  wide_int ub = (wi::one (prec) << z) - 1;
-  mask_range = int_range<2> (type (), wi::zero (prec), ub);
+  int prec = TYPE_PRECISION (m_type);
+  wide_int value = m_bitmask.value ();
+  wide_int mask = m_bitmask.mask ();
 
-  // Then remove the specific value these bits contain from the range.
-  wide_int value = m_bitmask.value () & ub;
-  mask_range.intersect (int_range<2> (type (), value, value, VR_ANTI_RANGE));
+  // Remove the [0, X] values which the trailing-zero mask rules out.
+  // For example, if z == 4, the mask is 0xFFF0, and the lowest 4 bits
+  // define the range [0, 15]. Only one of which (value & low_mask) is allowed.
+  wide_int ub = (wi::one (prec) << z) - 1;  // Upper bound of affected range.
+  int_range_max mask_range (m_type, wi::zero (prec), ub);
 
-  // Inverting produces a list of ranges which can be valid.
+  // Remove the one valid value from the excluded range and form an anti-range.
+  wide_int allow = value & ub;
+  mask_range.intersect (int_range<2> (m_type, allow, allow, VR_ANTI_RANGE));
+
+  // Invert it to get the allowed values and intersect it with the main range.
   mask_range.invert ();
+  bool changed = intersect (mask_range);
 
-  // And finally select R from only those valid values.
-  intersect (mask_range);
-  return true;
+  // Now handle the rest of the domain — the upper side for positive values,
+  // or [-X, -1] for signed negatives.
+  // Compute the maximum value representable under the mask/value constraint.
+  ub = mask | value;
+
+  // If value is non-negative, adjust the upper limit to remove values above
+  // UB that conflict with known fixed bits.
+  if (TYPE_SIGN (m_type) == UNSIGNED || wi::clz (ub) > 0)
+    mask_range = int_range<1> (m_type, wi::zero (prec), ub);
+  else
+    {
+      // For signed negative values, find the lowest value with trailing zeros.
+      // This forms a range such as [-512, -1] for z=9.
+      wide_int lb = -(wi::one (prec) << z);
+      mask_range = int_range<2> (m_type, lb, wi::minus_one (prec));
+
+      // Remove the one allowed value from that set.
+      allow = value | lb;
+      mask_range.intersect (int_range<2> (m_type, allow, allow, VR_ANTI_RANGE));
+      mask_range.invert ();
+    }
+
+  // Make sure we call intersect, so do it first.
+  changed = intersect (mask_range) | changed;
+  return changed;
 }
 
 void
-- 
2.45.0

Reply via email to