[gcc r17-3281] match.pd: combine a pair of vector comparisons against zero

Kyrylo Tkachov via Gcc-cvs <[email protected]>
Newsgroups gmane.comp.gcc.cvs
Message-ID <[email protected]>
https://gcc.gnu.org/g:bbf1dfa9da862caadf97ebf9a21b877b1553dbf4

commit r17-3281-gbbf1dfa9da862caadf97ebf9a21b877b1553dbf4
Author: Kyrylo Tkachov <[email protected]>
Date:   Wed Jul 29 21:27:05 2026 +0200

    match.pd: combine a pair of vector comparisons against zero
    
    A lane of A | B is zero exactly when the corresponding lanes of A and of B
    are both zero, so
    
      (A == 0) & (B == 0)  ->  (A | B) == 0
    
    and the De Morgan dual for inequality.  The existing scalar rule already
    implements this identity.  Extend it to vector integers and use a view
    conversion when the operands differ only in element signedness.  Extend the
    related all-ones rule in the same way.
    
      typedef int v4si __attribute__((vector_size (16)));
      v4si f (v4si a, v4si b) { return (a == 0) & (b == 0); }
    
    aarch64 -O3 before:
    
            cmeq    v0.4s, v0.4s, #0
            cmeq    v1.4s, v1.4s, #0
            and     v0.16b, v0.16b, v1.16b
    
    after:
    
            orr     v0.16b, v0.16b, v1.16b
            cmeq    v0.4s, v0.4s, #0
    
    Add vector_nop_conversion_p for the element-wise property shared by these
    rules and the existing nop_convert matcher.
    
    Bootstrapped and tested on aarch64-none-linux-gnu.
    
    gcc/ChangeLog:
    
            * match.pd (nop_convert): Use vector_nop_conversion_p.
            ((A == 0) & (B == 0), (A != 0) | (B != 0)): Extend the
            existing simplifications to vector operands.
            ((A == -1) & (B == -1), (A != -1) | (B != -1)): Likewise.
            * tree.cc (vector_nop_conversion_p): New function.
            * tree.h (vector_nop_conversion_p): Declare.
    
    gcc/testsuite/ChangeLog:
    
            * gcc.dg/tree-ssa/vec-mask-zero-1.c: New test.
    
    Signed-off-by: Kyrylo Tkachov <[email protected]>

Diff:
---
 gcc/match.pd                                    | 26 +++++++++++++++++--------
 gcc/testsuite/gcc.dg/tree-ssa/vec-mask-zero-1.c | 17 ++++++++++++++++
 gcc/tree.cc                                     | 15 ++++++++++++++
 gcc/tree.h                                      |  1 +
 4 files changed, 51 insertions(+), 8 deletions(-)

diff --git a/gcc/match.pd b/gcc/match.pd
index 4ddddc43bd72..7fa15845e7a5 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -161,10 +161,7 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
  (if (tree_nop_conversion_p (type, TREE_TYPE (@0)))))
 (match (nop_convert @0)
  (view_convert @0)
- (if (VECTOR_TYPE_P (type) && VECTOR_TYPE_P (TREE_TYPE (@0))
-      && known_eq (TYPE_VECTOR_SUBPARTS (type),
-		   TYPE_VECTOR_SUBPARTS (TREE_TYPE (@0)))
-      && tree_nop_conversion_p (TREE_TYPE (type), TREE_TYPE (TREE_TYPE (@0))))))
+ (if (vector_nop_conversion_p (type, TREE_TYPE (@0)))))
 
 /* These are used by gimple_bitwise_inverted_equal_p to simplify
    detection of BIT_NOT and comparisons. */
@@ -1573,20 +1570,33 @@ DEFINE_INT_AND_FLOAT_ROUND_FN (RINT)
     (x == 0 & y == 0) -> (x | typeof(x)(y)) == 0.
     (x != 0 | y != 0) -> (x | typeof(x)(y)) != 0.  */
  (simplify
-  (bitop (cmp @0 integer_zerop@2) (cmp @1 integer_zerop))
+  (bitop (cmp@3 @0 integer_zerop@2) (cmp@4 @1 integer_zerop))
+  (switch
    (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
 	&& INTEGRAL_TYPE_P (TREE_TYPE (@1))
 	&& TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1)))
-    (cmp (bit_ior @0 (convert @1)) @2)))
+    (cmp (bit_ior @0 (convert @1)) @2))
+   (if (VECTOR_INTEGER_TYPE_P (TREE_TYPE (@0))
+	&& VECTOR_INTEGER_TYPE_P (TREE_TYPE (@1))
+	&& vector_nop_conversion_p (TREE_TYPE (@0), TREE_TYPE (@1))
+	&& single_use (@3) && single_use (@4))
+    (cmp (bit_ior @0 (view_convert @1)) @2))))
  /* Transform:
     (x == -1 & y == -1) -> (x & typeof(x)(y)) == -1.
     (x != -1 | y != -1) -> (x & typeof(x)(y)) != -1.  */
  (simplify
-  (bitop (cmp @0 integer_all_onesp@2) (cmp @1 integer_all_onesp))
+  (bitop (cmp@3 @0 integer_all_onesp@2)
+	 (cmp@4 @1 integer_all_onesp))
+  (switch
    (if (INTEGRAL_TYPE_P (TREE_TYPE (@0))
 	&& INTEGRAL_TYPE_P (TREE_TYPE (@1))
 	&& TYPE_PRECISION (TREE_TYPE (@0)) == TYPE_PRECISION (TREE_TYPE (@1)))
-    (cmp (bit_and @0 (convert @1)) @2))))
+    (cmp (bit_and @0 (convert @1)) @2))
+   (if (VECTOR_INTEGER_TYPE_P (TREE_TYPE (@0))
+	&& VECTOR_INTEGER_TYPE_P (TREE_TYPE (@1))
+	&& vector_nop_conversion_p (TREE_TYPE (@0), TREE_TYPE (@1))
+	&& single_use (@3) && single_use (@4))
+    (cmp (bit_and @0 (view_convert @1)) @2)))))
 
 /* Fold (A & ~B) - (A & B) into (A ^ B) - B.  */
 (simplify
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/vec-mask-zero-1.c b/gcc/testsuite/gcc.dg/tree-ssa/vec-mask-zero-1.c
new file mode 100644
index 000000000000..1b31f7274887
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/tree-ssa/vec-mask-zero-1.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-optimized" } */
+/* Combine pairs of vector comparisons against zero or all-ones.  Use
+   different signedness for the operands to exercise the view conversion.  */
+typedef int v4si __attribute__((vector_size (16)));
+typedef unsigned int v4ui __attribute__((vector_size (16)));
+
+v4si f1 (v4si a, v4ui b) { return (a == 0) & (b == 0); }
+v4si f2 (v4si a, v4ui b) { return (a != 0) | (b != 0); }
+v4si f3 (v4si a, v4ui b) { return (a == -1) & (b == -1u); }
+v4si f4 (v4si a, v4ui b) { return (a != -1) | (b != -1u); }
+
+/* Each function keeps one bitwise operation and one comparison.  */
+/* { dg-final { scan-tree-dump-times " == " 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " != " 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " \\| " 2 "optimized" } } */
+/* { dg-final { scan-tree-dump-times " & " 2 "optimized" } } */
diff --git a/gcc/tree.cc b/gcc/tree.cc
index 94cd6fcfb04a..19069eb59607 100644
--- a/gcc/tree.cc
+++ b/gcc/tree.cc
@@ -12467,6 +12467,21 @@ tree_nop_conversion_p (const_tree outer_type, const_tree inner_type)
   return TYPE_MODE (outer_type) == TYPE_MODE (inner_type);
 }
 
+/* Return true iff a view conversion from vector type INNER_TYPE to vector
+   type OUTER_TYPE has the same number of elements and does not change the
+   representation of an element.  */
+
+bool
+vector_nop_conversion_p (const_tree outer_type, const_tree inner_type)
+{
+  return (VECTOR_TYPE_P (outer_type)
+	  && VECTOR_TYPE_P (inner_type)
+	  && known_eq (TYPE_VECTOR_SUBPARTS (outer_type),
+		       TYPE_VECTOR_SUBPARTS (inner_type))
+	  && tree_nop_conversion_p (TREE_TYPE (outer_type),
+				    TREE_TYPE (inner_type)));
+}
+
 /* Return true iff conversion in EXP generates no instruction.  Mark
    it inline so that we fully inline into the stripping functions even
    though we have two uses of this function.  */
diff --git a/gcc/tree.h b/gcc/tree.h
index 61a0639bbff1..e079082a81af 100644
--- a/gcc/tree.h
+++ b/gcc/tree.h
@@ -5789,6 +5789,7 @@ extern bool auto_var_p (const_tree);
 extern bool auto_var_in_fn_p (const_tree, const_tree);
 extern tree build_low_bits_mask (tree, unsigned);
 extern bool tree_nop_conversion_p (const_tree, const_tree);
+extern bool vector_nop_conversion_p (const_tree, const_tree);
 extern tree tree_strip_nop_conversions (tree);
 extern tree tree_strip_sign_nop_conversions (tree);
 extern const_tree strip_invariant_refs (const_tree);
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.