On Thu, 20 Aug 2026, [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?
See Andrews comments. I'll add here that for REAL_CST we attempt to canonicalize to positive constants, so x + -5.0 -> x - 5.0 to optimize constant pool size. Most vector constants are going to be materialized from the constant pool as well. Not to say that this of course should not stand in the way of canonicalization but instead should possibly be applied late. Richard. > 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))); > + if (!negatable > + && ANY_INTEGRAL_TYPE_P (type) > + && !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" } } > */ > -- Richard Biener <[email protected]> SUSE Software Solutions Germany GmbH, Frankenstrasse 146, 90461 Nuernberg, Germany; GF: Jochen Jaser, Andrew McDonald, Abhinav Puri; (HRB 36809, AG Nuernberg)
