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_instr_lds.h"
28
29 #include "sfn_debug.h"
30 #include "sfn_instr_alu.h"
31
32 namespace r600 {
33
34 using std::istream;
35
LDSReadInstr(std::vector<PRegister,Allocator<PRegister>> & value,AluInstr::SrcValues & address)36 LDSReadInstr::LDSReadInstr(std::vector<PRegister, Allocator<PRegister>>& value,
37 AluInstr::SrcValues& address):
38 m_address(address),
39 m_dest_value(value)
40 {
41 assert(m_address.size() == m_dest_value.size());
42
43 for (auto& v : value)
44 v->add_parent(this);
45
46 for (auto& s : m_address)
47 if (s->as_register())
48 s->as_register()->add_use(this);
49 }
50
51 void
accept(ConstInstrVisitor & visitor) const52 LDSReadInstr::accept(ConstInstrVisitor& visitor) const
53 {
54 visitor.visit(*this);
55 }
56
57 void
accept(InstrVisitor & visitor)58 LDSReadInstr::accept(InstrVisitor& visitor)
59 {
60 visitor.visit(this);
61 }
62
63 bool
remove_unused_components()64 LDSReadInstr::remove_unused_components()
65 {
66 uint8_t inactive_mask = 0;
67 for (size_t i = 0; i < m_dest_value.size(); ++i) {
68 if (m_dest_value[i]->uses().empty())
69 inactive_mask |= 1 << i;
70 }
71
72 if (!inactive_mask)
73 return false;
74
75 auto new_addr = AluInstr::SrcValues();
76 auto new_dest = std::vector<PRegister, Allocator<PRegister>>();
77
78 for (size_t i = 0; i < m_dest_value.size(); ++i) {
79 if ((1 << i) & inactive_mask) {
80 if (m_address[i]->as_register())
81 m_address[i]->as_register()->del_use(this);
82 m_dest_value[i]->del_parent(this);
83 } else {
84 new_dest.push_back(m_dest_value[i]);
85 new_addr.push_back(m_address[i]);
86 }
87 }
88
89 m_dest_value.swap(new_dest);
90 m_address.swap(new_addr);
91
92 return m_address.size() != new_addr.size();
93 }
94
95 class SetLDSAddrProperty : public AluInstrVisitor {
96 using AluInstrVisitor::visit;
visit(AluInstr * instr)97 void visit(AluInstr *instr) override { instr->set_alu_flag(alu_lds_address); }
98 };
99
100 AluInstr *
split(std::vector<AluInstr * > & out_block,AluInstr * last_lds_instr)101 LDSReadInstr::split(std::vector<AluInstr *>& out_block, AluInstr *last_lds_instr)
102 {
103 AluInstr *first_instr = nullptr;
104 SetLDSAddrProperty prop;
105 for (auto& addr : m_address) {
106 auto reg = addr->as_register();
107 if (reg) {
108 reg->del_use(this);
109 if (reg->parents().size() == 1) {
110 for (auto& p : reg->parents()) {
111 p->accept(prop);
112 }
113 }
114 }
115
116 auto instr = new AluInstr(DS_OP_READ_RET, nullptr, nullptr, addr);
117 instr->set_blockid(block_id(), index());
118
119 if (last_lds_instr)
120 instr->add_required_instr(last_lds_instr);
121 out_block.push_back(instr);
122 last_lds_instr = instr;
123 if (!first_instr) {
124 first_instr = instr;
125 first_instr->set_alu_flag(alu_lds_group_start);
126 } else {
127 /* In order to make it possible that the scheduler
128 * keeps the loads of a group close together, we
129 * require that the addresses are all already available
130 * when the first read instruction is emitted.
131 * Otherwise it might happen that the loads and reads from the
132 * queue are split across ALU cf clauses, and this is not allowed */
133 first_instr->add_extra_dependency(addr);
134 }
135 }
136
137 for (auto& dest : m_dest_value) {
138 dest->del_parent(this);
139 auto instr = new AluInstr(op1_mov,
140 dest,
141 new InlineConstant(ALU_SRC_LDS_OQ_A_POP),
142 AluInstr::last_write);
143 instr->add_required_instr(last_lds_instr);
144 instr->set_blockid(block_id(), index());
145 instr->set_always_keep();
146 out_block.push_back(instr);
147 last_lds_instr = instr;
148 }
149 if (last_lds_instr)
150 last_lds_instr->set_alu_flag(alu_lds_group_end);
151
152 return last_lds_instr;
153 }
154
155 bool
do_ready() const156 LDSReadInstr::do_ready() const
157 {
158 unreachable("This instruction is not handled by the scheduler");
159 return false;
160 }
161
162 void
do_print(std::ostream & os) const163 LDSReadInstr::do_print(std::ostream& os) const
164 {
165 os << "LDS_READ ";
166
167 os << "[ ";
168 for (auto d : m_dest_value) {
169 os << *d << " ";
170 }
171 os << "] : [ ";
172 for (auto a : m_address) {
173 os << *a << " ";
174 }
175 os << "]";
176 }
177
178 bool
is_equal_to(const LDSReadInstr & rhs) const179 LDSReadInstr::is_equal_to(const LDSReadInstr& rhs) const
180 {
181 if (m_address.size() != rhs.m_address.size())
182 return false;
183
184 for (unsigned i = 0; i < num_values(); ++i) {
185 if (!m_address[i]->equal_to(*rhs.m_address[i]))
186 return false;
187 if (!m_dest_value[i]->equal_to(*rhs.m_dest_value[i]))
188 return false;
189 }
190 return true;
191 }
192
193 auto
from_string(istream & is,ValueFactory & value_factory)194 LDSReadInstr::from_string(istream& is, ValueFactory& value_factory) -> Pointer
195 {
196 /* LDS_READ [ d1, d2, d3 ... ] : a1 a2 a3 ... */
197
198 std::string temp_str;
199
200 is >> temp_str;
201 assert(temp_str == "[");
202
203 std::vector<PRegister, Allocator<PRegister>> dests;
204 AluInstr::SrcValues srcs;
205
206 is >> temp_str;
207 while (temp_str != "]") {
208 auto dst = value_factory.dest_from_string(temp_str);
209 assert(dst);
210 dests.push_back(dst);
211 is >> temp_str;
212 }
213
214 is >> temp_str;
215 assert(temp_str == ":");
216 is >> temp_str;
217 assert(temp_str == "[");
218
219 is >> temp_str;
220 while (temp_str != "]") {
221 auto src = value_factory.src_from_string(temp_str);
222 assert(src);
223 srcs.push_back(src);
224 is >> temp_str;
225 };
226 assert(srcs.size() == dests.size() && !dests.empty());
227
228 return new LDSReadInstr(dests, srcs);
229 }
230
replace_dest(PRegister new_dest,AluInstr * move_instr)231 bool LDSReadInstr::replace_dest(PRegister new_dest, AluInstr *move_instr)
232 {
233 if (new_dest->pin() == pin_array)
234 return false;
235
236 auto old_dest = move_instr->psrc(0);
237
238 bool success = false;
239
240 for (unsigned i = 0; i < m_dest_value.size(); ++i) {
241 auto& dest = m_dest_value[i];
242
243 if (!dest->equal_to(*old_dest))
244 continue;
245
246 if (dest->equal_to(*new_dest))
247 continue;
248
249 if (dest->uses().size() > 1)
250 continue;
251
252 if (dest->pin() == pin_fully)
253 continue;
254
255 if (dest->pin() == pin_group)
256 continue;
257
258 if (dest->pin() == pin_chan && new_dest->chan() != dest->chan())
259 continue;
260
261 if (dest->pin() == pin_chan) {
262 if (new_dest->pin() == pin_group)
263 new_dest->set_pin(pin_chgr);
264 else
265 new_dest->set_pin(pin_chan);
266 }
267 m_dest_value[i] = new_dest;
268 success = true;
269 }
270 return success;
271 }
272
LDSAtomicInstr(ESDOp op,PRegister dest,PVirtualValue address,const SrcValues & srcs)273 LDSAtomicInstr::LDSAtomicInstr(ESDOp op,
274 PRegister dest,
275 PVirtualValue address,
276 const SrcValues& srcs):
277 m_opcode(op),
278 m_address(address),
279 m_dest(dest),
280 m_srcs(srcs)
281 {
282 if (m_dest)
283 m_dest->add_parent(this);
284
285 if (m_address->as_register())
286 m_address->as_register()->add_use(this);
287
288 for (auto& s : m_srcs) {
289 if (s->as_register())
290 s->as_register()->add_use(this);
291 }
292 }
293
294 void
accept(ConstInstrVisitor & visitor) const295 LDSAtomicInstr::accept(ConstInstrVisitor& visitor) const
296 {
297 visitor.visit(*this);
298 }
299
300 void
accept(InstrVisitor & visitor)301 LDSAtomicInstr::accept(InstrVisitor& visitor)
302 {
303 visitor.visit(this);
304 }
305
306 AluInstr *
split(std::vector<AluInstr * > & out_block,AluInstr * last_lds_instr)307 LDSAtomicInstr::split(std::vector<AluInstr *>& out_block, AluInstr *last_lds_instr)
308 {
309 AluInstr::SrcValues srcs = {m_address};
310
311 for (auto& s : m_srcs)
312 srcs.push_back(s);
313
314 for (auto& s : srcs) {
315 if (s->as_register())
316 s->as_register()->del_use(this);
317 }
318
319 SetLDSAddrProperty prop;
320 auto reg = srcs[0]->as_register();
321 if (reg) {
322 reg->del_use(this);
323 if (reg->parents().size() == 1) {
324 for (auto& p : reg->parents()) {
325 p->accept(prop);
326 }
327 }
328 }
329
330 auto op_instr = new AluInstr(m_opcode, srcs, {});
331 op_instr->set_blockid(block_id(), index());
332
333 if (last_lds_instr) {
334 op_instr->add_required_instr(last_lds_instr);
335 }
336 last_lds_instr = op_instr;
337
338 out_block.push_back(op_instr);
339 if (m_dest) {
340 op_instr->set_alu_flag(alu_lds_group_start);
341 m_dest->del_parent(this);
342 auto read_instr = new AluInstr(op1_mov,
343 m_dest,
344 new InlineConstant(ALU_SRC_LDS_OQ_A_POP),
345 AluInstr::last_write);
346 read_instr->add_required_instr(op_instr);
347 read_instr->set_blockid(block_id(), index());
348 read_instr->set_alu_flag(alu_lds_group_end);
349 out_block.push_back(read_instr);
350 last_lds_instr = read_instr;
351 }
352 return last_lds_instr;
353 }
354
355 bool
replace_source(PRegister old_src,PVirtualValue new_src)356 LDSAtomicInstr::replace_source(PRegister old_src, PVirtualValue new_src)
357 {
358 bool process = false;
359
360 if (new_src->as_uniform()) {
361 if (m_srcs.size() > 2) {
362 int nconst = 0;
363 for (auto& s : m_srcs) {
364 if (s->as_uniform() && !s->equal_to(*old_src))
365 ++nconst;
366 }
367 /* Conservative check: with two kcache values can always live,
368 * tree might be a problem, don't care for now, just reject
369 */
370 if (nconst > 2)
371 return false;
372 }
373
374 /* indirect constant buffer access means new CF, and this is something
375 * we can't do in the middle of an LDS read group */
376 auto u = new_src->as_uniform();
377 if (u->buf_addr())
378 return false;
379 }
380
381 /* If the source is an array element, we assume that there
382 * might have been an (untracked) indirect access, so don't replace
383 * this source */
384 if (old_src->pin() == pin_array || new_src->pin() == pin_array)
385 return false;
386
387 for (unsigned i = 0; i < m_srcs.size(); ++i) {
388 if (old_src->equal_to(*m_srcs[i])) {
389 m_srcs[i] = new_src;
390 process = true;
391 }
392 }
393
394 if (process) {
395 auto r = new_src->as_register();
396 if (r)
397 r->add_use(this);
398 old_src->del_use(this);
399 }
400 return process;
401 }
402
403 bool
do_ready() const404 LDSAtomicInstr::do_ready() const
405 {
406 unreachable("This instruction is not handled by the scheduler");
407 return false;
408 }
409
410 void
do_print(std::ostream & os) const411 LDSAtomicInstr::do_print(std::ostream& os) const
412 {
413 auto ii = lds_ops.find(m_opcode);
414 assert(ii != lds_ops.end());
415
416 os << "LDS " << ii->second.name << " ";
417 if (m_dest)
418 os << *m_dest;
419 else
420 os << "__.x";
421
422 os << " [ " << *m_address << " ] : " << *m_srcs[0];
423 if (m_srcs.size() > 1)
424 os << " " << *m_srcs[1];
425 }
426
427 bool
is_equal_to(const LDSAtomicInstr & rhs) const428 LDSAtomicInstr::is_equal_to(const LDSAtomicInstr& rhs) const
429 {
430 if (m_srcs.size() != rhs.m_srcs.size())
431 return false;
432
433 for (unsigned i = 0; i < m_srcs.size(); ++i) {
434 if (!m_srcs[i]->equal_to(*rhs.m_srcs[i]))
435 return false;
436 }
437
438 return m_opcode == rhs.m_opcode && sfn_value_equal(m_address, rhs.m_address) &&
439 sfn_value_equal(m_dest, rhs.m_dest);
440 }
441
442 auto
from_string(istream & is,ValueFactory & value_factory)443 LDSAtomicInstr::from_string(istream& is, ValueFactory& value_factory) -> Pointer
444 {
445 /* LDS WRITE2 __.x [ R1.x ] : R2.y R3.z */
446 /* LDS WRITE __.x [ R1.x ] : R2.y */
447 /* LDS ATOMIC_ADD_RET [ R5.y ] : R2.y */
448
449 std::string temp_str;
450
451 is >> temp_str;
452
453 ESDOp opcode = DS_OP_INVALID;
454 int nsrc = 0;
455
456 for (auto& [op, opinfo] : lds_ops) {
457 if (temp_str == opinfo.name) {
458 opcode = op;
459 nsrc = opinfo.nsrc;
460 break;
461 }
462 }
463
464 assert(opcode != DS_OP_INVALID);
465
466 is >> temp_str;
467
468 PRegister dest = nullptr;
469 if (temp_str[0] != '_')
470 dest = value_factory.dest_from_string(temp_str);
471
472 is >> temp_str;
473 assert(temp_str == "[");
474 is >> temp_str;
475 auto addr = value_factory.src_from_string(temp_str);
476
477 is >> temp_str;
478 assert(temp_str == "]");
479
480 is >> temp_str;
481 assert(temp_str == ":");
482
483 AluInstr::SrcValues srcs;
484 for (int i = 0; i < nsrc - 1; ++i) {
485 is >> temp_str;
486 auto src = value_factory.src_from_string(temp_str);
487 assert(src);
488 srcs.push_back(src);
489 }
490
491 return new LDSAtomicInstr(opcode, dest, addr, srcs);
492 }
493
494 } // namespace r600
495