On Wed, Aug 19, 2026 at 2:34 AM Andrea Pinski
<[email protected]> wrote:
>
> This adds support for !=, ^ and == to combine_comparisons and
> uses combine_comparisons in match for those cases instead of what
> was previously there. This allows for floating point comparisons to
> be merged in some more cases. And simplifies the match code to read.
> Also moves up the match pattern that uses combine_comparisons to
> above the other comparisons combines so it is matched first.
>
> Changes since v1:
> * v2: Add bitwise operators for comparison_code to hide the &0xf. Also
> place comparison_code in an anonymous namespace to mark it as local to
> the file. Update for the new sytanx of the match pattern.
>
> Bootstrapped and tested on x86_64-linux-gnu.

OK.

Thanks,
Richard.

>         PR tree-optimization/107881
>
> gcc/ChangeLog:
>
>         * fold-const.cc (enum comparison_code): Mark underlying type as
>         unsigned char. Wrap in an anonymous namespace.
>         (operator~): New function.
>         (operator|): New function.
>         (operator&): New function.
>         (operator^): New function.
>         (combine_comparisons): Add support for  NE/XOR and EQ.
>         * match.pd (`(a CMP1 b) OP (a CMP2 b)`): Move
>         above others and add NE, XOR and EQ to the list of OPs.
>         (`(a CMP1 b) ^ (a CMP2 b)`): Remove.
>         (`(a CMP1 b) == (a CMP2 b)`): Remove.
>
> gcc/testsuite/ChangeLog:
>
>         * gcc.dg/tree-ssa/cmpeq-5.c: New test.
>         * gcc.dg/tree-ssa/cmpxor-2.c: New test.
>
> Signed-off-by: Andrea Pinski <[email protected]>
> ---
>  gcc/fold-const.cc                        | 54 ++++++++++++++++++-
>  gcc/match.pd                             | 66 +++++++-----------------
>  gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c  | 51 ++++++++++++++++++
>  gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c | 51 ++++++++++++++++++
>  4 files changed, 174 insertions(+), 48 deletions(-)
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c
>  create mode 100644 gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c
>
> diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
> index 420e3185a2a..b0f2e51aec1 100644
> --- a/gcc/fold-const.cc
> +++ b/gcc/fold-const.cc
> @@ -100,10 +100,11 @@ int folding_initializer = 0;
>     during folding in that context.  */
>  bool folding_cxx_constexpr = false;
>
> +namespace {
>  /* The following constants represent a bit based encoding of GCC's
>     comparison operators.  This encoding simplifies transformations
>     on relational comparison operators, such as AND and OR.  */
> -enum comparison_code {
> +enum comparison_code : unsigned char {
>    COMPCODE_FALSE = 0,
>    COMPCODE_LT = 1,
>    COMPCODE_EQ = 2,
> @@ -122,6 +123,48 @@ enum comparison_code {
>    COMPCODE_TRUE = 15
>  };
>
> +// Implements bitwise operators on comparison_code so the
> +// upper unused bits are cleared.
> +
> +// Implements bitwise not on comparison_code
> +// clearing the upper unused bits.
> +comparison_code
> +operator ~(comparison_code cmp)
> +{
> +  unsigned char newcmp = cmp;
> +  newcmp = ~newcmp & 0xF;
> +  return (comparison_code)newcmp;
> +}
> +
> +// Implements bitwise ior on comparison_code.
> +comparison_code
> +operator |(comparison_code cmp0, comparison_code cmp1)
> +{
> +  unsigned char newcmp = ((unsigned char)cmp0) | cmp1;
> +  newcmp = newcmp & 0xF;
> +  return (comparison_code)newcmp;
> +}
> +
> +// Implements bitwise and on comparison_code.
> +comparison_code
> +operator &(comparison_code cmp0, comparison_code cmp1)
> +{
> +  unsigned char newcmp = ((unsigned char)cmp0) & cmp1;
> +  newcmp = newcmp & 0xF;
> +  return (comparison_code)newcmp;
> +}
> +
> +// Implements bitwise xor on comparison_code.
> +comparison_code
> +operator ^(comparison_code cmp0, comparison_code cmp1)
> +{
> +  unsigned char newcmp = ((unsigned char)cmp0) ^ cmp1;
> +  newcmp = newcmp & 0xF;
> +  return (comparison_code)newcmp;
> +}
> +
> +}
> +
>  static bool negate_expr_p (tree);
>  static tree negate_expr (tree);
>  static tree associate_trees (location_t, tree, tree, enum tree_code, tree);
> @@ -2964,6 +3007,15 @@ combine_comparisons (enum tree_code code, enum 
> tree_code lcode,
>        compcode = lcompcode | rcompcode;
>        break;
>
> +    case BIT_XOR_EXPR:
> +    case NE_EXPR:
> +      compcode = lcompcode ^ rcompcode;
> +      break;
> +
> +    case EQ_EXPR:
> +      compcode = ~(lcompcode ^ rcompcode);
> +      break;
> +
>      default:
>        return ERROR_MARK;
>      }
> diff --git a/gcc/match.pd b/gcc/match.pd
> index d255795c8dd..dfcbf961948 100644
> --- a/gcc/match.pd
> +++ b/gcc/match.pd
> @@ -3688,6 +3688,25 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>         && TYPE_OVERFLOW_WRAPS (TREE_TYPE (@1)))
>    (gt @0 (minus @1 { build_int_cst (TREE_TYPE (@1), 1); }))))
>
> +/* Optimize (a CMP b) &| (a CMP b)
> +   using the helper combine_comparisons function.  */
> +(for bitop (bit_and bit_ior bit_xor ne eq)
> + (for cmp1 (tcc_comparison)
> +  (for cmp2 (tcc_comparison)
> +   (simplify
> +    (bitop (cmp1 @0 @1) (cmp2 @0 @1))
> +    (with {
> +      tree_code rescmpcode;
> +      tree res;
> +      bool honor_nans = HONOR_NANS (@0);
> +      rescmpcode = combine_comparisons (bitop, cmp1, cmp2,
> +                                       type, honor_nans, &res);
> +     }
> +     (if (rescmpcode == INTEGER_CST)
> +      { res; }
> +      (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison)
> +       (rescmpcode @0 @1))))))))
> +
>  /* Convert (X == CST1) && ((other)X OP2 CST2) to a known value
>     based on CST1 OP2 CST2.  Similarly for (X != CST1).  */
>  /* Convert (X == Y) && (X OP2 Y) to a known value if X is an integral type.
> @@ -3973,53 +3992,6 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
>        { constant_boolean_node (true, type); })
>       ))))))
>
> -/* Optimize (a CMP b) ^ (a CMP b)  */
> -/* Optimize (a CMP b) != (a CMP b)  */
> -(for op (bit_xor ne)
> - (for cmp1 (lt lt lt le le le)
> -      cmp2 (gt eq ne ge eq ne)
> -      rcmp (ne le gt ne lt ge)
> -  (simplify
> -   (op:c (cmp1:c @0 @1) (cmp2 @0 @1))
> -   (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> -      || POINTER_TYPE_P (TREE_TYPE (@0))
> -      || ((VECTOR_INTEGER_TYPE_P (TREE_TYPE (@1))
> -          || VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (@1)))
> -      && expand_vec_cmp_expr_p (TREE_TYPE (@0), type, rcmp)))
> -    (rcmp @0 @1)))))
> -
> -/* Optimize (a CMP b) == (a CMP b)  */
> -(for cmp1 (lt lt lt le le le)
> -     cmp2 (gt eq ne ge eq ne)
> -     rcmp (eq gt le eq ge lt)
> - (simplify
> -  (eq:c (cmp1:c @0 @1) (cmp2 @0 @1))
> -  (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
> -               || POINTER_TYPE_P (TREE_TYPE (@0))
> -      || ((VECTOR_INTEGER_TYPE_P (TREE_TYPE (@1))
> -          || VECTOR_BOOLEAN_TYPE_P (TREE_TYPE (@1)))
> -      && expand_vec_cmp_expr_p (TREE_TYPE (@0), type,  rcmp)))
> -    (rcmp @0 @1))))
> -
> -/* Optimize (a CMP b) &| (a CMP b)
> -   using the helper combine_comparisons function.  */
> -(for bitop (bit_and bit_ior)
> - (for cmp1 (tcc_comparison)
> -  (for cmp2 (tcc_comparison)
> -   (simplify
> -    (bitop (cmp1 @0 @1) (cmp2 @0 @1))
> -    (with {
> -      tree_code rescmpcode;
> -      tree res;
> -      bool honor_nans = HONOR_NANS (@0);
> -      rescmpcode = combine_comparisons (bitop, cmp1, cmp2,
> -                                       type, honor_nans, &res);
> -     }
> -     (if (rescmpcode == INTEGER_CST)
> -      { res; }
> -      (if (TREE_CODE_CLASS (rescmpcode) == tcc_comparison)
> -       (rescmpcode @0 @1))))))))
> -
>  /* (type)([0,1]@a != 0) -> (type)a
>     (type)([0,1]@a == 1) -> (type)a
>     (type)([0,1]@a == 0) -> a ^ 1
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c
> new file mode 100644
> index 00000000000..16dc35ca39e
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c
> @@ -0,0 +1,51 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math" } */
> +/* PR tree-optimization/107881 */
> +
> +_Bool ltgt_eq(float a, float b)
> +{
> +  _Bool c = a < b;
> +  _Bool d = a > b;
> +  return c == d; // a u== b
> +}
> +/* { dg-final { scan-tree-dump "a_\[0-9\]+.D. u== 
> b_\[0-9\]+.D.|b_\[0-9\]+.D. u== a_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool lteq_eq(float x, float y)
> +{
> +  _Bool c = x < y;
> +  _Bool d = x == y;
> +  return c == d; // x u> y
> +}
> +/* { dg-final { scan-tree-dump "x_\[0-9\]+.D. u> y_\[0-9\]+.D.|y_\[0-9\]+.D. 
> u< x_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool ltne_eq(float z, float w)
> +{
> +  _Bool c = z < w;
> +  _Bool d = z != w;
> +  return c == d; // z <= w
> +}
> +/* { dg-final { scan-tree-dump "z_\[0-9\]+.D. <= w_\[0-9\]+.D.|w_\[0-9\]+.D. 
> >= y_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool lege_eq(float i, float j)
> +{
> +  _Bool c = i <= j;
> +  _Bool d = i >= j;
> +  return c == d; // i u== j
> +}
> +/* { dg-final { scan-tree-dump "i_\[0-9\]+.D. u== 
> j_\[0-9\]+.D.|j_\[0-9\]+.D. u== i_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool leeq_eq(float k, float l)
> +{
> +  _Bool c = k <= l;
> +  _Bool d = k == l;
> +  return c == d; // k u>= l
> +}
> +/* { dg-final { scan-tree-dump "k_\[0-9\]+.D. u>= 
> l_\[0-9\]+.D.|l_\[0-9\]+.D. u<= k_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool lene_eq(float m, float n)
> +{
> +  _Bool c = m <= n;
> +  _Bool d = m != n;
> +  return c == d; // m < n
> +}
> +/* { dg-final { scan-tree-dump "m_\[0-9\]+.D. < n_\[0-9\]+.D.|n_\[0-9\]+.D. 
> > m_\[0-9\]+.D." "optimized" } } */
> diff --git a/gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c 
> b/gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c
> new file mode 100644
> index 00000000000..00fa7120237
> --- /dev/null
> +++ b/gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c
> @@ -0,0 +1,51 @@
> +/* { dg-do compile } */
> +/* { dg-options "-O2 -fdump-tree-optimized -fno-trapping-math" } */
> +/* PR tree-optimization/107881 */
> +
> +_Bool ltgtxor(float a, float b)
> +{
> +  _Bool c = a < b;
> +  _Bool d = a > b;
> +  return c ^ d; // a <> b
> +}
> +/* { dg-final { scan-tree-dump "a_\[0-9\]+.D. <> b_\[0-9\]+.D.|b_\[0-9\]+.D. 
> <> a_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool lteqxor(float x, float y)
> +{
> +  _Bool c = x < y;
> +  _Bool d = x == y;
> +  return c ^ d; // x <= y (basically | here)
> +}
> +/* { dg-final { scan-tree-dump "x_\[0-9\]+.D. <= y_\[0-9\]+.D.|y_\[0-9\]+.D. 
> >= x_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool ltnexor(float z, float w)
> +{
> +  _Bool c = z < w;
> +  _Bool d = z != w;
> +  return c ^ d; // z u> w
> +}
> +/* { dg-final { scan-tree-dump "z_\[0-9\]+.D. u> w_\[0-9\]+.D.|w_\[0-9\]+.D. 
> u< y_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool legexor(float i, float j)
> +{
> +  _Bool c = i <= j;
> +  _Bool d = i >= j;
> +  return c ^ d; // i <> j
> +}
> +/* { dg-final { scan-tree-dump "i_\[0-9\]+.D. <> j_\[0-9\]+.D.|j_\[0-9\]+.D. 
> <> i_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool leeqxor(float k, float l)
> +{
> +  _Bool c = k <= l;
> +  _Bool d = k == l;
> +  return c ^ d; // k < l
> +}
> +/* { dg-final { scan-tree-dump "k_\[0-9\]+.D. < l_\[0-9\]+.D.|l_\[0-9\]+.D. 
> > k_\[0-9\]+.D." "optimized" } } */
> +
> +_Bool lenexor(float m, float n)
> +{
> +  _Bool c = m <= n;
> +  _Bool d = m != n;
> +  return c ^ d; // m u>= n
> +}
> +/* { dg-final { scan-tree-dump "m_\[0-9\]+.D. u>= 
> n_\[0-9\]+.D.|n_\[0-9\]+.D. u<= m_\[0-9\]+.D." "optimized" } } */
> --
> 2.43.0
>

Reply via email to