On Thu, Aug 13, 2026 at 5:58 PM Richard Biener
<[email protected]> wrote:
>
> On Sun, May 31, 2026 at 5:33 PM Shivam Gupta <[email protected]> wrote:
> >
> > Recognize XOR patterns involving zero_one_valued operands compared
> > against zero and simplify them to direct equality or inequality tests.
> >
> > Specifically:
> >
> > (a == 0) ^ (b != 0) -> a == b
> > (a != 0) ^ (b == 0) -> a == b
> > (a == 0) ^ (b == 0) -> a != b
> > (a != 0) ^ (b != 0) -> a != b
> >
> > Also handle a specific case:
> > (a == 0) ^ b -> a == b
> >
> > Extend the simplifications to handle the corresponding boolean
> > equality forms as well:
> > (a == 0) == (b != 0) -> a != b
> > (a != 0) == (b == 0) -> a != b
> > (a == 0) == (b == 0) -> a == b
> > (a != 0) == (b != 0) -> a == b
> >
> > Regression tested on aarch64-linux-gnu.
> >
> > Changes since v1:
> > * v2: Simplify (a == 0) ^ b to a == b.
> > Handle outer EQ as well as outer NE forms.
> > Use convert:type to handle differing operand types.
> >
> > gcc/ChangeLog:
> > * match.pd: Add simplifications for XORs and boolean
> > comparisons of zero_one_valued comparisons against zero.
> >
> > gcc/testsuite/ChangeLog:
> > * gcc.dg/tree-ssa/bool-eq-bitxor.c: Update expected number
> > of optimized XOR forms.
> > * gcc.dg/tree-ssa/bool-xor-zero-one-valued.c: New test.
> >
> > Signed-off-by: Shivam Gupta <[email protected]>
> > ---
> > gcc/match.pd | 29 +++++++++++++++
> > .../gcc.dg/tree-ssa/bool-eq-bitxor.c | 4 +-
> > .../tree-ssa/bool-xor-zero-one-valued.c | 37 +++++++++++++++++++
> > 3 files changed, 67 insertions(+), 3 deletions(-)
> > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/bool-xor-zero-one-valued.c
> >
> > diff --git a/gcc/match.pd b/gcc/match.pd
> > index 228bab78428..bed3ca23c6c 100644
> > --- a/gcc/match.pd
> > +++ b/gcc/match.pd
> > @@ -3765,6 +3765,35 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
> > (if (types_match (type, TREE_TYPE (@0)))
> > (bit_xor @0 { build_one_cst (type); } ))))))
> >
> > +/* For zero_one_valued operands:
> > + (a == 0) != b -> a == b. */
> > +(simplify
> > + (ne (convert:s (eq zero_one_valued_p@0 integer_zerop))
>
> Don't you need :c on the 'ne'?
>
> > + (convert:s zero_one_valued_p@1))
>
> I'd drop the :s everywhere.
>
> > + (if (types_match (TREE_TYPE (@0), TREE_TYPE (@1)))
> > + (convert (eq @0 @1))))
>
> Why the outer (convert ...)?
>
> > +
> > +/* For zero_one_valued operands:
> > + (a op1 0) cmp (b op2 0) -> a icmp b
> > + where op1 != op2. */
> > +(for cmp (ne eq)
> > + icmp (eq ne)
> > + (for op1 (eq ne)
> > + op2 (ne eq)
> > + (simplify
> > + (cmp:c (op1 zero_one_valued_p@0 integer_zerop)
> > + (op2 zero_one_valued_p@1 integer_zerop))
> > + (icmp (convert:type @0) (convert:type @1)))))
> > +
> > +/* For zero_one_valued operands:
> > + (a op 0) cmp (b op 0) -> a cmp b. */
>
> This coud be merged into the above by appending
> cmp == icmp, op1 == op2 cases, no?
>
> All patterns are problematic for vector types I think,
> zero_one_valued_p currently never fires for those
> but I wonder if we should make this explicit in the
> users rather relying on that never changing?
>
Thank you for the review. I have address all the comments in v3 of patch.
Best regards,
Shivam
> Thanks,
> Richard.
>
> > +(for cmp (ne eq)
> > + (for op (eq ne)
> > + (simplify
> > + (cmp:c (op zero_one_valued_p@0 integer_zerop)
> > + (op zero_one_valued_p@1 integer_zerop))
> > + (cmp (convert:type @0) (convert:type @1)))))
> > +
> > /* ((a ^ b) & c) cmp d || a != b --> (0 cmp d || a != b). */
> > (for cmp (simple_comparison)
> > (simplify
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-bitxor.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-bitxor.c
> > index bc263e80977..e39e82589b6 100644
> > --- a/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-bitxor.c
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-bitxor.c
> > @@ -36,7 +36,5 @@ xor_ne_ne (u32 a, u32 b)
> > }
> >
> > /* Verify all functions canonicalize to xor-mask tests. */
> > -/* { dg-final { scan-tree-dump-times "a_\[0-9\]+\\(D\\) \\^
> > b_\[0-9\]+\\(D\\)" 3 "optimized" } } */
> > +/* { dg-final { scan-tree-dump-times "a_\[0-9\]+\\(D\\) \\^
> > b_\[0-9\]+\\(D\\)" 4 "optimized" } } */
> >
> > -/* xor_eq_ne not optimized yet due to zero_one_valued_p canonicalization.
> > */
> > -/* { dg-final { scan-tree-dump-times "& 1" 2 "optimized" { xfail *-*-* } }
> > } */
> > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/bool-xor-zero-one-valued.c
> > b/gcc/testsuite/gcc.dg/tree-ssa/bool-xor-zero-one-valued.c
> > new file mode 100644
> > index 00000000000..2bc10db4e70
> > --- /dev/null
> > +++ b/gcc/testsuite/gcc.dg/tree-ssa/bool-xor-zero-one-valued.c
> > @@ -0,0 +1,37 @@
> > +/* { dg-do compile } */
> > +/* { dg-options "-O1 -fdump-tree-optimized" } */
> > +
> > +_Bool
> > +f1 (unsigned a, unsigned b)
> > +{
> > + if (a != 0 && a != 1) __builtin_unreachable();
> > + if (b != 0 && b != 1) __builtin_unreachable();
> > + return (a == 0) ^ (b != 0);
> > +}
> > +
> > +_Bool
> > +f2 (unsigned a, unsigned b)
> > +{
> > + if (a != 0 && a != 1) __builtin_unreachable();
> > + if (b != 0 && b != 1) __builtin_unreachable();
> > + return (a != 0) ^ (b == 0);
> > +}
> > +
> > +_Bool
> > +f3 (unsigned a, unsigned b)
> > +{
> > + if (a != 0 && a != 1) __builtin_unreachable();
> > + if (b != 0 && b != 1) __builtin_unreachable();
> > + return (a == 0) ^ (b == 0);
> > +}
> > +
> > +_Bool
> > +f4 (unsigned a, unsigned b)
> > +{
> > + if (a != 0 && a != 1) __builtin_unreachable();
> > + if (b != 0 && b != 1) __builtin_unreachable();
> > + return (a != 0) ^ (b != 0);
> > +}
> > +
> > +/* { dg-final { scan-tree-dump-times "== 0" 0 "optimized" } } */
> > +/* { dg-final { scan-tree-dump-times "!= 0" 0 "optimized" } } */
> > --
> > 2.34.1
> >