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