diff --git a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp --- a/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp +++ b/llvm/lib/Transforms/InstCombine/InstCombineLoadStoreAlloca.cpp @@ -39,8 +39,8 @@ /// the alloca, and if the source pointer is a pointer to a constant global, we /// can optimize this. static bool -isOnlyCopiedFromConstantMemory(AAResults *AA, - Value *V, MemTransferInst *&TheCopy, +isOnlyCopiedFromConstantMemory(AAResults *AA, AllocaInst *V, + MemTransferInst *&TheCopy, SmallVectorImpl &ToDelete) { // We track lifetime intrinsics as we encounter them. If we decide to go // ahead and replace the value with the global, this lets the caller quickly @@ -85,11 +85,12 @@ if (IsArgOperand && Call->isInAllocaArgument(DataOpNo)) return false; - // If this is a readonly/readnone call site, then we know it is just a - // load (but one that potentially returns the value itself), so we can + // If this call site doesn't modify the memory, then we know it is just + // a load (but one that potentially returns the value itself), so we can // ignore it if we know that the value isn't captured. - if (Call->onlyReadsMemory() && - (Call->use_empty() || Call->doesNotCapture(DataOpNo))) + bool CapturesArg = !Call->doesNotCapture(DataOpNo); + if ((Call->onlyReadsMemory() && (Call->use_empty() || !CapturesArg)) || + (IsArgOperand && Call->onlyReadsMemory(DataOpNo) && !CapturesArg)) continue; // If this is being passed as a byval argument, the caller is making a diff --git a/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly.ll b/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly.ll @@ -0,0 +1,23 @@ +; Tests that a local alloca that is the target of a memcpy from a constant that is +; only passed as a readonly nocapture parameter can be eliminated. +; +; RUN: opt -S -passes=instcombine < %s | FileCheck %s + +%struct.Big = type { [1024 x i32] } + +@x = constant { <{ i32, [1023 x i32] }> } { <{ i32, [1023 x i32] }> <{ i32 1, [1023 x i32] zeroinitializer }> }, align 4 + +define void @bar() { +; CHECK-LABEL: @bar( +; CHECK-NEXT: call void @foo(ptr nocapture noundef nonnull readonly @x) +; CHECK-NEXT: ret void +; + %1 = alloca %struct.Big, align 4 + call void @llvm.memcpy.p0.p0.i64(ptr noundef nonnull align 4 dereferenceable(4096) %1, ptr noundef nonnull align 4 dereferenceable(4096) @x, i64 4096, i1 false) + call void @foo(ptr noundef nonnull nocapture readonly %1) #4 + ret void +} + +declare void @llvm.memcpy.p0.p0.i64(ptr noalias nocapture writeonly, ptr noalias nocapture readonly, i64, i1 immarg) + +declare void @foo(ptr noundef nocapture readonly)