https://gcc.gnu.org/g:1c9808a71207eb217f4e190c60f2755a796b90d4
commit r17-632-g1c9808a71207eb217f4e190c60f2755a796b90d4 Author: Shivam Gupta <[email protected]> Date: Wed May 20 09:16:32 2026 +0530 match.pd: Simplify (T)(x) == (T)(y) -> (T)(x ^ y) == 0 [PR112533] This is a follow-up to the -O1 patch for PR112533. At -O2, GCC lowers (~x & 1) == (~y & 1) via inlining into: _14 = ~x; _12 = (bool) _14; _13 = ~y; _9 = (bool) _13; _8 = _9 == _12; The NOT cancellation (~x == ~y -> x == y) is handled by existing forwprop rules before this rule fires. Add match.pd rule for eq and ne: (T)(x) == (T)(y) -> (T)(x ^ y) == 0 (T)(x) != (T)(y) -> (T)(x ^ y) != 0 Bootstrapped and regression tested on aarch64-linux-gnu with RUNTESTFLAGS="tree-ssa.exp". Changes since v1: * v5: Split testcase into bool, integral, short and float-specific files. * v4: Moved match rule to right place alongside other simple_comparison narrowing cases. * v3: Simplify match rule. Add more cases in test file. * v2: Generalize the match rule to generic narrowing integral equality comparisons from bool equality. PR tree-optimization/112533 gcc/ChangeLog: * match.pd: Add integral narrowing eq/ne to XOR-against-zero rule for (T)(x) == (T)(y) where precision(T) < precision(x). gcc/testsuite/ChangeLog: * gcc.dg/tree-ssa/narrow-bool-eq.c: New test. * gcc.dg/tree-ssa/narrow-integral-eq.c: New test. * gcc.dg/tree-ssa/narrow-short-eq.c: New test. * gcc.dg/tree-ssa/narrow-float-eq.c: New test. Signed-off-by: Shivam Gupta <[email protected]> Diff: --- gcc/match.pd | 14 ++++++- gcc/testsuite/gcc.dg/tree-ssa/narrow-bool-eq.c | 31 +++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/narrow-float-eq.c | 13 ++++++ gcc/testsuite/gcc.dg/tree-ssa/narrow-integral-eq.c | 46 ++++++++++++++++++++++ gcc/testsuite/gcc.dg/tree-ssa/narrow-short-eq.c | 23 +++++++++++ 5 files changed, 126 insertions(+), 1 deletion(-) diff --git a/gcc/match.pd b/gcc/match.pd index ff13a07ea94b..195d10c80c73 100644 --- a/gcc/match.pd +++ b/gcc/match.pd @@ -7804,7 +7804,19 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT) (if (cmp == LT_EXPR || cmp == LE_EXPR) { constant_boolean_node (above ? true : false, type); } (if (cmp == GT_EXPR || cmp == GE_EXPR) - { constant_boolean_node (above ? false : true, type); }))))))))) + { constant_boolean_node (above ? false : true, type); }))))))) + + /* For eq/ne with narrowing conversion: + (T)(X) == (T)(Y) -> (T)(X ^ Y) == 0 */ + (if (TYPE_PRECISION (TREE_TYPE (@0)) + < TYPE_PRECISION (TREE_TYPE (@00)) + && (cmp == EQ_EXPR || cmp == NE_EXPR) + && types_match (TREE_TYPE (@00), + TREE_TYPE (@10))) + (with { tree itype = TREE_TYPE (@0); } + (cmp (convert:itype (bit_xor @00 @10)) + { build_zero_cst (itype); }))))) + /* Fold (double)float1 CMP (double)float2 into float1 CMP float2. */ (if (FLOAT_TYPE_P (TREE_TYPE (@00)) && (DECIMAL_FLOAT_TYPE_P (TREE_TYPE (@0)) diff --git a/gcc/testsuite/gcc.dg/tree-ssa/narrow-bool-eq.c b/gcc/testsuite/gcc.dg/tree-ssa/narrow-bool-eq.c new file mode 100644 index 000000000000..217edf7a0414 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/narrow-bool-eq.c @@ -0,0 +1,31 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* Verify the fix for PR112533 at -O2. + (~a & 1) == (~b & 1) is lowered via inlining to + (bool)(~a) == (bool)(~b), then existing forwprop rules cancel + the NOTs giving (bool)(a) == (bool)(b), which this rule + simplifies to (bool)(a ^ b) == 0. */ + +typedef unsigned int u32; + +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); +} + +/* Verify XOR form is produced. */ +/* { dg-final { scan-tree-dump-times "\\^" 2 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/narrow-float-eq.c b/gcc/testsuite/gcc.dg/tree-ssa/narrow-float-eq.c new file mode 100644 index 000000000000..25a264a128ef --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/narrow-float-eq.c @@ -0,0 +1,13 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +int +eq_float_no_xor (double a, double b) +{ + float fa = a; + float fb = b; + return fa == fb; +} + +/* Floating-point narrowing should not use XOR form. */ +/* { dg-final { scan-tree-dump-not "\\^" "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/narrow-integral-eq.c b/gcc/testsuite/gcc.dg/tree-ssa/narrow-integral-eq.c new file mode 100644 index 000000000000..1b08e38bdda8 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/narrow-integral-eq.c @@ -0,0 +1,46 @@ +/* { dg-do compile } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +unsigned char +eq_uchar (unsigned int a, unsigned int b) +{ + unsigned char ua = a; + unsigned char ub = b; + return ua == ub; +} + +signed char +ne_schar (int a, int b) +{ + signed char ca = a; + signed char cb = b; + return ca != cb; +} + +int +eq_int_from_ll (long long a, long long b) +{ + int ia = a; + int ib = b; + return ia == ib; +} + +int +eq_int_no_narrow (int a, int b) +{ + int ia = a; + int ib = b; + return ia == ib; +} + +long long +eq_widen (int a, int b) +{ + long long la = a; + long long lb = b; + return la == lb; +} + +/* Only narrowing integral cases should produce XOR form. + eq_int_no_narrow and eq_widen should not match. */ +/* { dg-final { scan-tree-dump-times "\\^" 3 "optimized" } } */ diff --git a/gcc/testsuite/gcc.dg/tree-ssa/narrow-short-eq.c b/gcc/testsuite/gcc.dg/tree-ssa/narrow-short-eq.c new file mode 100644 index 000000000000..27df711f8036 --- /dev/null +++ b/gcc/testsuite/gcc.dg/tree-ssa/narrow-short-eq.c @@ -0,0 +1,23 @@ +/* { dg-do compile { target { ! int16 } } } */ +/* { dg-options "-O2 -fdump-tree-optimized" } */ + +/* Requires sizeof(int) > sizeof(short). */ + +short +eq_short_not (int a, int b) +{ + short sa = ~a; + short sb = ~b; + return sa == sb; +} + +short +ne_short_not (int a, int b) +{ + short sa = ~a; + short sb = ~b; + return sa != sb; +} + +/* Verify XOR form is produced for narrowing short comparisons. */ +/* { dg-final { scan-tree-dump-times "\\^" 2 "optimized" } } */
