Index: llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp =================================================================== --- llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -945,6 +945,13 @@ C->getArgOperand(ArgI)->getType()->getPointerAddressSpace()) return false; + // If alias scopes get expanded, bail + auto callScope = C->getMetadata(LLVMContext::MD_alias_scope); + auto cpyScope = C->getMetadata(LLVMContext::MD_alias_scope); + if (MDNode::getMostGenericAliasScope(callScope, cpyScope) != nullptr) { + return false; + } + // All the checks have passed, so do the transformation. bool changedArgument = false; for (unsigned ArgI = 0; ArgI < C->arg_size(); ++ArgI) Index: llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll =================================================================== --- /dev/null +++ llvm/test/Transforms/MemCpyOpt/callslot_badaa.ll @@ -0,0 +1,34 @@ +; RUN: opt < %s -S -memcpyopt | FileCheck %s + +; Make sure callslot optimization does not allow merging here because of alias.scope. +; Merging here generates: +; call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %src, i64 1, i1 false), !alias.scope !1 +; call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !0 +; %ret_value = load i8, i8* %dst, align 1 +; ... +; !0 = distinct !{!0} +; !1 = !{!0, !2} +; !2 = distinct !{!2} +; Which is incorrect because the lifetime.end of %src will now "noalias" the above memcpy +; allowing future optimizations to incorrectly utilize %src after its lifetime has ended. +define i8 @test(i8 %input) { + %tmp = alloca i8 + %dst = alloca i8 + %src = alloca i8 +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tmp, i8* align 8 %src, i64 1, i1 false) +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %tmp, i64 1, i1 false) + call void @llvm.lifetime.start.p0i8(i64 8, i8* nonnull %src), !noalias !1 + store i8 %input, i8* %src + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %tmp, i8* align 8 %src, i64 1, i1 false), !alias.scope !0 + call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !1 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %tmp, i64 1, i1 false), !alias.scope !1 + %ret_value = load i8, i8* %dst + ret i8 %ret_value +} + +declare void @llvm.lifetime.start.p0i8(i64, i8* nocapture) +declare void @llvm.lifetime.end.p0i8(i64, i8* nocapture) +declare void @llvm.memcpy.p0i8.p0i8.i64(i8*, i8*, i64, i1) + +!0 = !{!0} +!1 = !{!1}