From: Ezra Sitorus <[email protected]>

Currently, vect_recog_dot_prod_pattern can only match sums.

This patch looks for:

       DPROD = X w* Y;          #widen mult
       sum_1 = sum_0 - DPROD;   #subtraction

and replaces it with:

       sum_1 = 0 - sum_0;
       sum_2 = DOT_PROD<X, Y, sum_1>;
       sum_3 = 0 - sum2;

It searches for a similar widening pattern but with subtraction instead.
In particular, for loops such as:

int acc, *a, *b, *c, *d;

for (int i = 0; i < N; i++)
  acc += a[i] * b[i] - c[i] * d[i];

This would ideally get transformed into something like this:

acc_1 = DOT_PROD<c[i], d[i], acc_0>
acc_2 = 0 - acc_2;
acc_3 = DOT_PROD<a[i], b[i], acc_2>
acc_4 = 0 - acc_3;

A possible patch could be done later on to combine
vect_recog_dot_prod_pattern and vect_recog_neg_dot_prod_pattern.
However, since they result in different gimple emission then perhaps
it's not as trivial as it seems.

Note that there is another match.pd patch to aid in this as well.

gcc/ChangeLog:
        * tree-vect-patterns.cc (vect_recog_neg_dot_prod_pattern): Add
          new vect pattern.
          (vect_vect_recog_func_ptrs): Add vect_recog_neg_dot_prod_pattern.

gcc/testsuite/ChangeLog:
        * gcc.dg/vect/vect-reduc-neg-dot-1.c: Add new test.
        * gcc.dg/vect/vect-reduc-neg-dot-2.c: Likewise.
---
 .../gcc.dg/vect/vect-reduc-neg-dot-1.c        |  46 +++++++
 .../gcc.dg/vect/vect-reduc-neg-dot-2.c        |  45 +++++++
 gcc/tree-vect-patterns.cc                     | 124 ++++++++++++++++++
 3 files changed, 215 insertions(+)
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-2.c

diff --git a/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-1.c 
b/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-1.c
new file mode 100644
index 000000000000..17a2e1513294
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-1.c
@@ -0,0 +1,46 @@
+/* { dg-require-effective-target vect_int } */
+
+#include "tree-vect.h"
+
+#define N 50
+
+#ifndef SIGNEDNESS_1
+#define SIGNEDNESS_1 signed
+#define SIGNEDNESS_2 signed
+#define SIGNEDNESS_3 signed
+#endif
+
+SIGNEDNESS_1 int __attribute__ ((noipa))
+f (SIGNEDNESS_1 short res, SIGNEDNESS_3 char *restrict a,
+   SIGNEDNESS_3 char *restrict b)
+{
+  for (__INTPTR_TYPE__ i = 0; i < N; ++i)
+    {
+      res -= (SIGNEDNESS_2 short) (a[i] * b[i]);
+    }
+  return res;
+}
+
+#define BASE ((SIGNEDNESS_3 short) -1 < 0 ? -126 : 4)
+#define OFFSET 20
+
+int
+main (void)
+{
+  check_vect ();
+
+  SIGNEDNESS_3 char a[N], b[N];
+  SIGNEDNESS_1 short expected = 0x1234;
+  for (int i = 0; i < N; ++i)
+    {
+      a[i] = BASE + i * 5;
+      b[i] = BASE + OFFSET + i * 4;
+      asm volatile ("" ::: "memory");
+      expected -= (SIGNEDNESS_2 short) (a[i] * b[i]);
+    }
+  if (f (0x1234, a, b) != expected)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump "vect_recog_neg_dot_prod_pattern: detected" 
"vect" } } */
+/* { dg-final { scan-tree-dump-times "vectorized 1 loop" 1 "vect" { target 
vect_sdot_qi } } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-2.c 
b/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-2.c
new file mode 100644
index 000000000000..e6250d455284
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-reduc-neg-dot-2.c
@@ -0,0 +1,45 @@
+/* { dg-require-effective-target vect_int } */
+
+#include "tree-vect.h"
+
+#define N 50
+
+#ifndef SIGNEDNESS_1
+#define SIGNEDNESS_1 signed
+#define SIGNEDNESS_2 signed
+#define SIGNEDNESS_3 signed
+#endif
+
+SIGNEDNESS_1 char __attribute__ ((noipa))
+f (SIGNEDNESS_1 char res, SIGNEDNESS_3 char *restrict a,
+   SIGNEDNESS_3 char *restrict b)
+{
+  for (__INTPTR_TYPE__ i = 0; i < N; ++i)
+    {
+      res -= (SIGNEDNESS_2 char) (a[i] * b[i]);
+    }
+  return res;
+}
+
+#define BASE ((SIGNEDNESS_3 char) -1 < 0 ? -126 : 4)
+#define OFFSET 20
+
+int
+main (void)
+{
+  check_vect ();
+
+  SIGNEDNESS_3 char a[N], b[N];
+  SIGNEDNESS_1 char expected = 0x12;
+  for (char i = 0; i < N; ++i)
+    {
+      a[i] = BASE + i * 5;
+      b[i] = BASE + OFFSET + i * 4;
+      asm volatile ("" ::: "memory");
+      expected -= (SIGNEDNESS_2 int) (a[i] * b[i]);
+    }
+  if (f (0x12, a, b) != expected)
+    __builtin_abort ();
+}
+
+/* { dg-final { scan-tree-dump-not "vect_recog_neg_dot_prod_pattern: detected" 
"vect" } } */
diff --git a/gcc/tree-vect-patterns.cc b/gcc/tree-vect-patterns.cc
index 5fcc37f053c7..ea75ec7fece7 100644
--- a/gcc/tree-vect-patterns.cc
+++ b/gcc/tree-vect-patterns.cc
@@ -1348,6 +1348,129 @@ vect_recog_dot_prod_pattern (vec_info *vinfo,
   return pattern_stmt;
 }
 
+static gimple *
+vect_recog_neg_dot_prod_pattern (vec_info *vinfo,
+                                stmt_vec_info stmt_vinfo, tree *type_out)
+{
+  tree oprnd0, oprnd1;
+  gimple *last_stmt = stmt_vinfo->stmt;
+  tree type, half_type;
+  gimple *neg_stmt, *dot_stmt, *pattern_stmt;
+  tree neg, dot, var;
+
+  /* Look for the following pattern
+         DX = (TYPE1) X;
+         DY = (TYPE1) Y;
+         DPROD = DX * DY;
+         DDPROD = (TYPE2) DPROD;
+         sum_1 = sum_0 - DDPROD;
+     In which
+     - DX is double the size of X
+     - DY is double the size of Y
+     - DX, DY, DPROD all have the same type but the sign
+       between X, Y and DPROD can differ.
+     - sum is the same size of DPROD or bigger
+     - sum has been recognized as a reduction variable.
+
+     This is equivalent to:
+       DPROD = X w* Y;          #widen mult
+       sum_1 = sum_0 - DPROD;   #subtraction
+
+     Replace with:
+       neg = 0 - sum_0;
+       prod = DOT_PROD<X, Y, neg>;
+       sum_1 = 0 - prod;
+
+       i.e -(X*Y + (-SUM)) = SUM - X*Y
+   */
+
+  /* Starting from LAST_STMT, follow the defs of its uses in search
+     of the above pattern.  */
+
+  if (!vect_reassociating_reduction_p (vinfo, stmt_vinfo, MINUS_EXPR,
+                                      &oprnd0, &oprnd1))
+    return NULL;
+
+  type = TREE_TYPE (gimple_get_lhs (last_stmt));
+
+  vect_unpromoted_value unprom_mult;
+  oprnd1 = vect_look_through_possible_promotion (vinfo, oprnd1, &unprom_mult);
+
+  /* So far so good.  Since last_stmt was detected as a (subtraction) 
reduction,
+     we know that oprnd0 is the reduction variable (defined by a loop-header
+     phi), and oprnd1 is an ssa-name defined by a stmt in the loop body.
+     Left to check that oprnd1 is defined by a (widen_)mult_expr.  */
+  if (!oprnd1)
+    return NULL;
+
+  stmt_vec_info mult_vinfo = vect_get_internal_def (vinfo, oprnd1);
+  if (!mult_vinfo)
+    return NULL;
+
+  /* FORNOW.  Can continue analyzing the def-use chain when this stmt in a phi
+     inside the loop (in case we are analyzing an outer-loop).  */
+  vect_unpromoted_value unprom0[2];
+  enum optab_subtype subtype = optab_vector;
+  if (!vect_widened_op_tree (vinfo, mult_vinfo, MULT_EXPR, WIDEN_MULT_EXPR,
+                            false, 2, unprom0, &half_type, &subtype))
+    return NULL;
+
+  /* If there are two widening operations, make sure they agree on the sign
+     of the extension.  The result of an optab_vector_mixed_sign operation
+     is signed; otherwise, the result has the same sign as the operands.  */
+  if (TYPE_PRECISION (unprom_mult.type) != TYPE_PRECISION (type)
+      && (subtype == optab_vector_mixed_sign
+         ? TYPE_UNSIGNED (unprom_mult.type)
+         : TYPE_SIGN (unprom_mult.type) != TYPE_SIGN (half_type)))
+    return NULL;
+
+  vect_pattern_detected ("vect_recog_neg_dot_prod_pattern", last_stmt);
+
+  /* If the inputs have mixed signs, canonicalize on using the signed
+     input type for analysis.  This also helps when emulating mixed-sign
+     operations using signed operations.  */
+  if (subtype == optab_vector_mixed_sign)
+    half_type = signed_type_for (half_type);
+
+  tree half_vectype;
+  if (!vect_supportable_conv_optab_p (vinfo, type, DOT_PROD_EXPR, half_type,
+                                       type_out, &half_vectype, subtype))
+    {
+      /* We can emulate a mixed-sign dot-product using a sequence of
+        signed dot-products; see vect_emulate_mixed_dot_prod for details.  */
+      if (subtype != optab_vector_mixed_sign
+         || !vect_supportable_conv_optab_p (vinfo, signed_type_for (type),
+                                              DOT_PROD_EXPR, half_type,
+                                              type_out, &half_vectype,
+                                              optab_vector))
+       return NULL;
+
+      *type_out = signed_or_unsigned_type_for (TYPE_UNSIGNED (type),
+                                              *type_out);
+    }
+
+  /* Get the inputs in the appropriate types.  */
+  tree mult_oprnd[2];
+  vect_convert_inputs (vinfo, stmt_vinfo, 2, mult_oprnd, half_type,
+                      unprom0, half_vectype, subtype);
+
+  tree zero = build_zero_cst (type);
+  neg = vect_recog_temp_ssa_var (type, NULL);
+  neg_stmt = gimple_build_assign (neg, MINUS_EXPR, zero, oprnd0);
+  append_pattern_def_seq (vinfo, stmt_vinfo, neg_stmt, *type_out);
+  gimple_set_location (neg_stmt, gimple_location (last_stmt));
+
+  dot = vect_recog_temp_ssa_var (type, NULL);
+  dot_stmt = gimple_build_assign (dot, DOT_PROD_EXPR,
+                                 mult_oprnd[0], mult_oprnd[1], neg);
+  append_pattern_def_seq (vinfo, stmt_vinfo, dot_stmt, *type_out);
+  gimple_set_location (dot_stmt, gimple_location (last_stmt));
+
+  var = vect_recog_temp_ssa_var (type, NULL);
+  pattern_stmt = gimple_build_assign (var, MINUS_EXPR, zero, dot);
+
+  return pattern_stmt;
+}
 
 /* Function vect_recog_sad_pattern
 
@@ -7537,6 +7660,7 @@ static vect_recog_func vect_vect_recog_func_ptrs[] = {
   { vect_recog_mulhs_pattern, "mult_high" },
   { vect_recog_cast_forwprop_pattern, "cast_forwprop" },
   { vect_recog_widen_mult_pattern, "widen_mult" },
+  { vect_recog_neg_dot_prod_pattern, "neg_dot_prod" },
   { vect_recog_dot_prod_pattern, "dot_prod" },
   { vect_recog_sad_pattern, "sad" },
   { vect_recog_widen_sum_pattern, "widen_sum" },
-- 
2.53.0

Reply via email to