This patch forms conditional vector compare IFNs during gimple isel.

The match.pd rules recognize integer vector compare idioms where a compare is
guarded by another vector predicate.  gimple isel then lowers those idioms to
IFN_COND_VEC_CMP or IFN_COND_LEN_VEC_CMP when the target reports support for
the corresponding optab.  If the original compare was only used by the
converted statement, the patch removes that now-dead definition.

The tests for this lowering are kept with the later AArch64 SVE expander
patch, since they require the SVE cond_vec_cmp target support added there.

Bootstrapped Regtested on aarch64-none-linux-gnu,
arm-none-linux-gnueabihf, x86_64-pc-linux-gnu
-m32, -m64 and no issues.

-O3, profiled, profiled -O3 and -O3 -LTO bootstrap tested and no issues.

 Also SPECCPU 2017 and SPECCPU 2026 tested.

OK for master?

gcc/ChangeLog:
        * gimple-isel.cc (gimple_convert_conditional_compare): Declare.
        (gimple_convert_conditional_len_compare): Likewise.
        (maybe_duplicate_comparison): Fix comment.
        (gimple_expand_conditional_compare): New function.
        (pass_gimple_isel::execute): Use it.
        * match.pd (convert_conditional_compare): New match.
        (convert_conditional_len_compare): Likewise.

---
diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
index 
1d346ee124ffdfdccf658ea79cc72cbf80240f81..9bd523cd81be541383ea9c962a9180565cea9f0d
 100644
--- a/gcc/gimple-isel.cc
+++ b/gcc/gimple-isel.cc
@@ -327,8 +327,8 @@ maybe_duplicate_comparison (gassign *stmt, basic_block bb)
   tree lhs = gimple_assign_lhs (stmt);
   unsigned cnt = 0;
 
-  /* This is should not be used for -O0 nor it is not useful
-     when ter is turned off. */
+  /* This should not be used for -O0 and is not useful when TER is turned
+     off.  */
   if (!optimize || !flag_tree_ter)
     return;
 
@@ -376,6 +376,9 @@ maybe_duplicate_comparison (gassign *stmt, basic_block bb)
 extern bool gimple_nop_atomic_bit_test_and_p (tree, tree *,
                                              tree (*) (tree));
 extern bool gimple_nop_convert (tree, tree*, tree (*) (tree));
+extern bool gimple_convert_conditional_compare (tree, tree *, tree (*) (tree));
+extern bool gimple_convert_conditional_len_compare (tree, tree *,
+                                                  tree (*) (tree));
 
 namespace {
 
@@ -488,6 +491,74 @@ convert_atomic_bit_not (enum internal_fn fn, gimple 
*use_stmt,
   return use_stmt;
 }
 
+/* Try to replace STMT with a conditional vector comparison internal function.
+   The generated matchers recognize the source patterns and capture the mask,
+   operands and original comparison SSA name.  If the original comparison is
+   used only by STMT, store its defining statement in *DEAD_CMP_DEF so that the
+   caller can remove it after replacing STMT.  */
+
+static gimple *
+gimple_expand_conditional_compare (gimple_stmt_iterator *gsi,
+                                  gassign **dead_cmp_def)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  *dead_cmp_def = nullptr;
+  tree lhs = gimple_get_lhs (stmt);
+
+  if (!lhs || TREE_CODE (lhs) != SSA_NAME)
+    return nullptr;
+
+  tree ops[6] = {};
+  location_t loc = gimple_location (stmt);
+  bool len_p = false;
+  if (!gimple_convert_conditional_compare (lhs, ops, nullptr)
+      && !gimple_convert_conditional_len_compare (lhs, ops, nullptr))
+    return nullptr;
+  len_p = ops[4] != NULL_TREE;
+
+  unsigned int cmp_opno = len_p ? 5 : 3;
+  tree cmp_name = ops[cmp_opno];
+  if (TREE_CODE (cmp_name) != SSA_NAME)
+    return nullptr;
+
+  gassign *cmp_def = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (cmp_name));
+  if (!cmp_def)
+    return nullptr;
+
+  tree_code code = gimple_assign_rhs_code (cmp_def);
+  if (TREE_CODE_CLASS (code) != tcc_comparison)
+    return nullptr;
+
+  internal_fn ifn = len_p ? IFN_COND_LEN_VEC_CMP : IFN_COND_VEC_CMP;
+  bool supported_p
+    = (len_p
+       ? expand_cond_len_vec_cmp_p (code, ops[0], ops[1], ops[2],
+                                   ops[3], ops[4])
+       : expand_cond_vec_cmp_p (code, ops[0], ops[1], ops[2]));
+  if (!supported_p)
+    return nullptr;
+
+  auto_vec<tree, 6> args;
+  args.quick_push (build_int_cst (integer_type_node, code));
+  args.quick_push (ops[0]);
+  args.quick_push (ops[1]);
+  args.quick_push (ops[2]);
+  if (len_p)
+    {
+      args.quick_push (ops[3]);
+      args.quick_push (ops[4]);
+    }
+
+  gcall *call = gimple_build_call_internal_vec (ifn, args);
+  gimple_call_set_lhs (call, lhs);
+  gimple_set_location (call, loc);
+  use_operand_p use_p;
+  gimple *use_stmt;
+  if (single_imm_use (cmp_name, &use_p, &use_stmt) && use_stmt == stmt)
+    *dead_cmp_def = cmp_def;
+  return call;
+}
+
 /* Optimize
      mask_2 = 1 << cnt_1;
      _4 = __atomic_fetch_or_* (ptr_6, mask_2, _3);
@@ -1369,6 +1440,20 @@ pass_gimple_isel::execute (struct function *fun)
          /* Give the target first try at replacing the instruction.  */
          cfg_changed |= targetm.instruction_selection (fun, &gsi);
 
+         gimple *isel_stmt = NULL;
+         gassign *dead_cmp_def = NULL;
+         if (!gsi_end_p (gsi))
+           isel_stmt = gimple_expand_conditional_compare (&gsi, &dead_cmp_def);
+         if (isel_stmt)
+           {
+             gsi_replace (&gsi, isel_stmt, true);
+             if (dead_cmp_def)
+               {
+                 gimple_stmt_iterator cmp_gsi = gsi_for_stmt (dead_cmp_def);
+                 gsi_remove (&cmp_gsi, true);
+               }
+           }
+
          /* Pre-expand VEC_COND_EXPRs to .VCOND* internal function
             calls mapping to supported optabs.  */
          gimple *g = gimple_expand_vec_cond_expr (&gsi);
@@ -1411,4 +1496,3 @@ make_pass_gimple_isel (gcc::context *ctxt)
 {
   return new pass_gimple_isel (ctxt);
 }
-
diff --git a/gcc/match.pd b/gcc/match.pd
index 
c1aba410b66e38c947a4d52064fda1594934f297..db5038b7dccd030a09b67d5070a2ce2b74651184
 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -13364,3 +13364,20 @@ and,
  (if (INTEGRAL_TYPE_P (type))
    (with { tree itype = TREE_TYPE (@2); }
     (convert (minus @2 (convert:itype @1))))))
+
+#if GIMPLE
+
+(for cmp (tcc_comparison)
+ (match (convert_conditional_compare @0 @1 @2 @3)
+  (bit_and:c @0 (cmp@3 @1 @2))
+  (if (VECTOR_BOOLEAN_TYPE_P (type)
+       && !FLOAT_TYPE_P (TREE_TYPE (@1)))))
+
+/* Transform .VCOND_MASK_LEN (a `cmp` b, {-1, ...}, {0, ...}, len, bias)
+   into cond_len_vec_cmp (cmp, mask, a, b, len, bias).  */
+ (match (convert_conditional_len_compare @0 @1 @2 @3 @4 @5)
+  (IFN_VCOND_MASK_LEN @0 (cmp@5 @1 @2) integer_minus_onep integer_zerop @3 @4)
+  (if (VECTOR_BOOLEAN_TYPE_P (type)
+       && !FLOAT_TYPE_P (TREE_TYPE (@1))))))
+
+#endif


-- 
diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
index 1d346ee124ffdfdccf658ea79cc72cbf80240f81..9bd523cd81be541383ea9c962a9180565cea9f0d 100644
--- a/gcc/gimple-isel.cc
+++ b/gcc/gimple-isel.cc
@@ -327,8 +327,8 @@ maybe_duplicate_comparison (gassign *stmt, basic_block bb)
   tree lhs = gimple_assign_lhs (stmt);
   unsigned cnt = 0;
 
-  /* This is should not be used for -O0 nor it is not useful
-     when ter is turned off. */
+  /* This should not be used for -O0 and is not useful when TER is turned
+     off.  */
   if (!optimize || !flag_tree_ter)
     return;
 
@@ -376,6 +376,9 @@ maybe_duplicate_comparison (gassign *stmt, basic_block bb)
 extern bool gimple_nop_atomic_bit_test_and_p (tree, tree *,
 					      tree (*) (tree));
 extern bool gimple_nop_convert (tree, tree*, tree (*) (tree));
+extern bool gimple_convert_conditional_compare (tree, tree *, tree (*) (tree));
+extern bool gimple_convert_conditional_len_compare (tree, tree *,
+						   tree (*) (tree));
 
 namespace {
 
@@ -488,6 +491,74 @@ convert_atomic_bit_not (enum internal_fn fn, gimple *use_stmt,
   return use_stmt;
 }
 
+/* Try to replace STMT with a conditional vector comparison internal function.
+   The generated matchers recognize the source patterns and capture the mask,
+   operands and original comparison SSA name.  If the original comparison is
+   used only by STMT, store its defining statement in *DEAD_CMP_DEF so that the
+   caller can remove it after replacing STMT.  */
+
+static gimple *
+gimple_expand_conditional_compare (gimple_stmt_iterator *gsi,
+				   gassign **dead_cmp_def)
+{
+  gimple *stmt = gsi_stmt (*gsi);
+  *dead_cmp_def = nullptr;
+  tree lhs = gimple_get_lhs (stmt);
+
+  if (!lhs || TREE_CODE (lhs) != SSA_NAME)
+    return nullptr;
+
+  tree ops[6] = {};
+  location_t loc = gimple_location (stmt);
+  bool len_p = false;
+  if (!gimple_convert_conditional_compare (lhs, ops, nullptr)
+      && !gimple_convert_conditional_len_compare (lhs, ops, nullptr))
+    return nullptr;
+  len_p = ops[4] != NULL_TREE;
+
+  unsigned int cmp_opno = len_p ? 5 : 3;
+  tree cmp_name = ops[cmp_opno];
+  if (TREE_CODE (cmp_name) != SSA_NAME)
+    return nullptr;
+
+  gassign *cmp_def = dyn_cast<gassign *> (SSA_NAME_DEF_STMT (cmp_name));
+  if (!cmp_def)
+    return nullptr;
+
+  tree_code code = gimple_assign_rhs_code (cmp_def);
+  if (TREE_CODE_CLASS (code) != tcc_comparison)
+    return nullptr;
+
+  internal_fn ifn = len_p ? IFN_COND_LEN_VEC_CMP : IFN_COND_VEC_CMP;
+  bool supported_p
+    = (len_p
+       ? expand_cond_len_vec_cmp_p (code, ops[0], ops[1], ops[2],
+				    ops[3], ops[4])
+       : expand_cond_vec_cmp_p (code, ops[0], ops[1], ops[2]));
+  if (!supported_p)
+    return nullptr;
+
+  auto_vec<tree, 6> args;
+  args.quick_push (build_int_cst (integer_type_node, code));
+  args.quick_push (ops[0]);
+  args.quick_push (ops[1]);
+  args.quick_push (ops[2]);
+  if (len_p)
+    {
+      args.quick_push (ops[3]);
+      args.quick_push (ops[4]);
+    }
+
+  gcall *call = gimple_build_call_internal_vec (ifn, args);
+  gimple_call_set_lhs (call, lhs);
+  gimple_set_location (call, loc);
+  use_operand_p use_p;
+  gimple *use_stmt;
+  if (single_imm_use (cmp_name, &use_p, &use_stmt) && use_stmt == stmt)
+    *dead_cmp_def = cmp_def;
+  return call;
+}
+
 /* Optimize
      mask_2 = 1 << cnt_1;
      _4 = __atomic_fetch_or_* (ptr_6, mask_2, _3);
@@ -1369,6 +1440,20 @@ pass_gimple_isel::execute (struct function *fun)
 	  /* Give the target first try at replacing the instruction.  */
 	  cfg_changed |= targetm.instruction_selection (fun, &gsi);
 
+	  gimple *isel_stmt = NULL;
+	  gassign *dead_cmp_def = NULL;
+	  if (!gsi_end_p (gsi))
+	    isel_stmt = gimple_expand_conditional_compare (&gsi, &dead_cmp_def);
+	  if (isel_stmt)
+	    {
+	      gsi_replace (&gsi, isel_stmt, true);
+	      if (dead_cmp_def)
+		{
+		  gimple_stmt_iterator cmp_gsi = gsi_for_stmt (dead_cmp_def);
+		  gsi_remove (&cmp_gsi, true);
+		}
+	    }
+
 	  /* Pre-expand VEC_COND_EXPRs to .VCOND* internal function
 	     calls mapping to supported optabs.  */
 	  gimple *g = gimple_expand_vec_cond_expr (&gsi);
@@ -1411,4 +1496,3 @@ make_pass_gimple_isel (gcc::context *ctxt)
 {
   return new pass_gimple_isel (ctxt);
 }
-
diff --git a/gcc/match.pd b/gcc/match.pd
index c1aba410b66e38c947a4d52064fda1594934f297..db5038b7dccd030a09b67d5070a2ce2b74651184 100644
--- a/gcc/match.pd
+++ b/gcc/match.pd
@@ -13364,3 +13364,20 @@ and,
  (if (INTEGRAL_TYPE_P (type))
    (with { tree itype = TREE_TYPE (@2); }
     (convert (minus @2 (convert:itype @1))))))
+
+#if GIMPLE
+
+(for cmp (tcc_comparison)
+ (match (convert_conditional_compare @0 @1 @2 @3)
+  (bit_and:c @0 (cmp@3 @1 @2))
+  (if (VECTOR_BOOLEAN_TYPE_P (type)
+       && !FLOAT_TYPE_P (TREE_TYPE (@1)))))
+
+/* Transform .VCOND_MASK_LEN (a `cmp` b, {-1, ...}, {0, ...}, len, bias)
+   into cond_len_vec_cmp (cmp, mask, a, b, len, bias).  */
+ (match (convert_conditional_len_compare @0 @1 @2 @3 @4 @5)
+  (IFN_VCOND_MASK_LEN @0 (cmp@5 @1 @2) integer_minus_onep integer_zerop @3 @4)
+  (if (VECTOR_BOOLEAN_TYPE_P (type)
+       && !FLOAT_TYPE_P (TREE_TYPE (@1))))))
+
+#endif

Reply via email to