This is an archive of the discontinued LLVM Phabricator instance.

An Assumption-Tracking Pass
ClosedPublic

Authored by hfinkel on Jul 27 2014, 11:13 PM.

Details

Summary

This patch adds an immutable pass, AssumptionTracker, which keeps a cache of @llvm.assume instructions within a module. It uses callback value handles to keep stale functions and intrinsics out of the map, and it relies on any code that creates new @llvm.assume calls to notify it of the new instructions. The benefit is that code needing to find @llvm.assume intrinsics can do so directly, without scanning the function, thus allowing the cost of @llvm.assume handling to be negligible when none are present.

I apologize ahead of time: Some of the patches in this series now depend on each other in odd ways. You probably can't compile them unless you apply them all.

Diff Detail

Event Timeline

hfinkel updated this revision to Diff 11928.Jul 27 2014, 11:13 PM
hfinkel retitled this revision from to An Assumption-Tracking Pass.
hfinkel updated this object.
hfinkel edited the test plan for this revision. (Show Details)
hfinkel added reviewers: chandlerc, reames, atrick.
hfinkel added a subscriber: Unknown Object (MLST).
atrick edited edge metadata.Jul 30 2014, 8:20 PM

(1) A high-level comment, which may have already been discussed: Rather that eagerly scanning for all callbacks whenever the first assumption is registered for a function, why not do this only when the assumptions are first queried? This may not be very important but seems like a pure win, especially when we have passes that are called repeatedly but may not need to actually query the analysis.

(2) I like that you updated passes the clone instructions to invalidate the cache. But this is not robust. Unfortunately, we don't have a hook for cloning instructions (we probably should). To make up for it, I think you at least need to write a verifier for this analysis, which should be easy.

Otherwise LGTM.

include/llvm/Analysis/AssumptionTracker.h
42–43

This comment looks copy-pasted.

79–80

I don't like this FIXME because it implies a design for concurrency that has not even been formally proposed and would require FIXMEs all over the code.

lib/Analysis/AssumptionTracker.cpp
29

Put 'this' in quotes to clarify meaning.

reames edited edge metadata.Aug 1 2014, 3:45 PM

Er, it looks like this patch is missing some of the changes? I don't see AT being piped through all the passes you're trying to use it in?

Overall approach seems reasonable.

include/llvm/Analysis/AssumptionTracker.h
12

Comment could be improved. Specifically, why are they tracked? What is the actually mapping?

Suggestions: pass that keeps track of @llvm.assume intrinsics found in each function of the module. As a result, scanning for assumes is quite cheap and can be used freely by the optimizers.

31

Order wise, I'd reverse this. Interface at the top, details at the bottom. What are most users going to want to find?

91

Naming wise, this is used to force a recalculation, not to remove intrinsics. Maybe a name like "forgetCachedAssumptions" might make that clearer?

102

Might be worth adding a couple of filter ranges here. e.g. dominating_assumptions(F, I), or entry_assumptions(F).

Putting them here would simplify implementation changes later if we wanted to tune for these.

lib/Analysis/AssumptionTracker.cpp
57

I might use a inst_iterator here. Also, I'm not a huge fan of the match notation for such trivial matches, but that's purely stylistic.

71

Adding a check that instruction belongs to a BB might be useful.

75

The choice to scan here is non-obvious. Doing so avoids a rescan latter, but is that valuable?

lib/Transforms/InstCombine/InstCombineCalls.cpp
1005

This part seems bug prone. Don't we already have hook into the builder which catches all adds to the worklist? What about using that mechanism to do assumption recognition?

lib/Transforms/Scalar/LoopUnswitch.cpp
828

I may be missing the obvious here, but where did this AT come from? I don't see it being piped through the class.

chandlerc edited edge metadata.Aug 12 2014, 8:07 PM

Generally I agree with Andy's high-level comment about populating the cache lazily. A few minor comments below. Otherwise, I think this makes sense given the current pass infrastructure. I look forward to this kind of thing getting *much* easier to design in the new PM world.

include/llvm/Analysis/AssumptionTracker.h
33

I wouldn't bother with the 'AT' prefix for a nested class.

57–59

Why do you support building these for instructions without parents?

79–80

Agreed.

81–83

Rather than storing the CallHandleSets by value, kep a mapping to unique_ptr<>s to them? That would in turn make it more reasonable to use a small-size optimized container in addition to reducing the size of the top level map.

96–98

I don't think these section comments are really adding much value. It's all pretty clear.

119

Does this actually release memory though?

hfinkel added inline comments.Aug 12 2014, 10:00 PM
include/llvm/Analysis/AssumptionTracker.h
57–59

Hrmm... maybe this is not needed, IIRC it comes from.... Currently, in AssumptionTracker::ATCallCallbackVH::deleted() I have this:
I->second.erase(cast<CallInst>(getValPtr()))
this causes a new temporary ATCallCallbackVH to be created from getValPtr() in order to remove *this from the map. getValPtr() has already been removed from its parent function, and so it has no parent, which is why this was needed.

Regarding the scanning-on-register, the rationale is that:

  • It keeps the implementation simple. This way we have an invariant: If a function is in the cache then its set of assumptions is complete. If we don't scan on registration then we could have partial sets in the cache, and we need to keep track of that. Obviously, that could be done, but I'd rather not add the extra complexity unless there is a clear benefit.
  • Functions with assumptions will likely be scanned at some point, and because the pass is never invalidated (the analysis is immutable), this is not wasted work (except perhaps that the cache in invalidated when we inline a callee, but that's a separate issue that will be fixed regardless).

A verification function has been added; because the pass manager currently does not verify immutable passes, I added a call to the verification inside the pass's finalization function (this does not provide a huge amount of coverage, but it is something).

include/llvm/Analysis/AssumptionTracker.h
12

Done.

31

Unfortunately, this required a number of forward declarations, and I'm not sure it is worthwhile. Many other analysis classes have their interface at the bottom too for this reason.

33

Done.

42–43

Indeed. Fixed.

57–59

Yes, can be cleaned up (and is in the next version).

79–80

Done.

81–83

Done.

Regarding the small-size-optimized container, we have a SmallDenseMap, however it is not clear to me that the key-set is actually going to be small. It will almost certainly contain all non-empty functions in the module. Is that small?

91

Done.

96–98

Removed.

102

I agree, but let's do this as-needed.

119

Only sometimes ;) -- shrink_and_clear() always does.

lib/Analysis/AssumptionTracker.cpp
29

Done.

57

I'm not sure that buys us anything, the range-based for loop is pretty simple.

71

Done.

75

Documented. (Also see general comment).

lib/Transforms/InstCombine/InstCombineCalls.cpp
1005

Thanks for pointing this out, I did not realize that IRBuilder had that hook. Done.

lib/Transforms/Scalar/LoopUnswitch.cpp
828

Yep; thus the comment about the patch interdependencies when I posted.

hfinkel updated this revision to Diff 13272.Sep 4 2014, 9:53 AM
hfinkel edited edge metadata.

Updated per reviewer comments (and rebased).

atrick accepted this revision.Sep 4 2014, 8:11 PM
atrick edited edge metadata.
This revision is now accepted and ready to land.Sep 4 2014, 8:11 PM
hfinkel closed this revision.Sep 7 2014, 6:17 AM

r217334, thanks!