• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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