Re: [PATCH] match.pd: merge two masked equality tests of one value

Andrea Pinski <[email protected]> Tue, 4 Aug 2026 13:19:09 -0700
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcB_r5hmkiO7TPANxPgF63mKY6F0X3RDZUFcOeU3kP3tkA@mail.gmail.com>
On Tue, Aug 4, 2026 at 3:39=E2=80=AFAM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> Two masked tests of the same value examine one set of bits, so one mask a=
nd
> one comparison do the whole job:
>
>   ((X & C1) =3D=3D C2) & ((X & C3) =3D=3D C4)
>     -> (X & (C1 | C3)) =3D=3D (C2 | C4)   when C2 and C4 agree on the bit=
s 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) =3D=3D 5) & ((x & 24) =3D=3D 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 a=
nd
> 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=3D93131#c14 . Which is the
same review Richi gave earlier.

Thanks,
Andrea

> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd (((X & C1) =3D=3D C2) &/| ((X & C3) =3D=3D 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) =3D=3D C2) & ((X & C3) =3D=3D C4)
> +       -> (X & (C1 | C3)) =3D=3D (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 =3D wi::to_wide (@1), v1 =3D wi::to_wide (@2);
> +          wide_int m2 =3D wi::to_wide (@3), v2 =3D wi::to_wide (@4);
> +          tree t0 =3D TREE_TYPE (@0); }
> +    /* A test whose constant has a bit outside its mask is decided alrea=
dy
> +       and is left to the rules that decide it.  */
> +    (if (wi::bit_and_not (v1, m1) =3D=3D 0
> +        && wi::bit_and_not (v2, m2) =3D=3D 0)
> +     (if ((m1 & m2 & (v1 ^ v2)) =3D=3D 0)
> +      (cmp (bit_and @0 { wide_int_to_tree (t0, m1 | m2); })
> +          { wide_int_to_tree (t0, v1 | v2); })
> +      { constant_boolean_node (cmp =3D=3D NE_EXPR, type); }))))))
> +
>  /* Combine two vector comparisons against zero into one:
>       (A =3D=3D 0) & (B =3D=3D 0)  -->  (A | B) =3D=3D 0
>       (A !=3D 0) | (B !=3D 0)  -->  (A | B) !=3D 0
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/maskcmp-merge-1.c b/gcc/testsu=
ite/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 an=
d
> +   one comparison do the whole job.  */
> +
> +int a1 (u x) { return ((x & 5) =3D=3D 5) & ((x & 24) =3D=3D 24); }
> +int a2 (u x) { return ((x & 5) =3D=3D 1) & ((x & 24) =3D=3D 16); }
> +int a3 (u x) { return ((x & 5) =3D=3D 0) & ((x & 24) =3D=3D 24); }
> +int a4 (u x) { return ((x & 12) =3D=3D 12) & ((x & 20) =3D=3D 4); }
> +
> +/* The De Morgan dual.  */
> +int b1 (u x) { return ((x & 5) !=3D 5) | ((x & 24) !=3D 24); }
> +int b2 (u x) { return ((x & 5) !=3D 1) | ((x & 24) !=3D 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) =3D=3D 6) & ((x & 12) =3D=3D 8); }
> +int c2 (u x) { return ((x & 6) !=3D 6) | ((x & 12) !=3D 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)
>