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
40namespace r600 {
41
42bool 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
67class DCEVisitor : public InstrVisitor {
68public:
69   DCEVisitor();
70
71   void visit(AluInstr *instr) override;
72   void visit(AluGroup *instr) override;
73   void visit(TexInstr  *instr) override;
74   void visit(ExportInstr *instr) override {(void)instr;};
75   void visit(FetchInstr *instr) override;
76   void visit(Block *instr) override;
77
78   void visit(ControlFlowInstr *instr) override {(void)instr;};
79   void visit(IfInstr *instr) override {(void)instr;};
80   void visit(ScratchIOInstr *instr) override {(void)instr;};
81   void visit(StreamOutInstr *instr) override {(void)instr;};
82   void visit(MemRingOutInstr *instr) override {(void)instr;};
83   void visit(EmitVertexInstr *instr) override {(void)instr;};
84   void visit(GDSInstr *instr) override {(void)instr;};
85   void visit(WriteTFInstr *instr) override {(void)instr;};
86   void visit(LDSAtomicInstr *instr) override {(void)instr;};
87   void visit(LDSReadInstr *instr) override;
88   void visit(RatInstr *instr) override {(void)instr;};
89
90
91   bool progress;
92};
93
94bool 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
120DCEVisitor::DCEVisitor():progress(false)
121{
122}
123
124void 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
160void DCEVisitor::visit(LDSReadInstr *instr)
161{
162   sfn_log << SfnLog::opt << "visit " << *instr << "\n";
163   progress |= instr->remove_unused_components();
164}
165
166void 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
173void 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
193void 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
215void 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
230void visit(ControlFlowInstr *instr)
231{
232   (void)instr;
233}
234
235void visit(IfInstr *instr)
236{
237   (void)instr;
238}
239
240class CopyPropFwdVisitor : public InstrVisitor {
241public:
242   CopyPropFwdVisitor();
243
244   void visit(AluInstr *instr) override;
245   void visit(AluGroup *instr) override;
246   void visit(TexInstr *instr) override;
247   void visit(ExportInstr *instr) override {(void)instr;}
248   void visit(FetchInstr *instr) override;
249   void visit(Block *instr) override;
250   void visit(ControlFlowInstr *instr) override {(void)instr;}
251   void visit(IfInstr *instr) override {(void)instr;}
252   void visit(ScratchIOInstr *instr) override {(void)instr;}
253   void visit(StreamOutInstr *instr) override {(void)instr;}
254   void visit(MemRingOutInstr *instr) override {(void)instr;}
255   void visit(EmitVertexInstr *instr) override {(void)instr;}
256   void visit(GDSInstr *instr) override {(void)instr;};
257   void visit(WriteTFInstr *instr) override {(void)instr;};
258   void visit(RatInstr *instr) override {(void)instr;};
259
260   // TODO: these two should use copy propagation
261   void visit(LDSAtomicInstr *instr) override {(void)instr;};
262   void visit(LDSReadInstr *instr) override {(void)instr;};
263
264   bool progress;
265};
266
267
268class CopyPropBackVisitor : public InstrVisitor {
269public:
270   CopyPropBackVisitor();
271
272   void visit(AluInstr *instr) override;
273   void visit(AluGroup *instr) override;
274   void visit(TexInstr *instr) override;
275   void visit(ExportInstr *instr) override {(void)instr;}
276   void visit(FetchInstr *instr) override;
277   void visit(Block *instr) override;
278   void visit(ControlFlowInstr *instr) override {(void)instr;}
279   void visit(IfInstr *instr) override {(void)instr;}
280   void visit(ScratchIOInstr *instr) override {(void)instr;}
281   void visit(StreamOutInstr *instr) override {(void)instr;}
282   void visit(MemRingOutInstr *instr) override {(void)instr;}
283   void visit(EmitVertexInstr *instr) override {(void)instr;}
284   void visit(GDSInstr *instr) override {(void)instr;};
285   void visit(WriteTFInstr *instr) override {(void)instr;};
286   void visit(LDSAtomicInstr *instr) override {(void)instr;};
287   void visit(LDSReadInstr *instr) override {(void)instr;};
288   void visit(RatInstr *instr) override {(void)instr;};
289
290   bool progress;
291};
292
293bool 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
315bool 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
335CopyPropFwdVisitor::CopyPropFwdVisitor():
336   progress(false)
337{}
338
339void 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
382void CopyPropFwdVisitor::visit(AluGroup *instr)
383{
384   (void)instr;
385}
386
387void CopyPropFwdVisitor::visit(TexInstr *instr)
388{
389   (void)instr;
390}
391
392void CopyPropFwdVisitor::visit(FetchInstr *instr)
393{
394   (void)instr;
395}
396
397void CopyPropFwdVisitor::visit(Block *instr)
398{
399   for (auto& i: *instr)
400      i->accept(*this);
401}
402
403CopyPropBackVisitor::CopyPropBackVisitor():
404   progress(false)
405{
406
407}
408
409void 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
459void CopyPropBackVisitor::visit(AluGroup *instr)
460{
461   for (auto& i: *instr) {
462      if (i)
463         i->accept(*this);
464   }
465}
466
467void CopyPropBackVisitor::visit(TexInstr *instr)
468{
469   (void)instr;
470}
471
472void CopyPropBackVisitor::visit(FetchInstr *instr)
473{
474   (void)instr;
475}
476
477void 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
484class SimplifySourceVecVisitor : public InstrVisitor {
485public:
486   SimplifySourceVecVisitor():progress(false) {}
487
488   void visit(AluInstr *instr) override{(void)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;
499   void visit(EmitVertexInstr *instr) override {(void)instr;}
500   void visit(GDSInstr *instr) override {(void)instr;};
501   void visit(WriteTFInstr *instr) override {(void)instr;};
502   void visit(LDSAtomicInstr *instr) override {(void)instr;};
503   void visit(LDSReadInstr *instr) override {(void)instr;};
504   void visit(RatInstr *instr) override {(void)instr;};
505
506   void replace_src(Instr *instr, RegisterVec4& reg4);
507
508   bool progress;
509};
510
511bool 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
521void SimplifySourceVecVisitor::visit(TexInstr *instr)
522{
523   if (instr->opcode() != TexInstr::get_resinfo) {
524      replace_src(instr, instr->src());
525   }
526}
527
528void SimplifySourceVecVisitor::visit(ScratchIOInstr *instr)
529{
530   (void) instr;
531}
532
533class ReplaceConstSource : public AluInstrVisitor {
534public:
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
548void SimplifySourceVecVisitor::visit(ExportInstr *instr)
549{
550   replace_src(instr, instr->value());
551}
552
553void 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
579void SimplifySourceVecVisitor::visit(StreamOutInstr *instr)
580{
581   (void)instr;
582}
583
584void SimplifySourceVecVisitor::visit(MemRingOutInstr *instr)
585{
586   (void)instr;
587}
588
589void 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
630void SimplifySourceVecVisitor::visit(FetchInstr *instr)
631{
632   (void) instr;
633}
634
635void 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
642void SimplifySourceVecVisitor::visit(ControlFlowInstr *instr)
643{
644   (void) instr;
645}
646
647void SimplifySourceVecVisitor::visit(IfInstr *instr)
648{
649   (void) instr;
650}
651
652
653
654}
655