Skip to content

Commit aaaa51f

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 2ee9f18 commit aaaa51f

File tree

3 files changed

+81
-7
lines changed

3 files changed

+81
-7
lines changed

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ class MemCpyOptPass : public PassInfoMixin<MemCpyOptPass> {
6868
BasicBlock::iterator &BBI);
6969
bool processMemSet(MemSetInst *SI, BasicBlock::iterator &BBI);
7070
bool processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI);
71-
bool processMemMove(MemMoveInst *M);
71+
bool processMemMove(MemMoveInst *M, BasicBlock::iterator &BBI);
7272
bool performCallSlotOptzn(Instruction *cpyLoad, Instruction *cpyStore,
7373
Value *cpyDst, Value *cpySrc, TypeSize cpyLen,
7474
Align cpyAlign, BatchAAResults &BAA,
@@ -87,6 +87,7 @@ class MemCpyOptPass : public PassInfoMixin<MemCpyOptPass> {
8787
bool performStackMoveOptzn(Instruction *Load, Instruction *Store,
8888
AllocaInst *DestAlloca, AllocaInst *SrcAlloca,
8989
TypeSize Size, BatchAAResults &BAA);
90+
bool isMemMoveMemSetDependency(MemMoveInst *M);
9091

9192
void eraseInstruction(Instruction *I);
9293
bool iterateOnFunction(Function &F);

llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp

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

7070
STATISTIC(NumMemCpyInstr, "Number of memcpy instructions deleted");
71+
STATISTIC(NumMemMoveInstr, "Number of memmove instructions deleted");
7172
STATISTIC(NumMemSetInfer, "Number of memsets inferred");
7273
STATISTIC(NumMoveToCpy, "Number of memmoves converted to memcpy");
7374
STATISTIC(NumCpyToSet, "Number of memcpys converted to memset");
@@ -1841,12 +1842,87 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
18411842
return false;
18421843
}
18431844

1845+
/// Memmove calls with overlapping src/dest buffers that come after a memset may
1846+
/// be removed.
1847+
bool MemCpyOptPass::isMemMoveMemSetDependency(MemMoveInst *M) {
1848+
const auto &DL = M->getDataLayout();
1849+
MemoryUseOrDef *MemMoveAccess = MSSA->getMemoryAccess(M);
1850+
if (!MemMoveAccess)
1851+
return false;
1852+
1853+
// The memmove is of form memmove(x, x + A, B).
1854+
MemoryLocation SourceLoc = MemoryLocation::getForSource(M);
1855+
auto *MemMoveSourceOp = M->getSource();
1856+
auto *Source = dyn_cast<GetElementPtrInst>(MemMoveSourceOp);
1857+
bool MemMoveHasGEPOperator = false;
1858+
if (!Source) {
1859+
if (auto *CE = dyn_cast<ConstantExpr>(MemMoveSourceOp))
1860+
if (isa<GEPOperator>(CE)) {
1861+
Source = cast<GetElementPtrInst>(CE->getAsInstruction());
1862+
MemMoveHasGEPOperator = true;
1863+
}
1864+
if (!Source)
1865+
return false;
1866+
}
1867+
1868+
APInt Offset(DL.getIndexTypeSizeInBits(Source->getType()), 0);
1869+
LocationSize MemMoveLocSize = SourceLoc.Size;
1870+
if (Source->getPointerOperand() != M->getDest() ||
1871+
!MemMoveLocSize.hasValue() || Offset.isNegative() ||
1872+
!Source->accumulateConstantOffset(DL, Offset)) {
1873+
if (MemMoveHasGEPOperator)
1874+
Source->dropAllReferences();
1875+
return false;
1876+
}
1877+
1878+
if (MemMoveHasGEPOperator)
1879+
Source->dropAllReferences();
1880+
1881+
const uint64_t MemMoveSize = MemMoveLocSize.getValue();
1882+
LocationSize TotalSize =
1883+
LocationSize::precise(Offset.getZExtValue() + MemMoveSize);
1884+
MemoryLocation CombinedSourceLoc(MemMoveSourceOp, TotalSize);
1885+
MemoryLocation CombinedDestLoc(M->getDest(), TotalSize);
1886+
1887+
// The first dominating clobbering MemoryAccess for the combined location
1888+
// needs to be a memset.
1889+
BatchAAResults BAA(*AA);
1890+
MemSetInst *MS = nullptr;
1891+
MemoryAccess *FirstDef = MemMoveAccess->getDefiningAccess();
1892+
MemoryAccess *DestClobber = MSSA->getWalker()->getClobberingMemoryAccess(
1893+
FirstDef, CombinedDestLoc, BAA);
1894+
if (auto *Def = dyn_cast<MemoryDef>(DestClobber))
1895+
MS = dyn_cast_or_null<MemSetInst>(Def->getMemoryInst());
1896+
if (!MS)
1897+
return false;
1898+
1899+
// Memset length must be sufficiently large.
1900+
if (cast<ConstantInt>(MS->getLength())->getZExtValue() < MemMoveSize)
1901+
return false;
1902+
1903+
// The destination buffer must have been memset'd.
1904+
if (!BAA.isMustAlias(MS->getDest(), M->getDest()))
1905+
return false;
1906+
1907+
return true;
1908+
}
1909+
18441910
/// Transforms memmove calls to memcpy calls when the src/dst are guaranteed
18451911
/// not to alias.
1846-
bool MemCpyOptPass::processMemMove(MemMoveInst *M) {
1912+
bool MemCpyOptPass::processMemMove(MemMoveInst *M, BasicBlock::iterator &BBI) {
18471913
// See if the source could be modified by this memmove potentially.
1848-
if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M))))
1914+
if (isModSet(AA->getModRefInfo(M, MemoryLocation::getForSource(M)))) {
1915+
// On the off-chance the memmove clobbers src with previously memset'd
1916+
// bytes, the memmove may be redundant.
1917+
if (!M->isVolatile() && isMemMoveMemSetDependency(M)) {
1918+
LLVM_DEBUG(dbgs() << "Removed redundant memmove.\n");
1919+
++BBI;
1920+
eraseInstruction(M);
1921+
++NumMemMoveInstr;
1922+
return true;
1923+
}
18491924
return false;
1925+
}
18501926

18511927
LLVM_DEBUG(dbgs() << "MemCpyOptPass: Optimizing memmove -> memcpy: " << *M
18521928
<< "\n");
@@ -2064,7 +2140,7 @@ bool MemCpyOptPass::iterateOnFunction(Function &F) {
20642140
else if (auto *M = dyn_cast<MemCpyInst>(I))
20652141
RepeatInstruction = processMemCpy(M, BI);
20662142
else if (auto *M = dyn_cast<MemMoveInst>(I))
2067-
RepeatInstruction = processMemMove(M);
2143+
RepeatInstruction = processMemMove(M, BI);
20682144
else if (auto *CB = dyn_cast<CallBase>(I)) {
20692145
for (unsigned i = 0, e = CB->arg_size(); i != e; ++i) {
20702146
if (CB->isByValArgument(i))

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ define i32 @redundant_memmove() {
77
; CHECK-NEXT: [[ARRAY:%.*]] = alloca [26 x i32], align 16
88
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr 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 align 16 [[ARRAY]], ptr 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
;
@@ -88,7 +87,6 @@ define i32 @redundant_memmove_different_bbs() {
8887
; CHECK-NEXT: [[ARRAY_IDX:%.*]] = getelementptr inbounds i8, ptr [[ARRAY]], i64 4
8988
; CHECK-NEXT: br label [[USE:%.*]]
9089
; CHECK: use:
91-
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 16 [[ARRAY]], ptr align 4 [[ARRAY_IDX]], i64 100, i1 false)
9290
; CHECK-NEXT: [[VAL:%.*]] = load i32, ptr [[ARRAY]], align 16
9391
; CHECK-NEXT: ret i32 [[VAL]]
9492
;
@@ -110,7 +108,6 @@ use: ; preds = %entry
110108
define ptr @redundant_memmove_memset_global_variable() {
111109
; CHECK-LABEL: @redundant_memmove_memset_global_variable(
112110
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr align 16 @g_var, i8 0, i64 104, i1 false)
113-
; CHECK-NEXT: call void @llvm.memmove.p0.p0.i64(ptr align 16 @g_var, ptr align 4 getelementptr inbounds nuw (i8, ptr @g_var, i64 4), i64 100, i1 false)
114111
; CHECK-NEXT: ret ptr @g_var
115112
;
116113
call void @llvm.memset.p0.i64(ptr align 16 @g_var, i8 0, i64 104, i1 false)

0 commit comments

Comments
 (0)