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) > >
