On Mon, Feb 16, 2026 at 2:47 AM Daniel Barboza
<[email protected]> wrote:
>
> Add two patterns to eliminate mispredicts in the following bit ops
> scenarios:
>
> - checking if a bitmask is not set, and in this case set it: always set
> the bitmask;
> - checking if a bitmask is set, and in this case clear it: always clear
> the bitmask.
>
> Bootstrapped and tested with x86_64-pc-linux-gnu.
>
> PR tree-optimization/64567
>
> gcc/ChangeLog:
>
> * match.pd (`cond (bit_and A IMM) (bit_or A IMM) A`): New
> pattern.
> (`cond (bit_and A IMM) (bit_and A ~IMM) A`): New pattern.
>
> gcc/testsuite/ChangeLog:
>
> * gcc.dg/tree-ssa/PR64567.c: New test.
> ---
>
> Changes from v1:
> - changed approach from phiopt to match.pd
> - renamed new test from "PR64567.c" to "pr64567.c"
> - v1 link: https://gcc.gnu.org/pipermail/gcc-patches/2026-February/708492.html
>
> gcc/match.pd | 21 +++++++++++++++++++++
> gcc/testsuite/gcc.dg/tree-ssa/pr64567.c | 23 +++++++++++++++++++++++
> 2 files changed, 44 insertions(+)
> create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/pr64567.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index 8910591a04b..d1831e90472 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -6316,6 +6316,27 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> && !expand_vec_cond_expr_p (TREE_TYPE (@1), TREE_TYPE (@0)))))
> (vec_cond @0 (op!:type @3 @1) (op!:type @3 @2)))))
>
> +/* If we have a "if a bitmask is not set, set it" case,
> + just set the bitmask all the time (see PR 64567). */
> +(simplify
> + (cond (eq (bit_and @0 @1) integer_zerop) (bit_ior@2 @0 @1) @0)
> + @2)
I think this only works if @1 is a single bit.
e.g. if @1 is 3 then we would have:
(a & 3) == 0 ? (a | 3) : a
so if either bit 1 or bit 2 are set you get back a; otherwise you set both bits.
That is if a == 1, we should get back 1 rather than 3.
So I think this should only be done for INTEGER_CST where
`wi::popcount (wi::to_wide (@1)) == 1`.
Add a (run) testcase for the 2 bit case here to make sure it is
working. using a volatile type should avoid the transformation.
> +
> +/* A clear bit version of the above: "if a bitmask is
> + set, clear it". In this case always clear the bitmask
> + (see PR 64567). */
> +(simplify
> + (cond (ne (bit_and @0 INTEGER_CST@1) integer_zerop)
> + (bit_and@3 @0 INTEGER_CST@2) @0)
> + (with
> + {
> + wide_int cond_imm = wi::to_wide (@1);
> + wide_int mid_imm = wi::to_wide (@2);
> + }
> + (if (wi::ltu_p (cond_imm, mid_imm)
> + && wi::eq_p (mid_imm, wi::bit_not (cond_imm)))
> + @3)))
I think this should just be reduced to:
wi::to_wide (@2) == ~wi::to_wide (@1)
Since both operator== and operator~ works on wide_int.
Also 0 won't show up as `@0 & 0` should already be reduced to 0.
The ltu here I think is too restrictive as @1 being ~2 and @2 being 2
works just fine.
Thanks,
Andrew
> +
> #if GIMPLE
> (match (nop_atomic_bit_test_and_p @0 @1 @4)
> (bit_and (convert?@4 (ATOMIC_FETCH_OR_XOR_N @2 INTEGER_CST@0 @3))
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/pr64567.c
> b/gcc/testsuite/gcc.dg/tree-ssa/pr64567.c
> new file mode 100644
> index 00000000000..09e8cee62ee
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/pr64567.c
> @@ -0,0 +1,23 @@
> +/* { dg-additional-options -O2 } */
> +/* { dg-additional-options -fdump-tree-phiopt } */
> +
> +#define F1 0x04
> +#define F2 0x08
> +
> +int bar(unsigned flags);
> +
> +int foo(unsigned flags)
> +{
> + if (flags & (F1 | F2))
> + flags &= ~(F1 | F2);
> + return bar(flags);
> +}
> +
> +int baz(unsigned flags)
> +{
> + if (!(flags & F1))
> + flags |= F1;
> + return bar(flags);
> +}
> +
> +/* { dg-final { scan-tree-dump-times " PHI " 0 phiopt2 } } */
> --
> 2.43.0
>