https://gcc.gnu.org/g:c7ebd58535ad63351123e2aa98b92b1eec3c8ca9

commit r17-4000-gc7ebd58535ad63351123e2aa98b92b1eec3c8ca9
Author: Andrea Pinski <[email protected]>
Date:   Tue Aug 11 11:17:08 2026 -0700

    match/fold: Add support for NE, XOR, and EQ to combine_comparisons 
[PR107881]
    
    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.
    * v3: Update for the newer syntax.
    
    Bootstrapped and tested on x86_64-linux-gnu.
    
            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]>

Diff:
---
 gcc/fold-const.cc                        | 54 ++++++++++++++++++++++++-
 gcc/match.pd                             | 68 ++++++++++----------------------
 gcc/testsuite/gcc.dg/tree-ssa/cmpeq-5.c  | 51 ++++++++++++++++++++++++
 gcc/testsuite/gcc.dg/tree-ssa/cmpxor-2.c | 51 ++++++++++++++++++++++++
 4 files changed, 175 insertions(+), 49 deletions(-)

diff --git a/gcc/fold-const.cc b/gcc/fold-const.cc
index 420e3185a2af..b0f2e51aec1a 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 9f834115a52d..5199c3457bc2 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -3685,6 +3685,26 @@ 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 rescmpcode (tcc_comparison)
+      {
+        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.
@@ -3970,54 +3990,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 rescmpcode (tcc_comparison)
-      {
-        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 000000000000..16dc35ca39e2
--- /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 000000000000..00fa71202376
--- /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" } } */

Reply via email to