Re: [PATCH] match.pd: fold the overflow-free average idiom
Andrea Pinski <[email protected]> Tue, 4 Aug 2026 16:54:56 -0700
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CALvbMcD6egUz65Cm4FrFGa83-ELxi4HV9-xePcEsR=nLP_0f2A@mail.gmail.com> |
On Tue, Aug 4, 2026 at 4:07=E2=80=AFAM <[email protected]> wrote: > > From: Kyrylo Tkachov <[email protected]> > > The average of two integers is often written so that it cannot overflow: > > ((x >> 1) + (y >> 1)) + (x & y & 1) > > Since x + y is 2 * (x & y) + (x ^ y), the same value is (x & y) + > ((x ^ y) >> 1), which is three operations instead of five. Both forms > are exact for signed and unsigned types, and neither can overflow, > because the result is always between the two inputs. > > int f (int a, int b) { return ((a >> 1) + (b >> 1)) + (a & b & 1); } > > aarch64 -O2 before: > > lsr w2, w1, 1 > add w2, w2, w0, lsr 1 > and w0, w0, w1 > and w0, w0, 1 > add w0, w2, w0 > > after: > > eor w2, w0, w1 > and w0, w0, w1 > add w0, w0, w2, lsr 1 > > The vectoriser emits the five-operation form itself when the target has > no halving add, so the same reduction applies there. On SVE without > SVE2 the loop body of pr89007-2.c goes from six vector operations to > four, and that test is updated to the shorter sequence. Targets that do > have a halving add are unaffected: IFN_AVG_FLOOR is recognised on the > scalar form before this rule can see anything, so NEON and SVE2 keep > their uhadd. > > Do not commute the identical inner shift forms. Reuse the matched > conjunction in the result. > > Bootstrapped and tested on aarch64-none-linux-gnu. > Ok for trunk? > Thanks, > Kyrill > > gcc/ChangeLog: > > * match.pd (((x >> 1) + (y >> 1)) + (x & y & 1)): New simplificat= ion > to (x & y) + ((x ^ y) >> 1). > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/avg-1.c: New test. > * gcc.target/aarch64/sve/pr89007-2.c: Update the expected loop bo= dy. > > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/match.pd | 9 +++++++ > gcc/testsuite/gcc.dg/tree-ssa/avg-1.c | 27 +++++++++++++++++++ > .../gcc.target/aarch64/sve/pr89007-2.c | 10 +++---- > 3 files changed, 40 insertions(+), 6 deletions(-) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/avg-1.c > > diff --git a/gcc/match.pd b/gcc/match.pd > index 0d58ff2c115..45811d10341 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -2181,6 +2181,15 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > && wi::to_widest (@2) =3D=3D 1) > (plus @0 @1))) > > +/* ((x >> 1) + (y >> 1)) + (x & y & 1) -> (x & y) + ((x ^ y) >> 1). > + Both are the average of x and y computed without overflowing, since > + x + y is 2 * (x & y) + (x ^ y), but the second form needs three > + operations instead of five. */ > +(simplify > + (plus:c (plus (rshift @0 integer_onep@2) (rshift @1 @2)) > + (bit_and:c (bit_and:c@3 @0 @1) integer_onep)) Note this is not a full review; just something I Noticed: The outer bit_and does not need `:c` as constants are always in the last operand. > + (plus @3 (rshift (bit_xor @0 @1) @2))) > + > /* (x & y) + (x | y) -> x + y */ > (simplify > (plus:c (bit_and @0 @1) (bit_ior @0 @1)) > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/avg-1.c b/gcc/testsuite/gcc.dg= /tree-ssa/avg-1.c > new file mode 100644 > index 00000000000..d1fde110a74 > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/avg-1.c > @@ -0,0 +1,27 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-optimized" } */ > + > +/* ((x >> 1) + (y >> 1)) + (x & y & 1) is the average of x and y without > + overflow. It must fold to (x & y) + ((x ^ y) >> 1), which needs thre= e > + operations instead of five. */ > + > +int > +f (int a, int b) > +{ > + return ((a >> 1) + (b >> 1)) + (a & b & 1); > +} > + > +unsigned > +g (unsigned a, unsigned b) > +{ > + return (a & b & 1) + ((a >> 1) + (b >> 1)); > +} > + > +long > +h (long a, long b) > +{ > + return ((a >> 1) + (b >> 1)) + (1 & b & a); > +} > + > +/* { dg-final { scan-tree-dump-times " \\^ " 3 "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " & 1;" "optimized" } } */ > diff --git a/gcc/testsuite/gcc.target/aarch64/sve/pr89007-2.c b/gcc/tests= uite/gcc.target/aarch64/sve/pr89007-2.c > index 1de44df96c9..26f00dc2258 100644 > --- a/gcc/testsuite/gcc.target/aarch64/sve/pr89007-2.c > +++ b/gcc/testsuite/gcc.target/aarch64/sve/pr89007-2.c > @@ -10,12 +10,10 @@ unsigned char in2[N]; > /* > ** foo: > ** ... > -** lsr (z[0-9]+\.b), z[0-9]+\.b, #1 > -** lsr (z[0-9]+\.b), z[0-9]+\.b, #1 > -** add (z[0-9]+\.b), (\1, \2|\2, \1) > -** and (z[0-9]+)\.d, z[0-9]+\.d, z[0-9]+\.d > -** and (z[0-9]+\.b), \5\.b, #0x1 > -** add z[0-9]+\.b, (\3, \6|\6, \3) > +** eor z[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d > +** lsr z[0-9]+\.b, z[0-9]+\.b, #1 > +** and z[0-9]+\.d, z[0-9]+\.d, z[0-9]+\.d > +** add z[0-9]+\.b, z[0-9]+\.b, z[0-9]+\.b > ** ... > */ > void > -- > 2.50.1 (Apple Git-155) >