[llvm-dev] Generalizing load/store promotion in LICM

Alina Sbirlea via llvm-dev llvm-dev at lists.llvm.org
Thu Sep 13 14:00:59 PDT 2018


For context, I've been looking into replacing the use of AST
(AliasSetTracker) with MemorySSA in LICM. This works great for
sinking/hoisting but does not apply well for promotion, and one of the
solutions I considered is precisely ripping out the promotion part and
replacing its functionality with a separate PRE pass + possibly store
sinking. FWIW I think that's the right way to go.
I did not get deep enough into working on the solution but I would gladly
have a detailed discussion to move this forward.

Reading into detail now.

Thanks,
Alina

On Thu, Sep 13, 2018 at 1:43 PM Philip Reames <listmail at philipreames.com>
wrote:

> (minor inline additions)
>
> On 09/13/2018 01:51 AM, Chandler Carruth wrote:
>
> Haven't had time to dig into this, but wanted to add +Alina Sbirlea
> <asbirlea at google.com> to the thread as she has been working on promotion
> and other aspects of LICM for a long time here.
>
> Thanks!
>
> On Wed, Sep 12, 2018 at 11:41 PM Philip Reames <listmail at philipreames.com>
> wrote:
>
>> I'm thinking about making some semi radical changes to load store
>> promotion works in LICM, and figured it would be a good idea to get buy in
>> before I actually started writing code.  :)
>>
>> TLDR: legality of sinking stores to exits is hard, can we separate load
>> handling into a super aggressive form of PRE, and use predicated stores to
>> avoid solving legality question?
>>
>>
>> Background
>>
>> We've been seeing an interesting class of problems recently that looks
>> roughly like this:
>>
>> for (int = 0; i < N; i++)
>>   if (a[i] == 0) // some data dependent check
>>     g_count++; // conditional load and store to shared location
>>
>> The critical detail in this example is that g_count is a global location
>> which may be accessed concurrently* by multiple threads.  The challenge
>> here is that we don't know whether the store ever executes, and we're not
>> allowed to insert a store along any path that didn't originally contain
>> them.  Said differently, if all elements in "a" are non-zero, we're not
>> allowed to store to g_count.  We do know that the g_count location is
>> dereferenceable though.
>>
>> (*Please, let's avoid the memory model derailment here.  I'm simplifying
>> and C++ language rules aren't real useful for my Java language frontend
>> anyways.  In practice, all the access are atomic, but unordered, but we'll
>> leave that out of discussion otherwise.)
>>
>> I have two approaches I'm considering here.  These are orthogonal, but I
>> suspect we'll want to implement both.
>>
>>
>> Proposal A - Use predicated stores in loop exits
>>
>> The basic idea is that we don't worry about solving the legality question
>> above, and just insert a store which is predicated on a condition which is
>> true exactly when the original store ran.  In pseudo code, this looks
>> something like:
>>
>> bool StoreLegal = false;
>> int LocalCount = g_count;
>> for (int = 0; i < N; i++)
>>   if (a[i] == 0) {
>>     LocalCount++;
>>     StoreLegal = true;
>>   }
>> if (StoreLegal) g_count = LocalCount;
>>
>> There are two obvious concerns here:
>>
>>    1. The predicated store might be expensive in practice - true for
>>    most current architectures.
>>    2. We''re introducing an extra boolean phi cycle around the loop.
>>
>> Talking to a couple of folks offline at the socials over the last few
>> months, the former seems to be the main objection.  I think we can control
>> this by restricting this transform to when the loop is believed to have a
>> high trip count and the conditional path is taken with some frequency.
>> Getting feedback on this particular point is one of my main reasons for
>> writing this mail.
>>
>> The second objection can frequently be resolved by finding other
>> expressions which evaluate to the same boolean.  (In this case, if
>> LocalCount != LocalCountOrig assuming i doesn't overflow.)  We already have
>> a framework with SCEV to do these replacements.  Though, from some quick
>> testing, it would definitely need strengthening.  However, SCEV can't
>> remove the extra phi in all cases, so we have to be okay with the extra phi
>> cycle in the general case.  This seems worthwhile to me, but thoughts?
>>
>>
>> Proposal B - Separate load and store handling into distinct transforms
>>
>> (For folks I've discussed this with before, this part is all new.)
>>
>> Thinking about the problem, it finally occurred to me that we can
>> decompose the original example into two steps: getting the loads out of the
>> loop, and sinking the stores out of the loop.  If we can accomplish the
>> former, but not the later, we've still made meaningful progress.
>>
>> So, what'd we'd essentially have is a load only transformation which
>> produces this:
>> int LocalCount = g_count;
>> for (int = 0; i < N; i++)
>>   if (a[i] == 0) {
>>     LocalCount++;
>>     g_count = LocalCount;
>>   }
>>
>> At this point, we've reduced memory traffic by half, and opened up the
>> possibility that other parts of the optimizer can exploit the simplified
>> form.  The last point is particular interesting since we generally try to
>> canonicalize loads out of loops, and much of the optimizer is tuned for a
>> form with as much as possible being loop invariant.  As one example,
>> completely by accident, there's some work going on in the LoopVectorizer
>> right now to handle such stores to loop invariant addresses during
>> vectorization.  Putting the two pieces together would let us fully
>> vectorize this loop without needing to sink the stores at all.
>>
>> In practice, the existing implementation in LICM would cleanly split
>> along these lines with little problem.
>>
>> One way of looking at this load specific transform is as an extreme form
>> of PRE (partial redundancy elimination).  Our current PRE implementation
>> doesn't handle cases this complicated.
>>
> It occurred to my later that simply framing the new transform as a
> separate pass (LoopPRE) and using the same AST + SSA construction approach
> would be straight forward.  So, if folks think that having an aggressive
> form of load PRE in LICM is going a bit too far, it'd be easy to represent
> as an optional separate pass.  I'd still prefer having LICM contain the
> logic though.
>
> Thoughts?
>>
>> Philip
>>
>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.llvm.org/pipermail/llvm-dev/attachments/20180913/efa66c72/attachment.html>


More information about the llvm-dev mailing list