Re: [PATCH] match.pd: sink a unary operation through a vector permute

Andrea Pinski <[email protected]> Tue, 4 Aug 2026 18:30:27 -0700
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcANZbfYcSvi3_86jKrbKhEQroYFtGQ3-Fa8zzCmDS-M0A@mail.gmail.com>
On Tue, Aug 4, 2026 at 2:50=E2=80=AFAM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> A permute of two results of the same unary operation needs only one such
> operation, applied to the permuted vector.  The selector and both vector
> types are unchanged, so the permute itself costs the same, and the lanes
> that the permute drops are no longer computed at all.
>
>   typedef float v4f __attribute__((vector_size (16)));
>   typedef int   v4i __attribute__((vector_size (16)));
>   v4f f (v4f a, v4f b)
>   {
>     v4i m =3D { 3, 6, 1, 4 };
>     return __builtin_shuffle (-a, -b, m);
>   }
>
> aarch64 -O2 before:
>
>         fneg    v0.4s, v0.4s
>         adrp    x0, .LANCHOR0
>         fneg    v1.4s, v1.4s
>         ldr     q29, [x0, #:lo12:.LANCHOR0]
>         mov     v30.16b, v0.16b
>         mov     v31.16b, v1.16b
>         tbl     v0.16b, {v30.16b - v31.16b}, v29.16b
>
> after:
>
>         adrp    x0, .LANCHOR0
>         ldr     q31, [x0, #:lo12:.LANCHOR0]
>         tbl     v0.16b, {v0.16b - v1.16b}, v31.16b
>         fneg    v0.4s, v0.4s
>
> The two register-pair moves also go away, because the permute can now
> take the incoming argument registers directly.
>
> Bootstrapped and tested on aarch64-none-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd (vec_perm of two identical unary operations): New
>         simplification sinking the operation through the permute.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/vec-perm-unary-1.c: New test.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                  |  9 ++++
>  .../gcc.dg/tree-ssa/vec-perm-unary-1.c        | 44 +++++++++++++++++++
>  2 files changed, 53 insertions(+)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index ec00347a968..4fca75d6fb6 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -12532,6 +12532,15 @@ and,
>        && !TYPE_OVERFLOW_SANITIZED (TREE_TYPE (@0)))
>    (convert @0)))
>
> +/* VEC_PERM_EXPR of two results of the same unary operation needs only o=
ne
> +   such operation, applied to the permuted vector.  The selector and bot=
h
> +   vector types are unchanged, so the permute itself costs the same, and
> +   the lanes that the permute drops are no longer computed.  */
> +(for uop (negate bit_not abs absu)
> + (simplify
> +  (vec_perm (uop:s @0) (uop:s @1) @2)
> +  (uop (vec_perm @0 @1 @2))))

Hmm, abs and negate might introduce new undefined behavior here for
the signed integer case.
(abs and negate is fine for fp since they don't trap).
So this needs more handling for that case. See the fp handling.
Note I think the already existing pattern:
```
(for op (plus minus mult bit_and bit_ior bit_xor
         lshift rshift)
 (simplify
  (op (vec_perm @0 @0 @2) (vec_perm @1 @1 @2))
   (if (VECTOR_INTEGER_TYPE_P (type))
    (vec_perm (op@3 @0 @1) @3 @2))))
```
Has the same issue of maybe introducing undefined behavior.
This means we should fix that too.

> +
>  /* Optimize
>     c1 =3D VEC_PERM_EXPR (a, a, mask)
>     c2 =3D VEC_PERM_EXPR (b, b, mask)
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c b/gcc/tests=
uite/gcc.dg/tree-ssa/vec-perm-unary-1.c
> new file mode 100644
> index 00000000000..7ff2498a6ce
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vec-perm-unary-1.c
> @@ -0,0 +1,44 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* A permute of two results of the same unary operation needs only one s=
uch
> +   operation, applied to the permuted vector.  */
> +
> +typedef float v4f __attribute__((vector_size (16)));
> +typedef int   v4i __attribute__((vector_size (16)));
> +
> +v4f
> +f (v4f a, v4f b)
> +{
> +  v4i m =3D { 3, 6, 1, 4 };
> +  return __builtin_shuffle (-a, -b, m);
> +}
> +
> +v4i
> +g (v4i a, v4i b)
> +{
> +  v4i m =3D { 0, 4, 1, 5 };
> +  return __builtin_shuffle (~a, ~b, m);
> +}
> +
> +v4i
> +h (v4i a, v4i b)
> +{
> +  v4i m =3D { 0, 4, 1, 5 };
> +  return __builtin_shuffle (-a, -b, m);
> +}
> +
> +/* The absolute value written as a sign mask folds to ABS_EXPR first, so
> +   this exercises the abs case of the same rule.  */
> +v4i
> +k (v4i a, v4i b)
> +{
> +  v4i m =3D { 0, 4, 1, 5 };
> +  v4i sa =3D a >> 31, sb =3D b >> 31;
> +  return __builtin_shuffle ((a ^ sa) - sa, (b ^ sb) - sb, m);
> +}
> +
> +/* { dg-final { scan-tree-dump-times "VEC_PERM_EXPR" 4 "optimized" } } *=
/
> +/* { dg-final { scan-tree-dump-times "ABS_EXPR" 1 "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " =3D -a" "optimized" } } */
> +/* { dg-final { scan-tree-dump-not " =3D ~a" "optimized" } } */
> --
> 2.50.1 (Apple Git-155)
>