• 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 #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