int main(int argc, char **argv) { assert(argc == 4); // TODO: maybe we want to get them from prompt args int num_vars = stoi(argv[2]); int depth = stoi(argv[3]); ifstream stream(argv[1]); StateTransitionSystemLoader loader(stream); unique_ptr<StateTransitionSystem> sts = loader.make(); State* si = sts->states()[0]; VariableAssignment vars = VariableAssignment(num_vars); for (auto *var : sts->variables()) { vars.set(var, false); } ReachabilitySet rs = computeReachabilitySet(Configuration(si, move(vars))); size_t siex = (num_vars + 1) + pow(2, depth + 1) - 1; cout << endl << "SIZE expected: " << siex; size_t size = rs.size(); cout << endl << "SIZE: " << size << endl; if (size - siex != 0) return 1; return 0; }
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) continue; auto Val = OuterMapping.first.Val; auto &AliasList = AliasMap[Val]; for (const auto &InnerMapping : OuterMapping.second) { // Again, AliasMap only cares about top-level values if (InnerMapping.first.DerefLevel == 0) AliasList.push_back(InnerMapping.first.Val); } // Sort AliasList for faster lookup std::sort(AliasList.begin(), AliasList.end(), std::less<const Value *>()); } // TODO: Populate function summary here }
static void propagate(InstantiatedValue From, InstantiatedValue To, MatchState State, ReachabilitySet &ReachSet, std::vector<WorkListItem> &WorkList) { if (From == To) return; if (ReachSet.insert(From, To, State)) WorkList.push_back(WorkListItem{From, To, State}); }
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 populateAliasMap(DenseMap<const Value *, std::vector<OffsetValue>> &AliasMap, const ReachabilitySet &ReachSet) { for (const auto &OuterMapping : ReachSet.value_mappings()) { // AliasMap only cares about top-level values if (OuterMapping.first.DerefLevel > 0) continue; auto Val = OuterMapping.first.Val; auto &AliasList = AliasMap[Val]; for (const auto &InnerMapping : OuterMapping.second) { // Again, AliasMap only cares about top-level values if (InnerMapping.first.DerefLevel == 0) AliasList.push_back(OffsetValue{InnerMapping.first.Val, UnknownOffset}); } // Sort AliasList for faster lookup llvm::sort(AliasList.begin(), AliasList.end()); } }
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; } }
static void populateExternalRelations( SmallVectorImpl<ExternalRelation> &ExtRelations, const Function &Fn, const SmallVectorImpl<Value *> &RetVals, const ReachabilitySet &ReachSet) { // If a function only returns one of its argument X, then X will be both an // argument and a return value at the same time. This is an edge case that // needs special handling here. for (const auto &Arg : Fn.args()) { if (is_contained(RetVals, &Arg)) { auto ArgVal = InterfaceValue{Arg.getArgNo() + 1, 0}; auto RetVal = InterfaceValue{0, 0}; ExtRelations.push_back(ExternalRelation{ArgVal, RetVal, 0}); } } // Below is the core summary construction logic. // A naive solution of adding only the value aliases that are parameters or // return values in ReachSet to the summary won't work: It is possible that a // parameter P is written into an intermediate value I, and the function // subsequently returns *I. In that case, *I is does not value alias anything // in ReachSet, and the naive solution will miss a summary edge from (P, 1) to // (I, 1). // To account for the aforementioned case, we need to check each non-parameter // and non-return value for the possibility of acting as an intermediate. // 'ValueMap' here records, for each value, which InterfaceValues read from or // write into it. If both the read list and the write list of a given value // are non-empty, we know that a particular value is an intermidate and we // need to add summary edges from the writes to the reads. DenseMap<Value *, ValueSummary> ValueMap; for (const auto &OuterMapping : ReachSet.value_mappings()) { if (auto Dst = getInterfaceValue(OuterMapping.first, RetVals)) { for (const auto &InnerMapping : OuterMapping.second) { // If Src is a param/return value, we get a same-level assignment. 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, 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}); if (hasWriteOnlyState(InnerMapping.second)) ValueMap[SrcIVal.Val].ToRecords.push_back( ValueSummary::Record{*Dst, SrcIVal.DerefLevel}); } } } } for (const auto &Mapping : ValueMap) { for (const auto &FromRecord : Mapping.second.FromRecords) { for (const auto &ToRecord : Mapping.second.ToRecords) { auto ToLevel = ToRecord.DerefLevel; auto FromLevel = FromRecord.DerefLevel; // Same-level assignments should have already been processed by now if (ToLevel == FromLevel) continue; auto SrcIndex = FromRecord.IValue.Index; auto SrcLevel = FromRecord.IValue.DerefLevel; auto DstIndex = ToRecord.IValue.Index; auto DstLevel = ToRecord.IValue.DerefLevel; if (ToLevel > FromLevel) SrcLevel += ToLevel - FromLevel; else DstLevel += FromLevel - ToLevel; ExtRelations.push_back(ExternalRelation{ InterfaceValue{SrcIndex, SrcLevel}, InterfaceValue{DstIndex, DstLevel}, UnknownOffset}); } } } // Remove duplicates in ExtRelations llvm::sort(ExtRelations.begin(), ExtRelations.end()); ExtRelations.erase(std::unique(ExtRelations.begin(), ExtRelations.end()), ExtRelations.end()); }