This is an archive of the discontinued LLVM Phabricator instance.

[CaptureTracker] Provide a numbered basic block to PointerMayBeCapturedBefore
ClosedPublic

Authored by bruno on Jul 20 2015, 12:33 PM.

Details

Summary

This patch is a follow up from r240560 and is a step further into trying
to mitigate the compile time performance issues in the path DeadStoreElimination -> MepDepAnalysis ->
CaptureTracking. I've basically moved the "cached numbered BB" out of the CaptureTracker.

By providing the CaptureTracker with the "cached numbered BB" instead of
computing it every time, MemDepAnalysis can use the same "cached numbered BB"
throughout its calls to AA->callCapturesBefore, avoiding to recompute it for
every scanned instruction. In the same testcase used in r240560, compile time goes
down from 2min to 30s.

Diff Detail

Repository
rL LLVM

Event Timeline

bruno updated this revision to Diff 30180.Jul 20 2015, 12:33 PM
bruno retitled this revision from to [CaptureTracker] Provide a numbered basic block to PointerMayBeCapturedBefore.
bruno updated this object.
bruno added reviewers: hfinkel, dberlin, reames.
bruno added subscribers: llvm-commits, rafael, chandlerc.
dberlin edited edge metadata.Jul 24 2015, 9:50 AM

I know you didn't build the original code that is being moved out here, and I really appreciate you taking the time to work on this :)

However, looking at it, while it seems okay for an internal implementation detail, moving it out into it's own class makes me think it needs a little work on the interface/implementation.
Hope that is okay.

include/llvm/Analysis/AliasAnalysis.h
479 ↗(On Diff #30180)

While you are here, can you please document the function and its arguments properly?

include/llvm/Analysis/NumberedBasicBlock.h
34 ↗(On Diff #30180)

Given the description of what this class does, why is it called numbered anything?
Shouldn't it be "OrderedBasicBlock"?

You are just describing and querying things about the order
The fact that you maintain the order using numbering is an implementation detail (and in fact i've seen proposals to change how BB's are laid out so that ordering would not require numbering.)

38 ↗(On Diff #30180)

Can you add comments describing what these represent?

43 ↗(On Diff #30180)

The implementation here seems confusing. Find doesn't find anything, instead, it is really trying to give a before relationship (IE it is literally answering the dominates query)

If you want to return the instruction, i'd call this "first_of" or something.
But i'm not clear on why it returns an instruction, instead of being called "comesBefore" and returning a boolean.

All returning the instruction does is make the logic of the caller more confusing :)

lib/Analysis/AliasAnalysis.cpp
348 ↗(On Diff #30180)

While you are here, can you please document the function and its arguments properly?

lib/Analysis/CaptureTracking.cpp
196 ↗(On Diff #30180)

Errr, it looks like this duplicates the logic below, just to avoid the construction cost of the object all the time?

If so, is there a reason you don't just new and delete one?

lib/Analysis/NumberedBasicBlock.cpp
71 ↗(On Diff #30180)

I have to admit i'm not a fan of treating the unsigneds like booleans values.
If you are going to do this, can you use explicit compares or something?

Or at least, explain the logic of all of this?

It only works because lookup default constructs values :)

At the very least, i would add a comment as to what this block of code is doing, like

"First we lookup the instructions. If they don't exist, lookup will give us back zero. If they both exist, we compare the numbers. Otherwise, if NA exists and NB doesn't, it means NA must come before NB because we would have numbered NB as well if it didn't. The same is true for NB. If it exists, but NA does not, NA must come after it.

If neither exist, we need to number the block".

(Note also that the last line of code would make more sense if the function was comesBefore or something, or if it returned a boolean)
Then it would be return comesBefore(A, B))

hfinkel edited edge metadata.Jul 25 2015, 4:00 PM

In the same testcase used in r240560, compile time goes down from 2min to 30s.

Nice!

include/llvm/Analysis/NumberedBasicBlock.h
10 ↗(On Diff #30180)

defines NumberedBasicBlock class -> defines the NumberedBasicBlock class

14 ↗(On Diff #30180)

NumberedBasicBlock lazily built after -> NumberedBasicBlock is lazily built [on|off of|around]

lib/Analysis/NumberedBasicBlock.cpp
15 ↗(On Diff #30180)

NumberedBasicBlock lazily built after -> NumberedBasicBlock is lazily built [on|off of|around]

37 ↗(On Diff #30180)

Indentation looks off here.

70 ↗(On Diff #30180)

So it seems you start numbering from 1 (that should be in a comment somewhere). Then, for every instruction you've not already visited you insert an implicit 0 entry in the map for it. You then replace this entry with another number when you call find. I'd rather we not insert implicit entries, instead, just test the iterators.

auto NAI = NumberedInsts.find(A);
auto NBI = NumberedInsts.find(B);

if (NAI != NumberedInsts.end() && NBI != NumberedInsts.end()) {
  ...

with that done, you can start numbering from 0, which I think makes more sense in general.

71 ↗(On Diff #30180)

Otherwise, if NA exists and NB doesn't, it means NA must come before NB because we would have numbered NB as well if it didn't. The same is true for NB. If it exists, but NA does not, NA must come after it.

Does this embed an assumption on the relative orderings of the calls to the function by the client. What if I start querying in the middle of the block somewhere using some previously-visited instruction and another I've not yet visited? I understand why CaptureTracking, which is following use/def chains walking from defs to uses, does not do this, but as a general utility, we either need to be more careful, or very carefully document the assumptions.

bruno added a comment.Jul 28 2015, 7:53 AM

Thanks Hal and Daniel for the review, really appreciated your feedback.

include/llvm/Analysis/NumberedBasicBlock.h
34 ↗(On Diff #30180)

Right. Done!

43 ↗(On Diff #30180)

comesBefore + retuning bool seems like a better approach indeed!

lib/Analysis/CaptureTracking.cpp
196 ↗(On Diff #30180)

OK

lib/Analysis/NumberedBasicBlock.cpp
70 ↗(On Diff #30180)

OK

71 ↗(On Diff #30180)

If you're using some previously-visited instruction (call it A) and another you've not yet visited (B), it's just like Daniel explained in his comment; either B is already cached (when we first looked up A) if it was somewhere between BB.begin() and A, in this case it B comesBefore A. If B isn't cached, it means it must come after. Doesn't matter if you start querying in the middle of the block, if it's not in the map, it will search from BB.begin() (or resume from the last position it scanned) until it finds either A or B. Let me know if I didn't get your question right :-)

bruno updated this revision to Diff 30817.Jul 28 2015, 7:54 AM
bruno edited edge metadata.

Updated a new version of the patch!

hfinkel accepted this revision.Jul 28 2015, 12:39 PM
hfinkel edited edge metadata.

LGTM.

include/llvm/Analysis/CaptureTracking.h
46 ↗(On Diff #30817)

capture tracker queries -> capture-tracker queries

lib/Analysis/AliasAnalysis.cpp
347 ↗(On Diff #30817)

instruction ordering queries -> instruction-ordering queries

lib/Analysis/NumberedBasicBlock.cpp
71 ↗(On Diff #30180)

Indeed, I re-read the code and now I understand. Sorry for being bothersome about this. For some reason, I thought that find(A, B) might only number the instructions in between A and B. But it always numbers from the beginning until A or B.

This revision is now accepted and ready to land.Jul 28 2015, 12:39 PM
This revision was automatically updated to reflect the committed changes.