From: Robin Dapp <[email protected]>

This patch makes use of the preparational patches and actives
non-wrapping DR analysis.  The resulting nowrap bounds are collected and
added to the existing niter assumptions by ANDing each term.

I needed to move DR analysis a bit earlier in order to re-use niter
checks and dumps.  We now retrieve the DRs directly after
vect_analyze_loop_form, then build the assumptions and check them.
Afterwards, the DRs are finalized.

The vect tests are unchanged from my last patch attempt.  Test 1-3
are based on 557.xz's hot loop.

        PR tree-optimization/121908

gcc/ChangeLog:

        * tree-vect-data-refs.cc (vect_find_stmt_data_reference):
        Allow non-wrapping DR analysis.
        (vect_reset_dr): New function.
        * tree-vect-loop.cc (vect_analyze_loop_form): Defer assumption
        checking.
        (vect_get_loop_datarefs): New function.
        (vect_build_assumptions): New function.
        (vect_analyze_loop): Use new functions.
        * tree-vectorizer.h (vect_reset_dr): Declare.

gcc/testsuite/ChangeLog:

        * gcc.dg/tree-prof/update-loopch.c:
        * gcc.dg/tree-ssa/scev-12.c:
        * gcc.dg/tree-ssa/scev-8.c:
        * gcc.dg/vect/vect-unsigned-assump-1.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-2.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-3.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-4.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-5.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-6.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-7.c: New test.
        * gcc.dg/vect/vect-unsigned-assump-8.c: New test.

vec
---
 .../gcc.dg/tree-prof/update-loopch.c          |   2 +
 gcc/testsuite/gcc.dg/tree-ssa/scev-12.c       |   2 +-
 gcc/testsuite/gcc.dg/tree-ssa/scev-8.c        |   2 +-
 .../gcc.dg/vect/vect-unsigned-assump-1.c      |  22 +++
 .../gcc.dg/vect/vect-unsigned-assump-2.c      |  24 +++
 .../gcc.dg/vect/vect-unsigned-assump-3.c      |  20 +++
 .../gcc.dg/vect/vect-unsigned-assump-4.c      |  13 ++
 .../gcc.dg/vect/vect-unsigned-assump-5.c      |  14 ++
 .../gcc.dg/vect/vect-unsigned-assump-6.c      |  13 ++
 .../gcc.dg/vect/vect-unsigned-assump-7.c      |  17 ++
 .../gcc.dg/vect/vect-unsigned-assump-8.c      |  13 ++
 gcc/tree-vect-data-refs.cc                    |  15 +-
 gcc/tree-vect-loop.cc                         | 164 +++++++++++++++---
 gcc/tree-vectorizer.h                         |   1 +
 14 files changed, 292 insertions(+), 30 deletions(-)
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-1.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-2.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-3.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-4.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-5.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-6.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-7.c
 create mode 100644 gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-8.c

diff --git a/gcc/testsuite/gcc.dg/tree-prof/update-loopch.c 
b/gcc/testsuite/gcc.dg/tree-prof/update-loopch.c
index a30b895bb67..efb92b2c4fc 100644
--- a/gcc/testsuite/gcc.dg/tree-prof/update-loopch.c
+++ b/gcc/testsuite/gcc.dg/tree-prof/update-loopch.c
@@ -5,6 +5,8 @@ int
 main ()
 {
   int i;
+/* If we vectorize this, the block count changes which defies the test.  */
+#pragma GCC novector
   for (i = 0; i < max; i++)
     {
       a[i % 8]++;
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/scev-12.c 
b/gcc/testsuite/gcc.dg/tree-ssa/scev-12.c
index e2ded46047e..b0feee911c1 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/scev-12.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/scev-12.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-ivopts-details" } */
+/* { dg-options "-O2 -fno-tree-vectorize -fdump-tree-ivopts-details" } */
 
 int a[128];
 extern int b[];
diff --git a/gcc/testsuite/gcc.dg/tree-ssa/scev-8.c 
b/gcc/testsuite/gcc.dg/tree-ssa/scev-8.c
index a5b2ff71958..abdbf0250dd 100644
--- a/gcc/testsuite/gcc.dg/tree-ssa/scev-8.c
+++ b/gcc/testsuite/gcc.dg/tree-ssa/scev-8.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-ivopts-details" } */
+/* { dg-options "-O2 -fno-tree-vectorize -fdump-tree-ivopts-details" } */
 
 int *a;
 
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-1.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-1.c
new file mode 100644
index 00000000000..2c64eb890fd
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target vect_early_break } */
+
+#define uint8_t unsigned char
+#define uint32_t unsigned int
+
+int foo (const uint8_t *const cur, uint32_t len, uint32_t len_limit,
+         uint32_t pos, uint32_t cur_match)
+{
+  const uint32_t delta = pos - cur_match;
+  const uint8_t *pb = cur - delta;
+
+  while (++len != len_limit)
+    if (pb[len] != cur[len])
+      break;
+
+  return len;
+}
+
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
+/* { dg-final { scan-tree-dump "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-2.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-2.c
new file mode 100644
index 00000000000..a071ec399f0
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-2.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target vect_early_break } */
+/* { dg-require-effective-target vect_load_lanes } */
+
+#define uint8_t unsigned char
+#define uint32_t unsigned int
+
+int foo (const uint8_t *const cur, uint32_t len, uint32_t len_limit,
+         uint32_t pos, uint32_t cur_match)
+{
+  const uint32_t delta = pos - cur_match;
+  const uint8_t *pb = cur - delta;
+
+  /* We vectorize this with struct loads right now.  */
+  while ((len += 2) != len_limit)
+    if (pb[len] != cur[len])
+      break;
+
+  return len;
+}
+
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
+/* { dg-final { scan-tree-dump "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-3.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-3.c
new file mode 100644
index 00000000000..c9f6c2b0ca7
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-3.c
@@ -0,0 +1,20 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target vect_early_break } */
+
+#define uint8_t unsigned char
+#define uint32_t unsigned int
+
+int foo (const uint8_t *const cur, uint32_t n)
+{
+  uint32_t i = 15;
+
+  while (i++ != n)
+    if (cur[i - 15] != cur[i])
+      break;
+
+  return i;
+}
+
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
+/* { dg-final { scan-tree-dump "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-4.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-4.c
new file mode 100644
index 00000000000..8ba7ce435f9
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-4.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-builtin" } */
+/* { dg-require-effective-target vect_int } */
+
+/* We should vectorize this without additional assumptions.
+   The data-ref uses a pointer-based access which never wraps.  */
+
+void f4 (int *p, unsigned start, unsigned end) {
+  for (unsigned i = start; i != end; i++)
+    *p++ = 0;
+}
+
+/* { dg-final { scan-tree-dump-not "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-5.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-5.c
new file mode 100644
index 00000000000..2922fd1c114
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-5.c
@@ -0,0 +1,14 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target vect_gather_load_ifn } */
+
+/* This should be vectorized without gather/scatter.  */
+
+void f5 (int *__restrict a, int *__restrict b, unsigned start, unsigned end) {
+    for (unsigned i = start; i != end; i++)
+      a[i] = b[i];
+}
+
+/* { dg-final { scan-tree-dump-not "gather" "vect" } } */
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
+/* { dg-final { scan-tree-dump "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-6.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-6.c
new file mode 100644
index 00000000000..e869e8095ea
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-6.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+/* { dg-require-effective-target vect_gather_load_ifn } */
+
+/* This should be vectorized without gather/scatter.  */
+
+void f6 (int *__restrict a, int *__restrict b, unsigned start, unsigned end) {
+  for (unsigned i = start; i <= end; i++)
+    a[i] = b[i];
+}
+
+/* { dg-final { scan-tree-dump-not "gather" "vect" } } */
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-7.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-7.c
new file mode 100644
index 00000000000..6d7df016a13
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-7.c
@@ -0,0 +1,17 @@
+/* { dg-do compile } */
+/* { dg-require-effective-target vect_int } */
+
+/* This should be vectorized without gather/scatter.  */
+
+void f7 (int *__restrict dst, unsigned n) {
+  unsigned i = 1;
+  while (i != n)
+    {
+      dst[i] = i;
+      i++;
+    }
+}
+
+/* { dg-final { scan-tree-dump-not "gather" "vect" } } */
+/* { dg-final { scan-tree-dump "vectorized 1 loops in function" "vect" } } */
+/* { dg-final { scan-tree-dump "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-8.c 
b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-8.c
new file mode 100644
index 00000000000..37d76304274
--- /dev/null
+++ b/gcc/testsuite/gcc.dg/vect/vect-unsigned-assump-8.c
@@ -0,0 +1,13 @@
+/* { dg-do compile } */
+/* { dg-additional-options "-fno-builtin" } */
+/* { dg-require-effective-target vect_int } */
+
+/* We should vectorize this without additional assumptions.
+   The data-ref uses a pointer-based access which never wraps.  */
+
+void f8 (int *__restrict a, unsigned start, unsigned n) {
+  for (unsigned i = start; i != n; i++)
+    a[i - start] = 0;
+}
+
+/* { dg-final { scan-tree-dump-not "adding no-wrap assumption" "vect" } } */
diff --git a/gcc/tree-vect-data-refs.cc b/gcc/tree-vect-data-refs.cc
index ab850de024b..4d90d285458 100644
--- a/gcc/tree-vect-data-refs.cc
+++ b/gcc/tree-vect-data-refs.cc
@@ -5180,7 +5180,7 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
                                   stmt);
 
   auto_vec<data_reference_p, 2> refs;
-  opt_result res = find_data_references_in_stmt (loop, stmt, &refs);
+  opt_result res = find_data_references_in_stmt (loop, stmt, &refs, true);
   if (!res)
     return res;
 
@@ -5237,7 +5237,8 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
     {
       struct data_reference *newdr
        = create_data_ref (NULL, loop_containing_stmt (stmt), DR_REF (dr), stmt,
-                          DR_IS_READ (dr), DR_IS_CONDITIONAL_IN_STMT (dr));
+                          DR_IS_READ (dr), DR_IS_CONDITIONAL_IN_STMT (dr),
+                          true);
       if (DR_BASE_ADDRESS (newdr)
          && DR_OFFSET (newdr)
          && DR_INIT (newdr)
@@ -5318,6 +5319,16 @@ vect_find_stmt_data_reference (loop_p loop, gimple *stmt,
   return opt_result::success ();
 }
 
+/* Reset DR_INNERMOST, DR_NOWRAP_BOUND, and access functions of DR.  */
+
+void
+vect_reset_dr (data_reference *dr)
+{
+  DR_INNERMOST (dr) = innermost_loop_behavior ();
+  DR_NOWRAP_BOUND (dr) = NULL_TREE;
+  dr->indices.access_fns.release ();
+}
+
 /* Function vect_analyze_data_refs.
 
   Find all the data references in the loop or basic block.
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index 931b942d7cb..3908cad5714 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -1651,17 +1651,6 @@ vect_analyze_loop_form (class loop *loop, gimple 
*loop_vectorized_call,
        }
     }
 
-  if (!integer_onep (info->assumptions))
-    {
-      if (dump_enabled_p ())
-       {
-         dump_printf_loc (MSG_NOTE, vect_location,
-                          "Loop to be versioned with niter assumption ");
-         dump_generic_expr (MSG_NOTE, TDF_SLIM, info->assumptions);
-         dump_printf (MSG_NOTE, "\n");
-       }
-    }
-
   return opt_result::success ();
 }
 
@@ -2053,6 +2042,132 @@ vect_get_datarefs_in_loop (loop_p loop, basic_block 
*bbs,
   return opt_result::success ();
 }
 
+/* Collect the data references of LOOP into SHARED.  Return true if
+   all references could be analyzed and false otherwise.    */
+
+static opt_result
+vect_get_loop_datarefs (class loop *loop, vec_info_shared *shared)
+{
+  basic_block *bbs = XCNEWVEC (basic_block, loop->num_nodes);
+  unsigned int nbbs = dfs_enumerate_from (loop->header, 0, bb_in_loop_p, bbs,
+                                         loop->num_nodes, loop);
+  gcc_assert (nbbs == loop->num_nodes);
+  opt_result res = vect_get_datarefs_in_loop (loop, bbs, &shared->datarefs);
+  free (bbs);
+  if (!res && dump_enabled_p ())
+    dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
+                    "not vectorized: loop contains function "
+                    "calls or data references that cannot "
+                    "be analyzed\n");
+  return res;
+}
+
+/* Gather no-wrap bounds from SHARED's datarefs and accumulate them in
+   one assumption.  Validate this assumption, as well as INFO's niter
+   assumption and return an error on failure.
+   If the assumptions are valid, reset loop properties as well as
+   scev's hashtable and return success.  */
+
+static opt_result
+vect_build_assumptions (class loop *loop, vec_info_shared *shared,
+                       vect_loop_form_info *info)
+{
+  tree niter = info->number_of_iterations;
+  bool niter_known = niter && !chrec_contains_undetermined (niter);
+
+  /* Accumulate the surviving nowrap predicates separately so we can report
+     just them, rather than the union with the pre-existing niter
+     assumptions.  */
+  tree nowrap_assum = NULL_TREE;
+
+  unsigned int i;
+  struct data_reference *dr;
+  FOR_EACH_VEC_ELT (shared->datarefs, i, dr)
+    {
+      tree bound = DR_NOWRAP_BOUND (dr);
+      if (!bound)
+       continue;
+
+      /* Build the assumption "NITER <= BOUND"  */
+      tree assum = NULL_TREE;
+      if (niter_known)
+       {
+         unsigned prec = MAX (TYPE_PRECISION (TREE_TYPE (niter)),
+                              TYPE_PRECISION (TREE_TYPE (bound)));
+         tree t = build_nonstandard_integer_type (prec, 1);
+         assum = fold_build2 (LE_EXPR, boolean_type_node,
+                              fold_convert (t, niter),
+                              fold_convert (t, bound));
+       }
+
+      if (assum && integer_onep (assum))
+       continue;
+
+      if (!assum || integer_zerop (assum))
+       {
+         vect_reset_dr (dr);
+
+         if (dump_enabled_p ())
+           dump_printf_loc (MSG_NOTE, vect_location,
+                            "data reference address wraps, discarding "
+                            "no-wrap assumption: %G", DR_STMT (dr));
+         continue;
+       }
+
+      /* Accumulate assumptions.  */
+      if (nowrap_assum && nowrap_assum != assum)
+       nowrap_assum = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
+                                   nowrap_assum, assum);
+      else
+       nowrap_assum = assum;
+    }
+
+  /* Combine nowrap assumptions and niter assumption.  */
+  if (nowrap_assum)
+    {
+      if (dump_enabled_p ())
+       dump_printf_loc (MSG_NOTE, vect_location,
+                        "adding no-wrap assumption: %T\n", nowrap_assum);
+
+      if (info->assumptions && !integer_onep (info->assumptions))
+       nowrap_assum = fold_build2 (TRUTH_AND_EXPR, boolean_type_node,
+                                   info->assumptions, nowrap_assum);
+      info->assumptions = nowrap_assum;
+    }
+
+  if (integer_zerop (info->assumptions))
+    return opt_result::failure_at
+      (vect_location,
+       "not vectorized: number of iterations cannot be computed.\n");
+
+  if (!integer_onep (info->assumptions))
+    {
+      if (dump_enabled_p ())
+       {
+         dump_printf_loc (MSG_NOTE, vect_location,
+                          "Loop to be versioned with assumption ");
+         dump_generic_expr (MSG_NOTE, TDF_SLIM, info->assumptions);
+         dump_printf (MSG_NOTE, "\n");
+       }
+
+      /* We consider to vectorize this loop by versioning it under
+        some assumptions.  In order to do this, we need to clear
+        existing information computed by scev and niter analyzer.  */
+      scev_reset_htab ();
+      free_numbers_of_iterations_estimates (loop);
+      /* Also set flag for this loop so that following scev and niter
+        analysis are done under the assumptions.  */
+      loop_constraint_set (loop, LOOP_C_FINITE);
+    }
+  else
+    /* Clear the existing niter information to make sure the nonwrapping flag
+       will be calculated and set propriately.  */
+    free_numbers_of_iterations_estimates (loop);
+
+  return opt_result::success ();
+}
+
+
 /* Determine if operating on full vectors for LOOP_VINFO might leave
    some scalar iterations still to do.  If so, decide how we should
    handle those scalar iterations.  The possibilities are:
@@ -2952,21 +3067,18 @@ vect_analyze_loop (class loop *loop, gimple 
*loop_vectorized_call,
                         "bad loop form.\n");
       return opt_loop_vec_info::propagate_failure (res);
     }
-  if (!integer_onep (loop_form_info.assumptions))
-    {
-      /* We consider to vectorize this loop by versioning it under
-        some assumptions.  In order to do this, we need to clear
-        existing information computed by scev and niter analyzer.  */
-      scev_reset_htab ();
-      free_numbers_of_iterations_estimates (loop);
-      /* Also set flag for this loop so that following scev and niter
-        analysis are done under the assumptions.  */
-      loop_constraint_set (loop, LOOP_C_FINITE);
-    }
-  else
-    /* Clear the existing niter information to make sure the nonwrapping flag
-       will be calculated and set propriately.  */
-    free_numbers_of_iterations_estimates (loop);
+
+  res = vect_get_loop_datarefs (loop, shared);
+  if (!res)
+    return opt_loop_vec_info::propagate_failure (res);
+
+  res = vect_build_assumptions (loop, shared, &loop_form_info);
+  if (!res)
+    return opt_loop_vec_info::propagate_failure (res);
+
+  /* vect_build_assumptions can still invalidate a dataref
+     but from here on they are fixed.  */
+  shared->save_datarefs ();
 
   auto_vector_modes vector_modes;
   /* Autodetect first vector size we try.  */
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index 2e4756926f2..d23c618d22c 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -2650,6 +2650,7 @@ extern void vect_describe_gather_scatter_call 
(stmt_vec_info,
 extern opt_result vect_find_stmt_data_reference (loop_p, gimple *,
                                                 vec<data_reference_p> *,
                                                 vec<int> *, int);
+extern void vect_reset_dr (data_reference *);
 extern opt_result vect_analyze_data_refs (vec_info *, bool *);
 extern void vect_record_base_alignments (vec_info *);
 extern tree vect_create_data_ref_ptr (vec_info *,
-- 
2.54.0

Reply via email to