This patch improves op1_range for bitwise and operations. Previously we did some basic attempts to determine a range but they often we not very precise.

This leverages the previous changes and recognizes that any known bit on the LHS of an AND that falls within the MASK must also be known in operand1.
   ie      [5,7] = op & 7

[5, 7] has binary patterns 101, 110, 111 .

In order to produce that result, we know that bit 100 must always be set in 'op' as well.   The final result now produces
   op =  [-INF, -1][5, +INF] MASK 0xfffffffffffffffb VALUE 0x4

This patch resolves the PR by successfully tracking the bit through to the AND operation.

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

Andrew
From ac55655ce45a237a6a01e0cce50211841603c2ec Mon Sep 17 00:00:00 2001
From: Andrew MacLeod <amacl...@redhat.com>
Date: Wed, 14 May 2025 11:32:58 -0400
Subject: [PATCH 4/4] Enhance bitwise_and::op1_range

Any known bits from the LHS range can be used to specify known bits in
the non-mask operand.

	PR tree-optimization/116546
	gcc/
	* range-op.cc (operator_bitwise_and::op1_range): Utilize bitmask
	from the LHS to improve op1's bitmask.

	gcc/testsuite/
	* gcc.dg/pr116546.c: New.
---
 gcc/range-op.cc                 | 22 +++++++++++++++-
 gcc/testsuite/gcc.dg/pr116546.c | 46 +++++++++++++++++++++++++++++++++
 2 files changed, 67 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/pr116546.c

diff --git a/gcc/range-op.cc b/gcc/range-op.cc
index 06d357f5199..e2b9c82bc7b 100644
--- a/gcc/range-op.cc
+++ b/gcc/range-op.cc
@@ -3716,14 +3716,34 @@ operator_bitwise_and::op1_range (irange &r, tree type,
       return true;
     }
 
+  if (!op2.singleton_p (mask))
+    return true;
+
   // For 0 = op1 & MASK, op1 is ~MASK.
-  if (lhs.zero_p () && op2.singleton_p ())
+  if (lhs.zero_p ())
     {
       wide_int nz = wi::bit_not (op2.get_nonzero_bits ());
       int_range<2> tmp (type);
       tmp.set_nonzero_bits (nz);
       r.intersect (tmp);
     }
+
+  irange_bitmask lhs_bm = lhs.get_bitmask ();
+  // given   [5,7]  mask 0x3 value 0x4 =  N &  [7, 7] mask 0x0 value 0x7
+  // Nothing is known about the bits not specified in the mask value (op2),
+  //  Start with the mask, 1's will occur where values were masked.
+  wide_int op1_mask = ~mask;
+  // Any bits that are unknown on the LHS are also unknown in op1,
+  // so union the current mask with the LHS mask.
+  op1_mask |= lhs_bm.mask ();
+  // The resulting zeros correspond to known bits in the LHS mask, and
+  // the LHS value should tell us what they are.  Mask off any
+  // extraneous values thats are not convered by the mask.
+  wide_int op1_value = lhs_bm.value () & ~op1_mask;
+  irange_bitmask op1_bm (op1_value, op1_mask);
+  // INtersect this mask with anything already known about the value.
+  op1_bm.intersect (r.get_bitmask ());
+  r.update_bitmask (op1_bm);
   return true;
 }
 
diff --git a/gcc/testsuite/gcc.dg/pr116546.c b/gcc/testsuite/gcc.dg/pr116546.c
new file mode 100644
index 00000000000..b82dc27f452
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/pr116546.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-evrp" } */
+
+extern long foo (void);
+extern long bar (void);
+
+long
+test1 (long n)
+{
+  n &= 7;
+  if (n == 4) {
+    if (n & 4)
+      return foo ();
+    else
+      return bar ();
+  }
+  return 0;
+}
+
+long
+test2 (long n)
+{
+  n &= 7;
+  if (n > 4) {
+    if (n & 4)
+      return foo ();
+    else
+      return bar ();
+  }
+  return 0;
+}
+
+long
+test3 (long n)
+{
+  n &= 7;
+  if (n >= 4) {
+    if (n & 4)
+      return foo ();
+    else
+      return bar ();
+  }
+  return 0;
+}
+
+/* { dg-final { scan-tree-dump-not "bar" "evrp" } } */
-- 
2.45.0

Reply via email to