Skip to content

Commit ff821fb

Browse files
[MemCpyOpt] Drop dead memmove calls on memset'd source data
When a memmove happens to clobber source data, and such data have been previously memset'd, the memmove may be redundant.
1 parent d25e401 commit ff821fb

File tree

3 files changed

+48
-5
lines changed

3 files changed

+48
-5
lines changed

llvm/include/llvm/Transforms/Scalar/MemCpyOptimizer.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ class MemCpyOptPass : public PassInfoMixin<MemCpyOptPass> {
6666
BasicBlock::iterator &BBI);
6767
bool processMemSet(MemSetInst *SI, BasicBlock::iterator &BBI);
6868
bool processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI);
69-
bool processMemMove(MemMoveInst *M);
69+
bool processMemMove(MemMoveInst *M, BasicBlock::iterator &BBI);
7070
bool performCallSlotOptzn(Instruction *cpyLoad, Instruction *cpyStore,
7171
Value *cpyDst, Value *cpySrc, TypeSize cpyLen,
7272
Align cpyAlign, BatchAAResults &BAA,
@@ -85,6 +85,7 @@ class MemCpyOptPass : public PassInfoMixin<MemCpyOptPass> {
8585
bool performStackMoveOptzn(Instruction *Load, Instruction *Store,
8686
AllocaInst *DestAlloca, AllocaInst *SrcAlloca,
8787
TypeSize Size, BatchAAResults &BAA);
88+
bool isMemMoveMemSetDependency(MemMoveInst *M);
8889

8990
void eraseInstruction(Instruction *I);
9091
bool iterateOnFunction(Function &F);

llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ static cl::opt<bool> EnableMemCpyOptWithoutLibcalls(
6969
cl::desc("Enable memcpyopt even when libcalls are disabled"));
7070

7171
STATISTIC(NumMemCpyInstr, "Number of memcpy instructions deleted");
72+
STATISTIC(NumMemMoveInstr, "Number of memmove instructions deleted");
7273
STATISTIC(NumMemSetInfer, "Number of memsets inferred");
7374
STATISTIC(NumMoveToCpy, "Number of memmoves converted to memcpy");
7475
STATISTIC(NumCpyToSet, "Number of memcpys converted to memset");
@@ -1853,12 +1854,54 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
18531854
return false;
18541855
}
18551856

1857+
/// Memmove calls with overlapping src/dest buffers that come after a memset may
1858+
/// be removed.
1859+
bool MemCpyOptPass::isMemMoveMemSetDependency(MemMoveInst *M) {
1860+
MemoryUseOrDef *MemMoveAccess = MSSA->getMemoryAccess(M);
1861+
if (!MemMoveAccess)
1862+
return false;
1863+
1864+
BatchAAResults BAA(*AA);
1865+
MemoryAccess *FirstDef = MemMoveAccess->getDefiningAccess();
1866+
MemoryLocation SourceLoc = MemoryLocation::getForSource(M);
1867+
MemoryAccess *SourceClobber =
1868+
MSSA->getWalker()->getClobberingMemoryAccess(FirstDef, SourceLoc, BAA);
1869+
1870+
MemSetInst *MS = nullptr;
1871+
if (auto *Def = dyn_cast<MemoryDef>(SourceClobber))
1872+
MS = dyn_cast_or_null<MemSetInst>(Def->getMemoryInst());
1873+
1874+
// Limit the memset to be within the same basic block.
1875+
if (!MS || MS->getParent() != M->getParent())
1876+
return false;
1877+
1878+
// The destination buffer must have been memset'd.
1879+
if (!BAA.isMustAlias(MS->getDest(), M->getDest()))
1880+
return false;
1881+
1882+
// No clobbering writes in between.
1883+
if (writtenBetween(MSSA, BAA, SourceLoc, MSSA->getMemoryAccess(MS),
1884+
MemMoveAccess))
1885+
return false;
1886+
return true;
1887+
}
1888+
18561889
/// Transforms memmove calls to memcpy calls when the src/dst are guaranteed
18571890
/// not to alias.
1858-
bool MemCpyOptPass::processMemMove(MemMoveInst *M) {
1891+
bool MemCpyOptPass::processMemMove(MemMoveInst *M, BasicBlock::iterator &BBI) {
18591892
// See if the source could be modified by this memmove potentially.
1860-
if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M))))
1893+
if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M)))) {
1894+
// On the off-chance the memmove clobbers src with previously memset'd
1895+
// bytes, the memmove may be redundant.
1896+
if (!M->isVolatile() && isMemMoveMemSetDependency(M)) {
1897+
LLVM_DEBUG(dbgs() << "Removed redundant memmove.\n");
1898+
++BBI;
1899+
eraseInstruction(M);
1900+
++NumMemMoveInstr;
1901+
return true;
1902+
}
18611903
return false;
1904+
}
18621905

18631906
LLVM_DEBUG(dbgs() << "MemCpyOptPass: Optimizing memmove -> memcpy: " << *M
18641907
<< "\n");
@@ -2067,7 +2110,7 @@ bool MemCpyOptPass::iterateOnFunction(Function &F) {
20672110
else if (auto *M = dyn_cast<MemCpyInst>(I))
20682111
RepeatInstruction = processMemCpy(M, BI);
20692112
else if (auto *M = dyn_cast<MemMoveInst>(I))
2070-
RepeatInstruction = processMemMove(M);
2113+
RepeatInstruction = processMemMove(M, BI);
20712114
else if (auto *CB = dyn_cast<CallBase>(I)) {
20722115
for (unsigned i = 0, e = CB->arg_size(); i != e; ++i) {
20732116
if (CB->isByValArgument(i))

llvm/test/Transforms/MemCpyOpt/memset-memmove-redundant-memmove.ll

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ define i32 @test() {
77
; CHECK-NEXT: [[ARRAY:%.*]] = alloca [26 x i32], align 16
88
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr noundef nonnull align 16 [[ARRAY]], i8 0, i64 104, i1 false)
99
; CHECK-NEXT: [[ARRAY_IDX:%.*]] = getelementptr inbounds i8, ptr [[ARRAY]], i64 4
10-
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr noundef nonnull align 16 [[ARRAY]], ptr noundef nonnull align 4 [[ARRAY_IDX]], i64 100, i1 false)
1110
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAY]], align 16
1211
; CHECK-NEXT: ret i32 [[VAL]]
1312
;

0 commit comments

Comments
 (0)