Home | History | Annotate | Download | only in Heuristics
      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 
     25 namespace 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::NodeItr n1Itr, Graph::NodeItr n2Itr) const {
     51           if (s->getSolverDegree(n1Itr) > s->getSolverDegree(n2Itr))
     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::NodeItr n1Itr, Graph::NodeItr n2Itr) const {
     64           const PBQP::Vector &cv1 = g->getNodeCosts(n1Itr);
     65           const PBQP::Vector &cv2 = g->getNodeCosts(n2Itr);
     66 
     67           PBQPNum cost1 = cv1[0] / s->getSolverDegree(n1Itr);
     68           PBQPNum cost2 = cv2[0] / s->getSolverDegree(n2Itr);
     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::NodeItr> RNAllocableList;
     81       typedef RNAllocableList::iterator RNAllocableListItr;
     82 
     83       typedef std::list<Graph::NodeItr> 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 nItr Node iterator 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::NodeItr nItr) {
    127         if (getSolver().getSolverDegree(nItr) < 3) {
    128           return true;
    129         }
    130         // else
    131         return false;
    132       }
    133 
    134       /// \brief Add a node to the heuristic reduce list.
    135       /// @param nItr Node iterator to add to the heuristic reduce list.
    136       void addToHeuristicReduceList(Graph::NodeItr nItr) {
    137         NodeData &nd = getHeuristicNodeData(nItr);
    138         initializeNode(nItr);
    139         nd.isHeuristic = true;
    140         if (nd.isAllocable) {
    141           nd.rnaItr = rnAllocableList.insert(rnAllocableList.end(), nItr);
    142         } else {
    143           nd.rnuItr = rnUnallocableList.insert(rnUnallocableList.end(), nItr);
    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::NodeItr nItr = *rnaItr;
    163           rnAllocableList.erase(rnaItr);
    164           handleRemoveNode(nItr);
    165           getSolver().pushToStack(nItr);
    166           return true;
    167         } else if (!rnUnallocableList.empty()) {
    168           RNUnallocableListItr rnuItr =
    169             min_element(rnUnallocableList.begin(), rnUnallocableList.end(),
    170                         SpillCostComparator(getSolver()));
    171           Graph::NodeItr nItr = *rnuItr;
    172           rnUnallocableList.erase(rnuItr);
    173           handleRemoveNode(nItr);
    174           getSolver().pushToStack(nItr);
    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 eItr Edge iterator.
    183       void preUpdateEdgeCosts(Graph::EdgeItr eItr) {
    184         Graph &g = getGraph();
    185         Graph::NodeItr n1Itr = g.getEdgeNode1(eItr),
    186                        n2Itr = g.getEdgeNode2(eItr);
    187         NodeData &n1 = getHeuristicNodeData(n1Itr),
    188                  &n2 = getHeuristicNodeData(n2Itr);
    189 
    190         if (n1.isHeuristic)
    191           subtractEdgeContributions(eItr, getGraph().getEdgeNode1(eItr));
    192         if (n2.isHeuristic)
    193           subtractEdgeContributions(eItr, getGraph().getEdgeNode2(eItr));
    194 
    195         EdgeData &ed = getHeuristicEdgeData(eItr);
    196         ed.isUpToDate = false;
    197       }
    198 
    199       /// \brief Handle the change in the costs on the given edge.
    200       /// @param eItr Edge iterator.
    201       void postUpdateEdgeCosts(Graph::EdgeItr eItr) {
    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(eItr);
    205       }
    206 
    207       /// \brief Handle the addition of a new edge into the PBQP graph.
    208       /// @param eItr Edge iterator 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::EdgeItr eItr) {
    214         Graph &g = getGraph();
    215         Graph::NodeItr n1Itr = g.getEdgeNode1(eItr),
    216                        n2Itr = g.getEdgeNode2(eItr);
    217         NodeData &n1 = getHeuristicNodeData(n1Itr),
    218                  &n2 = getHeuristicNodeData(n2Itr);
    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(eItr);
    227 
    228         // Update node 1 if it's managed by the heuristic.
    229         if (n1.isHeuristic) {
    230           bool n1WasAllocable = n1.isAllocable;
    231           addEdgeContributions(eItr, n1Itr);
    232           updateAllocability(n1Itr);
    233           if (n1WasAllocable && !n1.isAllocable) {
    234             rnAllocableList.erase(n1.rnaItr);
    235             n1.rnuItr =
    236               rnUnallocableList.insert(rnUnallocableList.end(), n1Itr);
    237           }
    238         }
    239 
    240         // Likewise for node 2.
    241         if (n2.isHeuristic) {
    242           bool n2WasAllocable = n2.isAllocable;
    243           addEdgeContributions(eItr, n2Itr);
    244           updateAllocability(n2Itr);
    245           if (n2WasAllocable && !n2.isAllocable) {
    246             rnAllocableList.erase(n2.rnaItr);
    247             n2.rnuItr =
    248               rnUnallocableList.insert(rnUnallocableList.end(), n2Itr);
    249           }
    250         }
    251       }
    252 
    253       /// \brief Handle disconnection of an edge from a node.
    254       /// @param eItr Edge iterator for edge being disconnected.
    255       /// @param nItr Node iterator 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::EdgeItr eItr, Graph::NodeItr nItr) {
    260         NodeData &nd = getHeuristicNodeData(nItr);
    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(eItr);
    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(eItr, nItr);
    274         updateAllocability(nItr);
    275 
    276         // If the node has gone optimal...
    277         if (shouldOptimallyReduce(nItr)) {
    278           nd.isHeuristic = false;
    279           addToOptimalReduceList(nItr);
    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(), nItr);
    291           }
    292         }
    293       }
    294 
    295     private:
    296 
    297       NodeData& getHeuristicNodeData(Graph::NodeItr nItr) {
    298         return getSolver().getHeuristicNodeData(nItr);
    299       }
    300 
    301       EdgeData& getHeuristicEdgeData(Graph::EdgeItr eItr) {
    302         return getSolver().getHeuristicEdgeData(eItr);
    303       }
    304 
    305       // Work out what this edge will contribute to the allocability of the
    306       // nodes connected to it.
    307       void computeEdgeContributions(Graph::EdgeItr eItr) {
    308         EdgeData &ed = getHeuristicEdgeData(eItr);
    309 
    310         if (ed.isUpToDate)
    311           return; // Edge data is already up to date.
    312 
    313         Matrix &eCosts = getGraph().getEdgeCosts(eItr);
    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::EdgeItr eItr, Graph::NodeItr nItr) {
    356         EdgeData &ed = getHeuristicEdgeData(eItr);
    357 
    358         assert(ed.isUpToDate && "Using out-of-date edge numbers.");
    359 
    360         NodeData &nd = getHeuristicNodeData(nItr);
    361         unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
    362 
    363         bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr);
    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::EdgeItr eItr, Graph::NodeItr nItr) {
    383         EdgeData &ed = getHeuristicEdgeData(eItr);
    384 
    385         assert(ed.isUpToDate && "Using out-of-date edge numbers.");
    386 
    387         NodeData &nd = getHeuristicNodeData(nItr);
    388         unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
    389 
    390         bool nIsNode1 = nItr == getGraph().getEdgeNode1(eItr);
    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::NodeItr nItr) {
    406         NodeData &nd = getHeuristicNodeData(nItr);
    407         unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
    408         nd.isAllocable = nd.numDenied < numRegs || nd.numSafe > 0;
    409       }
    410 
    411       void initializeNode(Graph::NodeItr nItr) {
    412         NodeData &nd = getHeuristicNodeData(nItr);
    413 
    414         if (nd.isInitialized)
    415           return; // Node data is already up to date.
    416 
    417         unsigned numRegs = getGraph().getNodeCosts(nItr).getLength() - 1;
    418 
    419         nd.numDenied = 0;
    420         const Vector& nCosts = getGraph().getNodeCosts(nItr);
    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(nItr),
    432                            aeEnd = getSolver().solverEdgesEnd(nItr);
    433              aeItr != aeEnd; ++aeItr) {
    434 
    435           Graph::EdgeItr eItr = *aeItr;
    436           computeEdgeContributions(eItr);
    437           addEdgeContributions(eItr, nItr);
    438         }
    439 
    440         updateAllocability(nItr);
    441         nd.isInitialized = true;
    442       }
    443 
    444       void handleRemoveNode(Graph::NodeItr xnItr) {
    445         typedef HeuristicSolverImpl<Briggs>::SolverEdgeItr SolverEdgeItr;
    446         std::vector<Graph::EdgeItr> edgesToRemove;
    447         for (SolverEdgeItr aeItr = getSolver().solverEdgesBegin(xnItr),
    448                            aeEnd = getSolver().solverEdgesEnd(xnItr);
    449              aeItr != aeEnd; ++aeItr) {
    450           Graph::NodeItr ynItr = getGraph().getEdgeOtherNode(*aeItr, xnItr);
    451           handleRemoveEdge(*aeItr, ynItr);
    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