match.pd: add the inclusive or dual of the masked comparison rule
Checks
| Context |
Check |
Description |
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-arm-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_simplebootstrap_build--master-aarch64-bootstrap |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-arm |
success
|
Build passed
|
| linaro-tcwg-bot/tcwg_gcc_build--master-aarch64 |
success
|
Build passed
|
Commit Message
From: Kyrylo Tkachov <ktkachov@nvidia.com>
(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 <ktkachov@nvidia.com>
---
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
Comments
On 8/4/2026 4:03 AM, ktkachov@nvidia.com wrote:
> From: Kyrylo Tkachov <ktkachov@nvidia.com>
>
> (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 <ktkachov@nvidia.com>
OK.
Jeff
@@ -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.
new file mode 100644
@@ -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" } } */