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_export.h"
28
29 #include "sfn_valuefactory.h"
30
31 #include <sstream>
32
33 namespace r600 {
34
35 using std::string;
36
37 static char *
writemask_to_swizzle(int writemask,char * buf)38 writemask_to_swizzle(int writemask, char *buf)
39 {
40 const char *swz = "xyzw";
41 for (int i = 0; i < 4; ++i) {
42 buf[i] = (writemask & (1 << i)) ? swz[i] : '_';
43 }
44 return buf;
45 }
46
WriteOutInstr(const RegisterVec4 & value)47 WriteOutInstr::WriteOutInstr(const RegisterVec4& value):
48 m_value(value)
49 {
50 m_value.add_use(this);
51 set_always_keep();
52 }
53
54 void
override_chan(int i,int chan)55 WriteOutInstr::override_chan(int i, int chan)
56 {
57 m_value.set_value(i, new Register(m_value[i]->sel(), chan, m_value[i]->pin()));
58 }
59
ExportInstr(ExportType type,unsigned loc,const RegisterVec4 & value)60 ExportInstr::ExportInstr(ExportType type, unsigned loc, const RegisterVec4& value):
61 WriteOutInstr(value),
62 m_type(type),
63 m_loc(loc),
64 m_is_last(false)
65 {
66 }
67
68 void
accept(ConstInstrVisitor & visitor) const69 ExportInstr::accept(ConstInstrVisitor& visitor) const
70 {
71 visitor.visit(*this);
72 }
73
74 void
accept(InstrVisitor & visitor)75 ExportInstr::accept(InstrVisitor& visitor)
76 {
77 visitor.visit(this);
78 }
79
80 bool
is_equal_to(const ExportInstr & lhs) const81 ExportInstr::is_equal_to(const ExportInstr& lhs) const
82 {
83 return
84
85 (m_type == lhs.m_type && m_loc == lhs.m_loc && value() == lhs.value() &&
86 m_is_last == lhs.m_is_last);
87 }
88
89 ExportInstr::ExportType
type_from_string(const std::string & s)90 ExportInstr::type_from_string(const std::string& s)
91 {
92 (void)s;
93 return param;
94 }
95
96 void
do_print(std::ostream & os) const97 ExportInstr::do_print(std::ostream& os) const
98 {
99 os << "EXPORT";
100 if (m_is_last)
101 os << "_DONE";
102
103 switch (m_type) {
104 case param:
105 os << " PARAM ";
106 break;
107 case pos:
108 os << " POS ";
109 break;
110 case pixel:
111 os << " PIXEL ";
112 break;
113 }
114 os << m_loc << " ";
115 value().print(os);
116 }
117
118 bool
do_ready() const119 ExportInstr::do_ready() const
120 {
121 return value().ready(block_id(), index());
122 }
123
124 Instr::Pointer
from_string(std::istream & is,ValueFactory & vf)125 ExportInstr::from_string(std::istream& is, ValueFactory& vf)
126 {
127 return from_string_impl(is, vf);
128 }
129
130 Instr::Pointer
last_from_string(std::istream & is,ValueFactory & vf)131 ExportInstr::last_from_string(std::istream& is, ValueFactory& vf)
132 {
133 auto result = from_string_impl(is, vf);
134 result->set_is_last_export(true);
135 return result;
136 }
137
138 ExportInstr::Pointer
from_string_impl(std::istream & is,ValueFactory & vf)139 ExportInstr::from_string_impl(std::istream& is, ValueFactory& vf)
140 {
141 string typestr;
142 int pos;
143 string value_str;
144
145 is >> typestr >> pos >> value_str;
146
147 ExportInstr::ExportType type;
148
149 if (typestr == "PARAM")
150 type = ExportInstr::param;
151 else if (typestr == "POS")
152 type = ExportInstr::pos;
153 else if (typestr == "PIXEL")
154 type = ExportInstr::pixel;
155 else
156 unreachable("Unknown export type");
157
158 RegisterVec4 value = vf.src_vec4_from_string(value_str);
159
160 return new ExportInstr(type, pos, value);
161 }
162
163 uint8_t
allowed_src_chan_mask() const164 ExportInstr::allowed_src_chan_mask() const
165 {
166 return value().free_chan_mask();
167 }
168
ScratchIOInstr(const RegisterVec4 & value,PRegister addr,int align,int align_offset,int writemask,int array_size,bool is_read)169 ScratchIOInstr::ScratchIOInstr(const RegisterVec4& value,
170 PRegister addr,
171 int align,
172 int align_offset,
173 int writemask,
174 int array_size,
175 bool is_read):
176 WriteOutInstr(value),
177 m_address(addr),
178 m_align(align),
179 m_align_offset(align_offset),
180 m_writemask(writemask),
181 m_array_size(array_size - 1),
182 m_read(is_read)
183 {
184 addr->add_use(this);
185 if (m_read) {
186 for (int i = 0; i < 4; ++i)
187 value[i]->add_parent(this);
188 }
189 }
190
ScratchIOInstr(const RegisterVec4 & value,int loc,int align,int align_offset,int writemask,bool is_read)191 ScratchIOInstr::ScratchIOInstr(const RegisterVec4& value,
192 int loc,
193 int align,
194 int align_offset,
195 int writemask,
196 bool is_read):
197 WriteOutInstr(value),
198 m_loc(loc),
199 m_align(align),
200 m_align_offset(align_offset),
201 m_writemask(writemask),
202 m_read(is_read)
203 {
204 if (m_read) {
205
206 for (int i = 0; i < 4; ++i)
207 value[i]->add_parent(this);
208 }
209 }
210
211 void
accept(ConstInstrVisitor & visitor) const212 ScratchIOInstr::accept(ConstInstrVisitor& visitor) const
213 {
214 visitor.visit(*this);
215 }
216
217 void
accept(InstrVisitor & visitor)218 ScratchIOInstr::accept(InstrVisitor& visitor)
219 {
220 visitor.visit(this);
221 }
222
223 bool
is_equal_to(const ScratchIOInstr & lhs) const224 ScratchIOInstr::is_equal_to(const ScratchIOInstr& lhs) const
225 {
226 if (m_address) {
227 if (!lhs.m_address)
228 return false;
229 if (!m_address->equal_to(*lhs.m_address))
230 return false;
231 } else if (lhs.m_address)
232 return false;
233
234 return m_loc == lhs.m_loc && m_align == lhs.m_align &&
235 m_align_offset == lhs.m_align_offset && m_writemask == lhs.m_writemask &&
236 m_array_size == lhs.m_array_size && value().sel() == lhs.value().sel();
237 }
238
239 bool
do_ready() const240 ScratchIOInstr::do_ready() const
241 {
242 bool address_ready = !m_address || m_address->ready(block_id(), index());
243 if (is_read())
244 return address_ready;
245 else
246 return address_ready && value().ready(block_id(), index());
247 }
248
249 void
do_print(std::ostream & os) const250 ScratchIOInstr::do_print(std::ostream& os) const
251 {
252 char buf[6] = {0};
253
254 os << (is_read() ? "READ_SCRATCH " : "WRITE_SCRATCH ");
255
256 if (is_read()) {
257 os << (value()[0]->has_flag(Register::ssa) ? " S" : " R") << value().sel() << "."
258 << writemask_to_swizzle(m_writemask, buf) << " ";
259 }
260
261 if (m_address)
262 os << "@" << *m_address << "[" << m_array_size + 1 << "]";
263 else
264 os << m_loc;
265
266 if (!is_read())
267 os << (value()[0]->has_flag(Register::ssa) ? " S" : " R") << value().sel() << "."
268 << writemask_to_swizzle(m_writemask, buf);
269
270 os << " "
271 << "AL:" << m_align << " ALO:" << m_align_offset;
272 }
273
274 auto
from_string(std::istream & is,ValueFactory & vf)275 ScratchIOInstr::from_string(std::istream& is, ValueFactory& vf) -> Pointer
276 {
277 string loc_str;
278 string value_str;
279 string align_str;
280 string align_offset_str;
281 int offset;
282
283 int array_size = 0;
284 PVirtualValue addr_reg = nullptr;
285
286 is >> loc_str >> value_str >> align_str >> align_offset_str;
287
288 std::istringstream loc_ss(loc_str);
289
290 auto align = int_from_string_with_prefix(align_str, "AL:");
291 auto align_offset = int_from_string_with_prefix(align_offset_str, "ALO:");
292 auto value = vf.src_vec4_from_string(value_str);
293
294 int writemask = 0;
295 for (int i = 0; i < 4; ++i) {
296 if (value[i]->chan() == i)
297 writemask |= 1 << i;
298 }
299
300 if (loc_str[0] == '@') {
301
302 string addr_str;
303 char c;
304 loc_ss >> c;
305 loc_ss >> c;
306
307 while (!loc_ss.eof() && c != '[') {
308 addr_str.append(1, c);
309 loc_ss >> c;
310 }
311 addr_reg = vf.src_from_string(addr_str);
312 assert(addr_reg && addr_reg->as_register());
313
314 loc_ss >> array_size;
315 loc_ss >> c;
316 assert(c == ']');
317 return new ScratchIOInstr(
318 value, addr_reg->as_register(), align, align_offset, writemask, array_size);
319 } else {
320 loc_ss >> offset;
321 return new ScratchIOInstr(value, offset, align, align_offset, writemask);
322 }
323 }
324
StreamOutInstr(const RegisterVec4 & value,int num_components,int array_base,int comp_mask,int out_buffer,int stream)325 StreamOutInstr::StreamOutInstr(const RegisterVec4& value,
326 int num_components,
327 int array_base,
328 int comp_mask,
329 int out_buffer,
330 int stream):
331 WriteOutInstr(value),
332 m_element_size(num_components == 3 ? 3 : num_components - 1),
333 m_array_base(array_base),
334 m_writemask(comp_mask),
335 m_output_buffer(out_buffer),
336 m_stream(stream)
337 {
338 }
339
340 unsigned
op(amd_gfx_level gfx_level) const341 StreamOutInstr::op(amd_gfx_level gfx_level) const
342 {
343 int op = 0;
344 if (gfx_level >= EVERGREEN) {
345 switch (m_output_buffer) {
346 case 0:
347 op = CF_OP_MEM_STREAM0_BUF0;
348 break;
349 case 1:
350 op = CF_OP_MEM_STREAM0_BUF1;
351 break;
352 case 2:
353 op = CF_OP_MEM_STREAM0_BUF2;
354 break;
355 case 3:
356 op = CF_OP_MEM_STREAM0_BUF3;
357 break;
358 }
359 return 4 * m_stream + op;
360 } else {
361 assert(m_stream == 0);
362 return CF_OP_MEM_STREAM0 + m_output_buffer;
363 }
364 }
365
366 bool
is_equal_to(const StreamOutInstr & oth) const367 StreamOutInstr::is_equal_to(const StreamOutInstr& oth) const
368 {
369
370 return value() == oth.value() && m_element_size == oth.m_element_size &&
371 m_burst_count == oth.m_burst_count && m_array_base == oth.m_array_base &&
372 m_array_size == oth.m_array_size && m_writemask == oth.m_writemask &&
373 m_output_buffer == oth.m_output_buffer && m_stream == oth.m_stream;
374 }
375
376 void
do_print(std::ostream & os) const377 StreamOutInstr::do_print(std::ostream& os) const
378 {
379 os << "WRITE STREAM(" << m_stream << ") " << value() << " ES:" << m_element_size
380 << " BC:" << m_burst_count << " BUF:" << m_output_buffer
381 << " ARRAY:" << m_array_base;
382 if (m_array_size != 0xfff)
383 os << "+" << m_array_size;
384 }
385
386 bool
do_ready() const387 StreamOutInstr::do_ready() const
388 {
389 return value().ready(block_id(), index());
390 }
391
392 void
accept(ConstInstrVisitor & visitor) const393 StreamOutInstr::accept(ConstInstrVisitor& visitor) const
394 {
395 visitor.visit(*this);
396 }
397
398 void
accept(InstrVisitor & visitor)399 StreamOutInstr::accept(InstrVisitor& visitor)
400 {
401 visitor.visit(this);
402 }
403
MemRingOutInstr(ECFOpCode ring,EMemWriteType type,const RegisterVec4 & value,unsigned base_addr,unsigned ncomp,PRegister index)404 MemRingOutInstr::MemRingOutInstr(ECFOpCode ring,
405 EMemWriteType type,
406 const RegisterVec4& value,
407 unsigned base_addr,
408 unsigned ncomp,
409 PRegister index):
410 WriteOutInstr(value),
411 m_ring_op(ring),
412 m_type(type),
413 m_base_address(base_addr),
414 m_num_comp(ncomp),
415 m_export_index(index)
416 {
417 assert(m_ring_op == cf_mem_ring || m_ring_op == cf_mem_ring1 ||
418 m_ring_op == cf_mem_ring2 || m_ring_op == cf_mem_ring3);
419 assert(m_num_comp <= 4);
420
421 if (m_export_index)
422 m_export_index->add_use(this);
423 }
424
425 unsigned
ncomp() const426 MemRingOutInstr::ncomp() const
427 {
428 switch (m_num_comp) {
429 case 1:
430 return 0;
431 case 2:
432 return 1;
433 case 3:
434 case 4:
435 return 3;
436 default:
437 assert(0);
438 }
439 return 3;
440 }
441
442 bool
is_equal_to(const MemRingOutInstr & oth) const443 MemRingOutInstr::is_equal_to(const MemRingOutInstr& oth) const
444 {
445
446 bool equal = value() == oth.value() && m_ring_op == oth.m_ring_op &&
447 m_type == oth.m_type && m_num_comp == oth.m_num_comp &&
448 m_base_address == oth.m_base_address;
449
450 if (m_type == mem_write_ind || m_type == mem_write_ind_ack)
451 equal &= (*m_export_index == *oth.m_export_index);
452 return equal;
453 }
454
455 static const char *write_type_str[4] = {
456 "WRITE", "WRITE_IDX", "WRITE_ACK", "WRITE_IDX_ACK"};
457 void
do_print(std::ostream & os) const458 MemRingOutInstr::do_print(std::ostream& os) const
459 {
460
461 os << "MEM_RING " << (m_ring_op == cf_mem_ring ? 0 : m_ring_op - cf_mem_ring1 + 1);
462 os << " " << write_type_str[m_type] << " " << m_base_address;
463 os << " " << value();
464 if (m_type == mem_write_ind || m_type == mem_write_ind_ack)
465 os << " @" << *m_export_index;
466 os << " ES:" << m_num_comp;
467 }
468
469 void
patch_ring(int stream,PRegister index)470 MemRingOutInstr::patch_ring(int stream, PRegister index)
471 {
472 const ECFOpCode ring_op[4] = {cf_mem_ring, cf_mem_ring1, cf_mem_ring2, cf_mem_ring3};
473
474 assert(stream < 4);
475 m_ring_op = ring_op[stream];
476 m_export_index = index;
477 }
478
479 bool
do_ready() const480 MemRingOutInstr::do_ready() const
481 {
482 if (m_export_index && !m_export_index->ready(block_id(), index()))
483 return false;
484
485 return value().ready(block_id(), index());
486 }
487
488 void
accept(ConstInstrVisitor & visitor) const489 MemRingOutInstr::accept(ConstInstrVisitor& visitor) const
490 {
491 visitor.visit(*this);
492 }
493
494 void
accept(InstrVisitor & visitor)495 MemRingOutInstr::accept(InstrVisitor& visitor)
496 {
497 visitor.visit(this);
498 }
499
500 static const std::map<string, MemRingOutInstr::EMemWriteType> type_lookop = {
501 {"WRITE", MemRingOutInstr::mem_write },
502 {"WRITE_IDX", MemRingOutInstr::mem_write_ind },
503 {"WRITE_ACK", MemRingOutInstr::mem_write_ack },
504 {"WRITE_IDX_ACK", MemRingOutInstr::mem_write_ind_ack}
505 };
506
507 auto
from_string(std::istream & is,ValueFactory & vf)508 MemRingOutInstr::from_string(std::istream& is, ValueFactory& vf) -> Pointer
509 {
510 string type_str;
511
512 int ring;
513
514 int base_address;
515 string value_str;
516
517 is >> ring >> type_str >> base_address >> value_str;
518 assert(ring < 4);
519
520 auto itype = type_lookop.find(type_str);
521 assert(itype != type_lookop.end());
522
523 auto type = itype->second;
524
525 PVirtualValue index{nullptr};
526 if (type == mem_write_ind || type == mem_write_ind_ack) {
527 char c;
528 string index_str;
529 is >> c >> index_str;
530 assert('@' == c);
531 index = vf.src_from_string(index_str);
532 }
533
534 string elm_size_str;
535 is >> elm_size_str;
536
537 int num_comp = int_from_string_with_prefix(elm_size_str, "ES:");
538
539 auto value = vf.src_vec4_from_string(value_str);
540
541 ECFOpCode opcodes[4] = {cf_mem_ring, cf_mem_ring1, cf_mem_ring2, cf_mem_ring3};
542 assert(ring < 4);
543
544 return new MemRingOutInstr(
545 opcodes[ring], type, value, base_address, num_comp, index->as_register());
546 }
547
EmitVertexInstr(int stream,bool cut)548 EmitVertexInstr::EmitVertexInstr(int stream, bool cut):
549 m_stream(stream),
550 m_cut(cut)
551 {
552 }
553
554 bool
is_equal_to(const EmitVertexInstr & oth) const555 EmitVertexInstr::is_equal_to(const EmitVertexInstr& oth) const
556 {
557 return oth.m_stream == m_stream && oth.m_cut == m_cut;
558 }
559
560 void
accept(ConstInstrVisitor & visitor) const561 EmitVertexInstr::accept(ConstInstrVisitor& visitor) const
562 {
563 visitor.visit(*this);
564 }
565
566 void
accept(InstrVisitor & visitor)567 EmitVertexInstr::accept(InstrVisitor& visitor)
568 {
569 visitor.visit(this);
570 }
571
572 bool
do_ready() const573 EmitVertexInstr::do_ready() const
574 {
575 return true;
576 }
577
578 void
do_print(std::ostream & os) const579 EmitVertexInstr::do_print(std::ostream& os) const
580 {
581 os << (m_cut ? "EMIT_CUT_VERTEX @" : "EMIT_VERTEX @") << m_stream;
582 }
583
584 auto
from_string(std::istream & is,bool cut)585 EmitVertexInstr::from_string(std::istream& is, bool cut) -> Pointer
586 {
587 char c;
588 is >> c;
589 assert(c == '@');
590
591 int stream;
592 is >> stream;
593
594 return new EmitVertexInstr(stream, cut);
595 }
596
597 void
accept(ConstInstrVisitor & visitor) const598 WriteTFInstr::accept(ConstInstrVisitor& visitor) const
599 {
600 visitor.visit(*this);
601 }
602
603 void
accept(InstrVisitor & visitor)604 WriteTFInstr::accept(InstrVisitor& visitor)
605 {
606 visitor.visit(this);
607 }
608
609 bool
is_equal_to(const WriteTFInstr & rhs) const610 WriteTFInstr::is_equal_to(const WriteTFInstr& rhs) const
611 {
612 return value() == rhs.value();
613 }
614
615 auto
from_string(std::istream & is,ValueFactory & vf)616 WriteTFInstr::from_string(std::istream& is, ValueFactory& vf) -> Pointer
617 {
618 string value_str;
619 is >> value_str;
620
621 auto value = vf.src_vec4_from_string(value_str);
622
623 return new WriteTFInstr(value);
624 }
625
626 uint8_t
allowed_src_chan_mask() const627 WriteTFInstr::allowed_src_chan_mask() const
628 {
629 return value().free_chan_mask();
630 }
631
632
633 bool
do_ready() const634 WriteTFInstr::do_ready() const
635 {
636 return value().ready(block_id(), index());
637 }
638
639 void
do_print(std::ostream & os) const640 WriteTFInstr::do_print(std::ostream& os) const
641 {
642 os << "WRITE_TF " << value();
643 }
644
645 } // namespace r600
646