This is an archive of the discontinued LLVM Phabricator instance.

[BasicAA] Avoid calling GetUnderlyingObject, when the result of a previous call can be reused.
ClosedPublic

Authored by amehsan on Jul 13 2016, 8:57 AM.

Details

Summary

This fixes the performance problem in PR27740. Also this patch includes code for a compile time opportunity in BasicAA.

Problem in 27740:

for the following pair :
%f35.pre-phi.i = phi float* [ %.pre6.i, %entry.if.else_crit_edge.i ], [ %.pre6.i, %land.lhs.true.if.else_crit_edge.i ]
%f11 = getelementptr inbounds %struct.s, %struct.s* %p1, i64 0, i32 1

where:
%.pre6.i = getelementptr inbounds %struct.s, %struct.s* %p1, i64 0, i32 3

We cannot prove that the pair is not aliased. This is because BasicAA first goes to aliasGEP, which recursively calls aliasCheck to look at aliasing for the base pointer of the GEP (with unknown size) and the phi node in the original pair. The result of that is a MayAlias. A number of alternative solutions have been discussed in this comment and also discussed with Hal.

The solution here, focuses on PHIs with a unique incoming value. This is very similar to what we do in stripPointerCasts, for GEP and some other cases, where we go past the current instruction, when all it does is hiding a pointer. I think potentially this functionality can be added to stripPointerCases, to look beyond phi nodes, but at this point I want to make this change limited, given the long conversations and investigations that we had about 27740.

Also, some of the recursive calls to aliasCheck, from alias[GEP|Select|PHI] pass one of the values for which GetUnderlyingObject is already called, again. So if we keep the underlying object that is already calculated around, we won't need to recompute it, given this is an expensive call.

I will add a test, by reducing the test case of 27740 and add to this.

Diff Detail

Event Timeline

amehsan updated this revision to Diff 63809.Jul 13 2016, 8:57 AM
amehsan retitled this revision from to [BasicAA] Strip phi nodes, when all incoming values are the same..
amehsan updated this object.
amehsan added reviewers: dberlin, hfinkel.
amehsan added a subscriber: llvm-commits.
amehsan updated this object.Jul 13 2016, 9:03 AM
dberlin edited edge metadata.Jul 13 2016, 9:10 AM
dberlin added a subscriber: dberlin.

This seems like a bad example.
For that example, the right solution is clearly to not generate a phi with
identical arguments.
Why not fix GVN to do that?

Given %f35.pre-phi.i = phi float* [ %.pre6.i, %entry.if.else_crit_edge.i ],
[ %.pre6.i, %land.lhs.true.if.else_crit_edge.i ]

%pre6.i must dominate this block, or else it would not be available through
both edges.

Thus, the phi is unnecessary, you could simply use %pre6.i instead, and you
would not have to add this code to BasicAA.

Given we generated this phi, we can simply not generate it :)

Do you have an example where that is not the case, and your patch to
BasicAA is needed?
(I would think EarlyCSE would eliminate all the above cases in input files
before other opts saw it)

Past that I'm not sure i believe this is the right thing to do, because
it's not clear to me where it logically ends and why.

IE if we next discover cases where all the phi arguments are redundant
bitcasts of the same value, do we add that?
What about more complex operations?

(aside from that, of course, i'm quite appreciative of speeding this up :P)

hfinkel added inline comments.Jul 13 2016, 5:08 PM
lib/Analysis/BasicAliasAnalysis.cpp
1427

Because GetUnderlyingObject is depth limited, as we walk up the use/def chain, we get closer to the real underlying object (in those somewhat-rare cases where we actually hit the depth cutoff). I'm quite happy to not do redundant work here, but we also don't want to regress on complex code either. I recommend trying something like this:

O1 = GetUnderlyingObject(O1 ? O1 : V1, DL, O1 ? 1 : MaxLookupSearchDepth).
amehsan added inline comments.Jul 13 2016, 5:31 PM
lib/Analysis/BasicAliasAnalysis.cpp
1427

Unless I am missing something, your suggested code, will change the behavior. MaxLookupSearchDepth is constant and does not change anywhere. So if we call aliasCheck with the exact same V1 again, we will call GetUnderlyingObject with exact same parameters. Given there has been no code change since the first call, the result will be exactly the same. O1 and O2 will be non-null only if we are passing exact same V1 (or V2) again to aliasCheck. So the proposed code change here, does not cause any change in the behavior.

@dberlin

I see two concerns in your comment:

Why not fix GVN to do that?

Before GVN this phi node has two different incoming values. At some point A in GVN, one of the incoming values change and we get the phi node in the example above. At some point B in the GVN, the phi node is removed. Somewhere between A and B, GVN makes an alias analysis query. So I think there is not really a problem in GVN to fix.

IE if we next discover cases where all the phi arguments are redundant
bitcasts of the same value, do we add that?
What about more complex operations?

I think eventually, this code should be part of stripPointerCasts that we call in lines 1403-1404. There we handle pointers that are hidden behind bitcasts and GEPs where indices are zero.

hfinkel edited edge metadata.Jul 13 2016, 6:14 PM

@dberlin

I see two concerns in your comment:

Why not fix GVN to do that?

Before GVN this phi node has two different incoming values. At some point A in GVN, one of the incoming values change and we get the phi node in the example above. At some point B in the GVN, the phi node is removed. Somewhere between A and B, GVN makes an alias analysis query. So I think there is not really a problem in GVN to fix.

On the one hand, we generally don't construct our analyses to deal with trivially-non-canonical IR. One could argue that if GVN expects to use analyses on its intermediate states, then those states need to follow the rules too. On the other hand, arbitrary uses of RAUW can result in these kinds of PHIs, so this is not really a GVN problem, and handling this reduces our phase-ordering sensitivities. While several passes will clean these up, the same is true for several other things that stripPointerCasts handles.

IE if we next discover cases where all the phi arguments are redundant
bitcasts of the same value, do we add that?
What about more complex operations?

I think eventually, this code should be part of stripPointerCasts that we call in lines 1403-1404. There we handle pointers that are hidden behind bitcasts and GEPs where indices are zero.

I agree. If we're going to do this at all, the code should be added to stripPointerCasts. I don't see a good reason to add it here. Please split out the functional change from the performance-related change, and put the functional change in stripPointerCasts so it can be discussed separately.

Please split out the functional change from the performance-related change, and put the functional change in stripPointerCasts so it can be discussed separately.

OK. I will change this patch to include only compile time speed up change. Then I will post another code review to include the look-past-the-phi change so we can discuss it separately.

amehsan updated this revision to Diff 63896.Jul 13 2016, 6:36 PM
amehsan retitled this revision from [BasicAA] Strip phi nodes, when all incoming values are the same. to [BasicAA] Avoid calling GetUnderlyingObject, when the result of a previous call can be reused..
amehsan edited edge metadata.

Separating two changes, so they can be discussed separately

Ping :)
This patch currently only includes the compile time improvement. @hfinkel Did I answer your question?

hfinkel accepted this revision.Aug 1 2016, 3:10 PM
hfinkel edited edge metadata.

Ping :)
This patch currently only includes the compile time improvement. @hfinkel Did I answer your question?

Yes, thanks, LGTM.

lib/Analysis/BasicAliasAnalysis.cpp
1427–1431

You can just say if (!O1) here, and similar below.

This revision is now accepted and ready to land.Aug 1 2016, 3:11 PM

Committed in 278519

amehsan closed this revision.Aug 12 2016, 1:25 PM