Here's my second try at making @llvm.assume processing more efficient. My previous attempt, which leveraged operand bundles, r289755, didn't end up working: it did make assume processing more efficient but eliminating the assumption cache made ephemeral value computation too expensive. This is a more-targeted change. We'll keep the assumption cache, but extend it to keep a map of affected values (i.e. values about which an assumption might provide some information) to the corresponding assumption intrinsics. This allows ValueTracking and LVI to find assumptions relevant to the value being queried without scanning all assumptions in the function. The fact that ValueTracking started doing O(number of assumptions in the function) work, for every known-bits query, has become prohibitively expensive in some cases.
Details
Diff Detail
- Repository
- rL LLVM
Event Timeline
I'm going to try this on the testcases where your previous approach regressed and review again. In the meanwhile, some early comments.
include/llvm/Analysis/AssumptionCache.h | ||
---|---|---|
125–128 ↗ | (On Diff #83570) | I prefer using the ternary operator here, but up to you. |
lib/Analysis/AssumptionCache.cpp | ||
39–42 ↗ | (On Diff #83570) | I see why you need this, but, can we think of a way of sharing (at least partially) the code between this and ValueTracking? |
78–82 ↗ | (On Diff #83570) | I would add a comment on this matchers (and the others). |
Can you provide an explanation at a high level for why you're going with this approach rather than having the AC for ephemeral value finding and operand bundles for affected values? Essentially, why not just use both previous approaches rather than adding the affected values to the AC? (I'm not questioning what you've done here, just trying to understand the thought process behind it.)
There are potential downsides to the operand-bundle approach. First, values in operand bundles are, by default, assumed to be captured, alias, etc. which could cause undesirable side effects for pointer-typed values. This could have been fixed, but there is another issue which is that adding all of the extra uses on intermediate values could inhibit simplification of more--complex conditions because of hasOneUse interference. Perhaps a minor problem, but a less tractable one. Also, depending on how you look at it, having the affected values "cached" in the IR, and having a dependence on InstCombine for them to work even for other passes, was suboptimal. As a result, I think that if we can't get rid of the assumption cache, we should use it.
lib/Analysis/AssumptionCache.cpp | ||
---|---|---|
39–42 ↗ | (On Diff #83570) | I'm worried about this too. I tried to make the code here as general as possible, without casting too wide a net, to mitigate this as much as possible. When I first started working on this, I tried to think of a way to share the matching patterns between ValueTracking and here (perhaps with some small generalization to pick up things that LVI needs that wouldn't otherwise be covered by ValueTracking's patterns. This was the best thing I could come up with. Part of the problem is that, in ValueTracking, we're matching against specific values. Here, we want to find all potentially-matching values. If you can think of a better way, I'l all ears :-) |
78–82 ↗ | (On Diff #83570) | You mean like this? // A & B or A | B or A ^ B. |
lib/Analysis/AssumptionCache.cpp | ||
---|---|---|
39–42 ↗ | (On Diff #83570) | Yeah, unfortunately, I don't think there's an easy way to generalize. The best we can do here is trying to be very careful when reviewing new changes (and I think your comment in ValueTracking is pretty much needed to make sure we don't forget about this in a month or two). |
78–82 ↗ | (On Diff #83570) | Yes, exactly. |
lib/Analysis/AssumptionCache.cpp | ||
---|---|---|
34 ↗ | (On Diff #83574) | emplace(first, second) or insert({first, second}) |
lib/Analysis/AssumptionCache.cpp | ||
---|---|---|
59–63 ↗ | (On Diff #83578) | They are assigned in m_Value (as in the example at the very top of include/llvm/IR/PatternMatch.h). You might be thinking of m_Specific, which matches a specific provided value. |
include/llvm/Analysis/AssumptionCache.h | ||
---|---|---|
57 ↗ | (On Diff #83578) | No, DenseMap won't work if this is explicit (because we're using Value*'s KeyInfo). |
Sorry for the delay, Hal.
I just checked and this doesn't regress the cases your previous change regressed, so we should be good on that side.
I somehow share Dan's feeling that this could be solved with an infrastructural changes rather than caching, but I don't feel to be in a position to hinder progress without a concrete/implemented alternative.
For those wondering, I mean http://lists.llvm.org/pipermail/llvm-commits/Week-of-Mon-20170102/416193.html
@dberlin , @chandlerc , et al. does anyone object to me committing this solution at this point? I'm obviously happy to help replace it later with something based on an extended SSA form once we figure out how that should work. In the mean time, this fixes the compile-time problems, which users are certainly hitting, in a fairly transparent way.
I think you should land this, at the very least for 4.0. But I'm not thrilled about it. It seems pragmatic and to address a very real set of problems, but as we've discussed several times here and elsewhere, I look forward to more principled or just less invasive approaches.
Anyways, LGTM.
Hi, I just wanted to say I applied your patch to the top of trunk, built it, and then used it to compile the example that previously resulted in a big compile time increase. I am happy to report that this patch did not meaningfully increase the compile time.