Building MemorySSA gathers alias information for Defs/Uses.
Store and expose this information when optimizing uses (when building MemorySSA),
and when optimizing defs or updating uses (getClobberingMemoryAccess).
Current patch does not propagate alias information through MemoryPhis.
Details
Diff Detail
- Build Status
Buildable 11924 Build 11924: arc lint + arc unit
Event Timeline
lib/Analysis/MemorySSA.cpp | ||
---|---|---|
277 | It is possible for a call to must-alias a single thing, no? | |
303 | So this is going to double the number of alias calls, unfortunately. I suspect it will make building significantly slower, since roughly *all* time is spent there. Do you have perf numbers to say one way or the other? (unfortunately, ->alias is not const, so it will call it again) Or
| |
2136 | I'm a little concerned about saying we must-alias the live on entry def. LiveOnEntry is really equivalent to "something before this function" not "one specific thing". Thoughts? |
Thanks for the patch!
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
268 | If we're going to call resetOptimized() outside of tests, we should probably remove this bit of the comment. | |
288 | Should be. In isOptimized(), we check to see if OptimizedID is the ID of the defining access. If we're setting the operand to something else, we should observe a different ID. IIRC, this was necessary to make things like RAUW work without us having to manually deoptimize everything. If that's broken somehow, we shouldn't be papering over it here. | |
298 | Nit: Can we pack this in a PtrIntPair with MemoryInst? | |
lib/Analysis/MemorySSA.cpp | ||
237–251 | I'd mildly prefer that we return a struct { bool IsClobber, IsMustAlias; }; or similar from this instead. That way, it's more clear what we want to hand back for FoundMustAlias in all cases. (Moreover, there are quite a few paths in this function that don't try to set FoundMustAlias. It doesn't seem that's intentional.) | |
296–315 | Same "please make this part of the return value" nit | |
298 | Nit: no else after if (...) return ...; | |
900 | Do we need to set Q.FoundMustAlias here, as well? | |
2136 | I was thinking this could be solved by using "Clobber" in the name instead of "Alias", but I don't know if "Clobber" would imply something else that's subtly wrong. That said, it may be a better idea to just have passes that don't care about MustAlias vs LiveOnEntry to roll their own helper: bool isMustAlias(const MemoryAccess *MA) { return MA->definingIsMustAlias() || MA->getDefiningAccess() == MSSA->getLiveOnEntryDef(); } ...So we can have isMustAlias just mean isMustAlias :) |
Updates - part1.
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
288 | Got it! Removing this, keeping above comment. | |
lib/Analysis/MemorySSA.cpp | ||
237–251 | I did not mean to set it on all paths, since FoundMustAlias should not be modified for NoAlias. I missed the case when it needs to reset to May. Updating to return pair, where isMustAlias should be ignored if !IsClobber. | |
277 | That's true. I didn't add that because I didn't see how we would use the must info here. If we know the call does not modify and it's a must alias, then we could consider some transformation (read: promotion). In practice, we won't promote calls, so keeping FoundMustAlias = false (i.e. may) should be conservative. | |
303 | Ack, looking into how to best address this one. | |
900 | Intended to keep the default (=false) for Phis. | |
2136 | Right now definingIsMayAlias and definingIsMustAlias are opposites. Since an optimized access skipped over NoAlias. I was planning to couple isOptimized() and definingIsMustAlias() to determine if a May alias was found. Must with LOE is equivalent to "The defining access is not a MayAlias".
Thoughts? |
lib/Analysis/MemorySSA.cpp | ||
---|---|---|
277 | I would do the right thing for calls, IMHO, if we can. While LICM may not promote them, PRE definitely does (and GCC tests that it does). This is true even through function pointers. double f(double a) { double b; double c,d; double (*fp) (double) __attribute__ ((const)); double (*fp) (double) __attribute__ ((const)); /* Partially redundant call */ if (a < 2.0) { fp = sin; c = fp (a); } else { c = 1.0; fp = cos; } d = fp (a); return d + c; } the fp call will get pushed into the else branch. |
I'd reuse the type we already have to express alias results.
While you don't care about partial alias, it makes all code have the same meaning for the same thing :)
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
258 | Why not just have definingAliasType() and use the AliasResult type here? Then this looks like any other aliasing code. | |
281–286 | IMHO, setDefiningAliasType(AliasResult) | |
285 | Take an AliasResult AliasType = MayAlias) | |
lib/Analysis/MemorySSA.cpp | ||
241 | Just use AliasResult for the second part here. (I'd suggest using ModRefType for the first but i believe nothing wants it right now) |
Use AliasResult to store alias information with the defining type.
Use MRI_Must bit to avoid double queries to AA.
The concept looks interesting to me. I'll let the experts review the patch, though (I just skimmed over it).
Out of curiosity, what are the clients you envision could use this functionality?
I can imagine that we can do store forwarding with a mustalias MemUse if stored size >= load size. Also, a mustalias MemDef can kill the previous MemDef if it has no other uses (2 stores to same location).
Are there any other uses cases? Can it be used to simplify more complex optimizations? Even better, are there missing blocks that would be needed to simplify these optimizations? (tricky question; just wondering if you have some thoughts on that).
Please excuse my academic curiosity :)
An immediate usecase I have in LICM, is in very large testcases where I want to rely on MemorySSA's threshhold for optimizing MemUses.
I need to use getDefiningAccess() on a Use and query if the result given is May_Alias or Must_Alias. If the Use is not optimized, the result will always be MayAlias. If optimized, it may be May or Must.
Alias info is needed to avoid an additional AA.alias call when creating mssa alias sets for promotion.
It can certainly be used for more complex optimizations, aliasing between MemDefs is the most interesting to me. Since in MemorySSA all stores clobber all stores (until optimized), must alias info could be used to shortcut def alias chains. AFAIK this is done for MemUses, I don't think it's done for MemDefs.
Overall approach looks good to me. Just a few naming/refactoring nits and I'm happy to stamp this. Sorry for taking so long to get back to it
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
258 | Is this the type of the defining access, or of the optimized access? (They're identical for Uses, but not for Defs) I assume it's the latter, but if not, the rest of this comment is just noise. :) In a perfect world, I think the right thing here would be to hide this and have the walker return it as part of getClobberingMemoryAccess(). I also realize that we have workarounds in place because MSSA's walker takes ~forever in some cases, so this 'ideal' API probably won't work so well yet. :( So, as a compromise, might it be good to rename this getOptimizedAccessType(), with a comment that it might disappear and become part of the walker's return value instead? | |
281–286 | Continuing with my assumption that AliasWithDefining is talking about the optimized access, can we rename to setOptimizedAccessType? | |
298 | ping :) | |
lib/Analysis/MemorySSA.cpp | ||
265 | Nit: parens are redundant | |
540 | Nit: no else after a return | |
2106 | Nit: reflow this comment to 80-cols, please. (In vim, gq will do what you want. Otherwise, joining the lines + running clang-tidy should split it up properly.) | |
2136 | (So... I had "That WFM. If we decide to go that way, please also add a comment explaining what it means if definingIsMayAlias returns false." that I never submitted. Is this thread still relevant? I don't see definingIsMayAlias anywhere but here...) | |
unittests/Analysis/MemorySSA.cpp | ||
1067 | Nit: it looks like (?) we're iterating over the same list in the same order in the other two loops. Can we pop it in a local array or initializer_list or something, then use that as the container for these loops? |
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
298 | Done, using 3 bits, 2 for AliasResult and 1 for None. | |
lib/Analysis/MemorySSA.cpp | ||
2136 | I used dberlin's advice to "use the AliasResult type", so the only accessor now is definingAccessType() renamed to getOptimizedAccessType(). This returns an AliasResult. The setter also gets an AliasResult. |
A bundle of nitpicks for you. LGTM assuming those get addressed.
Thanks for working on this!
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
300 | nit: newFunctionsShouldStartWithALowerCaseLetter :) | |
300 | nit: static? | |
303 | Nit: (int)OAR.getValueOr(4);? Or does that require awkward casting? If it does need additional casting, I'm happy with this as-is. | |
306 | Same casing + static nits | |
307 | Should this accept 6 and 7? I thought the values were {May,Must,Partial,No}Alias (for 0-3) and None (for 4) | |
309 | Nit: if ( | |
lib/Analysis/MemorySSA.cpp | ||
2136 | SGTM. |
Address comments.
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
303 | The value given as OR is expected to be of the same type inside the Optional, in this case AliasResult. I think it's not a casting issue, but a correctness one; casting the 4 to AliasResult is UB. |
include/llvm/Analysis/MemorySSA.h | ||
---|---|---|
303 | Ick, yeah, good call. WFM! |
Why not just have definingAliasType() and use the AliasResult type here?
Then this looks like any other aliasing code.