[PATCH] match.pd: add the inclusive or dual of the masked comparison rule

<[email protected]> Tue, 4 Aug 2026 12:03:43 +0200
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
From: Kyrylo Tkachov <[email protected]>

(X & C) == (Y & C) already folds to ((X ^ Y) & C) == 0.  The dual was
missing: two values ored with the same constant agree on the bits that
constant forces, so only the bits outside it can differ.

  int f (unsigned char a, unsigned char b) { return (a | 32) == (b | 32); }

aarch64 -O2 before:

	orr	w1, w1, 32
	orr	w0, w0, 32
	cmp	w1, w0
	cset	w0, eq

after:

	eor	w0, w0, w1
	tst	w0, 223
	cset	w0, eq

This is the case insensitive ASCII comparison, and any equality of two
values on a masked field.  The rule sits directly beside its bit_and dual,
inside the same eq and ne iterator.

Bootstrapped and tested on aarch64-none-linux-gnu.
Ok for trunk?
Thanks,
Kyrill

gcc/ChangeLog:

	* match.pd ((X | C) ==/!= (Y | C)): New simplification.

gcc/testsuite/ChangeLog:

	* gcc.dg/tree-ssa/ior-cmp-xor-1.c: New test.

Signed-off-by: Kyrylo Tkachov <[email protected]>
---
 gcc/match.pd                                  | 11 ++++++++++-
 gcc/testsuite/gcc.dg/tree-ssa/ior-cmp-xor-1.c | 14 ++++++++++++++
 2 files changed, 24 insertions(+), 1 deletion(-)
 create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/ior-cmp-xor-1.c

diff --git a/gcc/match.pd b/gcc/match.pd
index 85fea9fa18f..d5b5924d41f 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -8555,7 +8555,16 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  /* (X & C) op (Y & C) into (X ^ Y) & C op 0.  */
  (simplify
   (cmp (bit_and:cs @0 @2) (bit_and:cs @1 @2))
-  (cmp (bit_and (bit_xor @0 @1) @2) { build_zero_cst (TREE_TYPE (@2)); })))
+  (cmp (bit_and (bit_xor @0 @1) @2)
+       { build_zero_cst (TREE_TYPE (@2)); }))
+
+ /* (X | C) op (Y | C) into (X ^ Y) & ~C op 0, the dual of the rule above.
+    The two values agree on the bits C forces, so only the bits outside C
+    can differ.  */
+ (simplify
+  (cmp (bit_ior:cs @0 @2) (bit_ior:cs @1 @2))
+  (cmp (bit_and (bit_xor @0 @1) (bit_not! @2))
+       { build_zero_cst (TREE_TYPE (@2)); })))
 
 /* (X < 0) != (Y < 0) into (X ^ Y) < 0.
    (X >= 0) != (Y >= 0) into (X ^ Y) < 0.
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/ior-cmp-xor-1.c b/gcc/testsuite/gcc.dg/tree-ssa/ior-cmp-xor-1.c
new file mode 100644
index 00000000000..0abd54049c3
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/ior-cmp-xor-1.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+
+/* Two values ored with the same constant agree on the bits that constant
+   forces, so only the bits outside it can differ.  The dual of the existing
+   (X & C) == (Y & C) rule.  */
+
+int f1 (unsigned char a, unsigned char b) { return (a | 32) == (b | 32); }
+int f2 (unsigned a, unsigned b) { return (a | 32) != (b | 32); }
+int f3 (unsigned a, unsigned b) { return (a | 0xff000000u) == (b | 0xff000000u); }
+
+/* { dg-final { scan-tree-dump-not " \\| 32" "optimized" } } */
+/* { dg-final { scan-tree-dump-not " \\| 4278190080" "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\^ " 3 "optimized" } } */
-- 
2.50.1 (Apple Git-155)