On Tue, May 19, 2026 at 2:42 AM Shivam Gupta <[email protected]> wrote:
>
> 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".
>
>         PR tree-optimization/112533
>
> Changes since v1:
> * 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.
>
> 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-eq-simplify.c: New test for
>         narrowing integral eq/ne simplification.
>
> Signed-off-by: Shivam Gupta <[email protected]>
> ---
>  gcc/match.pd                                  | 14 ++-
>  .../gcc.dg/tree-ssa/narrow-eq-simplify.c      | 95 +++++++++++++++++++
>  2 files changed, 108 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/narrow-eq-simplify.c
>
> diff --git a/gcc/match.pd b/gcc/match.pd
> index ff13a07ea94..195d10c80c7 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-eq-simplify.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/narrow-eq-simplify.c
> new file mode 100644
> index 00000000000..6e48e40dd3a
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/narrow-eq-simplify.c
> @@ -0,0 +1,95 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized" } */
> +
> +/* Verify the fix for PR112533 at -O2.
> +   (~a & 1) == (~a & 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);
> +}

Split this testcase into 4 files. The first will have the
same_evenness/diff_evenness related ones and then the second testcase
will have the eq_*/ne_* functions.

> +
> +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;
> +}

This requires `sizeof(int) != sizeof(short)` so the testcase needs to
run on targets which is `!int16`.
So it might make sense to split out short also from char.
And the short case for the dg-do have it as:
/* { dg-do compile { target ! int16 } */

> +
> +unsigned char
> +eq_uchar (unsigned int a, unsigned int b)
> +{
> +  unsigned char sa = a;
> +  unsigned char sb = b;
> +  return sa == sb;
> +}
> +
> +signed char
> +ne_schar (int a, int b)
> +{
> +  signed char sa = a;
> +  signed char sb = b;
> +  return sa != sb;
> +}
> +
> +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;
> +}
> +
> +int
> +eq_float_no_xor (double a, double b)
> +{
> +  float fa = a;
> +  float fb = b;
> +  return fa == fb;
> +}

And then the 3rd would be the eq_float_no_xor which uses
scan-tree-dump-not there.

> +
> +/* Verify the XOR form is produced for integral narrowing comparisons.  */
> +/* { dg-final { scan-tree-dump-times "\\\^" 7 "optimized" } } */


Thanks,
Andrea

> --
> 2.34.1
>

Reply via email to