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