/* -*- mesa-c++ -*- * * Copyright (c) 2022 Collabora LTD * * Author: Gert Wollny * * Permission is hereby granted, free of charge, to any person obtaining a * copy of this software and associated documentation files (the "Software"), * to deal in the Software without restriction, including without limitation * on the rights to use, copy, modify, merge, publish, distribute, sub * license, and/or sell copies of the Software, and to permit persons to whom * the Software is furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice (including the next * paragraph) shall be included in all copies or substantial portions of the * Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL * THE AUTHOR(S) AND/OR THEIR SUPPLIERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE * USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "sfn_optimizer.h" #include "sfn_instr_alugroup.h" #include "sfn_instr_controlflow.h" #include "sfn_instr_export.h" #include "sfn_instr_tex.h" #include "sfn_instr_fetch.h" #include "sfn_instr_lds.h" #include "sfn_peephole.h" #include "sfn_debug.h" #include namespace r600 { bool optimize(Shader& shader) { bool progress; sfn_log << SfnLog::opt << "Shader before optimization\n"; if (sfn_log.has_debug_flag(SfnLog::opt)) { std::stringstream ss; shader.print(ss); sfn_log << ss.str() << "\n\n"; } do { progress = false; progress |= copy_propagation_fwd(shader); progress |= dead_code_elimination(shader); progress |= copy_propagation_backward(shader); progress |= dead_code_elimination(shader); progress |= simplify_source_vectors(shader); progress |= peephole(shader); progress |= dead_code_elimination(shader); } while (progress); return progress; } class DCEVisitor : public InstrVisitor { public: DCEVisitor(); void visit(AluInstr *instr) override; void visit(AluGroup *instr) override; void visit(TexInstr *instr) override; void visit(ExportInstr *instr) override {(void)instr;}; void visit(FetchInstr *instr) override; void visit(Block *instr) override; void visit(ControlFlowInstr *instr) override {(void)instr;}; void visit(IfInstr *instr) override {(void)instr;}; void visit(ScratchIOInstr *instr) override {(void)instr;}; void visit(StreamOutInstr *instr) override {(void)instr;}; void visit(MemRingOutInstr *instr) override {(void)instr;}; void visit(EmitVertexInstr *instr) override {(void)instr;}; void visit(GDSInstr *instr) override {(void)instr;}; void visit(WriteTFInstr *instr) override {(void)instr;}; void visit(LDSAtomicInstr *instr) override {(void)instr;}; void visit(LDSReadInstr *instr) override; void visit(RatInstr *instr) override {(void)instr;}; bool progress; }; bool dead_code_elimination(Shader& shader) { DCEVisitor dce; do { sfn_log << SfnLog::opt << "start dce run\n"; dce.progress = false; for (auto& b : shader.func()) b->accept(dce); sfn_log << SfnLog::opt << "finished dce run\n\n"; } while (dce.progress); sfn_log << SfnLog::opt << "Shader after DCE\n"; if (sfn_log.has_debug_flag(SfnLog::opt)) { std::stringstream ss; shader.print(ss); sfn_log << ss.str() << "\n\n"; } return dce.progress; } DCEVisitor::DCEVisitor():progress(false) { } void DCEVisitor::visit(AluInstr *instr) { sfn_log << SfnLog::opt << "DCE: visit '" << *instr; if (instr->has_instr_flag(Instr::dead)) return; if (instr->dest() && (instr->dest()->has_uses() || !instr->dest()->is_ssa()) ) { sfn_log << SfnLog::opt << " dest used\n"; return; } switch (instr->opcode()) { case op2_kille: case op2_killne: case op2_kille_int: case op2_killne_int: case op2_killge: case op2_killge_int: case op2_killge_uint: case op2_killgt: case op2_killgt_int: case op2_killgt_uint: case op0_group_barrier: sfn_log << SfnLog::opt << " never kill\n"; return; default: ; } bool dead = instr->set_dead(); sfn_log << SfnLog::opt << (dead ? "dead" : "alive") << "\n"; progress |= dead; } void DCEVisitor::visit(LDSReadInstr *instr) { sfn_log << SfnLog::opt << "visit " << *instr << "\n"; progress |= instr->remove_unused_components(); } void DCEVisitor::visit(AluGroup *instr) { /* Groups are created because the instructions are used together * so don't try to eliminate code there */ (void)instr; } void DCEVisitor::visit(TexInstr *instr) { auto& dest = instr->dst(); bool has_uses = false; RegisterVec4::Swizzle swz = instr->all_dest_swizzle(); for (int i = 0; i < 4; ++i) { if (!dest[i]->has_uses()) swz[i] = 7; else has_uses |= true; } instr->set_dest_swizzle(swz); if (has_uses) return; progress |= instr->set_dead(); } void DCEVisitor::visit(FetchInstr *instr) { auto& dest = instr->dst(); bool has_uses = false; RegisterVec4::Swizzle swz = instr->all_dest_swizzle(); for (int i = 0; i < 4; ++i) { if (!dest[i]->has_uses()) swz[i] = 7; else has_uses |= true; } instr->set_dest_swizzle(swz); if (has_uses) return; sfn_log << SfnLog::opt << "set dead: " << *instr << "\n"; progress |= instr->set_dead(); } void DCEVisitor::visit(Block *block) { auto i = block->begin(); auto e = block->end(); while (i != e) { auto n = i++; if (!(*n)->keep()) { (*n)->accept(*this); if ((*n)->is_dead()) { block->erase(n); } } } } void visit(ControlFlowInstr *instr) { (void)instr; } void visit(IfInstr *instr) { (void)instr; } class CopyPropFwdVisitor : public InstrVisitor { public: CopyPropFwdVisitor(); void visit(AluInstr *instr) override; void visit(AluGroup *instr) override; void visit(TexInstr *instr) override; void visit(ExportInstr *instr) override {(void)instr;} void visit(FetchInstr *instr) override; void visit(Block *instr) override; void visit(ControlFlowInstr *instr) override {(void)instr;} void visit(IfInstr *instr) override {(void)instr;} void visit(ScratchIOInstr *instr) override {(void)instr;} void visit(StreamOutInstr *instr) override {(void)instr;} void visit(MemRingOutInstr *instr) override {(void)instr;} void visit(EmitVertexInstr *instr) override {(void)instr;} void visit(GDSInstr *instr) override {(void)instr;}; void visit(WriteTFInstr *instr) override {(void)instr;}; void visit(RatInstr *instr) override {(void)instr;}; // TODO: these two should use copy propagation void visit(LDSAtomicInstr *instr) override {(void)instr;}; void visit(LDSReadInstr *instr) override {(void)instr;}; bool progress; }; class CopyPropBackVisitor : public InstrVisitor { public: CopyPropBackVisitor(); void visit(AluInstr *instr) override; void visit(AluGroup *instr) override; void visit(TexInstr *instr) override; void visit(ExportInstr *instr) override {(void)instr;} void visit(FetchInstr *instr) override; void visit(Block *instr) override; void visit(ControlFlowInstr *instr) override {(void)instr;} void visit(IfInstr *instr) override {(void)instr;} void visit(ScratchIOInstr *instr) override {(void)instr;} void visit(StreamOutInstr *instr) override {(void)instr;} void visit(MemRingOutInstr *instr) override {(void)instr;} void visit(EmitVertexInstr *instr) override {(void)instr;} void visit(GDSInstr *instr) override {(void)instr;}; void visit(WriteTFInstr *instr) override {(void)instr;}; void visit(LDSAtomicInstr *instr) override {(void)instr;}; void visit(LDSReadInstr *instr) override {(void)instr;}; void visit(RatInstr *instr) override {(void)instr;}; bool progress; }; bool copy_propagation_fwd(Shader& shader) { auto& root = shader.func(); CopyPropFwdVisitor copy_prop; do { copy_prop.progress = false; for (auto b : root) b->accept(copy_prop); } while (copy_prop.progress); sfn_log << SfnLog::opt << "Shader after Copy Prop forward\n"; if (sfn_log.has_debug_flag(SfnLog::opt)) { std::stringstream ss; shader.print(ss); sfn_log << ss.str() << "\n\n"; } return copy_prop.progress; } bool copy_propagation_backward(Shader& shader) { CopyPropBackVisitor copy_prop; do { copy_prop.progress = false; for (auto b: shader.func()) b->accept(copy_prop); } while (copy_prop.progress); sfn_log << SfnLog::opt << "Shader after Copy Prop backwards\n"; if (sfn_log.has_debug_flag(SfnLog::opt)) { std::stringstream ss; shader.print(ss); sfn_log << ss.str() << "\n\n"; } return copy_prop.progress; } CopyPropFwdVisitor::CopyPropFwdVisitor(): progress(false) {} void CopyPropFwdVisitor::visit(AluInstr *instr) { sfn_log << SfnLog::opt << "CopyPropFwdVisitor:[" << instr->block_id() << ":" << instr->index() << "] " << *instr << " dset=" << instr->dest() << " "; if (instr->dest()) { sfn_log << SfnLog::opt << "has uses; " << instr->dest()->uses().size(); } sfn_log << SfnLog::opt << "\n"; if (!instr->can_propagate_src()) { return; } auto src = instr->psrc(0); auto dest = instr->dest(); for (auto& i : instr->dest()->uses()) { /* SSA can always be propagated, registers only in the same block * and only if they are not assigned to more than once */ if (dest->is_ssa() || (instr->block_id() == i->block_id() && instr->index() < i->index() && dest->uses().size() == 1)) { sfn_log << SfnLog::opt << " Try replace in " << i->block_id() << ":" << i->index() << *i<< "\n"; progress |= i->replace_source(dest, src); } } if (instr->dest()) { sfn_log << SfnLog::opt << "has uses; " << instr->dest()->uses().size(); } sfn_log << SfnLog::opt << " done\n"; } void CopyPropFwdVisitor::visit(AluGroup *instr) { (void)instr; } void CopyPropFwdVisitor::visit(TexInstr *instr) { (void)instr; } void CopyPropFwdVisitor::visit(FetchInstr *instr) { (void)instr; } void CopyPropFwdVisitor::visit(Block *instr) { for (auto& i: *instr) i->accept(*this); } CopyPropBackVisitor::CopyPropBackVisitor(): progress(false) { } void CopyPropBackVisitor::visit(AluInstr *instr) { bool local_progress = false; sfn_log << SfnLog::opt << "CopyPropBackVisitor:[" << instr->block_id() << ":" << instr->index() << "] " << *instr << "\n"; if (!instr->can_propagate_dest()) { return; } auto src_reg = instr->psrc(0)->as_register(); if (!src_reg) { return; } if (src_reg->uses().size() > 1) return; auto dest = instr->dest(); if (!dest || !instr->has_alu_flag(alu_write)) { return; } if (!dest->is_ssa() && dest->parents().size() > 1) return; for (auto& i: src_reg->parents()) { sfn_log << SfnLog::opt << "Try replace dest in " << i->block_id() << ":" << i->index() << *i<< "\n"; if (i->replace_dest(dest, instr)) { dest->del_parent(instr); dest->add_parent(i); for (auto d : instr->dependend_instr()) { d->add_required_instr(i); } local_progress = true; } } if (local_progress) instr->set_dead(); progress |= local_progress; } void CopyPropBackVisitor::visit(AluGroup *instr) { for (auto& i: *instr) { if (i) i->accept(*this); } } void CopyPropBackVisitor::visit(TexInstr *instr) { (void)instr; } void CopyPropBackVisitor::visit(FetchInstr *instr) { (void)instr; } void CopyPropBackVisitor::visit(Block *instr) { for (auto i = instr->rbegin(); i != instr->rend(); ++i) if (!(*i)->is_dead()) (*i)->accept(*this); } class SimplifySourceVecVisitor : public InstrVisitor { public: SimplifySourceVecVisitor():progress(false) {} void visit(AluInstr *instr) override{(void)instr;} void visit(AluGroup *instr) override{(void)instr;} void visit(TexInstr *instr) override; void visit(ExportInstr *instr) override; void visit(FetchInstr *instr) override; void visit(Block *instr) override; void visit(ControlFlowInstr *instr) override; void visit(IfInstr *instr) override; void visit(ScratchIOInstr *instr) override; void visit(StreamOutInstr *instr) override; void visit(MemRingOutInstr *instr) override; void visit(EmitVertexInstr *instr) override {(void)instr;} void visit(GDSInstr *instr) override {(void)instr;}; void visit(WriteTFInstr *instr) override {(void)instr;}; void visit(LDSAtomicInstr *instr) override {(void)instr;}; void visit(LDSReadInstr *instr) override {(void)instr;}; void visit(RatInstr *instr) override {(void)instr;}; void replace_src(Instr *instr, RegisterVec4& reg4); bool progress; }; bool simplify_source_vectors(Shader& sh) { SimplifySourceVecVisitor visitor; for (auto b: sh.func()) b->accept(visitor); return visitor.progress; } void SimplifySourceVecVisitor::visit(TexInstr *instr) { if (instr->opcode() != TexInstr::get_resinfo) { replace_src(instr, instr->src()); } } void SimplifySourceVecVisitor::visit(ScratchIOInstr *instr) { (void) instr; } class ReplaceConstSource : public AluInstrVisitor { public: ReplaceConstSource(Instr *old_use_, RegisterVec4& vreg_, int i): old_use(old_use_), vreg(vreg_), index(i),success(false) {} using AluInstrVisitor::visit; void visit(AluInstr *alu) override; Instr *old_use; RegisterVec4& vreg; int index; bool success; }; void SimplifySourceVecVisitor::visit(ExportInstr *instr) { replace_src(instr, instr->value()); } void SimplifySourceVecVisitor::replace_src(Instr *instr, RegisterVec4& reg4) { for (int i = 0; i < 4; ++i) { auto s = reg4[i]; if (s->chan() > 3) continue; if (!s->is_ssa()) continue; /* Cayman trans ops have more then one parent for * one dest */ if (s->parents().size() != 1) continue; auto& op = *s->parents().begin(); ReplaceConstSource visitor(instr, reg4, i); op->accept(visitor); progress |= visitor.success; } } void SimplifySourceVecVisitor::visit(StreamOutInstr *instr) { (void)instr; } void SimplifySourceVecVisitor::visit(MemRingOutInstr *instr) { (void)instr; } void ReplaceConstSource::visit(AluInstr *alu) { if (alu->opcode() != op1_mov) return; if (alu->has_alu_flag(alu_src0_abs) || alu->has_alu_flag(alu_src0_neg)) return; auto src = alu->psrc(0); assert(src); int override_chan = -1; auto ic = src->as_inline_const(); if (ic) { if (ic->sel() == ALU_SRC_0) override_chan = 4; if (ic->sel() == ALU_SRC_1) override_chan = 5; } auto literal = src->as_literal(); if (literal) { if (literal->value() == 0) override_chan = 4; if (literal->value() == 0x3F800000) override_chan = 5; } if (override_chan >= 0) { vreg[index]->del_use(old_use); auto reg = new Register(vreg.sel(), override_chan, vreg[index]->pin()); vreg.set_value(index, reg); success = true; } } void SimplifySourceVecVisitor::visit(FetchInstr *instr) { (void) instr; } void SimplifySourceVecVisitor::visit(Block *instr) { for (auto i = instr->rbegin(); i != instr->rend(); ++i) if (!(*i)->is_dead()) (*i)->accept(*this); } void SimplifySourceVecVisitor::visit(ControlFlowInstr *instr) { (void) instr; } void SimplifySourceVecVisitor::visit(IfInstr *instr) { (void) instr; } }