On Tue, 25 Aug 2026, Andrea Pinski wrote:
> On Tue, Aug 25, 2026 at 10:59 PM <[email protected]> wrote:
> >
> > From: Kyrylo Tkachov <[email protected]>
> >
> > REDUC_MAX, REDUC_MIN, REDUC_AND and REDUC_IOR are idempotent. Reducing
> > a nonempty uniform vector with one of these operations gives the element
> > that the vector contains. Fold this form to that element:
> >
> > v = _Literal (v4si) { x, x, x, x };
> > res = .REDUC_MAX (v);
> > return res;
> >
> > to:
> >
> > return x;
> >
> > An XOR reduction of a uniform vector is zero when the number of lanes is
> > even. GCC permits variable-length vectors whose lane count has unknown
> > parity, such as [1, 1]. Fold REDUC_XOR only when multiple_p proves that
> > every possible lane count is even:
> >
> > v = _Literal (v4si) { x, x, x, x };
> > res = .REDUC_XOR (v);
> > return res;
> >
> > to:
> >
> > return 0;
> >
> > Use vec_same_elem_p to match the uniform vector and
> > ssa_uniform_vector_p to get its element. Keep a floating-point
> > reduction when the element can be a signaling NaN because the reduction
> > can quiet the NaN and raise an exception.
> >
> > These folds also let the constant reduction in the PR 127039 rule
> > simplify for scalable uniform constants. The scalar result cannot match
> > the inverse reduction-pair rule, so the nontermination cycle stays
> > blocked.
> >
> > The assembly comparison uses this function from the new test:
> >
> > typedef int v4si __attribute__ ((vector_size (16)));
> >
> > int __GIMPLE ()
> > reduc_max (int x)
> > {
> > v4si v;
> > int res;
> >
> > v = _Literal (v4si) { x, x, x, x };
> > res = .REDUC_MAX (v);
> > return res;
> > }
> >
> > With -O2 -fgimple on AArch64, the code changes from:
> >
> > reduc_max:
> > dup v31.4s, w0
> > smaxv s31, v31.4s
> > fmov w0, s31
> > ret
> >
> > to:
> >
> > reduc_max:
> > ret
> >
> > Bootstrapped and tested on aarch64-unknown-linux-gnu.
> > Ok for trunk?
> > Thanks,
> > Kyrill
> >
> > gcc/ChangeLog:
> >
> > PR tree-optimization/127039
> > * match.pd (reduction of a uniform vector): New simplifications to
> > the splatted element or zero.
> >
> > gcc/testsuite/ChangeLog:
> >
> > PR tree-optimization/127039
> > * gcc.dg/tree-ssa/vec-reduc-splat-1.c: New test.
> > * gcc.target/aarch64/sve/vec-reduc-uniform-1.c: New test.
> > * gcc.target/aarch64/vec-reduc-splat-float-1.c: New test.
> > * gcc.target/riscv/rvv/base/reduc-xor-uniform-1.c: New test.
> >
> > Signed-off-by: Kyrylo Tkachov <[email protected]>
> > ---
> > gcc/match.pd | 17 +++++
> > .../gcc.dg/tree-ssa/vec-reduc-splat-1.c | 69 ++++++++++++++++++
> > .../aarch64/sve/vec-reduc-uniform-1.c | 71 +++++++++++++++++++
> > .../aarch64/vec-reduc-splat-float-1.c | 31 ++++++++
> > .../riscv/rvv/base/reduc-xor-uniform-1.c | 47 ++++++++++++
> > 5 files changed, 235 insertions(+)
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/vec-reduc-splat-1.c
> > create mode 100644
> > gcc/testsuite/gcc.target/aarch64/sve/vec-reduc-uniform-1.c
> > create mode 100644
> > gcc/testsuite/gcc.target/aarch64/vec-reduc-splat-float-1.c
> > create mode 100644
> > gcc/testsuite/gcc.target/riscv/rvv/base/reduc-xor-uniform-1.c
> >
> > diff --git a/gcc/match.pd b/gcc/match.pd
> > index c7841cab459..529e3d5ff60 100644
> > --- a/gcc/match.pd
> > +++ b/gcc/match.pd
> > @@ -12536,6 +12536,23 @@ and,
> > (if (elem)
> > { build_vector_from_val (type, elem); }))))
> >
> > +/* A reduction of a uniform vector with an idempotent operation is the
> > + element it is a splat of. */
> > +(for reduc (IFN_REDUC_MAX IFN_REDUC_MIN IFN_REDUC_AND IFN_REDUC_IOR)
> > + (simplify
> > + (reduc vec_same_elem_p@0)
> > + (with { tree elem = ssa_uniform_vector_p (@0); }
> > + (if (elem
> > + && types_match (type, TREE_TYPE (elem))
> > + && !tree_expr_maybe_signaling_nan_p (elem))
> > + { elem; }))))
>
> No.
> Please instead do it similar to IFN_VEC_EXTRACT and IFN_VEC_SHL_INSERT.
But those use just (vec_duplicate ..), they possibly should use
vec_same_elem_p, but then there is only an ugly way to get the
duplicated element for
(match vec_same_elem_p
@0
(if (uniform_vector_p (@0))))
like with
(match (vec_same_elem_p@0 @1)
@0
(if ((@1 = uniform_vector_p (@0)))))
which works, but is not documented to be allowed. It also shows
that 't' is not captured (the implicit @0 for output-less (match ..)
is a workaround), when doing it this way we get two outputs,
so it will likely not work as intended. I'd probably suggest
to implicitly have @t captured but not as output, or, ignore
the expression root when assigning outputs ...
The last example currently code-generates
bool
gimple_vec_same_elem_p (tree t, tree *res_ops, tree (*valueize)(tree)
ATTRIBUTE_UNUSED)
{
const tree type = TREE_TYPE (t);
const bool debug_dump = dump_file && (dump_flags & TDF_FOLDING);
{
tree captures[2] ATTRIBUTE_UNUSED = { t };
if ((captures[1] = uniform_vector_p (captures[0]))
)
{
{
res_ops[0] = captures[0];
res_ops[1] = captures[1];
if (UNLIKELY (debug_dump)) gimple_dump_logs ("test.pd", 0,
__FILE__, __LINE__, false);
return true;
Richard.
> > +
> > +/* Fold an XOR reduction of an even-length uniform vector to zero. */
> > +(simplify
> > + (IFN_REDUC_XOR vec_same_elem_p@0)
> > + (if (multiple_p (TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)), 2))
> > + { build_zero_cst (type); }))
> > +
> > /* Push VEC_PERM earlier if that may help FMA perception (PR101895). */
> > (simplify
> > (plus:c (vec_perm:s (mult:c@0 @1 vec_same_elem_p@2) @0 @3) @4)
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vec-reduc-splat-1.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/vec-reduc-splat-1.c
> > new file mode 100644
> > index 00000000000..2ad23472a06
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/vec-reduc-splat-1.c
> > @@ -0,0 +1,69 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O2 -fgimple -Wno-psabi -fdump-tree-forwprop1" } */
> > +
> > +/* Check reductions of a variable splat. */
> > +
> > +typedef int v4si __attribute__ ((vector_size (16)));
> > +
> > +int __GIMPLE ()
> > +reduc_max (int x)
> > +{
> > + v4si v;
> > + int res;
> > +
> > + v = _Literal (v4si) { x, x, x, x };
> > + res = .REDUC_MAX (v);
> > + return res;
> > +}
> > +
> > +int __GIMPLE ()
> > +reduc_min (int x)
> > +{
> > + v4si v;
> > + int res;
> > +
> > + v = _Literal (v4si) { x, x, x, x };
> > + res = .REDUC_MIN (v);
> > + return res;
> > +}
> > +
> > +int __GIMPLE ()
> > +reduc_and (int x)
> > +{
> > + v4si v;
> > + int res;
> > +
> > + v = _Literal (v4si) { x, x, x, x };
> > + res = .REDUC_AND (v);
> > + return res;
> > +}
> > +
> > +int __GIMPLE ()
> > +reduc_ior (int x)
> > +{
> > + v4si v;
> > + int res;
> > +
> > + v = _Literal (v4si) { x, x, x, x };
> > + res = .REDUC_IOR (v);
> > + return res;
> > +}
> > +
> > +int __GIMPLE ()
> > +reduc_xor_even (int x)
> > +{
> > + v4si v;
> > + int res;
> > +
> > + v = _Literal (v4si) { x, x, x, x };
> > + res = .REDUC_XOR (v);
> > + return res;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_MAX} "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_MIN} "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_AND} "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_IOR} "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_XOR} "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-times {return x_} 4 "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-times {return 0;} 1 "forwprop1" } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/vec-reduc-uniform-1.c
> > b/gcc/testsuite/gcc.target/aarch64/sve/vec-reduc-uniform-1.c
> > new file mode 100644
> > index 00000000000..9830511b18d
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/sve/vec-reduc-uniform-1.c
> > @@ -0,0 +1,71 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O3 -msve-vector-bits=scalable
> > -mautovec-preference=sve-only -fvect-cost-model=unlimited
> > --param=vect-partial-vector-usage=0 -fdump-tree-vect-details
> > -fdump-tree-forwprop4" } */
> > +
> > +/* Check reductions of scalable uniform vector constants. */
> > +
> > +char c1, c2, c3, c4, c5;
> > +short d1, d2, d3, d4, d5;
> > +int rmax, rmin, r_and, r_ior, r_xor;
> > +unsigned n;
> > +
> > +__attribute__ ((noipa)) void
> > +max_uniform (void)
> > +{
> > + for (short i = 0; i < n; i += 3)
> > + {
> > + c1 ^= d1;
> > + rmax = rmax > 17 ? rmax : 17;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa)) void
> > +min_uniform (void)
> > +{
> > + for (short i = 0; i < n; i += 3)
> > + {
> > + c2 ^= d2;
> > + rmin = rmin < -19 ? rmin : -19;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa)) void
> > +and_uniform (void)
> > +{
> > + for (short i = 0; i < n; i += 3)
> > + {
> > + c3 ^= d3;
> > + r_and &= 1431655765;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa)) void
> > +ior_uniform (void)
> > +{
> > + for (short i = 0; i < n; i += 3)
> > + {
> > + c4 ^= d4;
> > + r_ior |= 572662306;
> > + }
> > +}
> > +
> > +__attribute__ ((noipa)) void
> > +xor_uniform (void)
> > +{
> > + for (short i = 0; i < n; i += 3)
> > + {
> > + c5 ^= d5;
> > + r_xor ^= 858993459;
> > + }
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {LOOP VECTORIZED} 5 "vect" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MAX} 1 "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MIN} 1 "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_AND} 1 "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_IOR} 1 "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MAX \(vect_rmax_lsm} 1
> > "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MIN \(vect_rmin_lsm} 1
> > "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_AND \(vect_r_and_lsm} 1
> > "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_IOR \(vect_r_ior_lsm} 1
> > "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_XOR} 5 "forwprop4" } } */
> > +/* { dg-final { scan-tree-dump-not {\.REDUC_XOR \(\{ 0, \.\.\. \}\)}
> > "forwprop4" } } */
> > diff --git a/gcc/testsuite/gcc.target/aarch64/vec-reduc-splat-float-1.c
> > b/gcc/testsuite/gcc.target/aarch64/vec-reduc-splat-float-1.c
> > new file mode 100644
> > index 00000000000..ca0764997a0
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/aarch64/vec-reduc-splat-float-1.c
> > @@ -0,0 +1,31 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O2 -fgimple -fsignaling-nans -fdump-tree-forwprop1" } */
> > +
> > +/* Keep reductions of a splat that can contain a signaling NaN. */
> > +
> > +typedef float v4sf __attribute__ ((vector_size (16)));
> > +
> > +float __GIMPLE ()
> > +reduc_max (float x)
> > +{
> > + v4sf v;
> > + float res;
> > +
> > + v = _Literal (v4sf) { x, x, x, x };
> > + res = .REDUC_MAX (v);
> > + return res;
> > +}
> > +
> > +float __GIMPLE ()
> > +reduc_min (float x)
> > +{
> > + v4sf v;
> > + float res;
> > +
> > + v = _Literal (v4sf) { x, x, x, x };
> > + res = .REDUC_MIN (v);
> > + return res;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MAX} 1 "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_MIN} 1 "forwprop1" } } */
> > diff --git a/gcc/testsuite/gcc.target/riscv/rvv/base/reduc-xor-uniform-1.c
> > b/gcc/testsuite/gcc.target/riscv/rvv/base/reduc-xor-uniform-1.c
> > new file mode 100644
> > index 00000000000..54339490fd8
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.target/riscv/rvv/base/reduc-xor-uniform-1.c
> > @@ -0,0 +1,47 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O2 -fgimple -march=rv64im_zve64f -mabi=lp64
> > -fdump-tree-forwprop1" } */
> > +/* { dg-final { check-function-bodies "**" "" } } */
> > +
> > +#include "riscv_vector.h"
> > +
> > +/*
> > +** reduc_xor_even:
> > +** li a0,0
> > +** ret
> > +*/
> > +long __GIMPLE ()
> > +reduc_xor_even (void)
> > +{
> > + vbool32_t mask;
> > + vint64m2_t ones;
> > + long res;
> > +
> > + mask = _Literal (vint64m2_t) 0 == _Literal (vint64m2_t) 0;
> > + ones = .VEC_CONVERT (mask);
> > + res = .REDUC_XOR (ones);
> > + return res;
> > +}
> > +
> > +/*
> > +** reduc_xor_unknown:
> > +** vsetvli [a-x0-9]+,zero,e64,m1,ta,ma
> > +** ...
> > +** vredxor\.vs v[0-9]+,v[0-9]+,v[0-9]+
> > +** ...
> > +** ret
> > +*/
> > +long __GIMPLE ()
> > +reduc_xor_unknown (void)
> > +{
> > + vbool64_t mask;
> > + vint64m1_t ones;
> > + long res;
> > +
> > + mask = _Literal (vint64m1_t) 0 == _Literal (vint64m1_t) 0;
> > + ones = .VEC_CONVERT (mask);
> > + res = .REDUC_XOR (ones);
> > + return res;
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times {\.REDUC_XOR} 1 "forwprop1" } } */
> > +/* { dg-final { scan-tree-dump-times {return 0;} 1 "forwprop1" } } */
> > --
> > 2.50.1 (Apple Git-155)
> >
>
--
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)