This is the mail archive of the gcc-patches@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

Re: Add a loop versioning pass


On Thu, Dec 6, 2018 at 2:19 PM Richard Sandiford
<richard.sandiford@arm.com> wrote:
>
> Richard Biener <rguenther@suse.de> writes:
> >> > The pass contains an awful lot of heuristics :/  Like last year
> >> > with the interchange pass I would suggest to rip most of it out
> >> > and first lay infrastructure with the cases you can positively
> >> > identify without applying heuristics or "hacks" like stripping
> >> > semantically required casts.  That makes it also clear which
> >> > testcases test which code-path.  That said, all the analyze
> >> > multiplications/plusses/factors stuff was extremely hard to review
> >> > and I have no overall picture why this is all so complicated or
> >> > necessary.
> >>
> >> I think the tests do cover most of this -- was glad to see that
> >> when Kyrill tried taking something out, one of the tests started
> >> to fail :-).  And the comments in the tests as well as the code
> >> are supposed to explain why this is desirable.  But I can try to
> >> spruce up the comments in the code if they're not detailed enough.
> >>
> >> The problem is that the motivating (Fortran) case can't be identified
> >> without applying heuristics.  All we see is a collection of adds and
> >> multiplies, with no semantic information to say which multiplies are for
> >> the inner dimension and which aren't.  I agree it would be nicer not
> >> to have them, which is why I'd originally suggested the IFN to provide
> >> information about dimensions.
> >
> > Sure, still a new pass having _all_ the heuristic built in with
> > 10s of testcases do not make it easy to review those heuristics...
>
> Fair :-)  In the patch below I've tried to cut down on the special cases
> and tried to add more comments explaining which patterns the code is
> trying to detect/reject.  Probably the key part is the one beginning:
>
> +   The main difficulty here isn't finding strides that could be used
> +   in a version check (that's pretty easy).  The problem instead is to
> +   avoid versioning for some stride S that is unlikely ever to be 1 at
> +   runtime.  Versioning for S == 1 on its own would lead to unnecessary
> +   code bloat, while adding S == 1 to more realistic version conditions
> +   would lose the optimisation opportunity offered by those other conditions.
>
> >> >> +/* Return true if in principle it is worth versioning an index fragment of
> >> >> +   the form:
> >> >> +
> >> >> +     (i * b * SCALE) / FACTOR
> >> >> +
> >> >> +   for the case in which b == 1.  */
> >> >> +
> >> >> +bool
> >> >> +loop_versioning::acceptable_scale_p (tree scale, poly_uint64 factor)
> >> >> +{
> >> >> +  /* See whether SCALE is a constant multiple of FACTOR, and if the
> >> >> +     multiple is small enough for us to treat it as a potential grouped
> >> >> +     access.  For example:
> >> >> +
> >> >> +       for (auto i : ...)
> >> >> +  y[i] = f (x[4 * i * stride],
> >> >> +            x[4 * i * stride + 1],
> >> >> +            x[4 * i * stride + 2]);
> >> >> +
> >> >> +     would benefit from versioning for the case in which stride == 1.
> >> >> +     High multiples of i * stride are less likely to benefit, and could
> >> >> +     indicate a simulated multi-dimensional array.
> >> >> +
> >> >> +     This is just a heuristic, to avoid having to do expensive group
> >> >> +     analysis of the data references in a loop.  */
> >> >> +  poly_uint64 const_scale;
> >> >> +  unsigned int multiple;
> >> >> +  if (poly_int_tree_p (scale, &const_scale)
> >> >> +      && constant_multiple_p (const_scale, factor, &multiple))
> >> >> +    {
> >> >> +      unsigned int maxval = PARAM_VALUE (PARAM_LOOP_VERSIONING_GROUP_SIZE);
> >> >> +      return IN_RANGE (multiple, 1, maxval);
> >> >
> >> > Hmm.  So you _do_ want to version sth like
> >> >
> >> > struct X { int i; int j; } a[2048];
> >> >
> >> > for (int i = start; i < end; ++i)
> >> >   a[i*s].i = 1;
> >> >
> >> > ?  That is, even with s == 1 the accesses will not become contiguous?
> >> > OK, I suppose that's because you are looking at a stmt in isolation
> >> > and another stmt may access a[i*s].j here.
> >> >
> >> > That is, would it be a future improvement to run sth like the
> >> > vectorizers group access analysis on the references and perform
> >> > this check on whole groups then, possibly better being able to
> >> > constrain what is now the magic parameter PARAM_LOOP_VERSIONING_GROUP_SIZE?
> >>
> >> Yeah, possibly.  The problem is that we might end up having to reproduce
> >> vectoriser heuristics.  E.g. stores with gaps should be vectorisable
> >> in future with SVE, using contiguous predicated stores in which some
> >> lanes are inactive.  So we don't necessarily need the .j access for
> >> this to be worthwhile.
> >>
> >> But perhaps the argument for versioning for vectorisation is stronger
> >> for grouped accesses than contiguous ones.
> >>
> >> Note that the above example doesn't rely on the grouping heuristic
> >> since we just strip the COMPONENT_REF and then analyze the ARRAY_REF
> >> with a factor of 1.  The heuristic instead handles things like:
> >>
> >>   a[i * stride * 2]
> >>
> >> etc., and the param is more there to decide when a constant factor is
> >> small enough to be a realistic group size instead of an outer dimension.
> >> E.g. if we see:
> >>
> >>   a[i * stride * 20000]
> >>
> >> the problem is that i * stride * 20000 is likely to be applying
> >> an outer dimension index.
> >>
> >> > I guess you tried to constrain it to the stmts access size but of course
> >> > that fails short of handling later SLP vectorized cases.
> >>
> >> Right.
> >
> > So I think we want to constrain it to sth like the maximum targets
> > vector size.  We want consecutive iterations to bring two memory
> > accesses to the same vector load, otherwise we can use strided
> > stores (or gathers) anyways.  This means the distance between
> > accesses should be at most max_vectsize / 2?  (non-power-of-two
> > interleaving is also quite limited, sth. to be considered)
>
> OK.  The updated patch removes the new param and instead uses the maximum of:
>
> - omp_max_vf () (maximum vector size in bytes, or 1 if loop vectorisation
>   is disabled)
>
> - MAX_FIXED_MODE_SIZE, which should be a good limit for scalar code.
>
> >> >> +{
> >> >> +  const unsigned int MAX_NSPLIT = 8;
> >> >> +
> >> >> +  if (dump_file && (dump_flags & TDF_DETAILS))
> >> >> +    {
> >> >> +      fprintf (dump_file, ";; Analyzing use of ");
> >> >> +      print_generic_expr (dump_file, expr, TDF_SLIM);
> >> >> +      if (maybe_ne (factor, 1U))
> >> >> + {
> >> >> +   fprintf (dump_file, " (which addresses ");
> >> >> +   print_dec (factor, dump_file);
> >> >> +   fprintf (dump_file, " bytes)");
> >> >> + }
> >> >> +      fprintf (dump_file, " in loop %d (depth %d)\n",
> >> >> +        loop->num, loop_depth (loop));
> >> >> +    }
> >> >> +
> >> >> +  /* The main problem we have here is that we cannot assume that the
> >> >> +     innermost loop iterates over the innermost dimension of an array.
> >> >> +     Accidentally adding versioning checks for outer dimensions would
> >> >> +     cause the version condition to be false, which as well as bloating
> >> >> +     the code would defeat loop versioning benefits for other accesses.
> >> >> +
> >> >> +     Unfortunately all we usually see at this stage is general address
> >> >> +     arithmetic, with no positive way of identifying how many dimensions
> >> >> +     an array access has and which multiplication factors in the address
> >> >> +     expression correspond to which array dimensions.  In C code this is
> >> >> +     often not even explicit in the source, since variable-sized multi-
> >> >> +     dimensional arrays are often simulated using one-dimensional arrays.
> >> >> +
> >> >> +     The three main ways in which we deal with this are:
> >> >> +
> >> >> +     - use heuristics that positively identify steps that are likely
> >> >> +       to represent the inner dimension.
> >> >> +
> >> >> +     - use heuristics that positively identify steps that are unlikely
> >> >> +       to represent the inner dimension.
> >> >> +
> >> >> +     - if a part of EXPR is invariant in LOOP, analyze its evolution in
> >> >> +       the outer loops to see whether we can positively identify any of
> >> >> +       it as iterating over the inner dimension.  */
> >> >> +  tree best_step = NULL_TREE;
> >> >> +  auto_vec<tree, MAX_NSPLIT> worklist;
> >> >> +  worklist.quick_push (expr);
> >> >> +  unsigned int nsplit = 0;
> >> >> +  while (!worklist.is_empty ())
> >> >> +    {
> >> >> +      expr = strip_casts (worklist.pop ());
> >> >> +      tree_code code = TREE_CODE (expr);
> >> >> +
> >> >> +      if (code == POLYNOMIAL_CHREC)
> >> >> + {
> >> >> +   /* Analyze the CHREC_RIGHT as the step in an index fragment.  */
> >> >> +   tree step_if_innermost;
> >> >> +   tree step = extract_step (CHREC_RIGHT (expr), factor,
> >> >> +                             &step_if_innermost);
> >> >> +   if (!best_step)
> >> >> +     {
> >> >> +       /* This is the outermost chrec for the original expression.
> >> >> +          It's not worth carrying on if the step isn't versionable,
> >> >> +          or if we're pretty sure it's not for the inner dimension.  */
> >> >> +       if (!step_if_innermost
> >> >> +           || TREE_CODE (step) != SSA_NAME
> >> >> +           || !expr_invariant_in_loop_p (loop, step))
> >> >> +         return;
> >> >> +
> >> >> +       best_step = step;
> >> >> +
> >> >> +       /* We should version for STEP == 1 if we know that that can be
> >> >> +          true under some circumstances.  */
> >> >> +       if (integer_onep (step_if_innermost))
> >> >> +         break;
> >> >> +
> >> >> +       /* Bail out if this appears to be the step for the innermost
> >> >> +          dimension, but isn't likely to be 1.
> >> >> +
> >> >> +          ??? We could instead version for when it equals
> >> >> +          STEP_IF_INNERMOST, but it's not likely to have as much
> >> >> +          benefit as versioning for 1.  */
> >> >> +       if (step_if_innermost != step)
> >> >> +         return;
> >> >> +     }
> >> >> +   else
> >> >> +     {
> >> >> +       /* This is an inner chrec.  If it looks like it iterates over
> >> >> +          the innermost dimension, abort any attempt to version for
> >> >> +          the outermost chrec (which if we reach here wasn't itself
> >> >> +          obviously iterating over the innermost dimension).  */
> >> >> +       if (step_if_innermost && TREE_CONSTANT (step_if_innermost))
> >> >> +         return;
> >> >> +     }
> >> >> +   worklist.quick_push (CHREC_LEFT (expr));
> >> >> +   continue;
> >> >> + }
> >> >> +
> >> >> +      /* Analyze parts of a queued CHREC_LEFT.  This is better than just
> >> >> +  analyzing the evolution of the whole expression since the value
> >> >> +  could include a mixture of analyzable and unanalyzable elements.
> >> >> +  Use NSPLIT to count cases in which we add more expressions to
> >> >> +  analyze, as opposed to just simplifying the existing one.  */
> >> >> +      if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR || code == MINUS_EXPR)
> >> >> + {
> >> >> +   worklist.quick_push (TREE_OPERAND (expr, 0));
> >> >> +   if (nsplit++ < MAX_NSPLIT)
> >> >> +     worklist.quick_push (TREE_OPERAND (expr, 1));
> >> >> +   continue;
> >> >> + }
> >> >> +      if (code == MULT_EXPR)
> >> >> + {
> >> >> +   tree op0 = strip_casts (TREE_OPERAND (expr, 0));
> >> >> +   tree op1 = TREE_OPERAND (expr, 1);
> >> >> +   if (TREE_CODE (op0) == PLUS_EXPR || TREE_CODE (op0) == MINUS_EXPR)
> >> >> +     {
> >> >> +       tree type = TREE_TYPE (expr);
> >> >> +       tree op00 = fold_convert (type, TREE_OPERAND (op0, 0));
> >> >> +       worklist.quick_push (fold_build2 (MULT_EXPR, type, op00, op1));
> >> >> +       if (nsplit++ < MAX_NSPLIT)
> >> >> +         {
> >> >> +           tree op01 = fold_convert (type, TREE_OPERAND (op0, 1));
> >> >> +           worklist.quick_push (fold_build2 (MULT_EXPR, type,
> >> >> +                                             op01, op1));
> >> >> +         }
> >> >> +       continue;
> >> >> +     }
> >> >> + }
> >> >> +
> >> >> +      /* If EXPR is invariant in LOOP, analyze it wrt the innermost loop
> >> >> +  for which it could evolve (i.e. the loop containing the outermost
> >> >> +  one for which EXPR is invariant).  */
> >> >
> >> > This isn't how analyze_scalar_evolution works - you _always_ have to
> >> > feed the innermost loop that the expression is used in (the context).
> >> > Then you instantiate the result in the outermost loop of the nest you
> >> > are interested in.  Otherwise you get garbage.
> >>
> >> I dispute this. :-)  You might remember we had a similar discussion
> >> about the use in get_base_for_alignment_1.
> >
> > I don't remember ;)
> >
> >> analyze_scalar_evolution describes how the supplied value V evolves,
> >> which isn't garbage in itself.  Then instantiating the SCEV tells
> >
> > Then analyze_scalar_evolution wouldn't need a 'loop' parameter.
> > It probably should get a stmt (or a use_operand) instead.
>
> Maybe :-)
>
> >> you what this means for the use in:
> >>
> >>      RES = V;   // A
> >>      ...
> >>      use of RES // B
> >>
> >> in terms of values that are available at B.  But we're not interested in
> >> the second bit here.  We only want to analyze *how* V evolves, not use
> >> or compute its value in a given context.  Instantiating the SCEV would
> >> lose useful information.
> >
> > Instantiating only "loses" information in case there's an overall
> > effect of a loop to consider.  I guess in your case it only brings
> > in not useful information.
>
> Well, any information we can get is useful here.  Even if something
> is never going to be a versioning opportunity itself, it can still
> help to stop us adding pointless version checks for other parts
> of the address calculation.
>
> > That said, you can indeed pass analyze_scalar_evolution outer loops
> > of the loop the use is in (it's also in the outer loops).
> >
> >> > It looks like you are re-analyzing SSA names in the evolution - that's
> >> > odd and shouldn't be necessary (but you forget to instantiate, so...).
> >> >
> >> > I suggest to move the analyze_scalar_evolution part out of the worklist
> >> > loop where you are sure you have an SSA name.
> >>
> >> This too is because we don't care whether the whole address can
> >> be expressed as a SCEV, we just want to search for parts of the
> >> calculation that are more likely to be inner dimensions.
> >>
> >> This isn't (just) because we don't instantiate.  It's because we're
> >> more aggressive than SCEV can be in looking through casts.
> >
> > :/
>
> The updated patch avoids this.
>
> >> >> +  /* Bail out if OP2 can be analyzed as an evolution; we want to use
> >> >> +     analyze_evolution in that case instead.  There's no point trying
> >> >> +     hard to avoid repeating the call to analyze_scalar_evolution since
> >> >> +     that function does its own caching.  */
> >> >> +  if (tree_contains_chrecs (analyze_scalar_evolution (loop, op2), NULL))
> >> >
> >> > Don't you want !chrec_contains_undetermined (...) instead?  I wonder what
> >> > is missing to allow all interesting expressions to be analyzed by
> >> > SCEV?
> >>
> >> This code is handling cases that vary arbitrarily between iterations,
> >> such as a histogram update.  I don't think SCEV could ever provide
> >> anything useful here.
> >>
> >> analyze_scalar_evolution returns op2 if it can't analyze op2 as a SCEV.
> >
> > Hmm, right.  Looks inconsistent to instantiation...
> >
> >> >> +/* Treat EXPR as a sum of products and apply analyze_product to each of the
> >> >> +   products.  Return true if one of the products provides a versioning
> >> >> +   opportunity.  FACTOR is as for analyze_product.  */
> >> >> +
> >> >> +bool
> >> >> +loop_versioning::analyze_sum_of_products (struct loop *loop, tree expr,
> >> >> +                                   poly_uint64 factor)
> >> >> +{
> >> >
> >> > This looks awfully close to what tree-affine.c does apart from more
> >> > aggressively stripping conversions?  I see all the analysis parts
> >> > are limited and thus "O(1)" but still there's going to be a lot of
> >> > redundancy involved for repeated use of (derived) "IVs"?  Wouldn't
> >> > it be good to have a bitmap of already handled SSA_NAMEs to stop
> >> > processing early?
> >>
> >> I did consider this but think it'll do more harm than good.
> >> Bitmap lookup is O(N) in the worst case, and the PR you were
> >> working on a month or so ago shows that that really can bite.
> >> Whereas like you say the limits make this O(1).
> >>
> >> So although Kyrill and Ramana's patch added this check, I'd prefer to
> >> leave it out.  It seems like premature optimisation and I think it'll
> >> make things worse in practice.
> >
> > OK.
> >
> >> >> +  const unsigned int MAX_NITERS = 8;
> >> >> +
> >> >> +  tree worklist[MAX_NITERS];
> >> >> +  unsigned int length = 0;
> >> >> +  worklist[length++] = expr;
> >> >> +  for (unsigned int i = 0; i < length; ++i)
> >> >> +    {
> >> >> +      expr = worklist[i];
> >> >> +      gassign *assign = maybe_get_assign_strip_casts (loop, expr);
> >> >> +      if (!assign)
> >> >> + continue;
> >> >> +
> >> >> +      tree_code code = gimple_assign_rhs_code (assign);
> >> >> +      if (code == PLUS_EXPR || code == POINTER_PLUS_EXPR || code == MINUS_EXPR)
> >> >
> >> > POINTER_MINUS_EXPR?
> >>
> >> Do you mean POINTER_DIFF_EXPR?  I wouldn't expect that to be interesting
> >> since it would give information about the two pointers being subtracted.
> >>
> >> >> +/* Analyze expression EXPR, which occurs in loop LOOP.  */
> >> >> +
> >> >> +void
> >> >> +loop_versioning::analyze_expr (struct loop *loop, tree expr)
> >> >> +{
> >> >> +  while (handled_component_p (expr))
> >> >> +    {
> >> >> +      /* See whether we can use versioning to avoid a multiplication
> >> >> +  in the array index.  */
> >> >> +      if (TREE_CODE (expr) == ARRAY_REF)
> >> >> + {
> >> >> +   if (!analyze_sum_of_products (loop, TREE_OPERAND (expr, 1), 1))
> >> >> +     analyze_evolution (loop, TREE_OPERAND (expr, 1), 1);
> >> >> + }
> >> >> +      expr = TREE_OPERAND (expr, 0);
> >> >> +    }
> >> >> +
> >> >> +  if (TREE_CODE (expr) == MEM_REF)
> >> >> +    {
> >> >> +      tree addr = TREE_OPERAND (expr, 0);
> >> >> +      /* See whether we can use versioning to avoid a multiplication
> >> >> +  in the pointer calculation.  This is generally only worth
> >> >> +  doing if the multiplication occurs in this loop rather than
> >> >> +  an outer loop.  */
> >> >
> >> > Why's that so here but not above for ARRAY_REF?  That is, what is
> >> > the difference between a[i] and ptr = &a[i]; *ptr?
> >>
> >> Good question.  I think there was a (probably poor) reason, but clearly
> >> I didn't write it down.
> >>
> >> > > +  struct loop *loop = gimple_bb (stmt)->loop_father;
> >> > > +
> >> > > +  unsigned int nops = gimple_num_ops (stmt);
> >> > > +  for (unsigned int i = 0; i < nops; ++i)
> >> > > +    if (tree op = gimple_op (stmt, i))
> >> > > +      analyze_expr (loop, op);
> >> >
> >> > I think you instead want to use gimple_walk_load_store_ops ().
> >>
> >> I agree with Kyrill's response to this.
> >
> > C++ bites you? ;)  I suppose marshalling a pmf through the void *
> > data and having the callback invoke the pmf might be possible?
> > But yeah - GCC is C and most callbacks are just function pointers
> > and not pointer-to-member fns.
> >
> > You are at least also walking debug-stmts here I think
> > (unless they are expensive_stmt_p ...).
>
> Bah, yes.  I remember this only about half the time :-(
>
> >> In addition, I'd originally
> >> wondered whether we should also version for ADDR_EXPRs, and that might
> >> make sense if we add more types of versioning (not just unit strides)
> >> in future.
> >
> > There's gimple_walk_load_store_addr_ops () of course.
> >
> >> >> +/* Analyze all the statements in BB looking for useful version checks.  */
> >> >> +
> >> >> +void
> >> >> +loop_versioning::analyze_block (basic_block bb)
> >> >> +{
> >> >> +  struct loop *loop = bb->loop_father;
> >> >> +  loop_info &li = get_loop_info (loop);
> >> >> +  if (li.rejected_p)
> >> >> +    return;
> >> >> +
> >> >> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
> >> >> +       gsi_next (&gsi))
> >> >> +    {
> >> >> +      gimple *stmt = gsi_stmt (gsi);
> >> >> +      if (expensive_stmt_p (stmt))
> >> >> + {
> >> >> +   if (dump_file && (dump_flags & TDF_DETAILS))
> >> >> +     {
> >> >> +       struct loop *loop = gimple_bb (stmt)->loop_father;
> >> >> +       fprintf (dump_file, ";; Loop %d (depth %d) contains expensive"
> >> >> +                " stmt: ", loop->num, loop_depth (loop));
> >> >> +       print_gimple_stmt (dump_file, stmt, 0, TDF_SLIM);
> >> >> +     }
> >> >> +   li.rejected_p = true;
> >> >
> >> > I assume that once a loop is rejected this or another way there's
> >> > no reason to look at any outer loop of it, thus ...
> >> >
> >> >> +   break;
> >> >> + }
> >> >> +
> >> >> +      /* Only look for direct versioning opportunities in inner loops
> >> >> +  since the benefit tends to be much smaller for outer loops.  */
> >> >> +      if (!loop->inner)
> >> >> + analyze_stmt (stmt);
> >> >> +
> >> >> +      /* The point of the instruction limit is to prevent excessive
> >> >> +  code growth, so this is a size-based estimate even though
> >> >> +  the optimization is aimed at speed.  */
> >> >> +      li.num_insns += estimate_num_insns (stmt, &eni_size_weights);
> >> >> +    }
> >> >> +}
> >> >> +
> >> >> +/* Analyze all the blocks in the function looking for useful version checks.
> >> >> +   Return true if we found one.  */
> >> >> +
> >> >> +bool
> >> >> +loop_versioning::analyze_blocks ()
> >> >> +{
> >> >> +  /* For now we don't try to version the whole function, although
> >> >> +     versioning at that level could be useful in some cases.  */
> >> >> +  get_loop_info (get_loop (m_fn, 0)).rejected_p = true;
> >> >> +
> >> >> +  basic_block bb;
> >> >> +  FOR_EACH_BB_FN (bb, m_fn)
> >> >> +    if (loop_outer (bb->loop_father))
> >> >> +      analyze_block (bb);
> >> >
> >> > .... I'd structure this as a
> >> >
> >> >   FOR_EACH_LOOP (... LI_FROM_INNERMOST)
> >> >     look at loop body, only analyze stmts belonging to loop (not subloops)
> >> >
> >> > walk eventually even open-coding this as recursion so you can quickly
> >> > finish off outer loop processing once an inner loop got disabled.
> >>
> >> OK.  Thought about doing that, but it seemed relatively hard to get the
> >> list of blocks that are in a loop but not in subloops.  (Let me know if
> >> there's an easy way.)
> >
> > Simply skip bbs with bb->loop_father != loop.
> >
> >>  But I guess it's probably worth an extra ad-hoc
> >> walk over the blocks (not the contents) to construct a list of blocks
> >> for each loop.
> >
> > Well, constructing a list of blocks in an order that visits inner loop
> > blocks first (recursively) would be useful here and in other places
> > I guess.  That can be done via get_loop_body (outermost-interesting-loop);
> > and then sorting the blocks after loop-father loop-dfs order or so.
> > But as said just skipping bb->loop_father != loop would work for me
> > (at the expense of looking at bb->loop_father N^2 times).
>
> Yeah, but I wanted to avoid even that quadraticness.  The updated patch
> uses a linear walk to create a linked list of blocks for each loop.
>
> Changes from last time (to address comments that I might have snipped):
>
> - We now decompose the address fragments into a sum of the form:
>
>     [base + ]var1 * const1 + var2 * const2 + ... + [CONST_MIN, CONST_MAX]
>
>   then analyse each var<i> * const<i> for versioning opportunities.
>   First we check whether var<i> is set by a multiplication by a
>   loop invariant, and only if that fails fall back to SCEV analysis.
>   There's no longer any processing of the CHREC_LEFT; only the
>   CHREC_RIGHT matters.  The processsing of SCEVs is generally
>   much simpler than before.
>
> - We now collect grouping information for accesses in a loop by
>   pooling any of the decomposed addresses fragments described above
>   that differ only in [CONST_MIN, CONST_MAX].  So for example:
>
>     a[i * stride] + ...;
>     a[i * stride + 1] + ...;
>
>   is treated as a single access of sizeof(a[0]) * 2 and:
>
>     a[i * stride] + ...;
>     a[i * stride + 3] + ...;
>
>   is treated as a single access of sizeof(a[0]) * 4 (ignoring the gap
>   in the middle).
>
> - We only version loops for conditions that would lead to consecutive groups,
>   rather than cases that would leave gaps between groups.
>
> - We process loops innermost-first, and only analyse address fragments
>   if we get to the end of the loop without finding something that stops
>   us from versioning.
>
> - The handling of pointers and array indices is consistent.
>
> - This version drops some of the heuristics related to enabling more
>   loop interchange opportunities.  I've kept the tests but changed
>   the expected result.
>
> - I think the only heuristic-heavy function left is get_inner_likelihood,
>   which is very difficult to avoid.
>
> Tested on x86_64-linux-gnu, aarch64-linux-gnu and aarch64_be-elf.
> Also repeated the performance testing (but haven't yet tried an
> LTO variant; will do that over the weekend).

Any results?  I've skimmed over the updated patch and it looks
a lot better now.

+bool
+loop_versioning
+::find_per_loop_multiplication (address_info &address, address_term_info &term)
+{

is that what coding convention allows?  For grepping I'd then say we should do

bool loop_versioning::
find_per_loop_multiplication (...)

;)  Anywhere else we you use

loop_versioning::foo

so please stick to that.

Otherwise OK.

I think I don't see a testcase where we could version both loops in a nest
so I'm not sure whether the transform works fine when you are only
updating SSA form at the very end of the pass.  There may also be some
subtle issues with substitute_and_fold being applied to non-up-to-date SSA
form given it folds stmts looking at (single-use!) SSA edges.  The single-use
guard might be what saves you here (SSA uses in the copies are not yet
updated to point to the copied DEFs).

Thanks,
Richard.

> Thanks,
> Richard
>
>
> 2018-12-06  Richard Sandiford  <richard.sandiford@arm.com>
>             Ramana Radhakrishnan  <ramana.radhakrishnan@arm.com>
>             Kyrylo Tkachov  <kyrylo.tkachov@arm.com>
>
> gcc/
>         * doc/invoke.texi (-fversion-loops-for-strides): Document
>         (loop-versioning-group-size, loop-versioning-max-inner-insns)
>         (loop-versioning-max-outer-insns): Document new --params.
>         * Makefile.in (OBJS): Add gimple-loop-versioning.o.
>         * common.opt (fversion-loops-for-strides): New option.
>         * opts.c (default_options_table): Enable fversion-loops-for-strides
>         at -O3.
>         * params.def (PARAM_LOOP_VERSIONING_GROUP_SIZE)
>         (PARAM_LOOP_VERSIONING_MAX_INNER_INSNS)
>         (PARAM_LOOP_VERSIONING_MAX_OUTER_INSNS): New parameters.
>         * passes.def: Add pass_loop_versioning.
>         * timevar.def (TV_LOOP_VERSIONING): New time variable.
>         * tree-ssa-propagate.h
>         (substitute_and_fold_engine::substitute_and_fold): Add an optional
>         block parameter.
>         * tree-ssa-propagate.c
>         (substitute_and_fold_engine::substitute_and_fold): Likewise.
>         When passed, only walk blocks dominated by that block.
>         * tree-vrp.h (range_includes_p): Declare.
>         (range_includes_zero_p): Turn into an inline wrapper around
>         range_includes_p.
>         * tree-vrp.c (range_includes_p): New function, generalizing...
>         (range_includes_zero_p): ...this.
>         * tree-pass.h (make_pass_loop_versioning): Declare.
>         * gimple-loop-versioning.cc: New file.
>
> gcc/testsuite/
>         * gcc.dg/loop-versioning-1.c: New test.
>         * gcc.dg/loop-versioning-10.c: Likewise.
>         * gcc.dg/loop-versioning-11.c: Likewise.
>         * gcc.dg/loop-versioning-2.c: Likewise.
>         * gcc.dg/loop-versioning-3.c: Likewise.
>         * gcc.dg/loop-versioning-4.c: Likewise.
>         * gcc.dg/loop-versioning-5.c: Likewise.
>         * gcc.dg/loop-versioning-6.c: Likewise.
>         * gcc.dg/loop-versioning-7.c: Likewise.
>         * gcc.dg/loop-versioning-8.c: Likewise.
>         * gcc.dg/loop-versioning-9.c: Likewise.
>         * gfortran.dg/loop_versioning_1.f90: Likewise.
>         * gfortran.dg/loop_versioning_2.f90: Likewise.
>         * gfortran.dg/loop_versioning_3.f90: Likewise.
>         * gfortran.dg/loop_versioning_4.f90: Likewise.
>         * gfortran.dg/loop_versioning_5.f90: Likewise.
>         * gfortran.dg/loop_versioning_6.f90: Likewise.
>         * gfortran.dg/loop_versioning_7.f90: Likewise.
>         * gfortran.dg/loop_versioning_8.f90: Likewise.
>
> Index: gcc/doc/invoke.texi
> ===================================================================
> --- gcc/doc/invoke.texi 2018-12-05 08:33:45.282882618 +0000
> +++ gcc/doc/invoke.texi 2018-12-06 12:33:59.212098431 +0000
> @@ -8256,7 +8256,8 @@ by @option{-O2} and also turns on the fo
>  -ftree-partial-pre @gol
>  -ftree-slp-vectorize @gol
>  -funswitch-loops @gol
> --fvect-cost-model}
> +-fvect-cost-model @gol
> +-fversion-loops-for-strides}
>
>  @item -O0
>  @opindex O0
> @@ -10808,6 +10809,30 @@ of the loop on both branches (modified a
>
>  Enabled by @option{-fprofile-use} and @option{-fauto-profile}.
>
> +@item -fversion-loops-for-strides
> +@opindex fversion-loops-for-strides
> +If a loop iterates over an array with a variable stride, create another
> +version of the loop that assumes the stride is always one.  For example:
> +
> +@smallexample
> +for (int i = 0; i < n; ++i)
> +  x[i * stride] = @dots{};
> +@end smallexample
> +
> +becomes:
> +
> +@smallexample
> +if (stride == 1)
> +  for (int i = 0; i < n; ++i)
> +    x[i] = @dots{};
> +else
> +  for (int i = 0; i < n; ++i)
> +    x[i * stride] = @dots{};
> +@end smallexample
> +
> +This is particularly useful for assumed-shape arrays in Fortran where
> +(for example) it allows better vectorization assuming contiguous accesses.
> +
>  @item -ffunction-sections
>  @itemx -fdata-sections
>  @opindex ffunction-sections
> @@ -12017,6 +12042,15 @@ Hardware autoprefetcher scheduler model
>  Number of lookahead cycles the model looks into; at '
>  ' only enable instruction sorting heuristic.
>
> +@item loop-versioning-max-inner-insns
> +The maximum number of instructions that an inner loop can have
> +before the loop versioning pass considers it too big to copy.
> +
> +@item loop-versioning-max-outer-insns
> +The maximum number of instructions that an outer loop can have
> +before the loop versioning pass considers it too big to copy,
> +discounting any instructions in inner loops that directly benefit
> +from versioning.
>
>  @end table
>  @end table
> Index: gcc/Makefile.in
> ===================================================================
> --- gcc/Makefile.in     2018-12-05 08:33:45.270882723 +0000
> +++ gcc/Makefile.in     2018-12-06 12:33:59.208098467 +0000
> @@ -1320,6 +1320,7 @@ OBJS = \
>         gimple-laddress.o \
>         gimple-loop-interchange.o \
>         gimple-loop-jam.o \
> +       gimple-loop-versioning.o \
>         gimple-low.o \
>         gimple-pretty-print.o \
>         gimple-ssa-backprop.o \
> Index: gcc/common.opt
> ===================================================================
> --- gcc/common.opt      2018-12-05 08:33:45.274882688 +0000
> +++ gcc/common.opt      2018-12-06 12:33:59.208098467 +0000
> @@ -2775,6 +2775,10 @@ fsplit-loops
>  Common Report Var(flag_split_loops) Optimization
>  Perform loop splitting.
>
> +fversion-loops-for-strides
> +Common Report Var(flag_version_loops_for_strides) Optimization
> +Version loops based on whether indices have a stride of one.
> +
>  funwind-tables
>  Common Report Var(flag_unwind_tables) Optimization
>  Just generate unwind tables for exception handling.
> Index: gcc/opts.c
> ===================================================================
> --- gcc/opts.c  2018-12-05 08:33:45.282882618 +0000
> +++ gcc/opts.c  2018-12-06 12:33:59.212098431 +0000
> @@ -556,6 +556,7 @@ static const struct default_options defa
>      { OPT_LEVELS_3_PLUS, OPT_ftree_slp_vectorize, NULL, 1 },
>      { OPT_LEVELS_3_PLUS, OPT_funswitch_loops, NULL, 1 },
>      { OPT_LEVELS_3_PLUS, OPT_fvect_cost_model_, NULL, VECT_COST_MODEL_DYNAMIC },
> +    { OPT_LEVELS_3_PLUS, OPT_fversion_loops_for_strides, NULL, 1 },
>
>      /* -Ofast adds optimizations to -O3.  */
>      { OPT_LEVELS_FAST, OPT_ffast_math, NULL, 1 },
> Index: gcc/params.def
> ===================================================================
> --- gcc/params.def      2018-12-05 08:33:45.282882618 +0000
> +++ gcc/params.def      2018-12-06 12:33:59.212098431 +0000
> @@ -1365,6 +1365,19 @@ DEFPARAM(PARAM_LOGICAL_OP_NON_SHORT_CIRC
>          "True if a non-short-circuit operation is optimal.",
>          -1, -1, 1)
>
> +DEFPARAM(PARAM_LOOP_VERSIONING_MAX_INNER_INSNS,
> +        "loop-versioning-max-inner-insns",
> +        "The maximum number of instructions in an inner loop that is being"
> +        " considered for versioning.",
> +        200, 0, 0)
> +
> +DEFPARAM(PARAM_LOOP_VERSIONING_MAX_OUTER_INSNS,
> +        "loop-versioning-max-outer-insns",
> +        "The maximum number of instructions in an outer loop that is being"
> +        " considered for versioning, on top of the instructions in inner"
> +        " loops.",
> +        100, 0, 0)
> +
>  /*
>
>  Local variables:
> Index: gcc/passes.def
> ===================================================================
> --- gcc/passes.def      2018-12-05 08:33:45.282882618 +0000
> +++ gcc/passes.def      2018-12-06 12:33:59.212098431 +0000
> @@ -265,6 +265,7 @@ along with GCC; see the file COPYING3.
>           NEXT_PASS (pass_tree_unswitch);
>           NEXT_PASS (pass_scev_cprop);
>           NEXT_PASS (pass_loop_split);
> +         NEXT_PASS (pass_loop_versioning);
>           NEXT_PASS (pass_loop_jam);
>           /* All unswitching, final value replacement and splitting can expose
>              empty loops.  Remove them now.  */
> Index: gcc/timevar.def
> ===================================================================
> --- gcc/timevar.def     2018-12-05 08:33:45.286882584 +0000
> +++ gcc/timevar.def     2018-12-06 12:33:59.220098363 +0000
> @@ -234,6 +234,7 @@ DEFTIMEVAR (TV_DSE1                  , "
>  DEFTIMEVAR (TV_DSE2                  , "dead store elim2")
>  DEFTIMEVAR (TV_LOOP                  , "loop analysis")
>  DEFTIMEVAR (TV_LOOP_INIT            , "loop init")
> +DEFTIMEVAR (TV_LOOP_VERSIONING      , "loop versioning")
>  DEFTIMEVAR (TV_LOOP_MOVE_INVARIANTS  , "loop invariant motion")
>  DEFTIMEVAR (TV_LOOP_UNROLL           , "loop unrolling")
>  DEFTIMEVAR (TV_LOOP_DOLOOP           , "loop doloop")
> Index: gcc/tree-ssa-propagate.h
> ===================================================================
> --- gcc/tree-ssa-propagate.h    2018-12-05 08:33:45.286882584 +0000
> +++ gcc/tree-ssa-propagate.h    2018-12-06 12:33:59.220098363 +0000
> @@ -104,7 +104,7 @@ extern void propagate_tree_value_into_st
>    virtual bool fold_stmt (gimple_stmt_iterator *) { return false; }
>    virtual tree get_value (tree) { return NULL_TREE; }
>
> -  bool substitute_and_fold (void);
> +  bool substitute_and_fold (basic_block = NULL);
>    bool replace_uses_in (gimple *);
>    bool replace_phi_args_in (gphi *);
>  };
> Index: gcc/tree-ssa-propagate.c
> ===================================================================
> --- gcc/tree-ssa-propagate.c    2018-12-05 08:33:45.286882584 +0000
> +++ gcc/tree-ssa-propagate.c    2018-12-06 12:33:59.220098363 +0000
> @@ -1154,6 +1154,10 @@ substitute_and_fold_dom_walker::before_d
>
>
>  /* Perform final substitution and folding of propagated values.
> +   Process the whole function if BLOCK is null, otherwise only
> +   process the blocks that BLOCK dominates.  In the latter case,
> +   it is the caller's responsibility to ensure that dominator
> +   information is available and up-to-date.
>
>     PROP_VALUE[I] contains the single value that should be substituted
>     at every use of SSA name N_I.  If PROP_VALUE is NULL, no values are
> @@ -1170,16 +1174,24 @@ substitute_and_fold_dom_walker::before_d
>     Return TRUE when something changed.  */
>
>  bool
> -substitute_and_fold_engine::substitute_and_fold (void)
> +substitute_and_fold_engine::substitute_and_fold (basic_block block)
>  {
>    if (dump_file && (dump_flags & TDF_DETAILS))
>      fprintf (dump_file, "\nSubstituting values and folding statements\n\n");
>
>    memset (&prop_stats, 0, sizeof (prop_stats));
>
> -  calculate_dominance_info (CDI_DOMINATORS);
> +  /* Don't call calculate_dominance_info when iterating over a subgraph.
> +     Callers that are using the interface this way are likely to want to
> +     iterate over several disjoint subgraphs, and it would be expensive
> +     in enable-checking builds to revalidate the whole dominance tree
> +     each time.  */
> +  if (block)
> +    gcc_assert (dom_info_state (CDI_DOMINATORS));
> +  else
> +    calculate_dominance_info (CDI_DOMINATORS);
>    substitute_and_fold_dom_walker walker (CDI_DOMINATORS, this);
> -  walker.walk (ENTRY_BLOCK_PTR_FOR_FN (cfun));
> +  walker.walk (block ? block : ENTRY_BLOCK_PTR_FOR_FN (cfun));
>
>    /* We cannot remove stmts during the BB walk, especially not release
>       SSA names there as that destroys the lattice of our callers.
> Index: gcc/tree-vrp.h
> ===================================================================
> --- gcc/tree-vrp.h      2018-12-05 08:33:45.290882549 +0000
> +++ gcc/tree-vrp.h      2018-12-06 12:33:59.220098363 +0000
> @@ -243,7 +243,7 @@ struct assert_info
>  extern void register_edge_assert_for (tree, edge, enum tree_code,
>                                       tree, tree, vec<assert_info> &);
>  extern bool stmt_interesting_for_vrp (gimple *);
> -extern bool range_includes_zero_p (const value_range_base *);
> +extern bool range_includes_p (const value_range_base *, HOST_WIDE_INT);
>  extern bool infer_value_range (gimple *, tree, tree_code *, tree *);
>
>  extern bool vrp_bitmap_equal_p (const_bitmap, const_bitmap);
> @@ -285,4 +285,12 @@ extern tree get_single_symbol (tree, boo
>  extern void maybe_set_nonzero_bits (edge, tree);
>  extern value_range_kind determine_value_range (tree, wide_int *, wide_int *);
>
> +/* Return TRUE if *VR includes the value zero.  */
> +
> +inline bool
> +range_includes_zero_p (const value_range_base *vr)
> +{
> +  return range_includes_p (vr, 0);
> +}
> +
>  #endif /* GCC_TREE_VRP_H */
> Index: gcc/tree-vrp.c
> ===================================================================
> --- gcc/tree-vrp.c      2018-12-05 08:33:45.290882549 +0000
> +++ gcc/tree-vrp.c      2018-12-06 12:33:59.220098363 +0000
> @@ -1173,15 +1173,14 @@ value_inside_range (tree val, tree min,
>  }
>
>
> -/* Return TRUE if *VR includes the value zero.  */
> +/* Return TRUE if *VR includes the value X.  */
>
>  bool
> -range_includes_zero_p (const value_range_base *vr)
> +range_includes_p (const value_range_base *vr, HOST_WIDE_INT x)
>  {
>    if (vr->varying_p () || vr->undefined_p ())
>      return true;
> -  tree zero = build_int_cst (vr->type (), 0);
> -  return vr->may_contain_p (zero);
> +  return vr->may_contain_p (build_int_cst (vr->type (), x));
>  }
>
>  /* If *VR has a value range that is a single constant value return that,
> Index: gcc/tree-pass.h
> ===================================================================
> --- gcc/tree-pass.h     2018-12-05 08:33:45.286882584 +0000
> +++ gcc/tree-pass.h     2018-12-06 12:33:59.220098363 +0000
> @@ -362,6 +362,7 @@ extern gimple_opt_pass *make_pass_fix_lo
>  extern gimple_opt_pass *make_pass_tree_loop (gcc::context *ctxt);
>  extern gimple_opt_pass *make_pass_tree_no_loop (gcc::context *ctxt);
>  extern gimple_opt_pass *make_pass_tree_loop_init (gcc::context *ctxt);
> +extern gimple_opt_pass *make_pass_loop_versioning (gcc::context *ctxt);
>  extern gimple_opt_pass *make_pass_lim (gcc::context *ctxt);
>  extern gimple_opt_pass *make_pass_linterchange (gcc::context *ctxt);
>  extern gimple_opt_pass *make_pass_tree_unswitch (gcc::context *ctxt);
> Index: gcc/gimple-loop-versioning.cc
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/gimple-loop-versioning.cc       2018-12-06 12:33:59.212098431 +0000
> @@ -0,0 +1,1743 @@
> +/* Loop versioning pass.
> +   Copyright (C) 2018 Free Software Foundation, Inc.
> +
> +This file is part of GCC.
> +
> +GCC is free software; you can redistribute it and/or modify it
> +under the terms of the GNU General Public License as published by the
> +Free Software Foundation; either version 3, or (at your option) any
> +later version.
> +
> +GCC is distributed in the hope that it will be useful, but WITHOUT
> +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
> +FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
> +for more details.
> +
> +You should have received a copy of the GNU General Public License
> +along with GCC; see the file COPYING3.  If not see
> +<http://www.gnu.org/licenses/>.  */
> +
> +#include "config.h"
> +#include "system.h"
> +#include "coretypes.h"
> +#include "backend.h"
> +#include "tree.h"
> +#include "gimple.h"
> +#include "gimple-iterator.h"
> +#include "tree-pass.h"
> +#include "gimplify-me.h"
> +#include "cfgloop.h"
> +#include "tree-ssa-loop.h"
> +#include "ssa.h"
> +#include "tree-scalar-evolution.h"
> +#include "tree-chrec.h"
> +#include "tree-ssa-loop-ivopts.h"
> +#include "fold-const.h"
> +#include "tree-ssa-propagate.h"
> +#include "tree-inline.h"
> +#include "domwalk.h"
> +#include "alloc-pool.h"
> +#include "vr-values.h"
> +#include "gimple-ssa-evrp-analyze.h"
> +#include "tree-vectorizer.h"
> +#include "omp-general.h"
> +#include "params.h"
> +
> +namespace {
> +
> +/* This pass looks for loops that could be simplified if certain loop
> +   invariant conditions were true.  It is effectively a form of loop
> +   splitting in which the pass produces the split conditions itself,
> +   instead of using ones that are already present in the IL.
> +
> +   Versioning for when strides are 1
> +   ---------------------------------
> +
> +   At the moment the only thing the pass looks for are memory references
> +   like:
> +
> +     for (auto i : ...)
> +       ...x[i * stride]...
> +
> +   It considers changing such loops to:
> +
> +     if (stride == 1)
> +       for (auto i : ...)    [A]
> +        ...x[i]...
> +     else
> +       for (auto i : ...)    [B]
> +        ...x[i * stride]...
> +
> +   This can have several benefits:
> +
> +   (1) [A] is often easier or cheaper to vectorize than [B].
> +
> +   (2) The scalar code in [A] is simpler than the scalar code in [B]
> +       (if the loops cannot be vectorized or need an epilogue loop).
> +
> +   (3) We might recognize [A] as a pattern, such as a memcpy or memset.
> +
> +   (4) [A] has simpler address evolutions, which can help other passes
> +       like loop interchange.
> +
> +   The optimization is particularly useful for assumed-shape arrays in
> +   Fortran, where the stride of the innermost dimension depends on the
> +   array descriptor but is often equal to 1 in practice.  For example:
> +
> +     subroutine f1(x)
> +       real :: x(:)
> +       x(:) = 100
> +     end subroutine f1
> +
> +   generates the equivalent of:
> +
> +     raw_stride = *x.dim[0].stride;
> +     stride = raw_stride != 0 ? raw_stride : 1;
> +     x_base = *x.data;
> +     ...
> +     tmp1 = stride * S;
> +     tmp2 = tmp1 - stride;
> +     *x_base[tmp2] = 1.0e+2;
> +
> +   but in the common case that stride == 1, the last three statements
> +   simplify to:
> +
> +     tmp3 = S + -1;
> +     *x_base[tmp3] = 1.0e+2;
> +
> +   The optimization is in principle very simple.  The difficult parts are:
> +
> +   (a) deciding which parts of a general address calculation correspond
> +       to the inner dimension of an array, since this usually isn't explicit
> +       in the IL, and for C often isn't even explicit in the source code
> +
> +   (b) estimating when the transformation is worthwhile
> +
> +   Structure
> +   ---------
> +
> +   The pass has four phases:
> +
> +   (1) Walk through the statements looking for and recording potential
> +       versioning opportunities.  Stop if there are none.
> +
> +   (2) Use context-sensitive range information to see whether any versioning
> +       conditions are impossible in practice.  Remove them if so, and stop
> +       if no opportunities remain.
> +
> +       (We do this only after (1) to keep compile time down when no
> +       versioning opportunities exist.)
> +
> +   (3) Apply the cost model.  Decide which versioning opportunities are
> +       worthwhile and at which nesting level they should be applied.
> +
> +   (4) Attempt to version all the loops selected by (3), so that:
> +
> +        for (...)
> +          ...
> +
> +       becomes:
> +
> +        if (!cond)
> +          for (...) // Original loop
> +            ...
> +        else
> +          for (...) // New loop
> +            ...
> +
> +       Use the version condition COND to simplify the new loop.  */
> +
> +/* Enumerates the likelihood that a particular value indexes the inner
> +   dimension of an array.  */
> +enum inner_likelihood {
> +  INNER_UNLIKELY,
> +  INNER_DONT_KNOW,
> +  INNER_LIKELY
> +};
> +
> +/* Information about one term of an address_info.  */
> +struct address_term_info
> +{
> +  /* The value of the term is EXPR * MULTIPLIER.  */
> +  tree expr;
> +  unsigned HOST_WIDE_INT multiplier;
> +
> +  /* The stride applied by EXPR in each iteration of some unrecorded loop,
> +     or null if no stride has been identified.  */
> +  tree stride;
> +
> +  /* Enumerates the likelihood that EXPR indexes the inner dimension
> +     of an array.  */
> +  enum inner_likelihood inner_likelihood;
> +
> +  /* True if STRIDE == 1 is a versioning opportunity when considered
> +     in isolation.  */
> +  bool versioning_opportunity_p;
> +};
> +
> +/* Information about an address calculation, and the range of constant
> +   offsets applied to it.  */
> +struct address_info
> +{
> +  static const unsigned int MAX_TERMS = 8;
> +
> +  /* One statement that calculates the address.  If multiple statements
> +     share the same address, we only record the first.  */
> +  gimple *stmt;
> +
> +  /* The loop containing STMT (cached for convenience).  If multiple
> +     statements share the same address, they all belong to this loop.  */
> +  struct loop *loop;
> +
> +  /* A decomposition of the calculation into a sum of terms plus an
> +     optional base.  When BASE is provided, it is never an SSA name.
> +     Once initialization is complete, all members of TERMs are SSA names.  */
> +  tree base;
> +  auto_vec<address_term_info, MAX_TERMS> terms;
> +
> +  /* All bytes accessed from the address fall in the offset range
> +     [MIN_OFFSET, MAX_OFFSET).  */
> +  HOST_WIDE_INT min_offset, max_offset;
> +};
> +
> +/* Stores addresses based on their base and terms (ignoring the offsets).  */
> +struct address_info_hasher : nofree_ptr_hash <address_info>
> +{
> +  static hashval_t hash (const address_info *);
> +  static bool equal (const address_info *, const address_info *);
> +};
> +
> +/* Information about the versioning we'd like to apply to a loop.  */
> +struct loop_info
> +{
> +  bool worth_versioning_p () const;
> +
> +  /* True if we've decided not to version this loop.  The remaining
> +     fields are meaningless if so.  */
> +  bool rejected_p;
> +
> +  /* True if at least one subloop of this loop benefits from versioning.  */
> +  bool subloops_benefit_p;
> +
> +  /* An estimate of the total number of instructions in the loop,
> +     excluding those in subloops that benefit from versioning.  */
> +  unsigned int num_insns;
> +
> +  /* The outermost loop that can handle all the version checks
> +     described below.  */
> +  struct loop *outermost;
> +
> +  /* The first entry in the list of blocks that belong to this loop
> +     (and not to subloops).  m_next_block_in_loop provides the chain
> +     pointers for the list.  */
> +  basic_block block_list;
> +
> +  /* We'd like to version the loop for the case in which these SSA names
> +     (keyed off their SSA_NAME_VERSION) are all equal to 1 at runtime.  */
> +  bitmap_head unity_names;
> +};
> +
> +/* The main pass structure.  */
> +class loop_versioning
> +{
> +public:
> +  loop_versioning (function *);
> +  ~loop_versioning ();
> +  unsigned int run ();
> +
> +private:
> +  /* Used to walk the dominator tree to find loop versioning conditions
> +     that are always false.  */
> +  class lv_dom_walker : public dom_walker
> +  {
> +  public:
> +    lv_dom_walker (loop_versioning &);
> +
> +    edge before_dom_children (basic_block) FINAL OVERRIDE;
> +    void after_dom_children (basic_block) FINAL OVERRIDE;
> +
> +  private:
> +    /* The parent pass.  */
> +    loop_versioning &m_lv;
> +
> +    /* Used to build context-dependent range information.  */
> +    evrp_range_analyzer m_range_analyzer;
> +  };
> +
> +  /* Used to simplify statements based on conditions that are established
> +     by the version checks.  */
> +  class name_prop : public substitute_and_fold_engine
> +  {
> +  public:
> +    name_prop (loop_info &li) : m_li (li) {}
> +    tree get_value (tree) FINAL OVERRIDE;
> +
> +  private:
> +    /* Information about the versioning we've performed on the loop.  */
> +    loop_info &m_li;
> +  };
> +
> +  loop_info &get_loop_info (struct loop *loop) { return m_loops[loop->num]; }
> +
> +  unsigned int max_insns_for_loop (struct loop *);
> +  bool expensive_stmt_p (gimple *);
> +
> +  void version_for_unity (gimple *, tree);
> +  bool acceptable_multiplier_p (tree, unsigned HOST_WIDE_INT,
> +                               unsigned HOST_WIDE_INT * = 0);
> +  bool acceptable_type_p (tree, unsigned HOST_WIDE_INT *);
> +  bool multiply_term_by (address_term_info &, tree);
> +  inner_likelihood get_inner_likelihood (tree, unsigned HOST_WIDE_INT);
> +  void analyze_stride (address_info &, address_term_info &,
> +                      tree, struct loop *);
> +  bool find_per_loop_multiplication (address_info &, address_term_info &);
> +  void analyze_term_using_scevs (address_info &, address_term_info &);
> +  void analyze_address_fragment (address_info &);
> +  void record_address_fragment (gimple *, unsigned HOST_WIDE_INT,
> +                               tree, unsigned HOST_WIDE_INT, HOST_WIDE_INT);
> +  void analyze_expr (gimple *, tree);
> +  bool analyze_block (basic_block);
> +  bool analyze_blocks ();
> +
> +  void prune_loop_conditions (struct loop *, vr_values *);
> +  bool prune_conditions ();
> +
> +  void merge_loop_info (struct loop *, struct loop *);
> +  void add_loop_to_queue (struct loop *);
> +  bool decide_whether_loop_is_versionable (struct loop *);
> +  bool make_versioning_decisions ();
> +
> +  bool version_loop (struct loop *);
> +  bool implement_versioning_decisions ();
> +
> +  /* The function we're optimizing.  */
> +  function *m_fn;
> +
> +  /* The obstack to use for all pass-specific bitmaps.  */
> +  bitmap_obstack m_bitmap_obstack;
> +
> +  /* An obstack to use for general allocation.  */
> +  obstack m_obstack;
> +
> +  /* The number of loops in the function.  */
> +  unsigned int m_nloops;
> +
> +  /* The total number of loop version conditions we've found.  */
> +  unsigned int m_num_conditions;
> +
> +  /* Assume that an address fragment of the form i * stride * scale
> +     (for variable stride and constant scale) will not benefit from
> +     versioning for stride == 1 when scale is greater than this value.  */
> +  unsigned HOST_WIDE_INT m_maximum_scale;
> +
> +  /* Information about each loop.  */
> +  auto_vec<loop_info> m_loops;
> +
> +  /* Used to form a linked list of blocks that belong to a loop,
> +     started by loop_info::block_list.  */
> +  auto_vec<basic_block> m_next_block_in_loop;
> +
> +  /* The list of loops that we've decided to version.  */
> +  auto_vec<struct loop *> m_loops_to_version;
> +
> +  /* A table of addresses in the current loop, keyed off their values
> +     but not their offsets.  */
> +  hash_table <address_info_hasher> m_address_table;
> +
> +  /* A list of all addresses in M_ADDRESS_TABLE, in a predictable order.  */
> +  auto_vec <address_info *, 32> m_address_list;
> +};
> +
> +/* If EXPR is an SSA name and not a default definition, return the
> +   defining statement, otherwise return null.  */
> +
> +static gimple *
> +maybe_get_stmt (tree expr)
> +{
> +  if (TREE_CODE (expr) == SSA_NAME && !SSA_NAME_IS_DEFAULT_DEF (expr))
> +    return SSA_NAME_DEF_STMT (expr);
> +  return NULL;
> +}
> +
> +/* Like maybe_get_stmt, but also return null if the defining
> +   statement isn't an assignment.  */
> +
> +static gassign *
> +maybe_get_assign (tree expr)
> +{
> +  return safe_dyn_cast <gassign *> (maybe_get_stmt (expr));
> +}
> +
> +/* Return true if this pass should look through a cast of expression FROM
> +   to type TYPE when analyzing pieces of an address.  */
> +
> +static bool
> +look_through_cast_p (tree type, tree from)
> +{
> +  return (INTEGRAL_TYPE_P (TREE_TYPE (from)) == INTEGRAL_TYPE_P (type)
> +         && POINTER_TYPE_P (TREE_TYPE (from)) == POINTER_TYPE_P (type));
> +}
> +
> +/* Strip all conversions of integers or pointers from EXPR, regardless
> +   of whether the conversions are nops.  This is useful in the context
> +   of this pass because we're not trying to fold or simulate the
> +   expression; we just want to see how it's structured.  */
> +
> +static tree
> +strip_casts (tree expr)
> +{
> +  const unsigned int MAX_NITERS = 4;
> +
> +  tree type = TREE_TYPE (expr);
> +  while (CONVERT_EXPR_P (expr)
> +        && look_through_cast_p (type, TREE_OPERAND (expr, 0)))
> +    expr = TREE_OPERAND (expr, 0);
> +
> +  for (unsigned int niters = 0; niters < MAX_NITERS; ++niters)
> +    {
> +      gassign *assign = maybe_get_assign (expr);
> +      if (assign
> +         && CONVERT_EXPR_CODE_P (gimple_assign_rhs_code (assign))
> +         && look_through_cast_p (type, gimple_assign_rhs1 (assign)))
> +       expr = gimple_assign_rhs1 (assign);
> +      else
> +       break;
> +    }
> +  return expr;
> +}
> +
> +/* Compare two address_term_infos in the same address_info.  */
> +
> +static int
> +compare_address_terms (const void *a_uncast, const void *b_uncast)
> +{
> +  const address_term_info *a = (const address_term_info *) a_uncast;
> +  const address_term_info *b = (const address_term_info *) b_uncast;
> +
> +  if (a->expr != b->expr)
> +    return SSA_NAME_VERSION (a->expr) < SSA_NAME_VERSION (b->expr) ? -1 : 1;
> +
> +  if (a->multiplier != b->multiplier)
> +    return a->multiplier < b->multiplier ? -1 : 1;
> +
> +  return 0;
> +}
> +
> +/* Dump ADDRESS using flags FLAGS.  */
> +
> +static void
> +dump_address_info (dump_flags_t flags, address_info &address)
> +{
> +  if (address.base)
> +    dump_printf (flags, "%T + ", address.base);
> +  for (unsigned int i = 0; i < address.terms.length (); ++i)
> +    {
> +      if (i != 0)
> +       dump_printf (flags, " + ");
> +      dump_printf (flags, "%T", address.terms[i].expr);
> +      if (address.terms[i].multiplier != 1)
> +       dump_printf (flags, " * %wd", address.terms[i].multiplier);
> +    }
> +  dump_printf (flags, " + [%wd, %wd]",
> +              address.min_offset, address.max_offset - 1);
> +}
> +
> +/* Hash an address_info based on its base and terms.  */
> +
> +hashval_t
> +address_info_hasher::hash (const address_info *info)
> +{
> +  inchash::hash hash;
> +  hash.add_int (info->base ? TREE_CODE (info->base) : 0);
> +  hash.add_int (info->terms.length ());
> +  for (unsigned int i = 0; i < info->terms.length (); ++i)
> +    {
> +      hash.add_int (SSA_NAME_VERSION (info->terms[i].expr));
> +      hash.add_hwi (info->terms[i].multiplier);
> +    }
> +  return hash.end ();
> +}
> +
> +/* Return true if two address_infos have equal bases and terms.  Other
> +   properties might be different (such as the statement or constant
> +   offset range).  */
> +
> +bool
> +address_info_hasher::equal (const address_info *a, const address_info *b)
> +{
> +  if (a->base != b->base
> +      && (!a->base || !b->base || !operand_equal_p (a->base, b->base, 0)))
> +    return false;
> +
> +  if (a->terms.length () != b->terms.length ())
> +    return false;
> +
> +  for (unsigned int i = 0; i < a->terms.length (); ++i)
> +    if (a->terms[i].expr != b->terms[i].expr
> +       || a->terms[i].multiplier != b->terms[i].multiplier)
> +      return false;
> +
> +  return true;
> +}
> +
> +/* Return true if we want to version the loop, i.e. if we have a
> +   specific reason for doing so and no specific reason not to.  */
> +
> +bool
> +loop_info::worth_versioning_p () const
> +{
> +  return (!rejected_p
> +         && (!bitmap_empty_p (&unity_names) || subloops_benefit_p));
> +}
> +
> +loop_versioning::lv_dom_walker::lv_dom_walker (loop_versioning &lv)
> +  : dom_walker (CDI_DOMINATORS), m_lv (lv)
> +{
> +}
> +
> +/* Process BB before processing the blocks it dominates.  */
> +
> +edge
> +loop_versioning::lv_dom_walker::before_dom_children (basic_block bb)
> +{
> +  m_range_analyzer.enter (bb);
> +
> +  if (bb == bb->loop_father->header)
> +    m_lv.prune_loop_conditions (bb->loop_father,
> +                               m_range_analyzer.get_vr_values ());
> +
> +  for (gimple_stmt_iterator si = gsi_start_bb (bb); !gsi_end_p (si);
> +       gsi_next (&si))
> +    m_range_analyzer.record_ranges_from_stmt (gsi_stmt (si), false);
> +
> +  return NULL;
> +}
> +
> +/* Process BB after processing the blocks it dominates.  */
> +
> +void
> +loop_versioning::lv_dom_walker::after_dom_children (basic_block bb)
> +{
> +  m_range_analyzer.leave (bb);
> +}
> +
> +/* Decide whether to replace VAL with a new value in a versioned loop.
> +   Return the new value if so, otherwise return null.  */
> +
> +tree
> +loop_versioning::name_prop::get_value (tree val)
> +{
> +  if (TREE_CODE (val) == SSA_NAME
> +      && bitmap_bit_p (&m_li.unity_names, SSA_NAME_VERSION (val)))
> +    return build_one_cst (TREE_TYPE (val));
> +  return NULL_TREE;
> +}
> +
> +/* Initialize the structure to optimize FN.  */
> +
> +loop_versioning::loop_versioning (function *fn)
> +  : m_fn (fn),
> +    m_nloops (number_of_loops (fn)),
> +    m_num_conditions (0),
> +    m_address_table (31)
> +{
> +  bitmap_obstack_initialize (&m_bitmap_obstack);
> +  gcc_obstack_init (&m_obstack);
> +
> +  /* Initialize the loop information.  */
> +  m_loops.safe_grow_cleared (m_nloops);
> +  for (unsigned int i = 0; i < m_nloops; ++i)
> +    {
> +      m_loops[i].outermost = get_loop (m_fn, 0);
> +      bitmap_initialize (&m_loops[i].unity_names, &m_bitmap_obstack);
> +    }
> +
> +  /* Initialize the list of blocks that belong to each loop.  */
> +  unsigned int nbbs = last_basic_block_for_fn (fn);
> +  m_next_block_in_loop.safe_grow (nbbs);
> +  basic_block bb;
> +  FOR_EACH_BB_FN (bb, fn)
> +    {
> +      loop_info &li = get_loop_info (bb->loop_father);
> +      m_next_block_in_loop[bb->index] = li.block_list;
> +      li.block_list = bb;
> +    }
> +
> +  /* MAX_FIXED_MODE_SIZE should be a reasonable maximum scale for
> +     unvectorizable code, since it is the largest size that can be
> +     handled efficiently by scalar code.  omp_max_vf calculates the
> +     maximum number of bytes in a vector, when such a value is relevant
> +     to loop optimization.  */
> +  m_maximum_scale = estimated_poly_value (omp_max_vf ());
> +  m_maximum_scale = MAX (m_maximum_scale, MAX_FIXED_MODE_SIZE);
> +}
> +
> +loop_versioning::~loop_versioning ()
> +{
> +  bitmap_obstack_release (&m_bitmap_obstack);
> +  obstack_free (&m_obstack, NULL);
> +}
> +
> +/* Return the maximum number of instructions allowed in LOOP before
> +   it becomes too big for versioning.
> +
> +   There are separate limits for inner and outer loops.  The limit for
> +   inner loops applies only to loops that benefit directly from versioning.
> +   The limit for outer loops applies to all code in the outer loop and
> +   its subloops that *doesn't* benefit directly from versioning; such code
> +   would be "taken along for the ride".  The idea is that if the cost of
> +   the latter is small, it is better to version outer loops rather than
> +   inner loops, both to reduce the number of repeated checks and to enable
> +   more of the loop nest to be optimized as a natural nest (e.g. by loop
> +   interchange or outer-loop vectorization).  */
> +
> +unsigned int
> +loop_versioning::max_insns_for_loop (struct loop *loop)
> +{
> +  return (loop->inner
> +         ? PARAM_VALUE (PARAM_LOOP_VERSIONING_MAX_OUTER_INSNS)
> +         : PARAM_VALUE (PARAM_LOOP_VERSIONING_MAX_INNER_INSNS));
> +}
> +
> +/* Return true if for cost reasons we should avoid versioning any loop
> +   that contains STMT.
> +
> +   Note that we don't need to check whether versioning is invalid for
> +   correctness reasons, since the versioning process does that for us.
> +   The conditions involved are too rare to be worth duplicating here.  */
> +
> +bool
> +loop_versioning::expensive_stmt_p (gimple *stmt)
> +{
> +  if (gcall *call = dyn_cast <gcall *> (stmt))
> +    /* Assume for now that the time spent in an "expensive" call would
> +       overwhelm any saving from versioning.  */
> +    return !gimple_inexpensive_call_p (call);
> +  return false;
> +}
> +
> +/* Record that we want to version the loop that contains STMT for the
> +   case in which SSA name NAME is equal to 1.  We already know that NAME
> +   is invariant in the loop.  */
> +
> +void
> +loop_versioning::version_for_unity (gimple *stmt, tree name)
> +{
> +  struct loop *loop = loop_containing_stmt (stmt);
> +  loop_info &li = get_loop_info (loop);
> +
> +  if (bitmap_set_bit (&li.unity_names, SSA_NAME_VERSION (name)))
> +    {
> +      /* This is the first time we've wanted to version LOOP for NAME.
> +        Keep track of the outermost loop that can handle all versioning
> +        checks in LI.  */
> +      struct loop *outermost
> +       = outermost_invariant_loop_for_expr (loop, name);
> +      if (loop_depth (li.outermost) < loop_depth (outermost))
> +       li.outermost = outermost;
> +
> +      if (dump_enabled_p ())
> +       {
> +         dump_printf_loc (MSG_NOTE, stmt, "want to version containing loop"
> +                          " for when %T == 1", name);
> +         if (outermost == loop)
> +           dump_printf (MSG_NOTE, "; cannot hoist check further");
> +         else
> +           {
> +             dump_printf (MSG_NOTE, "; could implement the check at loop"
> +                          " depth %d", loop_depth (outermost));
> +             if (loop_depth (li.outermost) > loop_depth (outermost))
> +               dump_printf (MSG_NOTE, ", but other checks only allow"
> +                            " a depth of %d", loop_depth (li.outermost));
> +           }
> +         dump_printf (MSG_NOTE, "\n");
> +       }
> +
> +      m_num_conditions += 1;
> +    }
> +  else
> +    {
> +      /* This is a duplicate request.  */
> +      if (dump_enabled_p ())
> +       dump_printf_loc (MSG_NOTE, stmt, "already asked to version containing"
> +                        " loop for when %T == 1\n", name);
> +    }
> +}
> +
> +/* Return true if OP1_TREE is constant and if in principle it is worth
> +   versioning an address fragment of the form:
> +
> +     i * OP1_TREE * OP2 * stride
> +
> +   for the case in which stride == 1.  This in practice means testing
> +   whether:
> +
> +     OP1_TREE * OP2 <= M_MAXIMUM_SCALE.
> +
> +   If RESULT is nonnull, store OP1_TREE * OP2 there when returning true.  */
> +
> +bool
> +loop_versioning::acceptable_multiplier_p (tree op1_tree,
> +                                         unsigned HOST_WIDE_INT op2,
> +                                         unsigned HOST_WIDE_INT *result)
> +{
> +  if (tree_fits_uhwi_p (op1_tree))
> +    {
> +      unsigned HOST_WIDE_INT op1 = tree_to_uhwi (op1_tree);
> +      /* The first part checks for overflow.  */
> +      if (op1 * op2 >= op2 && op1 * op2 <= m_maximum_scale)
> +       {
> +         if (result)
> +           *result = op1 * op2;
> +         return true;
> +       }
> +    }
> +  return false;
> +}
> +
> +/* Return true if it is worth using loop versioning on a memory access
> +   of type TYPE.  Store the size of the access in *SIZE if so.  */
> +
> +bool
> +loop_versioning::acceptable_type_p (tree type, unsigned HOST_WIDE_INT *size)
> +{
> +  return (TYPE_SIZE_UNIT (type)
> +         && acceptable_multiplier_p (TYPE_SIZE_UNIT (type), 1, size));
> +}
> +
> +/* See whether OP is constant and whether we can multiply TERM by that
> +   constant without exceeding M_MAXIMUM_SCALE.  Return true and update
> +   TERM if so.  */
> +
> +bool
> +loop_versioning::multiply_term_by (address_term_info &term, tree op)
> +{
> +  return acceptable_multiplier_p (op, term.multiplier, &term.multiplier);
> +}
> +
> +/* Decide whether an address fragment of the form STRIDE * MULTIPLIER
> +   is likely to be indexing an innermost dimension, returning the result
> +   as an INNER_* probability.  */
> +
> +inner_likelihood
> +loop_versioning::get_inner_likelihood (tree stride,
> +                                      unsigned HOST_WIDE_INT multiplier)
> +{
> +  const unsigned int MAX_NITERS = 8;
> +
> +  /* Iterate over possible values of STRIDE.  Return INNER_LIKELY if at
> +     least one of those values is likely to be for the innermost dimension.
> +     Record in UNLIKELY_P if at least one of those values is unlikely to be
> +     for the innermost dimension.
> +
> +     E.g. for:
> +
> +       stride = cond ? a * b : 1
> +
> +     we should treat STRIDE as being a likely inner dimension, since
> +     we know that it is 1 under at least some circumstances.  (See the
> +     Fortran example below.)  However:
> +
> +       stride = a * b
> +
> +     on its own is unlikely to be for the innermost dimension, since
> +     that would require both a and b to be 1 at runtime.  */
> +  bool unlikely_p = false;
> +  tree worklist[MAX_NITERS];
> +  unsigned int length = 0;
> +  worklist[length++] = stride;
> +  for (unsigned int i = 0; i < length; ++i)
> +    {
> +      tree expr = worklist[i];
> +
> +      if (CONSTANT_CLASS_P (expr))
> +       {
> +         /* See if EXPR * MULTIPLIER would be consistent with an individual
> +            access or a small grouped access.  */
> +         if (acceptable_multiplier_p (expr, multiplier))
> +           return INNER_LIKELY;
> +         else
> +           unlikely_p = true;
> +       }
> +      else if (gimple *stmt = maybe_get_stmt (expr))
> +       {
> +         /* If EXPR is set by a PHI node, queue its arguments in case
> +            we find one that is consistent with an inner dimension.
> +
> +            An important instance of this is the Fortran handling of array
> +            descriptors, which calculates the stride of the inner dimension
> +            using a PHI equivalent of:
> +
> +               raw_stride = a.dim[0].stride;
> +               stride = raw_stride != 0 ? raw_stride : 1;
> +
> +            (Strides for outer dimensions do not treat 0 specially.)  */
> +         if (gphi *phi = dyn_cast <gphi *> (stmt))
> +           {
> +             unsigned int nargs = gimple_phi_num_args (phi);
> +             for (unsigned int j = 0; j < nargs && length < MAX_NITERS; ++j)
> +               worklist[length++] = strip_casts (gimple_phi_arg_def (phi, j));
> +           }
> +         /* If the value is set by an assignment, expect it to be read
> +            from memory (such as an array descriptor) rather than be
> +            calculated.  */
> +         else if (gassign *assign = dyn_cast <gassign *> (stmt))
> +           {
> +             if (!gimple_assign_load_p (assign))
> +               unlikely_p = true;
> +           }
> +         /* Things like calls don't really tell us anything.  */
> +       }
> +    }
> +
> +  /* We didn't find any possible values of STRIDE that were likely to be
> +     for the innermost dimension.  If we found one that was actively
> +     unlikely to be for the innermost dimension, assume that that applies
> +     to STRIDE too.  */
> +  return unlikely_p ? INNER_UNLIKELY : INNER_DONT_KNOW;
> +}
> +
> +/* The caller has identified that STRIDE is the stride of interest
> +   in TERM, and that the stride is applied in OP_LOOP.  Record this
> +   information in TERM, deciding whether STRIDE is likely to be for
> +   the innermost dimension of an array and whether it represents a
> +   versioning opportunity.  ADDRESS is the address that contains TERM.  */
> +
> +void
> +loop_versioning::analyze_stride (address_info &address,
> +                                address_term_info &term,
> +                                tree stride, struct loop *op_loop)
> +{
> +  term.stride = stride;
> +
> +  term.inner_likelihood = get_inner_likelihood (stride, term.multiplier);
> +  if (dump_enabled_p ())
> +    {
> +      if (term.inner_likelihood == INNER_LIKELY)
> +       dump_printf_loc (MSG_NOTE, address.stmt, "%T is likely to be the"
> +                        " innermost dimension\n", stride);
> +      else if (term.inner_likelihood == INNER_UNLIKELY)
> +       dump_printf_loc (MSG_NOTE, address.stmt, "%T is probably not the"
> +                        " innermost dimension\n", stride);
> +      else
> +       dump_printf_loc (MSG_NOTE, address.stmt, "cannot tell whether %T"
> +                        " is the innermost dimension\n", stride);
> +    }
> +
> +  /* To be a versioning opportunity we require:
> +
> +     - The multiplier applied by TERM is equal to the access size,
> +       so that when STRIDE is 1, the accesses in successive loop
> +       iterations are consecutive.
> +
> +       This is deliberately conservative.  We could relax it to handle
> +       other cases (such as those with gaps between iterations) if we
> +       find any real testcases for which it's useful.
> +
> +     - the stride is applied in the same loop as STMT rather than
> +       in an outer loop.  Although versioning for strides applied in
> +       outer loops could help in some cases -- such as enabling
> +       more loop interchange -- the savings are much lower than for
> +       inner loops.
> +
> +     - the stride is an SSA name that is invariant in STMT's loop,
> +       since otherwise versioning isn't possible.  */
> +  unsigned HOST_WIDE_INT access_size = address.max_offset - address.min_offset;
> +  if (term.multiplier == access_size
> +      && address.loop == op_loop
> +      && TREE_CODE (stride) == SSA_NAME
> +      && expr_invariant_in_loop_p (address.loop, stride))
> +    {
> +      term.versioning_opportunity_p = true;
> +      if (dump_enabled_p ())
> +       dump_printf_loc (MSG_NOTE, address.stmt, "%T == 1 is a versioning"
> +                        " opportunity\n", stride);
> +    }
> +}
> +
> +/* See whether address term TERM (which belongs to ADDRESS) is the result
> +   of multiplying a varying SSA name by a loop-invariant SSA name.
> +   Return true and update TERM if so.
> +
> +   This handles both cases that SCEV might handle, such as:
> +
> +     for (int i = 0; i < n; ++i)
> +       res += a[i * stride];
> +
> +   and ones in which the term varies arbitrarily between iterations, such as:
> +
> +     for (int i = 0; i < n; ++i)
> +       res += a[index[i] * stride];  */
> +
> +bool
> +loop_versioning
> +::find_per_loop_multiplication (address_info &address, address_term_info &term)
> +{
> +  gimple *mult = maybe_get_assign (term.expr);
> +  if (!mult || gimple_assign_rhs_code (mult) != MULT_EXPR)
> +    return false;
> +
> +  struct loop *mult_loop = loop_containing_stmt (mult);
> +  if (!loop_outer (mult_loop))
> +    return false;
> +
> +  tree op1 = strip_casts (gimple_assign_rhs1 (mult));
> +  tree op2 = strip_casts (gimple_assign_rhs2 (mult));
> +  if (TREE_CODE (op1) != SSA_NAME || TREE_CODE (op2) != SSA_NAME)
> +    return false;
> +
> +  bool invariant1_p = expr_invariant_in_loop_p (mult_loop, op1);
> +  bool invariant2_p = expr_invariant_in_loop_p (mult_loop, op2);
> +  if (invariant1_p == invariant2_p)
> +    return false;
> +
> +  /* Make sure that the loop invariant is OP2 rather than OP1.  */
> +  if (invariant1_p)
> +    std::swap (op1, op2);
> +
> +  if (dump_enabled_p ())
> +    dump_printf_loc (MSG_NOTE, address.stmt, "address term %T = varying %T"
> +                    " * loop-invariant %T\n", term.expr, op1, op2);
> +  analyze_stride (address, term, op2, mult_loop);
> +  return true;
> +}
> +
> +/* Try to use scalar evolutions to find an address stride for TERM,
> +   which belongs to ADDRESS.
> +
> +   Here we are interested in any evolution information we can find,
> +   not just evolutions wrt ADDRESS->LOOP.  For example, if we find that
> +   an outer loop obviously iterates over the inner dimension of an array,
> +   that information can help us eliminate worthless versioning opportunities
> +   in inner loops.  */
> +
> +void
> +loop_versioning::analyze_term_using_scevs (address_info &address,
> +                                          address_term_info &term)
> +{
> +  gimple *setter = maybe_get_stmt (term.expr);
> +  if (!setter)
> +    return;
> +
> +  struct loop *wrt_loop = loop_containing_stmt (setter);
> +  if (!loop_outer (wrt_loop))
> +    return;
> +
> +  tree chrec = strip_casts (analyze_scalar_evolution (wrt_loop, term.expr));
> +  if (TREE_CODE (chrec) == POLYNOMIAL_CHREC)
> +    {
> +      if (dump_enabled_p ())
> +       dump_printf_loc (MSG_NOTE, address.stmt,
> +                        "address term %T = %T\n", term.expr, chrec);
> +
> +      /* Peel casts and accumulate constant multiplications, up to the
> +        limit allowed by M_MAXIMUM_SCALE.  */
> +      tree stride = strip_casts (CHREC_RIGHT (chrec));
> +      while (TREE_CODE (stride) == MULT_EXPR
> +            && multiply_term_by (term, TREE_OPERAND (stride, 1)))
> +       stride = strip_casts (TREE_OPERAND (stride, 0));
> +
> +      gassign *assign;
> +      while ((assign = maybe_get_assign (stride))
> +            && gimple_assign_rhs_code (assign) == MULT_EXPR
> +            && multiply_term_by (term, gimple_assign_rhs2 (assign)))
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_NOTE, address.stmt,
> +                            "looking through %G", assign);
> +         stride = strip_casts (gimple_assign_rhs1 (assign));
> +       }
> +
> +      analyze_stride (address, term, stride, get_chrec_loop (chrec));
> +    }
> +}
> +
> +/* Try to identify loop strides in ADDRESS and try to choose realistic
> +   versioning opportunities based on these strides.
> +
> +   The main difficulty here isn't finding strides that could be used
> +   in a version check (that's pretty easy).  The problem instead is to
> +   avoid versioning for some stride S that is unlikely ever to be 1 at
> +   runtime.  Versioning for S == 1 on its own would lead to unnecessary
> +   code bloat, while adding S == 1 to more realistic version conditions
> +   would lose the optimisation opportunity offered by those other conditions.
> +
> +   For example, versioning for a stride of 1 in the Fortran code:
> +
> +     integer :: a(:,:)
> +     a(1,:) = 1
> +
> +   is not usually a good idea, since the assignment is iterating over
> +   an outer dimension and is relatively unlikely to have a stride of 1.
> +   (It isn't impossible, since the inner dimension might be 1, or the
> +   array might be transposed.)  Similarly, in:
> +
> +     integer :: a(:,:), b(:,:)
> +     b(:,1) = a(1,:)
> +
> +   b(:,1) is relatively likely to have a stride of 1 while a(1,:) isn't.
> +   Versioning for when both strides are 1 would lose most of the benefit
> +   of versioning for b's access.
> +
> +   The approach we take is as follows:
> +
> +   - Analyze each term to see whether it has an identifiable stride,
> +     regardless of which loop applies the stride.
> +
> +   - Evaluate the likelihood that each such stride is for the innermost
> +     dimension of an array, on the scale "likely", "don't know" or "unlikely".
> +
> +   - If there is a single "likely" innermost stride, and that stride is
> +     applied in the loop that contains STMT, version the loop for when the
> +     stride is 1.  This deals with the cases in which we're fairly
> +     confident of doing the right thing, such as the b(:,1) reference above.
> +
> +   - If there are no "likely" innermost strides, and the loop that contains
> +     STMT uses a stride that we rated as "don't know", version for when
> +     that stride is 1.  This is principally used for C code such as:
> +
> +       for (int i = 0; i < n; ++i)
> +        a[i * x] = ...;
> +
> +     and:
> +
> +       for (int j = 0; j < n; ++j)
> +        for (int i = 0; i < n; ++i)
> +          a[i * x + j * y] = ...;
> +
> +     where nothing in the way "x" and "y" are set gives a hint as to
> +     whether "i" iterates over the innermost dimension of the array.
> +     In these situations it seems reasonable to assume the the
> +     programmer has nested the loops appropriately (although of course
> +     there are examples like GEMM in which this assumption doesn't hold
> +     for all accesses in the loop).
> +
> +     This case is also useful for the Fortran equivalent of the
> +     above C code.  */
> +
> +void
> +loop_versioning::analyze_address_fragment (address_info &address)
> +{
> +  if (dump_enabled_p ())
> +    {
> +      dump_printf_loc (MSG_NOTE, address.stmt, "analyzing address fragment ");
> +      dump_address_info (MSG_NOTE, address);
> +      dump_printf (MSG_NOTE, "\n");
> +    }
> +
> +  /* Analyze each component of the sum to see whether it involves an
> +     apparent stride.
> +
> +     There is an overlap between the addresses that
> +     find_per_loop_multiplication and analyze_term_using_scevs can handle,
> +     but the former is much cheaper than SCEV analysis, so try it first.  */
> +  for (unsigned int i = 0; i < address.terms.length (); ++i)
> +    if (!find_per_loop_multiplication (address, address.terms[i]))
> +      analyze_term_using_scevs (address, address.terms[i]);
> +
> +  /* Check for strides that are likely to be for the innermost dimension.
> +
> +     1. If there is a single likely inner stride, if it is an SSA name,
> +       and if it is worth versioning the loop for when the SSA name
> +       equals 1, record that we want to do so.
> +
> +     2. Otherwise, if there any likely inner strides, bail out.  This means
> +       one of:
> +
> +       (a) There are multiple likely inner strides.  This suggests we're
> +           confused and be can't be confident of doing the right thing.
> +
> +       (b) There is a single likely inner stride and it is a constant
> +           rather than an SSA name.  This can mean either that the access
> +           is a natural one without any variable strides, such as:
> +
> +             for (int i = 0; i < n; ++i)
> +               a[i] += 1;
> +
> +           or that a variable stride is applied to an outer dimension,
> +           such as:
> +
> +             for (int i = 0; i < n; ++i)
> +               for (int j = 0; j < n; ++j)
> +                 a[j * stride][i] += 1;
> +
> +       (c) There is a single likely inner stride, and it is an SSA name,
> +           but it isn't a worthwhile versioning opportunity.  This usually
> +           means that the variable stride is applied by an outer loop,
> +           such as:
> +
> +             for (int i = 0; i < n; ++i)
> +               for (int j = 0; j < n; ++j)
> +                 a[j][i * stride] += 1;
> +
> +           or (using an example with a more natural loop nesting):
> +
> +             for (int i = 0; i < n; ++i)
> +               for (int j = 0; j < n; ++j)
> +                 a[i][j] += b[i * stride];
> +
> +           in cases where b[i * stride] cannot (yet) be hoisted for
> +           aliasing reasons.
> +
> +     3. If there are no likely inner strides, fall through to the next
> +       set of checks.
> +
> +     Pointer equality is enough to check for uniqueness in (1), since we
> +     only care about SSA names.  */
> +  tree chosen_stride = NULL_TREE;
> +  tree version_stride = NULL_TREE;
> +  for (unsigned int i = 0; i < address.terms.length (); ++i)
> +    if (chosen_stride != address.terms[i].stride
> +       && address.terms[i].inner_likelihood == INNER_LIKELY)
> +      {
> +       if (chosen_stride)
> +         return;
> +       chosen_stride = address.terms[i].stride;
> +       if (address.terms[i].versioning_opportunity_p)
> +         version_stride = chosen_stride;
> +      }
> +
> +  /* If there are no likely inner strides, see if there is a single
> +     versioning opportunity for a stride that was rated as INNER_DONT_KNOW.
> +     See the comment above the function for the cases that this code
> +     handles.  */
> +  if (!chosen_stride)
> +    for (unsigned int i = 0; i < address.terms.length (); ++i)
> +      if (version_stride != address.terms[i].stride
> +         && address.terms[i].inner_likelihood == INNER_DONT_KNOW
> +         && address.terms[i].versioning_opportunity_p)
> +       {
> +         if (version_stride)
> +           return;
> +         version_stride = address.terms[i].stride;
> +       }
> +
> +  if (version_stride)
> +    version_for_unity (address.stmt, version_stride);
> +}
> +
> +/* Treat EXPR * MULTIPLIER + OFFSET as a fragment of an address that addresses
> +   TYPE_SIZE bytes and record this address fragment for later processing.
> +   STMT is the statement that contains the address.  */
> +
> +void
> +loop_versioning::record_address_fragment (gimple *stmt,
> +                                         unsigned HOST_WIDE_INT type_size,
> +                                         tree expr,
> +                                         unsigned HOST_WIDE_INT multiplier,
> +                                         HOST_WIDE_INT offset)
> +{
> +  /* We're only interested in computed values.  */
> +  if (TREE_CODE (expr) != SSA_NAME)
> +    return;
> +
> +  /* Quick exit if no part of the address is calculated in STMT's loop,
> +     since such addresses have no versioning opportunities.  */
> +  struct loop *loop = loop_containing_stmt (stmt);
> +  if (expr_invariant_in_loop_p (loop, expr))
> +    return;
> +
> +  /* Set up an address_info for EXPR * MULTIPLIER.  */
> +  address_info *address = XOBNEW (&m_obstack, address_info);
> +  new (address) address_info;
> +  address->stmt = stmt;
> +  address->loop = loop;
> +  address->base = NULL_TREE;
> +  address->terms.quick_grow (1);
> +  address->terms[0].expr = expr;
> +  address->terms[0].multiplier = multiplier;
> +  address->terms[0].stride = NULL_TREE;
> +  address->terms[0].inner_likelihood = INNER_UNLIKELY;
> +  address->terms[0].versioning_opportunity_p = false;
> +  address->min_offset = offset;
> +
> +  /* Peel apart the expression into a sum of address_terms, where each
> +     term is multiplied by a constant.  Treat a + b and a - b the same,
> +     since it doesn't matter for our purposes whether an address is
> +     increasing or decreasing.  Distribute (a + b) * constant into
> +     a * constant + b * constant.
> +
> +     We don't care which loop each term belongs to, since we want to
> +     examine as many candidate strides as possible when determining
> +     which is likely to be for the innermost dimension.  We therefore
> +     don't limit the search to statements in STMT's loop.  */
> +  for (unsigned int i = 0; i < address->terms.length (); )
> +    {
> +      if (gassign *assign = maybe_get_assign (address->terms[i].expr))
> +       {
> +         tree_code code = gimple_assign_rhs_code (assign);
> +         if (code == PLUS_EXPR
> +             || code == POINTER_PLUS_EXPR
> +             || code == MINUS_EXPR)
> +           {
> +             tree op1 = gimple_assign_rhs1 (assign);
> +             tree op2 = gimple_assign_rhs2 (assign);
> +             if (TREE_CODE (op2) == INTEGER_CST)
> +               {
> +                 address->terms[i].expr = strip_casts (op1);
> +                 /* This is heuristic only, so don't worry about truncation
> +                    or overflow.  */
> +                 address->min_offset += (TREE_INT_CST_LOW (op2)
> +                                         * address->terms[i].multiplier);
> +                 continue;
> +               }
> +             else if (address->terms.length () < address_info::MAX_TERMS)
> +               {
> +                 unsigned int j = address->terms.length ();
> +                 address->terms.quick_push (address->terms[i]);
> +                 address->terms[i].expr = strip_casts (op1);
> +                 address->terms[j].expr = strip_casts (op2);
> +                 continue;
> +               }
> +           }
> +         if (code == MULT_EXPR)
> +           {
> +             tree op1 = gimple_assign_rhs1 (assign);
> +             tree op2 = gimple_assign_rhs2 (assign);
> +             if (multiply_term_by (address->terms[i], op2))
> +               {
> +                 address->terms[i].expr = strip_casts (op1);
> +                 continue;
> +               }
> +           }
> +       }
> +      i += 1;
> +    }
> +
> +  /* Peel off any symbolic pointer.  */
> +  if (TREE_CODE (address->terms[0].expr) != SSA_NAME
> +      && address->terms[0].multiplier == 1)
> +    {
> +      if (address->terms.length () == 1)
> +       {
> +         obstack_free (&m_obstack, address);
> +         return;
> +       }
> +      address->base = address->terms[0].expr;
> +      address->terms.ordered_remove (0);
> +    }
> +
> +  /* Require all remaining terms to be SSA names.  (This could be false
> +     for unfolded statements, but they aren't worth dealing with.)  */
> +  for (unsigned int i = 0; i < address->terms.length (); ++i)
> +    if (TREE_CODE (address->terms[i].expr) != SSA_NAME)
> +      {
> +       obstack_free (&m_obstack, address);
> +       return;
> +      }
> +
> +  /* The loop above set MIN_OFFSET based on the first byte of the
> +     referenced data.  Calculate the end + 1.  */
> +  address->max_offset = address->min_offset + type_size;
> +
> +  /* Put the terms into a canonical order for the hash table lookup below.  */
> +  address->terms.qsort (compare_address_terms);
> +
> +  if (dump_enabled_p ())
> +    {
> +      dump_printf_loc (MSG_NOTE, stmt, "recording address fragment %T", expr);
> +      if (multiplier != 1)
> +       dump_printf (MSG_NOTE, " * %wd", multiplier);
> +      dump_printf (MSG_NOTE, " = ");
> +      dump_address_info (MSG_NOTE, *address);
> +      dump_printf (MSG_NOTE, "\n");
> +    }
> +
> +  /* Pool address information with the same terms (but potentially
> +     different offsets).  */
> +  address_info **slot = m_address_table.find_slot (address, INSERT);
> +  if (address_info *old_address = *slot)
> +    {
> +      /* We've already seen an address with the same terms.  Extend the
> +        offset range to account for this access.  Doing this can paper
> +        over gaps, such as in:
> +
> +          a[i * stride * 4] + a[i * stride * 4 + 3];
> +
> +        where nothing references "+ 1" or "+ 2".  However, the vectorizer
> +        handles such gapped accesses without problems, so it's not worth
> +        trying to exclude them.  */
> +      if (old_address->min_offset > address->min_offset)
> +       old_address->min_offset = address->min_offset;
> +      if (old_address->max_offset < address->max_offset)
> +       old_address->max_offset = address->max_offset;
> +      obstack_free (&m_obstack, address);
> +    }
> +  else
> +    {
> +      /* This is the first term we've seen an address with these terms.  */
> +      *slot = address;
> +      m_address_list.safe_push (address);
> +    }
> +}
> +
> +/* Analyze expression EXPR, which occurs in STMT.  */
> +
> +void
> +loop_versioning::analyze_expr (gimple *stmt, tree expr)
> +{
> +  unsigned HOST_WIDE_INT type_size;
> +
> +  while (handled_component_p (expr))
> +    {
> +      /* See whether we can use versioning to avoid a multiplication
> +        in an array index.  */
> +      if (TREE_CODE (expr) == ARRAY_REF
> +         && acceptable_type_p (TREE_TYPE (expr), &type_size))
> +       record_address_fragment (stmt, type_size,
> +                                TREE_OPERAND (expr, 1), type_size, 0);
> +      expr = TREE_OPERAND (expr, 0);
> +    }
> +
> +  /* See whether we can use versioning to avoid a multiplication
> +     in the pointer calculation of a MEM_REF.  */
> +  if (TREE_CODE (expr) == MEM_REF
> +      && acceptable_type_p (TREE_TYPE (expr), &type_size))
> +    record_address_fragment (stmt, type_size, TREE_OPERAND (expr, 0), 1,
> +                            /* This is heuristic only, so don't worry
> +                               about truncation or overflow.  */
> +                            TREE_INT_CST_LOW (TREE_OPERAND (expr, 1)));
> +
> +  /* These would be easy to handle if they existed at this stage.  */
> +  gcc_checking_assert (TREE_CODE (expr) != TARGET_MEM_REF);
> +}
> +
> +/* Analyze all the statements in BB looking for useful version checks.
> +   Return true on success, false if something prevents the block from
> +   being versioned.  */
> +
> +bool
> +loop_versioning::analyze_block (basic_block bb)
> +{
> +  struct loop *loop = bb->loop_father;
> +  loop_info &li = get_loop_info (loop);
> +  for (gimple_stmt_iterator gsi = gsi_start_bb (bb); !gsi_end_p (gsi);
> +       gsi_next (&gsi))
> +    {
> +      gimple *stmt = gsi_stmt (gsi);
> +      if (is_gimple_debug (stmt))
> +       continue;
> +
> +      if (expensive_stmt_p (stmt))
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_NOTE, stmt, "expensive statement"
> +                            " prevents versioning: %G", stmt);
> +         return false;
> +       }
> +
> +      /* Only look for direct versioning opportunities in inner loops
> +        since the benefit tends to be much smaller for outer loops.  */
> +      if (!loop->inner)
> +       {
> +         unsigned int nops = gimple_num_ops (stmt);
> +         for (unsigned int i = 0; i < nops; ++i)
> +           if (tree op = gimple_op (stmt, i))
> +             analyze_expr (stmt, op);
> +       }
> +
> +      /* The point of the instruction limit is to prevent excessive
> +        code growth, so this is a size-based estimate even though
> +        the optimization is aimed at speed.  */
> +      li.num_insns += estimate_num_insns (stmt, &eni_size_weights);
> +    }
> +
> +  return true;
> +}
> +
> +/* Analyze all the blocks in the function, looking for useful version checks.
> +   Return true if we found one.  */
> +
> +bool
> +loop_versioning::analyze_blocks ()
> +{
> +  AUTO_DUMP_SCOPE ("analyze_blocks",
> +                  dump_user_location_t::from_function_decl (m_fn->decl));
> +
> +  /* For now we don't try to version the whole function, although
> +     versioning at that level could be useful in some cases.  */
> +  get_loop_info (get_loop (m_fn, 0)).rejected_p = true;
> +
> +  struct loop *loop;
> +  FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
> +    {
> +      loop_info &linfo = get_loop_info (loop);
> +
> +      /* See whether an inner loop prevents versioning of this loop.  */
> +      for (struct loop *inner = loop->inner; inner; inner = inner->next)
> +       if (get_loop_info (inner).rejected_p)
> +         {
> +           linfo.rejected_p = true;
> +           break;
> +         }
> +
> +      /* If versioning the loop is still a possibility, examine the
> +        statements in the loop to look for versioning opportunities.  */
> +      if (!linfo.rejected_p)
> +       {
> +         void *start_point = obstack_alloc (&m_obstack, 0);
> +
> +         for (basic_block bb = linfo.block_list; bb;
> +              bb = m_next_block_in_loop[bb->index])
> +           if (!analyze_block (bb))
> +             {
> +               linfo.rejected_p = true;
> +               break;
> +           }
> +
> +         if (!linfo.rejected_p)
> +           {
> +             /* Process any queued address fragments, now that we have
> +                complete grouping information.  */
> +             address_info *address;
> +             unsigned int i;
> +             FOR_EACH_VEC_ELT (m_address_list, i, address)
> +               analyze_address_fragment (*address);
> +           }
> +
> +         m_address_table.empty ();
> +         m_address_list.truncate (0);
> +         obstack_free (&m_obstack, start_point);
> +       }
> +    }
> +
> +  return m_num_conditions != 0;
> +}
> +
> +/* Use the ranges in VRS to remove impossible versioning conditions from
> +   LOOP.  */
> +
> +void
> +loop_versioning::prune_loop_conditions (struct loop *loop, vr_values *vrs)
> +{
> +  loop_info &li = get_loop_info (loop);
> +
> +  int to_remove = -1;
> +  bitmap_iterator bi;
> +  unsigned int i;
> +  EXECUTE_IF_SET_IN_BITMAP (&li.unity_names, 0, i, bi)
> +    {
> +      tree name = ssa_name (i);
> +      value_range *vr = vrs->get_value_range (name);
> +      if (vr && !range_includes_p (vr, 1))
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_NOTE, find_loop_location (loop),
> +                            "%T can never be 1 in this loop\n", name);
> +
> +         if (to_remove >= 0)
> +           bitmap_clear_bit (&li.unity_names, to_remove);
> +         to_remove = i;
> +         m_num_conditions -= 1;
> +       }
> +    }
> +  if (to_remove >= 0)
> +    bitmap_clear_bit (&li.unity_names, to_remove);
> +}
> +
> +/* Remove any scheduled loop version conditions that will never be true.
> +   Return true if any remain.  */
> +
> +bool
> +loop_versioning::prune_conditions ()
> +{
> +  AUTO_DUMP_SCOPE ("prune_loop_conditions",
> +                  dump_user_location_t::from_function_decl (m_fn->decl));
> +
> +  calculate_dominance_info (CDI_DOMINATORS);
> +  lv_dom_walker dom_walker (*this);
> +  dom_walker.walk (ENTRY_BLOCK_PTR_FOR_FN (m_fn));
> +  return m_num_conditions != 0;
> +}
> +
> +/* Merge the version checks for INNER into immediately-enclosing loop
> +   OUTER.  */
> +
> +void
> +loop_versioning::merge_loop_info (struct loop *outer, struct loop *inner)
> +{
> +  loop_info &inner_li = get_loop_info (inner);
> +  loop_info &outer_li = get_loop_info (outer);
> +
> +  if (dump_enabled_p ())
> +    {
> +      bitmap_iterator bi;
> +      unsigned int i;
> +      EXECUTE_IF_SET_IN_BITMAP (&inner_li.unity_names, 0, i, bi)
> +       if (!bitmap_bit_p (&outer_li.unity_names, i))
> +         dump_printf_loc (MSG_NOTE, find_loop_location (inner),
> +                          "hoisting check that %T == 1 to outer loop\n",
> +                          ssa_name (i));
> +    }
> +
> +  bitmap_ior_into (&outer_li.unity_names, &inner_li.unity_names);
> +  if (loop_depth (outer_li.outermost) < loop_depth (inner_li.outermost))
> +    outer_li.outermost = inner_li.outermost;
> +}
> +
> +/* Add LOOP to the queue of loops to version.  */
> +
> +void
> +loop_versioning::add_loop_to_queue (struct loop *loop)
> +{
> +  loop_info &li = get_loop_info (loop);
> +
> +  if (dump_enabled_p ())
> +    dump_printf_loc (MSG_NOTE, find_loop_location (loop),
> +                    "queuing this loop for versioning\n");
> +  m_loops_to_version.safe_push (loop);
> +
> +  /* Don't try to version superloops.  */
> +  li.rejected_p = true;
> +}
> +
> +/* Decide whether the cost model would allow us to version LOOP,
> +   either directly or as part of a parent loop, and return true if so.
> +   This does not imply that the loop is actually worth versioning in its
> +   own right, just that it would be valid to version it if something
> +   benefited.
> +
> +   We have already made this decision for all inner loops of LOOP.  */
> +
> +bool
> +loop_versioning::decide_whether_loop_is_versionable (struct loop *loop)
> +{
> +  loop_info &li = get_loop_info (loop);
> +
> +  if (li.rejected_p)
> +    return false;
> +
> +  /* Examine the decisions made for inner loops.  */
> +  for (struct loop *inner = loop->inner; inner; inner = inner->next)
> +    {
> +      loop_info &inner_li = get_loop_info (inner);
> +      if (inner_li.rejected_p)
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_NOTE, find_loop_location (loop),
> +                            "not versioning this loop because one of its"
> +                            " inner loops should not be versioned\n");
> +         return false;
> +       }
> +
> +      if (inner_li.worth_versioning_p ())
> +       li.subloops_benefit_p = true;
> +
> +      /* Accumulate the number of instructions from subloops that are not
> +        the innermost, or that don't benefit from versioning.  Only the
> +        instructions from innermost loops that benefit from versioning
> +        should be weighed against loop-versioning-max-inner-insns;
> +        everything else should be weighed against
> +        loop-versioning-max-outer-insns.  */
> +      if (!inner_li.worth_versioning_p () || inner->inner)
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_NOTE, find_loop_location (loop),
> +                            "counting %d instructions from this loop"
> +                            " against its parent loop\n", inner_li.num_insns);
> +         li.num_insns += inner_li.num_insns;
> +       }
> +    }
> +
> +  /* Enforce the size limits.  */
> +  if (li.worth_versioning_p ())
> +    {
> +      unsigned int max_num_insns = max_insns_for_loop (loop);
> +      if (dump_enabled_p ())
> +       dump_printf_loc (MSG_NOTE, find_loop_location (loop),
> +                        "this loop has %d instructions, against"
> +                        " a versioning limit of %d\n",
> +                        li.num_insns, max_num_insns);
> +      if (li.num_insns > max_num_insns)
> +       {
> +         if (dump_enabled_p ())
> +           dump_printf_loc (MSG_MISSED_OPTIMIZATION
> +                            | MSG_PRIORITY_USER_FACING,
> +                            find_loop_location (loop),
> +                            "this loop is too big to version");
> +         return false;
> +       }
> +    }
> +
> +  /* Hoist all version checks from subloops to this loop.  */
> +  for (struct loop *subloop = loop->inner; subloop; subloop = subloop->next)
> +    merge_loop_info (loop, subloop);
> +
> +  return true;
> +}
> +
> +/* Decide which loops to version and add them to the versioning queue.
> +   Return true if there are any loops to version.  */
> +
> +bool
> +loop_versioning::make_versioning_decisions ()
> +{
> +  AUTO_DUMP_SCOPE ("make_versioning_decisions",
> +                  dump_user_location_t::from_function_decl (m_fn->decl));
> +
> +  struct loop *loop;
> +  FOR_EACH_LOOP (loop, LI_FROM_INNERMOST)
> +    {
> +      loop_info &linfo = get_loop_info (loop);
> +      if (decide_whether_loop_is_versionable (loop))
> +       {
> +         /* Commit to versioning LOOP directly if we can't hoist the
> +            version checks any further.  */
> +         if (linfo.worth_versioning_p ()
> +             && (loop_depth (loop) == 1 || linfo.outermost == loop))
> +           add_loop_to_queue (loop);
> +       }
> +      else
> +       {
> +         /* We can't version this loop, so individually version any
> +            subloops that would benefit and haven't been versioned yet.  */
> +         linfo.rejected_p = true;
> +         for (struct loop *subloop = loop->inner; subloop;
> +              subloop = subloop->next)
> +           if (get_loop_info (subloop).worth_versioning_p ())
> +             add_loop_to_queue (subloop);
> +       }
> +    }
> +
> +  return !m_loops_to_version.is_empty ();
> +}
> +
> +/* Attempt to implement loop versioning for LOOP, using the information
> +   cached in the associated loop_info.  Return true on success.  */
> +
> +bool
> +loop_versioning::version_loop (struct loop *loop)
> +{
> +  loop_info &li = get_loop_info (loop);
> +
> +  /* Build up a condition that selects the original loop instead of
> +     the simplified loop.  */
> +  tree cond = boolean_false_node;
> +  bitmap_iterator bi;
> +  unsigned int i;
> +  EXECUTE_IF_SET_IN_BITMAP (&li.unity_names, 0, i, bi)
> +    {
> +      tree name = ssa_name (i);
> +      tree ne_one = fold_build2 (NE_EXPR, boolean_type_node, name,
> +                                build_one_cst (TREE_TYPE (name)));
> +      cond = fold_build2 (TRUTH_OR_EXPR, boolean_type_node, cond, ne_one);
> +    }
> +
> +  /* Convert the condition into a suitable gcond.  */
> +  gimple_seq stmts = NULL;
> +  cond = force_gimple_operand_1 (cond, &stmts, is_gimple_condexpr, NULL_TREE);
> +
> +  /* Version the loop.  */
> +  initialize_original_copy_tables ();
> +  basic_block cond_bb;
> +  struct loop *else_loop
> +    = loop_version (loop, cond, &cond_bb,
> +                   profile_probability::unlikely (),
> +                   profile_probability::likely (),
> +                   profile_probability::unlikely (),
> +                   profile_probability::likely (), true);
> +  free_original_copy_tables ();
> +  if (!else_loop)
> +    {
> +      if (dump_enabled_p ())
> +       dump_printf_loc (MSG_MISSED_OPTIMIZATION, find_loop_location (loop),
> +                        "tried but failed to version this loop for when"
> +                        " certain strides are 1\n");
> +      return false;
> +    }
> +
> +  if (dump_enabled_p ())
> +    dump_printf_loc (MSG_OPTIMIZED_LOCATIONS, find_loop_location (loop),
> +                    "versioned this loop for when certain strides are 1\n");
> +
> +  /* Insert the statements that feed COND.  */
> +  if (stmts)
> +    {
> +      gimple_stmt_iterator gsi = gsi_last_bb (cond_bb);
> +      gsi_insert_seq_before (&gsi, stmts, GSI_SAME_STMT);
> +    }
> +
> +  /* Simplify the new loop, which is used when COND is false.  */
> +  name_prop (li).substitute_and_fold (else_loop->header);
> +  return true;
> +}
> +
> +/* Attempt to version all loops in the versioning queue.  Return true
> +   if we succeeded for at least one loop.  */
> +
> +bool
> +loop_versioning::implement_versioning_decisions ()
> +{
> +  /* No AUTO_DUMP_SCOPE here since all messages are top-level and
> +     user-facing at this point.  */
> +
> +  bool any_succeeded_p = false;
> +
> +  struct loop *loop;
> +  unsigned int i;
> +  FOR_EACH_VEC_ELT (m_loops_to_version, i, loop)
> +    if (version_loop (loop))
> +      any_succeeded_p = true;
> +
> +  return any_succeeded_p;
> +}
> +
> +/* Run the pass and return a set of TODO_* flags.  */
> +
> +unsigned int
> +loop_versioning::run ()
> +{
> +  gcc_assert (scev_initialized_p ());
> +
> +  if (!analyze_blocks ()
> +      || !prune_conditions ()
> +      || !make_versioning_decisions ()
> +      || !implement_versioning_decisions ())
> +    return 0;
> +
> +  return TODO_update_ssa;
> +}
> +
> +/* Loop versioning pass.  */
> +
> +const pass_data pass_data_loop_versioning =
> +{
> +  GIMPLE_PASS, /* type */
> +  "lversion", /* name */
> +  OPTGROUP_LOOP, /* optinfo_flags */
> +  TV_LOOP_VERSIONING, /* tv_id */
> +  PROP_cfg, /* properties_required */
> +  0, /* properties_provided */
> +  0, /* properties_destroyed */
> +  0, /* todo_flags_start */
> +  0, /* todo_flags_finish */
> +};
> +
> +class pass_loop_versioning : public gimple_opt_pass
> +{
> +public:
> +  pass_loop_versioning (gcc::context *ctxt)
> +    : gimple_opt_pass (pass_data_loop_versioning, ctxt)
> +  {}
> +
> +  /* opt_pass methods: */
> +  virtual bool gate (function *) { return flag_version_loops_for_strides; }
> +  virtual unsigned int execute (function *);
> +};
> +
> +unsigned int
> +pass_loop_versioning::execute (function *fn)
> +{
> +  if (number_of_loops (fn) <= 1)
> +    return 0;
> +
> +  return loop_versioning (fn).run ();
> +}
> +
> +} // anon namespace
> +
> +gimple_opt_pass *
> +make_pass_loop_versioning (gcc::context *ctxt)
> +{
> +  return new pass_loop_versioning (ctxt);
> +}
> Index: gcc/testsuite/gcc.dg/loop-versioning-1.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-1.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,92 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* The simplest IV case.  */
> +
> +void
> +f1 (double *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[stepx * i] = 100;
> +}
> +
> +void
> +f2 (double *x, int stepx, int limit)
> +{
> +  for (int i = 0; i < limit; i += stepx)
> +    x[i] = 100;
> +}
> +
> +void
> +f3 (double *x, int stepx, int limit)
> +{
> +  for (double *y = x; y < x + limit; y += stepx)
> +    *y = 100;
> +}
> +
> +void
> +f4 (double *x, int stepx, unsigned int n)
> +{
> +  for (unsigned int i = 0; i < n; ++i)
> +    x[stepx * i] = 100;
> +}
> +
> +void
> +f5 (double *x, int stepx, unsigned int limit)
> +{
> +  for (unsigned int i = 0; i < limit; i += stepx)
> +    x[i] = 100;
> +}
> +
> +void
> +f6 (double *x, int stepx, unsigned int limit)
> +{
> +  for (double *y = x; y < x + limit; y += stepx)
> +    *y = 100;
> +}
> +
> +double x[10000];
> +
> +void
> +g1 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[stepx * i] = 100;
> +}
> +
> +void
> +g2 (int stepx, int limit)
> +{
> +  for (int i = 0; i < limit; i += stepx)
> +    x[i] = 100;
> +}
> +
> +void
> +g3 (int stepx, int limit)
> +{
> +  for (double *y = x; y < x + limit; y += stepx)
> +    *y = 100;
> +}
> +
> +void
> +g4 (int stepx, unsigned int n)
> +{
> +  for (unsigned int i = 0; i < n; ++i)
> +    x[stepx * i] = 100;
> +}
> +
> +void
> +g5 (int stepx, unsigned int limit)
> +{
> +  for (unsigned int i = 0; i < limit; i += stepx)
> +    x[i] = 100;
> +}
> +
> +void
> +g6 (int stepx, unsigned int limit)
> +{
> +  for (double *y = x; y < x + limit; y += stepx)
> +    *y = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 12 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 12 "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-10.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-10.c   2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,52 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Test that we can version a gather-like operation in which a variable
> +   stride is applied to the index.  */
> +
> +int
> +f1 (int *x, int *index, int step, int n)
> +{
> +  int res = 0;
> +  for (int i = 0; i < n; ++i)
> +    res += x[index[i] * step];
> +  return res;
> +}
> +
> +int
> +f2 (int *x, int *index, int step, int n)
> +{
> +  int res = 0;
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int *ptr = x + index[i] * step;
> +      res += *ptr;
> +    }
> +  return res;
> +}
> +
> +int x[1000];
> +
> +int
> +g1 (int *index, int step, int n)
> +{
> +  int res = 0;
> +  for (int i = 0; i < n; ++i)
> +    res += x[index[i] * step];
> +  return res;
> +}
> +
> +int
> +g2 (int *index, int step, int n)
> +{
> +  int res = 0;
> +  for (int i = 0; i < n; ++i)
> +    {
> +      int *ptr = x + index[i] * step;
> +      res += *ptr;
> +    }
> +  return res;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {address term [^\n]* \* loop-invariant} 4 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 4 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 4 "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-11.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-11.c   2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,29 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Test that we don't try to version for something that is never 1.  */
> +
> +void
> +f1 (double *x, int stepx, int n)
> +{
> +  if (stepx == 1)
> +    for (int i = 0; i < n; ++i)
> +      x[i] = 100;
> +  else
> +    for (int i = 0; i < n; ++i)
> +      x[stepx * i] = 100;
> +}
> +
> +void
> +f2 (double *x, int stepx, int n)
> +{
> +  if (stepx <= 1)
> +    for (int i = 0; i < n; ++i)
> +      x[i] = 100;
> +  else
> +    for (int i = 0; i < n; ++i)
> +      x[stepx * i] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 2 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {can never be 1} 2 "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-2.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-2.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,73 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Versioning for step == 1 in these loops would allow loop interchange,
> +   but otherwise isn't worthwhile.  At the moment we decide not to version.  */
> +
> +void
> +f1 (double x[][100], int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step][i] = 100;
> +}
> +
> +void
> +f2 (double x[][100], int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j][i * step] = 100;
> +}
> +
> +void
> +f3 (double x[][100], int step, int limit)
> +{
> +  for (int i = 0; i < 100; ++i)
> +    for (int j = 0; j < limit; j += step)
> +      x[j][i] = 100;
> +}
> +
> +void
> +f4 (double x[][100], int step, int limit)
> +{
> +  for (int i = 0; i < limit; i += step)
> +    for (int j = 0; j < 100; ++j)
> +      x[j][i] = 100;
> +}
> +
> +double x[100][100];
> +
> +void
> +g1 (int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step][i] = 100;
> +}
> +
> +void
> +g2 (int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j][i * step] = 100;
> +}
> +
> +void
> +g3 (int step, int limit)
> +{
> +  for (int i = 0; i < 100; ++i)
> +    for (int j = 0; j < limit; j += step)
> +      x[j][i] = 100;
> +}
> +
> +void
> +g4 (int step, int limit)
> +{
> +  for (int i = 0; i < limit; i += step)
> +    for (int j = 0; j < 100; ++j)
> +      x[j][i] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-3.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-3.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,24 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Versioning these loops for when both steps are 1 allows loop
> +   interchange, but otherwise isn't worthwhile.  At the moment we decide
> +   not to version.  */
> +
> +void
> +f1 (double x[][100], int step1, int step2, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step1][i * step2] = 100;
> +}
> +
> +void
> +f2 (double x[][100], int step1, int step2, int limit)
> +{
> +  for (int i = 0; i < limit; i += step1)
> +    for (int j = 0; j < limit; j += step2)
> +      x[j][i] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-4.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-4.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,39 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* These shouldn't be versioned; it's extremely likely that the code
> +   is emulating two-dimensional arrays.  */
> +
> +void
> +f1 (double *x, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[i * step + j] = 100;
> +}
> +
> +void
> +f2 (double *x, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step + i] = 100;
> +}
> +
> +void
> +f3 (double *x, int *offsets, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[i * step + j + offsets[i]] = 100;
> +}
> +
> +void
> +f4 (double *x, int *offsets, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step + i + offsets[i]] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-5.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-5.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,17 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* There's no information about whether STEP1 or STEP2 is innermost,
> +   so we should assume the code is sensible and version for the inner
> +   evolution, i.e. when STEP2 is 1.  */
> +
> +void
> +f1 (double *x, int step1, int step2, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[i * step1 + j * step2] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop for when step2} 1 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 1 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 1 "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-6.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-6.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,31 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* The read from y in f1 will be hoisted to the outer loop.  In general
> +   it's not worth versioning outer loops when the inner loops don't also
> +   benefit.
> +
> +   This test is meant to be a slight counterexample, since versioning
> +   does lead to cheaper outer-loop vectorization.  However, the benefit
> +   isn't enough to justify the cost.  */
> +
> +void
> +f1 (double *restrict x, double *restrict y, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[i + j] = y[i * step];
> +}
> +
> +/* A similar example in which the read can't be hoisted, but could
> +   for example be handled by vectorizer alias checks.  */
> +
> +void
> +f2 (double *x, double *y, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[i + j] = y[i * step];
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-7.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-7.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,32 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Check that versioning can handle arrays of structures.  */
> +
> +struct foo {
> +  int a, b, c;
> +};
> +
> +void
> +f1 (struct foo *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[stepx * i].a = 1;
> +      x[stepx * i].b = 2;
> +      x[stepx * i].c = 3;
> +    }
> +}
> +
> +void
> +f2 (struct foo *x, int stepx, int limit)
> +{
> +  for (int i = 0; i < limit; i += stepx)
> +    {
> +      x[i].a = 1;
> +      x[i].b = 2;
> +      x[i].c = 3;
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 2 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 2 "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-8.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-8.c    2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,43 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Versioning for step == 1 in these loops would allow loop interchange,
> +   but otherwise isn't worthwhile.  At the moment we decide not to version.  */
> +
> +struct foo {
> +  int a[100];
> +};
> +
> +void
> +f1 (struct foo *x, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j * step].a[i] = 100;
> +}
> +
> +void
> +f2 (struct foo *x, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    for (int j = 0; j < n; ++j)
> +      x[j].a[i * step] = 100;
> +}
> +
> +void
> +f3 (struct foo *x, int step, int limit)
> +{
> +  for (int i = 0; i < 100; ++i)
> +    for (int j = 0; j < limit; j += step)
> +      x[j].a[i] = 100;
> +}
> +
> +void
> +f4 (struct foo *x, int step, int limit)
> +{
> +  for (int i = 0; i < limit; i += step)
> +    for (int j = 0; j < 100; ++j)
> +      x[j].a[i] = 100;
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-9.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-9.c    2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,48 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Check that versioning can handle small groups of accesses.  */
> +
> +void
> +f1 (int *x, int *y, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 2] + y[i * step * 2 + 1];
> +}
> +
> +void
> +f2 (int *x, int *y, __INTPTR_TYPE__ step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 2] + y[i * step * 2 + 1];
> +}
> +
> +void
> +f3 (int *x, int *y, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 3] + y[i * step * 3 + 2];
> +}
> +
> +void
> +f4 (int *x, int *y, __INTPTR_TYPE__ step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 3] + y[i * step * 3 + 2];
> +}
> +
> +void
> +f5 (int *x, int *y, int step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 4] + y[i * step * 4 + 3];
> +}
> +
> +void
> +f6 (int *x, int *y, __INTPTR_TYPE__ step, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    x[i] = y[i * step * 4] + y[i * step * 4 + 3];
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 6 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 6 "lversion" } } */
> Index: gcc/testsuite/gfortran.dg/loop_versioning_1.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_1.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,28 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details" }
> +
> +! The simplest IV case.
> +
> +subroutine f1(x)
> +  real :: x(:)
> +  x(:) = 100
> +end subroutine f1
> +
> +subroutine f2(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step)
> +  do i = 1, n
> +     x(i * step) = 100
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, limit, step)
> +  integer :: limit, step
> +  real :: x(limit)
> +  do i = 1, limit, step
> +     x(i) = 100
> +  end do
> +end subroutine f3
> +
> +! { dg-final { scan-tree-dump-times {likely to be the innermost dimension} 1 "lversion" } }
> +! { dg-final { scan-tree-dump-times {want to version containing loop} 3 "lversion" } }
> +! { dg-final { scan-tree-dump-times {versioned this loop} 3 "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_2.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_2.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,39 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details -fno-frontend-loop-interchange" }
> +
> +! We could version the loop for when the first dimension has a stride
> +! of 1, but at present there's no real benefit.  The gimple loop
> +! interchange pass couldn't handle the versioned loop, and interchange
> +! is instead done by the frontend (but disabled by the options above).
> +
> +subroutine f1(x)
> +  real :: x(:, :)
> +  do i = lbound(x, 1), ubound(x, 1)
> +     do j = lbound(x, 2), ubound(x, 2)
> +        x(i, j) = 100
> +     end do
> +  end do
> +end subroutine f1
> +
> +subroutine f2(x, n, step)
> +  integer :: n, step
> +  real :: x(100, 100)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i * step, j) = 100
> +     end do
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step, n)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i * step, j) = 100
> +     end do
> +  end do
> +end subroutine f3
> +
> +! { dg-final { scan-tree-dump-times {likely to be the innermost dimension} 1 "lversion" } }
> +! { dg-final { scan-tree-dump-not {want to version} "lversion" } }
> +! { dg-final { scan-tree-dump-not {versioned} "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_3.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_3.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,30 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details -fno-frontend-loop-interchange" }
> +
> +! Test a case in which the outer loop iterates over the inner dimension.
> +! The options above prevent the frontend from interchanging the loops.
> +
> +subroutine f1(x, limit, step, n)
> +  integer :: limit, step, n
> +  real :: x(limit, n)
> +  do i = 1, limit, step
> +     do j = 1, n
> +        x(i, j) = 100
> +     end do
> +  end do
> +end subroutine f1
> +
> +subroutine f2(x, n, limit, step)
> +  integer :: n, limit, step
> +  real :: x(limit, n)
> +  do i = 1, n
> +     do j = 1, limit, step
> +        x(j, i) = 100
> +     end do
> +  end do
> +end subroutine f2
> +
> +! FIXME: The frontend doesn't give us enough information to tell which loop
> +! is iterating over the innermost dimension, so we optimistically
> +! assume the inner one is.
> +! { dg-final { scan-tree-dump-not {want to version} "lversion" { xfail *-*-* } } }
> +! { dg-final { scan-tree-dump-not {versioned} "lversion" { xfail *-*-* } } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_4.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_4.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,52 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details -fno-frontend-loop-interchange" }
> +
> +! Test cases in which versioning is useful for a two-dimensional array.
> +
> +subroutine f1(x)
> +  real :: x(:, :)
> +  x(:, :) = 100
> +end subroutine f1
> +
> +subroutine f2(x)
> +  real :: x(:, :)
> +  do i = lbound(x, 1), ubound(x, 1)
> +     do j = lbound(x, 2), ubound(x, 2)
> +        x(j, i) = 100
> +     end do
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, n, step)
> +  integer :: n, step
> +  real :: x(100, 100)
> +  do i = 1, n
> +     do j = 1, n
> +        x(j * step, i) = 100
> +     end do
> +  end do
> +end subroutine f3
> +
> +subroutine f4(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step, n)
> +  do i = 1, n
> +     do j = 1, n
> +        x(j * step, i) = 100
> +     end do
> +  end do
> +end subroutine f4
> +
> +subroutine f5(x, n, limit, step)
> +  integer :: n, limit, step
> +  real :: x(limit, n)
> +  do i = 1, n
> +     do j = 1, limit, step
> +        x(j, i) = 100
> +     end do
> +  end do
> +end subroutine f5
> +
> +! { dg-final { scan-tree-dump-times {likely to be the innermost dimension} 2 "lversion" } }
> +! { dg-final { scan-tree-dump-times {want to version containing loop} 5 "lversion" } }
> +! { dg-final { scan-tree-dump-times {hoisting check} 5 "lversion" } }
> +! { dg-final { scan-tree-dump-times {versioned this loop} 5 "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_5.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_5.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,57 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details -fno-frontend-loop-interchange" }
> +
> +! Make sure that in a "badly nested" loop, we don't treat the inner loop
> +! as iterating over the inner dimension with a variable stride.
> +
> +subroutine f1(x, n)
> +  integer :: n
> +  real :: x(100, 100)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i, j) = 100
> +     end do
> +  end do
> +end subroutine f1
> +
> +subroutine f2(x, n, step)
> +  integer :: n, step
> +  real :: x(100, 100)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i, j * step) = 100
> +     end do
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, n)
> +  integer :: n
> +  real :: x(n, n)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i, j) = 100
> +     end do
> +  end do
> +end subroutine f3
> +
> +subroutine f4(x, n, step)
> +  integer :: n, step
> +  real :: x(n, n * step)
> +  do i = 1, n
> +     do j = 1, n
> +        x(i, j * step) = 100
> +     end do
> +  end do
> +end subroutine f4
> +
> +subroutine f5(x, n, limit, step)
> +  integer :: n, limit, step
> +  real :: x(n, limit)
> +  do i = 1, n
> +     do j = 1, limit, step
> +        x(i, j) = 100
> +     end do
> +  end do
> +end subroutine f5
> +
> +! { dg-final { scan-tree-dump-not {want to version} "lversion" } }
> +! { dg-final { scan-tree-dump-not {versioned} "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_6.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_6.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,93 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details" }
> +
> +! Check that versioning can handle small groups of accesses.
> +
> +subroutine f1(x)
> +  real :: x(:)
> +  do i = lbound(x, 1), ubound(x, 1) / 2
> +     x(i * 2) = 100
> +     x(i * 2 + 1) = 101
> +  end do
> +end subroutine f1
> +
> +subroutine f2(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step * 2)
> +  do i = 1, n
> +     x(i * step * 2) = 100
> +     x(i * step * 2 + 1) = 101
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, limit, step)
> +  integer :: limit, step
> +  real :: x(limit * 2)
> +  do i = 1, limit, step
> +     x(i * 2) = 100
> +     x(i * 2 + 1) = 101
> +  end do
> +end subroutine f3
> +
> +subroutine f4(x)
> +  real :: x(:)
> +  do i = lbound(x, 1), ubound(x, 1) / 3
> +     x(i * 3) = 100
> +     x(i * 3 + 1) = 101
> +     x(i * 3 + 2) = 102
> +  end do
> +end subroutine f4
> +
> +subroutine f5(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step * 3)
> +  do i = 1, n
> +     x(i * step * 3) = 100
> +     x(i * step * 3 + 1) = 101
> +     x(i * step * 3 + 2) = 102
> +  end do
> +end subroutine f5
> +
> +subroutine f6(x, limit, step)
> +  integer :: limit, step
> +  real :: x(limit * 3)
> +  do i = 1, limit, step
> +     x(i * 3) = 100
> +     x(i * 3 + 1) = 101
> +     x(i * 3 + 2) = 102
> +  end do
> +end subroutine f6
> +
> +subroutine f7(x)
> +  real :: x(:)
> +  do i = lbound(x, 1), ubound(x, 1) / 4
> +     x(i * 4) = 100
> +     x(i * 4 + 1) = 101
> +     x(i * 4 + 2) = 102
> +     x(i * 4 + 3) = 103
> +  end do
> +end subroutine f7
> +
> +subroutine f8(x, n, step)
> +  integer :: n, step
> +  real :: x(n * step * 4)
> +  do i = 1, n
> +     x(i * step * 4) = 100
> +     x(i * step * 4 + 1) = 101
> +     x(i * step * 4 + 2) = 102
> +     x(i * step * 4 + 3) = 103
> +  end do
> +end subroutine f8
> +
> +subroutine f9(x, limit, step)
> +  integer :: limit, step
> +  real :: x(limit * 4)
> +  do i = 1, limit, step
> +     x(i * 4) = 100
> +     x(i * 4 + 1) = 101
> +     x(i * 4 + 2) = 102
> +     x(i * 4 + 3) = 103
> +  end do
> +end subroutine f9
> +
> +! { dg-final { scan-tree-dump-times {want to version containing loop} 9 "lversion" } }
> +! { dg-final { scan-tree-dump-times {versioned this loop} 9 "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_7.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_7.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,67 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details" }
> +
> +! Check that versioning can handle small groups of accesses, with the
> +! group being a separate array dimension.
> +
> +subroutine f1(x, n, step)
> +  integer :: n, step
> +  real :: x(2, n * step)
> +  do i = 1, n
> +     x(1, i * step) = 100
> +     x(2, i * step) = 101
> +  end do
> +end subroutine f1
> +
> +subroutine f2(x, limit, step)
> +  integer :: limit, step
> +  real :: x(2, limit)
> +  do i = 1, limit, step
> +     x(1, i) = 100
> +     x(2, i) = 101
> +  end do
> +end subroutine f2
> +
> +subroutine f3(x, n, step)
> +  integer :: n, step
> +  real :: x(3, n * step)
> +  do i = 1, n
> +     x(1, i * step) = 100
> +     x(2, i * step) = 101
> +     x(3, i * step) = 102
> +  end do
> +end subroutine f3
> +
> +subroutine f4(x, limit, step)
> +  integer :: limit, step
> +  real :: x(3, limit)
> +  do i = 1, limit, step
> +     x(1, i) = 100
> +     x(2, i) = 101
> +     x(3, i) = 102
> +  end do
> +end subroutine f4
> +
> +subroutine f5(x, n, step)
> +  integer :: n, step
> +  real :: x(4, n * step)
> +  do i = 1, n
> +     x(1, i * step) = 100
> +     x(2, i * step) = 101
> +     x(3, i * step) = 102
> +     x(4, i * step) = 103
> +  end do
> +end subroutine f5
> +
> +subroutine f6(x, limit, step)
> +  integer :: limit, step
> +  real :: x(4, limit)
> +  do i = 1, limit, step
> +     x(1, i) = 100
> +     x(2, i) = 101
> +     x(3, i) = 102
> +     x(4, i) = 103
> +  end do
> +end subroutine f6
> +
> +! { dg-final { scan-tree-dump-times {want to version containing loop} 6 "lversion" } }
> +! { dg-final { scan-tree-dump-times {versioned this loop} 6 "lversion" } }
> Index: gcc/testsuite/gfortran.dg/loop_versioning_8.f90
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gfortran.dg/loop_versioning_8.f90     2018-12-06 12:33:59.220098363 +0000
> @@ -0,0 +1,13 @@
> +! { dg-options "-O3 -fdump-tree-lversion-details" }
> +
> +! Check that versioning is applied to a gather-like reduction operation.
> +
> +function f(x, index, n)
> +  integer :: n
> +  real :: x(:)
> +  integer :: index(n)
> +  f = sum(x(index(:)))
> +end function f
> +
> +! { dg-final { scan-tree-dump-times {want to version containing loop} 1 "lversion" } }
> +! { dg-final { scan-tree-dump-times {versioned this loop} 1 "lversion" } }
> Index: gcc/testsuite/gcc.dg/loop-versioning-12.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-12.c   2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,149 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Test that we don't try to version for a step of 1 when that would
> +   cause the iterations to overlap.  */
> +
> +void
> +f1 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx] = 100;
> +      x[i * stepx + 1] = 99;
> +    }
> +}
> +
> +void
> +f2 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +f3 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx - 16] = 100;
> +      x[i * stepx - 15] = 99;
> +    }
> +}
> +
> +void
> +f4 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +f5 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx - 16] = 100;
> +      x[i * stepx + 15] = 99;
> +    }
> +}
> +
> +void
> +f6 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i - 16] = 100;
> +      x[i + 15] = 99;
> +    }
> +}
> +
> +void
> +f7 (unsigned short *x, int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +unsigned short x[1000];
> +
> +void
> +g1 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx] = 100;
> +      x[i * stepx + 1] = 99;
> +    }
> +}
> +
> +void
> +g2 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +g3 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx - 16] = 100;
> +      x[i * stepx - 15] = 99;
> +    }
> +}
> +
> +void
> +g4 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +g5 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx - 16] = 100;
> +      x[i * stepx + 15] = 99;
> +    }
> +}
> +
> +void
> +g6 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx)
> +    {
> +      x[i - 16] = 100;
> +      x[i + 15] = 99;
> +    }
> +}
> +
> +void
> +g7 (int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-13.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-13.c   2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,109 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Test that we do version for a step of 1 when that would lead the
> +   iterations to access consecutive groups.  */
> +
> +void
> +f1 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 2] = 100;
> +      x[i * stepx * 2 + 1] = 99;
> +    }
> +}
> +
> +void
> +f2 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 2)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +f3 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 2 - 16] = 100;
> +      x[i * stepx * 2 - 15] = 99;
> +    }
> +}
> +
> +void
> +f4 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 2)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +f5 (unsigned short *x, int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx * 2)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +unsigned short x[1000];
> +
> +void
> +g1 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 2] = 100;
> +      x[i * stepx * 2 + 1] = 99;
> +    }
> +}
> +
> +void
> +g2 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 2)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +g3 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 2 - 16] = 100;
> +      x[i * stepx * 2 - 15] = 99;
> +    }
> +}
> +
> +void
> +g4 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 2)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +g5 (int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx * 2)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-times {want to version containing loop} 10 "lversion" } } */
> +/* { dg-final { scan-tree-dump-times {versioned this loop} 10 "lversion" } } */
> Index: gcc/testsuite/gcc.dg/loop-versioning-14.c
> ===================================================================
> --- /dev/null   2018-11-29 13:15:04.463550658 +0000
> +++ gcc/testsuite/gcc.dg/loop-versioning-14.c   2018-12-06 12:33:59.216098398 +0000
> @@ -0,0 +1,149 @@
> +/* { dg-options "-O3 -fdump-tree-lversion-details" } */
> +
> +/* Test that we don't try to version for a step of 1 when that would
> +   cause the iterations to leave a gap between accesses.  */
> +
> +void
> +f1 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 4] = 100;
> +      x[i * stepx * 4 + 1] = 99;
> +    }
> +}
> +
> +void
> +f2 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 4)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +f3 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 4 - 16] = 100;
> +      x[i * stepx * 4 - 15] = 99;
> +    }
> +}
> +
> +void
> +f4 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 4)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +f5 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 64 - 16] = 100;
> +      x[i * stepx * 64 + 15] = 99;
> +    }
> +}
> +
> +void
> +f6 (unsigned short *x, int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 64)
> +    {
> +      x[i - 16] = 100;
> +      x[i + 15] = 99;
> +    }
> +}
> +
> +void
> +f7 (unsigned short *x, int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx * 4)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +unsigned short x[1000];
> +
> +void
> +g1 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 4] = 100;
> +      x[i * stepx * 4 + 1] = 99;
> +    }
> +}
> +
> +void
> +g2 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 4)
> +    {
> +      x[i] = 100;
> +      x[i + 1] = 99;
> +    }
> +}
> +
> +void
> +g3 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 4 - 16] = 100;
> +      x[i * stepx * 4 - 15] = 99;
> +    }
> +}
> +
> +void
> +g4 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 4)
> +    {
> +      x[i - 16] = 100;
> +      x[i - 15] = 99;
> +    }
> +}
> +
> +void
> +g5 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; ++i)
> +    {
> +      x[i * stepx * 64 - 16] = 100;
> +      x[i * stepx * 64 + 15] = 99;
> +    }
> +}
> +
> +void
> +g6 (int stepx, int n)
> +{
> +  for (int i = 0; i < n; i += stepx * 64)
> +    {
> +      x[i - 16] = 100;
> +      x[i + 15] = 99;
> +    }
> +}
> +
> +void
> +g7 (int stepx, int n)
> +{
> +  for (unsigned short *y = x; y < x + n; y += stepx * 4)
> +    {
> +      y[0] = 100;
> +      y[1] = 99;
> +    }
> +}
> +
> +/* { dg-final { scan-tree-dump-not {want to version} "lversion" } } */
> +/* { dg-final { scan-tree-dump-not {versioned} "lversion" } } */
> Index: gcc/testsuite/gcc.dg/vect/slp-43.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/vect/slp-43.c  2018-05-02 08:37:48.993604639 +0100
> +++ gcc/testsuite/gcc.dg/vect/slp-43.c  2018-12-06 12:33:59.220098363 +0000
> @@ -1,5 +1,5 @@
>  /* { dg-require-effective-target vect_int } */
> -/* { dg-additional-options "-O3" } */
> +/* { dg-additional-options "-O3 -fno-version-loops-for-strides" } */
>
>  #include <string.h>
>  #include "tree-vect.h"
> Index: gcc/testsuite/gcc.dg/vect/slp-45.c
> ===================================================================
> --- gcc/testsuite/gcc.dg/vect/slp-45.c  2018-05-02 08:37:48.997604602 +0100
> +++ gcc/testsuite/gcc.dg/vect/slp-45.c  2018-12-06 12:33:59.220098363 +0000
> @@ -1,5 +1,5 @@
>  /* { dg-require-effective-target vect_int } */
> -/* { dg-additional-options "-O3" } */
> +/* { dg-additional-options "-O3 -fno-version-loops-for-strides" } */
>
>  #include <string.h>
>  #include "tree-vect.h"


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]