[llvm] [MemCpyOpt] Forward `memcpy` based on the actual copy memory location. (PR #87190)
Nikita Popov via llvm-commits
llvm-commits at lists.llvm.org
Tue Jul 9 02:24:37 PDT 2024
================
@@ -1124,28 +1125,67 @@ bool MemCpyOptPass::performCallSlotOptzn(Instruction *cpyLoad,
bool MemCpyOptPass::processMemCpyMemCpyDependence(MemCpyInst *M,
MemCpyInst *MDep,
BatchAAResults &BAA) {
- // We can only transforms memcpy's where the dest of one is the source of the
- // other.
- if (M->getSource() != MDep->getDest() || MDep->isVolatile())
- return false;
-
// If dep instruction is reading from our current input, then it is a noop
- // transfer and substituting the input won't change this instruction. Just
- // ignore the input and let someone else zap MDep. This handles cases like:
+ // transfer and substituting the input won't change this instruction. Just
+ // ignore the input and let someone else zap MDep. This handles cases like:
// memcpy(a <- a)
// memcpy(b <- a)
if (M->getSource() == MDep->getSource())
return false;
- // Second, the length of the memcpy's must be the same, or the preceding one
+ // We can only optimize non-volatile memcpy's.
+ if (MDep->isVolatile())
+ return false;
+
+ int64_t MForwardOffset = 0;
+ const DataLayout &DL = M->getModule()->getDataLayout();
+ // We can only transforms memcpy's where the dest of one is the source of the
+ // other, or they have an offset in a range.
+ if (M->getSource() != MDep->getDest()) {
+ std::optional<int64_t> Offset =
+ M->getSource()->getPointerOffsetFrom(MDep->getDest(), DL);
+ if (!Offset || *Offset < 0)
+ return false;
+ MForwardOffset = *Offset;
+ }
+
+ // The length of the memcpy's must be the same, or the preceding one
// must be larger than the following one.
- if (MDep->getLength() != M->getLength()) {
+ if (MForwardOffset != 0 || (MDep->getLength() != M->getLength())) {
auto *MDepLen = dyn_cast<ConstantInt>(MDep->getLength());
auto *MLen = dyn_cast<ConstantInt>(M->getLength());
- if (!MDepLen || !MLen || MDepLen->getZExtValue() < MLen->getZExtValue())
+ if (!MDepLen || !MLen ||
+ MDepLen->getZExtValue() < MLen->getZExtValue() + MForwardOffset)
return false;
}
+ IRBuilder<> Builder(M);
+ auto *CopySource = MDep->getRawSource();
+ auto CleanupOnFailure = llvm::make_scope_exit([&CopySource] {
+ if (CopySource->use_empty())
+ cast<Instruction>(CopySource)->eraseFromParent();
----------------
nikic wrote:
This is dangerous when BatchAA is used, if the pointer is cached in AAQI and then later a new instruction is allocated with the same pointer.
I think it is safe here because we will only allocate more instructions after finishing all BatchAA queries, but we have to be careful if we want to do something like this in another place. Then we'd probably have to delay instruction removal until all transforms on an instruction finished.
https://github.com/llvm/llvm-project/pull/87190
More information about the llvm-commits
mailing list