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

Reply via email to