On Mon, 25 May 2026 at 12:41 AM, Andrew Pinski < [email protected]> wrote:
> On Sat, May 16, 2026 at 11:50 PM Shivam Gupta <[email protected]> > wrote: > > > > At -O1, GCC lowers (~a & 1) == (~b & 1) into two different GIMPLE > > forms depending on whether the boolean values come from a function > > return boundary or an inline expression: > > > > 1. Via function return (e.g. inlined is_even), GCC preserves the > > eq/ne form because the function boundary hides the internal > > structure from the gimplifier: > > ((a&1)==0) == ((b&1)==0) outer node is eq/ne > > > > 2. Via inline expression (directly written), GCC sees the full > > boolean expression tree at once and folds to bit_xor during > > gimplification: > > ((a&1)!=0) ^ ((b&1)==0) outer node is bit_xor > > > > Add two match.pd rules to handle both forms: > > > > 1. (cmp (eq (x&1) 0) (eq (y&1) 0)) -> (cmp (x&1) (y&1)) > > 2. (bit_xor (eq (x&1) 0) (eq (y&1) 0)) -> (ne (x&1) (y&1)) > > (bit_xor (ne (x&1) 0) (eq (y&1) 0)) -> (eq (x&1) (y&1)) > > > > Subsequent passes further simplify these to (x^y)&1 == 0. > > > > Bootstrapped and tested on aarch64-linux-gnu with > > RUNTESTFLAGS="tree-ssa.exp". > > > > PR tree-optimization/112533 > > > > gcc/ChangeLog: > > * match.pd: Simplify ((x&1)==0)==((y&1)==0) to (x&1)==(y&1) > > and ((x&1) cmp 0)^((y&1)==0) to (x&1) icmp (y&1). > > > > gcc/testsuite/ChangeLog: > > * gcc.dg/tree-ssa/bool-eq-simplify-O1.c: New test. > > > > Signed-off-by: Shivam Gupta <[email protected]> > > --- > > gcc/match.pd | 27 +++++++++++ > > .../gcc.dg/tree-ssa/bool-eq-simplify-O1.c | 47 +++++++++++++++++++ > > 2 files changed, 74 insertions(+) > > create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/bool-eq-simplify-O1.c > > > > diff --git a/gcc/match.pd b/gcc/match.pd > > index ff13a07ea94..7afa3b0d546 100644 > > --- a/gcc/match.pd > > +++ b/gcc/match.pd > > @@ -1464,6 +1464,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) > > (if (TYPE_UNSIGNED (type)) > > (bit_and @0 (bit_not (lshift { build_all_ones_cst (type); } @1))))) > > > > +/* ((x & 1) == 0) == ((y & 1) == 0) -> (x & 1) == (y & 1) */ > > +/* ((x & 1) == 0) != ((y & 1) == 0) -> (x & 1) != (y & 1) */ > > +(for cmp (eq ne) > > + (simplify > > + (cmp > > + (eq (bit_and:c @0 INTEGER_CST@1) integer_zerop) > > + (eq (bit_and:c @2 @1) integer_zerop)) > > + (if (integer_onep (@1) > > This works for all values of powers of 2 rather than just 1. > So INTEGER_CST should be integer_pow2p. You can use integer_pow2p as a > predicate on the capture too (integer_onep could be done the same > way). > Also with both bit_and you don't need `:c` since constants are always > placed on the 2nd operand due to canonicalization. > Thanks for the review. Addressed in v2 of the patch. Thanks, Shivam > > > + && types_match (@0, @2)) > > + (cmp > > + (bit_and @0 @1) > > + (bit_and @2 @1))))) > > + > > +/* ((x & 1) != 0) ^ ((y & 1) == 0) -> (x & 1) == (y & 1) */ > > +/* ((x & 1) == 0) ^ ((y & 1) == 0) -> (x & 1) != (y & 1) */ > > +(for cmp (eq ne) > > + icmp (ne eq) > > + (simplify > > + (bit_xor:c > > + (cmp (bit_and:c @0 INTEGER_CST@1) integer_zerop) > > + (eq (bit_and:c @2 @1) integer_zerop)) > > + (if (integer_onep (@1) > > + && types_match (@0, @2)) > > Same story here. > You can combine these two by having a resulting operator. > > Thanks, > Andrea > > > > + (icmp > > + (bit_and @0 @1) > > + (bit_and @2 @1))))) > > + > > (for bitop (bit_and bit_ior) > > cmp (eq ne) > > /* PR35691: Transform > > diff --git a/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-simplify-O1.c > b/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-simplify-O1.c > > new file mode 100644 > > index 00000000000..3d4f5328c6e > > --- /dev/null > > +++ b/gcc/testsuite/gcc.dg/tree-ssa/bool-eq-simplify-O1.c > > @@ -0,0 +1,47 @@ > > +/* { dg-do compile } */ > > +/* { dg-options "-O1 -fdump-tree-optimized" } */ > > + > > +/* Verify the GIMPLE-level fix for PR112533: > > + (~A & 1) == (~B & 1) is lowered by GCC to one of two GIMPLE forms > > + which should both be simplified to (A ^ B) & 1 == 0. */ > > + > > +typedef unsigned int u32; > > + > > +/* Source-level form from the bug report. */ > > +static _Bool > > +is_even (u32 a) > > +{ > > + return a % 2 == 0; > > +} > > + > > +_Bool > > +same_evenness (u32 a, u32 b) > > +{ > > + return is_even (a) == is_even (b); > > +} > > + > > +_Bool > > +diff_evenness (u32 a, u32 b) > > +{ > > + return is_even (a) != is_even (b); > > +} > > + > > +/* GIMPLE-level form. */ > > +_Bool > > +same_evenness_o1 (u32 a, u32 b) > > +{ > > + u32 t1 = a & 1; > > + u32 t2 = b & 1; > > + return (t1 == 0) == (t2 == 0); > > +} > > + > > +_Bool > > +diff_evenness_o1 (u32 a, u32 b) > > +{ > > + u32 t1 = a & 1; > > + u32 t2 = b & 1; > > + return (t1 == 0) != (t2 == 0); > > +} > > + > > +/* Verify all four functions are optimized to the canonical XOR form. > */ > > +/* { dg-final { scan-tree-dump-times "\\\^" 4 "optimized" } } */ > > -- > > 2.34.1 > > >
