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