Re: [PATCH 2/2] match.pd: combine two single-bit tests into one masked compare

DominicP <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <CAH-q1HVmAsbzx0cDY1J9Fzc6z6nJbF=BDW-h5E6SmsaJLYqR9A@mail.gmail.com>
On Mon, 17 Aug 2026 at 14:03, Richard Biener <[email protected]> wrote:

> On Thu, 13 Aug 2026, Dominic P wrote:
>
> > The PR35691 rules combine x == 0 & y == 0 into (x | y) == 0, which
> > covers two of the eight ways a pair of single-bit tests of the same
> > value can be combined; the remaining combinations were left as two
> > tests and a boolean operation.  For single bits C1 and C2 of X, with
> > M = C1 | C2:
> >
> >   (X & C1) != 0 & (X & C2) != 0  ->  (X & M) == M
> >   (X & C1) == 0 | (X & C2) == 0  ->  (X & M) != M
> >   (X & C1) != 0 & (X & C2) == 0  ->  (X & M) == C1
> >   (X & C1) != 0 | (X & C2) == 0  ->  (X & M) != C2
> >
> > These hold only for power-of-two masks, where bit-set and bit-clear are
> > both single-bit tests; multi-bit masks are not combinable this way and
> > are rejected by the integer_pow2p guards.  The comparisons and inner
> > masks are required to be single-use so the rewrite never adds a
> > statement when the individual tests have other consumers.  On ARM each
> > combination reduces from 4-8 instructions to the uniform
> > and-compare-branch sequence.
> >
> > Assisted-by: Claude Opus 4.8 (Anthropic)
>
> Looks good, but how did you test this?
>
> Bootstrapped and regtested on x86_64-pc-linux-gnu, no regressions.


> Thanks,
> Richard.
>
> > gcc/ChangeLog:
> >
> >       * match.pd ((X & C1) cmp 0 op (X & C2) cmp 0): Combine pairs of
> >       single-bit tests of the same operand into a single compare of
> >       the union mask.
> >
> > gcc/testsuite/ChangeLog:
> >
> >       * gcc.dg/fold-bit-test-combine-1.c: New test.
> >
> > Signed-off-by: Dominic P <[email protected]>
> > ---
> >  gcc/match.pd                                  | 30 +++++++++++++++++
> >  .../gcc.dg/fold-bit-test-combine-1.c          | 33 +++++++++++++++++++
> >  2 files changed, 63 insertions(+)
> >  create mode 100644 gcc/testsuite/gcc.dg/fold-bit-test-combine-1.c
> >
> > diff --git a/gcc/match.pd b/gcc/match.pd
> > index 90fb2acede4..c160e9e4725 100644
> > --- a/gcc/match.pd
> > +++ b/gcc/match.pd
> > @@ -1603,6 +1603,36 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> >       && TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE
> (@1)))
> >      (cmp (bit_and @0 (convert @1)) @2))))
> >
> > +/* Combine two single-bit tests of the same value X into one masked
> > +   compare.  For distinct single bits C1, C2 and M = C1 | C2:
> > +     (X & C1) != 0 & (X & C2) != 0  ->  (X & M) == M
> > +     (X & C1) == 0 | (X & C2) == 0  ->  (X & M) != M
> > +     (X & C1) != 0 & (X & C2) == 0  ->  (X & M) == C1
> > +     (X & C1) != 0 | (X & C2) == 0  ->  (X & M) != C2
> > +   The == 0 & == 0 and != 0 | != 0 cases are handled by the more general
> > +   rule above.  Restricted to single-bit masks: for a multi-bit mask
> > +   (X & C) != 0 is not a single-bit test.  */
> > +(for bitop (bit_and bit_ior)
> > +     cmp (ne eq)
> > +     rcmp (eq ne)
> > + (simplify
> > +  (bitop (cmp:s (bit_and:s @0 INTEGER_CST@1) integer_zerop)
> > +      (cmp:s (bit_and:s @0 INTEGER_CST@2) integer_zerop))
> > +  (if (integer_pow2p (@1) && integer_pow2p (@2)
> > +       && wi::to_wide (@1) != wi::to_wide (@2))
> > +   (with { tree m = wide_int_to_tree (TREE_TYPE (@0),
> > +                                   wi::to_wide (@1) | wi::to_wide
> (@2)); }
> > +    (rcmp (bit_and @0 { m; }) { m; }))))
> > + (simplify
> > +  (bitop:c (ne:s (bit_and:s @0 INTEGER_CST@1) integer_zerop)
> > +        (eq:s (bit_and:s @0 INTEGER_CST@2) integer_zerop))
> > +  (if (integer_pow2p (@1) && integer_pow2p (@2)
> > +       && wi::to_wide (@1) != wi::to_wide (@2))
> > +   (with { tree m = wide_int_to_tree (TREE_TYPE (@0),
> > +                                   wi::to_wide (@1) | wi::to_wide
> (@2)); }
> > +    (rcmp (bit_and @0 { m; })
> > +       { bitop == BIT_AND_EXPR ? @1 : @2; })))))
> > +
> >  /* Fold (A & ~B) - (A & B) into (A ^ B) - B.  */
> >  (simplify
> >   (minus (bit_and:cs @0 (bit_not @1)) (bit_and:cs @0 @1))
> > diff --git a/gcc/testsuite/gcc.dg/fold-bit-test-combine-1.c
> b/gcc/testsuite/gcc.dg/fold-bit-test-combine-1.c
> > new file mode 100644
> > index 00000000000..69de70649dc
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/fold-bit-test-combine-1.c
> > @@ -0,0 +1,33 @@
> > +/* Two single-bit tests of the same value combine into one masked
> compare.  */
> > +/* { dg-do compile } */
> > +/* { dg-options "-O2 -fdump-tree-optimized" } */
> > +
> > +int
> > +t_and_set_set (unsigned x)           /* (x&8)!=0 & (x&16)!=0 ->
> (x&24)==24 */
> > +{
> > +  return ((x & 8) != 0) & ((x & 16) != 0);
> > +}
> > +
> > +int
> > +t_and_set_clr (unsigned x)           /* (x&8)!=0 & (x&16)==0 ->
> (x&24)==8 */
> > +{
> > +  return ((x & 8) != 0) & ((x & 16) == 0);
> > +}
> > +
> > +int
> > +t_or_clr_clr (unsigned x)            /* (x&8)==0 | (x&16)==0 ->
> (x&24)!=24 */
> > +{
> > +  return ((x & 8) == 0) | ((x & 16) == 0);
> > +}
> > +
> > +int
> > +t_or_set_clr (unsigned x)            /* (x&8)!=0 | (x&16)==0 ->
> (x&24)!=16 */
> > +{
> > +  return ((x & 8) != 0) | ((x & 16) == 0);
> > +}
> > +
> > +/* Each becomes a single (x & 24) compare; the separate & 8 / & 16
> masks and
> > +   the boolean combiner are gone.  */
> > +/* { dg-final { scan-tree-dump-times " & 24;" 4 "optimized" } } */
> > +/* { dg-final { scan-tree-dump-not " & 8;" "optimized" } } */
> > +/* { dg-final { scan-tree-dump-not " & 16;" "optimized" } } */
> >
>
> --
> 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)
>
>
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.