This is an archive of the discontinued LLVM Phabricator instance.

[LCG] Redesign the lazy post-order iteration mechanism for the LazyCallGraph to support repeated, stable iterations, even in the face of graph updates.
ClosedPublic

Authored by chandlerc on Sep 4 2016, 2:13 AM.

Details

Summary

This is particularly important to allow the CGSCC pass manager to walk
the RefSCCs (and thus everything else) in a module more than once. Lots
of unittests and other tests were hard or impossible to write because
repeated CGSCC pass managers which didn't invalidate the LazyCallGraph
would conclude the module was empty after the first one. =[ Really,
really bad.

The interesting thing is that in many ways this simplifies the code. We
can now re-use the same code for handling reference edge insertion
updates of the RefSCC graph as we use for handling call edge insertion
updates of the SCC graph. Outside of adapting to the shared logic for
this (which isn't trivial, but is *much* simpler than the DFS it
replaces!), the new code involves putting newly created RefSCCs when
deleting a reference edge into the cached list in the correct way, and
to re-formulate the iterator to be stable and effective even in the face
of these kinds of updates.

I've updated the unittests for the LazyCallGraph to re-iterate the
postorder sequence and verify that this all works. We even check for
using alternating iterators to trigger the lazy formation of RefSCCs
after mutation has occured.

It's worth noting that there are a reasonable number of likely simplifications
we can make past this. It isn't clear that we need to keep the "LeafRefSCCs"
around any more. But I've not removed that mostly because I want this to be
a more isolated change.

Diff Detail

Event Timeline

chandlerc updated this revision to Diff 70285.Sep 4 2016, 2:13 AM
chandlerc retitled this revision from to [LCG] Redesign the lazy post-order iteration mechanism for the LazyCallGraph to support repeated, stable iterations, even in the face of graph updates..
chandlerc updated this object.
chandlerc added a reviewer: sanjoy.
chandlerc added a subscriber: llvm-commits.
eastig added a subscriber: eastig.Sep 5 2016, 7:32 AM
eraman added a subscriber: eraman.Sep 7 2016, 5:56 PM
eraman added inline comments.
lib/Analysis/LazyCallGraph.cpp
804–810

Nit: RefSCC in place of SCC in assert messages here and below.

830

Why are ref edges excluded here?

903

The node to SCC mapping doesn't change right?

chandlerc marked 2 inline comments as done.Sep 8 2016, 2:23 AM

Thanks so much for the review! See responses below.

lib/Analysis/LazyCallGraph.cpp
804–810

Great catch! Thanks, and fixed. I think I have several more of these that I'm gonna do a pass over this entire code to clean up afterward...

830

Yikes!

Because this code is based originally on the code for call edges, and I didn't update this.

I've updated the tests to actually check that this works in graphs composed of reference cycles rather than call cycles.

903

Correct, this should be a no-op. But I'd prefer to remove it in a separate commit as this was already here and I'm not changing the fact that it isn't needed. =] Good catch though.

Sadly, I can't really test for this as it is in fact a no-op. Even an assert doesn't really seem valuable here.

chandlerc updated this revision to Diff 70666.Sep 8 2016, 2:24 AM
chandlerc marked 2 inline comments as done.

Updated with better assert strings, a big "DOH!" bug fix, and an important
added test case to cover that bug fix. Many thanks to Easwaran for the review
that found these issues.

eraman added inline comments.Sep 8 2016, 10:33 AM
lib/Analysis/LazyCallGraph.cpp
882–883

The comment is stale now since there is no DAG walk above anymore.

885–886

I don't think you should be reversing the MergeRange here. Consider this example where you have three RefSCCs R0, R1 and R2 with the numbers being index to the PostOrderRefSCCs. Assume R2 is this RefSCC and the edge being added is from R0 -> R2. Assume each Ri has one Si (call SCC). MergeRange is [R0, R1]. At the end of this loop, MergedSCCs will have [S1, S0] and outside the loop S2 gets added to MergedSCCs resulting in [S1, S0, S2]. I believe you want it to be [S0, S1, S2] to keep it in post order.

The unit test below doesn't test that call SCC's are in post-order. Ensuring verify on the merged RefSCC doesn't assert helps in general (but still won't catch this)

chandlerc marked an inline comment as done.Sep 11 2016, 8:24 PM
chandlerc added inline comments.
lib/Analysis/LazyCallGraph.cpp
885–886

Arrg, still more things that need a more complex test case to trigger.

I've added a couple more tests that exercise this and some other aspects of the code. They cause the verify below to catch this bug, as well as independently verifying the postorder result.

Thanks again!

chandlerc updated this revision to Diff 70970.Sep 11 2016, 8:26 PM

Update with more tests and a fix nicely spotted by Easwaran during review.

sanjoy edited edge metadata.Sep 11 2016, 10:01 PM

Some comments inline.

include/llvm/Analysis/LazyCallGraph.h
920

Not sure what you mean by "Only RefSCCs which have been created while walking the graph" -- what else is there?

lib/Analysis/LazyCallGraph.cpp
827

Why can't you do a DFS on the parent edges starting from SourceC, and bound it by the range in the postorder list?

870–876

s/SCCs/RefSCCs/ right?

Also, I know this is "controversial", but I think using an explicit type for MergeRange here is actually going to be more readable.

874

Nice!

892

copy_if ?

937

I know this isn't new, but does it make sense to erase the erased RefSCCs from RefSCCIndices too?

1212

s/sequnece/sequence/

1221

How about adding an accessor to LCG like getIndexForRefSCC that verifies that the index into the postorder is correct?

1225

Use llvm::seq ?

1588

*sequence

Working others, but a quick comment below:

lib/Analysis/LazyCallGraph.cpp
827

I mean, I could. Do you think that would be simpler? It seems like it would be strictly more complicated than this, and still O(edges), so it didn't seem like it was worth pursuing. But I'm happy to try it out if you think it'd be an improvement.

eraman added inline comments.Sep 12 2016, 3:19 PM
lib/Analysis/LazyCallGraph.cpp
827

Isn't what Sanjoy proposing O(|edges in RefSCC DAG|) which is a tighter bound than O(|edges in the callgraph|) of the current implementation?

unittests/Analysis/LazyCallGraphTest.cpp
1082

Incorrect comment. No calls in this RefSCC.

sanjoy added inline comments.Sep 12 2016, 10:55 PM
lib/Analysis/LazyCallGraph.cpp
827

Do you think that would be simpler?

Not in a one-to-one comparison, but perhaps you could write it in a way that can be shared with ComputeTargetConnectedSet ?

+ What @eraman said.

chandlerc marked 18 inline comments as done.Sep 13 2016, 4:49 AM

Thanks for all the comments Sanjoy and Easwaran!

include/llvm/Analysis/LazyCallGraph.h
920

No idea. Removed.

lib/Analysis/LazyCallGraph.cpp
827

Sanjoy and I chatted about this on IRC and now I understand *much* better what your idea is here Sanjoy.

The idea is just to walk the parent edges to build connected sets. It's not the full complexity (in code) of Tarjan's, its just walking like this but with a nicer complexity bound.

However, thinking about this I think I've made this entire problem much more complex than is necessary. But the simplification I have in mind will impact both this code and the SCC caller, so I'd rather do that in a follow-up if its OK.

870–876

Done on the first count.

The type seems really long, opaque, and to add little value to me? It's: iterator_range<SmallVectorImpl<RefSCC *>::iterator>. That said, if it makes it easier to read, no problem. I've put it in here, and I can update the other caller.

To help me understand this better, what about seeing this type helps you read the code?

892

It's quite awkward to use. std::inserter doesn't help because the insert routine doesn't accept an iterator position (and even if it did, there is no meaningful iterator to provide).

We could use the range insert routine and a filtered iterator, but that seems also overkill.

1225

Hah, good idea!

chandlerc updated this revision to Diff 71144.Sep 13 2016, 4:52 AM
chandlerc marked 4 inline comments as done.
chandlerc edited edge metadata.

Updates from codereview (and rebased).

sanjoy accepted this revision.Sep 13 2016, 10:39 PM
sanjoy edited edge metadata.

lgtm modulo minor things here and there

include/llvm/Analysis/LazyCallGraph.h
978

Can we add an assert(PostOrderRefSCCs[IndexIt->second] == &RC) here?

lib/Analysis/LazyCallGraph.cpp
851

But the simplification I have in mind will impact both this code and the SCC caller, so I'd rather do that in a follow-up if its OK.

SGTM.

870–878

Okay, I agree that iterator_range<SmallVectorImpl<RefSCC *>::iterator> isn't great. :)

However, I had made my suggestion under the assumption that updatePostorderSequenceForEdgeInsertion returns an ArrayRef<RefSCC *>. Does changing updatePostorderSequenceForEdgeInsertion to return ArrayRef<RefSCC *> sound generally cleaner?

In any case, there's no reason to hold checking this on this specific point, it's very minor.

887

Not clear what "This RefSCC should terminate the DFS without being reached." means now.

892

Ok.

1212

There's one more ("sequnece").

unittests/Analysis/LazyCallGraphTest.cpp
142

Are the trailing \n 's needed? Seems like we should be able to replace them with spaces (which may look less distracting).

581

Writing to dbgs() seems fairly uncommon in unittests/ (I only see one other instance). I
can't see any obvious reason why this will be a problem, but perhaps there is a reason why it is usually avoided?

895

Can this sequence be a C macro or a C++ class with A1, A2 etc. as fields (in the latter case all of the lookup logic could be in a constructor or some helper function)? Right now this is repeated a few times.

1018

llvm::find (here and below)?

1082

@eraman 's comment has not been addressed.

This revision is now accepted and ready to land.Sep 13 2016, 10:39 PM
This revision was automatically updated to reflect the committed changes.
chandlerc marked 9 inline comments as done.

Thanks all! Adjustments made, landed.

Easwaran, if you have more comments, don't hesitate to post them and I'll keep track here. I've still got a few things to clean up post-submit anyways.

lib/Analysis/LazyCallGraph.cpp
870–878

Doesn't really work ... we hand the range to erase, etc. We could hack it with addresses into the ArrayRef, but that seems pretty gross. Happy to keep tweaking the API though...

unittests/Analysis/LazyCallGraphTest.cpp
142

I think so. But can try some stuff with this whole file in a follow-up commit.

581

No idea, it made debugging this stuff easier and it seems harmless...

895

Yea, I'd like to refactor a bunch of this, probably in a follow-up. The repeated pattern got a lot more frequent somewhat organically.

1082

Doh, sorry I missed this. Thanks!