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