diff mbox series

Handle peeling for alignment with masking

Message ID 878tf5ym3f.fsf@linaro.org
State New
Headers show
Series Handle peeling for alignment with masking | expand

Commit Message

Richard Sandiford Nov. 17, 2017, 3:13 p.m. UTC
This patch adds support for aligning vectors by using a partial
first iteration.  E.g. if the start pointer is 3 elements beyond
an aligned address, the first iteration will have a mask in which
the first three elements are false.

On SVE, the optimisation is only useful for vector-length-specific
code.  Vector-length-agnostic code doesn't try to align vectors
since the vector length might not be a power of 2.

Tested on aarch64-linux-gnu (with and without SVE), x86_64-linux-gnu
and powerpc64le-linux-gnu.  OK to install?

Richard


2017-11-17  Richard Sandiford  <richard.sandiford@linaro.org>
	    Alan Hayward  <alan.hayward@arm.com>
	    David Sherwood  <david.sherwood@arm.com>

gcc/
	* tree-vectorizer.h (_loop_vec_info::mask_skip_niters): New field.
	(LOOP_VINFO_MASK_SKIP_NITERS): New macro.
	(vect_use_loop_mask_for_alignment_p): New function.
	(vect_prepare_for_masked_peels, vect_gen_while_not): Declare.
	* tree-vect-loop-manip.c (vect_set_loop_masks_directly): Add an
	niters_skip argument.  Make sure that the first niters_skip elements
	of the first iteration are inactive.
	(vect_set_loop_condition_masked): Handle LOOP_VINFO_MASK_SKIP_NITERS.
	Update call to vect_set_loop_masks_directly.
	(get_misalign_in_elems): New function, split out from...
	(vect_gen_prolog_loop_niters): ...here.
	(vect_update_init_of_dr): Take a code argument that specifies whether
	the adjustment should be added or subtracted.
	(vect_update_init_of_drs): Likewise.
	(vect_prepare_for_masked_peels): New function.
	(vect_do_peeling): Skip prologue peeling if we're using a mask
	instead.  Update call to vect_update_inits_of_drs.
	* tree-vect-loop.c (_loop_vec_info::_loop_vec_info): Initialize
	mask_skip_niters.
	(vect_analyze_loop_2): Allow fully-masked loops with peeling for
	alignment.  Do not include the number of peeled iterations in
	the minimum threshold in that case.
	(vectorizable_induction): Adjust the start value down by
	LOOP_VINFO_MASK_SKIP_NITERS iterations.
	(vect_transform_loop): Call vect_prepare_for_masked_peels.
	Take the number of skipped iterations into account when calculating
	the loop bounds.
	* tree-vect-stmts.c (vect_gen_while_not): New function.

gcc/testsuite/
	* gcc.target/aarch64/sve_nopeel_1.c: New test.
	* gcc.target/aarch64/sve_peel_ind_1.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_1_run.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_2.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_2_run.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_3.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_3_run.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_4.c: Likewise.
	* gcc.target/aarch64/sve_peel_ind_4_run.c: Likewise.

Comments

Jeff Law Dec. 14, 2017, 12:12 a.m. UTC | #1
On 11/17/2017 08:13 AM, Richard Sandiford wrote:
> This patch adds support for aligning vectors by using a partial

> first iteration.  E.g. if the start pointer is 3 elements beyond

> an aligned address, the first iteration will have a mask in which

> the first three elements are false.

> 

> On SVE, the optimisation is only useful for vector-length-specific

> code.  Vector-length-agnostic code doesn't try to align vectors

> since the vector length might not be a power of 2.

> 

> Tested on aarch64-linux-gnu (with and without SVE), x86_64-linux-gnu

> and powerpc64le-linux-gnu.  OK to install?

> 

> Richard

> 

> 

> 2017-11-17  Richard Sandiford  <richard.sandiford@linaro.org>

> 	    Alan Hayward  <alan.hayward@arm.com>

> 	    David Sherwood  <david.sherwood@arm.com>

> 

> gcc/

> 	* tree-vectorizer.h (_loop_vec_info::mask_skip_niters): New field.

> 	(LOOP_VINFO_MASK_SKIP_NITERS): New macro.

> 	(vect_use_loop_mask_for_alignment_p): New function.

> 	(vect_prepare_for_masked_peels, vect_gen_while_not): Declare.

> 	* tree-vect-loop-manip.c (vect_set_loop_masks_directly): Add an

> 	niters_skip argument.  Make sure that the first niters_skip elements

> 	of the first iteration are inactive.

> 	(vect_set_loop_condition_masked): Handle LOOP_VINFO_MASK_SKIP_NITERS.

> 	Update call to vect_set_loop_masks_directly.

> 	(get_misalign_in_elems): New function, split out from...

> 	(vect_gen_prolog_loop_niters): ...here.

> 	(vect_update_init_of_dr): Take a code argument that specifies whether

> 	the adjustment should be added or subtracted.

> 	(vect_update_init_of_drs): Likewise.

> 	(vect_prepare_for_masked_peels): New function.

> 	(vect_do_peeling): Skip prologue peeling if we're using a mask

> 	instead.  Update call to vect_update_inits_of_drs.

> 	* tree-vect-loop.c (_loop_vec_info::_loop_vec_info): Initialize

> 	mask_skip_niters.

> 	(vect_analyze_loop_2): Allow fully-masked loops with peeling for

> 	alignment.  Do not include the number of peeled iterations in

> 	the minimum threshold in that case.

> 	(vectorizable_induction): Adjust the start value down by

> 	LOOP_VINFO_MASK_SKIP_NITERS iterations.

> 	(vect_transform_loop): Call vect_prepare_for_masked_peels.

> 	Take the number of skipped iterations into account when calculating

> 	the loop bounds.

> 	* tree-vect-stmts.c (vect_gen_while_not): New function.

OK.
jeff
James Greenhalgh Jan. 7, 2018, 8:54 p.m. UTC | #2
On Thu, Dec 14, 2017 at 12:12:01AM +0000, Jeff Law wrote:
> On 11/17/2017 08:13 AM, Richard Sandiford wrote:

> > This patch adds support for aligning vectors by using a partial

> > first iteration.  E.g. if the start pointer is 3 elements beyond

> > an aligned address, the first iteration will have a mask in which

> > the first three elements are false.

> > 

> > On SVE, the optimisation is only useful for vector-length-specific

> > code.  Vector-length-agnostic code doesn't try to align vectors

> > since the vector length might not be a power of 2.

> > 

> > Tested on aarch64-linux-gnu (with and without SVE), x86_64-linux-gnu

> > and powerpc64le-linux-gnu.  OK to install?

> > 

> > Richard

> > 

> > 

> > 2017-11-17  Richard Sandiford  <richard.sandiford@linaro.org>

> > 	    Alan Hayward  <alan.hayward@arm.com>

> > 	    David Sherwood  <david.sherwood@arm.com>

> > 

> > gcc/

> > 	* tree-vectorizer.h (_loop_vec_info::mask_skip_niters): New field.

> > 	(LOOP_VINFO_MASK_SKIP_NITERS): New macro.

> > 	(vect_use_loop_mask_for_alignment_p): New function.

> > 	(vect_prepare_for_masked_peels, vect_gen_while_not): Declare.

> > 	* tree-vect-loop-manip.c (vect_set_loop_masks_directly): Add an

> > 	niters_skip argument.  Make sure that the first niters_skip elements

> > 	of the first iteration are inactive.

> > 	(vect_set_loop_condition_masked): Handle LOOP_VINFO_MASK_SKIP_NITERS.

> > 	Update call to vect_set_loop_masks_directly.

> > 	(get_misalign_in_elems): New function, split out from...

> > 	(vect_gen_prolog_loop_niters): ...here.

> > 	(vect_update_init_of_dr): Take a code argument that specifies whether

> > 	the adjustment should be added or subtracted.

> > 	(vect_update_init_of_drs): Likewise.

> > 	(vect_prepare_for_masked_peels): New function.

> > 	(vect_do_peeling): Skip prologue peeling if we're using a mask

> > 	instead.  Update call to vect_update_inits_of_drs.

> > 	* tree-vect-loop.c (_loop_vec_info::_loop_vec_info): Initialize

> > 	mask_skip_niters.

> > 	(vect_analyze_loop_2): Allow fully-masked loops with peeling for

> > 	alignment.  Do not include the number of peeled iterations in

> > 	the minimum threshold in that case.

> > 	(vectorizable_induction): Adjust the start value down by

> > 	LOOP_VINFO_MASK_SKIP_NITERS iterations.

> > 	(vect_transform_loop): Call vect_prepare_for_masked_peels.

> > 	Take the number of skipped iterations into account when calculating

> > 	the loop bounds.

> > 	* tree-vect-stmts.c (vect_gen_while_not): New function.

> OK.

> jeff


The AArch64 tests are OK, but:

> > Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2_run.c

> > ===================================================================

> > --- /dev/null	2017-11-14 14:28:07.424493901 +0000

> > +++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2_run.c	2017-11-17 15:11:51.121849349 +0000

> > @@ -0,0 +1,18 @@

> > +/* { dg-do run { target aarch64_sve_hw } } */

> > +/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx" } */

> > +/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx -msve-vector-bits=256" { target aarch64_sve256_hw } } */

> > +


I'd put the comment from sve_peel_ind_2.c as to why we have
the -mtune=thunderx here too.

Thanks,
James
diff mbox series

Patch

Index: gcc/tree-vectorizer.h
===================================================================
--- gcc/tree-vectorizer.h	2017-11-17 15:07:59.275435265 +0000
+++ gcc/tree-vectorizer.h	2017-11-17 15:11:51.124849349 +0000
@@ -351,6 +351,12 @@  typedef struct _loop_vec_info : public v
      on inactive scalars.  */
   vec_loop_masks masks;
 
+  /* If we are using a loop mask to align memory addresses, this variable
+     contains the number of vector elements that we should skip in the
+     first iteration of the vector loop (i.e. the number of leading
+     elements that should be false in the first mask).  */
+  tree mask_skip_niters;
+
   /* Type of the variables to use in the WHILE_ULT call for fully-masked
      loops.  */
   tree mask_compare_type;
@@ -480,6 +486,7 @@  #define LOOP_VINFO_FULLY_MASKED_P(L)
 #define LOOP_VINFO_VECT_FACTOR(L)          (L)->vectorization_factor
 #define LOOP_VINFO_MAX_VECT_FACTOR(L)      (L)->max_vectorization_factor
 #define LOOP_VINFO_MASKS(L)                (L)->masks
+#define LOOP_VINFO_MASK_SKIP_NITERS(L)     (L)->mask_skip_niters
 #define LOOP_VINFO_MASK_COMPARE_TYPE(L)    (L)->mask_compare_type
 #define LOOP_VINFO_PTR_MASK(L)             (L)->ptr_mask
 #define LOOP_VINFO_LOOP_NEST(L)            (L)->loop_nest
@@ -1230,6 +1237,17 @@  unlimited_cost_model (loop_p loop)
   return (flag_vect_cost_model == VECT_COST_MODEL_UNLIMITED);
 }
 
+/* Return true if the loop described by LOOP_VINFO is fully-masked and
+   if the first iteration should use a partial mask in order to achieve
+   alignment.  */
+
+static inline bool
+vect_use_loop_mask_for_alignment_p (loop_vec_info loop_vinfo)
+{
+  return (LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
+	  && LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo));
+}
+
 /* Return the number of vectors of type VECTYPE that are needed to get
    NUNITS elements.  NUNITS should be based on the vectorization factor,
    so it is always a known multiple of the number of elements in VECTYPE.  */
@@ -1328,6 +1346,7 @@  extern void vect_loop_versioning (loop_v
 				  poly_uint64);
 extern struct loop *vect_do_peeling (loop_vec_info, tree, tree,
 				     tree *, tree *, tree *, int, bool, bool);
+extern void vect_prepare_for_masked_peels (loop_vec_info);
 extern source_location find_loop_location (struct loop *);
 extern bool vect_can_advance_ivs_p (loop_vec_info);
 
@@ -1392,6 +1411,7 @@  extern tree vect_gen_perm_mask_any (tree
 extern tree vect_gen_perm_mask_checked (tree, vec_perm_indices);
 extern void optimize_mask_stores (struct loop*);
 extern gcall *vect_gen_while (tree, tree, tree);
+extern tree vect_gen_while_not (gimple_seq *, tree, tree, tree);
 
 /* In tree-vect-data-refs.c.  */
 extern bool vect_can_force_dr_alignment_p (const_tree, unsigned int);
Index: gcc/tree-vect-loop-manip.c
===================================================================
--- gcc/tree-vect-loop-manip.c	2017-11-17 15:07:59.273608779 +0000
+++ gcc/tree-vect-loop-manip.c	2017-11-17 15:11:51.122849349 +0000
@@ -362,6 +362,11 @@  vect_maybe_permute_loop_masks (gimple_se
    times and has been vectorized according to LOOP_VINFO.  Each iteration
    of the vectorized loop handles VF iterations of the scalar loop.
 
+   If NITERS_SKIP is nonnull, the first iteration of the vectorized loop
+   starts with NITERS_SKIP dummy iterations of the scalar loop before
+   the real work starts.  The mask elements for these dummy iterations
+   must be 0, to ensure that the extra iterations do not have an effect.
+
    It is known that:
 
      NITERS * RGM->max_nscalars_per_iter
@@ -373,7 +378,7 @@  vect_maybe_permute_loop_masks (gimple_se
 
    might overflow before hitting a value above:
 
-     NITERS * RGM->max_nscalars_per_iter
+     (NITERS + NITERS_SKIP) * RGM->max_nscalars_per_iter
 
    This means that we cannot guarantee that such an induction variable
    would ever hit a value that produces a set of all-false masks for RGM.  */
@@ -383,7 +388,8 @@  vect_set_loop_masks_directly (struct loo
 			      gimple_seq *preheader_seq,
 			      gimple_stmt_iterator loop_cond_gsi,
 			      rgroup_masks *rgm, tree vf,
-			      tree niters, bool might_wrap_p)
+			      tree niters, tree niters_skip,
+			      bool might_wrap_p)
 {
   tree compare_type = LOOP_VINFO_MASK_COMPARE_TYPE (loop_vinfo);
   tree mask_type = rgm->mask_type;
@@ -391,10 +397,12 @@  vect_set_loop_masks_directly (struct loo
   poly_uint64 nscalars_per_mask = TYPE_VECTOR_SUBPARTS (mask_type);
 
   /* Calculate the maximum number of scalar values that the rgroup
-     handles in total and the number that it handles for each iteration
-     of the vector loop.  */
+     handles in total, the number that it handles for each iteration
+     of the vector loop, and the number that it should skip during the
+     first iteration of the vector loop.  */
   tree nscalars_total = niters;
   tree nscalars_step = vf;
+  tree nscalars_skip = niters_skip;
   if (nscalars_per_iter != 1)
     {
       /* We checked before choosing to use a fully-masked loop that these
@@ -404,6 +412,9 @@  vect_set_loop_masks_directly (struct loo
 				     nscalars_total, factor);
       nscalars_step = gimple_build (preheader_seq, MULT_EXPR, compare_type,
 				    nscalars_step, factor);
+      if (nscalars_skip)
+	nscalars_skip = gimple_build (preheader_seq, MULT_EXPR, compare_type,
+				      nscalars_skip, factor);
     }
 
   /* Create an induction variable that counts the number of scalars
@@ -416,29 +427,66 @@  vect_set_loop_masks_directly (struct loo
   create_iv (zero_index, nscalars_step, NULL_TREE, loop, &incr_gsi,
 	     insert_after, &index_before_incr, &index_after_incr);
 
-  tree test_index, test_limit;
+  tree test_index, test_limit, first_limit;
   gimple_stmt_iterator *test_gsi;
   if (might_wrap_p)
     {
       /* In principle the loop should stop iterating once the incremented
-	 IV reaches a value greater than or equal to NSCALAR_TOTAL.
-	 However, there's no guarantee that the IV hits a value above
-	 this value before wrapping around.  We therefore adjust the
-	 limit down by one IV step:
+	 IV reaches a value greater than or equal to:
+
+	   NSCALARS_TOTAL +[infinite-prec] NSCALARS_SKIP
+
+	 However, there's no guarantee that this addition doesn't overflow
+	 the comparison type, or that the IV hits a value above it before
+	 wrapping around.  We therefore adjust the limit down by one
+	 IV step:
 
-	   NSCALARS_TOTAL -[infinite-prec] NSCALARS_STEP
+	   (NSCALARS_TOTAL +[infinite-prec] NSCALARS_SKIP)
+	   -[infinite-prec] NSCALARS_STEP
 
 	 and compare the IV against this limit _before_ incrementing it.
 	 Since the comparison type is unsigned, we actually want the
 	 subtraction to saturate at zero:
 
-	   NSCALARS_TOTAL -[sat] NSCALARS_STEP.  */
+	   (NSCALARS_TOTAL +[infinite-prec] NSCALARS_SKIP)
+	   -[sat] NSCALARS_STEP
+
+	 And since NSCALARS_SKIP < NSCALARS_STEP, we can reassociate this as:
+
+	   NSCALARS_TOTAL -[sat] (NSCALARS_STEP - NSCALARS_SKIP)
+
+	 where the rightmost subtraction can be done directly in
+	 COMPARE_TYPE.  */
       test_index = index_before_incr;
+      tree adjust = nscalars_step;
+      if (nscalars_skip)
+	adjust = gimple_build (preheader_seq, MINUS_EXPR, compare_type,
+			       adjust, nscalars_skip);
       test_limit = gimple_build (preheader_seq, MAX_EXPR, compare_type,
-				 nscalars_total, nscalars_step);
+				 nscalars_total, adjust);
       test_limit = gimple_build (preheader_seq, MINUS_EXPR, compare_type,
-				 test_limit, nscalars_step);
+				 test_limit, adjust);
       test_gsi = &incr_gsi;
+
+      /* Get a safe limit for the first iteration.  */
+      if (nscalars_skip)
+	{
+	  /* The first vector iteration can handle at most NSCALARS_STEP
+	     scalars.  NSCALARS_STEP <= CONST_LIMIT, and adding
+	     NSCALARS_SKIP to that cannot overflow.  */
+	  tree const_limit = build_int_cst (compare_type,
+					    LOOP_VINFO_VECT_FACTOR (loop_vinfo)
+					    * nscalars_per_iter);
+	  first_limit = gimple_build (preheader_seq, MIN_EXPR, compare_type,
+				      nscalars_total, const_limit);
+	  first_limit = gimple_build (preheader_seq, PLUS_EXPR, compare_type,
+				      first_limit, nscalars_skip);
+	}
+      else
+	/* For the first iteration it doesn't matter whether the IV hits
+	   a value above NSCALARS_TOTAL.  That only matters for the latch
+	   condition.  */
+	first_limit = nscalars_total;
     }
   else
     {
@@ -446,7 +494,12 @@  vect_set_loop_masks_directly (struct loo
 	 the bound before wrapping.  */
       test_index = index_after_incr;
       test_limit = nscalars_total;
+      if (nscalars_skip)
+	test_limit = gimple_build (preheader_seq, PLUS_EXPR, compare_type,
+				   test_limit, nscalars_skip);
       test_gsi = &loop_cond_gsi;
+
+      first_limit = test_limit;
     }
 
   /* Provide a definition of each mask in the group.  */
@@ -465,7 +518,7 @@  vect_set_loop_masks_directly (struct loo
 	 to have a full mask.  */
       poly_uint64 const_limit;
       bool first_iteration_full
-	= (poly_int_tree_p (nscalars_total, &const_limit)
+	= (poly_int_tree_p (first_limit, &const_limit)
 	   && must_ge (const_limit, (i + 1) * nscalars_per_mask));
 
       /* Rather than have a new IV that starts at BIAS and goes up to
@@ -482,12 +535,13 @@  vect_set_loop_masks_directly (struct loo
 					  bias_tree);
 	}
 
-      /* Create the initial mask.  */
+      /* Create the initial mask.  First include all scalars that
+	 are within the loop limit.  */
       tree init_mask = NULL_TREE;
       if (!first_iteration_full)
 	{
 	  tree start, end;
-	  if (nscalars_total == test_limit)
+	  if (first_limit == test_limit)
 	    {
 	      /* Use a natural test between zero (the initial IV value)
 		 and the loop limit.  The "else" block would be valid too,
@@ -498,8 +552,11 @@  vect_set_loop_masks_directly (struct loo
 	    }
 	  else
 	    {
+	      /* FIRST_LIMIT is the maximum number of scalars handled by the
+		 first iteration of the vector loop.  Test the portion
+		 associated with this mask.  */
 	      start = bias_tree;
-	      end = nscalars_total;
+	      end = first_limit;
 	    }
 
 	  init_mask = make_temp_ssa_name (mask_type, NULL, "max_mask");
@@ -507,6 +564,22 @@  vect_set_loop_masks_directly (struct loo
 	  gimple_seq_add_stmt (preheader_seq, tmp_stmt);
 	}
 
+      /* Now AND out the bits that are within the number of skipped
+	 scalars.  */
+      poly_uint64 const_skip;
+      if (nscalars_skip
+	  && !(poly_int_tree_p (nscalars_skip, &const_skip)
+	       && must_le (const_skip, bias)))
+	{
+	  tree unskipped_mask = vect_gen_while_not (preheader_seq, mask_type,
+						    bias_tree, nscalars_skip);
+	  if (init_mask)
+	    init_mask = gimple_build (preheader_seq, BIT_AND_EXPR, mask_type,
+				      init_mask, unskipped_mask);
+	  else
+	    init_mask = unskipped_mask;
+	}
+
       if (!init_mask)
 	/* First iteration is full.  */
 	init_mask = build_minus_one_cst (mask_type);
@@ -564,6 +637,9 @@  vect_set_loop_condition_masked (struct l
   else
     niters = gimple_convert (&preheader_seq, compare_type, niters);
 
+  /* Convert skip_niters to the right type.  */
+  tree niters_skip = LOOP_VINFO_MASK_SKIP_NITERS (loop_vinfo);
+
   /* Now calculate the value that the induction variable must be able
      to hit in order to ensure that we end the loop with an all-false mask.
      This involves adding the maximum number of inactive trailing scalar
@@ -572,6 +648,15 @@  vect_set_loop_condition_masked (struct l
   bool known_max_iters = max_loop_iterations (loop, &iv_limit);
   if (known_max_iters)
     {
+      if (niters_skip)
+	{
+	  /* Add the maximum number of skipped iterations to the
+	     maximum iteration count.  */
+	  if (TREE_CODE (niters_skip) == INTEGER_CST)
+	    iv_limit += wi::to_widest (niters_skip);
+	  else
+	    iv_limit += max_vf - 1;
+	}
       /* IV_LIMIT is the maximum number of latch iterations, which is also
 	 the maximum in-range IV value.  Round this value down to the previous
 	 vector alignment boundary and then add an extra full iteration.  */
@@ -617,7 +702,8 @@  vect_set_loop_condition_masked (struct l
 	test_mask = vect_set_loop_masks_directly (loop, loop_vinfo,
 						  &preheader_seq,
 						  loop_cond_gsi, rgm, vf,
-						  niters, might_wrap_p);
+						  niters, niters_skip,
+						  might_wrap_p);
       }
 
   /* Emit all accumulated statements.  */
@@ -1439,6 +1525,46 @@  vect_update_ivs_after_vectorizer (loop_v
     }
 }
 
+/* Return a gimple value containing the misalignment (measured in vector
+   elements) for the loop described by LOOP_VINFO, i.e. how many elements
+   it is away from a perfectly aligned address.  Add any new statements
+   to SEQ.  */
+
+static tree
+get_misalign_in_elems (gimple **seq, loop_vec_info loop_vinfo)
+{
+  struct data_reference *dr = LOOP_VINFO_UNALIGNED_DR (loop_vinfo);
+  gimple *dr_stmt = DR_STMT (dr);
+  stmt_vec_info stmt_info = vinfo_for_stmt (dr_stmt);
+  tree vectype = STMT_VINFO_VECTYPE (stmt_info);
+
+  unsigned int target_align = DR_TARGET_ALIGNMENT (dr);
+  gcc_assert (target_align != 0);
+
+  bool negative = tree_int_cst_compare (DR_STEP (dr), size_zero_node) < 0;
+  tree offset = (negative
+		 ? size_int (-TYPE_VECTOR_SUBPARTS (vectype) + 1)
+		 : size_zero_node);
+  tree start_addr = vect_create_addr_base_for_vector_ref (dr_stmt, seq,
+							  offset);
+  tree type = unsigned_type_for (TREE_TYPE (start_addr));
+  tree target_align_minus_1 = build_int_cst (type, target_align - 1);
+  HOST_WIDE_INT elem_size
+    = int_cst_value (TYPE_SIZE_UNIT (TREE_TYPE (vectype)));
+  tree elem_size_log = build_int_cst (type, exact_log2 (elem_size));
+
+  /* Create:  misalign_in_bytes = addr & (target_align - 1).  */
+  tree int_start_addr = fold_convert (type, start_addr);
+  tree misalign_in_bytes = fold_build2 (BIT_AND_EXPR, type, int_start_addr,
+					target_align_minus_1);
+
+  /* Create:  misalign_in_elems = misalign_in_bytes / element_size.  */
+  tree misalign_in_elems = fold_build2 (RSHIFT_EXPR, type, misalign_in_bytes,
+					elem_size_log);
+
+  return misalign_in_elems;
+}
+
 /* Function vect_gen_prolog_loop_niters
 
    Generate the number of iterations which should be peeled as prolog for the
@@ -1450,7 +1576,7 @@  vect_update_ivs_after_vectorizer (loop_v
    If the misalignment of DR is known at compile time:
      addr_mis = int mis = DR_MISALIGNMENT (dr);
    Else, compute address misalignment in bytes:
-     addr_mis = addr & (vectype_align - 1)
+     addr_mis = addr & (target_align - 1)
 
    prolog_niters = ((VF - addr_mis/elem_size)&(VF-1))/step
 
@@ -1497,33 +1623,17 @@  vect_gen_prolog_loop_niters (loop_vec_in
     }
   else
     {
-      bool negative = tree_int_cst_compare (DR_STEP (dr), size_zero_node) < 0;
-      tree offset = negative
-	  ? size_int (-TYPE_VECTOR_SUBPARTS (vectype) + 1) : size_zero_node;
-      tree start_addr = vect_create_addr_base_for_vector_ref (dr_stmt,
-							      &stmts, offset);
-      tree type = unsigned_type_for (TREE_TYPE (start_addr));
-      tree target_align_minus_1 = build_int_cst (type, target_align - 1);
+      tree misalign_in_elems = get_misalign_in_elems (&stmts, loop_vinfo);
+      tree type = TREE_TYPE (misalign_in_elems);
       HOST_WIDE_INT elem_size
 	= int_cst_value (TYPE_SIZE_UNIT (TREE_TYPE (vectype)));
-      tree elem_size_log = build_int_cst (type, exact_log2 (elem_size));
       HOST_WIDE_INT align_in_elems = target_align / elem_size;
       tree align_in_elems_minus_1 = build_int_cst (type, align_in_elems - 1);
       tree align_in_elems_tree = build_int_cst (type, align_in_elems);
-      tree misalign_in_bytes;
-      tree misalign_in_elems;
-
-      /* Create:  misalign_in_bytes = addr & (target_align - 1).  */
-      misalign_in_bytes
-	= fold_build2 (BIT_AND_EXPR, type, fold_convert (type, start_addr),
-		       target_align_minus_1);
-
-      /* Create:  misalign_in_elems = misalign_in_bytes / element_size.  */
-      misalign_in_elems
-	= fold_build2 (RSHIFT_EXPR, type, misalign_in_bytes, elem_size_log);
 
       /* Create:  (niters_type) ((align_in_elems - misalign_in_elems)
 				 & (align_in_elems - 1)).  */
+      bool negative = tree_int_cst_compare (DR_STEP (dr), size_zero_node) < 0;
       if (negative)
 	iters = fold_build2 (MINUS_EXPR, type, misalign_in_elems,
 			     align_in_elems_tree);
@@ -1563,20 +1673,22 @@  vect_gen_prolog_loop_niters (loop_vec_in
 
 /* Function vect_update_init_of_dr
 
-   NITERS iterations were peeled from LOOP.  DR represents a data reference
-   in LOOP.  This function updates the information recorded in DR to
-   account for the fact that the first NITERS iterations had already been
-   executed.  Specifically, it updates the OFFSET field of DR.  */
+   If CODE is PLUS, the vector loop starts NITERS iterations after the
+   scalar one, otherwise CODE is MINUS and the vector loop starts NITERS
+   iterations before the scalar one (using masking to skip inactive
+   elements).  This function updates the information recorded in DR to
+   account for the difference.  Specifically, it updates the OFFSET
+   field of DR.  */
 
 static void
-vect_update_init_of_dr (struct data_reference *dr, tree niters)
+vect_update_init_of_dr (struct data_reference *dr, tree niters, tree_code code)
 {
   tree offset = DR_OFFSET (dr);
 
   niters = fold_build2 (MULT_EXPR, sizetype,
 			fold_convert (sizetype, niters),
 			fold_convert (sizetype, DR_STEP (dr)));
-  offset = fold_build2 (PLUS_EXPR, sizetype,
+  offset = fold_build2 (code, sizetype,
 			fold_convert (sizetype, offset), niters);
   DR_OFFSET (dr) = offset;
 }
@@ -1584,14 +1696,12 @@  vect_update_init_of_dr (struct data_refe
 
 /* Function vect_update_inits_of_drs
 
-   NITERS iterations were peeled from the loop represented by LOOP_VINFO.
-   This function updates the information recorded for the data references in
-   the loop to account for the fact that the first NITERS iterations had
-   already been executed.  Specifically, it updates the initial_condition of
-   the access_function of all the data_references in the loop.  */
+   Apply vect_update_inits_of_dr to all accesses in LOOP_VINFO.
+   CODE and NITERS are as for vect_update_inits_of_dr.  */
 
 static void
-vect_update_inits_of_drs (loop_vec_info loop_vinfo, tree niters)
+vect_update_inits_of_drs (loop_vec_info loop_vinfo, tree niters,
+			  tree_code code)
 {
   unsigned int i;
   vec<data_reference_p> datarefs = LOOP_VINFO_DATAREFS (loop_vinfo);
@@ -1618,9 +1728,57 @@  vect_update_inits_of_drs (loop_vec_info
     }
 
   FOR_EACH_VEC_ELT (datarefs, i, dr)
-    vect_update_init_of_dr (dr, niters);
+    vect_update_init_of_dr (dr, niters, code);
 }
 
+/* For the information recorded in LOOP_VINFO prepare the loop for peeling
+   by masking.  This involves calculating the number of iterations to
+   be peeled and then aligning all memory references appropriately.  */
+
+void
+vect_prepare_for_masked_peels (loop_vec_info loop_vinfo)
+{
+  tree misalign_in_elems;
+  tree type = LOOP_VINFO_MASK_COMPARE_TYPE (loop_vinfo);
+
+  gcc_assert (vect_use_loop_mask_for_alignment_p (loop_vinfo));
+
+  /* From the information recorded in LOOP_VINFO get the number of iterations
+     that need to be skipped via masking.  */
+  if (LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo) > 0)
+    {
+      poly_int64 misalign = (LOOP_VINFO_VECT_FACTOR (loop_vinfo)
+			     - LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo));
+      misalign_in_elems = build_int_cst (type, misalign);
+    }
+  else
+    {
+      gimple_seq seq1 = NULL, seq2 = NULL;
+      misalign_in_elems = get_misalign_in_elems (&seq1, loop_vinfo);
+      misalign_in_elems = fold_convert (type, misalign_in_elems);
+      misalign_in_elems = force_gimple_operand (misalign_in_elems,
+						&seq2, true, NULL_TREE);
+      gimple_seq_add_seq (&seq1, seq2);
+      if (seq1)
+	{
+	  edge pe = loop_preheader_edge (LOOP_VINFO_LOOP (loop_vinfo));
+	  basic_block new_bb = gsi_insert_seq_on_edge_immediate (pe, seq1);
+	  gcc_assert (!new_bb);
+	}
+    }
+
+  if (dump_enabled_p ())
+    {
+      dump_printf_loc (MSG_NOTE, vect_location,
+		       "misalignment for fully-masked loop: ");
+      dump_generic_expr (MSG_NOTE, TDF_SLIM, misalign_in_elems);
+      dump_printf (MSG_NOTE, "\n");
+    }
+
+  LOOP_VINFO_MASK_SKIP_NITERS (loop_vinfo) = misalign_in_elems;
+
+  vect_update_inits_of_drs (loop_vinfo, misalign_in_elems, MINUS_EXPR);
+}
 
 /* This function builds ni_name = number of iterations.  Statements
    are emitted on the loop preheader edge.  If NEW_VAR_P is not NULL, set
@@ -2226,7 +2384,9 @@  vect_do_peeling (loop_vec_info loop_vinf
   int bound_prolog = 0;
   poly_uint64 bound_scalar = 0;
   int estimated_vf;
-  int prolog_peeling = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
+  int prolog_peeling = 0;
+  if (!vect_use_loop_mask_for_alignment_p (loop_vinfo))
+    prolog_peeling = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
   bool epilog_peeling = (LOOP_VINFO_PEELING_FOR_NITER (loop_vinfo)
 			 || LOOP_VINFO_PEELING_FOR_GAPS (loop_vinfo));
 
@@ -2343,7 +2503,7 @@  vect_do_peeling (loop_vec_info loop_vinf
 	  scale_loop_profile (prolog, prob_prolog, bound_prolog);
 	}
       /* Update init address of DRs.  */
-      vect_update_inits_of_drs (loop_vinfo, niters_prolog);
+      vect_update_inits_of_drs (loop_vinfo, niters_prolog, PLUS_EXPR);
       /* Update niters for vector loop.  */
       LOOP_VINFO_NITERS (loop_vinfo)
 	= fold_build2 (MINUS_EXPR, type, niters, niters_prolog);
Index: gcc/tree-vect-loop.c
===================================================================
--- gcc/tree-vect-loop.c	2017-11-17 15:09:28.969330125 +0000
+++ gcc/tree-vect-loop.c	2017-11-17 15:11:51.123849349 +0000
@@ -1119,6 +1119,7 @@  _loop_vec_info::_loop_vec_info (struct l
     versioning_threshold (0),
     vectorization_factor (0),
     max_vectorization_factor (0),
+    mask_skip_niters (NULL_TREE),
     mask_compare_type (NULL_TREE),
     unaligned_dr (NULL),
     peeling_for_alignment (0),
@@ -2266,16 +2267,6 @@  vect_analyze_loop_2 (loop_vec_info loop_
 			 " gaps is required.\n");
     }
 
-  if (LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo)
-      && LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo))
-    {
-      LOOP_VINFO_CAN_FULLY_MASK_P (loop_vinfo) = false;
-      if (dump_enabled_p ())
-	dump_printf_loc (MSG_MISSED_OPTIMIZATION, vect_location,
-			 "can't use a fully-masked loop because peeling for"
-			 " alignment is required.\n");
-    }
-
   /* Decide whether to use a fully-masked loop for this vectorization
      factor.  */
   LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
@@ -2376,18 +2367,21 @@  vect_analyze_loop_2 (loop_vec_info loop_
      increase threshold for this case if necessary.  */
   if (LOOP_REQUIRES_VERSIONING (loop_vinfo))
     {
-      poly_uint64 niters_th;
+      poly_uint64 niters_th = 0;
 
-      /* Niters for peeled prolog loop.  */
-      if (LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo) < 0)
+      if (!vect_use_loop_mask_for_alignment_p (loop_vinfo))
 	{
-	  struct data_reference *dr = LOOP_VINFO_UNALIGNED_DR (loop_vinfo);
-	  tree vectype = STMT_VINFO_VECTYPE (vinfo_for_stmt (DR_STMT (dr)));
-
-	  niters_th = TYPE_VECTOR_SUBPARTS (vectype) - 1;
+	  /* Niters for peeled prolog loop.  */
+	  if (LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo) < 0)
+	    {
+	      struct data_reference *dr = LOOP_VINFO_UNALIGNED_DR (loop_vinfo);
+	      tree vectype
+		= STMT_VINFO_VECTYPE (vinfo_for_stmt (DR_STMT (dr)));
+	      niters_th += TYPE_VECTOR_SUBPARTS (vectype) - 1;
+	    }
+	  else
+	    niters_th += LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
 	}
-      else
-	niters_th = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
 
       /* Niters for at least one iteration of vectorized loop.  */
       if (!LOOP_VINFO_FULLY_MASKED_P (loop_vinfo))
@@ -7167,9 +7161,28 @@  vectorizable_induction (gimple *phi,
   init_expr = PHI_ARG_DEF_FROM_EDGE (phi,
 				     loop_preheader_edge (iv_loop));
 
-  /* Convert the step to the desired type.  */
+  /* Convert the initial value and step to the desired type.  */
   stmts = NULL;
+  init_expr = gimple_convert (&stmts, TREE_TYPE (vectype), init_expr);
   step_expr = gimple_convert (&stmts, TREE_TYPE (vectype), step_expr);
+
+  /* If we are using the loop mask to "peel" for alignment then we need
+     to adjust the start value here.  */
+  tree skip_niters = LOOP_VINFO_MASK_SKIP_NITERS (loop_vinfo);
+  if (skip_niters != NULL_TREE)
+    {
+      if (FLOAT_TYPE_P (vectype))
+	skip_niters = gimple_build (&stmts, FLOAT_EXPR, TREE_TYPE (vectype),
+				    skip_niters);
+      else
+	skip_niters = gimple_convert (&stmts, TREE_TYPE (vectype),
+				      skip_niters);
+      tree skip_step = gimple_build (&stmts, MULT_EXPR, TREE_TYPE (vectype),
+				     skip_niters, step_expr);
+      init_expr = gimple_build (&stmts, MINUS_EXPR, TREE_TYPE (vectype),
+				init_expr, skip_step);
+    }
+
   if (stmts)
     {
       new_bb = gsi_insert_seq_on_edge_immediate (pe, stmts);
@@ -8040,6 +8053,11 @@  vect_transform_loop (loop_vec_info loop_
 
   split_edge (loop_preheader_edge (loop));
 
+  if (LOOP_VINFO_FULLY_MASKED_P (loop_vinfo)
+      && vect_use_loop_mask_for_alignment_p (loop_vinfo))
+    /* This will deal with any possible peeling.  */
+    vect_prepare_for_masked_peels (loop_vinfo);
+
   /* FORNOW: the vectorizer supports only loops which body consist
      of one basic block (header + empty latch). When the vectorizer will
      support more involved loop forms, the order by which the BBs are
@@ -8319,29 +8337,40 @@  vect_transform_loop (loop_vec_info loop_
   /* +1 to convert latch counts to loop iteration counts,
      -min_epilogue_iters to remove iterations that cannot be performed
        by the vector code.  */
-  int bias = 1 - min_epilogue_iters;
+  int bias_for_lowest = 1 - min_epilogue_iters;
+  int bias_for_assumed = bias_for_lowest;
+  int alignment_npeels = LOOP_VINFO_PEELING_FOR_ALIGNMENT (loop_vinfo);
+  if (alignment_npeels && LOOP_VINFO_FULLY_MASKED_P (loop_vinfo))
+    {
+      /* When the amount of peeling is known at compile time, the first
+	 iteration will have exactly alignment_npeels active elements.
+	 In the worst case it will have at least one.  */
+      int min_first_active = (alignment_npeels > 0 ? alignment_npeels : 1);
+      bias_for_lowest += lowest_vf - min_first_active;
+      bias_for_assumed += assumed_vf - min_first_active;
+    }
   /* In these calculations the "- 1" converts loop iteration counts
      back to latch counts.  */
   if (loop->any_upper_bound)
     loop->nb_iterations_upper_bound
       = (final_iter_may_be_partial
-	 ? wi::udiv_ceil (loop->nb_iterations_upper_bound + bias,
+	 ? wi::udiv_ceil (loop->nb_iterations_upper_bound + bias_for_lowest,
 			  lowest_vf) - 1
-	 : wi::udiv_floor (loop->nb_iterations_upper_bound + bias,
+	 : wi::udiv_floor (loop->nb_iterations_upper_bound + bias_for_lowest,
 			   lowest_vf) - 1);
   if (loop->any_likely_upper_bound)
     loop->nb_iterations_likely_upper_bound
       = (final_iter_may_be_partial
-	 ? wi::udiv_ceil (loop->nb_iterations_likely_upper_bound + bias,
-			  lowest_vf) - 1
-	 : wi::udiv_floor (loop->nb_iterations_likely_upper_bound + bias,
-			   lowest_vf) - 1);
+	 ? wi::udiv_ceil (loop->nb_iterations_likely_upper_bound
+			  + bias_for_lowest, lowest_vf) - 1
+	 : wi::udiv_floor (loop->nb_iterations_likely_upper_bound
+			   + bias_for_lowest, lowest_vf) - 1);
   if (loop->any_estimate)
     loop->nb_iterations_estimate
       = (final_iter_may_be_partial
-	 ? wi::udiv_ceil (loop->nb_iterations_estimate + bias,
+	 ? wi::udiv_ceil (loop->nb_iterations_estimate + bias_for_assumed,
 			  assumed_vf) - 1
-	 : wi::udiv_floor (loop->nb_iterations_estimate + bias,
+	 : wi::udiv_floor (loop->nb_iterations_estimate + bias_for_assumed,
 			   assumed_vf) - 1);
 
   if (dump_enabled_p ())
Index: gcc/tree-vect-stmts.c
===================================================================
--- gcc/tree-vect-stmts.c	2017-11-17 15:07:59.275435265 +0000
+++ gcc/tree-vect-stmts.c	2017-11-17 15:11:51.124849349 +0000
@@ -9975,3 +9975,16 @@  vect_gen_while (tree mask, tree start_in
   gimple_call_set_lhs (call, mask);
   return call;
 }
+
+/* Generate a vector mask of type MASK_TYPE for which index I is false iff
+   J + START_INDEX < END_INDEX for all J <= I.  Add the statements to SEQ.  */
+
+tree
+vect_gen_while_not (gimple_seq *seq, tree mask_type, tree start_index,
+		    tree end_index)
+{
+  tree tmp = make_ssa_name (mask_type);
+  gcall *call = vect_gen_while (tmp, start_index, end_index);
+  gimple_seq_add_stmt (seq, call);
+  return gimple_build (seq, BIT_NOT_EXPR, mask_type, tmp);
+}
Index: gcc/testsuite/gcc.target/aarch64/sve_nopeel_1.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_nopeel_1.c	2017-11-17 15:11:51.120849349 +0000
@@ -0,0 +1,39 @@ 
+/* { dg-options "-O2 -ftree-vectorize -march=armv8-a+sve -msve-vector-bits=256" } */
+
+#include <stdint.h>
+
+#define TEST(NAME, TYPE)			\
+ void						\
+ NAME##1 (TYPE *x, int n)			\
+ {						\
+   for (int i = 0; i < n; ++i)			\
+     x[i] += 1;					\
+ }						\
+ TYPE NAME##_array[1024];			\
+ void						\
+ NAME##2 (void)					\
+ {						\
+   for (int i = 1; i < 200; ++i)		\
+     NAME##_array[i] += 1;			\
+ }
+
+TEST (s8, int8_t)
+TEST (u8, uint8_t)
+TEST (s16, int16_t)
+TEST (u16, uint16_t)
+TEST (s32, int32_t)
+TEST (u32, uint32_t)
+TEST (s64, int64_t)
+TEST (u64, uint64_t)
+TEST (f16, _Float16)
+TEST (f32, float)
+TEST (f64, double)
+
+/* No scalar memory accesses.  */
+/* { dg-final { scan-assembler-not {[wx][0-9]*, \[} } } */
+/* 2 for each NAME##1 test, one in the header and one in the main loop
+   and 1 for each NAME##2 test, in the main loop only.  */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.b,} 6 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.h,} 6 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.s,} 9 } } */
+/* { dg-final { scan-assembler-times {\twhilelo\tp[0-7]\.d,} 9 } } */
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_1.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_1.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,27 @@ 
+/* { dg-do compile } */
+/* Pick an arbitrary target for which unaligned accesses are more
+   expensive.  */
+/* { dg-options "-O3 -march=armv8-a+sve -msve-vector-bits=256 -mtune=thunderx" } */
+
+#define N 512
+#define START 1
+#define END 505
+
+int x[N] __attribute__((aligned(32)));
+
+void __attribute__((noinline, noclone))
+foo (void)
+{
+  unsigned int v = 0;
+  for (unsigned int i = START; i < END; ++i)
+    {
+      x[i] = v;
+      v += 5;
+    }
+}
+
+/* We should operate on aligned vectors.  */
+/* { dg-final { scan-assembler {\tadrp\tx[0-9]+, x\n} } } */
+/* We should use an induction that starts at -5, with only the last
+   7 elements of the first iteration being active.  */
+/* { dg-final { scan-assembler {\tindex\tz[0-9]+\.s, #-5, #5\n} } } */
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_1_run.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_1_run.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,18 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx" } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx -msve-vector-bits=256" { target aarch64_sve256_hw } } */
+
+#include "sve_peel_ind_1.c"
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+  foo ();
+  for (int i = 0; i < N; ++i)
+    {
+      if (x[i] != (i < START || i >= END ? 0 : (i - START) * 5))
+	__builtin_abort ();
+      asm volatile ("" ::: "memory");
+    }
+  return 0;
+}
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,22 @@ 
+/* { dg-do compile } */
+/* Pick an arbitrary target for which unaligned accesses are more
+   expensive.  */
+/* { dg-options "-O3 -march=armv8-a+sve -msve-vector-bits=256 -mtune=thunderx" } */
+
+#define N 512
+#define START 7
+#define END 22
+
+int x[N] __attribute__((aligned(32)));
+
+void __attribute__((noinline, noclone))
+foo (void)
+{
+  for (unsigned int i = START; i < END; ++i)
+    x[i] = i;
+}
+
+/* We should operate on aligned vectors.  */
+/* { dg-final { scan-assembler {\tadrp\tx[0-9]+, x\n} } } */
+/* We should unroll the loop three times.  */
+/* { dg-final { scan-assembler-times "\tst1w\t" 3 } } */
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2_run.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_2_run.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,18 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx" } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx -msve-vector-bits=256" { target aarch64_sve256_hw } } */
+
+#include "sve_peel_ind_2.c"
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+  foo ();
+  for (int i = 0; i < N; ++i)
+    {
+      if (x[i] != (i < START || i >= END ? 0 : i))
+	__builtin_abort ();
+      asm volatile ("" ::: "memory");
+    }
+  return 0;
+}
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_3.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_3.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* Pick an arbitrary target for which unaligned accesses are more
+   expensive.  */
+/* { dg-options "-O3 -march=armv8-a+sve -msve-vector-bits=256 -mtune=thunderx" } */
+
+#define N 32
+#define MAX_START 8
+#define COUNT 16
+
+int x[MAX_START][N] __attribute__((aligned(32)));
+
+void __attribute__((noinline, noclone))
+foo (int start)
+{
+  for (int i = start; i < start + COUNT; ++i)
+    x[start][i] = i;
+}
+
+/* We should operate on aligned vectors.  */
+/* { dg-final { scan-assembler {\tadrp\tx[0-9]+, x\n} } } */
+/* { dg-final { scan-assembler {\tubfx\t} } } */
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_3_run.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_3_run.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,21 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx" } */
+/* { dg-options "-O3 -march=armv8-a+sve -mtune=thunderx -msve-vector-bits=256" { target aarch64_sve256_hw } } */
+
+#include "sve_peel_ind_3.c"
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+  for (int start = 0; start < MAX_START; ++start)
+    {
+      foo (start);
+      for (int i = 0; i < N; ++i)
+	{
+	  if (x[start][i] != (i < start || i >= start + COUNT ? 0 : i))
+	    __builtin_abort ();
+	  asm volatile ("" ::: "memory");
+	}
+    }
+  return 0;
+}
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_4.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_4.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,21 @@ 
+/* { dg-do compile } */
+/* Pick an arbitrary target for which unaligned accesses are more
+   expensive.  */
+/* { dg-options "-Ofast -march=armv8-a+sve -msve-vector-bits=256 -mtune=thunderx -fno-vect-cost-model" } */
+
+#define START 1
+#define END 505
+
+void __attribute__((noinline, noclone))
+foo (double *x)
+{
+  double v = 10.0;
+  for (unsigned int i = START; i < END; ++i)
+    {
+      x[i] = v;
+      v += 5.0;
+    }
+}
+
+/* We should operate on aligned vectors.  */
+/* { dg-final { scan-assembler {\tubfx\t} } } */
Index: gcc/testsuite/gcc.target/aarch64/sve_peel_ind_4_run.c
===================================================================
--- /dev/null	2017-11-14 14:28:07.424493901 +0000
+++ gcc/testsuite/gcc.target/aarch64/sve_peel_ind_4_run.c	2017-11-17 15:11:51.121849349 +0000
@@ -0,0 +1,29 @@ 
+/* { dg-do run { target aarch64_sve_hw } } */
+/* { dg-options "-Ofast -march=armv8-a+sve -mtune=thunderx" } */
+/* { dg-options "-Ofast -march=armv8-a+sve -mtune=thunderx -mtune=thunderx" { target aarch64_sve256_hw } } */
+
+#include "sve_peel_ind_4.c"
+
+int __attribute__ ((optimize (1)))
+main (void)
+{
+  double x[END + 1];
+  for (int i = 0; i < END + 1; ++i)
+    {
+      x[i] = i;
+      asm volatile ("" ::: "memory");
+    }
+  foo (x);
+  for (int i = 0; i < END + 1; ++i)
+    {
+      double expected;
+      if (i < START || i >= END)
+	expected = i;
+      else
+	expected = 10 + (i - START) * 5;
+      if (x[i] != expected)
+	__builtin_abort ();
+      asm volatile ("" ::: "memory");
+    }
+  return 0;
+}