diff --git a/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp b/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp index 9371407f74ae..7d5bd94133a7 100644 --- a/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp +++ b/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp @@ -141,6 +141,38 @@ public: } }; +// We use AliasAttrMap to keep track of the AliasAttr of each node. +class AliasAttrMap { + typedef DenseMap MapType; + MapType AttrMap; + +public: + typedef MapType::const_iterator const_iterator; + + bool add(InstantiatedValue V, AliasAttrs Attr) { + if (Attr.none()) + return false; + auto &OldAttr = AttrMap[V]; + auto NewAttr = OldAttr | Attr; + if (OldAttr == NewAttr) + return false; + OldAttr = NewAttr; + return true; + } + + AliasAttrs getAttrs(InstantiatedValue V) const { + AliasAttrs Attr; + auto Itr = AttrMap.find(V); + if (Itr != AttrMap.end()) + Attr = Itr->second; + return Attr; + } + + iterator_range mappings() const { + return make_range(AttrMap.begin(), AttrMap.end()); + } +}; + struct WorkListItem { InstantiatedValue From; InstantiatedValue To; @@ -155,17 +187,33 @@ class CFLAndersAAResult::FunctionInfo { /// AliasMap[a] but not vice versa. DenseMap> AliasMap; + /// Map a value to its corresponding AliasAttrs + DenseMap AttrMap; + /// Summary of externally visible effects. AliasSummary Summary; + AliasAttrs getAttrs(const Value *) const; + public: - FunctionInfo(const ReachabilitySet &); + FunctionInfo(const ReachabilitySet &, AliasAttrMap); bool mayAlias(const Value *LHS, const Value *RHS) const; const AliasSummary &getAliasSummary() const { return Summary; } }; -CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet) { +CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet, + AliasAttrMap AMap) { + // Populate AttrMap + for (const auto &Mapping : AMap.mappings()) { + auto IVal = Mapping.first; + + // AttrMap only cares about top-level values + if (IVal.DerefLevel == 0) + AttrMap[IVal.Val] = Mapping.second; + } + + // Populate AliasMap for (const auto &OuterMapping : ReachSet.value_mappings()) { // AliasMap only cares about top-level values if (OuterMapping.first.DerefLevel > 0) @@ -186,18 +234,39 @@ CFLAndersAAResult::FunctionInfo::FunctionInfo(const ReachabilitySet &ReachSet) { // TODO: Populate function summary here } +AliasAttrs CFLAndersAAResult::FunctionInfo::getAttrs(const Value *V) const { + assert(V != nullptr); + + AliasAttrs Attr; + auto Itr = AttrMap.find(V); + if (Itr != AttrMap.end()) + Attr = Itr->second; + return Attr; +} + bool CFLAndersAAResult::FunctionInfo::mayAlias(const Value *LHS, const Value *RHS) const { assert(LHS && RHS); auto Itr = AliasMap.find(LHS); - if (Itr == AliasMap.end()) + if (Itr != AliasMap.end()) { + if (std::binary_search(Itr->second.begin(), Itr->second.end(), RHS, + std::less())) + return true; + } + + // Even if LHS and RHS are not reachable, they may still alias due to their + // AliasAttrs + auto AttrsA = getAttrs(LHS); + auto AttrsB = getAttrs(RHS); + + if (AttrsA.none() || AttrsB.none()) return false; - - // TODO: Check AliasAttrs before drawing any conclusions - - return std::binary_search(Itr->second.begin(), Itr->second.end(), RHS, - std::less()); + if (hasUnknownOrCallerAttr(AttrsA) || hasUnknownOrCallerAttr(AttrsB)) + return true; + if (isGlobalOrArgAttr(AttrsA) && isGlobalOrArgAttr(AttrsB)) + return true; + return false; } static void propagate(InstantiatedValue From, InstantiatedValue To, @@ -247,7 +316,6 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph, auto NodeInfo = Graph.getNode(ToNode); assert(NodeInfo != nullptr); - // TODO: propagate AliasAttr as well // TODO: propagate field offsets // FIXME: Here is a neat trick we can do: since both ReachSet and MemSet holds @@ -325,6 +393,52 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph, } } +static AliasAttrMap buildAttrMap(const CFLGraph &Graph, + const ReachabilitySet &ReachSet) { + AliasAttrMap AttrMap; + std::vector WorkList, NextList; + + // Initialize each node with its original AliasAttrs in CFLGraph + for (const auto &Mapping : Graph.value_mappings()) { + auto Val = Mapping.first; + auto &ValueInfo = Mapping.second; + for (unsigned I = 0, E = ValueInfo.getNumLevels(); I < E; ++I) { + auto Node = InstantiatedValue{Val, I}; + AttrMap.add(Node, ValueInfo.getNodeInfoAtLevel(I).Attr); + WorkList.push_back(Node); + } + } + + while (!WorkList.empty()) { + for (const auto &Dst : WorkList) { + auto DstAttr = AttrMap.getAttrs(Dst); + if (DstAttr.none()) + continue; + + // Propagate attr on the same level + for (const auto &Mapping : ReachSet.reachableValueAliases(Dst)) { + auto Src = Mapping.first; + if (AttrMap.add(Src, DstAttr)) + NextList.push_back(Src); + } + + // Propagate attr to the levels below + auto DstBelow = getNodeBelow(Graph, Dst); + while (DstBelow) { + if (AttrMap.add(*DstBelow, DstAttr)) { + NextList.push_back(*DstBelow); + break; + } + DstBelow = getNodeBelow(Graph, *DstBelow); + } + } + WorkList.swap(NextList); + NextList.clear(); + } + + return AttrMap; +} + CFLAndersAAResult::FunctionInfo CFLAndersAAResult::buildInfoFrom(const Function &Fn) { CFLGraphBuilder GraphBuilder( @@ -347,7 +461,11 @@ CFLAndersAAResult::buildInfoFrom(const Function &Fn) { NextList.clear(); } - return FunctionInfo(ReachSet); + // Now that we have all the reachability info, propagate AliasAttrs according + // to it + auto IValueAttrMap = buildAttrMap(Graph, ReachSet); + + return FunctionInfo(ReachSet, std::move(IValueAttrMap)); } void CFLAndersAAResult::scan(const Function &Fn) { diff --git a/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs-below.ll b/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs-below.ll new file mode 100644 index 000000000000..4cf918081bb5 --- /dev/null +++ b/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs-below.ll @@ -0,0 +1,61 @@ +; This testcase ensures that AliasAttrs are propagated not only on the same +; level but also downward. + +; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s +; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s + +; CHECK-LABEL: Function: test_attr_below +; CHECK: MayAlias: i64* %q, i64*** %p +; CHECK: NoAlias: i64* %esc, i64*** %p +; CHECK: NoAlias: i64* %esc, i64* %q + +; CHECK: MayAlias: i64* %unknown, i64*** %p +; CHECK: MayAlias: i64* %q, i64* %unknown +; CHECK: MayAlias: i64* %esc, i64* %unknown +; CHECK: MayAlias: i64* %q, i64** %pdrf +; CHECK: MayAlias: i64* %esc, i64** %pdrf +; CHECK: MayAlias: i64* %unknown, i64** %pdrf +; CHECK: MayAlias: i64* %pdrf2, i64* %q +; CHECK: MayAlias: i64* %esc, i64* %pdrf2 +; CHECK: MayAlias: i64* %pdrf2, i64* %unknown +define void @test_attr_below(i64*** %p, i64* %q) { + %esc = alloca i64, align 8 + %escint = ptrtoint i64* %esc to i64 + %unknown = inttoptr i64 %escint to i64* + + %pdrf = load i64**, i64*** %p + %pdrf2 = load i64*, i64** %pdrf + + ret void +} + +; CHECK-LABEL: Function: test_attr_assign_below +; CHECK: MayAlias: i64** %sel, i64*** %p +; CHECK: MayAlias: i64* %q, i64** %sel +; CHECK: MayAlias: i64** %a, i64** %sel +; CHECK: MayAlias: i64** %pdrf, i64** %sel + +; CHECK: MayAlias: i64** %c, i64*** %p +; CHECK: MayAlias: i64* %q, i64** %c +; CHECK: MayAlias: i64** %a, i64** %c +; CHECK: MayAlias: i64** %c, i64** %pdrf +; CHECK: MayAlias: i64** %c, i64** %sel + +; CHECK: MayAlias: i64* %d, i64*** %p +; CHECK: MayAlias: i64* %d, i64* %q +; CHECK: MayAlias: i64* %d, i64** %pdrf +; CHECK: MayAlias: i64* %d, i64** %sel +define void @test_attr_assign_below(i64*** %p, i64* %q, i1 %cond) { + %a = alloca i64*, align 8 + %pdrf = load i64**, i64*** %p + %sel = select i1 %cond, i64** %a, i64** %pdrf + + %b = alloca i64**, align 8 + store i64** %sel, i64*** %b + + %c = load i64**, i64*** %b + %d = load i64*, i64** %c + + ret void +} + diff --git a/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs.ll b/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs.ll new file mode 100644 index 000000000000..4ba3d1510be9 --- /dev/null +++ b/llvm/test/Analysis/CFLAliasAnalysis/Andersen/attrs.ll @@ -0,0 +1,94 @@ +; This testcase ensures that CFL AA handles escaped values no more conservative than it should + +; RUN: opt < %s -disable-basicaa -cfl-anders-aa -aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s +; RUN: opt < %s -aa-pipeline=cfl-anders-aa -passes=aa-eval -print-all-alias-modref-info -disable-output 2>&1 | FileCheck %s + +; CHECK-LABEL: Function: test_local +; CHECK: NoAlias: i32* %a, i32* %b +; CHECK: MayAlias: i32* %a, i32* %aAlias +; CHECK: NoAlias: i32* %aAlias, i32* %b +define void @test_local() { + %a = alloca i32, align 4 + %b = alloca i32, align 4 + %aint = ptrtoint i32* %a to i64 + %aAlias = inttoptr i64 %aint to i32* + ret void +} + +; CHECK-LABEL: Function: test_global_param +; CHECK: NoAlias: i32* %a, i32** %x +; CHECK: MayAlias: i32* %a, i32* %xload +; CHECK: MayAlias: i32* %a, i32* %gload +; CHECK: MayAlias: i32* %gload, i32* %xload +; CHECK: MayAlias: i32** %x, i32** @ext_global +; CHECK: NoAlias: i32* %a, i32** @ext_global +@ext_global = external global i32* +define void @test_global_param(i32** %x) { + %a = alloca i32, align 4 + %aint = ptrtoint i32* %a to i64 + %xload = load i32*, i32** %x + %gload = load i32*, i32** @ext_global + ret void +} + +declare void @external_func(i32**) +; CHECK-LABEL: Function: test_external_call +; CHECK: NoAlias: i32* %b, i32* %x +; CHECK: NoAlias: i32* %b, i32** %a +; CHECK: MayAlias: i32* %c, i32* %x +; CHECK: MayAlias: i32* %c, i32** %a +; CHECK: NoAlias: i32* %b, i32* %c +define void @test_external_call(i32* %x) { + %a = alloca i32*, align 8 + %b = alloca i32, align 4 + call void @external_func(i32** %a) + %c = load i32*, i32** %a + ret void +} + +declare void @external_func_readonly(i32**) readonly +; CHECK-LABEL: Function: test_external_call_func_readonly +; CHECK: MayAlias: i32* %c, i32* %x +; CHECK: NoAlias: i32* %c, i32** %a +define void @test_external_call_func_readonly(i32* %x) { + %a = alloca i32*, align 8 + %b = alloca i32, align 4 + store i32* %x, i32** %a, align 4 + call void @external_func_readonly(i32** %a) + %c = load i32*, i32** %a + ret void +} + +; CHECK-LABEL: Function: test_external_call_callsite_readonly +; CHECK: MayAlias: i32* %c, i32* %x +; CHECK: NoAlias: i32* %c, i32** %a +define void @test_external_call_callsite_readonly(i32* %x) { + %a = alloca i32*, align 8 + %b = alloca i32, align 4 + store i32* %x, i32** %a, align 4 + call void @external_func(i32** %a) readonly + %c = load i32*, i32** %a + ret void +} + +declare i32* @external_func_normal_return(i32*) +; CHECK-LABEL: Function: test_external_call_normal_return +; CHECK: MayAlias: i32* %c, i32* %x +; CHECK: MayAlias: i32* %a, i32* %c +define void @test_external_call_normal_return(i32* %x) { + %a = alloca i32, align 8 + %b = alloca i32, align 4 + %c = call i32* @external_func_normal_return(i32* %a) + ret void +} + +declare noalias i32* @external_func_noalias_return(i32*) +; CHECK-LABEL: Function: test_external_call_noalias_return +; CHECK: NoAlias: i32* %c, i32* %x +; CHECK: NoAlias: i32* %a, i32* %c +define void @test_external_call_noalias_return(i32* %x) { + %a = alloca i32, align 8 + %b = alloca i32, align 4 + %c = call i32* @external_func_noalias_return(i32* %a) + ret void +}