Index: llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp =================================================================== --- llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp +++ llvm/lib/Transforms/Scalar/MemCpyOptimizer.cpp @@ -945,6 +945,14 @@ C->getArgOperand(ArgI)->getType()->getPointerAddressSpace()) return false; + // If merging alias scopes results in a non-null, bail + // TODO: merging should be legal if no new domains get introduced + 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,39 @@ +; 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 !3 +; call void @llvm.lifetime.end.p0i8(i64 8, i8* nonnull %src), !noalias !0 +; ... +; !0 = !{!1} +; !1 = distinct !{!1, !2, !"hello2: %a"} +; !2 = distinct !{!2, !"hello2"} +; !3 = !{!1, !4, !5, !"hello: %a"} +; !4 = distinct !{!4, !5, !"hello: %a"} +; !5 = distinct !{!5, !"hello"} +; Which is incorrect because the lifetime.end of %src will now "noalias" the above memcpy. +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 !3 + 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 !3 + call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %tmp, i64 1, i1 false), !alias.scope !3 + %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 = !{!1} +!1 = distinct !{!1, !2, !"hello: %a"} +!2 = distinct !{!2, !"hello"} +!3 = !{!4} +!4 = distinct !{!4, !5, !"hello2: %a"} +!5 = distinct !{!5, !"hello2"}