LLVM API Documentation
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