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)
+       && 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))
+   (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

Reply via email to