This is an archive of the discontinued LLVM Phabricator instance.

[ValueTracking] Mark GEP operand as nonnull if the result was loaded or stored
AbandonedPublic

Authored by danlark on May 15 2021, 4:03 AM.

Details

Summary

Nonnull inbounds GEP cannot be obtained from null pointer. So I decided to mark the GEP operand as non null if this happens. This saves around 0.05%-0.1% of the binary size at Google and 0.06% of binary size of clang. I don't have commit rights. Danila Kutenin. kutdanila@yandex.ru

Diff Detail

Event Timeline

danlark created this revision.May 15 2021, 4:03 AM
danlark requested review of this revision.May 15 2021, 4:03 AM
Herald added a project: Restricted Project. · View Herald TranscriptMay 15 2021, 4:03 AM
danlark edited the summary of this revision. (Show Details)May 15 2021, 4:04 AM
lebedev.ri added inline comments.May 15 2021, 4:10 AM
llvm/lib/Analysis/ValueTracking.cpp
2134

Doesn't this cause endless recursion back into isKnownNonNullFromDominatingCondition()
for the very same value we've started with?
(and we happen to not deadloop just because of depth cutoff)

danlark updated this revision to Diff 345624.May 15 2021, 4:15 AM

Add gep after check test to test DominatorTree

danlark added inline comments.May 15 2021, 4:18 AM
llvm/lib/Analysis/ValueTracking.cpp
2134
lebedev.ri added inline comments.May 15 2021, 4:20 AM
llvm/lib/Analysis/ValueTracking.cpp
2134

Sure, but before that cutoff triggers, won't we waste time mutually recursing with no progress?

danlark added inline comments.May 15 2021, 4:20 AM
llvm/lib/Analysis/ValueTracking.cpp
2134

Not to compute the same values we may not go recursively and only look for constants, I believe this is the most popular case

danlark updated this revision to Diff 345625.EditedMay 15 2021, 4:31 AM

Don't compute isKnownNonZero for operand

danlark marked 2 inline comments as done.May 15 2021, 4:32 AM
danlark added inline comments.
llvm/lib/Analysis/ValueTracking.cpp
2134

Done.

danlark updated this revision to Diff 345631.May 15 2021, 6:13 AM

Fix clang-tidy warning

nikic added a comment.May 15 2021, 7:11 AM

I'm somewhat concerned about the general direction here. This looks through a GEP, but what about bitcasts? What about a bitcast of a GEP? A GEP of a bitcast? A longer chain of GEPs and bitcasts? We can't reasonably walk the whole use graph and this is really pushing the bounds of what it appropriate for a ValueTracking helper.

We have a more principled version of this optimization in LVI, which scans whole blocks for pointer dereferences and records their underlying objects, thus handling this in full generality. The only caveat is that LVI only uses this information to optimize the terminator instruction, because it does not store where exactly inside the block the first dereference occurs. If the motivation here is handling of non-terminator comparisons, then that might be a better avenue to explore?

llvm/lib/Analysis/ValueTracking.cpp
2132

How can a GEP not have pointer type?

2134

I don't get it. What is this isGEPKnownNonNull() check here for? Why do the GEP indices matter at all?

2135

This effectively raises the maximum uses walked from DomConditionsMaxUses to DomConditionsMaxUses^2, because you can walk that many GEPs, and then that many uses of each GEP.

Shouldn't this be counting towards the main NumUsesExplored, rather than a separate limit?

danlark updated this revision to Diff 345638.May 15 2021, 8:44 AM
danlark marked 2 inline comments as done.

Remove unnecessary checks

danlark updated this revision to Diff 345639.May 15 2021, 8:45 AM

Fix compilation issues

danlark marked an inline comment as done.May 15 2021, 8:56 AM

I'm somewhat concerned about the general direction here. This looks through a GEP, but what about bitcasts? What about a bitcast of a GEP? A GEP of a bitcast? A longer chain of GEPs and bitcasts? We can't reasonably walk the whole use graph and this is really pushing the bounds of what it appropriate for a ValueTracking helper.

We have a more principled version of this optimization in LVI, which scans whole blocks for pointer dereferences and records their underlying objects, thus handling this in full generality. The only caveat is that LVI only uses this information to optimize the terminator instruction, because it does not store where exactly inside the block the first dereference occurs. If the motivation here is handling of non-terminator comparisons, then that might be a better avenue to explore?

I was looking at LVI and haven't come up with anything meaningful, probably because I am not familiar with that part.

I was following more a statistical approach at Google -- what the users do more with pointers, it turned out that dereferencing + check and pointer arithmetic+deref+check are the most common ones by far. That said, I found that deref+check is already optimized in ValueTracking and I decided to do the same with pointer arithmetic, however, yes, it starts to be a little bloaty. Also GCC does these kind of things with C/C++ code consistently and llvm is far behind in tracking the (non)nullness of pointers. 0.1% in big binaries looks like a big low hanging win.

Currently I don't want to go to LVI if I have a choice.

I basically agree with @nikic here.
There is a huge number of low-hanging fruit like this,
but the approach just doesn't seem viable to me.

I would instead like to see the Attributor finally enabled,
there such things would be much more straight-forward.

Sorry for a very not helpful feedback.

danlark abandoned this revision.May 15 2021, 9:47 AM

I basically agree with @nikic here.
There is a huge number of low-hanging fruit like this,
but the approach just doesn't seem viable to me.

I would instead like to see the Attributor finally enabled,
there such things would be much more straight-forward.

Sorry for a very not helpful feedback.

Okay, thanks. Probably I am abandoning this then.