1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2022 Collabora LTD
4 *
5 * Author: Gert Wollny <gert.wollny@collabora.com>
6 *
7 * Permission is hereby granted, free of charge, to any person obtaining a
8 * copy of this software and associated documentation files (the "Software"),
9 * to deal in the Software without restriction, including without limitation
10 * on the rights to use, copy, modify, merge, publish, distribute, sub
11 * license, and/or sell copies of the Software, and to permit persons to whom
12 * the Software is furnished to do so, subject to the following conditions:
13 *
14 * The above copyright notice and this permission notice (including the next
15 * paragraph) shall be included in all copies or substantial portions of the
16 * Software.
17 *
18 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL
21 * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM,
22 * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
23 * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
24 * USE OR OTHER DEALINGS IN THE SOFTWARE.
25 */
26
27 #include "sfn_ra.h"
28
29 #include "sfn_alu_defines.h"
30 #include "sfn_debug.h"
31
32 #include <cassert>
33 #include <queue>
34
35 namespace r600 {
36
37 void
prepare_row(int row)38 ComponentInterference::prepare_row(int row)
39 {
40 m_rows.resize(row + 1);
41 }
42
43 void
add(size_t idx1,size_t idx2)44 ComponentInterference::add(size_t idx1, size_t idx2)
45 {
46 assert(idx1 > idx2);
47 assert(m_rows.size() > idx1);
48 m_rows[idx1].push_back(idx2);
49 m_rows[idx2].push_back(idx1);
50 }
51
Interference(LiveRangeMap & map)52 Interference::Interference(LiveRangeMap& map):
53 m_map(map)
54 {
55 initialize();
56 }
57
58 void
initialize()59 Interference::initialize()
60 {
61 for (int i = 0; i < 4; ++i) {
62 initialize(m_components_maps[i], m_map.component(i));
63 }
64 }
65
66 void
initialize(ComponentInterference & comp_interference,LiveRangeMap::ChannelLiveRange & clr)67 Interference::initialize(ComponentInterference& comp_interference,
68 LiveRangeMap::ChannelLiveRange& clr)
69 {
70 for (size_t row = 0; row < clr.size(); ++row) {
71 auto& row_entry = clr[row];
72 comp_interference.prepare_row(row);
73 for (size_t col = 0; col < row; ++col) {
74 auto& col_entry = clr[col];
75 if (row_entry.m_end >= col_entry.m_start && row_entry.m_start <= col_entry.m_end)
76 comp_interference.add(row, col);
77 }
78 }
79 }
80
81 struct Group {
82 int priority;
83 std::array<PRegister, 4> channels;
84 };
85
86 static inline bool
operator <(const Group & lhs,const Group & rhs)87 operator<(const Group& lhs, const Group& rhs)
88 {
89 return lhs.priority < rhs.priority;
90 }
91
92 using GroupRegisters = std::priority_queue<Group>;
93
94 static bool
group_allocation(LiveRangeMap & lrm,const Interference & interference,GroupRegisters & groups)95 group_allocation(LiveRangeMap& lrm,
96 const Interference& interference,
97 GroupRegisters& groups)
98 {
99 int color = 0;
100 // allocate grouped registers
101 while (!groups.empty()) {
102 auto group = groups.top();
103 groups.pop();
104
105 int start_comp = 0;
106 while (!group.channels[start_comp])
107 ++start_comp;
108
109 sfn_log << SfnLog::merge << "Color group with " << *group.channels[start_comp]
110 << "\n";
111
112 // don't restart registers for exports, we may be able tp merge the
113 // export calls, is fthe registers are consecutive
114 if (group.priority > 0)
115 color = 0;
116
117 while (color < g_registers_end) {
118 /* Find the coloring for the first channel */
119 bool color_in_use = false;
120 int comp = start_comp;
121
122 auto& adjecency = interference.row(start_comp, group.channels[comp]->index());
123 auto& regs = lrm.component(comp);
124
125 sfn_log << SfnLog::merge << "Try color " << color;
126
127 for (auto adj : adjecency) {
128 if (regs[adj].m_color == color) {
129 color_in_use = true;
130 sfn_log << SfnLog::merge << " in use\n";
131 break;
132 }
133 }
134
135 if (color_in_use) {
136 ++color;
137 continue;
138 }
139
140 /* First channel color found, check whether it can be used for all
141 * channels */
142 while (comp < 4) {
143 sfn_log << SfnLog::merge << " interference: ";
144 if (group.channels[comp]) {
145 auto& component_life_ranges = lrm.component(comp);
146 auto& adjecencies = interference.row(comp, group.channels[comp]->index());
147
148 for (auto adj_index : adjecencies) {
149 sfn_log << SfnLog::merge << *component_life_ranges[adj_index].m_register
150 << " ";
151 if (component_life_ranges[adj_index].m_color == color) {
152 color_in_use = true;
153 sfn_log << SfnLog::merge << "used";
154 break;
155 }
156 }
157
158 if (color_in_use)
159 break;
160 }
161 ++comp;
162 }
163
164 /* We couldn't allocate all channels with this color, so try next */
165 if (color_in_use) {
166 ++color;
167 sfn_log << SfnLog::merge << "\n";
168 continue;
169 }
170 sfn_log << SfnLog::merge << " success\n";
171
172 /* Coloring successful */
173 for (auto reg : group.channels) {
174 if (reg) {
175 auto& vregs = lrm.component(reg->chan());
176 auto& vreg_cmp = vregs[reg->index()];
177 assert(vreg_cmp.m_start != -1 || vreg_cmp.m_end != -1);
178 vreg_cmp.m_color = color;
179 }
180 }
181 break;
182 }
183
184 if (color == g_registers_end)
185 return false;
186 }
187
188 return true;
189 }
190
191 static bool
scalar_allocation(LiveRangeMap & lrm,const Interference & interference)192 scalar_allocation(LiveRangeMap& lrm, const Interference& interference)
193 {
194 for (int comp = 0; comp < 4; ++comp) {
195 auto& live_ranges = lrm.component(comp);
196 for (auto& r : live_ranges) {
197 if (r.m_color != -1)
198 continue;
199
200 if (r.m_start == -1 && r.m_end == -1)
201 continue;
202
203 sfn_log << SfnLog::merge << "Color " << *r.m_register << "\n";
204
205 auto& adjecency = interference.row(comp, r.m_register->index());
206
207 int color = 0;
208
209 while (color < g_registers_end) {
210 bool color_in_use = false;
211 for (auto adj : adjecency) {
212 if (live_ranges[adj].m_color == color) {
213 color_in_use = true;
214 break;
215 }
216 }
217
218 if (color_in_use) {
219 ++color;
220 continue;
221 }
222
223 r.m_color = color;
224 break;
225 }
226 if (color == g_registers_end)
227 return false;
228 }
229 }
230 return true;
231 }
232
233 struct AluRegister {
234 int lifetime;
235 LiveRangeEntry *lre;
236 };
237
operator <(const AluRegister & lhs,const AluRegister & rhs)238 static inline bool operator < (const AluRegister& lhs, const AluRegister& rhs)
239 {
240 return lhs.lifetime > rhs.lifetime;
241 }
242
243 using AluClauseRegisters = std::priority_queue<AluRegister>;
244
245
246 static void
scalar_clause_local_allocation(LiveRangeMap & lrm,const Interference & interference)247 scalar_clause_local_allocation (LiveRangeMap& lrm, const Interference& interference)
248 {
249 for (int comp = 0; comp < 4; ++comp) {
250 AluClauseRegisters clause_reg;
251 auto& live_ranges = lrm.component(comp);
252 for (auto& r : live_ranges) {
253
254 sfn_log << SfnLog::merge << "LR: " << *r.m_register
255 << "[ " << r.m_start << ", " << r.m_end
256 << " ], AC: " << r.m_alu_clause_local
257 << " Color; " << r.m_color << "\n";
258
259 if (r.m_color != -1)
260 continue;
261
262 if (r.m_start == -1 &&
263 r.m_end == -1)
264 continue;
265
266 if (!r.m_alu_clause_local)
267 continue;
268
269 int len = r.m_end - r.m_start;
270 if (len > 1) {
271 clause_reg.push({len, &r});
272 sfn_log << SfnLog::merge << "Consider " << *r.m_register
273 << " for clause local\n";
274 }
275 }
276
277 while (!clause_reg.empty()) {
278 auto& r = clause_reg.top().lre;
279 clause_reg.pop();
280
281 sfn_log << SfnLog::merge << "Color " << *r->m_register << "\n";
282
283 auto& adjecency = interference.row(comp, r->m_register->index());
284
285 int color = g_clause_local_start;
286
287 while (color < g_clause_local_end) {
288 bool color_in_use = false;
289 for (auto adj : adjecency) {
290 if (live_ranges[adj].m_color == color) {
291 color_in_use = true;
292 break;
293 }
294 }
295
296 if (color_in_use) {
297 ++color;
298 continue;
299 }
300
301 r->m_color = color;
302 break;
303 }
304 if (color == g_clause_local_end)
305 break;
306 }
307 }
308 }
309
310 bool
register_allocation(LiveRangeMap & lrm)311 register_allocation(LiveRangeMap& lrm)
312 {
313 Interference interference(lrm);
314
315 std::map<int, Group> groups;
316
317 // setup fixed colors and group relationships
318 for (int i = 0; i < 4; ++i) {
319 auto& comp = lrm.component(i);
320 for (auto& entry : comp) {
321 sfn_log << SfnLog::merge << "Prepare RA for " << *entry.m_register << " ["
322 << entry.m_start << ", " << entry.m_end << "]\n";
323 auto pin = entry.m_register->pin();
324 if (entry.m_start == -1 && entry.m_end == -1) {
325 if (pin == pin_group || pin == pin_chgr)
326 entry.m_register->set_chan(7);
327 continue;
328 }
329
330 auto sel = entry.m_register->sel();
331 /* fully pinned registers contain system values with the
332 * definite register index, and array values are allocated
333 * right after the system registers, so just reuse the IDs (for now) */
334 if (pin == pin_fully || pin == pin_array) {
335 /* Must set all array element entries */
336 sfn_log << SfnLog::merge << "Pin color " << sel << " to " << *entry.m_register
337 << "\n";
338 entry.m_color = sel;
339 } else if (pin == pin_group || pin == pin_chgr) {
340 /* Groups must all have the same sel() value, because they are
341 * used as vec4 registers */
342 auto igroup = groups.find(sel);
343 if (igroup != groups.end()) {
344 igroup->second.channels[i] = entry.m_register;
345 assert(comp[entry.m_register->index()].m_register->index() ==
346 entry.m_register->index());
347 } else {
348 int priority = entry.m_use.test(LiveRangeEntry::use_export)
349 ? -entry.m_end
350 : entry.m_start;
351 Group group{
352 priority, {nullptr, nullptr, nullptr, nullptr}
353 };
354 group.channels[i] = entry.m_register;
355 assert(comp[group.channels[i]->index()].m_register->index() ==
356 entry.m_register->index());
357 groups[sel] = group;
358 }
359 }
360 }
361 }
362
363 GroupRegisters groups_sorted;
364 for (auto& [sel, group] : groups)
365 groups_sorted.push(group);
366
367 if (!group_allocation(lrm, interference, groups_sorted))
368 return false;
369
370 scalar_clause_local_allocation(lrm, interference);
371
372 if (!scalar_allocation(lrm, interference))
373 return false;
374
375 for (int i = 0; i < 4; ++i) {
376 auto& comp = lrm.component(i);
377 for (auto& entry : comp) {
378 sfn_log << SfnLog::merge << "Set " << *entry.m_register << " to ";
379 entry.m_register->set_sel(entry.m_color);
380 entry.m_register->set_pin(pin_none);
381 sfn_log << SfnLog::merge << *entry.m_register << "\n";
382 }
383 }
384
385 return true;
386 }
387
388 } // namespace r600
389