<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
  </head>
  <body>
    <p>Responding to this old thread to let interested parties know
      there's been some progress on this (finally).  <br>
    </p>
    <p>The first sub-item described below - multiple exit loops with
      computable trip counts - is in progress, and will likely be
      wrapped up in the not too distant future.  The first major change
      (4b33b2387) landed two weeks ago, two smaller changes are on
      review (<span class="phui-oi-objname" data-sigil="ungrabbable">D93725,
      </span><span class="phui-oi-objname" data-sigil="ungrabbable"><span
          class="phui-oi-objname" data-sigil="ungrabbable">and D93865),
          and there's likely only one major patch needed after that.</span></span></p>
    <p><span class="phui-oi-objname" data-sigil="ungrabbable"><span
          class="phui-oi-objname" data-sigil="ungrabbable">To my
          knowledge, there's been no progress on the second item and I'm
          not anticipating any in the near future.  <br>
        </span></span></p>
    <p><span class="phui-oi-objname" data-sigil="ungrabbable"><span
          class="phui-oi-objname" data-sigil="ungrabbable">Philip<br>
        </span></span></p>
    <div class="moz-cite-prefix">On 9/9/19 10:53 AM, Philip Reames
      wrote:<br>
    </div>
    <blockquote type="cite"
      cite="mid:a48a26de-46af-3814-3967-c395e8230e51@philipreames.com">
      <meta http-equiv="content-type" content="text/html; charset=UTF-8">
      <p>I've recently mentioned in a few places that I'm interested in
        enhancing the loop vectorizer to handle multiple exit loops, and
        have been asked to share plans.  This email is intended to a)
        share my current thinking and b) help spark discussion among
        interested parties.  I do need to warn that my near term plans
        for this have been delayed; I got pulled into an internal
        project instead.</p>
      <p><b>Background</b></p>
      <p>At the moment, our loop vectorizer does not handle any loop
        with more than one exit, or where that sole exit is not from the
        latch block.  Interestingly, it does handle internal predication
        within a single loop iteration.  This results in a slightly odd
        behavior where a loop which can be written with either a
        continue or a break can exhibit wildly different performance
        depending on that choice.  It does hint at a possible direction
        for implementation though, and implies that most of the cost
        modeling pieces are already in place.<br>
      </p>
      <p>The use cases I'm looking at basically fall into two buckets:</p>
      <p>for (int i = 0; i < N; i++) { <br>
          if (expr(a[i])) break; <br>
          ... other vectorizable stuff ... <br>
        } <br>
        <br>
        for (int i = 0; i < N; i++) { <br>
          if (expr(i)) break; <br>
          ... other vectorizable stuff ... <br>
        } <br>
        <br>
        The former are the actually "interesting" examples.  The later
        are cases where we missed eliminating some check we could have,
        but not-vectorizing creates an unfortunate performance cliff. <br>
      </p>
      <p><b>Framing</b></p>
      <p>We have three important sub-cases to handle.  <br>
      </p>
      <p>First, there are all the cases where we could have handled the
        multiple exit loop, but chose not to for implementation
        reasons.  A great example is:</p>
      <p>for (int i = 0; i < N; i++) {<br>
          if (i > M) break;<br>
          a[i] = i;<br>
        }</p>
      <p>In this case, SCEV can happily compute the actual exit bound of
        the loop, and we could use the existing vector-body/scalar
        slow-path structure.  The only change needed would be to exit
        the vector body earlier.  (There are some details here, but it's
        almost as easy as I'm describing if my POC patch isn't missing
        something major.)</p>
      <p>There's a couple other cases like this.  I suspect we can get
        decent mileage out of just generalizing the existing code.  <br>
      </p>
      <p><br>
      </p>
      <p>Second, there are the cases where we actually have to handle
        iterations within the vector-body with predication (or
        speculation).  The good news is that there's already support in
        code for predication, we just need to add another source of
        potential predication.  The bad news is that's a fairly major
        change.  <br>
      </p>
      <p>Our challenge will be finding a runtime check for
        dereferenceability.  Consider the following example:<span
          data-sigil="slippery"><br>
          for (int i = 0; i < N; i++) <br>
            if (a[i] == 0) return false;<br>
          return true;</span></p>
      <p><span data-sigil="slippery">If 'a' is an alloca, or statically
          sized global, we can form a runtime check which ensures 'i' is
          in bounds and a speculative load will not fault.  <br>
        </span></p>
      <p>Here's a nice example we could handle with this approach.  <br>
      </p>
      <p>// Assume a and b are both statically sized.<br>
        <span data-sigil="slippery"><span data-sigil="slippery">for (int
            i = 0; i < N; i++) {<br>
              t = b[i];<br>
              if (t > M) throw();<br>
              sum += a[t];<br>
            }</span></span></p>
      <p><span data-sigil="slippery"><span data-sigil="slippery">(This
            is a classic a[b[i]] pattern, but with range checks added.)</span></span></p>
      <p><span data-sigil="slippery">This is broadly applicable enough
          to be useful, and in practice covers my use cases, but I'm
          hoping others have ideas for extensions here.</span><br>
        <span data-sigil="slippery"><span data-sigil="slippery"></span></span></p>
      Before resorting to that though, we have the potential to rely
      more on speculation safety reasoning.  I have a patch out for
      review currently (<span data-sigil="slippery"><span
          class="phui-oi-objname">D66688</span> <a
          href="https://reviews.llvm.org/D66688" class="phui-oi-link"
          title="[LoopVectorize] Leverage speculation safety to avoid
          masked.loads" moz-do-not-send="true">[LoopVectorize] Leverage
          speculation safety to avoid masked.loads</a>) which fell out
        of some prototyping in this area; it benefits existing
        predication logic, so I separated it.  <br>
      </span>
      <p><span data-sigil="slippery">The other major challenge here is
          profitability.  Consider a simple loop such as:</span></p>
      <p><span data-sigil="slippery">// assume a[0:N] is known
          dereferenceable<br>
          for (int i = 0; i < N; i++) <br>
            if (a[i] == 0) return false;<br>
          return true;</span></p>
      <p><span data-sigil="slippery">If N is large, and the array is
          non-zero, then this is profitable to vectorize.  If a[0] == 0,
          then it isn't, regardless of the value of N.  <br>
        </span></p>
      <p><span data-sigil="slippery">Figuring out when to vectorize vs
          not for cases like this will require some thought.  I don't
          really have a good answer for this yet, other than when the
          profile on the early exit tells us it's rarely taken.  <br>
        </span></p>
      <p><span data-sigil="slippery"><br>
        </span></p>
      <p><span data-sigil="slippery">Third, for both of the former
          cases, we need to be able to compute exit values along the
          early exit edges.  We can get a lot of mileage out of simply
          skipping loops with exit values (i.e. lcssa phis), as our loop
          exit value rewriting will tend to eliminate them.  However, we
          will eventually want to handle this case, which will require
          generating some interesting complicated code down the exit
          path to figure out which iteration actually exit.  <br>
        </span></p>
      <p><span data-sigil="slippery">I see two general options here:</span></p>
      <p><span data-sigil="slippery">1) Use the vector-body/scalar-body
          idiom of today, and have the vector body exit with IV = I when
          any iteration in [I, I+VF-1] would exit.  (i.e. roll back)  <br>
        </span></p>
      <p><span data-sigil="slippery">2) Insert dedicated exit blocks
          which recompute exit conditions to determine exact exit value,
          and then let the vector body run all iterations in VF which
          contain the exiting iteration.  (Assumes predicated stores,
          and that the exit blocks skip the scalar loop)  <br>
        </span></p>
      <p><span data-sigil="slippery">I currently don't have a reason to
          strongly prefer one over the other.  (2) is conceptually
          cleaner and the one which keeps coming up in conversation, but
          (1) may be simpler to actually implement.  <br>
        </span></p>
      <p><br>
      </p>
      <p><b>Current Plans</b></p>
      <p>At the current moment, I'm reasonable sure that I'm going to
        get the resources to at least tackle some of the cases where we
        bail out unnecessarily.  This will be a huge practical
        improvement in vectorizing robustness, at (I hope) relatively
        little implementation cost.  <br>
      </p>
      <p>I'm also going to continue playing around with enhancements to
        our current dereferenceability logic.  I see that as being a key
        building block to make any predication based approach
        practical.  <br>
      </p>
      <p>I'm not sure I'm going to get to the predication support.  I'd
        like to, but am not sure my resource constraints allow it.  I'll
        also mention that I'm not at all sure how all of this might fit
        in with the VPLAN work.  I'd really welcome feedback on that; is
        what I'm proposing at all consistent with others plans?</p>
      <p><br>
      </p>
      <p>Philip<br>
      </p>
    </blockquote>
  </body>
</html>