[PATCH v2 12/13] vect: Add HSSR versioning.

Alfie Richards <[email protected]>
Newsgroups gmane.comp.gcc.patches
Message-ID <[email protected]>
This enables loop versioning of the shape:

                      │
                      ▼
            non-alignment versioning
         ┌──checks                  ────────────┐
  ┌──────▼──────┐                               ▼
  │ Scalar Loop │                 ┌──────alignment checks─────┐
  └──────┬──────┘                 ▼                ┌──────────▼────────────┐
         │         ┌───hssr safety checks───┐      │ Mutually aligned loop │
         │  ┌──────▼──────┐          ┌──────▼────┐ └───────────┬───────────┘
         │  │ Scalar Loop │          │ HSSR Loop │             │
         │  └──────┬──────┘          └──────┬────┘             │
         │         │                        │                  │
         └─────────┴──┬─────────────────────┴──────────────────┘
                      ▼

For supporting aligned loop vectorization where pointers are mutually
aligned and the (slower) HSSR vectorization when the pointers aren't
mutually aligned.

There is room for improvement in this at a later date.

gcc/ChangeLog:

	* config/aarch64/aarch64.cc (better_main_loop_than_p): Update the
	use of param_vect_hssr_usage.
	* params.opt: Add new value for versioning.
	* tree-vect-loop-manip.cc (vect_loop_versioning): Dont version
	for alignment when doing HSSR versioning.
	(vect_loop_hssr_alias_versioning): New function.
	* tree-vect-loop.cc (_loop_vec_info::_loop_vec_info): Add
	use_hssr_unaligned_version_p.
	(vect_determine_hssr_and_versioning): Add logic for opting into
	HSSR versioning when already versioning for alignment.
	(vect_analyze_loop_1): Add force_hssr option.
	(vect_analyze_loop): Add logic for enabling HSSR versioning.
	(vect_transform_loop): Add simduid_to_vf_htab and
	num_vectorized_loops arguments and logic for doing HSSR
	versioning.
	* tree-vectorizer.cc (class simduid_to_vf): Move to
	tree-vectorizer.h.
	(vect_transform_loops): Add simduid_to_vf_htab and
	num_vectorized_loops arguments to vect_transform_loop call.
	(try_vectorize_loop_1): Make non-static and add
	hssr_alignment_loop flag.
	(try_vectorize_loop): Update call to try_vectorize_loop_1.
	* tree-vectorizer.h (LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P):
	New macro.
	(class _loop_vec_info ): Add use_ffr_unaligned_version_p flag.
	(vect_analyze_loop): Add new argument.
	(class simduid_to_vf): Move from tree-vectorizer.cc
	(vect_transform_loop): Add new arguments.
	(try_vectorize_loop_1): Add new arguments.

gcc/testsuite/ChangeLog:

	* gcc.target/aarch64/sve/ffr_1.c:
	Update usage of param_vect_hssr_usage.
	* gcc.target/aarch64/sve/ffr_11.c: Likewise.
	* gcc.target/aarch64/sve/ffr_12.c: Likewise.
	* gcc.target/aarch64/sve/ffr_13.c: Likewise.
	* gcc.target/aarch64/sve/ffr_14.c: Likewise.
	* gcc.target/aarch64/sve/ffr_2.c: Likewise.
	* gcc.target/aarch64/sve/ffr_3.c: Likewise.
	* gcc.target/aarch64/sve/ffr_4.c: Likewise.
	* gcc.target/aarch64/sve/ffr_5.c: Likewise.
	* gcc.target/aarch64/sve/ffr_6.c: Likewise.
	* gcc.target/aarch64/sve/ffr_6_run.c: Likewise.
	* gcc.target/aarch64/sve/ffr_7.c: Likewise.
	* gcc.target/aarch64/sve/ffr_8.c: Likewise.
	* gcc.target/aarch64/sve/ffr_9.c: Likewise.
---
 gcc/config/aarch64/aarch64.cc                 |   2 +-
 gcc/params.opt                                |   4 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c  |   2 +-
 .../gcc.target/aarch64/sve/ffr_6_run.c        |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c  |   2 +-
 gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c  |   2 +-
 gcc/tree-vect-loop-manip.cc                   | 193 +++++++++++++++++-
 gcc/tree-vect-loop.cc                         |  63 +++++-
 gcc/tree-vectorizer.cc                        |  47 ++---
 gcc/tree-vectorizer.h                         |  41 +++-
 20 files changed, 317 insertions(+), 61 deletions(-)

diff --git a/gcc/config/aarch64/aarch64.cc b/gcc/config/aarch64/aarch64.cc
index 7ea3bc0f061..1eb2930cdb1 100644
--- a/gcc/config/aarch64/aarch64.cc
+++ b/gcc/config/aarch64/aarch64.cc
@@ -19619,7 +19619,7 @@ better_main_loop_than_p (const vector_costs *uncast_other) const
 
       if ((LOOP_VINFO_USING_HSSR_P (this_loop_vinfo)
 	   != LOOP_VINFO_USING_HSSR_P (other_loop_vinfo))
-	  && param_vect_hssr_usage == 2)
+	  && param_vect_hssr_usage == 3)
 	{
 	  if (dump_enabled_p ())
 	    dump_printf_loc (MSG_NOTE, vect_location,
diff --git a/gcc/params.opt b/gcc/params.opt
index 881efa2d02b..3e8f83e3fb2 100644
--- a/gcc/params.opt
+++ b/gcc/params.opt
@@ -1292,8 +1292,8 @@ Common Joined UInteger Var(param_vect_partial_vector_usage) Init(2) IntegerRange
 Controls how loop vectorizer uses partial vectors.  0 means never, 1 means only for loops whose need to iterate can be removed, 2 means for all loops.  The default value is 2.
 
 -param=vect-hssr-usage=
-Common Joined UInteger Var(param_vect_hssr_usage) Init(0) IntegerRange(0, 2) Param Optimization NoOffload
-Controls how loop vectorizer uses hardware safe speculative loads.  0 means never, 1 means only when profitable, 2 means whenever possible.  The default value is 2.
+Common Joined UInteger Var(param_vect_hssr_usage) Init(0) IntegerRange(0, 3) Param Optimization NoOffload
+Controls how loop vectorizer uses hardware safe speculative loads.  0 means never, 1 means only when profitable, 2 means whenever profitable possibly as a versioned option, 3 means whenever possible.  The default value is 0.
 
 -param=vect-inner-loop-cost-factor=
 Common Joined UInteger Var(param_vect_inner_loop_cost_factor) Init(50) IntegerRange(1, 10000) Param Optimization NoOffload
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
index 3027c6d69f0..58e08db2010 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_1.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 
 #include <stdint.h>
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
index dedea70dd3b..842705446b1 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_11.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 --param=vect-hssr-usage=2" } */
+/* { dg-options "-O2 --param=vect-hssr-usage=3" } */
 
 // Check this doesnt ICE
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
index db70c7ee87b..02f92da5093 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_12.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-mcpu=neoverse-v3 -O3 --param=vect-hssr-usage=2" } */
+/* { dg-options "-mcpu=neoverse-v3 -O3 --param=vect-hssr-usage=3" } */
 
 // Check this doesnt ICE
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
index a6b49c3c9df..31912596f66 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_13.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=2" } */
+/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=3" } */
 
 // Check this doesn't ICE
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
index bba9893c3a0..1101dd8d220 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_14.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=2" } */
+/* { dg-options "-mcpu=neoverse-v3 -O2 -mautovec-preference=sve-only -mmax-vectorization --param=vect-hssr-usage=3" } */
 
 // Check this doesn't ICE
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
index 45198266cee..9f6d72e6ca9 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_2.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 
 #include <stdint.h>
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
index b52869c99a3..3fac6c2a817 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_3.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 /* { dg-final { check-function-bodies "**" "" "" } } */
 
 #include <stdint.h>
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
index 9def8e348ec..937d0b6c55e 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_4.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 
 #include <stdint.h>
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
index e41c453188a..dc9b7cace08 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_5.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 
 #include <stdint.h>
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
index 4f225746be3..08a3c866bc7 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 --param=vect-hssr-usage=3" } */
 
 #include <stdint.h>
 
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
index 452c7307cff..243c0af34ec 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_6_run.c
@@ -1,5 +1,5 @@
 /* { dg-do run { target aarch64_sve_hw } } */
-/* { dg-options "-O3  --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3  --param=vect-hssr-usage=3" } */
 
 #include "ffr_6.c"
 #include <stdint.h>
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
index 1f508eb370e..6c5881b1c4e 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_7.c
@@ -1,6 +1,6 @@
 /* { dg-do compile } */
 
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic-armv9-a --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic-armv9-a --param=vect-hssr-usage=3" } */
 // Options to try get the cost model to select VNx4SI
 
 #include <stdint.h>
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
index bdecf2e1875..ff75167f880 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_8.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic --param=vect-hssr-usage=2" } */
+/* { dg-options "-O3 -fdump-tree-vect-details --save-temps -mtune=generic --param=vect-hssr-usage=3" } */
 // Options to get the cost model to select VNx8HI
 
 #include <stdint.h>
diff --git a/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c b/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c
index 5b4a38355af..8cc822b1ba4 100644
--- a/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c
+++ b/gcc/testsuite/gcc.target/aarch64/sve/ffr_9.c
@@ -1,5 +1,5 @@
 /* { dg-do compile } */
-/* { dg-options "-O2 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=2" } */
+/* { dg-options "-O2 -fdump-tree-vect-details --save-temps --param=vect-hssr-usage=3" } */
 
 /* Check we use HSSR at O2. */
 
diff --git a/gcc/tree-vect-loop-manip.cc b/gcc/tree-vect-loop-manip.cc
index 28a5978d9a3..7ac7f2141ba 100644
--- a/gcc/tree-vect-loop-manip.cc
+++ b/gcc/tree-vect-loop-manip.cc
@@ -4559,7 +4559,8 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
   profile_probability prob = profile_probability::likely ();
   gimple_seq gimplify_stmt_list = NULL;
   tree scalar_loop_iters = LOOP_VINFO_NITERSM1 (loop_vinfo);
-  bool version_align = LOOP_REQUIRES_VERSIONING_FOR_ALIGNMENT (loop_vinfo);
+  bool version_align = LOOP_REQUIRES_VERSIONING_FOR_ALIGNMENT (loop_vinfo)
+		       && !LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P (loop_vinfo);
   bool version_spec_read = LOOP_REQUIRES_VERSIONING_FOR_SPEC_READ (loop_vinfo);
   bool version_alias = LOOP_REQUIRES_VERSIONING_FOR_ALIAS (loop_vinfo);
   bool version_niter = LOOP_REQUIRES_VERSIONING_FOR_NITERS (loop_vinfo);
@@ -4940,6 +4941,196 @@ vect_loop_versioning (loop_vec_info loop_vinfo,
   return nloop;
 }
 
+/* Function vect_loop_hssr_alias_versioning.
+
+   Similar to vect_loop_versioning, however only handles alias checks to go
+   to an HSSR loop.  */
+
+class loop *
+vect_loop_hssr_alias_versioning (loop_vec_info loop_vinfo)
+{
+  class loop *loop = LOOP_VINFO_LOOP (loop_vinfo), *nloop;
+
+  basic_block condition_bb;
+  gphi_iterator gsi;
+  gimple_stmt_iterator cond_exp_gsi;
+  basic_block merge_bb;
+  basic_block new_exit_bb;
+  edge new_exit_e, e;
+  gphi *orig_phi, *new_phi;
+  tree cond_expr = NULL_TREE;
+  gimple_seq cond_expr_stmt_list = NULL;
+  tree arg;
+  profile_probability prob = profile_probability::likely ();
+  gimple_seq gimplify_stmt_list = NULL;
+
+  vect_create_cond_for_align_checks (loop_vinfo, &cond_expr,
+				     &cond_expr_stmt_list);
+
+  cond_expr = force_gimple_operand_1 (unshare_expr (cond_expr),
+				      &gimplify_stmt_list,
+				      is_gimple_condexpr_for_cond, NULL_TREE);
+
+  gimple_seq_add_seq (&cond_expr_stmt_list, gimplify_stmt_list);
+
+  /* Compute the outermost loop cond_expr and cond_expr_stmt_list are
+     invariant in.  */
+  class loop *outermost = outermost_invariant_loop_for_expr (loop, cond_expr);
+  for (gimple_stmt_iterator gsi = gsi_start (cond_expr_stmt_list);
+       !gsi_end_p (gsi); gsi_next (&gsi))
+    {
+      gimple *stmt = gsi_stmt (gsi);
+      update_stmt (stmt);
+      ssa_op_iter iter;
+      use_operand_p use_p;
+      basic_block def_bb;
+      FOR_EACH_SSA_USE_OPERAND (use_p, stmt, iter, SSA_OP_USE)
+	if ((def_bb = gimple_bb (SSA_NAME_DEF_STMT (USE_FROM_PTR (use_p))))
+	    && flow_bb_inside_loop_p (outermost, def_bb))
+	  outermost = superloop_at_depth (loop, bb_loop_depth (def_bb) + 1);
+    }
+
+  /* Search for the outermost loop we can version.  Avoid versioning of
+     non-perfect nests but allow if-conversion versioned loops inside.  */
+  class loop *loop_to_version = loop;
+  if (flow_loop_nested_p (outermost, loop))
+    {
+      if (dump_enabled_p ())
+	dump_printf_loc (MSG_NOTE, vect_location,
+			 "trying to apply versioning to outer loop %d\n",
+			 outermost->num);
+      if (outermost->num == 0)
+	outermost = superloop_at_depth (loop, 1);
+      /* And avoid applying versioning on non-perfect nests.  */
+      while (loop_to_version != outermost
+	     && (e = single_exit (loop_outer (loop_to_version)))
+	     && !(e->flags & EDGE_COMPLEX)
+	     && (!loop_outer (loop_to_version)->inner->next
+		 || vect_loop_vectorized_call (loop_to_version))
+	     && (!loop_outer (loop_to_version)->inner->next
+		 || !loop_outer (loop_to_version)->inner->next->next)
+	     && can_duplicate_loop_p (loop_outer (loop_to_version)))
+	loop_to_version = loop_outer (loop_to_version);
+    }
+
+  /* Apply versioning.  */
+  if (loop_to_version != loop && dump_enabled_p ())
+    dump_printf_loc (MSG_NOTE, vect_location,
+		     "applying loop versioning to outer loop %d\n",
+		     loop_to_version->num);
+
+  unsigned orig_pe_idx = loop_preheader_edge (loop)->dest_idx;
+
+  initialize_original_copy_tables ();
+  nloop = loop_version (loop_to_version, cond_expr, &condition_bb, prob,
+			(prob).invert (), prob,
+			(prob).invert (), true);
+
+  /* If the PHI nodes in the loop header were reallocated, we need to fix up
+     our internally stashed copies of those.  */
+  if (loop_to_version == loop)
+    for (auto gsi = gsi_start_phis (loop->header); !gsi_end_p (gsi);
+	 gsi_next (&gsi))
+      loop_vinfo->resync_stmt_addr (gsi.phi ());
+
+  /* We will later insert second conditional so overall outcome of
+     both is prob * prob2.  */
+  edge true_e, false_e;
+  extract_true_false_edges_from_block (condition_bb, &true_e, &false_e);
+  true_e->probability = prob;
+  false_e->probability = prob.invert ();
+  gcc_assert (nloop);
+  nloop = get_loop_copy (loop);
+
+  /* Assign hierarchical discriminators to distinguish loop versions.
+     Only assign to the scalar version here; the vectorized version will
+     get discriminators later during transformation/peeling.
+     Use dynamic copy_id allocation instead of hardcoded constants.  */
+  gimple *nloop_last = last_nondebug_stmt (nloop->header);
+  location_t nloop_loc
+    = nloop_last ? gimple_location (nloop_last) : UNKNOWN_LOCATION;
+  if (nloop_loc != UNKNOWN_LOCATION)
+    {
+      unsigned int nloop_copyid = allocate_copyid_base (nloop_loc, 1);
+      assign_discriminators_to_loop (nloop, 0, nloop_copyid);
+    }
+  /* For cycle vectorization with SLP we rely on the PHI arguments
+     appearing in the same order as the SLP node operands which for the
+     loop PHI nodes means the preheader edge dest index needs to remain
+     the same for the analyzed loop which also becomes the vectorized one.
+     Make it so in case the state after versioning differs by redirecting
+     the first edge into the header to the same destination which moves
+     it last.  */
+  if (loop_preheader_edge (loop)->dest_idx != orig_pe_idx)
+    {
+      edge e = EDGE_PRED (loop->header, 0);
+      ssa_redirect_edge (e, e->dest);
+      flush_pending_stmts (e);
+    }
+  gcc_assert (loop_preheader_edge (loop)->dest_idx == orig_pe_idx);
+
+  free_original_copy_tables ();
+
+  if (cond_expr_stmt_list)
+    {
+      cond_exp_gsi = gsi_last_bb (condition_bb);
+      gsi_insert_seq_before (&cond_exp_gsi, cond_expr_stmt_list, GSI_SAME_STMT);
+    }
+
+  /* Loop versioning violates an assumption we try to maintain during
+     vectorization - that the loop exit block has a single predecessor.
+     After versioning, the exit block of both loop versions is the same
+     basic block (i.e. it has two predecessors). Just in order to simplify
+     following transformations in the vectorizer, we fix this situation
+     here by adding a new (empty) block on the exit-edge of the loop,
+     with the proper loop-exit phis to maintain loop-closed-form.
+     If loop versioning wasn't done from loop, but scalar_loop instead,
+     merge_bb will have already just a single successor.  */
+
+  /* When the loop has multiple exits then we can only version itself.
+     This is denoted by loop_to_version == loop.  In this case we can
+     do the versioning by selecting the exit edge the vectorizer is
+     currently using.  */
+  edge exit_edge;
+  if (loop_to_version == loop)
+    exit_edge = LOOP_VINFO_MAIN_EXIT (loop_vinfo);
+  else
+    exit_edge = single_exit (loop_to_version);
+
+  gcc_assert (exit_edge);
+  merge_bb = exit_edge->dest;
+  if (EDGE_COUNT (merge_bb->preds) >= 2)
+    {
+      gcc_assert (EDGE_COUNT (merge_bb->preds) >= 2);
+      new_exit_bb = split_edge (exit_edge);
+      new_exit_e = exit_edge;
+      e = EDGE_SUCC (new_exit_bb, 0);
+
+      for (gsi = gsi_start_phis (merge_bb); !gsi_end_p (gsi); gsi_next (&gsi))
+	{
+	  tree new_res;
+	  orig_phi = gsi.phi ();
+	  new_res = copy_ssa_name (PHI_RESULT (orig_phi));
+	  new_phi = create_phi_node (new_res, new_exit_bb);
+	  arg = PHI_ARG_DEF_FROM_EDGE (orig_phi, e);
+	  add_phi_arg (new_phi, arg, new_exit_e,
+		       gimple_phi_arg_location_from_edge (orig_phi, e));
+	  adjust_phi_and_debug_stmts (orig_phi, e, PHI_RESULT (new_phi));
+	}
+    }
+
+  update_ssa (TODO_update_ssa_no_phi);
+
+  if (LOCATION_LOCUS (vect_location.get_location_t ()) != UNKNOWN_LOCATION
+      && dump_enabled_p ())
+    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS | MSG_PRIORITY_USER_FACING,
+		     vect_location,
+		     "loop versioned for hssr vectorization to enhance "
+		     "alignment\n");
+
+  return nloop;
+}
+
 /* Checks if it's possible to create the necessary controls for an HSSR loop.
 
    For loops with HSSR we need to generate all the required masks from the
diff --git a/gcc/tree-vect-loop.cc b/gcc/tree-vect-loop.cc
index aa55c846719..20fbaea1656 100644
--- a/gcc/tree-vect-loop.cc
+++ b/gcc/tree-vect-loop.cc
@@ -759,6 +759,7 @@ _loop_vec_info::_loop_vec_info (class loop *loop_in, vec_info_shared *shared)
     must_use_hssr_p (false),
     can_use_hssr_p (true),
     using_hssr_p (false),
+    use_hssr_unaligned_version_p (false),
     using_partial_vectors_p (false),
     using_decrementing_iv_p (false),
     using_select_vl_p (false),
@@ -2360,11 +2361,27 @@ vect_determine_hssr_and_versioning (loop_vec_info loop_vinfo)
 	  goto exit;
 	}
 
-      /* We can and will use HSSR.  */
+      /* We can use HSSR.  */
       if (dump_enabled_p ())
-	dump_printf_loc (MSG_NOTE, vect_location, "HSSR: Will use hssr\n");
+	dump_printf_loc (MSG_NOTE, vect_location, "HSSR: Can use HSSR\n");
 
-      /* We no longer need a bunch of versioning.  */
+      /* If we are going to use versioning, then record that we can use HSSR,
+	 but dont use it for this loop, as we can consider it for the versioned
+	 alternative.  */
+      if (LOOP_REQUIRES_VERSIONING_FOR_ALIGNMENT (loop_vinfo))
+	{
+	  if (dump_enabled_p ())
+	    dump_printf_loc (MSG_NOTE, vect_location,
+			     "FFR: Not using FFR because versioning, will "
+			     "consider it for versioned unaligned loop.\n");
+	  goto exit;
+	}
+
+      /* We can use HSSR.  */
+      if (dump_enabled_p ())
+	dump_printf_loc (MSG_NOTE, vect_location, "HSSR: Using HSSR\n");
+
+      /* Can and will use HSSR.  */
       LOOP_VINFO_MUST_USE_PARTIAL_VECTORS_P (loop_vinfo) = true;
       LOOP_VINFO_USING_HSSR_P (loop_vinfo) = true;
       LOOP_VINFO_MAY_MISALIGN_STMTS (loop_vinfo) = vNULL;
@@ -3033,6 +3050,7 @@ again:
   LOOP_VINFO_USING_HSSR_P (loop_vinfo) = false;
   LOOP_VINFO_MUST_USE_HSSR_P (loop_vinfo) = false;
   LOOP_VINFO_CAN_USE_HSSR_P (loop_vinfo) = true;
+  LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P (loop_vinfo) = false;
   LOOP_VINFO_USING_PARTIAL_VECTORS_P (loop_vinfo) = false;
   LOOP_VINFO_USING_SELECT_VL_P (loop_vinfo) = false;
   LOOP_VINFO_USING_DECREMENTING_IV_P (loop_vinfo) = false;
@@ -3109,7 +3127,7 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared,
 		     const vector_modes &vector_modes, unsigned &mode_i,
 		     int masked_p,
 		     machine_mode &autodetected_vector_mode,
-		     bool &fatal)
+		     bool &fatal, bool force_hssr)
 {
   loop_vec_info loop_vinfo
     = vect_create_loop_vinfo (loop, shared, loop_form_info, orig_loop_vinfo);
@@ -3118,6 +3136,7 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared,
   loop_vinfo->vector_mode = vector_mode;
   unsigned int suggested_unroll_factor = 1;
   bool single_lane_slp_done_for_suggested_uf = false;
+  LOOP_VINFO_MUST_USE_HSSR_P (loop_vinfo) = force_hssr;
 
   /* Run the main analysis.  */
   opt_result res = vect_analyze_loop_2 (loop_vinfo, masked_p, fatal,
@@ -3227,7 +3246,7 @@ vect_analyze_loop_1 (class loop *loop, vec_info_shared *shared,
    loop_vec_info struct.  */
 opt_loop_vec_info
 vect_analyze_loop (class loop *loop, gimple *loop_vectorized_call,
-		   vec_info_shared *shared)
+		   vec_info_shared *shared, bool force_hssr)
 {
   DUMP_VECT_SCOPE ("analyze_loop_nest");
 
@@ -3289,6 +3308,8 @@ vect_analyze_loop (class loop *loop, gimple *loop_vectorized_call,
   for (unsigned i = 0; i < vector_modes.length (); ++i)
     cached_vf_per_mode.safe_push (0);
 
+  bool can_hssr_any_mode = false;
+
   /* First determine the main loop vectorization mode, either the first
      one that works, starting with auto-detecting the vector mode and then
      following the targets order of preference, or the one with the
@@ -3303,12 +3324,19 @@ vect_analyze_loop (class loop *loop, gimple *loop_vectorized_call,
       opt_loop_vec_info loop_vinfo
 	= vect_analyze_loop_1 (loop, shared, &loop_form_info,
 			       NULL, vector_modes, mode_i, -1,
-			       autodetected_vector_mode, fatal);
+			       autodetected_vector_mode, fatal, force_hssr);
       if (fatal)
 	break;
 
       if (loop_vinfo)
 	{
+	  /* If this loop could have used FFR, that means it's possible and
+	     is profitable over scalar so it should be used for the
+	     alignment versioning alternative.  */
+	  if (LOOP_VINFO_CAN_USE_HSSR_P (loop_vinfo)
+	      && LOOP_REQUIRES_VERSIONING_FOR_ALIGNMENT (loop_vinfo))
+	    can_hssr_any_mode = true;
+
 	  /*  Analysis has been successful so update the VF value.  The
 	      VF should always be a multiple of unroll_factor and we want to
 	      capture the original VF here.  */
@@ -3358,6 +3386,14 @@ vect_analyze_loop (class loop *loop, gimple *loop_vectorized_call,
   if (!first_loop_vinfo)
     return opt_loop_vec_info::propagate_failure (res);
 
+  /* It it was profitable to use FFR over scalar for any of the modes, but we
+     decided not to and to version for alignment, then we should use this for
+     the misaligned pointer fallback.  */
+  if (can_hssr_any_mode
+      && !LOOP_VINFO_USING_HSSR_P (first_loop_vinfo)
+      && LOOP_REQUIRES_VERSIONING_FOR_ALIGNMENT (first_loop_vinfo))
+    LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P (first_loop_vinfo) = true;
+
   if (dump_enabled_p ())
     dump_printf_loc (MSG_NOTE, vect_location,
 		     "***** Choosing vector mode %s\n",
@@ -3456,7 +3492,7 @@ vect_analyze_loop (class loop *loop, gimple *loop_vectorized_call,
 	    = vect_analyze_loop_1 (loop, shared, &loop_form_info,
 				   orig_loop_vinfo,
 				   vector_modes, mode_i, masked_p,
-				   autodetected_vector_mode, fatal);
+				   autodetected_vector_mode, fatal, false);
 	  if (fatal)
 	    break;
 
@@ -11563,7 +11599,9 @@ vect_update_ivs_after_vectorizer_for_early_breaks (loop_vec_info loop_vinfo)
    Returns scalar epilogue loop if any.  */
 
 class loop *
-vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
+vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call,
+		     hash_table<simduid_to_vf> *&simduid_to_vf_htab,
+		     unsigned *num_vectorized_loops)
 {
   class loop *loop = LOOP_VINFO_LOOP (loop_vinfo);
   class loop *epilogue = NULL;
@@ -11622,6 +11660,15 @@ vect_transform_loop (loop_vec_info loop_vinfo, gimple *loop_vectorized_call)
       check_profitability = false;
     }
 
+  /* Version for the alignment check and vectorize the alternative with FFR.  */
+  if (LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P (loop_vinfo)
+      && LOOP_VINFO_MAY_MISALIGN_STMTS (loop_vinfo).length () > 0)
+    {
+      class loop *sloop = vect_loop_hssr_alias_versioning (loop_vinfo);
+      try_vectorize_loop_1 (simduid_to_vf_htab, num_vectorized_loops, sloop,
+			    NULL, NULL, true, cfun);
+    }
+
   /* Make sure there exists a single-predecessor exit bb also on the
      scalar loop copy.  Do this after versioning but before peeling
      so CFG structure is fine for both scalar and if-converted loop
diff --git a/gcc/tree-vectorizer.cc b/gcc/tree-vectorizer.cc
index 398edbd7ecc..831d40cad9f 100644
--- a/gcc/tree-vectorizer.cc
+++ b/gcc/tree-vectorizer.cc
@@ -196,31 +196,6 @@ dump_stmt_cost (FILE *f, int count, enum vect_cost_for_stmt kind,
   fprintf (f, "in %s\n", ws);
 }
 
-/* For mapping simduid to vectorization factor.  */
-
-class simduid_to_vf : public free_ptr_hash<simduid_to_vf>
-{
-public:
-  unsigned int simduid;
-  poly_uint64 vf;
-
-  /* hash_table support.  */
-  static inline hashval_t hash (const simduid_to_vf *);
-  static inline int equal (const simduid_to_vf *, const simduid_to_vf *);
-};
-
-inline hashval_t
-simduid_to_vf::hash (const simduid_to_vf *p)
-{
-  return p->simduid;
-}
-
-inline int
-simduid_to_vf::equal (const simduid_to_vf *p1, const simduid_to_vf *p2)
-{
-  return p1->simduid == p2->simduid;
-}
-
 /* This hash maps the OMP simd array to the corresponding simduid used
    to index into it.  Like thus,
 
@@ -1009,8 +984,9 @@ set_uid_loop_bbs (loop_vec_info loop_vinfo, gimple *loop_vectorized_call,
 
 /* Generate vectorized code for LOOP and its epilogues.  */
 
-static unsigned
+unsigned
 vect_transform_loops (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
+		      unsigned *num_vectorized_loops,
 		      loop_p loop, gimple *loop_vectorized_call,
 		      function *fun)
 {
@@ -1039,8 +1015,9 @@ vect_transform_loops (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 			 ? "epilogue " : "");
     }
 
-  loop_p new_loop = vect_transform_loop (loop_vinfo,
-					 loop_vectorized_call);
+  loop_p new_loop
+    = vect_transform_loop (loop_vinfo, loop_vectorized_call, simduid_to_vf_htab,
+			   num_vectorized_loops);
   /* Now that the loop has been vectorized, allow it to be unrolled
      etc.  */
   loop->force_vectorize = false;
@@ -1072,18 +1049,20 @@ vect_transform_loops (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 
   /* Epilogue of vectorized loop must be vectorized too.  */
   if (new_loop)
-    todo |= vect_transform_loops (simduid_to_vf_htab, new_loop, NULL, fun);
+    todo |= vect_transform_loops (simduid_to_vf_htab, num_vectorized_loops,
+				  new_loop, NULL, fun);
 
   return todo;
 }
 
 /* Try to vectorize LOOP.  */
 
-static unsigned
+unsigned
 try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 		      unsigned *num_vectorized_loops, loop_p loop,
 		      gimple *loop_vectorized_call,
 		      gimple *loop_dist_alias_call,
+		      bool hssr_alignment_loop,
 		      function *fun)
 {
   unsigned ret = 0;
@@ -1100,7 +1079,8 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 
   /* Try to analyze the loop, retaining an opt_problem if dump_enabled_p.  */
   opt_loop_vec_info loop_vinfo = vect_analyze_loop (loop, loop_vectorized_call,
-						    &shared);
+						    &shared,
+						    hssr_alignment_loop);
   loop->aux = loop_vinfo;
 
   if (!loop_vinfo)
@@ -1188,7 +1168,7 @@ try_vectorize_loop_1 (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 
   (*num_vectorized_loops)++;
   /* Transform LOOP and its epilogues.  */
-  ret |= vect_transform_loops (simduid_to_vf_htab, loop,
+  ret |= vect_transform_loops (simduid_to_vf_htab, num_vectorized_loops, loop,
 			       loop_vectorized_call, fun);
 
   if (loop_vectorized_call)
@@ -1220,7 +1200,8 @@ try_vectorize_loop (hash_table<simduid_to_vf> *&simduid_to_vf_htab,
 
   return try_vectorize_loop_1 (simduid_to_vf_htab, num_vectorized_loops, loop,
 			       vect_loop_vectorized_call (loop),
-			       vect_loop_dist_alias_call (loop, fun), fun);
+			       vect_loop_dist_alias_call (loop, fun),
+			       false, fun);
 }
 
 
diff --git a/gcc/tree-vectorizer.h b/gcc/tree-vectorizer.h
index b8297d95e5d..040ccfd77d1 100644
--- a/gcc/tree-vectorizer.h
+++ b/gcc/tree-vectorizer.h
@@ -1189,6 +1189,8 @@ public:
   bool can_use_hssr_p;
   bool using_hssr_p;
 
+  bool use_hssr_unaligned_version_p;
+
   /* True if we've decided to use partially-populated vectors, so that
      the vector loop can handle fewer than VF scalars.  */
   bool using_partial_vectors_p;
@@ -1353,6 +1355,8 @@ public:
 #define LOOP_VINFO_CAN_USE_HSSR_P(L) (L)->can_use_hssr_p
 #define LOOP_VINFO_MUST_USE_HSSR_P(L) (L)->must_use_hssr_p
 #define LOOP_VINFO_USING_HSSR_P(L) (L)->using_hssr_p
+#define LOOP_VINFO_USE_HSSR_UNALIGNED_VERSION_P(L) \
+  (L)->use_hssr_unaligned_version_p
 #define LOOP_VINFO_USING_DECREMENTING_IV_P(L) (L)->using_decrementing_iv_p
 #define LOOP_VINFO_USING_SELECT_VL_P(L) (L)->using_select_vl_p
 #define LOOP_VINFO_ALLOW_MUTUAL_ALIGNMENT(L) (L)->allow_mutual_alignment
@@ -2559,6 +2563,7 @@ 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);
+class loop *vect_loop_hssr_alias_versioning (loop_vec_info);
 extern class loop *vect_do_peeling (loop_vec_info, tree, tree,
 				    tree *, tree *, tree *, int, bool, bool,
 				    tree *);
@@ -2744,7 +2749,7 @@ extern bool check_reduction_path (dump_user_location_t, loop_p, gphi *, tree,
 extern bool needs_fold_left_reduction_p (tree, code_helper);
 /* Drive for loop analysis stage.  */
 extern opt_loop_vec_info vect_analyze_loop (class loop *, gimple *,
-					    vec_info_shared *);
+					    vec_info_shared *, bool);
 extern tree vect_build_loop_niters (loop_vec_info, bool * = NULL);
 extern void vect_gen_vector_loop_niters (loop_vec_info, tree, tree *,
 					 tree *, bool);
@@ -2769,8 +2774,40 @@ extern gimple_seq vect_gen_len (tree, tree, tree, tree);
 extern vect_reduc_info info_for_reduction (loop_vec_info, slp_tree);
 extern bool reduction_fn_for_scalar_code (code_helper, internal_fn *);
 extern unsigned vect_min_prec_for_max_niters (loop_vec_info, unsigned int);
+/* For mapping simduid to vectorization factor.  */
+
+class simduid_to_vf : public free_ptr_hash<simduid_to_vf>
+{
+public:
+  unsigned int simduid;
+  poly_uint64 vf;
+
+  /* hash_table support.  */
+  static inline hashval_t hash (const simduid_to_vf *);
+  static inline int equal (const simduid_to_vf *, const simduid_to_vf *);
+};
+
+inline hashval_t
+simduid_to_vf::hash (const simduid_to_vf *p)
+{
+  return p->simduid;
+}
+
+inline int
+simduid_to_vf::equal (const simduid_to_vf *p1, const simduid_to_vf *p2)
+{
+  return p1->simduid == p2->simduid;
+}
+
 /* Drive for loop transformation stage.  */
-extern class loop *vect_transform_loop (loop_vec_info, gimple *);
+extern class loop *vect_transform_loop (loop_vec_info, gimple *,
+					 hash_table<simduid_to_vf> *&,
+					 unsigned *);
+
+extern unsigned
+try_vectorize_loop_1 (hash_table<simduid_to_vf> *&, unsigned *, loop_p,
+		      gimple *, gimple *, bool, function *);
+
 struct vect_loop_form_info
 {
   tree number_of_iterations;
-- 
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.