Path: blob/main/contrib/llvm-project/llvm/lib/Support/DAGDeltaAlgorithm.cpp
35232 views
//===--- DAGDeltaAlgorithm.cpp - A DAG Minimization Algorithm --*- C++ -*--===//1//2// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.3// See https://llvm.org/LICENSE.txt for license information.4// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception5//===----------------------------------------------------------------------===//6//7// The algorithm we use attempts to exploit the dependency information by8// minimizing top-down. We start by constructing an initial root set R, and9// then iteratively:10//11// 1. Minimize the set R using the test predicate:12// P'(S) = P(S union pred*(S))13//14// 2. Extend R to R' = R union pred(R).15//16// until a fixed point is reached.17//18// The idea is that we want to quickly prune entire portions of the graph, so we19// try to find high-level nodes that can be eliminated with all of their20// dependents.21//22// FIXME: The current algorithm doesn't actually provide a strong guarantee23// about the minimality of the result. The problem is that after adding nodes to24// the required set, we no longer consider them for elimination. For strictly25// well formed predicates, this doesn't happen, but it commonly occurs in26// practice when there are unmodelled dependencies. I believe we can resolve27// this by allowing the required set to be minimized as well, but need more test28// cases first.29//30//===----------------------------------------------------------------------===//3132#include "llvm/ADT/DAGDeltaAlgorithm.h"33#include "llvm/ADT/DeltaAlgorithm.h"34#include "llvm/Support/Debug.h"35#include "llvm/Support/Format.h"36#include "llvm/Support/raw_ostream.h"37#include <algorithm>38#include <cassert>39#include <map>40using namespace llvm;4142#define DEBUG_TYPE "dag-delta"4344namespace {4546class DAGDeltaAlgorithmImpl {47friend class DeltaActiveSetHelper;4849public:50typedef DAGDeltaAlgorithm::change_ty change_ty;51typedef DAGDeltaAlgorithm::changeset_ty changeset_ty;52typedef DAGDeltaAlgorithm::changesetlist_ty changesetlist_ty;53typedef DAGDeltaAlgorithm::edge_ty edge_ty;5455private:56typedef std::vector<change_ty>::iterator pred_iterator_ty;57typedef std::vector<change_ty>::iterator succ_iterator_ty;58typedef std::set<change_ty>::iterator pred_closure_iterator_ty;59typedef std::set<change_ty>::iterator succ_closure_iterator_ty;6061DAGDeltaAlgorithm &DDA;6263std::vector<change_ty> Roots;6465/// Cache of failed test results. Successful test results are never cached66/// since we always reduce following a success. We maintain an independent67/// cache from that used by the individual delta passes because we may get68/// hits across multiple individual delta invocations.69mutable std::set<changeset_ty> FailedTestsCache;7071// FIXME: Gross.72std::map<change_ty, std::vector<change_ty> > Predecessors;73std::map<change_ty, std::vector<change_ty> > Successors;7475std::map<change_ty, std::set<change_ty> > PredClosure;76std::map<change_ty, std::set<change_ty> > SuccClosure;7778private:79pred_iterator_ty pred_begin(change_ty Node) {80assert(Predecessors.count(Node) && "Invalid node!");81return Predecessors[Node].begin();82}83pred_iterator_ty pred_end(change_ty Node) {84assert(Predecessors.count(Node) && "Invalid node!");85return Predecessors[Node].end();86}8788pred_closure_iterator_ty pred_closure_begin(change_ty Node) {89assert(PredClosure.count(Node) && "Invalid node!");90return PredClosure[Node].begin();91}92pred_closure_iterator_ty pred_closure_end(change_ty Node) {93assert(PredClosure.count(Node) && "Invalid node!");94return PredClosure[Node].end();95}9697succ_iterator_ty succ_begin(change_ty Node) {98assert(Successors.count(Node) && "Invalid node!");99return Successors[Node].begin();100}101succ_iterator_ty succ_end(change_ty Node) {102assert(Successors.count(Node) && "Invalid node!");103return Successors[Node].end();104}105106succ_closure_iterator_ty succ_closure_begin(change_ty Node) {107assert(SuccClosure.count(Node) && "Invalid node!");108return SuccClosure[Node].begin();109}110succ_closure_iterator_ty succ_closure_end(change_ty Node) {111assert(SuccClosure.count(Node) && "Invalid node!");112return SuccClosure[Node].end();113}114115void UpdatedSearchState(const changeset_ty &Changes,116const changesetlist_ty &Sets,117const changeset_ty &Required) {118DDA.UpdatedSearchState(Changes, Sets, Required);119}120121/// ExecuteOneTest - Execute a single test predicate on the change set \p S.122bool ExecuteOneTest(const changeset_ty &S) {123// Check dependencies invariant.124LLVM_DEBUG({125for (changeset_ty::const_iterator it = S.begin(), ie = S.end(); it != ie;126++it)127for (succ_iterator_ty it2 = succ_begin(*it), ie2 = succ_end(*it);128it2 != ie2; ++it2)129assert(S.count(*it2) && "Attempt to run invalid changeset!");130});131132return DDA.ExecuteOneTest(S);133}134135public:136DAGDeltaAlgorithmImpl(DAGDeltaAlgorithm &DDA, const changeset_ty &Changes,137const std::vector<edge_ty> &Dependencies);138139changeset_ty Run();140141/// GetTestResult - Get the test result for the active set \p Changes with142/// \p Required changes from the cache, executing the test if necessary.143///144/// \param Changes - The set of active changes being minimized, which should145/// have their pred closure included in the test.146/// \param Required - The set of changes which have previously been147/// established to be required.148/// \return - The test result.149bool GetTestResult(const changeset_ty &Changes, const changeset_ty &Required);150};151152/// Helper object for minimizing an active set of changes.153class DeltaActiveSetHelper : public DeltaAlgorithm {154DAGDeltaAlgorithmImpl &DDAI;155156const changeset_ty &Required;157158protected:159/// UpdatedSearchState - Callback used when the search state changes.160void UpdatedSearchState(const changeset_ty &Changes,161const changesetlist_ty &Sets) override {162DDAI.UpdatedSearchState(Changes, Sets, Required);163}164165bool ExecuteOneTest(const changeset_ty &S) override {166return DDAI.GetTestResult(S, Required);167}168169public:170DeltaActiveSetHelper(DAGDeltaAlgorithmImpl &DDAI,171const changeset_ty &Required)172: DDAI(DDAI), Required(Required) {}173};174175} // namespace176177DAGDeltaAlgorithmImpl::DAGDeltaAlgorithmImpl(178DAGDeltaAlgorithm &DDA, const changeset_ty &Changes,179const std::vector<edge_ty> &Dependencies)180: DDA(DDA) {181for (change_ty Change : Changes) {182Predecessors.insert(std::make_pair(Change, std::vector<change_ty>()));183Successors.insert(std::make_pair(Change, std::vector<change_ty>()));184}185for (const edge_ty &Dep : Dependencies) {186Predecessors[Dep.second].push_back(Dep.first);187Successors[Dep.first].push_back(Dep.second);188}189190// Compute the roots.191for (change_ty Change : Changes)192if (succ_begin(Change) == succ_end(Change))193Roots.push_back(Change);194195// Pre-compute the closure of the successor relation.196std::vector<change_ty> Worklist(Roots.begin(), Roots.end());197while (!Worklist.empty()) {198change_ty Change = Worklist.back();199Worklist.pop_back();200201std::set<change_ty> &ChangeSuccs = SuccClosure[Change];202for (pred_iterator_ty it = pred_begin(Change),203ie = pred_end(Change); it != ie; ++it) {204SuccClosure[*it].insert(Change);205SuccClosure[*it].insert(ChangeSuccs.begin(), ChangeSuccs.end());206Worklist.push_back(*it);207}208}209210// Invert to form the predecessor closure map.211for (change_ty Change : Changes)212PredClosure.insert(std::make_pair(Change, std::set<change_ty>()));213for (change_ty Change : Changes)214for (succ_closure_iterator_ty it2 = succ_closure_begin(Change),215ie2 = succ_closure_end(Change);216it2 != ie2; ++it2)217PredClosure[*it2].insert(Change);218219// Dump useful debug info.220LLVM_DEBUG({221llvm::errs() << "-- DAGDeltaAlgorithmImpl --\n";222llvm::errs() << "Changes: [";223for (changeset_ty::const_iterator it = Changes.begin(), ie = Changes.end();224it != ie; ++it) {225if (it != Changes.begin())226llvm::errs() << ", ";227llvm::errs() << *it;228229if (succ_begin(*it) != succ_end(*it)) {230llvm::errs() << "(";231for (succ_iterator_ty it2 = succ_begin(*it), ie2 = succ_end(*it);232it2 != ie2; ++it2) {233if (it2 != succ_begin(*it))234llvm::errs() << ", ";235llvm::errs() << "->" << *it2;236}237llvm::errs() << ")";238}239}240llvm::errs() << "]\n";241242llvm::errs() << "Roots: [";243for (std::vector<change_ty>::const_iterator it = Roots.begin(),244ie = Roots.end();245it != ie; ++it) {246if (it != Roots.begin())247llvm::errs() << ", ";248llvm::errs() << *it;249}250llvm::errs() << "]\n";251252llvm::errs() << "Predecessor Closure:\n";253for (change_ty Change : Changes) {254llvm::errs() << format(" %-4d: [", Change);255for (pred_closure_iterator_ty it2 = pred_closure_begin(Change),256ie2 = pred_closure_end(Change);257it2 != ie2; ++it2) {258if (it2 != pred_closure_begin(Change))259llvm::errs() << ", ";260llvm::errs() << *it2;261}262llvm::errs() << "]\n";263}264265llvm::errs() << "Successor Closure:\n";266for (change_ty Change : Changes) {267llvm::errs() << format(" %-4d: [", Change);268for (succ_closure_iterator_ty it2 = succ_closure_begin(Change),269ie2 = succ_closure_end(Change);270it2 != ie2; ++it2) {271if (it2 != succ_closure_begin(Change))272llvm::errs() << ", ";273llvm::errs() << *it2;274}275llvm::errs() << "]\n";276}277278llvm::errs() << "\n\n";279});280}281282bool DAGDeltaAlgorithmImpl::GetTestResult(const changeset_ty &Changes,283const changeset_ty &Required) {284changeset_ty Extended(Required);285Extended.insert(Changes.begin(), Changes.end());286for (change_ty Change : Changes)287Extended.insert(pred_closure_begin(Change), pred_closure_end(Change));288289if (FailedTestsCache.count(Extended))290return false;291292bool Result = ExecuteOneTest(Extended);293if (!Result)294FailedTestsCache.insert(Extended);295296return Result;297}298299DAGDeltaAlgorithm::changeset_ty300DAGDeltaAlgorithmImpl::Run() {301// The current set of changes we are minimizing, starting at the roots.302changeset_ty CurrentSet(Roots.begin(), Roots.end());303304// The set of required changes.305changeset_ty Required;306307// Iterate until the active set of changes is empty. Convergence is guaranteed308// assuming input was a DAG.309//310// Invariant: CurrentSet intersect Required == {}311// Invariant: Required == (Required union succ*(Required))312while (!CurrentSet.empty()) {313LLVM_DEBUG({314llvm::errs() << "DAG_DD - " << CurrentSet.size() << " active changes, "315<< Required.size() << " required changes\n";316});317318// Minimize the current set of changes.319DeltaActiveSetHelper Helper(*this, Required);320changeset_ty CurrentMinSet = Helper.Run(CurrentSet);321322// Update the set of required changes. Since323// CurrentMinSet subset CurrentSet324// and after the last iteration,325// succ(CurrentSet) subset Required326// then327// succ(CurrentMinSet) subset Required328// and our invariant on Required is maintained.329Required.insert(CurrentMinSet.begin(), CurrentMinSet.end());330331// Replace the current set with the predecssors of the minimized set of332// active changes.333CurrentSet.clear();334for (change_ty CT : CurrentMinSet)335CurrentSet.insert(pred_begin(CT), pred_end(CT));336337// FIXME: We could enforce CurrentSet intersect Required == {} here if we338// wanted to protect against cyclic graphs.339}340341return Required;342}343344void DAGDeltaAlgorithm::anchor() {345}346347DAGDeltaAlgorithm::changeset_ty348DAGDeltaAlgorithm::Run(const changeset_ty &Changes,349const std::vector<edge_ty> &Dependencies) {350return DAGDeltaAlgorithmImpl(*this, Changes, Dependencies).Run();351}352353354