diff options
Diffstat (limited to 'src/shader_recompiler/ir_opt')
5 files changed, 213 insertions, 0 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 new file mode 100644 index 000000000..bbaa412f6 --- /dev/null +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | |||
| @@ -0,0 +1,23 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <ranges> | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 9 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 10 | |||
| 11 | namespace Shader::Optimization { | ||
| 12 | |||
| 13 | void DeadCodeEliminationPass(IR::Block& block) { | ||
| 14 | // We iterate over the instructions in reverse order. | ||
| 15 | // This is because removing an instruction reduces the number of uses for earlier instructions. | ||
| 16 | for (IR::Inst& inst : std::views::reverse(block)) { | ||
| 17 | if (!inst.HasUses() && !inst.MayHaveSideEffects()) { | ||
| 18 | inst.Invalidate(); | ||
| 19 | } | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp b/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp new file mode 100644 index 000000000..21b8526cd --- /dev/null +++ b/src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp | |||
| @@ -0,0 +1,87 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <array> | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 9 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 10 | |||
| 11 | namespace Shader::Optimization { | ||
| 12 | namespace { | ||
| 13 | using Iterator = IR::Block::iterator; | ||
| 14 | |||
| 15 | enum class TrackingType { | ||
| 16 | Reg, | ||
| 17 | }; | ||
| 18 | |||
| 19 | struct RegisterInfo { | ||
| 20 | IR::Value register_value; | ||
| 21 | TrackingType tracking_type; | ||
| 22 | Iterator last_set_instruction; | ||
| 23 | bool set_instruction_present = false; | ||
| 24 | }; | ||
| 25 | |||
| 26 | void DoSet(IR::Block& block, RegisterInfo& info, IR::Value value, Iterator set_inst, | ||
| 27 | TrackingType tracking_type) { | ||
| 28 | if (info.set_instruction_present) { | ||
| 29 | info.last_set_instruction->Invalidate(); | ||
| 30 | block.Instructions().erase(info.last_set_instruction); | ||
| 31 | } | ||
| 32 | info.register_value = value; | ||
| 33 | info.tracking_type = tracking_type; | ||
| 34 | info.set_instruction_present = true; | ||
| 35 | info.last_set_instruction = set_inst; | ||
| 36 | } | ||
| 37 | |||
| 38 | RegisterInfo Nothing(Iterator get_inst, TrackingType tracking_type) { | ||
| 39 | RegisterInfo info{}; | ||
| 40 | info.register_value = IR::Value{&*get_inst}; | ||
| 41 | info.tracking_type = tracking_type; | ||
| 42 | return info; | ||
| 43 | } | ||
| 44 | |||
| 45 | void DoGet(RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) { | ||
| 46 | if (info.register_value.IsEmpty()) { | ||
| 47 | info = Nothing(get_inst, tracking_type); | ||
| 48 | return; | ||
| 49 | } | ||
| 50 | if (info.tracking_type == tracking_type) { | ||
| 51 | get_inst->ReplaceUsesWith(info.register_value); | ||
| 52 | return; | ||
| 53 | } | ||
| 54 | info = Nothing(get_inst, tracking_type); | ||
| 55 | } | ||
| 56 | } // Anonymous namespace | ||
| 57 | |||
| 58 | void GetSetElimination(IR::Block& block) { | ||
| 59 | std::array<RegisterInfo, 255> reg_info; | ||
| 60 | |||
| 61 | for (Iterator inst = block.begin(); inst != block.end(); ++inst) { | ||
| 62 | switch (inst->Opcode()) { | ||
| 63 | case IR::Opcode::GetRegister: { | ||
| 64 | const IR::Reg reg{inst->Arg(0).Reg()}; | ||
| 65 | if (reg == IR::Reg::RZ) { | ||
| 66 | break; | ||
| 67 | } | ||
| 68 | const size_t index{static_cast<size_t>(reg)}; | ||
| 69 | DoGet(reg_info.at(index), inst, TrackingType::Reg); | ||
| 70 | break; | ||
| 71 | } | ||
| 72 | case IR::Opcode::SetRegister: { | ||
| 73 | const IR::Reg reg{inst->Arg(0).Reg()}; | ||
| 74 | if (reg == IR::Reg::RZ) { | ||
| 75 | break; | ||
| 76 | } | ||
| 77 | const size_t index{static_cast<size_t>(reg)}; | ||
| 78 | DoSet(block, reg_info.at(index), inst->Arg(1), inst, TrackingType::Reg); | ||
| 79 | break; | ||
| 80 | } | ||
| 81 | default: | ||
| 82 | break; | ||
| 83 | } | ||
| 84 | } | ||
| 85 | } | ||
| 86 | |||
| 87 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp new file mode 100644 index 000000000..f9bb063fb --- /dev/null +++ b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp | |||
| @@ -0,0 +1,37 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <vector> | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 9 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 10 | |||
| 11 | namespace Shader::Optimization { | ||
| 12 | |||
| 13 | void IdentityRemovalPass(IR::Block& block) { | ||
| 14 | std::vector<IR::Inst*> to_invalidate; | ||
| 15 | |||
| 16 | for (auto inst = block.begin(); inst != block.end();) { | ||
| 17 | const size_t num_args{inst->NumArgs()}; | ||
| 18 | for (size_t i = 0; i < num_args; ++i) { | ||
| 19 | IR::Value arg; | ||
| 20 | while ((arg = inst->Arg(i)).IsIdentity()) { | ||
| 21 | inst->SetArg(i, arg.Inst()->Arg(0)); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | if (inst->Opcode() == IR::Opcode::Identity || inst->Opcode() == IR::Opcode::Void) { | ||
| 25 | to_invalidate.push_back(&*inst); | ||
| 26 | inst = block.Instructions().erase(inst); | ||
| 27 | } else { | ||
| 28 | ++inst; | ||
| 29 | } | ||
| 30 | } | ||
| 31 | |||
| 32 | for (IR::Inst* const inst : to_invalidate) { | ||
| 33 | inst->Invalidate(); | ||
| 34 | } | ||
| 35 | } | ||
| 36 | |||
| 37 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h new file mode 100644 index 000000000..fe5454e9a --- /dev/null +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -0,0 +1,16 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #pragma once | ||
| 6 | |||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 8 | |||
| 9 | namespace Shader::Optimization { | ||
| 10 | |||
| 11 | void DeadCodeEliminationPass(IR::Block& block); | ||
| 12 | void GetSetElimination(IR::Block& block); | ||
| 13 | void IdentityRemovalPass(IR::Block& block); | ||
| 14 | void VerificationPass(const IR::Block& block); | ||
| 15 | |||
| 16 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/ir_opt/verification_pass.cpp b/src/shader_recompiler/ir_opt/verification_pass.cpp new file mode 100644 index 000000000..36d9ae39b --- /dev/null +++ b/src/shader_recompiler/ir_opt/verification_pass.cpp | |||
| @@ -0,0 +1,50 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | #include <map> | ||
| 6 | |||
| 7 | #include "shader_recompiler/exception.h" | ||
| 8 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 10 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 11 | |||
| 12 | namespace Shader::Optimization { | ||
| 13 | |||
| 14 | static void ValidateTypes(const IR::Block& block) { | ||
| 15 | for (const IR::Inst& inst : block) { | ||
| 16 | const size_t num_args{inst.NumArgs()}; | ||
| 17 | for (size_t i = 0; i < num_args; ++i) { | ||
| 18 | const IR::Type t1{inst.Arg(i).Type()}; | ||
| 19 | const IR::Type t2{IR::ArgTypeOf(inst.Opcode(), i)}; | ||
| 20 | if (!IR::AreTypesCompatible(t1, t2)) { | ||
| 21 | throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(block)); | ||
| 22 | } | ||
| 23 | } | ||
| 24 | } | ||
| 25 | } | ||
| 26 | |||
| 27 | static void ValidateUses(const IR::Block& block) { | ||
| 28 | std::map<IR::Inst*, int> actual_uses; | ||
| 29 | for (const IR::Inst& inst : block) { | ||
| 30 | const size_t num_args{inst.NumArgs()}; | ||
| 31 | for (size_t i = 0; i < num_args; ++i) { | ||
| 32 | const IR::Value arg{inst.Arg(i)}; | ||
| 33 | if (!arg.IsImmediate()) { | ||
| 34 | ++actual_uses[arg.Inst()]; | ||
| 35 | } | ||
| 36 | } | ||
| 37 | } | ||
| 38 | for (const auto [inst, uses] : actual_uses) { | ||
| 39 | if (inst->UseCount() != uses) { | ||
| 40 | throw LogicError("Invalid uses in block:\n{}", IR::DumpBlock(block)); | ||
| 41 | } | ||
| 42 | } | ||
| 43 | } | ||
| 44 | |||
| 45 | void VerificationPass(const IR::Block& block) { | ||
| 46 | ValidateTypes(block); | ||
| 47 | ValidateUses(block); | ||
| 48 | } | ||
| 49 | |||
| 50 | } // namespace Shader::Optimization | ||