This is an archive of the discontinued LLVM Phabricator instance.

[LVI] Cleanup/unify cache access
ClosedPublic

Authored by nikic on Mar 25 2020, 10:55 AM.

Details

Summary

This patch combines the "has" and "get" parts of the cache access. getCachedValueInfo() now both sets the BBLV return argument, and returns whether the value was found.

Additionally, the management of the work stack is now integrated into getBlockValue(). If the value is not cached yet, we try to push to the stack (and return false, indicating that we need to solve first), or return overdefined in case of a cycle.

These changes a) avoid a duplicate cache lookup for has & get and b) ensure that the logic is uniform everywhere. For this reason this change is also not quite NFC, because previously overdefined values from the cache, and overdefined values from a cycle received slightly different treatment in some places.

Diff Detail

Event Timeline

nikic created this revision.Mar 25 2020, 10:55 AM
Herald added a project: Restricted Project. · View Herald TranscriptMar 25 2020, 10:55 AM
fhahn added a subscriber: fhahn.Apr 5 2020, 5:22 AM
fhahn added inline comments.
lib/Analysis/LazyValueInfo.cpp
212 ↗(On Diff #252625)

Would it make sense to return Optional<ValuelLatticeElement> instead? IMO that would make things a bit more explicit (and personally I think it's easier to follow than using 'out' parameters)

nikic marked an inline comment as done.Apr 5 2020, 7:04 AM
nikic added inline comments.
lib/Analysis/LazyValueInfo.cpp
212 ↗(On Diff #252625)

I'm a bit fan of Option(al) ... in Rust. Unfortunately, the ergonomics of Optional in C++ really leave something to be desired :(

LVI is mostly based around the out parameter + bool return pattern, with only one part using Optional returns: https://github.com/llvm/llvm-project/blob/9e1455dc236252a60066c312c6d2e8a7ed66f609/llvm/lib/Analysis/LazyValueInfo.cpp#L1047-L1051

This kind of code is (imho) rather ugly and inefficient (in this example, it will result in an entirely unnecessary copy of the range).

Possibly I'm just missing some kind of standard pattern for this? I wasn't able to find anything on this, not even in C++17.

Another possibility would be to add another state to ValueLatticeElement like unresolved for this purpose. I tried using unknown for this initially, because I thought it would not occur inside the LVI cache, but it does occur for values coming from unreachable blocks.

fhahn added inline comments.Apr 5 2020, 11:15 AM
lib/Analysis/LazyValueInfo.cpp
212 ↗(On Diff #252625)

I'm a bit fan of Option(al) ... in Rust. Unfortunately, the ergonomics of Optional in C++ really leave something to be desired :(

Unfortunately there's no direct equivalent for Rust's if let Some(m) = &MaybeFoo {} which lets you unpack the value from the optional, if it has one as far as I am aware. Probably the next best thing would be to use if (Optional<ConstantRange> CR = getOptionalCR()) { and use *CR/CR.getValue() in the if body. Using auto in the if that's just one character more :)

This kind of code is (imho) rather ugly and inefficient (in this example, it will result in an entirely unnecessary copy of the range).

Are you referring to the the explicit copy ConstantRange LHSRange = LHSRes.getValue();? That's not necessary, getValue returns a reference. There's the copy/move of the optional result from callee to caller of course, but ideally the compiler should be able to remove them I guess, as long as it can inline things. In some cases, it might even save some work, as you do not have to unconditionally construct a ValueLatticeElement in the caller.

Possibly I'm just missing some kind of standard pattern for this? I wasn't able to find anything on this, not even in C++17.

I don't think there is much potential for improvement here, as it is just a regular type and the if (auto X = ..) syntax relies on the bool operator of the result :(

LVI is mostly based around the out parameter + bool return pattern, with only one part using Optional returns: https://github.com/llvm/llvm-project/blob/9e1455dc236252a60066c312c6d2e8a7ed66f609/llvm/lib/Analysis/LazyValueInfo.cpp#L1047-L1051

Right. Conceptually it seems Optional would be a good fit here, but if the existing code uses the other pattern the incentive for change is not that big I guess.

lebedev.ri resigned from this revision.Apr 8 2020, 11:46 PM

No idea who is comfortable/qualified doing LVI reviews, but that isn't me.

nikic marked an inline comment as done.Apr 9 2020, 12:19 PM
nikic added inline comments.
lib/Analysis/LazyValueInfo.cpp
212 ↗(On Diff #252625)

I just gave the Optional conversion a try, and it looks a lot nicer than I expected: https://gist.github.com/nikic/f437fc7f667e433f462232aeb5272bc1

I'd like to apply this as a NFC cleanup after this patch though, as has to touch a lot more code.

fhahn added inline comments.Apr 11 2020, 1:24 PM
lib/Analysis/LazyValueInfo.cpp
212 ↗(On Diff #252625)

SGTM, thanks!

1000 ↗(On Diff #252625)

It looks like the original code checks for the block value again after pushing it. I think the new code skips the check. Not sure if it actually matters though?

1523 ↗(On Diff #252625)

This case seems to be gone in the new code? Not sure if it matters much in practice?

nikic marked 2 inline comments as done.Apr 11 2020, 2:12 PM
nikic added inline comments.
lib/Analysis/LazyValueInfo.cpp
1000 ↗(On Diff #252625)

pushBlockValue() does not change the result of hasBlockValue() (it pushes to the work stack, but does not modify the cache). The duplicate hasBlockValue() call here was to handle the case where pushBlockValue() returned false (and thus did not early exit above).

There is indeed a change in behavior here, this is one of the "not quite NFC" parts mentioned in the summary: Previously the case where the value is assumed overdefined due to a cycle would always result in a full range here, while now it will be treated like other overdefined values by trying to perform the assume intersect first.

1523 ↗(On Diff #252625)

Right, this is the second "not quite NFC" part. If we assume an overdefined block value due to a cycle, this previously did not perform the assume intersections below, while now it does.

In both cases, this may improve analysis results in some edge cases, though I doubt it happens in practice. It could also make analysis more expensive, but at least I did not see any compile-time regressions.

fhahn accepted this revision.Apr 16 2020, 8:30 AM

LGTM, thanks for clarifying the not quite NFC bits. Sounds reasonable and safe. I also checked test-suite & SPEC, there are now binary changes with this patch on -O3 & LTO. But it would be good to wait a day or so with committing, in case there are additional thoughts.

This revision is now accepted and ready to land.Apr 16 2020, 8:30 AM
This revision was automatically updated to reflect the committed changes.