Skip to content

Commit 07460b6

Browse files
committed
[MemCpyOpt] Avoid infinite loop in processMemSetMemCpyDependence (PR54983)
This adds an additional transform to drop zero-size memcpys, also in the case where the size is only zero after instruction simplification. The motivation is the case from PR54983 where the size is non-trivially zero, and processMemSetMemCpyDependence() keeps trying to reduce the memset size by zero bytes. This fix it's not really principled. It only works on the premise that if InstSimplify doesn't realize the size is zero, then AA also won't. The principled approach would be to instead add a isKnownNonZero() guard to the processMemSetMemCpyDependence() transform, but I suspect that would render that optimization mostly useless (at least it breaks all the existing test coverage -- worth noting that the constant size case is also handled by DSE, so I think this transform is primarily about the dynamic size case). Fixes #54983. Fixes #64886. Differential Revision: https://reviews.llvm.org/D124078
1 parent 5d3489e commit 07460b6

File tree

2 files changed

+55
-0
lines changed

2 files changed

+55
-0
lines changed

llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "llvm/Analysis/CFG.h"
2323
#include "llvm/Analysis/CaptureTracking.h"
2424
#include "llvm/Analysis/GlobalsModRef.h"
25+
#include "llvm/Analysis/InstructionSimplify.h"
2526
#include "llvm/Analysis/Loads.h"
2627
#include "llvm/Analysis/MemoryLocation.h"
2728
#include "llvm/Analysis/MemorySSA.h"
@@ -1626,6 +1627,16 @@ bool MemCpyOptPass::performStackMoveOptzn(Instruction *Load, Instruction *Store,
16261627
return true;
16271628
}
16281629

1630+
static bool isZeroSize(Value *Size) {
1631+
if (auto *I = dyn_cast<Instruction>(Size))
1632+
if (auto *Res = simplifyInstruction(I, I->getModule()->getDataLayout()))
1633+
Size = Res;
1634+
// Treat undef/poison size like zero.
1635+
if (auto *C = dyn_cast<Constant>(Size))
1636+
return isa<UndefValue>(C) || C->isNullValue();
1637+
return false;
1638+
}
1639+
16291640
/// Perform simplification of memcpy's. If we have memcpy A
16301641
/// which copies X to Y, and memcpy B which copies Y to Z, then we can rewrite
16311642
/// B to be a memcpy from X to Z (or potentially a memmove, depending on
@@ -1642,6 +1653,14 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
16421653
return true;
16431654
}
16441655

1656+
// If the size is zero, remove the memcpy. This also prevents infinite loops
1657+
// in processMemSetMemCpyDependence, which is a no-op for zero-length memcpys.
1658+
if (isZeroSize(M->getLength())) {
1659+
++BBI;
1660+
eraseInstruction(M);
1661+
return true;
1662+
}
1663+
16451664
// If copying from a constant, try to turn the memcpy into a memset.
16461665
if (auto *GV = dyn_cast<GlobalVariable>(M->getSource()))
16471666
if (GV->isConstant() && GV->hasDefinitiveInitializer())
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
; NOTE: Assertions have been autogenerated by utils/update_test_checks.py
2+
; RUN: opt -S -passes=memcpyopt < %s | FileCheck %s
3+
4+
declare void @llvm.memset.p0.i64(ptr, i8, i64, i1)
5+
declare void @llvm.memcpy.p0.p0.i64(ptr, ptr, i64, i1)
6+
7+
define void @zero_size(ptr %p, ptr %p2) {
8+
; CHECK-LABEL: @zero_size(
9+
; CHECK-NEXT: ret void
10+
;
11+
call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 0, i1 false)
12+
ret void
13+
}
14+
15+
; The memcpy size is zero in a way that is non-trivial, but understood by AA.
16+
define void @pr54983(ptr %p, ptr noalias %p2) {
17+
; CHECK-LABEL: @pr54983(
18+
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr [[P:%.*]], i8 0, i64 1, i1 false)
19+
; CHECK-NEXT: [[SIZE:%.*]] = shl i64 0, 0
20+
; CHECK-NEXT: ret void
21+
;
22+
call void @llvm.memset.p0.i64(ptr %p, i8 0, i64 1, i1 false)
23+
%size = shl i64 0, 0
24+
call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %size, i1 false)
25+
ret void
26+
}
27+
28+
define void @pr64886(i64 %len, ptr noalias %p) {
29+
; CHECK-LABEL: @pr64886(
30+
; CHECK-NEXT: call void @llvm.memset.p0.i64(ptr inttoptr (i64 -1 to ptr), i8 0, i64 [[LEN:%.*]], i1 false)
31+
; CHECK-NEXT: ret void
32+
;
33+
call void @llvm.memset.p0.i64(ptr inttoptr (i64 -1 to ptr), i8 0, i64 %len, i1 false)
34+
call void @llvm.memcpy.p0.p0.i64(ptr inttoptr (i64 -1 to ptr), ptr %p, i64 poison, i1 false)
35+
ret void
36+
}

0 commit comments

Comments
 (0)