static Optional<InstantiatedValue> getNodeBelow(const CFLGraph &Graph, InstantiatedValue V) { auto NodeBelow = InstantiatedValue{V.Val, V.DerefLevel + 1}; if (Graph.getNode(NodeBelow)) return NodeBelow; return None; }
static void addInstructionToGraph(CFLAAResult &Analysis, Instruction &Inst, SmallVectorImpl<Value *> &ReturnedValues, CFLGraph &Graph, const TargetLibraryInfo &TLI) { // We don't want the edges of most "return" instructions, but we *do* want // to know what can be returned. if (isa<ReturnInst>(&Inst)) ReturnedValues.push_back(&Inst); if (!hasUsefulEdges(&Inst)) return; SmallVector<Edge, 8> Edges; argsToEdges(Analysis, &Inst, Edges, TLI); // In the case of an unused alloca (or similar), edges may be empty. Note // that it exists so we can potentially answer NoAlias. if (Edges.empty()) { auto MaybeVal = getTargetValue(&Inst); assert(MaybeVal.hasValue()); auto *Target = *MaybeVal; Graph.addNode(Target); return; } auto addEdgeToGraph = [&Graph](const Edge &E) { Graph.addEdge(E.From, E.To, E.Weight, E.AdditionalAttrs); }; SmallVector<ConstantExpr *, 4> ConstantExprs; for (const Edge &E : Edges) { addEdgeToGraph(E); if (auto *Constexpr = dyn_cast<ConstantExpr>(E.To)) ConstantExprs.push_back(Constexpr); if (auto *Constexpr = dyn_cast<ConstantExpr>(E.From)) ConstantExprs.push_back(Constexpr); } for (ConstantExpr *CE : ConstantExprs) { Edges.clear(); constexprToEdges(Analysis, *CE, Edges, TLI); std::for_each(Edges.begin(), Edges.end(), addEdgeToGraph); } }
static AliasAttrMap buildAttrMap(const CFLGraph &Graph, const ReachabilitySet &ReachSet) { AliasAttrMap AttrMap; std::vector<InstantiatedValue> 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; }
static void initializeWorkList(std::vector<WorkListItem> &WorkList, ReachabilitySet &ReachSet, const CFLGraph &Graph) { for (const auto &Mapping : Graph.value_mappings()) { auto Val = Mapping.first; auto &ValueInfo = Mapping.second; assert(ValueInfo.getNumLevels() > 0); // Insert all immediate assignment neighbors to the worklist for (unsigned I = 0, E = ValueInfo.getNumLevels(); I < E; ++I) { auto Src = InstantiatedValue{Val, I}; // 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, MatchState::FlowFrom, ReachSet, WorkList); propagate(Src, Edge.Other, MatchState::FlowTo, ReachSet, WorkList); } } } }
static void processWorkListItem(const WorkListItem &Item, const CFLGraph &Graph, ReachabilitySet &ReachSet, AliasMemSet &MemSet, std::vector<WorkListItem> &WorkList) { auto FromNode = Item.From; auto ToNode = Item.To; 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. 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<size_t>(FromState))) propagate(Src, *ToNodeBelow, ToState, ReachSet, WorkList); }; MemAliasPropagate(MatchState::FlowFromReadOnly, MatchState::FlowFromMemAliasReadOnly); MemAliasPropagate(MatchState::FlowToWriteOnly, MatchState::FlowToMemAliasWriteOnly); MemAliasPropagate(MatchState::FlowToReadWrite, MatchState::FlowToMemAliasReadWrite); } } // This is the core of the state machine walking algorithm. We expand ReachSet // based on which state we are at (which in turn dictates what edges we // should examine) // From a high-level point of view, the state machine here guarantees two // properties: // - If *X and *Y are memory aliases, then X and Y are value aliases // - 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, State, ReachSet, WorkList); }; auto NextRevAssignState = [&](MatchState State) { 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, State, ReachSet, WorkList); } }; switch (Item.State) { case MatchState::FlowFromReadOnly: NextRevAssignState(MatchState::FlowFromReadOnly); NextAssignState(MatchState::FlowToReadWrite); NextMemState(MatchState::FlowFromMemAliasReadOnly); break; case MatchState::FlowFromMemAliasNoReadWrite: NextRevAssignState(MatchState::FlowFromReadOnly); NextAssignState(MatchState::FlowToWriteOnly); break; case MatchState::FlowFromMemAliasReadOnly: NextRevAssignState(MatchState::FlowFromReadOnly); NextAssignState(MatchState::FlowToReadWrite); break; case MatchState::FlowToWriteOnly: NextAssignState(MatchState::FlowToWriteOnly); NextMemState(MatchState::FlowToMemAliasWriteOnly); break; case MatchState::FlowToReadWrite: NextAssignState(MatchState::FlowToReadWrite); NextMemState(MatchState::FlowToMemAliasReadWrite); break; case MatchState::FlowToMemAliasWriteOnly: NextAssignState(MatchState::FlowToWriteOnly); break; case MatchState::FlowToMemAliasReadWrite: NextAssignState(MatchState::FlowToReadWrite); break; } }
// Builds the graph + StratifiedSets for a function. CFLAAResult::FunctionInfo CFLAAResult::buildSetsFrom(Function *Fn) { CFLGraph Graph; SmallVector<Value *, 4> ReturnedValues; buildGraphFrom(*this, Fn, ReturnedValues, Graph, TLI); StratifiedSetsBuilder<Value *> Builder; SmallVector<Value *, 16> Worklist; SmallPtrSet<Value *, 16> Globals; for (auto Node : Graph.nodes()) Worklist.push_back(Node); while (!Worklist.empty()) { auto *CurValue = Worklist.pop_back_val(); Builder.add(CurValue); if (canSkipAddingToSets(CurValue)) continue; if (isa<GlobalValue>(CurValue)) Globals.insert(CurValue); for (const auto &Edge : Graph.edgesFor(CurValue)) { auto Label = Edge.Type; auto *OtherValue = Edge.Other; if (canSkipAddingToSets(OtherValue)) continue; if (isa<GlobalValue>(OtherValue)) Globals.insert(OtherValue); bool Added; switch (directionOfEdgeType(Label)) { case Level::Above: Added = Builder.addAbove(CurValue, OtherValue); break; case Level::Below: Added = Builder.addBelow(CurValue, OtherValue); break; case Level::Same: Added = Builder.addWith(CurValue, OtherValue); break; } auto Aliasing = Edge.Attr; Builder.noteAttributes(CurValue, Aliasing); Builder.noteAttributes(OtherValue, Aliasing); if (Added) Worklist.push_back(OtherValue); } } // Special handling for globals and arguments auto ProcessGlobalOrArgValue = [&Builder](Value &Val) { Builder.add(&Val); auto Attr = valueToAttr(&Val); if (Attr.hasValue()) { Builder.noteAttributes(&Val, *Attr); // TODO: do we need to filter out non-pointer values here? Builder.addAttributesBelow(&Val, AttrUnknown); } }; for (auto &Arg : Fn->args()) ProcessGlobalOrArgValue(Arg); for (auto *Global : Globals) ProcessGlobalOrArgValue(*Global); return FunctionInfo(Builder.build(), std::move(ReturnedValues)); }