1 //===-- Briggs.h --- Briggs Heuristic for PBQP ------------------*- C++ -*-===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 // 10 // This class implements the Briggs test for "allocability" of nodes in a 11 // PBQP graph representing a register allocation problem. Nodes which can be 12 // proven allocable (by a safe and relatively accurate test) are removed from 13 // the PBQP graph first. If no provably allocable node is present in the graph 14 // then the node with the minimal spill-cost to degree ratio is removed. 15 // 16 //===----------------------------------------------------------------------===// 17 18 #ifndef LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H 19 #define LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H 20 21 #include "../HeuristicSolver.h" 22 #include "../HeuristicBase.h" 23 24 #include <limits> 25 26 namespace PBQP { 27 namespace Heuristics { 28 29 /// \brief PBQP Heuristic which applies an allocability test based on 30 /// Briggs. 31 /// 32 /// This heuristic assumes that the elements of cost vectors in the PBQP 33 /// problem represent storage options, with the first being the spill 34 /// option and subsequent elements representing legal registers for the 35 /// corresponding node. Edge cost matrices are likewise assumed to represent 36 /// register constraints. 37 /// If one or more nodes can be proven allocable by this heuristic (by 38 /// inspection of their constraint matrices) then the allocable node of 39 /// highest degree is selected for the next reduction and pushed to the 40 /// solver stack. If no nodes can be proven allocable then the node with 41 /// the lowest estimated spill cost is selected and push to the solver stack 42 /// instead. 43 /// 44 /// This implementation is built on top of HeuristicBase. 45 class Briggs : public HeuristicBase<Briggs> { 46 private: 47 48 class LinkDegreeComparator { 49 public: 50 LinkDegreeComparator(HeuristicSolverImpl<Briggs> &s) : s(&s) {} 51 bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const { 52 if (s->getSolverDegree(n1Itr) > s->getSolverDegree(n2Itr)) 53 return true; 54 return false; 55 } 56 private: 57 HeuristicSolverImpl<Briggs> *s; 58 }; 59 60 class SpillCostComparator { 61 public: 62 SpillCostComparator(HeuristicSolverImpl<Briggs> &s) 63 : s(&s), g(&s.getGraph()) {} 64 bool operator()(Graph::NodeItr n1Itr, Graph::NodeItr n2Itr) const { 65 const PBQP::Vector &cv1 = g->getNodeCosts(n1Itr); 66 const PBQP::Vector &cv2 = g->getNodeCosts(n2Itr); 67 68 PBQPNum cost1 = cv1[0] / s->getSolverDegree(n1Itr); 69 PBQPNum cost2 = cv2[0] / s->getSolverDegree(n2Itr); 70 71 if (cost1 < cost2) 72 return true; 73 return false; 74 } 75 76 private: 77 HeuristicSolverImpl<Briggs> *s; 78 Graph *g; 79 }; 80 81 typedef std::list<Graph::NodeItr> RNAllocableList; 82 typedef RNAllocableList::iterator RNAllocableListItr; 83 84 typedef std::list<Graph::NodeItr> RNUnallocableList; 85 typedef RNUnallocableList::iterator RNUnallocableListItr; 86 87 public: 88 89 struct NodeData { 90 typedef std::vector<unsigned> UnsafeDegreesArray; 91 bool isHeuristic, isAllocable, isInitialized; 92 unsigned numDenied, numSafe; 93 UnsafeDegreesArray unsafeDegrees; 94 RNAllocableListItr rnaItr; 95 RNUnallocableListItr rnuItr; 96 97 NodeData() 98 : isHeuristic(false), isAllocable(false), isInitialized(false), 99 numDenied(0), numSafe(0) { } 100 }; 101 102 struct EdgeData { 103 typedef std::vector<unsigned> UnsafeArray; 104 unsigned worst, reverseWorst; 105 UnsafeArray unsafe, reverseUnsafe; 106 bool isUpToDate; 107 108 EdgeData() : worst(0), reverseWorst(0), isUpToDate(false) {} 109 }; 110 111 /// \brief Construct an instance of the Briggs heuristic. 112 /// @param solver A reference to the solver which is using this heuristic. 113 Briggs(HeuristicSolverImpl<Briggs> &solver) : 114 HeuristicBase<Briggs>(solver) {} 115 116 /// \brief Determine whether a node should be reduced using optimal 117 /// reduction. 118 /// @param nItr Node iterator to be considered. 119 /// @return True if the given node should be optimally reduced, false 120 /// otherwise. 121 /// 122 /// Selects nodes of degree 0, 1 or 2 for optimal reduction, with one 123 /// exception. Nodes whose spill cost (element 0 of their cost vector) is 124 /// infinite are checked for allocability first. Allocable nodes may be 125 /// optimally reduced, but nodes whose allocability cannot be proven are 126 /// selected for heuristic reduction instead. 127 bool shouldOptimallyReduce(Graph::NodeItr nItr) { 128 if (getSolver().getSolverDegree(nItr) < 3) { 129 return true; 130 } 131 // else 132 return false; 133 } 134 135 /// \brief Add a node to the heuristic reduce list. 136 /// @param nItr Node iterator to add to the heuristic reduce list. 137 void addToHeuristicReduceList(Graph::NodeItr nItr) { 138 NodeData &nd = getHeuristicNodeData(nItr); 139 initializeNode(nItr); 140 nd.isHeuristic = true; 141 if (nd.isAllocable) { 142 nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr); 143 } else { 144 nd.rnuItr = rnUnallocableList.insert(rnUnallocableList.end(), nItr); 145 } 146 } 147 148 /// \brief Heuristically reduce one of the nodes in the heuristic 149 /// reduce list. 150 /// @return True if a reduction takes place, false if the heuristic reduce 151 /// list is empty. 152 /// 153 /// If the list of allocable nodes is non-empty a node is selected 154 /// from it and pushed to the stack. Otherwise if the non-allocable list 155 /// is non-empty a node is selected from it and pushed to the stack. 156 /// If both lists are empty the method simply returns false with no action 157 /// taken. 158 bool heuristicReduce() { 159 if (!rnAllocableList.empty()) { 160 RNAllocableListItr rnaItr = 161 min_element(rnAllocableList.begin(), rnAllocableList.end(), 162 LinkDegreeComparator(getSolver())); 163 Graph::NodeItr nItr = *rnaItr; 164 rnAllocableList.erase(rnaItr); 165 handleRemoveNode(nItr); 166 getSolver().pushToStack(nItr); 167 return true; 168 } else if (!rnUnallocableList.empty()) { 169 RNUnallocableListItr rnuItr = 170 min_element(rnUnallocableList.begin(), rnUnallocableList.end(), 171 SpillCostComparator(getSolver())); 172 Graph::NodeItr nItr = *rnuItr; 173 rnUnallocableList.erase(rnuItr); 174 handleRemoveNode(nItr); 175 getSolver().pushToStack(nItr); 176 return true; 177 } 178 // else 179 return false; 180 } 181 182 /// \brief Prepare a change in the costs on the given edge. 183 /// @param eItr Edge iterator. 184 void preUpdateEdgeCosts(Graph::EdgeItr eItr) { 185 Graph &g = getGraph(); 186 Graph::NodeItr n1Itr = g.getEdgeNode1(eItr), 187 n2Itr = g.getEdgeNode2(eItr); 188 NodeData &n1 = getHeuristicNodeData(n1Itr), 189 &n2 = getHeuristicNodeData(n2Itr); 190 191 if (n1.isHeuristic) 192 subtractEdgeContributions(eItr, getGraph().getEdgeNode1(eItr)); 193 if (n2.isHeuristic) 194 subtractEdgeContributions(eItr, getGraph().getEdgeNode2(eItr)); 195 196 EdgeData &ed = getHeuristicEdgeData(eItr); 197 ed.isUpToDate = false; 198 } 199 200 /// \brief Handle the change in the costs on the given edge. 201 /// @param eItr Edge iterator. 202 void postUpdateEdgeCosts(Graph::EdgeItr eItr) { 203 // This is effectively the same as adding a new edge now, since 204 // we've factored out the costs of the old one. 205 handleAddEdge(eItr); 206 } 207 208 /// \brief Handle the addition of a new edge into the PBQP graph. 209 /// @param eItr Edge iterator for the added edge. 210 /// 211 /// Updates allocability of any nodes connected by this edge which are 212 /// being managed by the heuristic. If allocability changes they are 213 /// moved to the appropriate list. 214 void handleAddEdge(Graph::EdgeItr eItr) { 215 Graph &g = getGraph(); 216 Graph::NodeItr n1Itr = g.getEdgeNode1(eItr), 217 n2Itr = g.getEdgeNode2(eItr); 218 NodeData &n1 = getHeuristicNodeData(n1Itr), 219 &n2 = getHeuristicNodeData(n2Itr); 220 221 // If neither node is managed by the heuristic there's nothing to be 222 // done. 223 if (!n1.isHeuristic && !n2.isHeuristic) 224 return; 225 226 // Ok - we need to update at least one node. 227 computeEdgeContributions(eItr); 228 229 // Update node 1 if it's managed by the heuristic. 230 if (n1.isHeuristic) { 231 bool n1WasAllocable = n1.isAllocable; 232 addEdgeContributions(eItr, n1Itr); 233 updateAllocability(n1Itr); 234 if (n1WasAllocable && !n1.isAllocable) { 235 rnAllocableList.erase(n1.rnaItr); 236 n1.rnuItr = 237 rnUnallocableList.insert(rnUnallocableList.end(), n1Itr); 238 } 239 } 240 241 // Likewise for node 2. 242 if (n2.isHeuristic) { 243 bool n2WasAllocable = n2.isAllocable; 244 addEdgeContributions(eItr, n2Itr); 245 updateAllocability(n2Itr); 246 if (n2WasAllocable && !n2.isAllocable) { 247 rnAllocableList.erase(n2.rnaItr); 248 n2.rnuItr = 249 rnUnallocableList.insert(rnUnallocableList.end(), n2Itr); 250 } 251 } 252 } 253 254 /// \brief Handle disconnection of an edge from a node. 255 /// @param eItr Edge iterator for edge being disconnected. 256 /// @param nItr Node iterator for the node being disconnected from. 257 /// 258 /// Updates allocability of the given node and, if appropriate, moves the 259 /// node to a new list. 260 void handleRemoveEdge(Graph::EdgeItr eItr, Graph::NodeItr nItr) { 261 NodeData &nd = getHeuristicNodeData(nItr); 262 263 // If the node is not managed by the heuristic there's nothing to be 264 // done. 265 if (!nd.isHeuristic) 266 return; 267 268 EdgeData &ed = getHeuristicEdgeData(eItr); 269 (void)ed; 270 assert(ed.isUpToDate && "Edge data is not up to date."); 271 272 // Update node. 273 bool ndWasAllocable = nd.isAllocable; 274 subtractEdgeContributions(eItr, nItr); 275 updateAllocability(nItr); 276 277 // If the node has gone optimal... 278 if (shouldOptimallyReduce(nItr)) { 279 nd.isHeuristic = false; 280 addToOptimalReduceList(nItr); 281 if (ndWasAllocable) { 282 rnAllocableList.erase(nd.rnaItr); 283 } else { 284 rnUnallocableList.erase(nd.rnuItr); 285 } 286 } else { 287 // Node didn't go optimal, but we might have to move it 288 // from "unallocable" to "allocable". 289 if (!ndWasAllocable && nd.isAllocable) { 290 rnUnallocableList.erase(nd.rnuItr); 291 nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr); 292 } 293 } 294 } 295 296 private: 297 298 NodeData& getHeuristicNodeData(Graph::NodeItr nItr) { 299 return getSolver().getHeuristicNodeData(nItr); 300 } 301 302 EdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) { 303 return getSolver().getHeuristicEdgeData(eItr); 304 } 305 306 // Work out what this edge will contribute to the allocability of the 307 // nodes connected to it. 308 void computeEdgeContributions(Graph::EdgeItr eItr) { 309 EdgeData &ed = getHeuristicEdgeData(eItr); 310 311 if (ed.isUpToDate) 312 return; // Edge data is already up to date. 313 314 Matrix &eCosts = getGraph().getEdgeCosts(eItr); 315 316 unsigned numRegs = eCosts.getRows() - 1, 317 numReverseRegs = eCosts.getCols() - 1; 318 319 std::vector<unsigned> rowInfCounts(numRegs, 0), 320 colInfCounts(numReverseRegs, 0); 321 322 ed.worst = 0; 323 ed.reverseWorst = 0; 324 ed.unsafe.clear(); 325 ed.unsafe.resize(numRegs, 0); 326 ed.reverseUnsafe.clear(); 327 ed.reverseUnsafe.resize(numReverseRegs, 0); 328 329 for (unsigned i = 0; i < numRegs; ++i) { 330 for (unsigned j = 0; j < numReverseRegs; ++j) { 331 if (eCosts[i + 1][j + 1] == 332 std::numeric_limits<PBQPNum>::infinity()) { 333 ed.unsafe[i] = 1; 334 ed.reverseUnsafe[j] = 1; 335 ++rowInfCounts[i]; 336 ++colInfCounts[j]; 337 338 if (colInfCounts[j] > ed.worst) { 339 ed.worst = colInfCounts[j]; 340 } 341 342 if (rowInfCounts[i] > ed.reverseWorst) { 343 ed.reverseWorst = rowInfCounts[i]; 344 } 345 } 346 } 347 } 348 349 ed.isUpToDate = true; 350 } 351 352 // Add the contributions of the given edge to the given node's 353 // numDenied and safe members. No action is taken other than to update 354 // these member values. Once updated these numbers can be used by clients 355 // to update the node's allocability. 356 void addEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) { 357 EdgeData &ed = getHeuristicEdgeData(eItr); 358 359 assert(ed.isUpToDate && "Using out-of-date edge numbers."); 360 361 NodeData &nd = getHeuristicNodeData(nItr); 362 unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; 363 364 bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr); 365 EdgeData::UnsafeArray &unsafe = 366 nIsNode1 ? ed.unsafe : ed.reverseUnsafe; 367 nd.numDenied += nIsNode1 ? ed.worst : ed.reverseWorst; 368 369 for (unsigned r = 0; r < numRegs; ++r) { 370 if (unsafe[r]) { 371 if (nd.unsafeDegrees[r]==0) { 372 --nd.numSafe; 373 } 374 ++nd.unsafeDegrees[r]; 375 } 376 } 377 } 378 379 // Subtract the contributions of the given edge to the given node's 380 // numDenied and safe members. No action is taken other than to update 381 // these member values. Once updated these numbers can be used by clients 382 // to update the node's allocability. 383 void subtractEdgeContributions(Graph::EdgeItr eItr, Graph::NodeItr nItr) { 384 EdgeData &ed = getHeuristicEdgeData(eItr); 385 386 assert(ed.isUpToDate && "Using out-of-date edge numbers."); 387 388 NodeData &nd = getHeuristicNodeData(nItr); 389 unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; 390 391 bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr); 392 EdgeData::UnsafeArray &unsafe = 393 nIsNode1 ? ed.unsafe : ed.reverseUnsafe; 394 nd.numDenied -= nIsNode1 ? ed.worst : ed.reverseWorst; 395 396 for (unsigned r = 0; r < numRegs; ++r) { 397 if (unsafe[r]) { 398 if (nd.unsafeDegrees[r] == 1) { 399 ++nd.numSafe; 400 } 401 --nd.unsafeDegrees[r]; 402 } 403 } 404 } 405 406 void updateAllocability(Graph::NodeItr nItr) { 407 NodeData &nd = getHeuristicNodeData(nItr); 408 unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; 409 nd.isAllocable = nd.numDenied < numRegs || nd.numSafe > 0; 410 } 411 412 void initializeNode(Graph::NodeItr nItr) { 413 NodeData &nd = getHeuristicNodeData(nItr); 414 415 if (nd.isInitialized) 416 return; // Node data is already up to date. 417 418 unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1; 419 420 nd.numDenied = 0; 421 const Vector& nCosts = getGraph().getNodeCosts(nItr); 422 for (unsigned i = 1; i < nCosts.getLength(); ++i) { 423 if (nCosts[i] == std::numeric_limits<PBQPNum>::infinity()) 424 ++nd.numDenied; 425 } 426 427 nd.numSafe = numRegs; 428 nd.unsafeDegrees.resize(numRegs, 0); 429 430 typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr; 431 432 for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(nItr), 433 aeEnd = getSolver().solverEdgesEnd(nItr); 434 aeItr != aeEnd; ++aeItr) { 435 436 Graph::EdgeItr eItr = *aeItr; 437 computeEdgeContributions(eItr); 438 addEdgeContributions(eItr, nItr); 439 } 440 441 updateAllocability(nItr); 442 nd.isInitialized = true; 443 } 444 445 void handleRemoveNode(Graph::NodeItr xnItr) { 446 typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr; 447 std::vector<Graph::EdgeItr> edgesToRemove; 448 for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(xnItr), 449 aeEnd = getSolver().solverEdgesEnd(xnItr); 450 aeItr != aeEnd; ++aeItr) { 451 Graph::NodeItr ynItr = getGraph().getEdgeOtherNode(*aeItr, xnItr); 452 handleRemoveEdge(*aeItr, ynItr); 453 edgesToRemove.push_back(*aeItr); 454 } 455 while (!edgesToRemove.empty()) { 456 getSolver().removeSolverEdge(edgesToRemove.back()); 457 edgesToRemove.pop_back(); 458 } 459 } 460 461 RNAllocableList rnAllocableList; 462 RNUnallocableList rnUnallocableList; 463 }; 464 465 } 466 } 467 468 469 #endif // LLVM_CODEGEN_PBQP_HEURISTICS_BRIGGS_H 470