Re: [PATCH] match: drop a mask that the following right shift discards

Andrea Pinski <[email protected]> Tue, 4 Aug 2026 15:47:09 -0700
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcA7bDMv+UyDoFOVEgcNad=Jk2b7kOpYrNzGJtXv_XorfA@mail.gmail.com>
On Tue, Aug 4, 2026 at 3:48=E2=80=AFAM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> (X & (-1 << Y)) >> Y clears the low Y bits and then shifts them out, so
> the mask is redundant.  This drops three operations, and it turns up in
> code that aligns a value down before scaling it.
>
>   unsigned f (unsigned x, int y) { return (x & (~0u << y)) >> y; }

We can do a generic version for this rather than depending on -1 or any val=
ue.
That is:
```
unsigned g0 (unsigned x, int y, unsigned a)
{
    return (x & (a << y)) >> y;
}
```
can be convert into:
```
unsigned g (unsigned x, int y, unsigned a)
{
    return (x >> y) & a;
}
```

When a and x are unsigned or a is all ones, this can be done.

For signed types, x needs to be non-negative, y needs to be constant
and a needs to have bit y not being set (that is (non_zero(a)>>y)&1 =3D=3D
0).
e.g.:
```
int h0 (int x, int y, unsigned short a)
{
    return (x & (a << 3)) >> 3;
}
```
can be translated into:
```
int h1 (int x, int y, unsigned short a)
{
    return (x) >> 3 & a;
}
```


>
> aarch64 -O2:
>
>   before                          after
>     mov   w2, -1                    lsr   w0, w0, w1
>     lsl   w2, w2, w1
>     and   w0, w2, w0
>     lsr   w0, w0, w1
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd ((X & (-1 << Y)) >> Y): New simplification.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/shift-mask-1.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                 |  5 +++++
>  gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c | 13 +++++++++++++
>  2 files changed, 18 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index a97158bf062..ac35442f8ba 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -5261,6 +5261,11 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>                         - TYPE_PRECISION (TREE_TYPE (@2)))))
>    (bit_and (convert @0) (lshift { build_minus_one_cst (type); } @1))))
>
> +/* (X & (-1 << Y)) >> Y -> X >> Y.  The mask only clears bits that the
> +   shift discards.  */
> +(simplify
> + (rshift (bit_and:c @0 (lshift integer_all_onesp @1)) @1)
> + (rshift @0 @1))
>  /* (X op Y) >> C -> X >> C when every set bit of Y lies below bit C.
>     Neither an inclusive nor an exclusive or can carry into the bits the
>     shift keeps, so Y contributes nothing to the result.  */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c b/gcc/testsuite=
/gcc.dg/tree-ssa/shift-mask-1.c
> new file mode 100644
> index 00000000000..16d129d1f29
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/shift-mask-1.c
> @@ -0,0 +1,13 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* Masking off the bits that the following right shift discards is a no-=
op.  */
> +
> +unsigned int f1 (unsigned int x, int y) { return (x & (~0u << y)) >> y; =
}
> +int f2 (int x, int y) { return (x & (~0 << y)) >> y; }
> +unsigned long f3 (unsigned long x, int y) { return (x & (~0ul << y)) >> =
y; }
> +unsigned int f4 (unsigned int x, int y) { return ((~0u << y) & x) >> y; =
}
> +
> +/* { dg-final { scan-tree-dump-not " & " "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " << " "optimized" } } */
> +/* { dg-final { scan-tree-dump-times " >> " 4 "optimized" } } */
> --
> 2.50.1 (Apple Git-155)
>