Page MenuHomePhabricator

[MemorySSA] Extend removeMemoryAccess API to optimize MemoryPhis.

Authored by asbirlea on Jan 24 2019, 3:31 PM.



EarlyCSE needs to optimize MemoryPhis after an access is removed and has
special handling for it. This should be handled by MemorySSA instead.
The default remains that MemoryPhis are *not* optimized after an access
is removed.

Once special handling is removed from EarlyCSE, the testcase that uses this
patch is: test/Transforms/EarlyCSE/memoryssa.ll.

Diff Detail


Event Timeline

asbirlea created this revision.Jan 24 2019, 3:31 PM

Thanks for this!

Is there a reason we don't want to have this behavior by default? ISTR we're pretty good about keeping Phis well-optimized, but could be wrong.

Please also include a test-case.

1108 ↗(On Diff #183417)

It looks like this may result in a use-after-free. Consider a CFG:

|  / \
| B   C
|/ \ /|
D<--E | 
\     /
 \   /
  \ / 

Control-flow flows down, except for the edge E -> D. (and there's an A->D edge that only looks somewhat awkward. :) )

All BBs are clear, though B has a Def in it. This Def would cause us to generate a Phi in E, D, and F.

So we remove that Def, remembering that the Phis in {D, E} are removal candidates. We check that E only has liveOnEntry first, it does, so we recurse on it. E's removal makes the Phi in D trivial, so we recurse on it, delete it, etc. We then land back at the top level of our stack and check onlySingleValue(D).

If you buy all of this, ISTM we could either fix it by hoisting the onlySingleValue check into the while (!MA->use_empty()) { loop (if the Phi only points to one Def, we're not going to find it otherwise...). I'm willing to bet that the number of cases where the same Phi points to the same value N times is pretty small.

Alternatively, we could takeVector(), completely filter out the Phis that aren't onlySingleValue, from that, and *then* recurse on those Phis.

asbirlea updated this revision to Diff 183665.Jan 25 2019, 5:10 PM
asbirlea marked 2 inline comments as done.

Address comment.

Thank you for the comments!

We may want this by default, yes. I think we currently simply didn't come across this, due to little usage. Only EarlyCSE needed it and added the special handling.
I would prefer to do the flag-flip separately though, in case it unearths some problematic usage.

There are already a few tests that cover this scenario: e.g., test/Transforms/EarlyCSE/memoryssa.ll.
(i.e. it has failures without this change and the special handling removed in EarlyCSE)

1108 ↗(On Diff #183417)

Still calling removeMemoryAccess recursively.
AFAICT we can reuse tryRemoveTrivialPhi and not pre-filter for onlySingleValue, but the vector will be TrackingVH since tryRemoveTrivialPhi will remove other phis in the vector being processed.
Both approaches work. Let me know if you prefer to reuse tryRemoveTrivialPhi.

I would prefer to do the flag-flip separately though, in case it unearths some problematic usage.


There are already a few tests that cover this scenario: e.g., test/Transforms/EarlyCSE/memoryssa.ll.

Please note that in your commit message when this is landed, and I'm OK without a formal test-case here.

1108 ↗(On Diff #183417)

I'd prefer to centralize this logic as much as reasonable, so I like the tryRemoveTrivialPhi approach. (I forgot that existed -- thanks for catching that :) )

If TrackingVH doesn't work as set keys (I don't think it does, but am happy to be proven wrong), we can probably split the SetVector into SmallVector<TrackingVH, ...> and SmallPtrSet<MemoryPhi *, ...>

asbirlea updated this revision to Diff 184184.Jan 29 2019, 2:52 PM

Address comment: reuse tryRemoveTrivialPhi.

asbirlea edited the summary of this revision. (Show Details)Jan 29 2019, 3:11 PM
asbirlea marked an inline comment as done.
1107 ↗(On Diff #184184)

nit: !PhisToCheck.empty(), please

1108 ↗(On Diff #184184)

Why is this a vector of TrackingVH?

I'm under the impression, though I haven't totally proven it to myself, that onlySingleValue(MP) implies that we'll never try to remove/RAUW/etc. MP in tryRemoveTrivialPhi, unless we're specifically calling tryRemoveTrivialPhi on MP. So, we shouldn't need to track anything here.

Happy to have this be an AssertingVH to assert that these nothing in this vector is dropped before we get to it, though.

asbirlea updated this revision to Diff 184346.Jan 30 2019, 11:53 AM
asbirlea marked 3 inline comments as done.

Address comments.

asbirlea added inline comments.Jan 30 2019, 11:55 AM
1108 ↗(On Diff #184184)

Oh, I meant to remove the onlySingleValue(MP).

If we're going to use tryRemoveTrivialPhi, which recurses internally, then it makes sense to actually let it remove more than a single MemoryPhi. Otherwise, we could just call removeMemoryAccess.

I meant to call tryRemoveTrivialPhi on the unfiltered list of MemoryPhis, and have that list be TrackingVH, since some may go away (there's a test in that triggers this).

Updated now.

LGTM after one nit is addressed.

If I'm just imagining that my nit is possible, I'm happy to have this patch submitted as-is.

Thanks again!

1108 ↗(On Diff #184346)

...But it can remove more than a single MemoryPhi, no? It'll traverse the users of the MemoryPhis we know we can remove (e.g. onlySingleValue ones); there may be many of these.

In any case, both approaches are functionally equivalent in my mind, and I have no preference either way, so this approach LGTM.

Nit: can we simplify to PhisToOptimize{PhisToCheck.begin(), PhisToCheck.end()}; and skip the loop below?

This revision is now accepted and ready to land.Jan 30 2019, 12:59 PM
asbirlea updated this revision to Diff 184370.Jan 30 2019, 2:25 PM

Address nit.
Changed to WeakVH after offline discussion.

Thank you for the review!

This revision was automatically updated to reflect the committed changes.