Index: llvm/include/llvm/Analysis/ScopedNoAliasAA.h =================================================================== --- llvm/include/llvm/Analysis/ScopedNoAliasAA.h +++ llvm/include/llvm/Analysis/ScopedNoAliasAA.h @@ -25,6 +25,27 @@ class MDNode; class MemoryLocation; +/// This is a simple wrapper around an MDNode which provides a higher-level +/// interface by hiding the details of how alias analysis information is encoded +/// in its operands. +class AliasScopeNode { + const MDNode *Node = nullptr; + +public: + AliasScopeNode() = default; + explicit AliasScopeNode(const MDNode *N) : Node(N) {} + + /// Get the MDNode for this AliasScopeNode. + const MDNode *getNode() const { return Node; } + + /// Get the MDNode for this AliasScopeNode's domain. + const MDNode *getDomain() const { + if (Node->getNumOperands() < 2) + return nullptr; + return dyn_cast_or_null(Node->getOperand(1)); + } +}; + /// A simple AA result which uses scoped-noalias metadata to answer queries. class ScopedNoAliasAAResult : public AAResultBase { friend AAResultBase; Index: llvm/lib/Analysis/ScopedNoAliasAA.cpp =================================================================== --- llvm/lib/Analysis/ScopedNoAliasAA.cpp +++ llvm/lib/Analysis/ScopedNoAliasAA.cpp @@ -50,31 +50,6 @@ static cl::opt EnableScopedNoAlias("enable-scoped-noalias", cl::init(true), cl::Hidden); -namespace { - -/// This is a simple wrapper around an MDNode which provides a higher-level -/// interface by hiding the details of how alias analysis information is encoded -/// in its operands. -class AliasScopeNode { - const MDNode *Node = nullptr; - -public: - AliasScopeNode() = default; - explicit AliasScopeNode(const MDNode *N) : Node(N) {} - - /// Get the MDNode for this AliasScopeNode. - const MDNode *getNode() const { return Node; } - - /// Get the MDNode for this AliasScopeNode's domain. - const MDNode *getDomain() const { - if (Node->getNumOperands() < 2) - return nullptr; - return dyn_cast_or_null(Node->getOperand(1)); - } -}; - -} // end anonymous namespace - AliasResult ScopedNoAliasAAResult::alias(const MemoryLocation &LocA, const MemoryLocation &LocB, AAQueryInfo &AAQI) { Index: llvm/lib/IR/Metadata.cpp =================================================================== --- llvm/lib/IR/Metadata.cpp +++ llvm/lib/IR/Metadata.cpp @@ -27,6 +27,7 @@ #include "llvm/ADT/StringMap.h" #include "llvm/ADT/StringRef.h" #include "llvm/ADT/Twine.h" +#include "llvm/Analysis/ScopedNoAliasAA.h" #include "llvm/IR/Argument.h" #include "llvm/IR/BasicBlock.h" #include "llvm/IR/Constant.h" @@ -926,6 +927,28 @@ if (!A || !B) return nullptr; + // Can only concatenate if one domain is a subset of the other + SmallPtrSet ADomains; + SmallPtrSet BDomains; + for (const MDOperand &MDOp : A->operands()) + if (const MDNode *NAMD = dyn_cast(MDOp)) + if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain()) + ADomains.insert(Domain); + + for (const MDOperand &MDOp : B->operands()) + if (const MDNode *NAMD = dyn_cast(MDOp)) + if (const MDNode *Domain = AliasScopeNode(NAMD).getDomain()) + BDomains.insert(Domain); + + const auto &SmallerDomain = + ADomains.size() <= BDomains.size() ? ADomains : BDomains; + const auto &LargerDomain = + ADomains.size() > BDomains.size() ? ADomains : BDomains; + + if (!std::includes(LargerDomain.begin(), LargerDomain.end(), + SmallerDomain.begin(), SmallerDomain.end())) + return nullptr; + return concatenate(A, B); } 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 --match-full-lines %s + +; Make sure callslot optimization goes conservative when merging scopes with different domains +; Merging here naively 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, !"callee1: %a"} +; !2 = distinct !{!2, !"callee1"} +; !3 = !{!1, !4, !5, !"callee0: %a"} +; !4 = distinct !{!4, !5, !"callee0: %a"} +; !5 = distinct !{!5, !"callee0"} +; 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 +; NOTE: we're matching the full line and looking for the lack of !alias.scope here +; CHECK: call void @llvm.memcpy.p0i8.p0i8.i64(i8* align 8 %dst, i8* align 8 %src, 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, !"callee0: %a"} +!2 = distinct !{!2, !"callee0"} +!3 = !{!4} +!4 = distinct !{!4, !5, !"callee1: %a"} +!5 = distinct !{!5, !"callee1"}