Просмотр исходного кода

Merge pull request #8068 from ameerj/shader-if-false

shader_recompiler/dead_code_elimination: Add DeadBranchElimination pass
Fernando S 4 лет назад
Родитель
Сommit
d108c2873d

+ 5 - 0
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<ptrdiff_t>(index)};
+    phi_args.erase(operand_it);
+}
+
 void Inst::OrderPhiArgs() {
     if (op != Opcode::Phi) {
         throw LogicError("{} is not a Phi instruction", op);

+ 4 - 0
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();
 

+ 89 - 9
src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp

@@ -2,24 +2,104 @@
 // Licensed under GPLv2 or any later version
 // Refer to the license.txt file included.
 
+#include <algorithm>
+
+#include <boost/container/small_vector.hpp>
+
 #include "shader_recompiler/frontend/ir/basic_block.h"
 #include "shader_recompiler/frontend/ir/value.h"
 #include "shader_recompiler/ir_opt/passes.h"
 
 namespace Shader::Optimization {
-
-void DeadCodeEliminationPass(IR::Program& program) {
+namespace {
+template <bool TEST_USES>
+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 DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> 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<const IR::Block*, 3> 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) {
+            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<false>(block);
+                dead_blocks.push_back(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;
+    }
+    if (!dead_blocks.empty()) {
+        DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
+    }
+}
+} // namespace
+
+void DeadCodeEliminationPass(IR::Program& program) {
+    DeadBranchElimination(program);
+    for (IR::Block* const block : program.post_order_blocks) {
+        DeadInstElimination<true>(block);
     }
 }