diff --git a/llvm/lib/Analysis/AliasAnalysisSummary.h b/llvm/lib/Analysis/AliasAnalysisSummary.h index 1ed2f21cdcc1..fb93a12420f8 100644 --- a/llvm/lib/Analysis/AliasAnalysisSummary.h +++ b/llvm/lib/Analysis/AliasAnalysisSummary.h @@ -134,31 +134,18 @@ inline bool operator>=(InterfaceValue LHS, InterfaceValue RHS) { return !(LHS < RHS); } -using FieldOffset = int64_t; // We use UnknownOffset to represent pointer offsets that cannot be determined // at compile time. Note that MemoryLocation::UnknownSize cannot be used here // because we require a signed value. -static const FieldOffset UnknownOffset = - std::numeric_limits::max(); +static const int64_t UnknownOffset = INT64_MAX; -inline FieldOffset addFieldOffset(FieldOffset LHS, FieldOffset RHS) { +inline int64_t addOffset(int64_t LHS, int64_t RHS) { if (LHS == UnknownOffset || RHS == UnknownOffset) return UnknownOffset; - + // FIXME: Do we need to guard against integer overflow here? return LHS + RHS; } -inline FieldOffset subFieldOffset(FieldOffset LHS, FieldOffset RHS) { - if (LHS == UnknownOffset || RHS == UnknownOffset) - return UnknownOffset; - - return LHS - RHS; -} - -inline FieldOffset negFieldOffset(FieldOffset Offset) { - return subFieldOffset(0, Offset); -} - /// We use ExternalRelation to describe an externally visible aliasing relations /// between parameters/return value of a function. struct ExternalRelation { diff --git a/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp b/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp index b07240e1ac35..ef7e95ffb1fe 100644 --- a/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp +++ b/llvm/lib/Analysis/CFLAndersAliasAnalysis.cpp @@ -153,59 +153,11 @@ struct OffsetInstantiatedValue { bool operator==(OffsetInstantiatedValue LHS, OffsetInstantiatedValue RHS) { return LHS.IVal == RHS.IVal && LHS.Offset == RHS.Offset; } -} - -namespace llvm { - -// Specialize DenseMapInfo for OffsetValue. -template <> struct DenseMapInfo { - static OffsetValue getEmptyKey() { - return OffsetValue{DenseMapInfo::getEmptyKey(), - DenseMapInfo::getEmptyKey()}; - } - static OffsetValue getTombstoneKey() { - return OffsetValue{DenseMapInfo::getTombstoneKey(), - DenseMapInfo::getEmptyKey()}; - } - static unsigned getHashValue(const OffsetValue &OVal) { - return DenseMapInfo>::getHashValue( - std::make_pair(OVal.Val, OVal.Offset)); - } - static bool isEqual(const OffsetValue &LHS, const OffsetValue &RHS) { - return LHS == RHS; - } -}; - -// Specialize DenseMapInfo for OffsetInstantiatedValue. -template <> struct DenseMapInfo { - static OffsetInstantiatedValue getEmptyKey() { - return OffsetInstantiatedValue{ - DenseMapInfo::getEmptyKey(), - DenseMapInfo::getEmptyKey()}; - } - static OffsetInstantiatedValue getTombstoneKey() { - return OffsetInstantiatedValue{ - DenseMapInfo::getTombstoneKey(), - DenseMapInfo::getEmptyKey()}; - } - static unsigned getHashValue(const OffsetInstantiatedValue &OVal) { - return DenseMapInfo>:: - getHashValue(std::make_pair(OVal.IVal, OVal.Offset)); - } - static bool isEqual(const OffsetInstantiatedValue &LHS, - const OffsetInstantiatedValue &RHS) { - return LHS == RHS; - } -}; - -} // namespace llvm - -namespace { // We use ReachabilitySet to keep track of value aliases (The nonterminal "V" in // the paper) during the analysis. class ReachabilitySet { - using ValueStateMap = DenseMap; + using ValueStateMap = DenseMap; using ValueReachMap = DenseMap; ValueReachMap ReachMap; @@ -214,11 +166,10 @@ public: using const_valuestate_iterator = ValueStateMap::const_iterator; using const_value_iterator = ValueReachMap::const_iterator; - // Insert edge 'From->To' at state 'State' with offset 'Offset' - bool insert(InstantiatedValue From, InstantiatedValue To, FieldOffset Offset, - MatchState State) { + // Insert edge 'From->To' at state 'State' + bool insert(InstantiatedValue From, InstantiatedValue To, MatchState State) { assert(From != To); - auto &States = ReachMap[To][OffsetInstantiatedValue{From, Offset}]; + auto &States = ReachMap[To][From]; auto Idx = static_cast(State); if (!States.test(Idx)) { States.set(Idx); @@ -303,7 +254,6 @@ public: struct WorkListItem { InstantiatedValue From; InstantiatedValue To; - FieldOffset Offset; MatchState State; }; @@ -311,13 +261,63 @@ struct ValueSummary { struct Record { InterfaceValue IValue; unsigned DerefLevel; - FieldOffset Offset; }; SmallVector FromRecords, ToRecords; }; } // end anonymous namespace +namespace llvm { + +// Specialize DenseMapInfo for OffsetValue. +template <> struct DenseMapInfo { + static OffsetValue getEmptyKey() { + return OffsetValue{DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()}; + } + + static OffsetValue getTombstoneKey() { + return OffsetValue{DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getEmptyKey()}; + } + + static unsigned getHashValue(const OffsetValue &OVal) { + return DenseMapInfo>::getHashValue( + std::make_pair(OVal.Val, OVal.Offset)); + } + + static bool isEqual(const OffsetValue &LHS, const OffsetValue &RHS) { + return LHS == RHS; + } +}; + +// Specialize DenseMapInfo for OffsetInstantiatedValue. +template <> struct DenseMapInfo { + static OffsetInstantiatedValue getEmptyKey() { + return OffsetInstantiatedValue{ + DenseMapInfo::getEmptyKey(), + DenseMapInfo::getEmptyKey()}; + } + + static OffsetInstantiatedValue getTombstoneKey() { + return OffsetInstantiatedValue{ + DenseMapInfo::getTombstoneKey(), + DenseMapInfo::getEmptyKey()}; + } + + static unsigned getHashValue(const OffsetInstantiatedValue &OVal) { + return DenseMapInfo>::getHashValue( + std::make_pair(OVal.IVal, OVal.Offset)); + } + + static bool isEqual(const OffsetInstantiatedValue &LHS, + const OffsetInstantiatedValue &RHS) { + return LHS == RHS; + } +}; + +} // end namespace llvm + class CFLAndersAAResult::FunctionInfo { /// Map a value to other values that may alias it /// Since the alias relation is symmetric, to save some space we assume values @@ -389,11 +389,9 @@ populateAliasMap(DenseMap> &AliasMap, auto Val = OuterMapping.first.Val; auto &AliasList = AliasMap[Val]; for (const auto &InnerMapping : OuterMapping.second) { - auto OVal = InnerMapping.first; // Again, AliasMap only cares about top-level values - if (OVal.IVal.DerefLevel == 0) - AliasList.push_back( - OffsetValue{OVal.IVal.Val, negFieldOffset(OVal.Offset)}); + if (InnerMapping.first.DerefLevel == 0) + AliasList.push_back(OffsetValue{InnerMapping.first.Val, UnknownOffset}); } // Sort AliasList for faster lookup @@ -432,27 +430,24 @@ static void populateExternalRelations( for (const auto &OuterMapping : ReachSet.value_mappings()) { if (auto Dst = getInterfaceValue(OuterMapping.first, RetVals)) { for (const auto &InnerMapping : OuterMapping.second) { - auto OVal = InnerMapping.first; - auto SrcIVal = OVal.IVal; - auto Offset = OVal.Offset; // If Src is a param/return value, we get a same-level assignment. - if (auto Src = getInterfaceValue(SrcIVal, RetVals)) { + if (auto Src = getInterfaceValue(InnerMapping.first, RetVals)) { // This may happen if both Dst and Src are return values if (*Dst == *Src) continue; if (hasReadOnlyState(InnerMapping.second)) - ExtRelations.push_back(ExternalRelation{*Dst, *Src, Offset}); + ExtRelations.push_back(ExternalRelation{*Dst, *Src, UnknownOffset}); // No need to check for WriteOnly state, since ReachSet is symmetric } else { // If Src is not a param/return, add it to ValueMap - + auto SrcIVal = InnerMapping.first; if (hasReadOnlyState(InnerMapping.second)) ValueMap[SrcIVal.Val].FromRecords.push_back( - ValueSummary::Record{*Dst, SrcIVal.DerefLevel, Offset}); + ValueSummary::Record{*Dst, SrcIVal.DerefLevel}); if (hasWriteOnlyState(InnerMapping.second)) ValueMap[SrcIVal.Val].ToRecords.push_back( - ValueSummary::Record{*Dst, SrcIVal.DerefLevel, Offset}); + ValueSummary::Record{*Dst, SrcIVal.DerefLevel}); } } } @@ -471,20 +466,14 @@ static void populateExternalRelations( auto SrcLevel = FromRecord.IValue.DerefLevel; auto DstIndex = ToRecord.IValue.Index; auto DstLevel = ToRecord.IValue.DerefLevel; - - // Offsets to be pushed to External Relations - auto SrcOffset = FromRecord.Offset; - auto DstOffset = ToRecord.Offset; - if (ToLevel > FromLevel) SrcLevel += ToLevel - FromLevel; else DstLevel += FromLevel - ToLevel; - ExtRelations.push_back( - ExternalRelation{InterfaceValue{SrcIndex, SrcLevel}, - InterfaceValue{DstIndex, DstLevel}, - subFieldOffset(SrcOffset, DstOffset)}); + ExtRelations.push_back(ExternalRelation{ + InterfaceValue{SrcIndex, SrcLevel}, + InterfaceValue{DstIndex, DstLevel}, UnknownOffset}); } } } @@ -602,14 +591,12 @@ bool CFLAndersAAResult::FunctionInfo::mayAlias(const Value *LHS, } static void propagate(InstantiatedValue From, InstantiatedValue To, - FieldOffset Offset, MatchState State, - ReachabilitySet &ReachSet, + MatchState State, ReachabilitySet &ReachSet, std::vector &WorkList) { if (From == To) return; - - if (ReachSet.insert(From, To, Offset, State)) - WorkList.push_back(WorkListItem{From, To, Offset, State}); + if (ReachSet.insert(From, To, State)) + WorkList.push_back(WorkListItem{From, To, State}); } static void initializeWorkList(std::vector &WorkList, @@ -623,15 +610,13 @@ static void initializeWorkList(std::vector &WorkList, // Insert all immediate assignment neighbors to the worklist for (unsigned I = 0, E = ValueInfo.getNumLevels(); I < E; ++I) { auto Src = InstantiatedValue{Val, I}; - // If we have an assignment edge from X + Offset to Y, it means that - // (Y - Offset) is reachable from X at the state FlowToWriteOnly - // At the same time, (X + Offset) is reachable from Y at the state - // FlowFromReadOnly + // If there's an assignment edge from X to Y, it means Y is reachable from + // X at S2 and X is reachable from Y at S1 for (auto &Edge : ValueInfo.getNodeInfoAtLevel(I).Edges) { - propagate(Edge.Other, Src, Edge.Offset, MatchState::FlowFromReadOnly, - ReachSet, WorkList); - propagate(Src, Edge.Other, negFieldOffset(Edge.Offset), - MatchState::FlowToWriteOnly, ReachSet, WorkList); + propagate(Edge.Other, Src, MatchState::FlowFromReadOnly, ReachSet, + WorkList); + propagate(Src, Edge.Other, MatchState::FlowToWriteOnly, ReachSet, + WorkList); } } } @@ -650,41 +635,37 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph, std::vector &WorkList) { auto FromNode = Item.From; auto ToNode = Item.To; - auto Offset = Item.Offset; auto NodeInfo = Graph.getNode(ToNode); assert(NodeInfo != nullptr); + // TODO: propagate field offsets + // FIXME: Here is a neat trick we can do: since both ReachSet and MemSet holds // relations that are symmetric, we could actually cut the storage by half by // sorting FromNode and ToNode before insertion happens. // The newly added value alias pair may potentially generate more memory // alias pairs. Check for them here. - // MemAlias reachability can only be triggered when Offset is 0 or Unknown - if (Offset == 0 || Offset == UnknownOffset) { - auto FromNodeBelow = getNodeBelow(Graph, FromNode); - auto ToNodeBelow = getNodeBelow(Graph, ToNode); - if (FromNodeBelow && ToNodeBelow && - MemSet.insert(*FromNodeBelow, *ToNodeBelow)) { - propagate(*FromNodeBelow, *ToNodeBelow, Offset, - MatchState::FlowFromMemAliasNoReadWrite, ReachSet, WorkList); - for (const auto &Mapping : - ReachSet.reachableValueAliases(*FromNodeBelow)) { - auto SrcOVal = Mapping.first; - auto MemAliasPropagate = [&](MatchState FromState, MatchState ToState) { - if (Mapping.second.test(static_cast(FromState))) - propagate(SrcOVal.IVal, *ToNodeBelow, SrcOVal.Offset, ToState, - ReachSet, WorkList); - }; + auto FromNodeBelow = getNodeBelow(Graph, FromNode); + auto ToNodeBelow = getNodeBelow(Graph, ToNode); + if (FromNodeBelow && ToNodeBelow && + MemSet.insert(*FromNodeBelow, *ToNodeBelow)) { + propagate(*FromNodeBelow, *ToNodeBelow, + MatchState::FlowFromMemAliasNoReadWrite, ReachSet, WorkList); + for (const auto &Mapping : ReachSet.reachableValueAliases(*FromNodeBelow)) { + auto Src = Mapping.first; + auto MemAliasPropagate = [&](MatchState FromState, MatchState ToState) { + if (Mapping.second.test(static_cast(FromState))) + propagate(Src, *ToNodeBelow, ToState, ReachSet, WorkList); + }; - MemAliasPropagate(MatchState::FlowFromReadOnly, - MatchState::FlowFromMemAliasReadOnly); - MemAliasPropagate(MatchState::FlowToWriteOnly, - MatchState::FlowToMemAliasWriteOnly); - MemAliasPropagate(MatchState::FlowToReadWrite, - MatchState::FlowToMemAliasReadWrite); - } + MemAliasPropagate(MatchState::FlowFromReadOnly, + MatchState::FlowFromMemAliasReadOnly); + MemAliasPropagate(MatchState::FlowToWriteOnly, + MatchState::FlowToMemAliasWriteOnly); + MemAliasPropagate(MatchState::FlowToReadWrite, + MatchState::FlowToMemAliasReadWrite); } } @@ -697,23 +678,17 @@ static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph, // - If Y is an alias of X, then reverse assignment edges (if there is any) // should precede any assignment edges on the path from X to Y. auto NextAssignState = [&](MatchState State) { - for (const auto &AssignEdge : NodeInfo->Edges) { - propagate(FromNode, AssignEdge.Other, - subFieldOffset(Offset, AssignEdge.Offset), State, ReachSet, - WorkList); - } + for (const auto &AssignEdge : NodeInfo->Edges) + propagate(FromNode, AssignEdge.Other, State, ReachSet, WorkList); }; auto NextRevAssignState = [&](MatchState State) { - for (const auto &RevAssignEdge : NodeInfo->ReverseEdges) { - propagate(FromNode, RevAssignEdge.Other, - addFieldOffset(Offset, RevAssignEdge.Offset), State, ReachSet, - WorkList); - } + for (const auto &RevAssignEdge : NodeInfo->ReverseEdges) + propagate(FromNode, RevAssignEdge.Other, State, ReachSet, WorkList); }; auto NextMemState = [&](MatchState State) { if (auto AliasSet = MemSet.getMemoryAliases(ToNode)) { for (const auto &MemAlias : *AliasSet) - propagate(FromNode, MemAlias, Offset, State, ReachSet, WorkList); + propagate(FromNode, MemAlias, State, ReachSet, WorkList); } }; @@ -778,7 +753,7 @@ static AliasAttrMap buildAttrMap(const CFLGraph &Graph, // Propagate attr on the same level for (const auto &Mapping : ReachSet.reachableValueAliases(Dst)) { - auto Src = Mapping.first.IVal; + auto Src = Mapping.first; if (AttrMap.add(Src, DstAttr)) NextList.push_back(Src); } @@ -813,19 +788,11 @@ CFLAndersAAResult::buildInfoFrom(const Function &Fn) { std::vector WorkList, NextList; initializeWorkList(WorkList, ReachSet, Graph); - - bool LoopInGraph = false; + // TODO: make sure we don't stop before the fix point is reached while (!WorkList.empty()) { for (const auto &Item : WorkList) processWorkListItem(Item, Graph, ReachSet, MemSet, NextList); - if (NextList.size() == WorkList.size()) { - // stop if loop in CFL graph - if (LoopInGraph) - break; - LoopInGraph = true; - } - NextList.swap(WorkList); NextList.clear(); } diff --git a/llvm/lib/Analysis/CFLGraph.h b/llvm/lib/Analysis/CFLGraph.h index 98e33fba7360..357951bdf54a 100644 --- a/llvm/lib/Analysis/CFLGraph.h +++ b/llvm/lib/Analysis/CFLGraph.h @@ -62,7 +62,7 @@ public: struct Edge { Node Other; - FieldOffset Offset; + int64_t Offset; }; using EdgeList = std::vector; @@ -125,7 +125,7 @@ public: Info->Attr |= Attr; } - void addEdge(Node From, Node To, FieldOffset Offset = 0) { + void addEdge(Node From, Node To, int64_t Offset = 0) { auto *FromInfo = getNode(From); assert(FromInfo != nullptr); auto *ToInfo = getNode(To); @@ -219,7 +219,7 @@ template class CFLGraphBuilder { Graph.addNode(InstantiatedValue{Val, 0}, Attr); } - void addAssignEdge(Value *From, Value *To, FieldOffset Offset = 0) { + void addAssignEdge(Value *From, Value *To, int64_t Offset = 0) { assert(From != nullptr && To != nullptr); if (!From->getType()->isPointerTy() || !To->getType()->isPointerTy()) return; @@ -312,7 +312,7 @@ template class CFLGraphBuilder { } void visitGEP(GEPOperator &GEPOp) { - FieldOffset Offset = UnknownOffset; + uint64_t Offset = UnknownOffset; APInt APOffset(DL.getPointerSizeInBits(GEPOp.getPointerAddressSpace()), 0); if (GEPOp.accumulateConstantOffset(DL, APOffset)) @@ -397,7 +397,7 @@ template class CFLGraphBuilder { if (IRelation.hasValue()) { Graph.addNode(IRelation->From); Graph.addNode(IRelation->To); - Graph.addEdge(IRelation->From, IRelation->To, IRelation->Offset); + Graph.addEdge(IRelation->From, IRelation->To); } } diff --git a/llvm/test/Analysis/CFLAliasAnalysis/Andersen/field.ll b/llvm/test/Analysis/CFLAliasAnalysis/Andersen/field.ll deleted file mode 100644 index 59b81a13a1f4..000000000000 --- a/llvm/test/Analysis/CFLAliasAnalysis/Andersen/field.ll +++ /dev/null @@ -1,102 +0,0 @@ -; NOTE: Assertions have been autogenerated by utils/update_test_checks.py -; 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: Function: test_simple_offsets -; CHECK: NoAlias: i64* %b, i64** %acast -; CHECK: NoAlias: i64** %acast, i64** %aoff -; CHECK: NoAlias: i64* %b, i64** %acastoff -; CHECK: NoAlias: i64** %acast, i64** %acastoff -; CHECK: NoAlias: [2 x i64*]* %a, i64* %acastload -; CHECK: MayAlias: i64* %acastload, i64* %b -; CHECK: NoAlias: i64* %acastload, i64** %aoff -; CHECK: NoAlias: i64* %acastload, i64** %acast -; CHECK: NoAlias: i64* %acastload, i64** %acastoff -define void @test_simple_offsets() { - %a = alloca [2 x i64*], align 8 - %b = alloca i64, align 4 - %aoff = getelementptr inbounds [2 x i64*], [2 x i64*]* %a, i64 0, i64 1 - store i64* %b, i64** %aoff - - %acast = bitcast [2 x i64*]* %a to i64** - %acastoff = getelementptr inbounds i64*, i64** %acast, i64 1 - %acastload = load i64*, i64** %acastoff - ret void -} - -; CHECK: Function: test_unknown_offset -; CHECK: MayAlias: [3 x i32]* %a, i32* %an -; CHECK: MayAlias: i32* %a4, i32* %an -; CHECK: MayAlias: i32* %a8, i32* %an -; CHECK: MayAlias: i32* %an, i32* %b -; CHECK: MayAlias: [3 x i32]* %a, i32* %bn -; CHECK: MayAlias: i32* %a4, i32* %bn -; CHECK: MayAlias: i32* %a8, i32* %bn -; CHECK: MayAlias: i32* %an, i32* %bn -; CHECK: MayAlias: i32* %b, i32* %bn -define void @test_unknown_offset(i32 %n) { - %a = alloca [3 x i32], align 4 - %a4 = getelementptr inbounds [3 x i32], [3 x i32]* %a, i32 0, i32 1 - %a8 = getelementptr inbounds [3 x i32], [3 x i32]* %a, i32 0, i32 2 - %an = getelementptr inbounds [3 x i32], [3 x i32]* %a, i32 0, i32 %n - %b = bitcast [3 x i32]* %a to i32* - %bn = getelementptr inbounds i32, i32* %b, i32 %n - ret void -} - - -%S = type { i32, i32, i32 } - -define i32* @return_arg(%S* %arg1, %S* %arg2) { - %acast = bitcast %S* %arg1 to i32* - %aoffset = getelementptr i32, i32* %acast, i32 1 - ret i32* %aoffset -} - -define i32* @return_derefence_arg(%S** %arg1) { - %deref = load %S*, %S** %arg1 - %deref2 = getelementptr %S, %S* %deref, i32 0, i32 1 - ret i32* %deref2 -} - -; CHECK-LABEL: Function: test_return_arg_offset -; CHECK: NoAlias: %S* %a, %S* %b -; CHECK: MayAlias: %S* %a, i32* %c -; CHECK: NoAlias: %S* %b, i32* %c -; CHECK: NoAlias: i32* %acast, i32* %c -; CHECK: NoAlias: i32* %acast, i32* %d -define void @test_return_arg_offset() { - %a = alloca %S, align 8 - %b = alloca %S, align 8 - - %c = call i32* @return_arg(%S* %a, %S* %b) - %d = getelementptr %S, %S* %a, i32 0, i32 1 - %acast = bitcast %S* %a to i32* - - ret void -} - -; CHECK-LABEL: Function: test_return_derefence_arg -; CHECK: NoAlias: %S** %p, i32* %c -; CHECK: NoAlias: %S* %lp, %S** %p -; CHECK: MayAlias: %S* %lp, i32* %c -; CHECK: NoAlias: %S** %p, i32* %lp_off -; CHECK: MayAlias: i32* %c, i32* %lp_off -; CHECK: MayAlias: %S* %lp, i32* %lp_off -; CHECK: NoAlias: %S** %p, i32* %acast -; CHECK: NoAlias: i32* %acast, i32* %c -; CHECK: MayAlias: %S* %lp, i32* %acast -; CHECK: NoAlias: i32* %acast, i32* %lp_off -define void @test_return_derefence_arg() { - %a = alloca %S, align 8 - %p = alloca %S*, align 8 - - store %S* %a, %S** %p - %c = call i32* @return_derefence_arg(%S** %p) - - %lp = load %S*, %S** %p - %lp_off = getelementptr %S, %S* %lp, i32 0, i32 1 - %acast = bitcast %S* %a to i32* - - ret void -}