Consider the following dot-product computation:

  uint32_t
  tcp_checksum(int n, uint8_t* data)
  {
    uint32_t sum = 0;
    for (int i=0; i<n; i+=1)
      sum += data[i] * data[i];
    return sum;
  }

At present, following vectorization and the subsequent optimization
passes, we will end up with the following GIMPLE code:

  vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
  masked_op1_1 = VEC_COND_EXPR <loop_mask_1, vect__1, { 0, ... }>;
  vect_patt_1 = DOT_PROD_EXPR <vect__1, masked_op1_1, vectt_sum_1>;

By looking at the input data going into the VEC_COND_EXPR, its mask
and its else value, we can walk back up the USE-DEF chain to see
whether the source of the input data shares the same mask and else
values.  If so, we can safely remove the VEC_COND_EXPR statement from
the cfg, thus resulting in the more optimal variant:

  vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
  vect_patt_1 = DOT_PROD_EXPR <vect__1, vect__1, vectt_sum_1>;

Regression testes on AArch64 & x86_64, no new regressions.

        PR tree-optimization/111770

gcc/ChangeLog:

        * gimple-isel.cc (stmt_is_any_masked_load_p): New.
        (stmt_is_vec_cond_expr_zeroing): Likewise.
        (redundant_condexpr_removal): Likewise.
        (pass_gimple_isel::execute): Add call to
        `redundant_condexpr_removal'.

gcc/testsuite/ChangeLog:

        * gcc.dg/vect/vect-cond-dot.c: New.
---
 gcc/gimple-isel.cc                        | 78 +++++++++++++++++++++++
 gcc/testsuite/gcc.dg/vect/vect-cond-dot.c | 21 ++++++
 2 files changed, 99 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-cond-dot.c

diff --git a/gcc/gimple-isel.cc b/gcc/gimple-isel.cc
index b193e27b183..aa020f402e0 100644
--- a/gcc/gimple-isel.cc
+++ b/gcc/gimple-isel.cc
@@ -1343,6 +1343,82 @@ gimple_isel_builtin_call (gcall *call, 
gimple_stmt_iterator *gsi)
     }
 }
 
+static internal_fn
+stmt_is_any_masked_load_p (gimple *stmt)
+{
+  if (stmt && is_gimple_call (stmt) && gimple_call_internal_p (stmt))
+    {
+      internal_fn ifn = gimple_call_internal_fn (stmt);
+      if (internal_load_fn_p (gimple_call_internal_fn (stmt))
+         && internal_fn_mask_index (ifn) >= 0)
+       return ifn;
+    }
+  return IFN_LAST;
+}
+
+/* Ensure we have a statement of the form:
+     VEC_COND_EXPR <mask, then_val, { 0, ... }>
+   If the else value of STMT is a zero initializer, return the then value,
+   returning NULL_TREE otherwise.  */
+static tree
+stmt_is_vec_cond_expr_zeroing (gimple *stmt)
+{
+  if (is_gimple_assign (stmt))
+    {
+      enum tree_code code = gimple_assign_rhs_code (stmt);
+      if (code == VEC_COND_EXPR)
+       {
+         tree then_val = gimple_assign_rhs2 (stmt);
+         tree else_val = gimple_assign_rhs3 (stmt);
+         if (initializer_zerop (else_val))
+           return then_val;
+       }
+    }
+  return NULL_TREE;
+}
+
+  /* Remove redundant VEC_COND_EXPR statments.
+     Consider the following set of statements:
+
+      vect__1 = .MASK_LOAD (vectp_data, 8B, loop_mask_1, { 0, ... });
+      masked_op1_1 = VEC_COND_EXPR <loop_mask_1, vect__1, { 0, ... }>;
+      vect_patt_1 = DOT_PROD_EXPR <vect__1, masked_op1_1 vect
+
+     By looking at the input data going into the VEC_COND_EXPR, we can walk
+     back up the USE-DEF chain to see whether the source of the input data
+     shares the same mask and else values.  If so, we can safely remove the
+     VEC_COND_EXPR statement from the cfg.  */
+static void
+redundant_condexpr_removal (gimple_stmt_iterator &gsi)
+{
+
+  gimple *vcond_stmt = *gsi;
+  tree then_val = stmt_is_vec_cond_expr_zeroing (vcond_stmt);
+  if (then_val && TREE_CODE (then_val) == SSA_NAME)
+    {
+      gimple *then_defn = SSA_NAME_DEF_STMT (then_val);
+      internal_fn then_ifn = stmt_is_any_masked_load_p (then_defn);
+      if (then_ifn != IFN_LAST)
+       {
+         tree vcond_mask, load_mask, load_else;
+         vcond_mask = gimple_assign_rhs1 (vcond_stmt);
+         load_mask = gimple_call_arg (then_defn,
+                                      internal_fn_mask_index (then_ifn));
+         load_else = gimple_call_arg (then_defn,
+                                      internal_fn_else_index (then_ifn));
+         if (vcond_mask == load_mask && initializer_zerop (load_else))
+           {
+             tree dest = gimple_assign_lhs (gsi_stmt (gsi));
+             replace_uses_by (dest, then_val);
+             gimple *def_stmt = gsi_stmt (gsi);
+             gsi_remove (&gsi, true);
+             release_defs (def_stmt);
+           }
+       }
+    }
+}
+
+
 /* Iterate all gimple statements and perform pre RTL expansion
    GIMPLE massaging to improve instruction selection.  */
 
@@ -1360,6 +1436,8 @@ pass_gimple_isel::execute (struct function *fun)
          /* Give the target first try at replacing the instruction.  */
          cfg_changed |= targetm.instruction_selection (fun, &gsi);
 
+         redundant_condexpr_removal (gsi);
+
          /* Pre-expand VEC_COND_EXPRs to .VCOND* internal function
             calls mapping to supported optabs.  */
          gimple *g = gimple_expand_vec_cond_expr (&gsi);
diff --git a/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c 
b/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
new file mode 100644
index 00000000000..7b6d920c9e5
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-cond-dot.c
@@ -0,0 +1,21 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fdump-tree-optimized" } */
+/* { dg-require-effective-target vect_masked_load } */
+#include <stdint.h>
+
+#define CHECK_DOT(IN, OUT)             \
+OUT check_dot_##OUT(int n, IN* data) {         \
+  OUT sum = 0;                         \
+  for (int i=0; i<n; i+=1) {           \
+    sum += data[i] * data[i];          \
+  }                                    \
+  return sum;                          \
+}
+
+CHECK_DOT (uint8_t, uint32_t);
+CHECK_DOT (int8_t, int32_t);
+CHECK_DOT (int16_t, int64_t);
+
+/* { dg-final { scan-tree-dump-times {vectorized 1 loops} 3 "vect"  } } */
+/* { dg-final { scan-tree-dump-times {[\n\r]\s*(vect__[^ \t=]+) = \.MASK_LOAD 
\([a-zA-Z0-9_.]+, [0-9]+B, (loop_mask_[0-9]+), \{ 0, \.\.\. 
\}\);.*[\n\r]\s+masked_op[^ \t=]+ = VEC_COND_EXPR <\2, \1, \{ 0, \.\.\. \}} 9 
"vect" { target aarch64*-*-* } } } */
+/* { dg-final { scan-tree-dump-not {VEC_COND_EXPR} "optimized" } } */
-- 
2.43.0

Reply via email to