LLVM API Documentation

RegAllocSolver.h
Go to the documentation of this file.
00001 //===-- RegAllocSolver.h - Heuristic PBQP Solver for reg alloc --*- C++ -*-===//
00002 //
00003 //                     The LLVM Compiler Infrastructure
00004 //
00005 // This file is distributed under the University of Illinois Open Source
00006 // License. See LICENSE.TXT for details.
00007 //
00008 //===----------------------------------------------------------------------===//
00009 //
00010 // Heuristic PBQP solver for register allocation problems. This solver uses a
00011 // graph reduction approach. Nodes of degree 0, 1 and 2 are eliminated with
00012 // optimality-preserving rules (see ReductionRules.h). When no low-degree (<3)
00013 // nodes are present, a heuristic derived from Brigg's graph coloring approach
00014 // is used.
00015 //
00016 //===----------------------------------------------------------------------===//
00017 
00018 #ifndef LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H
00019 #define LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H
00020 
00021 #include "CostAllocator.h"
00022 #include "Graph.h"
00023 #include "ReductionRules.h"
00024 #include "Solution.h"
00025 #include "llvm/Support/ErrorHandling.h"
00026 #include <limits>
00027 #include <vector>
00028 
00029 namespace PBQP {
00030 
00031   namespace RegAlloc {
00032 
00033     /// \brief Metadata to speed allocatability test.
00034     ///
00035     /// Keeps track of the number of infinities in each row and column.
00036     class MatrixMetadata {
00037     private:
00038       MatrixMetadata(const MatrixMetadata&);
00039       void operator=(const MatrixMetadata&);
00040     public:
00041       MatrixMetadata(const PBQP::Matrix& M)
00042         : WorstRow(0), WorstCol(0),
00043           UnsafeRows(new bool[M.getRows() - 1]()),
00044           UnsafeCols(new bool[M.getCols() - 1]()) {
00045 
00046         unsigned* ColCounts = new unsigned[M.getCols() - 1]();
00047 
00048         for (unsigned i = 1; i < M.getRows(); ++i) {
00049           unsigned RowCount = 0;
00050           for (unsigned j = 1; j < M.getCols(); ++j) {
00051             if (M[i][j] == std::numeric_limits<PBQP::PBQPNum>::infinity()) {
00052               ++RowCount;
00053               ++ColCounts[j - 1];
00054               UnsafeRows[i - 1] = true;
00055               UnsafeCols[j - 1] = true;
00056             }
00057           }
00058           WorstRow = std::max(WorstRow, RowCount);
00059         }
00060         unsigned WorstColCountForCurRow =
00061           *std::max_element(ColCounts, ColCounts + M.getCols() - 1);
00062         WorstCol = std::max(WorstCol, WorstColCountForCurRow);
00063         delete[] ColCounts;
00064       }
00065 
00066       ~MatrixMetadata() {
00067         delete[] UnsafeRows;
00068         delete[] UnsafeCols;
00069       }
00070 
00071       unsigned getWorstRow() const { return WorstRow; }
00072       unsigned getWorstCol() const { return WorstCol; }
00073       const bool* getUnsafeRows() const { return UnsafeRows; }
00074       const bool* getUnsafeCols() const { return UnsafeCols; }
00075 
00076     private:
00077       unsigned WorstRow, WorstCol;
00078       bool* UnsafeRows;
00079       bool* UnsafeCols;
00080     };
00081 
00082     class NodeMetadata {
00083     public:
00084       typedef enum { Unprocessed,
00085                      OptimallyReducible,
00086                      ConservativelyAllocatable,
00087                      NotProvablyAllocatable } ReductionState;
00088 
00089       NodeMetadata() : RS(Unprocessed), DeniedOpts(0), OptUnsafeEdges(nullptr){}
00090       ~NodeMetadata() { delete[] OptUnsafeEdges; }
00091 
00092       void setup(const Vector& Costs) {
00093         NumOpts = Costs.getLength() - 1;
00094         OptUnsafeEdges = new unsigned[NumOpts]();
00095       }
00096 
00097       ReductionState getReductionState() const { return RS; }
00098       void setReductionState(ReductionState RS) { this->RS = RS; }
00099 
00100       void handleAddEdge(const MatrixMetadata& MD, bool Transpose) {
00101         DeniedOpts += Transpose ? MD.getWorstCol() : MD.getWorstRow();
00102         const bool* UnsafeOpts =
00103           Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
00104         for (unsigned i = 0; i < NumOpts; ++i)
00105           OptUnsafeEdges[i] += UnsafeOpts[i];
00106       }
00107 
00108       void handleRemoveEdge(const MatrixMetadata& MD, bool Transpose) {
00109         DeniedOpts -= Transpose ? MD.getWorstCol() : MD.getWorstRow();
00110         const bool* UnsafeOpts =
00111           Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
00112         for (unsigned i = 0; i < NumOpts; ++i)
00113           OptUnsafeEdges[i] -= UnsafeOpts[i];
00114       }
00115 
00116       bool isConservativelyAllocatable() const {
00117         return (DeniedOpts < NumOpts) ||
00118                (std::find(OptUnsafeEdges, OptUnsafeEdges + NumOpts, 0) !=
00119                   OptUnsafeEdges + NumOpts);
00120       }
00121 
00122     private:
00123       ReductionState RS;
00124       unsigned NumOpts;
00125       unsigned DeniedOpts;
00126       unsigned* OptUnsafeEdges;
00127     };
00128 
00129     class RegAllocSolverImpl {
00130     private:
00131       typedef PBQP::MDMatrix<MatrixMetadata> RAMatrix;
00132     public:
00133       typedef PBQP::Vector RawVector;
00134       typedef PBQP::Matrix RawMatrix;
00135       typedef PBQP::Vector Vector;
00136       typedef RAMatrix     Matrix;
00137       typedef PBQP::PoolCostAllocator<
00138                 Vector, PBQP::VectorComparator,
00139                 Matrix, PBQP::MatrixComparator> CostAllocator;
00140 
00141       typedef PBQP::GraphBase::NodeId NodeId;
00142       typedef PBQP::GraphBase::EdgeId EdgeId;
00143 
00144       typedef RegAlloc::NodeMetadata NodeMetadata;
00145 
00146       struct EdgeMetadata { };
00147 
00148       typedef PBQP::Graph<RegAllocSolverImpl> Graph;
00149 
00150       RegAllocSolverImpl(Graph &G) : G(G) {}
00151 
00152       Solution solve() {
00153         G.setSolver(*this);
00154         Solution S;
00155         setup();
00156         S = backpropagate(G, reduce());
00157         G.unsetSolver();
00158         return S;
00159       }
00160 
00161       void handleAddNode(NodeId NId) {
00162         G.getNodeMetadata(NId).setup(G.getNodeCosts(NId));
00163       }
00164       void handleRemoveNode(NodeId NId) {}
00165       void handleSetNodeCosts(NodeId NId, const Vector& newCosts) {}
00166 
00167       void handleAddEdge(EdgeId EId) {
00168         handleReconnectEdge(EId, G.getEdgeNode1Id(EId));
00169         handleReconnectEdge(EId, G.getEdgeNode2Id(EId));
00170       }
00171 
00172       void handleRemoveEdge(EdgeId EId) {
00173         handleDisconnectEdge(EId, G.getEdgeNode1Id(EId));
00174         handleDisconnectEdge(EId, G.getEdgeNode2Id(EId));
00175       }
00176 
00177       void handleDisconnectEdge(EdgeId EId, NodeId NId) {
00178         NodeMetadata& NMd = G.getNodeMetadata(NId);
00179         const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
00180         NMd.handleRemoveEdge(MMd, NId == G.getEdgeNode2Id(EId));
00181         if (G.getNodeDegree(NId) == 3) {
00182           // This node is becoming optimally reducible.
00183           moveToOptimallyReducibleNodes(NId);
00184         } else if (NMd.getReductionState() ==
00185                      NodeMetadata::NotProvablyAllocatable &&
00186                    NMd.isConservativelyAllocatable()) {
00187           // This node just became conservatively allocatable.
00188           moveToConservativelyAllocatableNodes(NId);
00189         }
00190       }
00191 
00192       void handleReconnectEdge(EdgeId EId, NodeId NId) {
00193         NodeMetadata& NMd = G.getNodeMetadata(NId);
00194         const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
00195         NMd.handleAddEdge(MMd, NId == G.getEdgeNode2Id(EId));
00196       }
00197 
00198       void handleSetEdgeCosts(EdgeId EId, const Matrix& NewCosts) {
00199         handleRemoveEdge(EId);
00200 
00201         NodeId N1Id = G.getEdgeNode1Id(EId);
00202         NodeId N2Id = G.getEdgeNode2Id(EId);
00203         NodeMetadata& N1Md = G.getNodeMetadata(N1Id);
00204         NodeMetadata& N2Md = G.getNodeMetadata(N2Id);
00205         const MatrixMetadata& MMd = NewCosts.getMetadata();
00206         N1Md.handleAddEdge(MMd, N1Id != G.getEdgeNode1Id(EId));
00207         N2Md.handleAddEdge(MMd, N2Id != G.getEdgeNode1Id(EId));
00208       }
00209 
00210     private:
00211 
00212       void removeFromCurrentSet(NodeId NId) {
00213         switch (G.getNodeMetadata(NId).getReductionState()) {
00214           case NodeMetadata::Unprocessed: break;
00215           case NodeMetadata::OptimallyReducible:
00216             assert(OptimallyReducibleNodes.find(NId) !=
00217                      OptimallyReducibleNodes.end() &&
00218                    "Node not in optimally reducible set.");
00219             OptimallyReducibleNodes.erase(NId);
00220             break;
00221           case NodeMetadata::ConservativelyAllocatable:
00222             assert(ConservativelyAllocatableNodes.find(NId) !=
00223                      ConservativelyAllocatableNodes.end() &&
00224                    "Node not in conservatively allocatable set.");
00225             ConservativelyAllocatableNodes.erase(NId);
00226             break;
00227           case NodeMetadata::NotProvablyAllocatable:
00228             assert(NotProvablyAllocatableNodes.find(NId) !=
00229                      NotProvablyAllocatableNodes.end() &&
00230                    "Node not in not-provably-allocatable set.");
00231             NotProvablyAllocatableNodes.erase(NId);
00232             break;
00233         }
00234       }
00235 
00236       void moveToOptimallyReducibleNodes(NodeId NId) {
00237         removeFromCurrentSet(NId);
00238         OptimallyReducibleNodes.insert(NId);
00239         G.getNodeMetadata(NId).setReductionState(
00240           NodeMetadata::OptimallyReducible);
00241       }
00242 
00243       void moveToConservativelyAllocatableNodes(NodeId NId) {
00244         removeFromCurrentSet(NId);
00245         ConservativelyAllocatableNodes.insert(NId);
00246         G.getNodeMetadata(NId).setReductionState(
00247           NodeMetadata::ConservativelyAllocatable);
00248       }
00249 
00250       void moveToNotProvablyAllocatableNodes(NodeId NId) {
00251         removeFromCurrentSet(NId);
00252         NotProvablyAllocatableNodes.insert(NId);
00253         G.getNodeMetadata(NId).setReductionState(
00254           NodeMetadata::NotProvablyAllocatable);
00255       }
00256 
00257       void setup() {
00258         // Set up worklists.
00259         for (auto NId : G.nodeIds()) {
00260           if (G.getNodeDegree(NId) < 3)
00261             moveToOptimallyReducibleNodes(NId);
00262           else if (G.getNodeMetadata(NId).isConservativelyAllocatable())
00263             moveToConservativelyAllocatableNodes(NId);
00264           else
00265             moveToNotProvablyAllocatableNodes(NId);
00266         }
00267       }
00268 
00269       // Compute a reduction order for the graph by iteratively applying PBQP
00270       // reduction rules. Locally optimal rules are applied whenever possible (R0,
00271       // R1, R2). If no locally-optimal rules apply then any conservatively
00272       // allocatable node is reduced. Finally, if no conservatively allocatable
00273       // node exists then the node with the lowest spill-cost:degree ratio is
00274       // selected.
00275       std::vector<GraphBase::NodeId> reduce() {
00276         assert(!G.empty() && "Cannot reduce empty graph.");
00277 
00278         typedef GraphBase::NodeId NodeId;
00279         std::vector<NodeId> NodeStack;
00280 
00281         // Consume worklists.
00282         while (true) {
00283           if (!OptimallyReducibleNodes.empty()) {
00284             NodeSet::iterator NItr = OptimallyReducibleNodes.begin();
00285             NodeId NId = *NItr;
00286             OptimallyReducibleNodes.erase(NItr);
00287             NodeStack.push_back(NId);
00288             switch (G.getNodeDegree(NId)) {
00289               case 0:
00290                 break;
00291               case 1:
00292                 applyR1(G, NId);
00293                 break;
00294               case 2:
00295                 applyR2(G, NId);
00296                 break;
00297               default: llvm_unreachable("Not an optimally reducible node.");
00298             }
00299           } else if (!ConservativelyAllocatableNodes.empty()) {
00300             // Conservatively allocatable nodes will never spill. For now just
00301             // take the first node in the set and push it on the stack. When we
00302             // start optimizing more heavily for register preferencing, it may
00303             // would be better to push nodes with lower 'expected' or worst-case
00304             // register costs first (since early nodes are the most
00305             // constrained).
00306             NodeSet::iterator NItr = ConservativelyAllocatableNodes.begin();
00307             NodeId NId = *NItr;
00308             ConservativelyAllocatableNodes.erase(NItr);
00309             NodeStack.push_back(NId);
00310             G.disconnectAllNeighborsFromNode(NId);
00311 
00312           } else if (!NotProvablyAllocatableNodes.empty()) {
00313             NodeSet::iterator NItr =
00314               std::min_element(NotProvablyAllocatableNodes.begin(),
00315                                NotProvablyAllocatableNodes.end(),
00316                                SpillCostComparator(G));
00317             NodeId NId = *NItr;
00318             NotProvablyAllocatableNodes.erase(NItr);
00319             NodeStack.push_back(NId);
00320             G.disconnectAllNeighborsFromNode(NId);
00321           } else
00322             break;
00323         }
00324 
00325         return NodeStack;
00326       }
00327 
00328       class SpillCostComparator {
00329       public:
00330         SpillCostComparator(const Graph& G) : G(G) {}
00331         bool operator()(NodeId N1Id, NodeId N2Id) {
00332           PBQPNum N1SC = G.getNodeCosts(N1Id)[0] / G.getNodeDegree(N1Id);
00333           PBQPNum N2SC = G.getNodeCosts(N2Id)[0] / G.getNodeDegree(N2Id);
00334           return N1SC < N2SC;
00335         }
00336       private:
00337         const Graph& G;
00338       };
00339 
00340       Graph& G;
00341       typedef std::set<NodeId> NodeSet;
00342       NodeSet OptimallyReducibleNodes;
00343       NodeSet ConservativelyAllocatableNodes;
00344       NodeSet NotProvablyAllocatableNodes;
00345     };
00346 
00347     typedef Graph<RegAllocSolverImpl> Graph;
00348 
00349     inline Solution solve(Graph& G) {
00350       if (G.empty())
00351         return Solution();
00352       RegAllocSolverImpl RegAllocSolver(G);
00353       return RegAllocSolver.solve();
00354     }
00355 
00356   }
00357 }
00358 
00359 #endif // LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H