Re: [PATCH 3/4] match.pd: canonicalize subtraction of an integral vector constant

Andrea Pinski <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CALvbMcCeqraLNeSAoRt9cHHu74Qq1PZeGCpxCzAiZ3xOy83Rig@mail.gmail.com>
On Thu, Aug 20, 2026 at 4:57 AM <[email protected]> wrote:
>
> From: Kyrylo Tkachov <[email protected]>
>
> The A - B -> A + (-B) rule uses negate_expr_p to decide whether B can be
> negated cheaply.  The predicate rejects non-wrapping integral vector
> constants, even when every encoded element can be negated without overflow.
> This leaves vector subtraction in a noncanonical form and can hide an
> encodable constant from target expansion.
>
>   typedef int v4si __attribute__ ((vector_size (16)));
>
>   v4si
>   f (v4si x)
>   {
>     return x - (v4si) { -16711935, -16711935, -16711935, -16711935 };
>   }
>
> aarch64 -O2 before:
>
>   f:
>           adrp    x0, .LC0
>           ldr     q31, [x0, #:lo12:.LC0]
>           sub     v0.4s, v0.4s, v31.4s
>           ret
>
> The function also needs a 16-byte constant in .rodata.
>
> After:
>
>   f:
>           movi    v31.8h, 0xff
>           add     v0.4s, v0.4s, v31.4s
>           ret
>
> Accept a non-stepped integral vector constant when every encoded element can
> be negated without overflow.  A stepped encoding can extrapolate an unencoded
> signed minimum, so keep it in subtraction form.  Unsigned and -fwrapv vectors
> already wrap and do not need an element scan.  Use vector-aware predicates
> directly.  Keep the integral type check before TYPE_OVERFLOW_WRAPS because
> fixed-point vectors can reach the predicate, but TYPE_OVERFLOW_WRAPS does not
> accept them.  Keep a signed minimum element and an overflow-sanitized
> subtraction.
>
> The canonical form remains visible before dead-code elimination in one
> existing forwprop dump.  Adjust its addition count.
>
> Bootstrapped and tested on aarch64-none-linux-gnu and x86_64-pc-linux-gnu.
> Ok for trunk?
> Thanks,
> Kyrill
>
> gcc/ChangeLog:
>
>         * match.pd (negate_expr_p): Handle non-wrapping integral vector
>         constants.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/forwprop-27.c: Adjust the expected addition count.
>         * gcc.dg/tree-ssa/vector-sub-const-1.c: New test.
>         * gcc.dg/tree-ssa/vector-sub-const-2.c: Likewise.
>
> Signed-off-by: Kyrylo Tkachov <[email protected]>
> ---
>  gcc/match.pd                                  | 27 ++++++++++--
>  gcc/testsuite/gcc.dg/tree-ssa/forwprop-27.c   |  2 +-
>  .../gcc.dg/tree-ssa/vector-sub-const-1.c      | 43 +++++++++++++++++++
>  .../gcc.dg/tree-ssa/vector-sub-const-2.c      | 12 ++++++
>  4 files changed, 80 insertions(+), 4 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-1.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-2.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index eae8717bcfe..e41f4c19bf4 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -2428,11 +2428,32 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>  (match negate_expr_p
>   REAL_CST
>   (if (REAL_VALUE_NEGATIVE (TREE_REAL_CST (t)))))
> -/* VECTOR_CST handling of non-wrapping types would recurse in unsupported
> -   ways.  */
> +/* A non-stepped, non-wrapping integral vector constant can be negated when
> +   none of its encoded elements is the minimum value.  */
>  (match negate_expr_p
>   VECTOR_CST
> - (if (FLOAT_TYPE_P (TREE_TYPE (type)) || TYPE_OVERFLOW_WRAPS (type))))
> + (with
> +  {
> +    bool negatable = (FLOAT_TYPE_P (type)
> +                     || (ANY_INTEGRAL_TYPE_P (type)
> +                         && TYPE_OVERFLOW_WRAPS (type)));

This can be just `FLOAT_TYPE_P (type) || TYPE_OVERFLOW_WRAPS (type)`
like the corresponding one in fold-const.

> +    if (!negatable
> +       && ANY_INTEGRAL_TYPE_P (type)

You don't need the ANY_INTEGRAL_TYPE_P check for the same reason as above.

Otherwise ok.

> +       && !TYPE_OVERFLOW_SANITIZED (type)
> +       && !VECTOR_CST_STEPPED_P (t))
> +      {
> +       negatable = true;
> +       for (unsigned int i = 0; i < vector_cst_encoded_nelts (t); ++i)
> +         if (TREE_CODE (VECTOR_CST_ENCODED_ELT (t, i)) != INTEGER_CST
> +             || !may_negate_without_overflow_p
> +                  (VECTOR_CST_ENCODED_ELT (t, i)))
> +           {
> +             negatable = false;
> +             break;
> +           }
> +      }
> +  }
> +  (if (negatable))))
>  (match negate_expr_p
>   (minus @0 @1)
>   (if ((ANY_INTEGRAL_TYPE_P (type) && TYPE_OVERFLOW_WRAPS (type))
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-27.c b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-27.c
> index 6c71a4fc81c..29d39c67c79 100644
> --- a/gcc/testsuite/gcc.dg/tree-ssa/forwprop-27.c
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/forwprop-27.c
> @@ -33,7 +33,7 @@ void i (V *v1, V *v2){
>    *v2 = (c1-*v2)+c2;
>  }
>
> -/* { dg-final { scan-tree-dump-times "\\\+" 1 "forwprop1"} } */
> +/* { dg-final { scan-tree-dump-times "\\\+" 2 "forwprop1"} } */
>  /* { dg-final { scan-tree-dump "{ 0, 4 }" "forwprop1"} } */
>  /* { dg-final { scan-tree-dump "{ 37, -5 }" "forwprop1"} } */
>  /* { dg-final { scan-tree-dump "{ 27, 23 }" "forwprop1"} } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-1.c
> new file mode 100644
> index 00000000000..56e1c146d70
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-1.c
> @@ -0,0 +1,43 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -Wno-psabi -fdump-tree-forwprop1" } */
> +
> +typedef __INT32_TYPE__ v4si __attribute__ ((vector_size (16)));
> +typedef __UINT32_TYPE__ v4ui __attribute__ ((vector_size (16)));
> +
> +v4si
> +sub_splat (v4si x)
> +{
> +  return x - (v4si) { 1, 1, 1, 1 };
> +}
> +
> +v4si
> +sub_mixed (v4si x)
> +{
> +  return x - (v4si) { 1, -2, 3, -4 };
> +}
> +
> +v4si
> +sub_encodable (v4si x)
> +{
> +  return x - (v4si) { -16711935, -16711935, -16711935, -16711935 };
> +}
> +
> +v4si
> +keep_min (v4si x)
> +{
> +  return x - (v4si) { -__INT32_MAX__ - 1, 1, 1, 1 };
> +}
> +
> +v4si
> +keep_stepped_min (v4si x)
> +{
> +  return x - (v4si) ((v4ui) { 2147483644u, 2147483645u,
> +                              2147483646u, 2147483647u }
> +                     + (v4ui) { 1, 1, 1, 1 });
> +}
> +
> +/* { dg-final { scan-tree-dump-times " \\+ \\{ -1, -1, -1, -1 \\}" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " \\+ \\{ -1, 2, -3, 4 \\}" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " \\+ \\{ 16711935, 16711935, 16711935, 16711935 \\}" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " - \\{ -2147483648, 1, 1, 1 \\}" 1 "forwprop1" } } */
> +/* { dg-final { scan-tree-dump-times " - \\{ 2147483645, 2147483646, 2147483647, -2147483648 \\}" 1 "forwprop1" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-2.c b/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-2.c
> new file mode 100644
> index 00000000000..27de9e77be2
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/vector-sub-const-2.c
> @@ -0,0 +1,12 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -Wno-psabi -fsanitize=signed-integer-overflow -fdump-tree-forwprop1" } */
> +
> +typedef __INT32_TYPE__ v4si __attribute__ ((vector_size (16)));
> +
> +v4si
> +f (v4si x)
> +{
> +  return x - (v4si) { 1, 1, 1, 1 };
> +}
> +
> +/* { dg-final { scan-tree-dump-times "\\.UBSAN_CHECK_SUB" 1 "forwprop1" } } */
> --
> 2.50.1 (Apple Git-155)
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.