On Tue, Aug 4, 2026 at 3:39 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> Two masked tests of the same value examine one set of bits, so one mask and
> one comparison do the whole job:
>
>   ((X & C1) == C2) & ((X & C3) == C4)
>     -> (X & (C1 | C3)) == (C2 | C4)   when C2 and C4 agree on the bits both
>                                       masks select
>     -> false                          when they disagree
>
> and the inclusive or of the two inequalities is the De Morgan dual.
>
>   int f (unsigned x) { return ((x & 5) == 5) & ((x & 24) == 24); }
>
> aarch64 -O2 before:
>
>         and     w1, w0, 5
>         and     w0, w0, 24
>         cmp     w1, 5
>         cset    w1, eq
>         cmp     w0, 24
>         cset    w0, eq
>         and     w0, w0, w1
>
> after:
>
>         and     w0, w0, 29
>         cmp     w0, 29
>         cset    w0, eq
>
> A test whose constant has a bit outside its own mask is decided already and
> is left to the rules that decide it.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?

No because Daniel already submitted a more complete patch and Richi
reviewed it here:
https://gcc.gnu.org/pipermail/gcc-patches/2026-May/716820.html

Also see starting at
https://gcc.gnu.org/bugzilla/show_bug.cgi?id=93131#c14 . Which is the
same review Richi gave earlier.

Thanks,
Andrea

> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd (((X & C1) == C2) &/| ((X & C3) == C4)): New
>         simplification.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/maskcmp-merge-1.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                  | 28 +++++++++++++++++++
>  .../gcc.dg/tree-ssa/maskcmp-merge-1.c         | 26 +++++++++++++++++
>  2 files changed, 54 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/maskcmp-merge-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 41acf869f9c..fa3856fc50e 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -3939,6 +3939,34 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>        { constant_boolean_node (true, type); })
>       ))))))
>
> +/* Merge two masked equality tests of the same value.
> +
> +     ((X & C1) == C2) & ((X & C3) == C4)
> +       -> (X & (C1 | C3)) == (C2 | C4)   when C2 and C4 agree on the bits
> +                                        both masks select
> +       -> false when they disagree
> +
> +   and the inclusive or of the two inequalities is the De Morgan dual.  Two
> +  masked tests of one value examine one set of bits, so one mask and one
> +  comparison do the whole job.  */
> +(for cmp (eq ne)
> +     bitop (bit_and bit_ior)
> + (simplify
> +  (bitop (cmp:s (bit_and:s @0 INTEGER_CST@1) INTEGER_CST@2)
> +        (cmp:s (bit_and:s @0 INTEGER_CST@3) INTEGER_CST@4))
> +  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0)))
> +   (with { wide_int m1 = wi::to_wide (@1), v1 = wi::to_wide (@2);
> +          wide_int m2 = wi::to_wide (@3), v2 = wi::to_wide (@4);
> +          tree t0 = TREE_TYPE (@0); }
> +    /* A test whose constant has a bit outside its mask is decided already
> +       and is left to the rules that decide it.  */
> +    (if (wi::bit_and_not (v1, m1) == 0
> +        && wi::bit_and_not (v2, m2) == 0)
> +     (if ((m1 & m2 & (v1 ^ v2)) == 0)
> +      (cmp (bit_and @0 { wide_int_to_tree (t0, m1 | m2); })
> +          { wide_int_to_tree (t0, v1 | v2); })
> +      { constant_boolean_node (cmp == NE_EXPR, type); }))))))
> +
>  /* Combine two vector comparisons against zero into one:
>       (A == 0) & (B == 0)  -->  (A | B) == 0
>       (A != 0) | (B != 0)  -->  (A | B) != 0
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/maskcmp-merge-1.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/maskcmp-merge-1.c
> new file mode 100644
> index 00000000000..9509afce27e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/maskcmp-merge-1.c
> @@ -0,0 +1,26 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +typedef unsigned int u;
> +
> +/* Two masked tests of one value examine one set of bits, so one mask and
> +   one comparison do the whole job.  */
> +
> +int a1 (u x) { return ((x & 5) == 5) & ((x & 24) == 24); }
> +int a2 (u x) { return ((x & 5) == 1) & ((x & 24) == 16); }
> +int a3 (u x) { return ((x & 5) == 0) & ((x & 24) == 24); }
> +int a4 (u x) { return ((x & 12) == 12) & ((x & 20) == 4); }
> +
> +/* The De Morgan dual.  */
> +int b1 (u x) { return ((x & 5) != 5) | ((x & 24) != 24); }
> +int b2 (u x) { return ((x & 5) != 1) | ((x & 24) != 16); }
> +
> +/* Bit 2 is selected by both masks and the two tests disagree on it, so the
> +   conjunction is false and the disjunction is true.  */
> +int c1 (u x) { return ((x & 6) == 6) & ((x & 12) == 8); }
> +int c2 (u x) { return ((x & 6) != 6) | ((x & 12) != 8); }
> +
> +/* { dg-final { scan-tree-dump-times " & 29" 5 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " & 28" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "return 0;" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-times "return 1;" 1 "optimized" } } */
> --
> 2.50.1 (Apple Git-155)
>

Reply via email to