Re: [PATCH] match.pd: drop an operand discarded by a shift
Andrea Pinski <[email protected]> Tue, 4 Aug 2026 17:55:45 -0700
| Newsgroups | gmane.comp.gcc.patches |
|---|---|
| Message-ID | <CALvbMcD4uD_g2A=FWwH=iApQZD4CEYJ+YBaS5kiS4LGxhTPWKw@mail.gmail.com> |
On Tue, Aug 4, 2026 at 2:47=E2=80=AFAM <[email protected]> wrote: > > From: Kyrylo Tkachov <[email protected]> > > Neither an inclusive nor an exclusive or can carry, so an operand whose s= et > bits all lie below the shift count contributes nothing to the result: > > int f (int a, int b) { return (a ^ (b & 1)) >> 1; } > > aarch64 -O2 before: > > and w1, w1, 1 > eor w0, w1, w0 > asr w0, w0, 1 > > after: > > asr w0, w0, 1 > > The set bits are read from tree_nonzero_bits, so the rule also fires when > the operand is a boolean, a narrow value or anything else whose range the > middle end already knows. Found by mining the optimized dumps of real co= de, > where the shape comes from flag bits packed into the low bits of a word. > > There is deliberately no single use restriction. Most real instances kee= p > the exclusive or alive for another use and still save the shift's operand > being computed on this path. > > Bootstrapped and tested on aarch64-none-linux-gnu. > Ok for trunk? > Thanks, > Kyrill > > gcc/ChangeLog: > > * match.pd ((X | Y) >> C, (X ^ Y) >> C): New simplification. > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/shift-drops-bitop-1.c: New test. > > Signed-off-by: Kyrylo Tkachov <[email protected]> > --- > gcc/match.pd | 11 ++++++++++ > .../gcc.dg/tree-ssa/shift-drops-bitop-1.c | 22 +++++++++++++++++++ > 2 files changed, 33 insertions(+) > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/shift-drops-bitop-1.c > > diff --git a/gcc/match.pd b/gcc/match.pd > index b27d9a0bad0..21bcfd069d4 100644 > --- a/gcc/match.pd > +++ b/gcc/match.pd > @@ -5032,6 +5032,17 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > - TYPE_PRECISION (TREE_TYPE (@2))))) > (bit_and (convert @0) (lshift { build_minus_one_cst (type); } @1)))) > > +/* (X op Y) >> C -> X >> C when every set bit of Y lies below bit C. > + Neither an inclusive nor an exclusive or can carry into the bits the > + shift keeps, so Y contributes nothing to the result. */ > +(for op (bit_ior bit_xor) > + (simplify > + (rshift (op:c @0 @1) INTEGER_CST@2) Remove the `:c` (and see below). > + (if (INTEGRAL_TYPE_P (type) > + && wi::ltu_p (wi::to_wide (@2), element_precision (type)) tree_fits_uhwi_p (@2) && tree_to_uhwi (@2) < TYPE_PRECISION (type) > + && (tree_nonzero_bits (@1) > + & wi::mask (tree_to_uhwi (@2), true, element_precision (type))= ) =3D=3D 0) > + (rshift @0 @2)))) You can use TREE_PRECISION instead of element_precision here since you already test if it is a scalar type. So this should just be: (if (INTEGRAL_TYPE_P (type) && tree_fits_uhwi_p (@2) && tree_to_uhwi (@2) < TYPE_PRECISION (type)) (with { auto mask =3D wi::mask (tree_to_uhwi (@2), true, TYPE_PRECISION (t= ype); } (if ((tree_nonzero_bits (@1) & mask)) =3D=3D 0) (rshift @0 @2) (if ((tree_nonzero_bits (@2) & mask)) =3D=3D 0) (rshift @0 @1)) This simplifies the generated code slightly and should speed up the matching so won't need to go through the first part to be only canceled out by the second part. > #if GIMPLE > /* (X >> C1) << (C1 + C2) -> X << C2 if the low C1 bits of X are zero. = */ > (simplify > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/shift-drops-bitop-1.c b/gcc/te= stsuite/gcc.dg/tree-ssa/shift-drops-bitop-1.c > new file mode 100644 > index 00000000000..fe9cca34b4d > --- /dev/null > +++ b/gcc/testsuite/gcc.dg/tree-ssa/shift-drops-bitop-1.c > @@ -0,0 +1,22 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O2 -fdump-tree-original -fdump-tree-optimized" } */ > + > +/* Neither an inclusive nor an exclusive or can carry, so an operand who= se > + set bits all lie below the shift count contributes nothing. */ > + > +int f1 (int a, int b) { return (a ^ (b & 1)) >> 1; } > +unsigned f2 (unsigned a, unsigned b) { return (a | (b & 7)) >> 3; } > +long f3 (long a, int c) { return (a ^ (long) (c !=3D 0)) >> 1; } > + > +/* GENERIC folding must preserve evaluation of the discarded operand. *= / > +int side; > +int f4 (int a, int b) { return (a ^ ((side++, b) & 1)) >> 1; } > + > +/* Bit 1 of the mask survives the shift, so the exclusive or stays. */ > +int keep (int a, int b) { return (a ^ (b & 3)) >> 1; } > + > +/* { dg-final { scan-tree-dump-times " \\^ " 1 "optimized" } } */ > +/* { dg-final { scan-tree-dump-not " \\| " "optimized" } } */ > +/* { dg-final { scan-tree-dump-times " \\^ " 2 "original" } } */ > +/* { dg-final { scan-tree-dump-not " \\| " "original" } } */ > +/* { dg-final { scan-tree-dump-times "side\\+\\+" 1 "original" } } */ > -- > 2.50.1 (Apple Git-155) >