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_optimizer.h"
28
29 #include "sfn_instr_alugroup.h"
30 #include "sfn_instr_controlflow.h"
31 #include "sfn_instr_export.h"
32 #include "sfn_instr_tex.h"
33 #include "sfn_instr_fetch.h"
34 #include "sfn_instr_lds.h"
35 #include "sfn_peephole.h"
36 #include "sfn_debug.h"
37
38 #include <sstream>
39
40 namespace r600 {
41
optimize(Shader & shader)42 bool optimize(Shader& shader)
43 {
44 bool progress;
45
46 sfn_log << SfnLog::opt << "Shader before optimization\n";
47 if (sfn_log.has_debug_flag(SfnLog::opt)) {
48 std::stringstream ss;
49 shader.print(ss);
50 sfn_log << ss.str() << "\n\n";
51 }
52
53 do {
54 progress = false;
55 progress |= copy_propagation_fwd(shader);
56 progress |= dead_code_elimination(shader);
57 progress |= copy_propagation_backward(shader);
58 progress |= dead_code_elimination(shader);
59 progress |= simplify_source_vectors(shader);
60 progress |= peephole(shader);
61 progress |= dead_code_elimination(shader);
62 } while (progress);
63
64 return progress;
65 }
66
67 class DCEVisitor : public InstrVisitor {
68 public:
69 DCEVisitor();
70
71 void visit(AluInstr *instr) override;
72 void visit(AluGroup *instr) override;
73 void visit(TexInstr *instr) override;
visit(ExportInstr * instr)74 void visit(ExportInstr *instr) override {(void)instr;};
75 void visit(FetchInstr *instr) override;
76 void visit(Block *instr) override;
77
visit(ControlFlowInstr * instr)78 void visit(ControlFlowInstr *instr) override {(void)instr;};
visit(IfInstr * instr)79 void visit(IfInstr *instr) override {(void)instr;};
visit(ScratchIOInstr * instr)80 void visit(ScratchIOInstr *instr) override {(void)instr;};
visit(StreamOutInstr * instr)81 void visit(StreamOutInstr *instr) override {(void)instr;};
visit(MemRingOutInstr * instr)82 void visit(MemRingOutInstr *instr) override {(void)instr;};
visit(EmitVertexInstr * instr)83 void visit(EmitVertexInstr *instr) override {(void)instr;};
visit(GDSInstr * instr)84 void visit(GDSInstr *instr) override {(void)instr;};
visit(WriteTFInstr * instr)85 void visit(WriteTFInstr *instr) override {(void)instr;};
visit(LDSAtomicInstr * instr)86 void visit(LDSAtomicInstr *instr) override {(void)instr;};
87 void visit(LDSReadInstr *instr) override;
visit(RatInstr * instr)88 void visit(RatInstr *instr) override {(void)instr;};
89
90
91 bool progress;
92 };
93
dead_code_elimination(Shader & shader)94 bool dead_code_elimination(Shader& shader)
95 {
96 DCEVisitor dce;
97
98 do {
99
100 sfn_log << SfnLog::opt << "start dce run\n";
101
102 dce.progress = false;
103 for (auto& b : shader.func())
104 b->accept(dce);
105
106 sfn_log << SfnLog::opt << "finished dce run\n\n";
107
108 } while (dce.progress);
109
110 sfn_log << SfnLog::opt << "Shader after DCE\n";
111 if (sfn_log.has_debug_flag(SfnLog::opt)) {
112 std::stringstream ss;
113 shader.print(ss);
114 sfn_log << ss.str() << "\n\n";
115 }
116
117 return dce.progress;
118 }
119
DCEVisitor()120 DCEVisitor::DCEVisitor():progress(false)
121 {
122 }
123
visit(AluInstr * instr)124 void DCEVisitor::visit(AluInstr *instr)
125 {
126 sfn_log << SfnLog::opt << "DCE: visit '" << *instr;
127
128 if (instr->has_instr_flag(Instr::dead))
129 return;
130
131 if (instr->dest() &&
132 (instr->dest()->has_uses() || !instr->dest()->is_ssa()) ) {
133 sfn_log << SfnLog::opt << " dest used\n";
134 return;
135 }
136
137 switch (instr->opcode()) {
138 case op2_kille:
139 case op2_killne:
140 case op2_kille_int:
141 case op2_killne_int:
142 case op2_killge:
143 case op2_killge_int:
144 case op2_killge_uint:
145 case op2_killgt:
146 case op2_killgt_int:
147 case op2_killgt_uint:
148 case op0_group_barrier:
149 sfn_log << SfnLog::opt << " never kill\n";
150 return;
151 default:
152 ;
153 }
154
155 bool dead = instr->set_dead();
156 sfn_log << SfnLog::opt << (dead ? "dead" : "alive") << "\n";
157 progress |= dead;
158 }
159
visit(LDSReadInstr * instr)160 void DCEVisitor::visit(LDSReadInstr *instr)
161 {
162 sfn_log << SfnLog::opt << "visit " << *instr << "\n";
163 progress |= instr->remove_unused_components();
164 }
165
visit(AluGroup * instr)166 void DCEVisitor::visit(AluGroup *instr)
167 {
168 /* Groups are created because the instructions are used together
169 * so don't try to eliminate code there */
170 (void)instr;
171 }
172
visit(TexInstr * instr)173 void DCEVisitor::visit(TexInstr *instr)
174 {
175 auto& dest = instr->dst();
176
177 bool has_uses = false;
178 RegisterVec4::Swizzle swz = instr->all_dest_swizzle();
179 for (int i = 0; i < 4; ++i) {
180 if (!dest[i]->has_uses())
181 swz[i] = 7;
182 else
183 has_uses |= true;
184 }
185 instr->set_dest_swizzle(swz);
186
187 if (has_uses)
188 return;
189
190 progress |= instr->set_dead();
191 }
192
visit(FetchInstr * instr)193 void DCEVisitor::visit(FetchInstr *instr)
194 {
195 auto& dest = instr->dst();
196
197 bool has_uses = false;
198 RegisterVec4::Swizzle swz = instr->all_dest_swizzle();
199 for (int i = 0; i < 4; ++i) {
200 if (!dest[i]->has_uses())
201 swz[i] = 7;
202 else
203 has_uses |= true;
204 }
205 instr->set_dest_swizzle(swz);
206
207 if (has_uses)
208 return;
209
210 sfn_log << SfnLog::opt << "set dead: " << *instr << "\n";
211
212 progress |= instr->set_dead();
213 }
214
visit(Block * block)215 void DCEVisitor::visit(Block *block)
216 {
217 auto i = block->begin();
218 auto e = block->end();
219 while (i != e) {
220 auto n = i++;
221 if (!(*n)->keep()) {
222 (*n)->accept(*this);
223 if ((*n)->is_dead()) {
224 block->erase(n);
225 }
226 }
227 }
228 }
229
visit(ControlFlowInstr * instr)230 void visit(ControlFlowInstr *instr)
231 {
232 (void)instr;
233 }
234
visit(IfInstr * instr)235 void visit(IfInstr *instr)
236 {
237 (void)instr;
238 }
239
240 class CopyPropFwdVisitor : public InstrVisitor {
241 public:
242 CopyPropFwdVisitor();
243
244 void visit(AluInstr *instr) override;
245 void visit(AluGroup *instr) override;
246 void visit(TexInstr *instr) override;
visit(ExportInstr * instr)247 void visit(ExportInstr *instr) override {(void)instr;}
248 void visit(FetchInstr *instr) override;
249 void visit(Block *instr) override;
visit(ControlFlowInstr * instr)250 void visit(ControlFlowInstr *instr) override {(void)instr;}
visit(IfInstr * instr)251 void visit(IfInstr *instr) override {(void)instr;}
visit(ScratchIOInstr * instr)252 void visit(ScratchIOInstr *instr) override {(void)instr;}
visit(StreamOutInstr * instr)253 void visit(StreamOutInstr *instr) override {(void)instr;}
visit(MemRingOutInstr * instr)254 void visit(MemRingOutInstr *instr) override {(void)instr;}
visit(EmitVertexInstr * instr)255 void visit(EmitVertexInstr *instr) override {(void)instr;}
visit(GDSInstr * instr)256 void visit(GDSInstr *instr) override {(void)instr;};
visit(WriteTFInstr * instr)257 void visit(WriteTFInstr *instr) override {(void)instr;};
visit(RatInstr * instr)258 void visit(RatInstr *instr) override {(void)instr;};
259
260 // TODO: these two should use copy propagation
visit(LDSAtomicInstr * instr)261 void visit(LDSAtomicInstr *instr) override {(void)instr;};
visit(LDSReadInstr * instr)262 void visit(LDSReadInstr *instr) override {(void)instr;};
263
264 bool progress;
265 };
266
267
268 class CopyPropBackVisitor : public InstrVisitor {
269 public:
270 CopyPropBackVisitor();
271
272 void visit(AluInstr *instr) override;
273 void visit(AluGroup *instr) override;
274 void visit(TexInstr *instr) override;
visit(ExportInstr * instr)275 void visit(ExportInstr *instr) override {(void)instr;}
276 void visit(FetchInstr *instr) override;
277 void visit(Block *instr) override;
visit(ControlFlowInstr * instr)278 void visit(ControlFlowInstr *instr) override {(void)instr;}
visit(IfInstr * instr)279 void visit(IfInstr *instr) override {(void)instr;}
visit(ScratchIOInstr * instr)280 void visit(ScratchIOInstr *instr) override {(void)instr;}
visit(StreamOutInstr * instr)281 void visit(StreamOutInstr *instr) override {(void)instr;}
visit(MemRingOutInstr * instr)282 void visit(MemRingOutInstr *instr) override {(void)instr;}
visit(EmitVertexInstr * instr)283 void visit(EmitVertexInstr *instr) override {(void)instr;}
visit(GDSInstr * instr)284 void visit(GDSInstr *instr) override {(void)instr;};
visit(WriteTFInstr * instr)285 void visit(WriteTFInstr *instr) override {(void)instr;};
visit(LDSAtomicInstr * instr)286 void visit(LDSAtomicInstr *instr) override {(void)instr;};
visit(LDSReadInstr * instr)287 void visit(LDSReadInstr *instr) override {(void)instr;};
visit(RatInstr * instr)288 void visit(RatInstr *instr) override {(void)instr;};
289
290 bool progress;
291 };
292
copy_propagation_fwd(Shader & shader)293 bool copy_propagation_fwd(Shader& shader)
294 {
295 auto& root = shader.func();
296 CopyPropFwdVisitor copy_prop;
297
298 do {
299 copy_prop.progress = false;
300 for (auto b : root)
301 b->accept(copy_prop);
302 } while (copy_prop.progress);
303
304 sfn_log << SfnLog::opt << "Shader after Copy Prop forward\n";
305 if (sfn_log.has_debug_flag(SfnLog::opt)) {
306 std::stringstream ss;
307 shader.print(ss);
308 sfn_log << ss.str() << "\n\n";
309 }
310
311
312 return copy_prop.progress;
313 }
314
copy_propagation_backward(Shader & shader)315 bool copy_propagation_backward(Shader& shader)
316 {
317 CopyPropBackVisitor copy_prop;
318
319 do {
320 copy_prop.progress = false;
321 for (auto b: shader.func())
322 b->accept(copy_prop);
323 } while (copy_prop.progress);
324
325 sfn_log << SfnLog::opt << "Shader after Copy Prop backwards\n";
326 if (sfn_log.has_debug_flag(SfnLog::opt)) {
327 std::stringstream ss;
328 shader.print(ss);
329 sfn_log << ss.str() << "\n\n";
330 }
331
332 return copy_prop.progress;
333 }
334
CopyPropFwdVisitor()335 CopyPropFwdVisitor::CopyPropFwdVisitor():
336 progress(false)
337 {}
338
visit(AluInstr * instr)339 void CopyPropFwdVisitor::visit(AluInstr *instr)
340 {
341 sfn_log << SfnLog::opt << "CopyPropFwdVisitor:["
342 << instr->block_id() << ":" << instr->index() << "] " << *instr
343 << " dset=" << instr->dest() << " ";
344
345
346
347 if (instr->dest()) {
348 sfn_log << SfnLog::opt << "has uses; "
349 << instr->dest()->uses().size();
350 }
351
352 sfn_log << SfnLog::opt << "\n";
353
354 if (!instr->can_propagate_src()) {
355 return;
356 }
357
358 auto src = instr->psrc(0);
359 auto dest = instr->dest();
360
361 for (auto& i : instr->dest()->uses()) {
362 /* SSA can always be propagated, registers only in the same block
363 * and only if they are not assigned to more than once */
364 if (dest->is_ssa() ||
365 (instr->block_id() == i->block_id() &&
366 instr->index() < i->index() &&
367 dest->uses().size() == 1)) {
368 sfn_log << SfnLog::opt << " Try replace in "
369 << i->block_id() << ":" << i->index()
370 << *i<< "\n";
371 progress |= i->replace_source(dest, src);
372 }
373 }
374 if (instr->dest()) {
375 sfn_log << SfnLog::opt << "has uses; "
376 << instr->dest()->uses().size();
377 }
378 sfn_log << SfnLog::opt << " done\n";
379 }
380
381
visit(AluGroup * instr)382 void CopyPropFwdVisitor::visit(AluGroup *instr)
383 {
384 (void)instr;
385 }
386
visit(TexInstr * instr)387 void CopyPropFwdVisitor::visit(TexInstr *instr)
388 {
389 (void)instr;
390 }
391
visit(FetchInstr * instr)392 void CopyPropFwdVisitor::visit(FetchInstr *instr)
393 {
394 (void)instr;
395 }
396
visit(Block * instr)397 void CopyPropFwdVisitor::visit(Block *instr)
398 {
399 for (auto& i: *instr)
400 i->accept(*this);
401 }
402
CopyPropBackVisitor()403 CopyPropBackVisitor::CopyPropBackVisitor():
404 progress(false)
405 {
406
407 }
408
visit(AluInstr * instr)409 void CopyPropBackVisitor::visit(AluInstr *instr)
410 {
411 bool local_progress = false;
412
413 sfn_log << SfnLog::opt << "CopyPropBackVisitor:["
414 << instr->block_id() << ":" << instr->index() << "] " << *instr << "\n";
415
416
417 if (!instr->can_propagate_dest()) {
418 return;
419 }
420
421 auto src_reg = instr->psrc(0)->as_register();
422 if (!src_reg) {
423 return;
424 }
425
426 if (src_reg->uses().size() > 1)
427 return;
428
429 auto dest = instr->dest();
430 if (!dest ||
431 !instr->has_alu_flag(alu_write)) {
432 return;
433 }
434
435 if (!dest->is_ssa() && dest->parents().size() > 1)
436 return;
437
438 for (auto& i: src_reg->parents()) {
439 sfn_log << SfnLog::opt << "Try replace dest in "
440 << i->block_id() << ":" << i->index()
441 << *i<< "\n";
442
443 if (i->replace_dest(dest, instr)) {
444 dest->del_parent(instr);
445 dest->add_parent(i);
446 for (auto d : instr->dependend_instr()) {
447 d->add_required_instr(i);
448 }
449 local_progress = true;
450 }
451 }
452
453 if (local_progress)
454 instr->set_dead();
455
456 progress |= local_progress;
457 }
458
visit(AluGroup * instr)459 void CopyPropBackVisitor::visit(AluGroup *instr)
460 {
461 for (auto& i: *instr) {
462 if (i)
463 i->accept(*this);
464 }
465 }
466
visit(TexInstr * instr)467 void CopyPropBackVisitor::visit(TexInstr *instr)
468 {
469 (void)instr;
470 }
471
visit(FetchInstr * instr)472 void CopyPropBackVisitor::visit(FetchInstr *instr)
473 {
474 (void)instr;
475 }
476
visit(Block * instr)477 void CopyPropBackVisitor::visit(Block *instr)
478 {
479 for (auto i = instr->rbegin(); i != instr->rend(); ++i)
480 if (!(*i)->is_dead())
481 (*i)->accept(*this);
482 }
483
484 class SimplifySourceVecVisitor : public InstrVisitor {
485 public:
SimplifySourceVecVisitor()486 SimplifySourceVecVisitor():progress(false) {}
487
visit(AluInstr * instr)488 void visit(AluInstr *instr) override{(void)instr;}
visit(AluGroup * instr)489 void visit(AluGroup *instr) override{(void)instr;}
490 void visit(TexInstr *instr) override;
491 void visit(ExportInstr *instr) override;
492 void visit(FetchInstr *instr) override;
493 void visit(Block *instr) override;
494 void visit(ControlFlowInstr *instr) override;
495 void visit(IfInstr *instr) override;
496 void visit(ScratchIOInstr *instr) override;
497 void visit(StreamOutInstr *instr) override;
498 void visit(MemRingOutInstr *instr) override;
visit(EmitVertexInstr * instr)499 void visit(EmitVertexInstr *instr) override {(void)instr;}
visit(GDSInstr * instr)500 void visit(GDSInstr *instr) override {(void)instr;};
visit(WriteTFInstr * instr)501 void visit(WriteTFInstr *instr) override {(void)instr;};
visit(LDSAtomicInstr * instr)502 void visit(LDSAtomicInstr *instr) override {(void)instr;};
visit(LDSReadInstr * instr)503 void visit(LDSReadInstr *instr) override {(void)instr;};
visit(RatInstr * instr)504 void visit(RatInstr *instr) override {(void)instr;};
505
506 void replace_src(Instr *instr, RegisterVec4& reg4);
507
508 bool progress;
509 };
510
simplify_source_vectors(Shader & sh)511 bool simplify_source_vectors(Shader& sh)
512 {
513 SimplifySourceVecVisitor visitor;
514
515 for (auto b: sh.func())
516 b->accept(visitor);
517
518 return visitor.progress;
519 }
520
visit(TexInstr * instr)521 void SimplifySourceVecVisitor::visit(TexInstr *instr)
522 {
523 if (instr->opcode() != TexInstr::get_resinfo) {
524 replace_src(instr, instr->src());
525 }
526 }
527
visit(ScratchIOInstr * instr)528 void SimplifySourceVecVisitor::visit(ScratchIOInstr *instr)
529 {
530 (void) instr;
531 }
532
533 class ReplaceConstSource : public AluInstrVisitor {
534 public:
ReplaceConstSource(Instr * old_use_,RegisterVec4 & vreg_,int i)535 ReplaceConstSource(Instr *old_use_, RegisterVec4& vreg_, int i):
536 old_use(old_use_), vreg(vreg_), index(i),success(false) {}
537
538 using AluInstrVisitor::visit;
539
540 void visit(AluInstr *alu) override;
541
542 Instr *old_use;
543 RegisterVec4& vreg;
544 int index;
545 bool success;
546 };
547
visit(ExportInstr * instr)548 void SimplifySourceVecVisitor::visit(ExportInstr *instr)
549 {
550 replace_src(instr, instr->value());
551 }
552
replace_src(Instr * instr,RegisterVec4 & reg4)553 void SimplifySourceVecVisitor::replace_src(Instr *instr, RegisterVec4& reg4)
554 {
555 for (int i = 0; i < 4; ++i) {
556 auto s = reg4[i];
557
558 if (s->chan() > 3)
559 continue;
560
561 if (!s->is_ssa())
562 continue;
563
564 /* Cayman trans ops have more then one parent for
565 * one dest */
566 if (s->parents().size() != 1)
567 continue;
568
569 auto& op = *s->parents().begin();
570
571 ReplaceConstSource visitor(instr, reg4, i);
572
573 op->accept(visitor);
574
575 progress |= visitor.success;
576 }
577 }
578
visit(StreamOutInstr * instr)579 void SimplifySourceVecVisitor::visit(StreamOutInstr *instr)
580 {
581 (void)instr;
582 }
583
visit(MemRingOutInstr * instr)584 void SimplifySourceVecVisitor::visit(MemRingOutInstr *instr)
585 {
586 (void)instr;
587 }
588
visit(AluInstr * alu)589 void ReplaceConstSource::visit(AluInstr *alu)
590 {
591 if (alu->opcode() != op1_mov)
592 return;
593
594 if (alu->has_alu_flag(alu_src0_abs) ||
595 alu->has_alu_flag(alu_src0_neg))
596 return;
597
598 auto src = alu->psrc(0);
599 assert(src);
600
601 int override_chan = -1;
602
603 auto ic = src->as_inline_const();
604 if (ic) {
605 if (ic->sel() == ALU_SRC_0)
606 override_chan = 4;
607
608 if (ic->sel() == ALU_SRC_1)
609 override_chan = 5;
610 }
611
612 auto literal = src->as_literal();
613 if (literal) {
614
615 if (literal->value() == 0)
616 override_chan = 4;
617
618 if (literal->value() == 0x3F800000)
619 override_chan = 5;
620 }
621
622 if (override_chan >= 0) {
623 vreg[index]->del_use(old_use);
624 auto reg = new Register(vreg.sel(), override_chan, vreg[index]->pin());
625 vreg.set_value(index, reg);
626 success = true;
627 }
628 }
629
visit(FetchInstr * instr)630 void SimplifySourceVecVisitor::visit(FetchInstr *instr)
631 {
632 (void) instr;
633 }
634
visit(Block * instr)635 void SimplifySourceVecVisitor::visit(Block *instr)
636 {
637 for (auto i = instr->rbegin(); i != instr->rend(); ++i)
638 if (!(*i)->is_dead())
639 (*i)->accept(*this);
640 }
641
visit(ControlFlowInstr * instr)642 void SimplifySourceVecVisitor::visit(ControlFlowInstr *instr)
643 {
644 (void) instr;
645 }
646
visit(IfInstr * instr)647 void SimplifySourceVecVisitor::visit(IfInstr *instr)
648 {
649 (void) instr;
650 }
651
652
653
654 }
655