| //===-- Briggs.h --- Briggs Heuristic for PBQP ------------------*- C++ -*-===// | |
| // | |
| // The LLVM Compiler Infrastructure | |
| // | |
| // This file is distributed under the University of Illinois Open Source | |
| // License. See LICENSE.TXT for details. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| // | |
| // This class implements the Briggs test for "allocability" of nodes in a | |
| // PBQP graph representing a register allocation problem. Nodes which can be | |
| // proven allocable (by a safe and relatively accurate test) are removed from | |
| // the PBQP graph first. If no provably allocable node is present in the graph | |
| // then the node with the minimal spill-cost to degree ratio is removed. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #ifndef LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H | |
| #define LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H | |
| #include "../HeuristicSolver.h" | |
| #include "../HeuristicBase.h" | |
| #include <limits> | |
| namespace PBQP { | |
| namespace Heuristics { | |
| /// \brief PBQP Heuristic which applies an allocability test based on | |
| /// Briggs. | |
| /// | |
| /// This heuristic assumes that the elements of cost vectors in the PBQP | |
| /// problem represent storage options, with the first being the spill | |
| /// option and subsequent elements representing legal registers for the | |
| /// corresponding node. Edge cost matrices are likewise assumed to represent | |
| /// register constraints. | |
| /// If one or more nodes can be proven allocable by this heuristic (by | |
| /// inspection of their constraint matrices) then the allocable node of | |
| /// highest degree is selected for the next reduction and pushed to the | |
| /// solver stack. If no nodes can be proven allocable then the node with | |
| /// the lowest estimated spill cost is selected and push to the solver stack | |
| /// instead. | |
| /// | |
| /// This implementation is built on top of HeuristicBase. | |
| class Briggs : public HeuristicBase<Briggs> { | |
| private: | |
| class LinkDegreeComparator { | |
| public: | |
| LinkDegreeComparator(HeuristicSolverImpl<Briggs> &s) : s(&s) {} | |
| bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const { | |
| if (s->getSolverDegree(n1Itr) > s->getSolverDegree(n2Itr)) | |
| return true; | |
| return false; | |
| } | |
| private: | |
| HeuristicSolverImpl<Briggs> *s; | |
| }; | |
| class SpillCostComparator { | |
| public: | |
| SpillCostComparator(HeuristicSolverImpl<Briggs> &s) | |
| : s(&s), g(&s.getGraph()) {} | |
| bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const { | |
| const PBQP::Vector &cv1 = g->getNodeCosts(n1Itr); | |
| const PBQP::Vector &cv2 = g->getNodeCosts(n2Itr); | |
| PBQPNum cost1 = cv1[0] / s->getSolverDegree(n1Itr); | |
| PBQPNum cost2 = cv2[0] / s->getSolverDegree(n2Itr); | |
| if (cost1 < cost2) | |
| return true; | |
| return false; | |
| } | |
| private: | |
| HeuristicSolverImpl<Briggs> *s; | |
| Graph *g; | |
| }; | |
| typedef std::list<Graph::NodeItr> RNAllocableList; | |
| typedef RNAllocableList::iterator RNAllocableListItr; | |
| typedef std::list<Graph::NodeItr> RNUnallocableList; | |
| typedef RNUnallocableList::iterator RNUnallocableListItr; | |
| public: | |
| struct NodeData { | |
| typedef std::vector<unsigned> UnsafeDegreesArray; | |
| bool isHeuristic, isAllocable, isInitialized; | |
| unsigned numDenied, numSafe; | |
| UnsafeDegreesArray unsafeDegrees; | |
| RNAllocableListItr rnaItr; | |
| RNUnallocableListItr rnuItr; | |
| NodeData() | |
| : isHeuristic(false), isAllocable(false), isInitialized(false), | |
| numDenied(0), numSafe(0) { } | |
| }; | |
| struct EdgeData { | |
| typedef std::vector<unsigned> UnsafeArray; | |
| unsigned worst, reverseWorst; | |
| UnsafeArray unsafe, reverseUnsafe; | |
| bool isUpToDate; | |
| EdgeData() : worst(0), reverseWorst(0), isUpToDate(false) {} | |
| }; | |
| /// \brief Construct an instance of the Briggs heuristic. | |
| /// @param solver A reference to the solver which is using this heuristic. | |
| Briggs(HeuristicSolverImpl<Briggs> &solver) : | |
| HeuristicBase<Briggs>(solver) {} | |
| /// \brief Determine whether a node should be reduced using optimal | |
| /// reduction. | |
| /// @param nItr Node iterator to be considered. | |
| /// @return True if the given node should be optimally reduced, false | |
| /// otherwise. | |
| /// | |
| /// Selects nodes of degree 0, 1 or 2 for optimal reduction, with one | |
| /// exception. Nodes whose spill cost (element 0 of their cost vector) is | |
| /// infinite are checked for allocability first. Allocable nodes may be | |
| /// optimally reduced, but nodes whose allocability cannot be proven are | |
| /// selected for heuristic reduction instead. | |
| bool shouldOptimallyReduce(Graph::NodeItr nItr) { | |
| if (getSolver().getSolverDegree(nItr) < 3) { | |
| return true; | |
| } | |
| // else | |
| return false; | |
| } | |
| /// \brief Add a node to the heuristic reduce list. | |
| /// @param nItr Node iterator to add to the heuristic reduce list. | |
| void addToHeuristicReduceList(Graph::NodeItr nItr) { | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| initializeNode(nItr); | |
| nd.isHeuristic = true; | |
| if (nd.isAllocable) { | |
| nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr); | |
| } else { | |
| nd.rnuItr = rnUnallocableList.insert(rnUnallocableList.end(), nItr); | |
| } | |
| } | |
| /// \brief Heuristically reduce one of the nodes in the heuristic | |
| /// reduce list. | |
| /// @return True if a reduction takes place, false if the heuristic reduce | |
| /// list is empty. | |
| /// | |
| /// If the list of allocable nodes is non-empty a node is selected | |
| /// from it and pushed to the stack. Otherwise if the non-allocable list | |
| /// is non-empty a node is selected from it and pushed to the stack. | |
| /// If both lists are empty the method simply returns false with no action | |
| /// taken. | |
| bool heuristicReduce() { | |
| if (!rnAllocableList.empty()) { | |
| RNAllocableListItr rnaItr = | |
| min_element(rnAllocableList.begin(), rnAllocableList.end(), | |
| LinkDegreeComparator(getSolver())); | |
| Graph::NodeItr nItr = *rnaItr; | |
| rnAllocableList.erase(rnaItr); | |
| handleRemoveNode(nItr); | |
| getSolver().pushToStack(nItr); | |
| return true; | |
| } else if (!rnUnallocableList.empty()) { | |
| RNUnallocableListItr rnuItr = | |
| min_element(rnUnallocableList.begin(), rnUnallocableList.end(), | |
| SpillCostComparator(getSolver())); | |
| Graph::NodeItr nItr = *rnuItr; | |
| rnUnallocableList.erase(rnuItr); | |
| handleRemoveNode(nItr); | |
| getSolver().pushToStack(nItr); | |
| return true; | |
| } | |
| // else | |
| return false; | |
| } | |
| /// \brief Prepare a change in the costs on the given edge. | |
| /// @param eItr Edge iterator. | |
| void preUpdateEdgeCosts(Graph::EdgeItr eItr) { | |
| Graph &g = getGraph(); | |
| Graph::NodeItr n1Itr = g.getEdgeNode1(eItr), | |
| n2Itr = g.getEdgeNode2(eItr); | |
| NodeData &n1 = getHeuristicNodeData(n1Itr), | |
| &n2 = getHeuristicNodeData(n2Itr); | |
| if (n1.isHeuristic) | |
| subtractEdgeContributions(eItr, getGraph().getEdgeNode1(eItr)); | |
| if (n2.isHeuristic) | |
| subtractEdgeContributions(eItr, getGraph().getEdgeNode2(eItr)); | |
| EdgeData &ed = getHeuristicEdgeData(eItr); | |
| ed.isUpToDate = false; | |
| } | |
| /// \brief Handle the change in the costs on the given edge. | |
| /// @param eItr Edge iterator. | |
| void postUpdateEdgeCosts(Graph::EdgeItr eItr) { | |
| // This is effectively the same as adding a new edge now, since | |
| // we've factored out the costs of the old one. | |
| handleAddEdge(eItr); | |
| } | |
| /// \brief Handle the addition of a new edge into the PBQP graph. | |
| /// @param eItr Edge iterator for the added edge. | |
| /// | |
| /// Updates allocability of any nodes connected by this edge which are | |
| /// being managed by the heuristic. If allocability changes they are | |
| /// moved to the appropriate list. | |
| void handleAddEdge(Graph::EdgeItr eItr) { | |
| Graph &g = getGraph(); | |
| Graph::NodeItr n1Itr = g.getEdgeNode1(eItr), | |
| n2Itr = g.getEdgeNode2(eItr); | |
| NodeData &n1 = getHeuristicNodeData(n1Itr), | |
| &n2 = getHeuristicNodeData(n2Itr); | |
| // If neither node is managed by the heuristic there's nothing to be | |
| // done. | |
| if (!n1.isHeuristic && !n2.isHeuristic) | |
| return; | |
| // Ok - we need to update at least one node. | |
| computeEdgeContributions(eItr); | |
| // Update node 1 if it's managed by the heuristic. | |
| if (n1.isHeuristic) { | |
| bool n1WasAllocable = n1.isAllocable; | |
| addEdgeContributions(eItr, n1Itr); | |
| updateAllocability(n1Itr); | |
| if (n1WasAllocable && !n1.isAllocable) { | |
| rnAllocableList.erase(n1.rnaItr); | |
| n1.rnuItr = | |
| rnUnallocableList.insert(rnUnallocableList.end(), n1Itr); | |
| } | |
| } | |
| // Likewise for node 2. | |
| if (n2.isHeuristic) { | |
| bool n2WasAllocable = n2.isAllocable; | |
| addEdgeContributions(eItr, n2Itr); | |
| updateAllocability(n2Itr); | |
| if (n2WasAllocable && !n2.isAllocable) { | |
| rnAllocableList.erase(n2.rnaItr); | |
| n2.rnuItr = | |
| rnUnallocableList.insert(rnUnallocableList.end(), n2Itr); | |
| } | |
| } | |
| } | |
| /// \brief Handle disconnection of an edge from a node. | |
| /// @param eItr Edge iterator for edge being disconnected. | |
| /// @param nItr Node iterator for the node being disconnected from. | |
| /// | |
| /// Updates allocability of the given node and, if appropriate, moves the | |
| /// node to a new list. | |
| void handleRemoveEdge(Graph::EdgeItr eItr, Graph::NodeItr nItr) { | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| // If the node is not managed by the heuristic there's nothing to be | |
| // done. | |
| if (!nd.isHeuristic) | |
| return; | |
| EdgeData &ed = getHeuristicEdgeData(eItr); | |
| (void)ed; | |
| assert(ed.isUpToDate && "Edge data is not up to date."); | |
| // Update node. | |
| bool ndWasAllocable = nd.isAllocable; | |
| subtractEdgeContributions(eItr, nItr); | |
| updateAllocability(nItr); | |
| // If the node has gone optimal... | |
| if (shouldOptimallyReduce(nItr)) { | |
| nd.isHeuristic = false; | |
| addToOptimalReduceList(nItr); | |
| if (ndWasAllocable) { | |
| rnAllocableList.erase(nd.rnaItr); | |
| } else { | |
| rnUnallocableList.erase(nd.rnuItr); | |
| } | |
| } else { | |
| // Node didn't go optimal, but we might have to move it | |
| // from "unallocable" to "allocable". | |
| if (!ndWasAllocable && nd.isAllocable) { | |
| rnUnallocableList.erase(nd.rnuItr); | |
| nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr); | |
| } | |
| } | |
| } | |
| private: | |
| NodeData& getHeuristicNodeData(Graph::NodeItr nItr) { | |
| return getSolver().getHeuristicNodeData(nItr); | |
| } | |
| EdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) { | |
| return getSolver().getHeuristicEdgeData(eItr); | |
| } | |
| // Work out what this edge will contribute to the allocability of the | |
| // nodes connected to it. | |
| void computeEdgeContributions(Graph::EdgeItr eItr) { | |
| EdgeData &ed = getHeuristicEdgeData(eItr); | |
| if (ed.isUpToDate) | |
| return; // Edge data is already up to date. | |
| Matrix &eCosts = getGraph().getEdgeCosts(eItr); | |
| unsigned numRegs = eCosts.getRows() - 1, | |
| numReverseRegs = eCosts.getCols() - 1; | |
| std::vector<unsigned> rowInfCounts(numRegs, 0), | |
| colInfCounts(numReverseRegs, 0); | |
| ed.worst = 0; | |
| ed.reverseWorst = 0; | |
| ed.unsafe.clear(); | |
| ed.unsafe.resize(numRegs, 0); | |
| ed.reverseUnsafe.clear(); | |
| ed.reverseUnsafe.resize(numReverseRegs, 0); | |
| for (unsigned i = 0; i < numRegs; ++i) { | |
| for (unsigned j = 0; j < numReverseRegs; ++j) { | |
| if (eCosts[i + 1][j + 1] == | |
| std::numeric_limits<PBQPNum>::infinity()) { | |
| ed.unsafe[i] = 1; | |
| ed.reverseUnsafe[j] = 1; | |
| ++rowInfCounts[i]; | |
| ++colInfCounts[j]; | |
| if (colInfCounts[j] > ed.worst) { | |
| ed.worst = colInfCounts[j]; | |
| } | |
| if (rowInfCounts[i] > ed.reverseWorst) { | |
| ed.reverseWorst = rowInfCounts[i]; | |
| } | |
| } | |
| } | |
| } | |
| ed.isUpToDate = true; | |
| } | |
| // Add the contributions of the given edge to the given node's | |
| // numDenied and safe members. No action is taken other than to update | |
| // these member values. Once updated these numbers can be used by clients | |
| // to update the node's allocability. | |
| void addEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) { | |
| EdgeData &ed = getHeuristicEdgeData(eItr); | |
| assert(ed.isUpToDate && "Using out-of-date edge numbers."); | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; | |
| bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr); | |
| EdgeData::UnsafeArray &unsafe = | |
| nIsNode1 ? ed.unsafe : ed.reverseUnsafe; | |
| nd.numDenied += nIsNode1 ? ed.worst : ed.reverseWorst; | |
| for (unsigned r = 0; r < numRegs; ++r) { | |
| if (unsafe[r]) { | |
| if (nd.unsafeDegrees[r]==0) { | |
| --nd.numSafe; | |
| } | |
| ++nd.unsafeDegrees[r]; | |
| } | |
| } | |
| } | |
| // Subtract the contributions of the given edge to the given node's | |
| // numDenied and safe members. No action is taken other than to update | |
| // these member values. Once updated these numbers can be used by clients | |
| // to update the node's allocability. | |
| void subtractEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) { | |
| EdgeData &ed = getHeuristicEdgeData(eItr); | |
| assert(ed.isUpToDate && "Using out-of-date edge numbers."); | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; | |
| bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr); | |
| EdgeData::UnsafeArray &unsafe = | |
| nIsNode1 ? ed.unsafe : ed.reverseUnsafe; | |
| nd.numDenied -= nIsNode1 ? ed.worst : ed.reverseWorst; | |
| for (unsigned r = 0; r < numRegs; ++r) { | |
| if (unsafe[r]) { | |
| if (nd.unsafeDegrees[r] == 1) { | |
| ++nd.numSafe; | |
| } | |
| --nd.unsafeDegrees[r]; | |
| } | |
| } | |
| } | |
| void updateAllocability(Graph::NodeItr nItr) { | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; | |
| nd.isAllocable = nd.numDenied < numRegs || nd.numSafe > 0; | |
| } | |
| void initializeNode(Graph::NodeItr nItr) { | |
| NodeData &nd = getHeuristicNodeData(nItr); | |
| if (nd.isInitialized) | |
| return; // Node data is already up to date. | |
| unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; | |
| nd.numDenied = 0; | |
| nd.numSafe = numRegs; | |
| nd.unsafeDegrees.resize(numRegs, 0); | |
| typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr; | |
| for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(nItr), | |
| aeEnd = getSolver().solverEdgesEnd(nItr); | |
| aeItr != aeEnd; ++aeItr) { | |
| Graph::EdgeItr eItr = *aeItr; | |
| computeEdgeContributions(eItr); | |
| addEdgeContributions(eItr, nItr); | |
| } | |
| updateAllocability(nItr); | |
| nd.isInitialized = true; | |
| } | |
| void handleRemoveNode(Graph::NodeItr xnItr) { | |
| typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr; | |
| std::vector<Graph::EdgeItr> edgesToRemove; | |
| for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(xnItr), | |
| aeEnd = getSolver().solverEdgesEnd(xnItr); | |
| aeItr != aeEnd; ++aeItr) { | |
| Graph::NodeItr ynItr = getGraph().getEdgeOtherNode(*aeItr, xnItr); | |
| handleRemoveEdge(*aeItr, ynItr); | |
| edgesToRemove.push_back(*aeItr); | |
| } | |
| while (!edgesToRemove.empty()) { | |
| getSolver().removeSolverEdge(edgesToRemove.back()); | |
| edgesToRemove.pop_back(); | |
| } | |
| } | |
| RNAllocableList rnAllocableList; | |
| RNUnallocableList rnUnallocableList; | |
| }; | |
| } | |
| } | |
| #endif // LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H |