[PATCH v2 11/13] vect: Add HSSR transformation.

Alfie Richards <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
Add the transformation code required to generate HSSR loops.

This includes adding a "fixup" BB for handling when the HSSR loads return
partial reads.

gcc/ChangeLog:

	* tree-vect-loop-manip.cc (vect_get_loop_iv_increment):
	Add support for HSSR iv increments.
	(vect_add_hssr_read): New function.
	(vect_add_hssr_fixup_controls): New function.
	* tree-vect-loop.cc (vect_get_loop_mask): Add logic for getting
	the governing mask from the HSSR region (if applicable).
	(vect_transform_loop): Add logic for HSSR.
	* tree-vect-slp.cc (vect_schedule_slp_node): Add logic to make
	sure nodes requiring a HSSR mask come after the HSSR loads.
	(vect_schedule_slp): Add logic for inserting HSSR read points.
	* tree-vect-stmts.cc (vect_finish_replace_stmt): Add logic for
	inserting HSSR loads.
	(vectorizable_load): Add logic for generating HSSR loads.
	* tree-vectorizer.h (LOOP_VINFO_IV_INCREMENT_INVARIANT_P): Add
	case for HSSR loops.
	(vect_add_hssr_fixup_controls): New function.
	(vect_add_hssr_read): New function.

gcc/testsuite/ChangeLog:

	* gcc.target/aarch64/sve/ffr_1.c: New test.
	* gcc.target/aarch64/sve/ffr_11.c: New test.
	* gcc.target/aarch64/sve/ffr_12.c: New test.
	* gcc.target/aarch64/sve/ffr_13.c: New test.
	* gcc.target/aarch64/sve/ffr_14.c: New test.
	* gcc.target/aarch64/sve/ffr_2.c: New test.
	* gcc.target/aarch64/sve/ffr_3.c: New test.
	* gcc.target/aarch64/sve/ffr_4.c: New test.
	* gcc.target/aarch64/sve/ffr_5.c: New test.
	* gcc.target/aarch64/sve/ffr_6.c: New test.
	* gcc.target/aarch64/sve/ffr_6_run.c: New test.
	* gcc.target/aarch64/sve/ffr_7.c: New test.
	* gcc.target/aarch64/sve/ffr_8.c: New test.
	* gcc.target/aarch64/sve/ffr_9.c: New test.
---
 gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c  |  16 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c |  13 +
 gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c |  17 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c |  15 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c |  18 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c  |  35 +++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c  |  44 ++++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c  |  25 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c  |  25 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c  |  43 ++++
 .../gcc.target/aarch64/sve/ffr_6_run.c        |  82 ++++++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c  |  19 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c  |  17 ++
 gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c  |  18 ++
 gcc/tree-vect-loop-manip.cc                   | 234 ++++++++++++++++++
 gcc/tree-vect-loop.cc                         |  73 +++++-
 gcc/tree-vect-slp.cc                          |  17 ++
 gcc/tree-vect-stmts.cc                        |  61 +++--
 gcc/tree-vectorizer.h                         |   4 +-
 19 files changed, 758 insertions(+), 18 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c

diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
new file mode 100644
index 00000000000..3027c6d69f0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
@@ -0,0 +1,16 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+
+#include <stdint.h>
+
+int foo (int *restrict a, int *restrict b, int N) {
+  for (int i = 0; i < N; i++)
+   {
+     if (a[i] == b[i])
+       return 1;
+   }
+   return 0;
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-times {\tldff1w\tz[0-9]+\.s, p[0-9]+/z,} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
new file mode 100644
index 00000000000..dedea70dd3b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 --param=vect-hssr-usage=2" } */
+
+// Check this doesnt ICE
+
+int a;
+char b[8];
+int c() {
+  unsigned d = 0;
+  for (; d < a; ++d)
+    if (b[d + 1])
+      return 0;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
new file mode 100644
index 00000000000..db70c7ee87b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-mcpu=neoverse-v3 -O3 --param=vect-hssr-usage=2" } */
+
+// Check this doesnt ICE
+
+char a;
+int b, d;
+char *c;
+void
+e ()
+{
+  int f;
+  while (f < d && c + f && (&a)[f] == c[f])
+    f++;
+  if (f)
+    b = d;
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
new file mode 100644
index 00000000000..a6b49c3c9df
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
@@ -0,0 +1,15 @@
+/* { dg-do compile } */
+/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=2" } */
+
+// Check this doesn't ICE
+
+int c[3];
+long *d;
+int e, f, g;
+void h() {
+  for (;;) {
+    for (int i = 0; i < g; i++)
+      if (c[f + i] || d[e])
+        break;
+  }
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
new file mode 100644
index 00000000000..bba9893c3a0
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=2" } */
+
+// Check this doesn't ICE
+
+int a, d, e, f, g;
+int *b, *c;
+void h() {
+  int i, j = 0;
+  for (; a; j++) {
+    for (; g; g++)
+      f = b[j];
+    for (; i < e; i++)
+      if (c[i])
+        break;
+    d = i;
+  }
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
new file mode 100644
index 00000000000..45198266cee
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
@@ -0,0 +1,35 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+
+#include <stdint.h>
+
+#define type uint16_t
+
+void foo(
+        type * const restrict dst1,
+        type * const restrict dst2,
+        type * const restrict src1,
+        type * const restrict src1b,
+        type * const restrict src2,
+        type * const restrict src3,
+        unsigned int n)
+{
+
+    for (int i = 0; i < n; i++) {
+        type v1 = src1[i];
+        type v1b = src1b[i];
+        if (v1 == v1b) {break;}
+        type v2 = src2[i];
+        dst1[i] = v1 + v2;
+        if (v2 == 1) {break;}
+        type v3 = src3[i];
+        dst2[i] = v1 + v2 + v3;
+    }
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-times {\tldff1h\tz[0-9]+\.h, p[0-9]+/z,} 3 } } */
+
+// We need two hssr regions here so there should be 2 reads
+ 
+/* { dg-final { scan-assembler-times {\trdffrs?\tp[0-9]+\.b} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
new file mode 100644
index 00000000000..b52869c99a3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-final { check-function-bodies "**" "" "" } } */
+
+#include <stdint.h>
+
+#define type uint16_t
+
+type call [[gnu::simd, gnu::const]] (type);
+
+void foo(
+        type * const restrict dst1,
+        type * const restrict src1,
+        type * const restrict src1b,
+        type * const restrict src2,
+        unsigned int n)
+{
+
+    for (int i = 0; i < n; i++) {
+        type v1b = call (src1b[i]);
+        type v1 = src1[i];
+        if (v1 == v1b) {break;}
+        type v2 = src2[i];
+        dst1[i] = v1 + v2;
+    }
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+
+// Make sure the call does not come between the loads and the rdffr
+// As the call can clobber the HSSR state
+/*
+** foo:
+** ...
+** 	ldff1h	z[0-9]+\.h, p[0-9]/z, \[x[0-9]+\]
+** 	ldff1h	z[0-9]+\.h, p[0-9]/z, \[x[0-9]+\]
+** ...
+** 	rdffrs	p[0-9]+.b, p[0-9]+/z
+** ...
+** 	b.nlast	\.L[0-9]+
+** ...
+** 	bl	_ZGVsMxv_call
+** ...
+*/
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
new file mode 100644
index 00000000000..9def8e348ec
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+
+#include <stdint.h>
+
+#define type uint16_t
+
+ #include <stdint.h>
+void
+foo (uint16_t *const restrict dst1,
+     uint8_t *const restrict src1,
+     uint16_t *const restrict src2, unsigned int n)
+{
+  for (int i = 0; i < n && src1[2 * i] + src1[2 * i + 1] != 5; i++)
+    {
+      uint8_t v1 = src1[2 * i];
+      uint8_t v1a = src1[2 * i + 1];
+      uint16_t v2 = src2[i];
+      dst1[i] = v1 + v1a + v2;
+    }
+}
+
+/* This should not be vectorized with HSSR as we can't handle multiple lanes.  */
+/* { dg-final { scan-tree-dump-not "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-not {\tldff1} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
new file mode 100644
index 00000000000..e41c453188a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+
+#include <stdint.h>
+
+#define type uint16_t
+
+void
+foo (uint16_t *const restrict dst1,
+     uint16_t *const restrict src1,
+     uint16_t *const restrict src2,
+     unsigned int n)
+{
+  for (int i = 0; i < n; i++)
+    {
+      uint16_t v1 = src1[i];
+      uint16_t v2 = src1[i] == 0 ? 0 : src2[i];
+      if (v1 + v2 == 100) break;
+      dst1[i] = v1 + v2;
+    }
+}
+
+/* This should not be vectorized with HSSR as can not mask HSSR reads.  */
+/* { dg-final { scan-tree-dump-not "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-not {\tldff1} } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
new file mode 100644
index 00000000000..4f225746be3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
@@ -0,0 +1,43 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 --param=vect-hssr-usage=2" } */
+
+#include <stdint.h>
+
+__attribute__ ((noipa))
+int
+foo_vect (uint16_t *const restrict out,
+     uint16_t *const restrict src1,
+     uint16_t *const restrict src2,
+     unsigned int n)
+{
+  for (int i = 0; i < n; i++)
+    {
+      uint16_t v1 = src1[i];
+      uint16_t v2 = src2[i];
+      *out += v1 == v2;
+      if (v1 == 0)
+	return 1;
+    }
+    return 0;
+}
+
+__attribute__ ((noipa))
+int
+foo_no_vect (uint16_t *const restrict out,
+             uint16_t *const restrict src1,
+	     uint16_t *const restrict src2,
+	     unsigned int n)
+{
+#pragma GCC novector
+  for (int i = 0; i < n; i++)
+    {
+      uint16_t v1 = src1[i];
+      uint16_t v2 = src2[i];
+      *out += v1 == v2;
+      if (v1 == 0)
+	return 1;
+    }
+  return 0;
+}
+
+/* { dg-final { scan-assembler-times {\tldff1} 1 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
new file mode 100644
index 00000000000..452c7307cff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
@@ -0,0 +1,82 @@
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3  --param=vect-hssr-usage=2" } */
+
+#include "ffr_6.c"
+#include <stdint.h>
+#include <stdlib.h>
+#include <assert.h>
+
+#define N 10000
+int main () {
+  uint16_t *b1 = malloc (sizeof(uint16_t) * N);
+  uint16_t *b2 = malloc (sizeof(uint16_t) * N);
+
+  for (int i = 0; i < N; i++) {
+    b1[i] = 1 + (i % 3) * 3;
+    b2[i] = 1 + (i % 11) * 11;
+    /* These should only intersect when i % 7 = 0 and i % 11 = 0 */
+  }
+
+  uint16_t vect_out;
+  uint16_t no_vect_out;
+  int res_vect;
+  int res_no_vect;
+
+  /* Check while aligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1, b2, N);
+  res_no_vect = foo_no_vect(&no_vect_out, b1, b2, N);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+
+  /* Check while mutually misaligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1+5, b2+5, N-5);
+  res_no_vect = foo_no_vect(&no_vect_out, b1+5, b2+5, N-5);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+
+  /* Check while independently misaligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1+9, b2+25, N-25);
+  res_no_vect = foo_no_vect(&no_vect_out, b1+9, b2+25, N-25);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+
+  /* insert an early break and check it still works */
+
+  b1[N/2+75] = 0;
+
+  /* Check while aligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1, b2, N);
+  res_no_vect = foo_no_vect(&no_vect_out, b1, b2, N);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+
+  /* Check while mutually misaligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1+5, b2+5, N-5);
+  res_no_vect = foo_no_vect(&no_vect_out, b1+5, b2+5, N-5);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+
+  /* Check while independently misaligned */
+  vect_out = 0;
+  no_vect_out = 0;
+  res_vect = foo_vect(&vect_out, b1+9, b2+25, N-25);
+  res_no_vect = foo_no_vect(&no_vect_out, b1+9, b2+25, N-25);
+
+  assert (res_vect == res_no_vect);
+  assert (vect_out == no_vect_out);
+}
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
new file mode 100644
index 00000000000..1f508eb370e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
@@ -0,0 +1,19 @@
+/* { dg-do compile } */
+
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic-armv9-a --param=vect-hssr-usage=2" } */
+// Options to try get the cost model to select VNx4SI
+
+#include <stdint.h>
+
+int foo (char *restrict a, char *restrict b, int N) {
+  for (int i = 0; i < N; i++)
+   {
+     if (a[i] + b[i] == 0)
+       return 1;
+   }
+   return 0;
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+/* Check it actually uses the widening loads as desired.  */
+/* { dg-final { scan-assembler-times {\tldff1b\tz[0-9]+\.s, p[0-9]+/z,} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
new file mode 100644
index 00000000000..bdecf2e1875
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic --param=vect-hssr-usage=2" } */
+// Options to get the cost model to select VNx8HI
+
+#include <stdint.h>
+
+int foo (char *restrict a, char *restrict b, int N) {
+  for (int i = 0; i < N; i++)
+   {
+     if (a[i] + b[i] == 0)
+       return 1;
+   }
+   return 0;
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-times {\tldff1b\tz[0-9]+\.h, p[0-9]+/z,} 2 } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c
new file mode 100644
index 00000000000..5b4a38355af
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c
@@ -0,0 +1,18 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+
+/* Check we use HSSR at O2. */
+
+#include <stdint.h>
+
+int foo (int *restrict a, int *restrict b, int N) {
+  for (int i = 0; i < N; i++)
+   {
+     if (a[i] == b[i])
+       return 1;
+   }
+   return 0;
+}
+
+/* { dg-final { scan-tree-dump "Will use hssr" "vect" } } */
+/* { dg-final { scan-assembler-times {\tldff1w\tz[0-9]+\.s, p[0-9]+/z,} 2 } } */
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index d7a815ebaec..28a5978d9a3 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -3212,6 +3212,23 @@ vect_get_loop_iv_increment (loop_vec_info loop_vinfo)
 
       return iv_increment;
     }
+  if (LOOP_VINFO_USING_HSSR_P (loop_vinfo))
+    {
+      slp_instance instance;
+      int i;
+      FOR_EACH_VEC_ELT (LOOP_VINFO_SLP_INSTANCES (loop_vinfo), i,
+			instance)
+	if (SLP_INSTANCE_HSSR_REGION (instance))
+	  {
+	    SLP_INSTANCE_HSSR_REGION (instance)->num_iter
+	      = make_temp_ssa_name (LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo),
+				    NULL, "hssr_num_iters");
+	    SSA_NAME_DEF_STMT (SLP_INSTANCE_HSSR_REGION (instance)->num_iter)
+	      = gimple_build_nop ();
+	    return SLP_INSTANCE_HSSR_REGION (instance)->num_iter;
+	  }
+      gcc_unreachable ();
+    }
   else
     return build_int_cst (sizetype, LOOP_VINFO_VECT_FACTOR (loop_vinfo));
 }
@@ -4958,3 +4975,220 @@ vect_can_add_hssr_controls (loop_vec_info loop_vinfo)
 
   return true;
 }
+
+/* Insert the HSSR read.  */
+void
+vect_add_hssr_read (vec_info *vinfo, slp_instance instance)
+{
+  loop_vec_info loop_vinfo = dyn_cast <loop_vec_info> (vinfo);
+  gcc_assert (loop_vinfo);
+
+  gimple_stmt_iterator insert_point
+    = gsi_for_stmt (SLP_INSTANCE_HSSR_REGION (instance)->hssr_read_point);
+
+  gcall *hssr_read_call = gimple_build_call_internal (IFN_READ_HSSR_STATE, 0);
+  gsi_insert_after (&insert_point, hssr_read_call, GSI_SAME_STMT);
+
+  update_ssa (TODO_update_ssa_only_virtuals);
+
+  SLP_INSTANCE_HSSR_REGION (instance)->hssr_read_point = hssr_read_call;
+}
+
+/* Inserts fixup block for HSSR controls.
+
+   This inserts the following logic:
+
+   <bb 0>:
+     v1 = .MASK_FF_HSSR_LOAD (src1, 64B, loop_mask, { 0, ... });
+     v2 = .MASK_FF_HSSR_LOAD (src2, 64B, loop_mask, { 0, ... });
+     hssr_mask = .READ_FAULT_STATE ();
+     if (hssr_mask == { -1, ... })
+       goto <bb 2>; [99.95%]
+     else
+       goto <bb 1>; [0.05%]
+
+   <bb 1>:
+     .SET_FAULT_STATE ({ -1, ... });
+     hssr_loop_mask = loop_mask & hssr_mask;
+      num_iters = .COUNT_ACTIVE (hssr_mask);
+
+   <bb 2>:
+     # loop_mask_1 = PHI <loop_mask(0), hssr_loop_mask(1)>
+     # num_iters_56 = PHI <POLY_INT_CST [4, 4](0), num_iters (1)>
+
+     ... Logic using loop_mask_1 and incrementing IV's by num_iters_56
+    */
+void
+vect_add_hssr_fixup_controls (loop_vec_info loop_vinfo)
+{
+  gcc_assert (LOOP_VINFO_USING_HSSR_P (loop_vinfo));
+  gcc_assert (LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo));
+  slp_instance instance;
+  int index;
+  FOR_EACH_VEC_ELT_REVERSE (LOOP_VINFO_SLP_INSTANCES (loop_vinfo), index,
+			    instance)
+    {
+      /* This instance does not define an HSSR region.  No work to do.  */
+      if (!SLP_INSTANCE_HSSR_REGION (instance))
+	continue;
+
+      /* At the very least the GCOND must use the HSSR mask so it can't be that
+	 there are no uses.  */
+      gcc_assert (!SLP_INSTANCE_HSSR_REGION (instance)->controls.is_empty ());
+
+      hssr_region *region = SLP_INSTANCE_HSSR_REGION (instance);
+      vec_loop_masks *masks = &LOOP_VINFO_MASKS (loop_vinfo);
+      rgroup_controls *base_rgm = &masks->rgc_vec[0];
+      tree mask_type = base_rgm->type;
+
+      /* The base mask is established by reading from hssr state.  */
+      tree hssr_read_val = make_temp_ssa_name (mask_type, NULL, "hssr_mask");
+      gimple_call_set_lhs (region->hssr_read_point, hssr_read_val);
+
+      gcall *detect_fault_call
+	= gimple_build_call_internal (IFN_DETECT_HSSR_FAULT, 1,
+				      hssr_read_val);
+      tree fault_detected_p = make_temp_ssa_name (boolean_type_node,
+						  detect_fault_call,
+						  "partial_read_p");
+      gimple_call_set_lhs (detect_fault_call, fault_detected_p);
+      gimple_stmt_iterator gsi_read = gsi_for_stmt (region->hssr_read_point);
+      gsi_insert_after (&gsi_read, detect_fault_call, GSI_NEW_STMT);
+
+      /* The fixup is taken if the load is partial.  */
+      gcond *cond = gimple_build_cond (EQ_EXPR, fault_detected_p,
+				       boolean_false_node, NULL_TREE,
+				       NULL_TREE);
+      gsi_insert_after (&gsi_read, cond, GSI_SAME_STMT);
+
+      /* Split the BB after the FF reads and setup control flow for the fixup
+	 block.  */
+      basic_block read_bb = cond->bb;
+      edge non_hssr_edge = split_block (read_bb, cond);
+      non_hssr_edge->flags = EDGE_TRUE_VALUE;
+      non_hssr_edge->probability = profile_probability::very_likely ();
+
+      basic_block continuation_bb = non_hssr_edge->dest;
+      continuation_bb->count = read_bb->count;
+
+      basic_block fixup_bb = create_empty_bb (read_bb);
+      fixup_bb->count = read_bb->count.apply_scale (1, 100);
+
+      edge fixup_edge = make_edge (read_bb, fixup_bb, EDGE_FALSE_VALUE);
+      fixup_edge->probability = profile_probability::very_unlikely ();
+
+      edge fixup_cont_edge = make_edge (fixup_bb, continuation_bb,
+					EDGE_FALLTHRU);
+      fixup_cont_edge->probability = profile_probability::always ();
+
+      set_immediate_dominator (CDI_DOMINATORS, fixup_bb, read_bb);
+      set_immediate_dominator (CDI_DOMINATORS, continuation_bb, read_bb);
+
+      add_bb_to_loop (fixup_bb, LOOP_VINFO_LOOP (loop_vinfo));
+
+      /* Mask nodes in the fixup.  */
+      vec<vec<tree>> temp_masks = {};
+      temp_masks.safe_grow_cleared (masks->rgc_vec.length ());
+      temp_masks[0].safe_grow (1);
+
+      gimple_seq fixup_logic = NULL;
+
+      /* We always need the loop to have the nV=1 mask.  */
+      if (base_rgm->controls.is_empty ())
+	{
+	  base_rgm->controls.safe_grow_cleared (1, true);
+	  tree mask = make_temp_ssa_name (mask_type, NULL, "loop_mask");
+	  /* Provide a dummy definition until the real one is available.  */
+	  base_rgm->controls[0] = mask;
+	}
+      if (region->controls[0].is_empty ())
+	{
+	  region->controls[0].safe_grow_cleared (1, true);
+	  region->controls[0][0]
+	    = make_temp_ssa_name (mask_type, NULL, "loop_mask_AFTER_HSSR");
+	}
+      tree previous_mask = (region->prev_region)
+			     ? region->prev_region->controls[0][0]
+			     : masks->rgc_vec[0].controls[0];
+
+      gcall *call = gimple_build_call_internal (IFN_SET_HSSR_STATE, 1,
+						build_all_ones_cst (mask_type));
+      gimple_seq_add_stmt (&fixup_logic, call);
+
+      tree num_iters = gimple_build (&fixup_logic, IFN_COND_COUNT_ACTIVE,
+				     sizetype, previous_mask, hssr_read_val,
+				     build_zero_cst (mask_type));
+      temp_masks[0][0] = make_temp_ssa_name (mask_type, NULL, "hssr_loop_mask");
+      gimple *hssr_and_loop
+	= gimple_build_assign (temp_masks[0][0], BIT_AND_EXPR, previous_mask,
+			       hssr_read_val);
+      gimple_seq_add_stmt (&fixup_logic, hssr_and_loop);
+
+      /* PHI node for the NV=1 mask in the region.  */
+      gphi *phi = create_phi_node (region->controls[0][0], continuation_bb);
+      add_phi_arg (phi, previous_mask, non_hssr_edge, UNKNOWN_LOCATION);
+      add_phi_arg (phi, temp_masks[0][0], fixup_cont_edge, UNKNOWN_LOCATION);
+
+      int i;
+      rgroup_controls *rgc;
+      FOR_EACH_VEC_ELT (masks->rgc_vec, i, rgc)
+	{
+	  /* The base case is handled above.  */
+	  if (i == 0)
+	    continue;
+
+	  unsigned int nmasks = i + 1;
+	  vec<tree> *this_masks = &region->controls[i];
+	  vec<tree> *previous_masks = region->prev_region
+					? &region->prev_region->controls[i]
+					: &rgc->controls;
+
+	  vec<tree> *half_controls_temp = &(temp_masks[nmasks / 2 - 1]);
+	  rgroup_controls *half_controls_rgc = &masks->rgc_vec[nmasks / 2 - 1];
+
+	  vec<tree> *this_temps = &temp_masks[i];
+	  this_temps->safe_grow_cleared (nmasks);
+	  for (unsigned j = 0; j < nmasks; j++)
+	    if (!(*this_temps)[j])
+	      (*this_temps)[j] = make_temp_ssa_name (rgc->type, NULL,
+						     "hssr_mask");
+
+	  /* Permute the masks from the half rgoup to this one.  */
+	  gcc_assert (vect_maybe_permute_loop_masks (&fixup_logic, rgc,
+						     half_controls_rgc,
+						     this_temps,
+						     half_controls_temp));
+
+	  /* Create PHI nodes for the masks.  */
+	  for (unsigned int j = 0; j < nmasks; ++j)
+	    {
+	      gphi *mask_phi
+		= create_phi_node ((*this_masks)[j], continuation_bb);
+	      add_phi_arg (mask_phi, (*previous_masks)[j], non_hssr_edge,
+			   UNKNOWN_LOCATION);
+	      add_phi_arg (mask_phi, (*this_temps)[j], fixup_cont_edge,
+			   UNKNOWN_LOCATION);
+	    }
+	}
+
+      /* Initialize the num_iter nodes.  */
+      if (!region->num_iter)
+	region->num_iter
+	  = make_temp_ssa_name (LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo), NULL,
+				"hssr_num_iters");
+
+      gphi *num_iter_phi = create_phi_node (region->num_iter, continuation_bb);
+      add_phi_arg (num_iter_phi,
+		   region->prev_region
+		     ? region->prev_region->num_iter
+		     : build_int_cst (LOOP_VINFO_RGROUP_IV_TYPE (loop_vinfo),
+				      LOOP_VINFO_VECT_FACTOR (loop_vinfo)),
+		   non_hssr_edge, UNKNOWN_LOCATION);
+      add_phi_arg (num_iter_phi,
+		   num_iters,
+		   fixup_cont_edge, UNKNOWN_LOCATION);
+
+      gimple_stmt_iterator fixup_gsi = gsi_start_bb (fixup_bb);
+      gsi_insert_seq_before (&fixup_gsi, fixup_logic, GSI_SAME_STMT);
+    }
+}
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index b54cdf35aed..aa55c846719 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -60,6 +60,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "langhooks.h"
 #include "opts.h"
 #include "hierarchical_discriminator.h"
+#include "tree-into-ssa.h"
 
 /* Loop Vectorization Pass.
 
@@ -10932,7 +10933,7 @@ tree
 vect_get_loop_mask (loop_vec_info loop_vinfo,
 		    gimple_stmt_iterator *gsi, vec_loop_masks *masks,
 		    unsigned int nvectors, tree vectype, unsigned int index,
-		    slp_tree slp_node ATTRIBUTE_UNUSED)
+		    slp_tree slp_node)
 {
   if (LOOP_VINFO_PARTIAL_VECTORS_STYLE (loop_vinfo)
       == vect_partial_vectors_while_ult)
@@ -10955,6 +10956,38 @@ vect_get_loop_mask (loop_vec_info loop_vinfo,
 	}
 
       tree mask = rgm->controls[index];
+
+      /* If we are in an HSSR region, also populate all the other required
+	 masks, and use the HSSR mask.  */
+      if (LOOP_VINFO_USING_HSSR_P (loop_vinfo))
+	{
+	  for (slp_instance instance : LOOP_VINFO_SLP_INSTANCES (loop_vinfo))
+	    if (hssr_region *region = SLP_INSTANCE_HSSR_REGION (instance))
+	      {
+		if (region->controls.length () < nvectors)
+		  region->controls.safe_grow_cleared (nvectors);
+
+		vec<tree> *controls = &region->controls[nvectors - 1];
+		if (controls->is_empty ())
+		  {
+		    controls->safe_grow_cleared (nvectors, true);
+		    for (unsigned int i = 0; i < nvectors; ++i)
+		      {
+			tree mask = make_temp_ssa_name (mask_type, NULL,
+							"hssr_loop_mask");
+			/* Provide a dummy definition until the real one is
+			   available.  */
+			SSA_NAME_DEF_STMT (mask) = gimple_build_nop ();
+			(*controls)[i] = mask;
+		      }
+		  }
+	      }
+
+	  if (SLP_TREE_HSSR_REGION (slp_node))
+	    mask
+	      = SLP_TREE_HSSR_REGION (slp_node)->controls[nvectors - 1][index];
+	}
+
       if (maybe_ne (TYPE_VECTOR_SUBPARTS (mask_type),
 		    TYPE_VECTOR_SUBPARTS (vectype)))
 	{
@@ -11834,7 +11867,8 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
 	  /* Both peeling for alignment and peeling for gaps can end up
 	     with the scalar epilogue running for more than VF-1 iterations.  */
 	  && !main_vinfo->peeling_for_alignment
-	  && !main_vinfo->peeling_for_gaps)
+	  && !main_vinfo->peeling_for_gaps
+	  && LOOP_VINFO_IV_INCREMENT_INVARIANT_P (loop_vinfo))
 	{
 	  unsigned int bound;
 	  poly_uint64 main_iters
@@ -11850,7 +11884,10 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
 	      = wi::umin ((bound_wide_int) (bound - 1),
 			  loop->nb_iterations_upper_bound);
       }
-  }
+
+      if (loop_vinfo && !LOOP_VINFO_IV_INCREMENT_INVARIANT_P (loop_vinfo))
+	loop->any_upper_bound = false;
+    }
   if (loop->any_likely_upper_bound)
     loop->nb_iterations_likely_upper_bound
       = (final_iter_may_be_partial
@@ -11895,6 +11932,36 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
 			 " variable-length vectorization factor\n");
     }
 
+  /* Save and restore the HSSR state.  */
+  if (LOOP_VINFO_USING_HSSR_P (loop_vinfo))
+    {
+      tree hssr_type = LOOP_VINFO_MASKS (loop_vinfo).rgc_vec[0].type;
+
+      tree hssr_ssa = make_temp_ssa_name (hssr_type, NULL, "hssr_preservation");
+
+      gcall *call = gimple_build_call_internal (IFN_READ_HSSR_STATE, 0);
+      gimple_set_lhs (call, hssr_ssa);
+      gsi_insert_on_edge_immediate (loop_preheader_edge (loop), call);
+
+
+      /* Initializze the hssr state to all true.  */
+      gcall *set_hssr_call
+	= gimple_build_call_internal (IFN_SET_HSSR_STATE, 1,
+				      build_all_ones_cst (hssr_type));
+      gsi_insert_on_edge_immediate (loop_preheader_edge (loop), set_hssr_call);
+
+      auto_vec<edge> exits = get_loop_exit_edges (loop);
+      for (edge e: exits)
+	{
+	  gcall *call = gimple_build_call_internal (IFN_SET_HSSR_STATE, 1,
+						    hssr_ssa);
+	  gsi_insert_on_edge_immediate (e, call);
+	}
+
+      vect_add_hssr_fixup_controls (loop_vinfo);
+      update_ssa (TODO_update_ssa_only_virtuals);
+    }
+
   /* When we have unrolled the loop due to a user requested value we should
      leave it up to the RTL unroll heuristics to determine if it's still worth
      while to unroll more.  */
diff --git a/gcc/tree-vect-slp.cc b/gcc/tree-vect-slp.cc
index 47d8bef38da..bf63fb51d99 100644
--- a/gcc/tree-vect-slp.cc
+++ b/gcc/tree-vect-slp.cc
@@ -12019,6 +12019,13 @@ vect_schedule_slp_node (vec_info *vinfo,
       gcc_checking_assert (!last_stmt
 			   || !is_ctrl_altering_stmt (last_stmt));
 
+      loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo);
+      if (loop_vinfo && LOOP_VINFO_USING_HSSR_P (loop_vinfo)
+	  && SLP_TREE_HSSR_REGION (node))
+	if (vect_stmt_dominates_stmt_p (
+	      last_stmt, SLP_TREE_HSSR_REGION (node)->hssr_read_point))
+	  last_stmt = SLP_TREE_HSSR_REGION (node)->hssr_read_point;
+
       if (is_a <bb_vec_info> (vinfo)
 	  && !SLP_TREE_PERMUTE_P (node)
 	  && (!last_stmt
@@ -12484,10 +12491,20 @@ vect_schedule_slp (vec_info *vinfo, const vec<slp_instance> &slp_instances)
   slp_instance instance;
   unsigned int i;
 
+  loop_vec_info loop_vinfo = dyn_cast<loop_vec_info> (vinfo);
+
   hash_map<slp_tree, slp_scc_info> scc_info;
   int maxdfs = 0;
   FOR_EACH_VEC_ELT (slp_instances, i, instance)
     {
+      /* Insert the call to read the HSSR now as we need it for data
+	 dependencies when scheduling, the actual fixup logic will be added
+	 later.  */
+      if (loop_vinfo
+	  && LOOP_VINFO_USING_HSSR_P (loop_vinfo)
+	  && SLP_INSTANCE_HSSR_REGION (instance))
+	vect_add_hssr_read (vinfo, instance);
+
       slp_tree node = SLP_INSTANCE_TREE (instance);
       if (dump_enabled_p ())
 	{
diff --git a/gcc/tree-vect-stmts.cc b/gcc/tree-vect-stmts.cc
index b77688b6f97..37734b162c5 100644
--- a/gcc/tree-vect-stmts.cc
+++ b/gcc/tree-vect-stmts.cc
@@ -58,6 +58,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "attribs.h"
 #include "optabs-libfuncs.h"
 #include "tree-dfa.h"
+#include "tree-into-ssa.h"
 
 /* For lang_hooks.types.type_for_mode.  */
 #include "langhooks.h"
@@ -1302,7 +1303,9 @@ vect_finish_replace_stmt (vec_info *vinfo,
 }
 
 /* Add VEC_STMT to the vectorized implementation of STMT_INFO and insert it
-   before *GSI.  Create and return a stmt_vec_info for VEC_STMT.  */
+   before *GSI.  Create and return a stmt_vec_info for VEC_STMT.
+
+   If this is a HSSR, insert the new load at the HSSR insertion point.  */
 
 void
 vect_finish_stmt_generation (vec_info *vinfo,
@@ -11607,18 +11610,43 @@ vectorizable_load (vec_info *vinfo,
 	      }
 	    else if (final_mask)
 	      {
-		tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
-		vec_els = vect_get_mask_load_else (maskload_elsval, vectype);
-		if (type_mode_padding_p
-		    && maskload_elsval != MASK_LOAD_ELSE_ZERO)
-		  need_zeroing = true;
-		gcall *call = gimple_build_call_internal (IFN_MASK_LOAD, 4,
-							  dataref_ptr, ptr,
-							  final_mask,
-							  vec_els);
-		gimple_call_set_nothrow (call, true);
-		new_stmt = call;
-		data_ref = NULL_TREE;
+		if (LOOP_VINFO_USING_HSSR_P (loop_vinfo)
+		    && dr_safe_speculative_read_required (stmt_info)
+		    && ls.defines_region)
+		  {
+		    /* Do the HSSR read, and then read the HSSR and save it for
+		       this r-group.  There should only be one, so we should be
+		       safe... */
+		    tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
+		    vec_els = vect_get_mask_load_else (maskload_elsval,
+						       vectype);
+		    if (type_mode_padding_p
+			&& maskload_elsval != MASK_LOAD_ELSE_ZERO)
+		      need_zeroing = true;
+		    gcall *call
+		      = gimple_build_call_internal (IFN_MASK_FF_HSSR_LOAD, 4,
+						    dataref_ptr, ptr,
+						    final_mask, vec_els);
+		    gimple_call_set_nothrow (call, true);
+		    new_stmt = call;
+		    data_ref = NULL_TREE;
+		  }
+		else
+		  {
+		    tree ptr = build_int_cst (ref_type, align * BITS_PER_UNIT);
+		    vec_els = vect_get_mask_load_else (maskload_elsval,
+						       vectype);
+		    if (type_mode_padding_p
+			&& maskload_elsval != MASK_LOAD_ELSE_ZERO)
+		      need_zeroing = true;
+		    gcall *call = gimple_build_call_internal (IFN_MASK_LOAD, 4,
+							      dataref_ptr, ptr,
+							      final_mask,
+							      vec_els);
+		    gimple_call_set_nothrow (call, true);
+		    new_stmt = call;
+		    data_ref = NULL_TREE;
+		  }
 	      }
 	    else
 	      {
@@ -11965,7 +11993,7 @@ vectorizable_load (vec_info *vinfo,
 
       /* Store vector loads in the corresponding SLP_NODE.  */
       if (!costing_p && !ls.slp_perm)
-	slp_node->push_vec_def (new_stmt);
+	slp_node->push_vec_def (new_stmt, true);
 
       /* With SLP permutation we load the gaps as well, without
 	 we need to skip the gaps after we manage to fully load
@@ -12043,6 +12071,11 @@ vectorizable_load (vec_info *vinfo,
       slp_node->data = new vect_load_store_data (std::move (ls));
     }
 
+  /* If we used a HSSR on this, we need to update the virtual ssa nodes because
+     of the HSSR read.  */
+  if (loop_vinfo && LOOP_VINFO_USING_HSSR_P (loop_vinfo)
+      && dr_safe_speculative_read_required (stmt_info))
+    update_ssa (TODO_update_ssa_only_virtuals);
   return true;
 }
 
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index a52e3dae738..b8297d95e5d 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1360,7 +1360,7 @@ public:
 #define LOOP_VINFO_VECT_FACTOR(L)          (L)->vectorization_factor
 #define LOOP_VINFO_IV_INCREMENT(L)         (L)->iv_increment
 #define LOOP_VINFO_IV_INCREMENT_INVARIANT_P(L) \
-  (!LOOP_VINFO_USING_SELECT_VL_P (L))
+  (!LOOP_VINFO_USING_SELECT_VL_P (L) && !LOOP_VINFO_USING_HSSR_P (L))
 #define LOOP_VINFO_MAX_VECT_FACTOR(L)      (L)->max_vectorization_factor
 #define LOOP_VINFO_MASKS(L)                (L)->masks
 #define LOOP_VINFO_LENS(L)                 (L)->lens
@@ -2557,6 +2557,8 @@ class loop *slpeel_tree_duplicate_loop_to_edge_cfg (class loop *, edge,
 						    bool = true);
 class loop *vect_loop_versioning (loop_vec_info, gimple *);
 extern bool vect_can_add_hssr_controls (loop_vec_info);
+extern void vect_add_hssr_fixup_controls (loop_vec_info);
+extern void vect_add_hssr_read (vec_info *, slp_instance);
 extern class loop *vect_do_peeling (loop_vec_info, tree, tree,
 				    tree *, tree *, tree *, int, bool, bool,
 				    tree *);
-- 
2.43.0
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.