The majority of this change is sinking logic from instcombine into MemoryLocation such that it can be generically reused. If we have a call with a single analyzable write to an argument, we can treat that as-if it were a store of unknown size.
Merging the code in this was unblocks DSE in the store to dead memory code paths. In theory, it should also enable classic DSE of such calls, but the code appears to not know how to use object sizes to refine unknown access bounds (yet).
In addition, this does make the isAllocRemovable path slightly stronger by reusing the libfunc and additional intrinsics bits which are already in getForDest.
A couple ideas for follow up (which I don't plan to do):
- The more I look at this, the more I'm starting to think the capture handling is conservative. It feels like the write and no-return handling should be enough to disallow capture in the problematic cases. Maybe we can relax this? Or adjust inference rules to simply infer?
- We should be able to use known object size info in DSE per the added test cases.
- We should be able to remove empty lifetime start/end ranges in DSE, and thus kill off remaining alloca uses. Today, this will take another instcombine run. Adding the DSE support is most useful when we have two distinct live ranges, one live and one dead. DSE can recognize the dead one cheaply (even if large), where instcombine really can't.
I'm not sure the willreturn and nounwind checks really belong in here. We should try to separate the modelling of different effects, and MemoryLocation should only be modelling memory effects. The willreturn/nounwind checks can be done in the caller.
After all, the statement that only this one location is written remains true regardless of whether the call unwind or diverge, the latter only affect whether it can be removed.