1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
|
#pragma once
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wunused-parameter"
#endif
//===- ReductionRules.h - Reduction Rules -----------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// Reduction Rules.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_CODEGEN_PBQP_REDUCTIONRULES_H
#define LLVM_CODEGEN_PBQP_REDUCTIONRULES_H
#include "Graph.h"
#include "Math.h"
#include "Solution.h"
#include <cassert>
#include <limits>
namespace llvm {
namespace PBQP {
/// Reduce a node of degree one.
///
/// Propagate costs from the given node, which must be of degree one, to its
/// neighbor. Notify the problem domain.
template <typename GraphT>
void applyR1(GraphT &G, typename GraphT::NodeId NId) {
using NodeId = typename GraphT::NodeId;
using EdgeId = typename GraphT::EdgeId;
using Vector = typename GraphT::Vector;
using Matrix = typename GraphT::Matrix;
using RawVector = typename GraphT::RawVector;
assert(G.getNodeDegree(NId) == 1 &&
"R1 applied to node with degree != 1.");
EdgeId EId = *G.adjEdgeIds(NId).begin();
NodeId MId = G.getEdgeOtherNodeId(EId, NId);
const Matrix &ECosts = G.getEdgeCosts(EId);
const Vector &XCosts = G.getNodeCosts(NId);
RawVector YCosts = G.getNodeCosts(MId);
// Duplicate a little to avoid transposing matrices.
if (NId == G.getEdgeNode1Id(EId)) {
for (unsigned j = 0; j < YCosts.getLength(); ++j) {
PBQPNum Min = ECosts[0][j] + XCosts[0];
for (unsigned i = 1; i < XCosts.getLength(); ++i) {
PBQPNum C = ECosts[i][j] + XCosts[i];
if (C < Min)
Min = C;
}
YCosts[j] += Min;
}
} else {
for (unsigned i = 0; i < YCosts.getLength(); ++i) {
PBQPNum Min = ECosts[i][0] + XCosts[0];
for (unsigned j = 1; j < XCosts.getLength(); ++j) {
PBQPNum C = ECosts[i][j] + XCosts[j];
if (C < Min)
Min = C;
}
YCosts[i] += Min;
}
}
G.setNodeCosts(MId, YCosts);
G.disconnectEdge(EId, MId);
}
template <typename GraphT>
void applyR2(GraphT &G, typename GraphT::NodeId NId) {
using NodeId = typename GraphT::NodeId;
using EdgeId = typename GraphT::EdgeId;
using Vector = typename GraphT::Vector;
using Matrix = typename GraphT::Matrix;
using RawMatrix = typename GraphT::RawMatrix;
assert(G.getNodeDegree(NId) == 2 &&
"R2 applied to node with degree != 2.");
const Vector &XCosts = G.getNodeCosts(NId);
typename GraphT::AdjEdgeItr AEItr = G.adjEdgeIds(NId).begin();
EdgeId YXEId = *AEItr,
ZXEId = *(++AEItr);
NodeId YNId = G.getEdgeOtherNodeId(YXEId, NId),
ZNId = G.getEdgeOtherNodeId(ZXEId, NId);
bool FlipEdge1 = (G.getEdgeNode1Id(YXEId) == NId),
FlipEdge2 = (G.getEdgeNode1Id(ZXEId) == NId);
const Matrix *YXECosts = FlipEdge1 ?
new Matrix(G.getEdgeCosts(YXEId).transpose()) :
&G.getEdgeCosts(YXEId);
const Matrix *ZXECosts = FlipEdge2 ?
new Matrix(G.getEdgeCosts(ZXEId).transpose()) :
&G.getEdgeCosts(ZXEId);
unsigned XLen = XCosts.getLength(),
YLen = YXECosts->getRows(),
ZLen = ZXECosts->getRows();
RawMatrix Delta(YLen, ZLen);
for (unsigned i = 0; i < YLen; ++i) {
for (unsigned j = 0; j < ZLen; ++j) {
PBQPNum Min = (*YXECosts)[i][0] + (*ZXECosts)[j][0] + XCosts[0];
for (unsigned k = 1; k < XLen; ++k) {
PBQPNum C = (*YXECosts)[i][k] + (*ZXECosts)[j][k] + XCosts[k];
if (C < Min) {
Min = C;
}
}
Delta[i][j] = Min;
}
}
if (FlipEdge1)
delete YXECosts;
if (FlipEdge2)
delete ZXECosts;
EdgeId YZEId = G.findEdge(YNId, ZNId);
if (YZEId == G.invalidEdgeId()) {
YZEId = G.addEdge(YNId, ZNId, Delta);
} else {
const Matrix &YZECosts = G.getEdgeCosts(YZEId);
if (YNId == G.getEdgeNode1Id(YZEId)) {
G.updateEdgeCosts(YZEId, Delta + YZECosts);
} else {
G.updateEdgeCosts(YZEId, Delta.transpose() + YZECosts);
}
}
G.disconnectEdge(YXEId, YNId);
G.disconnectEdge(ZXEId, ZNId);
// TODO: Try to normalize newly added/modified edge.
}
#ifndef NDEBUG
// Does this Cost vector have any register options ?
template <typename VectorT>
bool hasRegisterOptions(const VectorT &V) {
unsigned VL = V.getLength();
// An empty or spill only cost vector does not provide any register option.
if (VL <= 1)
return false;
// If there are registers in the cost vector, but all of them have infinite
// costs, then ... there is no available register.
for (unsigned i = 1; i < VL; ++i)
if (V[i] != std::numeric_limits<PBQP::PBQPNum>::infinity())
return true;
return false;
}
#endif
// Find a solution to a fully reduced graph by backpropagation.
//
// Given a graph and a reduction order, pop each node from the reduction
// order and greedily compute a minimum solution based on the node costs, and
// the dependent costs due to previously solved nodes.
//
// Note - This does not return the graph to its original (pre-reduction)
// state: the existing solvers destructively alter the node and edge
// costs. Given that, the backpropagate function doesn't attempt to
// replace the edges either, but leaves the graph in its reduced
// state.
template <typename GraphT, typename StackT>
Solution backpropagate(GraphT& G, StackT stack) {
using NodeId = GraphBase::NodeId;
using Matrix = typename GraphT::Matrix;
using RawVector = typename GraphT::RawVector;
Solution s;
while (!stack.empty()) {
NodeId NId = stack.back();
stack.pop_back();
RawVector v = G.getNodeCosts(NId);
#ifndef NDEBUG
// Although a conservatively allocatable node can be allocated to a register,
// spilling it may provide a lower cost solution. Assert here that spilling
// is done by choice, not because there were no register available.
if (G.getNodeMetadata(NId).wasConservativelyAllocatable())
assert(hasRegisterOptions(v) && "A conservatively allocatable node "
"must have available register options");
#endif
for (auto EId : G.adjEdgeIds(NId)) {
const Matrix& edgeCosts = G.getEdgeCosts(EId);
if (NId == G.getEdgeNode1Id(EId)) {
NodeId mId = G.getEdgeNode2Id(EId);
v += edgeCosts.getColAsVector(s.getSelection(mId));
} else {
NodeId mId = G.getEdgeNode1Id(EId);
v += edgeCosts.getRowAsVector(s.getSelection(mId));
}
}
s.setSelection(NId, v.minIndex());
}
return s;
}
} // end namespace PBQP
} // end namespace llvm
#endif // LLVM_CODEGEN_PBQP_REDUCTIONRULES_H
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
|