[PBQP] Add support for graph-level metadata to the PBQP graph. This will be used
[oota-llvm.git] / include / llvm / CodeGen / PBQP / RegAllocSolver.h
1 //===-- RegAllocSolver.h - Heuristic PBQP Solver for reg alloc --*- 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 // Heuristic PBQP solver for register allocation problems. This solver uses a
11 // graph reduction approach. Nodes of degree 0, 1 and 2 are eliminated with
12 // optimality-preserving rules (see ReductionRules.h). When no low-degree (<3)
13 // nodes are present, a heuristic derived from Brigg's graph coloring approach
14 // is used.
15 //
16 //===----------------------------------------------------------------------===//
17
18 #ifndef LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H
19 #define LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H
20
21 #include "CostAllocator.h"
22 #include "Graph.h"
23 #include "ReductionRules.h"
24 #include "Solution.h"
25 #include "llvm/Support/ErrorHandling.h"
26 #include <limits>
27 #include <vector>
28
29 namespace PBQP {
30
31   namespace RegAlloc {
32
33     /// \brief Metadata to speed allocatability test.
34     ///
35     /// Keeps track of the number of infinities in each row and column.
36     class MatrixMetadata {
37     private:
38       MatrixMetadata(const MatrixMetadata&);
39       void operator=(const MatrixMetadata&);
40     public:
41       MatrixMetadata(const PBQP::Matrix& M)
42         : WorstRow(0), WorstCol(0),
43           UnsafeRows(new bool[M.getRows() - 1]()),
44           UnsafeCols(new bool[M.getCols() - 1]()) {
45
46         unsigned* ColCounts = new unsigned[M.getCols() - 1]();
47
48         for (unsigned i = 1; i < M.getRows(); ++i) {
49           unsigned RowCount = 0;
50           for (unsigned j = 1; j < M.getCols(); ++j) {
51             if (M[i][j] == std::numeric_limits<PBQP::PBQPNum>::infinity()) {
52               ++RowCount;
53               ++ColCounts[j - 1];
54               UnsafeRows[i - 1] = true;
55               UnsafeCols[j - 1] = true;
56             }
57           }
58           WorstRow = std::max(WorstRow, RowCount);
59         }
60         unsigned WorstColCountForCurRow =
61           *std::max_element(ColCounts, ColCounts + M.getCols() - 1);
62         WorstCol = std::max(WorstCol, WorstColCountForCurRow);
63         delete[] ColCounts;
64       }
65
66       ~MatrixMetadata() {
67         delete[] UnsafeRows;
68         delete[] UnsafeCols;
69       }
70
71       unsigned getWorstRow() const { return WorstRow; }
72       unsigned getWorstCol() const { return WorstCol; }
73       const bool* getUnsafeRows() const { return UnsafeRows; }
74       const bool* getUnsafeCols() const { return UnsafeCols; }
75
76     private:
77       unsigned WorstRow, WorstCol;
78       bool* UnsafeRows;
79       bool* UnsafeCols;
80     };
81
82     class NodeMetadata {
83     public:
84       typedef enum { Unprocessed,
85                      OptimallyReducible,
86                      ConservativelyAllocatable,
87                      NotProvablyAllocatable } ReductionState;
88
89       NodeMetadata() : RS(Unprocessed), DeniedOpts(0), OptUnsafeEdges(nullptr){}
90       ~NodeMetadata() { delete[] OptUnsafeEdges; }
91
92       void setup(const Vector& Costs) {
93         NumOpts = Costs.getLength() - 1;
94         OptUnsafeEdges = new unsigned[NumOpts]();
95       }
96
97       ReductionState getReductionState() const { return RS; }
98       void setReductionState(ReductionState RS) { this->RS = RS; }
99
100       void handleAddEdge(const MatrixMetadata& MD, bool Transpose) {
101         DeniedOpts += Transpose ? MD.getWorstCol() : MD.getWorstRow();
102         const bool* UnsafeOpts =
103           Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
104         for (unsigned i = 0; i < NumOpts; ++i)
105           OptUnsafeEdges[i] += UnsafeOpts[i];
106       }
107
108       void handleRemoveEdge(const MatrixMetadata& MD, bool Transpose) {
109         DeniedOpts -= Transpose ? MD.getWorstCol() : MD.getWorstRow();
110         const bool* UnsafeOpts =
111           Transpose ? MD.getUnsafeCols() : MD.getUnsafeRows();
112         for (unsigned i = 0; i < NumOpts; ++i)
113           OptUnsafeEdges[i] -= UnsafeOpts[i];
114       }
115
116       bool isConservativelyAllocatable() const {
117         return (DeniedOpts < NumOpts) ||
118                (std::find(OptUnsafeEdges, OptUnsafeEdges + NumOpts, 0) !=
119                   OptUnsafeEdges + NumOpts);
120       }
121
122     private:
123       ReductionState RS;
124       unsigned NumOpts;
125       unsigned DeniedOpts;
126       unsigned* OptUnsafeEdges;
127     };
128
129     class RegAllocSolverImpl {
130     private:
131       typedef PBQP::MDMatrix<MatrixMetadata> RAMatrix;
132     public:
133       typedef PBQP::Vector RawVector;
134       typedef PBQP::Matrix RawMatrix;
135       typedef PBQP::Vector Vector;
136       typedef RAMatrix     Matrix;
137       typedef PBQP::PoolCostAllocator<
138                 Vector, PBQP::VectorComparator,
139                 Matrix, PBQP::MatrixComparator> CostAllocator;
140
141       typedef PBQP::GraphBase::NodeId NodeId;
142       typedef PBQP::GraphBase::EdgeId EdgeId;
143
144       typedef RegAlloc::NodeMetadata NodeMetadata;
145
146       struct EdgeMetadata { };
147       struct GraphMetadata { };
148
149       typedef PBQP::Graph<RegAllocSolverImpl> Graph;
150
151       RegAllocSolverImpl(Graph &G) : G(G) {}
152
153       Solution solve() {
154         G.setSolver(*this);
155         Solution S;
156         setup();
157         S = backpropagate(G, reduce());
158         G.unsetSolver();
159         return S;
160       }
161
162       void handleAddNode(NodeId NId) {
163         G.getNodeMetadata(NId).setup(G.getNodeCosts(NId));
164       }
165       void handleRemoveNode(NodeId NId) {}
166       void handleSetNodeCosts(NodeId NId, const Vector& newCosts) {}
167
168       void handleAddEdge(EdgeId EId) {
169         handleReconnectEdge(EId, G.getEdgeNode1Id(EId));
170         handleReconnectEdge(EId, G.getEdgeNode2Id(EId));
171       }
172
173       void handleRemoveEdge(EdgeId EId) {
174         handleDisconnectEdge(EId, G.getEdgeNode1Id(EId));
175         handleDisconnectEdge(EId, G.getEdgeNode2Id(EId));
176       }
177
178       void handleDisconnectEdge(EdgeId EId, NodeId NId) {
179         NodeMetadata& NMd = G.getNodeMetadata(NId);
180         const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
181         NMd.handleRemoveEdge(MMd, NId == G.getEdgeNode2Id(EId));
182         if (G.getNodeDegree(NId) == 3) {
183           // This node is becoming optimally reducible.
184           moveToOptimallyReducibleNodes(NId);
185         } else if (NMd.getReductionState() ==
186                      NodeMetadata::NotProvablyAllocatable &&
187                    NMd.isConservativelyAllocatable()) {
188           // This node just became conservatively allocatable.
189           moveToConservativelyAllocatableNodes(NId);
190         }
191       }
192
193       void handleReconnectEdge(EdgeId EId, NodeId NId) {
194         NodeMetadata& NMd = G.getNodeMetadata(NId);
195         const MatrixMetadata& MMd = G.getEdgeCosts(EId).getMetadata();
196         NMd.handleAddEdge(MMd, NId == G.getEdgeNode2Id(EId));
197       }
198
199       void handleSetEdgeCosts(EdgeId EId, const Matrix& NewCosts) {
200         handleRemoveEdge(EId);
201
202         NodeId N1Id = G.getEdgeNode1Id(EId);
203         NodeId N2Id = G.getEdgeNode2Id(EId);
204         NodeMetadata& N1Md = G.getNodeMetadata(N1Id);
205         NodeMetadata& N2Md = G.getNodeMetadata(N2Id);
206         const MatrixMetadata& MMd = NewCosts.getMetadata();
207         N1Md.handleAddEdge(MMd, N1Id != G.getEdgeNode1Id(EId));
208         N2Md.handleAddEdge(MMd, N2Id != G.getEdgeNode1Id(EId));
209       }
210
211     private:
212
213       void removeFromCurrentSet(NodeId NId) {
214         switch (G.getNodeMetadata(NId).getReductionState()) {
215           case NodeMetadata::Unprocessed: break;
216           case NodeMetadata::OptimallyReducible:
217             assert(OptimallyReducibleNodes.find(NId) !=
218                      OptimallyReducibleNodes.end() &&
219                    "Node not in optimally reducible set.");
220             OptimallyReducibleNodes.erase(NId);
221             break;
222           case NodeMetadata::ConservativelyAllocatable:
223             assert(ConservativelyAllocatableNodes.find(NId) !=
224                      ConservativelyAllocatableNodes.end() &&
225                    "Node not in conservatively allocatable set.");
226             ConservativelyAllocatableNodes.erase(NId);
227             break;
228           case NodeMetadata::NotProvablyAllocatable:
229             assert(NotProvablyAllocatableNodes.find(NId) !=
230                      NotProvablyAllocatableNodes.end() &&
231                    "Node not in not-provably-allocatable set.");
232             NotProvablyAllocatableNodes.erase(NId);
233             break;
234         }
235       }
236
237       void moveToOptimallyReducibleNodes(NodeId NId) {
238         removeFromCurrentSet(NId);
239         OptimallyReducibleNodes.insert(NId);
240         G.getNodeMetadata(NId).setReductionState(
241           NodeMetadata::OptimallyReducible);
242       }
243
244       void moveToConservativelyAllocatableNodes(NodeId NId) {
245         removeFromCurrentSet(NId);
246         ConservativelyAllocatableNodes.insert(NId);
247         G.getNodeMetadata(NId).setReductionState(
248           NodeMetadata::ConservativelyAllocatable);
249       }
250
251       void moveToNotProvablyAllocatableNodes(NodeId NId) {
252         removeFromCurrentSet(NId);
253         NotProvablyAllocatableNodes.insert(NId);
254         G.getNodeMetadata(NId).setReductionState(
255           NodeMetadata::NotProvablyAllocatable);
256       }
257
258       void setup() {
259         // Set up worklists.
260         for (auto NId : G.nodeIds()) {
261           if (G.getNodeDegree(NId) < 3)
262             moveToOptimallyReducibleNodes(NId);
263           else if (G.getNodeMetadata(NId).isConservativelyAllocatable())
264             moveToConservativelyAllocatableNodes(NId);
265           else
266             moveToNotProvablyAllocatableNodes(NId);
267         }
268       }
269
270       // Compute a reduction order for the graph by iteratively applying PBQP
271       // reduction rules. Locally optimal rules are applied whenever possible (R0,
272       // R1, R2). If no locally-optimal rules apply then any conservatively
273       // allocatable node is reduced. Finally, if no conservatively allocatable
274       // node exists then the node with the lowest spill-cost:degree ratio is
275       // selected.
276       std::vector<GraphBase::NodeId> reduce() {
277         assert(!G.empty() && "Cannot reduce empty graph.");
278
279         typedef GraphBase::NodeId NodeId;
280         std::vector<NodeId> NodeStack;
281
282         // Consume worklists.
283         while (true) {
284           if (!OptimallyReducibleNodes.empty()) {
285             NodeSet::iterator NItr = OptimallyReducibleNodes.begin();
286             NodeId NId = *NItr;
287             OptimallyReducibleNodes.erase(NItr);
288             NodeStack.push_back(NId);
289             switch (G.getNodeDegree(NId)) {
290               case 0:
291                 break;
292               case 1:
293                 applyR1(G, NId);
294                 break;
295               case 2:
296                 applyR2(G, NId);
297                 break;
298               default: llvm_unreachable("Not an optimally reducible node.");
299             }
300           } else if (!ConservativelyAllocatableNodes.empty()) {
301             // Conservatively allocatable nodes will never spill. For now just
302             // take the first node in the set and push it on the stack. When we
303             // start optimizing more heavily for register preferencing, it may
304             // would be better to push nodes with lower 'expected' or worst-case
305             // register costs first (since early nodes are the most
306             // constrained).
307             NodeSet::iterator NItr = ConservativelyAllocatableNodes.begin();
308             NodeId NId = *NItr;
309             ConservativelyAllocatableNodes.erase(NItr);
310             NodeStack.push_back(NId);
311             G.disconnectAllNeighborsFromNode(NId);
312
313           } else if (!NotProvablyAllocatableNodes.empty()) {
314             NodeSet::iterator NItr =
315               std::min_element(NotProvablyAllocatableNodes.begin(),
316                                NotProvablyAllocatableNodes.end(),
317                                SpillCostComparator(G));
318             NodeId NId = *NItr;
319             NotProvablyAllocatableNodes.erase(NItr);
320             NodeStack.push_back(NId);
321             G.disconnectAllNeighborsFromNode(NId);
322           } else
323             break;
324         }
325
326         return NodeStack;
327       }
328
329       class SpillCostComparator {
330       public:
331         SpillCostComparator(const Graph& G) : G(G) {}
332         bool operator()(NodeId N1Id, NodeId N2Id) {
333           PBQPNum N1SC = G.getNodeCosts(N1Id)[0] / G.getNodeDegree(N1Id);
334           PBQPNum N2SC = G.getNodeCosts(N2Id)[0] / G.getNodeDegree(N2Id);
335           return N1SC < N2SC;
336         }
337       private:
338         const Graph& G;
339       };
340
341       Graph& G;
342       typedef std::set<NodeId> NodeSet;
343       NodeSet OptimallyReducibleNodes;
344       NodeSet ConservativelyAllocatableNodes;
345       NodeSet NotProvablyAllocatableNodes;
346     };
347
348     typedef Graph<RegAllocSolverImpl> Graph;
349
350     inline Solution solve(Graph& G) {
351       if (G.empty())
352         return Solution();
353       RegAllocSolverImpl RegAllocSolver(G);
354       return RegAllocSolver.solve();
355     }
356
357   }
358 }
359
360 #endif // LLVM_CODEGEN_PBQP_REGALLOCSOLVER_H