dead_code_elimination_pass.cpp 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <boost/container/small_vector.hpp>
  6. #include "shader_recompiler/frontend/ir/basic_block.h"
  7. #include "shader_recompiler/frontend/ir/value.h"
  8. #include "shader_recompiler/ir_opt/passes.h"
  9. namespace Shader::Optimization {
  10. namespace {
  11. template <bool TEST_USES>
  12. void DeadInstElimination(IR::Block* const block) {
  13. // We iterate over the instructions in reverse order.
  14. // This is because removing an instruction reduces the number of uses for earlier instructions.
  15. auto it{block->end()};
  16. while (it != block->begin()) {
  17. --it;
  18. if constexpr (TEST_USES) {
  19. if (it->HasUses() || it->MayHaveSideEffects()) {
  20. continue;
  21. }
  22. }
  23. it->Invalidate();
  24. it = block->Instructions().erase(it);
  25. }
  26. }
  27. void DeletedPhiArgElimination(IR::Program& program, std::span<const IR::Block*> dead_blocks) {
  28. for (IR::Block* const block : program.blocks) {
  29. for (IR::Inst& phi : *block) {
  30. if (!IR::IsPhi(phi)) {
  31. continue;
  32. }
  33. for (size_t i = 0; i < phi.NumArgs(); ++i) {
  34. if (std::ranges::find(dead_blocks, phi.PhiBlock(i)) == dead_blocks.end()) {
  35. continue;
  36. }
  37. // Phi operand at this index is an unreachable block
  38. phi.ErasePhiOperand(i);
  39. --i;
  40. }
  41. }
  42. }
  43. }
  44. void DeadBranchElimination(IR::Program& program) {
  45. boost::container::small_vector<const IR::Block*, 3> dead_blocks;
  46. const auto begin_it{program.syntax_list.begin()};
  47. for (auto node_it = begin_it; node_it != program.syntax_list.end(); ++node_it) {
  48. if (node_it->type != IR::AbstractSyntaxNode::Type::If) {
  49. continue;
  50. }
  51. IR::Inst* const cond_ref{node_it->data.if_node.cond.Inst()};
  52. const IR::U1 cond{cond_ref->Arg(0)};
  53. if (!cond.IsImmediate()) {
  54. continue;
  55. }
  56. if (cond.U1()) {
  57. continue;
  58. }
  59. // False immediate condition. Remove condition ref, erase the entire branch.
  60. cond_ref->Invalidate();
  61. // Account for nested if-statements within the if(false) branch
  62. u32 nested_ifs{1u};
  63. while (node_it->type != IR::AbstractSyntaxNode::Type::EndIf || nested_ifs > 0) {
  64. node_it = program.syntax_list.erase(node_it);
  65. switch (node_it->type) {
  66. case IR::AbstractSyntaxNode::Type::If:
  67. ++nested_ifs;
  68. break;
  69. case IR::AbstractSyntaxNode::Type::EndIf:
  70. --nested_ifs;
  71. break;
  72. case IR::AbstractSyntaxNode::Type::Block: {
  73. IR::Block* const block{node_it->data.block};
  74. DeadInstElimination<false>(block);
  75. dead_blocks.push_back(block);
  76. break;
  77. }
  78. default:
  79. break;
  80. }
  81. }
  82. // Erase EndIf node of the if(false) branch
  83. node_it = program.syntax_list.erase(node_it);
  84. // Account for loop increment
  85. --node_it;
  86. }
  87. if (!dead_blocks.empty()) {
  88. DeletedPhiArgElimination(program, std::span(dead_blocks.data(), dead_blocks.size()));
  89. }
  90. }
  91. } // namespace
  92. void DeadCodeEliminationPass(IR::Program& program) {
  93. DeadBranchElimination(program);
  94. for (IR::Block* const block : program.post_order_blocks) {
  95. DeadInstElimination<true>(block);
  96. }
  97. }
  98. } // namespace Shader::Optimization