Skip to content

Backport https://github.com/llvm/llvm-project/commit/5c3beb7b1e26d38b… #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Nov 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1455,6 +1455,13 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
eraseInstruction(M);
return true;
}
// If the size is zero, remove the memcpy. This also prevents infinite loops
// in processMemSetMemCpyDependence, which is a no-op for zero-length memcpys.

MemoryUseOrDef *MA = MSSA->getMemoryAccess(M);
if (!MA)
// Degenerate case: memcpy marked as not accessing memory.
return false;

// If copying from a constant, try to turn the memcpy into a memset.
if (auto *GV = dyn_cast<GlobalVariable>(M->getSource()))
Expand All @@ -1464,8 +1471,7 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
IRBuilder<> Builder(M);
Instruction *NewM = Builder.CreateMemSet(
M->getRawDest(), ByteVal, M->getLength(), M->getDestAlign(), false);
auto *LastDef =
cast<MemoryDef>(MSSAU->getMemorySSA()->getMemoryAccess(M));
auto *LastDef = cast<MemoryDef>(MA);
auto *NewAccess =
MSSAU->createMemoryAccessAfter(NewM, LastDef, LastDef);
MSSAU->insertDef(cast<MemoryDef>(NewAccess), /*RenameUses=*/true);
Expand All @@ -1476,7 +1482,6 @@ bool MemCpyOptPass::processMemCpy(MemCpyInst *M, BasicBlock::iterator &BBI) {
}

BatchAAResults BAA(*AA);
MemoryUseOrDef *MA = MSSA->getMemoryAccess(M);
// FIXME: Not using getClobberingMemoryAccess() here due to PR54682.
MemoryAccess *AnyClobber = MA->getDefiningAccess();
MemoryLocation DestLoc = MemoryLocation::getForDest(M);
Expand Down
344 changes: 344 additions & 0 deletions llvm/test/Transforms/MemCpyOpt/memcpy.ll
Original file line number Diff line number Diff line change
Expand Up @@ -377,3 +377,347 @@ define void @test11(ptr addrspace(1) nocapture dereferenceable(80) %P) {

declare void @f1(ptr nocapture sret(%struct.big))
declare void @f2(ptr)

declare void @f(ptr)
declare void @f_byval(ptr byval(i32))
declare void @f_full_readonly(ptr nocapture noalias readonly)

define void @immut_param(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param(
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL:%.*]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr align 4 nocapture noalias readonly %val1)
ret void
}

; Can't remove memcpy because dest may be captured.
define void @immut_param_maycapture(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_maycapture(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy because dest may be aliased.
define void @immut_param_mayalias(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_mayalias(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr align 4 nocapture readonly %val1)
ret void
}

; Can't remove memcpy because dest may be written.
define void @immut_param_maywrite(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_maywrite(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr align 4 nocapture noalias %val1)
ret void
}

define void @immut_param_readonly(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_readonly(
; CHECK-NEXT: call void @f_full_readonly(ptr align 4 [[VAL:%.*]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f_full_readonly(ptr align 4 %val1)
ret void
}

define void @immut_param_no_align(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_no_align(
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL:%.*]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr nocapture noalias readonly %val1)
ret void
}

@gp = external constant [0 x i8]
; Can't remove memcpy because dest is not unescaped alloca, so cpying is meaningfull.
define void @immut_param_global(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_global(
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 @gp, ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 @gp)
; CHECK-NEXT: ret void
;
call void @llvm.memcpy.p0.p0.i64(ptr align 4 @gp, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr nocapture align 4 noalias readonly @gp)
ret void
}

; Can't remove memcpy for VLA because of unknown size and alignment.
define void @immut_param_vla(ptr align 4 noalias %val, i64 %n) {
; CHECK-LABEL: @immut_param_vla(
; CHECK-NEXT: [[VAL1:%.*]] = alloca ptr, i64 [[N:%.*]], align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca ptr, i64 %n
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy for scalable vector, because of memcpy size sufficiency is unknown
define void @immut_param_scalable_vector(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_scalable_vector(
; CHECK-NEXT: [[VAL1:%.*]] = alloca <vscale x 2 x i32>, align 8
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 2, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca <vscale x 2 x i32>
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 2, i1 false)
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy because dst is modified between call and memcpy
define void @immut_param_modified_dst(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_modified_dst(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: store i32 13, ptr [[VAL1]], align 4
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
store i32 13, ptr %val1
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy because src is modified between call and memcpy
define void @immut_param_modified_src(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_modified_src(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: store i32 13, ptr [[VAL]], align 4
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
store i32 13, ptr %val
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy because memcpy is volatile
define void @immut_param_volatile(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_volatile(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 true)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 true)
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

; Can't remove memcpy because address spaces are different.
define void @immut_param_different_addrespace(ptr addrspace(1) align 4 noalias %val) {
; CHECK-LABEL: @immut_param_different_addrespace(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p1.i64(ptr align 4 [[VAL1]], ptr addrspace(1) align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly align 4 [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p1.i64(ptr align 4 %val1, ptr addrspace(1) align 4 %val, i64 1, i1 false)
call void @f(ptr nocapture align 4 noalias readonly %val1)
ret void
}

define void @immut_param_bigger_align(ptr align 16 noalias %val) {
; CHECK-LABEL: @immut_param_bigger_align(
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL:%.*]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr %val, i64 1, i1 false)
call void @f(ptr nocapture noalias readonly %val1)
ret void
}

; Can't remove memcpy if we remove, the bigger alignment couldn't replaced by smaller one.
define void @immut_param_smaller_align(ptr align 4 noalias %val) {
; CHECK-LABEL: @immut_param_smaller_align(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 16
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 16 [[VAL1]], ptr [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 16
call void @llvm.memcpy.p0.p0.i64(ptr align 16 %val1, ptr %val, i64 1, i1 false)
call void @f(ptr nocapture noalias readonly %val1)
ret void
}

define void @immut_param_enforced_alignment() {
; CHECK-LABEL: @immut_param_enforced_alignment(
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL]])
; CHECK-NEXT: ret void
;
%val = alloca i8, align 1
store i32 42, ptr %val
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr %val1, ptr %val, i64 1, i1 false)
call void @f(ptr nocapture noalias readonly %val1)
ret void
}

; Can't remove memcpy, because if the %val directly passed to @f,
; alignment of ptr to f's argument will be different.
define void @immut_invalid_align_branched(i1 %c, ptr noalias %val) {
; CHECK-LABEL: @immut_invalid_align_branched(
; CHECK-NEXT: [[VAL1:%.*]] = alloca [4 x i8], align 4
; CHECK-NEXT: [[VAL2:%.*]] = alloca [16 x i8], align 16
; CHECK-NEXT: [[VAL3:%.*]] = select i1 [[C:%.*]], ptr [[VAL1]], ptr [[VAL2]]
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL3]], ptr align 4 [[VAL:%.*]], i64 4, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL3]])
; CHECK-NEXT: ret void
;
%val1 = alloca [4 x i8], align 4
%val2 = alloca [16 x i8], align 16
%val3 = select i1 %c, ptr %val1, ptr %val2
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val3, ptr align 4 %val, i64 4, i1 false)
call void @f(ptr nocapture noalias readonly %val3)
ret void
}

; Can't remove memcpy, because alias might modify the src.
define void @immut_but_alias_src(ptr %val) {
; CHECK-LABEL: @immut_but_alias_src(
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL:%.*]], i64 1, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL1]])
; CHECK-NEXT: ret void
;
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f(ptr nocapture noalias readonly %val1)
ret void
}

define void @immut_unescaped_alloca() {
; CHECK-LABEL: @immut_unescaped_alloca(
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
; CHECK-NEXT: call void @f_full_readonly(ptr [[VAL]])
; CHECK-NEXT: ret void
;
%val = alloca i8, align 4
store i32 42, ptr %val
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
call void @f_full_readonly(ptr %val1)
ret void
}

; Can't remove memcpy, because alloca src is modified
define void @immut_unescaped_alloca_modified() {
; CHECK-LABEL: @immut_unescaped_alloca_modified(
; CHECK-NEXT: [[VAL:%.*]] = alloca i8, align 4
; CHECK-NEXT: store i32 42, ptr [[VAL]], align 4
; CHECK-NEXT: [[VAL1:%.*]] = alloca i8, align 4
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL1]], ptr align 4 [[VAL]], i64 1, i1 false)
; CHECK-NEXT: call void @f_full_readonly(ptr [[VAL1]])
; CHECK-NEXT: ret void
;
%val = alloca i8, align 4
store i32 42, ptr %val
%val1 = alloca i8, align 4
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val1, ptr align 4 %val, i64 1, i1 false)
store i32 13, ptr %val
call void @f_full_readonly(ptr %val1)
ret void
}

; TODO: Remove memcpy
define void @immut_valid_align_branched(i1 %c, ptr noalias align 4 %val) {
; CHECK-LABEL: @immut_valid_align_branched(
; CHECK-NEXT: [[VAL1:%.*]] = alloca [4 x i8], align 4
; CHECK-NEXT: [[VAL2:%.*]] = alloca [16 x i8], align 4
; CHECK-NEXT: [[VAL3:%.*]] = select i1 [[C:%.*]], ptr [[VAL1]], ptr [[VAL2]]
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr align 4 [[VAL3]], ptr align 4 [[VAL:%.*]], i64 4, i1 false)
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[VAL3]])
; CHECK-NEXT: ret void
;
%val1 = alloca [4 x i8], align 4
%val2 = alloca [16 x i8], align 4
%val3 = select i1 %c, ptr %val1, ptr %val2
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %val3, ptr align 4 %val, i64 4, i1 false)
call void @f(ptr nocapture noalias readonly %val3)
ret void
}

; Merge/drop noalias metadata when replacing parameter.
define void @immut_param_noalias_metadata(ptr align 4 byval(i32) %ptr) {
; CHECK-LABEL: @immut_param_noalias_metadata(
; CHECK-NEXT: store i32 1, ptr [[PTR:%.*]], align 4, !noalias !0
; CHECK-NEXT: call void @f(ptr noalias nocapture readonly [[PTR]])
; CHECK-NEXT: ret void
;
%tmp = alloca i32, align 4
store i32 1, ptr %ptr, !noalias !2
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp, ptr align 4 %ptr, i64 4, i1 false)
call void @f(ptr nocapture noalias readonly %tmp), !alias.scope !2
ret void
}

define void @byval_param_noalias_metadata(ptr align 4 byval(i32) %ptr) {
; CHECK-LABEL: @byval_param_noalias_metadata(
; CHECK-NEXT: store i32 1, ptr [[PTR:%.*]], align 4, !noalias !0
; CHECK-NEXT: call void @f_byval(ptr byval(i32) align 4 [[PTR]])
; CHECK-NEXT: ret void
;
%tmp = alloca i32, align 4
store i32 1, ptr %ptr, !noalias !2
call void @llvm.memcpy.p0.p0.i64(ptr align 4 %tmp, ptr align 4 %ptr, i64 4, i1 false)
call void @f_byval(ptr align 4 byval(i32) %tmp), !alias.scope !2
ret void
}

define void @memcpy_memory_none(ptr %p, ptr %p2, i64 %size) {
; CHECK-LABEL: @memcpy_memory_none(
; CHECK-NEXT: call void @llvm.memcpy.p0.p0.i64(ptr [[P:%.*]], ptr [[P2:%.*]], i64 [[SIZE:%.*]], i1 false) #[[ATTR6:[0-9]+]]
; CHECK-NEXT: ret void
;
call void @llvm.memcpy.p0.p0.i64(ptr %p, ptr %p2, i64 %size, i1 false) memory(none)
ret void
}

!0 = !{!0}
!1 = !{!1, !0}
!2 = !{!1}