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,10 +85,13 @@ 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() && + if ((Call->onlyReadsMemory() || + (IsArgOperand && + Call->paramHasAttr(DataOpNo, Attribute::ReadOnly) && + Call->paramHasAttr(DataOpNo, Attribute::NoAlias))) && (Call->use_empty() || Call->doesNotCapture(DataOpNo))) continue; diff --git a/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll b/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll new file mode 100644 --- /dev/null +++ b/llvm/test/Transforms/InstCombine/forward-memcpy-to-readonly-noalias.ll @@ -0,0 +1,22 @@ +; Tests that a local alloca that is the target of a memcpy from a constant that is +; only passed as a readonly noalias 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 + +; CHECK-LABEL: @bar +; CHECK-NEXT: call void @foo +; CHECK-NEXT: ret void +define void @bar() { + %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 noalias 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 noalias readonly)