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] 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 631446cf7..4a2564f47 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<ptrdiff_t>(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 947579852..14f6e55bc 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 6c7c7b32d..6697fde85 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 <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"
@@ -25,7 +29,26 @@ void DeadInstElimination(IR::Block* const block) {
     }
 }
 
+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) {
@@ -55,6 +78,7 @@ void DeadBranchElimination(IR::Program& program) {
             case IR::AbstractSyntaxNode::Type::Block: {
                 IR::Block* const block{node_it->data.block};
                 DeadInstElimination<false>(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