1 /* -*- mesa-c++ -*-
2 *
3 * Copyright (c) 2021 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_alugroup.h"
28 #include "sfn_instr_export.h"
29 #include "sfn_instr_fetch.h"
30 #include "sfn_instr_mem.h"
31 #include "sfn_instr_lds.h"
32 #include "sfn_instr_tex.h"
33 #include "sfn_instr_controlflow.h"
34
35 #include <iostream>
36 #include <sstream>
37 #include <numeric>
38
39 namespace r600 {
40
41 using std::string;
42 using std::vector;
43
Instr()44 Instr::Instr():
45 m_use_count(0),
46 m_block_id(std::numeric_limits<int>::max()),
47 m_index(std::numeric_limits<int>::max())
48 {
49 }
50
~Instr()51 Instr::~Instr()
52 {
53
54 }
55
print(std::ostream & os) const56 void Instr::print(std::ostream& os) const
57 {
58 do_print(os);
59 }
60
ready() const61 bool Instr::ready() const
62 {
63 for (auto& i : m_required_instr)
64 if (!i->ready())
65 return false;
66 return do_ready();
67 }
68
int_from_string_with_prefix(const std::string & str,const std::string & prefix)69 int int_from_string_with_prefix(const std::string& str, const std::string& prefix)
70 {
71 if (str.substr(0, prefix.length()) != prefix) {
72 std::cerr << "Expect '" << prefix << "' as start of '" << str << "'\n";
73 assert(0);
74 }
75
76 std::stringstream help(str.substr(prefix.length()));
77 int retval;
78 help >> retval;
79 return retval;
80 }
81
sel_and_szw_from_string(const std::string & str,RegisterVec4::Swizzle & swz,bool & is_ssa)82 int sel_and_szw_from_string(const std::string& str, RegisterVec4::Swizzle &swz, bool& is_ssa)
83 {
84 assert(str[0] == 'R' || str[0] == '_' || str[0] == 'S');
85 int sel = 0;
86
87 auto istr = str.begin() + 1;
88
89 if (str[0] == '_') {
90 while (istr != str.end() && *istr == '_')
91 ++istr;
92 sel = std::numeric_limits<int>::max();
93 } else {
94 while (istr != str.end() && isdigit(*istr)) {
95 sel *= 10;
96 sel += *istr - '0';
97 ++istr;
98 }
99 }
100
101 assert(*istr == '.');
102 istr++;
103
104 int i = 0;
105 while (istr != str.end()) {
106 switch (*istr) {
107 case 'x': swz[i] = 0; break;
108 case 'y': swz[i] = 1; break;
109 case 'z': swz[i] = 2; break;
110 case 'w': swz[i] = 3; break;
111 case '0': swz[i] = 4; break;
112 case '1': swz[i] = 5; break;
113 case '_': swz[i] = 7; break;
114 default:
115 unreachable("Unknown swizzle character");
116 }
117 ++istr;
118 ++i;
119 }
120
121 is_ssa = str[0] == 'S';
122
123 return sel;
124 }
125
is_last() const126 bool Instr::is_last() const
127 {
128 return true;
129 }
130
set_dead()131 bool Instr::set_dead()
132 {
133 if (m_instr_flags.test(always_keep))
134 return false;
135 bool is_dead = propagate_death();
136 m_instr_flags.set(dead);
137 return is_dead;
138 }
139
propagate_death()140 bool Instr::propagate_death()
141 {
142 return true;
143 }
144
replace_source(PRegister old_src,PVirtualValue new_src)145 bool Instr::replace_source(PRegister old_src, PVirtualValue new_src)
146 {
147 (void)old_src;
148 (void)new_src;
149 return false;
150 }
151
add_required_instr(Instr * instr)152 void Instr::add_required_instr(Instr *instr)
153 {
154 assert(instr);
155 m_required_instr.push_back(instr);
156 instr->m_dependend_instr.push_back(this);
157 }
158
replace_required_instr(Instr * old_instr,Instr * new_instr)159 void Instr::replace_required_instr(Instr *old_instr, Instr *new_instr)
160 {
161
162 for (auto i = m_required_instr.begin(); i != m_required_instr.end(); ++i) {
163 if (*i == old_instr)
164 *i = new_instr;
165 }
166 }
167
replace_dest(PRegister new_dest,r600::AluInstr * move_instr)168 bool Instr::replace_dest(PRegister new_dest, r600::AluInstr *move_instr)
169 {
170 (void)new_dest;
171 (void)move_instr;
172 return false;
173 }
174
set_blockid(int id,int index)175 void Instr::set_blockid(int id, int index)
176 {
177 m_block_id = id;
178 m_index = index;
179 forward_set_blockid(id, index);
180 }
181
182
forward_set_blockid(int id,int index)183 void Instr::forward_set_blockid(int id, int index)
184 {
185 (void)id;
186 (void)index;
187 }
188
InstrWithVectorResult(const RegisterVec4 & dest,const RegisterVec4::Swizzle & dest_swizzle)189 InstrWithVectorResult::InstrWithVectorResult(const RegisterVec4& dest,
190 const RegisterVec4::Swizzle& dest_swizzle):
191 m_dest(dest),
192 m_dest_swizzle(dest_swizzle)
193 {
194 for (int i = 0; i < 4; ++i) {
195 if (m_dest_swizzle[i] < 6)
196 m_dest[i]->add_parent(this);
197 }
198 }
199
print_dest(std::ostream & os) const200 void InstrWithVectorResult::print_dest(std::ostream& os) const
201 {
202 os << (m_dest[0]->is_ssa() ? 'S' : 'R' ) << m_dest.sel();
203 os << ".";
204 for (int i = 0; i < 4; ++i)
205 os << VirtualValue::chanchar[m_dest_swizzle[i]];
206 }
207
comp_dest(const RegisterVec4 & dest,const RegisterVec4::Swizzle & dest_swizzle) const208 bool InstrWithVectorResult::comp_dest(const RegisterVec4& dest,
209 const RegisterVec4::Swizzle& dest_swizzle) const
210 {
211 for(int i = 0; i < 4; ++i) {
212 if (!m_dest[i]->equal_to(*dest[i])) {
213 return false;
214 }
215 if (m_dest_swizzle[i] != dest_swizzle[i])
216 return false;
217 }
218 return true;
219 }
220
do_print(std::ostream & os) const221 void Block::do_print(std::ostream& os) const
222 {
223 for (int j = 0; j < 2 * m_nesting_depth; ++j)
224 os << ' ';
225 os << "BLOCK START\n";
226 for (auto& i : m_instructions) {
227 for (int j = 0; j < 2 * (m_nesting_depth + i->nesting_corr()) + 2; ++j)
228 os << ' ';
229 os << *i << "\n";
230 }
231 for (int j = 0; j < 2 * m_nesting_depth; ++j)
232 os << ' ';
233 os << "BLOCK END\n";
234 }
235
is_equal_to(const Block & lhs) const236 bool Block::is_equal_to(const Block& lhs) const
237 {
238 if (m_id != lhs.m_id || m_nesting_depth != lhs.m_nesting_depth)
239 return false;
240
241 if (m_instructions.size() != lhs.m_instructions.size())
242 return false;
243
244 return std::inner_product(m_instructions.begin(), m_instructions.end(), lhs.m_instructions.begin(),
245 true,
246 [] (bool l, bool r) { return l && r;},
247 [](PInst l, PInst r) { return l->equal_to(*r);});
248 }
249
operator !=(const Block & lhs,const Block & rhs)250 inline bool operator != (const Block& lhs, const Block& rhs)
251 {
252 return !lhs.is_equal_to(rhs);
253 }
254
erase(iterator node)255 void Block::erase(iterator node)
256 {
257 m_instructions.erase(node);
258 }
259
set_type(Type t)260 void Block::set_type(Type t)
261 {
262 m_blocK_type = t;
263 switch (t) {
264 case vtx:
265 case gds:
266 case tex: m_remaining_slots = 8; break; /* TODO: 16 for >= EVERGREEN */
267 default:
268 m_remaining_slots = 0xffff;
269 }
270 }
271
Block(int nesting_depth,int id)272 Block::Block(int nesting_depth, int id):
273 m_nesting_depth(nesting_depth),
274 m_id(id),
275 m_next_index(0)
276 {
277 assert(!has_instr_flag(force_cf));
278 }
279
accept(ConstInstrVisitor & visitor) const280 void Block::accept(ConstInstrVisitor& visitor) const
281 {
282 visitor.visit(*this);
283 }
284
accept(InstrVisitor & visitor)285 void Block::accept(InstrVisitor& visitor)
286 {
287 visitor.visit(this);
288 }
289
push_back(PInst instr)290 void Block::push_back(PInst instr)
291 {
292 instr->set_blockid(m_id, m_next_index++);
293 if (m_remaining_slots != 0xffff) {
294 uint32_t new_slots = instr->slots();
295 m_remaining_slots -= new_slots;
296 }
297 if (m_lds_group_start)
298 m_lds_group_requirement += instr->slots();
299
300 m_instructions.push_back(instr);
301 }
302
try_reserve_kcache(const AluGroup & group)303 bool Block::try_reserve_kcache(const AluGroup& group)
304 {
305 auto kcache = m_kcache;
306
307 auto kcache_constants = group.get_kconsts();
308 for (auto& kc : kcache_constants) {
309 auto u = kc->as_uniform();
310 assert(u);
311 if (!try_reserve_kcache(*u, kcache)) {
312 m_kcache_alloc_failed = true;
313 return false;
314 }
315 }
316
317 m_kcache = kcache;
318 m_kcache_alloc_failed = false;
319 return true;
320 }
321
try_reserve_kcache(const AluInstr & instr)322 bool Block::try_reserve_kcache(const AluInstr& instr)
323 {
324 auto kcache = m_kcache;
325
326 for (auto& src : instr.sources()) {
327 auto u = src->as_uniform();
328 if (u) {
329 if (!try_reserve_kcache(*u, kcache)) {
330 m_kcache_alloc_failed = true;
331 return false;
332 }
333 }
334 }
335 m_kcache = kcache;
336 m_kcache_alloc_failed = false;
337 return true;
338 }
339
set_chipclass(r600_chip_class chip_class)340 void Block::set_chipclass(r600_chip_class chip_class)
341 {
342 if (chip_class < ISA_CC_EVERGREEN)
343 s_max_kcache_banks = 2;
344 else
345 s_max_kcache_banks = 4;
346 }
347
348 unsigned Block::s_max_kcache_banks = 4;
349
try_reserve_kcache(const UniformValue & u,std::array<KCacheLine,4> & kcache) const350 bool Block::try_reserve_kcache(const UniformValue& u,
351 std::array<KCacheLine, 4>& kcache) const
352 {
353 const int kcache_banks = s_max_kcache_banks; // TODO: handle pre-evergreen
354
355 int bank = u.kcache_bank();
356 int sel = (u.sel() - 512);
357 int line = sel >> 4;
358
359 bool found = false;
360
361 for (int i = 0; i < kcache_banks && !found; ++i) {
362 if (kcache[i].mode) {
363 if (kcache[i].bank < bank)
364 continue;
365
366 if ((kcache[i].bank == bank &&
367 kcache[i].addr > line + 1) ||
368 kcache[i].bank > bank) {
369 if (kcache[kcache_banks - 1].mode)
370 return false;
371
372 memmove(&kcache[i+1],&kcache[i], (kcache_banks-i-1)*sizeof(KCacheLine));
373 kcache[i].mode = KCacheLine::lock_1;
374 kcache[i].bank = bank;
375 kcache[i].addr = line;
376 return true;
377 }
378
379 int d = line - kcache[i].addr;
380
381 if (d == -1) {
382 kcache[i].addr--;
383 if (kcache[i].mode == KCacheLine::lock_2) {
384 /* we are prepending the line to the current set,
385 * discarding the existing second line,
386 * so we'll have to insert line+2 after it */
387 line += 2;
388 continue;
389 } else if (kcache[i].mode == KCacheLine::lock_1) {
390 kcache[i].mode = KCacheLine::lock_2;
391 return true;
392 } else {
393 /* V_SQ_CF_KCACHE_LOCK_LOOP_INDEX is not supported */
394 return false;
395 }
396 } else if (d == 1) {
397 kcache[i].mode = KCacheLine::lock_2;
398 return true;
399 } else if (d == 0) {
400 return true;
401 }
402 } else { /* free kcache set - use it */
403 kcache[i].mode = KCacheLine::lock_1;
404 kcache[i].bank = bank;
405 kcache[i].addr = line;
406 return true;
407 }
408 }
409 return false;
410 }
411
lds_group_start(AluInstr * alu)412 void Block::lds_group_start(AluInstr *alu)
413 {
414 assert(!m_lds_group_start);
415 m_lds_group_start = alu;
416 m_lds_group_requirement = 0;
417 }
418
lds_group_end()419 void Block::lds_group_end()
420 {
421 assert(m_lds_group_start);
422 m_lds_group_start->set_required_slots(m_lds_group_requirement);
423 m_lds_group_start = 0;
424 }
425
InstrWithVectorResult(const InstrWithVectorResult & orig)426 InstrWithVectorResult::InstrWithVectorResult(const InstrWithVectorResult& orig):
427 m_dest(orig.m_dest),
428 m_dest_swizzle(orig.m_dest_swizzle)
429 {
430 }
431
432 class InstrComparer : public ConstInstrVisitor {
433 public:
434 InstrComparer() = default;
435 bool result {false};
436
437 #define DECLARE_MEMBER(TYPE) \
438 InstrComparer(const TYPE *instr) \
439 { \
440 this_ ## TYPE = instr; \
441 } \
442 \
443 void visit(const TYPE& instr) \
444 { \
445 result = false; \
446 if (!this_ ## TYPE) \
447 return; \
448 result = this_ ## TYPE->is_equal_to(instr); \
449 } \
450 \
451 const TYPE *this_ ## TYPE{nullptr};
452
453 DECLARE_MEMBER(AluInstr);
454 DECLARE_MEMBER(AluGroup);
455 DECLARE_MEMBER(TexInstr);
456 DECLARE_MEMBER(ExportInstr);
457 DECLARE_MEMBER(FetchInstr);
458 DECLARE_MEMBER(Block);
459 DECLARE_MEMBER(ControlFlowInstr);
460 DECLARE_MEMBER(IfInstr);
461 DECLARE_MEMBER(ScratchIOInstr);
462 DECLARE_MEMBER(StreamOutInstr);
463 DECLARE_MEMBER(MemRingOutInstr);
464 DECLARE_MEMBER(EmitVertexInstr);
465 DECLARE_MEMBER(GDSInstr);
466 DECLARE_MEMBER(WriteTFInstr);
467 DECLARE_MEMBER(LDSAtomicInstr);
468 DECLARE_MEMBER(LDSReadInstr);
469 DECLARE_MEMBER(RatInstr);
470 };
471
472 class InstrCompareForward: public ConstInstrVisitor {
473 public:
474
visit(const AluInstr & instr)475 void visit(const AluInstr& instr) override {
476 m_comparer = InstrComparer(&instr);
477 }
478
visit(const AluGroup & instr)479 void visit(const AluGroup& instr) override {
480 m_comparer = InstrComparer(&instr);
481 }
482
visit(const TexInstr & instr)483 void visit(const TexInstr& instr) override {
484 m_comparer = InstrComparer(&instr);
485 }
486
visit(const ExportInstr & instr)487 void visit(const ExportInstr& instr) override {
488 m_comparer = InstrComparer(&instr);
489 }
490
visit(const FetchInstr & instr)491 void visit(const FetchInstr& instr) override {
492 m_comparer = InstrComparer(&instr);
493 }
494
visit(const Block & instr)495 void visit(const Block& instr) override {
496 m_comparer = InstrComparer(&instr);
497 }
498
visit(const ControlFlowInstr & instr)499 void visit(const ControlFlowInstr& instr) override {
500 m_comparer = InstrComparer(&instr);
501 }
502
visit(const IfInstr & instr)503 void visit(const IfInstr& instr) override {
504 m_comparer = InstrComparer(&instr);
505 }
506
visit(const ScratchIOInstr & instr)507 void visit(const ScratchIOInstr& instr) override {
508 m_comparer = InstrComparer(&instr);
509 }
510
visit(const StreamOutInstr & instr)511 void visit(const StreamOutInstr& instr) override {
512 m_comparer = InstrComparer(&instr);
513 }
514
visit(const MemRingOutInstr & instr)515 void visit(const MemRingOutInstr& instr) override {
516 m_comparer = InstrComparer(&instr);
517 }
518
visit(const EmitVertexInstr & instr)519 void visit(const EmitVertexInstr& instr) override {
520 m_comparer = InstrComparer(&instr);
521 }
522
visit(const GDSInstr & instr)523 void visit(const GDSInstr& instr) override {
524 m_comparer = InstrComparer(&instr);
525 }
526
visit(const WriteTFInstr & instr)527 void visit(const WriteTFInstr& instr) override {
528 m_comparer = InstrComparer(&instr);
529 }
530
visit(const LDSAtomicInstr & instr)531 void visit(const LDSAtomicInstr& instr) override {
532 m_comparer = InstrComparer(&instr);
533 }
534
visit(const LDSReadInstr & instr)535 void visit(const LDSReadInstr& instr) override {
536 m_comparer = InstrComparer(&instr);
537 }
538
visit(const RatInstr & instr)539 void visit(const RatInstr& instr) override {
540 m_comparer = InstrComparer(&instr);
541 }
542
543 InstrComparer m_comparer;
544 };
545
546
equal_to(const Instr & lhs) const547 bool Instr::equal_to(const Instr& lhs) const
548 {
549 InstrCompareForward cmp;
550 accept(cmp);
551 lhs.accept(cmp.m_comparer);
552
553 return cmp.m_comparer.result;
554 }
555
556
557
558
559 } // ns r600
560