This is an archive of the discontinued LLVM Phabricator instance.

Don't recompute LCSSA after loop-unrolling when possible.
ClosedPublic

Authored by mzolotukhin on Nov 9 2015, 8:54 PM.

Details

Summary

Currently we always recompute LCSSA for outer loops after unrolling an
inner loop. That leads to compile time problem when we have big loop
nests, and we can solve it by avoiding unnecessary work. For instance,
if w eonly do partial unrolling, we don't break LCSSA, so we don't need
to rebuild it. Also, if all exits from the inner loop are inside the
enclosing loop, then complete unrolling won't break LCSSA either.

I replaced unconditional LCSSA recomputation with conditional recomputation +
unconditional assert and added several tests, which were failing when I
experimented with it.

Soon I plan to follow up with a similar patch for recalculation of dominators
tree.

Diff Detail

Repository
rL LLVM

Event Timeline

mzolotukhin updated this revision to Diff 39782.Nov 9 2015, 8:54 PM
mzolotukhin retitled this revision from to Don't recompute LCSSA after loop-unrolling when possible..
mzolotukhin updated this object.
mzolotukhin added a subscriber: llvm-commits.
mehdi_amini added inline comments.Nov 10 2015, 9:49 AM
lib/Transforms/Utils/LoopUnroll.cpp
233 ↗(On Diff #39782)

LI->getLoopFor(BB) != ParentL; could be written ParentL->contains(BB);

Also, as an alternative to the raw loop:

bool AllExitsAreInsideParentLoop = std::all_of(ExitBlocks.begin(), ExitBlocks.end(), [&] (BasicBlock *BB) { return ParentL->contains(BB); })
567 ↗(On Diff #39782)

Are we sure that there is no case where partial unrolling can break the LCSSA form for the enclosing loop? I don't think so but not sure why or if it is an implementation guarantee that can be lost?
Also, are we allowed to break LCSSA for the current loop in case of partial unrolling? I.e. if I have a pass manager that contains unrolling + another loop pass, and the second expect LCSSA form for every loop, it seems your patch could change that (unless partial unrolling guarantee LCSSA on the partially unrolled loop as well).

Hi Mehdi,

Thanks for the remarks, replies below:

Michael

lib/Transforms/Utils/LoopUnroll.cpp
233 ↗(On Diff #39782)

Good idea, thanks!

567 ↗(On Diff #39782)

AFAIU, partial unrolling shouldn't break LCSSA. The reason is that we don't change loops structure and don't add/remove exits from loops. Provided LCSSA was valid before partial unrolling, it should remain valid as all LCSSA phis would stay in correct places, and we won't create new in-loop definitions with uses outside the loop and those phis.
Complete loop unrolling, in contrast, changes loop structure and might change exits even for an outer loop - that's why we usually need to rebuild LCSSA after it. However, if all loop exits are inside the parent loop, then full unrolling of the inner loop shouldn't change exits for outer loops, and we won't create definitions inside the loop with uses outside (since outside uses would be in phis, which are located in exits, which are inside the enclosing loop). Thus, in this case there is also no need to rebuild LCSSA.
If my logic is flawed anywhere, I'd appreciate to be corrected!

  • Address review remarks.
hfinkel added inline comments.Nov 11 2015, 9:37 PM
lib/Transforms/Utils/LoopUnroll.cpp
229 ↗(On Diff #39865)

Can't ParentL be nullptr here for a top-level loop?

mzolotukhin marked 4 inline comments as done.Nov 11 2015, 9:59 PM
mzolotukhin added inline comments.
lib/Transforms/Utils/LoopUnroll.cpp
229 ↗(On Diff #39865)

It can, and that explains failures I see in testing after this change:) I'll fix this by adding !ParentL || before the std::all_of call.

  • Don't fail if ParentL is nullptr.
mzolotukhin marked 2 inline comments as done.Nov 13 2015, 4:28 PM
mehdi_amini added inline comments.Nov 13 2015, 4:34 PM
lib/Transforms/Utils/LoopUnroll.cpp
227 ↗(On Diff #40186)

It seems you didn't update the test, if it was passing before it means this is not tested in the non-regression suite?

mzolotukhin added inline comments.Nov 13 2015, 5:28 PM
lib/Transforms/Utils/LoopUnroll.cpp
227 ↗(On Diff #40186)

I didn't wait for test-suite when I made this change as I erroneously considered it NFC. However, with it even compiler-rt failed to build, so we definitely have tests for this case.

chandlerc accepted this revision.Nov 13 2015, 7:38 PM
chandlerc edited edge metadata.

LGTM as soon as you have a test case for the top-most loop!

lib/Transforms/Utils/LoopUnroll.cpp
227 ↗(On Diff #40186)

I still think we should have a regression test that exercises this situation.

563–567 ↗(On Diff #40186)

I agree with your reasoning, and the assert is a good way to stay confident that we don't break this going forward.

test/Transforms/LoopUnroll/rebuild_lcssa.ll
19 ↗(On Diff #40186)

All of the function attrs comments seem stale (the attrs are gone).

24 ↗(On Diff #40186)

I'd really prefer to strip all the predecessor comments. It makes it a lot harder to update IMO.

78 ↗(On Diff #40186)

It'd be good to check the structure here (ie, that its a single-entry phi from the exiting block).

This revision is now accepted and ready to land.Nov 13 2015, 7:38 PM
This revision was automatically updated to reflect the committed changes.

Thanks! Committed in r253126.

I didn't add a test for outermost loop, since any test with a single loop checks it and we have plenty of such tests already (currently we have 38 loop-unroll tests that'll fail if we remove !ParentL ||). If we you believe we still need a separate test for it, I can commit it as a follow-up.

Michael