This is an archive of the discontinued LLVM Phabricator instance.

[IndVars] Make loop varying predicates loop invariant.
ClosedPublic

Authored by sanjoy on Jul 16 2015, 2:54 PM.

Details

Summary

Was D9784: "Remove loop variant range check when induction variable is
strictly increasing"

This change re-implements D9784 with the two differences:

  1. It does not use SCEVExpander and does not generate new instructions. Instead, it does a quick local search for existing llvm::Values that it needs when modifying the icmp instruction.
  2. It is more general -- it deals with both increasing and decreasing induction variables.

I've added all of the tests included with D9784, and two more.

As an example on what this change does (copied from D9784):

Given C code:

for (int i = M; i < N; i++) // i is known not to overflow
  if (i < 0) break;
  a[i] = 0;
}

This transformation produces:

for (int i = M; i < N; i++)
  if (M < 0) break;
  a[i] = 0;
}

Which can be unswitched into:

if (!(M < 0))
  for (int i = M; i < N; i++)
    a[i] = 0;
}

I went back and forth on whether the top level logic should live in
SimplifyIndvar::eliminateIVComparison or be put into its own
routine. Right now I've put it under eliminateIVComparison because
even though the icmp is not *eliminated*, it no longer is an IV
comparison. I'm open to putting it in its own helper routine if you
think that is better.

Diff Detail

Event Timeline

sanjoy updated this revision to Diff 29948.Jul 16 2015, 2:54 PM
sanjoy retitled this revision from to [IndVars] Make loop varying predicates loop invariant..
sanjoy updated this object.
sanjoy added reviewers: reames, atrick.
sanjoy added a subscriber: llvm-commits.
reames added inline comments.Jul 17 2015, 6:47 PM
include/llvm/Analysis/ScalarEvolution.h
587

This part of the comment makes no sense to me. Typos?

594

You might want to note the ordering of the arguments to the comparison. You implicitly do it in the naming, but not in the comment.

919

I really can't tell from this comment what this function actually does.

lib/Analysis/ScalarEvolution.cpp
6619

If I'm reading this right, a strictly decreasing predicate can be converted to a strictly increasing one by just swapping the direction of the predicate right? That seems like it might form the basis for either s simplification in this code, it's caller, or possibly a useful assert.

6638

I don't think NonNegative is enough here. Don't you need Positive? i.e. what's would a zero step value mean?

6686

Early return preferred.

6704

I think I've convinced myself the reasoning here for an increasing predicate (false->true) is valid, but I'm struggling with the reasoning for the decreasing (true->false) case. In particular, the order of operands in the result seems suspicious.

... I convinced myself this is correct, but a better comment about why would help. In particular, you're relying on the fact that inverting a decreasing predicate creates an increasing one which switches at the same point. This is fine, but should be explained.

Also, is this robust against future enhancements to isMonotonicPredicate? I think it is, but how do we ensure that?

One natural case to handle would be an equals comparison. That's not monotonic, so I think we're fine... that would take the form of a weird unswitch (i.e. start..end doesn't include test value). What about the case where an equals comparison is monotonic precisely because it's controlling whether the induction variable is incremented? Does that cause us problems? We'd end with either an infinite loop or our weird "monotonic equal" being the guard on the backedge... I think we'd be fine. The infinite loop case is caught by the isLoopBackedgeGuardByCond, the backedge guard itself is caught by... well nothing?

So yeah, we do need to make sure we define this in such a way that the backedge guard itself can't be considered monotonic. Or at least include that test case to prevent future person from shooting themselves in the foot. :)

6705

This utility function is unfamilar to me. Does this imply that if the backedge executes, the condition must be true? Does it say anything about when the backedge is not taken? Might we have problems with two potential early exits and the wrong one being selected? (Given the condition is just made invariant, I think this would be fine...)

lib/Transforms/Utils/SimplifyIndVar.cpp
195

This looks correct, but rather restricted. Could we do something slightly more general? This might be a good follow up patch.

204

Is getting a SCEV for each incoming value expensive? I don't have a good mental cost model for this.

I might organize this as two loops with a break for clarity.

Or alternatively, put a break in for when both NewLHS and RHS are set.

212

Some info about those subtle tradeoffs?

Looking through the code, won't NewRHS either be S or X by definition?

I think we should only need to be finding the start index?

sanjoy marked 4 inline comments as done.Jul 20 2015, 5:34 PM

Sending in new patch shortly.

include/llvm/Analysis/ScalarEvolution.h
594

I have re-written this comment, please take a look.

919

Cleaned up the comment, PTAL.

lib/Analysis/ScalarEvolution.cpp
6619

I think that's a good idea. I'll split out a Impl version of this function and do the assert you suggested. The assertion won't work with the function as written because the case for UGT isn't symmetric -- I didn't fill in the other cases because an nuw unsigned decreasing variable does not make much sense in practice -- but there is no reason why I cannot add the missing cases.

6638

A zero step value means the induction variable is essentially a loop invariant value. We don't really depend on the predicate actually flipping from false to true (for increasing predicates), all we care about is that if the predicate changes then it only changes from false to true.

A zero step value in itself is not very useful, but there may be places where SCEV can prove X >= 0 but not prove X > 0, so it is helpful to be maximally general.

6705

Does this imply that if the backedge executes, the condition must be true?

Yes.

Does it say anything about when the backedge is not taken?

No.

Might we have problems with two potential early exits and the wrong one being selected?

I don't see how -- the condition should evaluate to whatever it was evaluating to before.

lib/Transforms/Utils/SimplifyIndVar.cpp
195

Yes. I think there is scope to extend this part to emit some basic arithmetic, but I think there is value in checking this in as is, and then taking stock of what cases we're still missing.

204

Is getting a SCEV for each incoming value expensive? I don't have a good mental cost model for this.

IIUC, internally, computing a SCEV for a PHINode or addition on a phinode also computes SCEV for all of its incoming values, so this should be just a hashtable lookup since we've already computed S. Having said that, I should not be calling getSCEV twice for no good reason, so I'll fix that.

Or alternatively, put a break in for when both NewLHS and RHS are set.

Done.

212

Some info about those subtle tradeoffs?

While I don't see making loop varying predicates loop invariant being a pessimization, it can be performance neutral in the worst case. If it is performance neutral then emitting extra instructions outside the loop to compute the loop invariant check's operands may be a pessimization overall.

Looking through the code, won't NewRHS either be S or X by definition?

I think we should only need to be finding the start index?

I don't see how I can do that without coupling this bit of code with the implementation of isLoopInvariantPredicate.

sanjoy updated this revision to Diff 30220.Jul 20 2015, 5:35 PM
sanjoy marked 2 inline comments as done.
  • address Philip's review
  • add more tests
reames edited edge metadata.Jul 21 2015, 2:34 PM

LGTM w/comments addressed. I would prefer to have another set of eyes on it (Andy?,
Nick?) before submission though. I'm fairly sure of the algorithm involved, but am not entirely familiar with the code involved.

lib/Analysis/ScalarEvolution.cpp
6634

Rather than sharing the work, it might be cleaner to just put this block standalone in it's own scope and reuse the NDEBUG code even in debug. It makes it less likely you'll have a NDEBUG only error.

6677

Your reasoning about the zero case should be here in a comment. It makes perfect sense once explained, but isn't obvious from the code.

atrick accepted this revision.Jul 26 2015, 6:27 PM
atrick edited edge metadata.

LGTM. Nice optimization.

lib/Analysis/ScalarEvolution.cpp
6634

I think it would be slightly preferable to have:

  bool Result = isMonotonicPredicateImpl(...);
#ifndef NDEBUG
  bool SwappedResult = ... 
  assert(...)
#endif
  return Result;
6677

Definitely.

This revision is now accepted and ready to land.Jul 26 2015, 6:27 PM
This revision was automatically updated to reflect the committed changes.
sanjoy marked 2 inline comments as done.