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)
>

Reply via email to