From f10d40a0a25dc6709b8cbd0a6793175434db6472 Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Tue, 22 Mar 2022 01:22:21 -0400 Subject: [PATCH 1/2] shader_recompiler/dead_code_elimination: Add DeadBranchElimination pass This adds a pass to eliminate if(false) branches within the shader code --- .../ir_opt/dead_code_elimination_pass.cpp | 71 ++++++++++++++++--- 1 file changed, 62 insertions(+), 9 deletions(-) diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 4008363016..6c7c7b32db 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp @@ -7,19 +7,72 @@ #include "shader_recompiler/ir_opt/passes.h" namespace Shader::Optimization { - -void DeadCodeEliminationPass(IR::Program& program) { +namespace { +template +void DeadInstElimination(IR::Block* const block) { // We iterate over the instructions in reverse order. // This is because removing an instruction reduces the number of uses for earlier instructions. - for (IR::Block* const block : program.post_order_blocks) { - auto it{block->end()}; - while (it != block->begin()) { - --it; - if (!it->HasUses() && !it->MayHaveSideEffects()) { - it->Invalidate(); - it = block->Instructions().erase(it); + auto it{block->end()}; + while (it != block->begin()) { + --it; + if constexpr (TEST_USES) { + if (it->HasUses() || it->MayHaveSideEffects()) { + continue; } } + it->Invalidate(); + it = block->Instructions().erase(it); + } +} + +void DeadBranchElimination(IR::Program& program) { + const auto begin_it{program.syntax_list.begin()}; + for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) { + if (node_it->type != IR::AbstractSyntaxNode::Type::If) { + continue; + } + IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()}; + const IR::U1 cond{cond_ref->Arg(0)}; + if (!cond.IsImmediate()) { + continue; + } + if (cond.U1()) { + continue; + } + // False immediate condition. Remove condition ref, erase the entire branch. + cond_ref->Invalidate(); + // Account for nested if-statements within the if(false) branch + u32 nested_ifs{1u}; + while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) { + node_it = program.syntax_list.erase(node_it); + switch (node_it->type) { + case IR::AbstractSyntaxNode::Type::If: + ++nested_ifs; + break; + case IR::AbstractSyntaxNode::Type::EndIf: + --nested_ifs; + break; + case IR::AbstractSyntaxNode::Type::Block: { + IR::Block* const block{node_it->data.block}; + DeadInstElimination(block); + break; + } + default: + break; + } + } + // Erase EndIf node of the if(false) branch + node_it = program.syntax_list.erase(node_it); + // Account for loop increment + --node_it; + } +} +} // namespace + +void DeadCodeEliminationPass(IR::Program& program) { + DeadBranchElimination(program); + for (IR::Block* const block : program.post_order_blocks) { + DeadInstElimination(block); } } From 67159e3be76cf468288ae492ea22fc43a77a9d1e Mon Sep 17 00:00:00 2001 From: ameerj <52414509+ameerj@users.noreply.github.com> Date: Tue, 22 Mar 2022 18:34:31 -0400 Subject: [PATCH 2/2] dead_code_elimination_pass: Remove unreachable Phi arguments --- .../frontend/ir/microinstruction.cpp | 5 ++++ src/shader_recompiler/frontend/ir/value.h | 4 +++ .../ir_opt/dead_code_elimination_pass.cpp | 27 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 631446cf7d..4a2564f472 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp @@ -326,6 +326,11 @@ void Inst::AddPhiOperand(Block* predecessor, const Value& value) { phi_args.emplace_back(predecessor, value); } +void Inst::ErasePhiOperand(size_t index) { + const auto operand_it{phi_args.begin() + static_cast(index)}; + phi_args.erase(operand_it); +} + void Inst::OrderPhiArgs() { if (op != Opcode::Phi) { throw LogicError("{} is not a Phi instruction", op); diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 9475798524..14f6e55bc6 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h @@ -179,9 +179,13 @@ public: /// Get a pointer to the block of a phi argument. [[nodiscard]] Block* PhiBlock(size_t index) const; + /// Add phi operand to a phi instruction. void AddPhiOperand(Block* predecessor, const Value& value); + // Erase the phi operand at the given index. + void ErasePhiOperand(size_t index); + /// Orders the Phi arguments from farthest away to nearest. void OrderPhiArgs(); diff --git a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp index 6c7c7b32db..6697fde850 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp @@ -2,6 +2,10 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. +#include + +#include + #include "shader_recompiler/frontend/ir/basic_block.h" #include "shader_recompiler/frontend/ir/value.h" #include "shader_recompiler/ir_opt/passes.h" @@ -25,7 +29,26 @@ void DeadInstElimination(IR::Block* const block) { } } +void DeletedPhiArgElimination(IR::Program& program, std::span dead_blocks) { + for (IR::Block* const block : program.blocks) { + for (IR::Inst& phi : *block) { + if (!IR::IsPhi(phi)) { + continue; + } + for (size_t i = 0; i < phi.NumArgs(); ++i) { + if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) { + continue; + } + // Phi operand at this index is an unreachable block + phi.ErasePhiOperand(i); + --i; + } + } + } +} + void DeadBranchElimination(IR::Program& program) { + boost::container::small_vector dead_blocks; const auto begin_it{program.syntax_list.begin()}; for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) { if (node_it->type != IR::AbstractSyntaxNode::Type::If) { @@ -55,6 +78,7 @@ void DeadBranchElimination(IR::Program& program) { case IR::AbstractSyntaxNode::Type::Block: { IR::Block* const block{node_it->data.block}; DeadInstElimination(block); + dead_blocks.push_back(block); break; } default: @@ -66,6 +90,9 @@ void DeadBranchElimination(IR::Program& program) { // Account for loop increment --node_it; } + if (!dead_blocks.empty()) { + DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size())); + } } } // namespace