summaryrefslogtreecommitdiffstats
path: root/src/shader_recompiler/ir_opt
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp23
-rw-r--r--src/shader_recompiler/ir_opt/get_set_elimination_pass.cpp87
-rw-r--r--src/shader_recompiler/ir_opt/identity_removal_pass.cpp37
-rw-r--r--src/shader_recompiler/ir_opt/passes.h16
-rw-r--r--src/shader_recompiler/ir_opt/verification_pass.cpp50
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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <ranges>
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/microinstruction.h"
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Optimization {
+
+void DeadCodeEliminationPass(IR::Block& 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::Inst& inst : std::views::reverse(block)) {
+ if (!inst.HasUses() && !inst.MayHaveSideEffects()) {
+ inst.Invalidate();
+ }
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/microinstruction.h"
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Optimization {
+namespace {
+using Iterator = IR::Block::iterator;
+
+enum class TrackingType {
+ Reg,
+};
+
+struct RegisterInfo {
+ IR::Value register_value;
+ TrackingType tracking_type;
+ Iterator last_set_instruction;
+ bool set_instruction_present = false;
+};
+
+void DoSet(IR::Block& block, RegisterInfo& info, IR::Value value, Iterator set_inst,
+ TrackingType tracking_type) {
+ if (info.set_instruction_present) {
+ info.last_set_instruction->Invalidate();
+ block.Instructions().erase(info.last_set_instruction);
+ }
+ info.register_value = value;
+ info.tracking_type = tracking_type;
+ info.set_instruction_present = true;
+ info.last_set_instruction = set_inst;
+}
+
+RegisterInfo Nothing(Iterator get_inst, TrackingType tracking_type) {
+ RegisterInfo info{};
+ info.register_value = IR::Value{&*get_inst};
+ info.tracking_type = tracking_type;
+ return info;
+}
+
+void DoGet(RegisterInfo& info, Iterator get_inst, TrackingType tracking_type) {
+ if (info.register_value.IsEmpty()) {
+ info = Nothing(get_inst, tracking_type);
+ return;
+ }
+ if (info.tracking_type == tracking_type) {
+ get_inst->ReplaceUsesWith(info.register_value);
+ return;
+ }
+ info = Nothing(get_inst, tracking_type);
+}
+} // Anonymous namespace
+
+void GetSetElimination(IR::Block& block) {
+ std::array<RegisterInfo, 255> reg_info;
+
+ for (Iterator inst = block.begin(); inst != block.end(); ++inst) {
+ switch (inst->Opcode()) {
+ case IR::Opcode::GetRegister: {
+ const IR::Reg reg{inst->Arg(0).Reg()};
+ if (reg == IR::Reg::RZ) {
+ break;
+ }
+ const size_t index{static_cast<size_t>(reg)};
+ DoGet(reg_info.at(index), inst, TrackingType::Reg);
+ break;
+ }
+ case IR::Opcode::SetRegister: {
+ const IR::Reg reg{inst->Arg(0).Reg()};
+ if (reg == IR::Reg::RZ) {
+ break;
+ }
+ const size_t index{static_cast<size_t>(reg)};
+ DoSet(block, reg_info.at(index), inst->Arg(1), inst, TrackingType::Reg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <vector>
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/microinstruction.h"
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Optimization {
+
+void IdentityRemovalPass(IR::Block& block) {
+ std::vector<IR::Inst*> to_invalidate;
+
+ for (auto inst = block.begin(); inst != block.end();) {
+ const size_t num_args{inst->NumArgs()};
+ for (size_t i = 0; i < num_args; ++i) {
+ IR::Value arg;
+ while ((arg = inst->Arg(i)).IsIdentity()) {
+ inst->SetArg(i, arg.Inst()->Arg(0));
+ }
+ }
+ if (inst->Opcode() == IR::Opcode::Identity || inst->Opcode() == IR::Opcode::Void) {
+ to_invalidate.push_back(&*inst);
+ inst = block.Instructions().erase(inst);
+ } else {
+ ++inst;
+ }
+ }
+
+ for (IR::Inst* const inst : to_invalidate) {
+ inst->Invalidate();
+ }
+}
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "shader_recompiler/frontend/ir/basic_block.h"
+
+namespace Shader::Optimization {
+
+void DeadCodeEliminationPass(IR::Block& block);
+void GetSetElimination(IR::Block& block);
+void IdentityRemovalPass(IR::Block& block);
+void VerificationPass(const IR::Block& block);
+
+} // 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 @@
+// Copyright 2021 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <map>
+
+#include "shader_recompiler/exception.h"
+#include "shader_recompiler/frontend/ir/basic_block.h"
+#include "shader_recompiler/frontend/ir/microinstruction.h"
+#include "shader_recompiler/ir_opt/passes.h"
+
+namespace Shader::Optimization {
+
+static void ValidateTypes(const IR::Block& block) {
+ for (const IR::Inst& inst : block) {
+ const size_t num_args{inst.NumArgs()};
+ for (size_t i = 0; i < num_args; ++i) {
+ const IR::Type t1{inst.Arg(i).Type()};
+ const IR::Type t2{IR::ArgTypeOf(inst.Opcode(), i)};
+ if (!IR::AreTypesCompatible(t1, t2)) {
+ throw LogicError("Invalid types in block:\n{}", IR::DumpBlock(block));
+ }
+ }
+ }
+}
+
+static void ValidateUses(const IR::Block& block) {
+ std::map<IR::Inst*, int> actual_uses;
+ for (const IR::Inst& inst : block) {
+ const size_t num_args{inst.NumArgs()};
+ for (size_t i = 0; i < num_args; ++i) {
+ const IR::Value arg{inst.Arg(i)};
+ if (!arg.IsImmediate()) {
+ ++actual_uses[arg.Inst()];
+ }
+ }
+ }
+ for (const auto [inst, uses] : actual_uses) {
+ if (inst->UseCount() != uses) {
+ throw LogicError("Invalid uses in block:\n{}", IR::DumpBlock(block));
+ }
+ }
+}
+
+void VerificationPass(const IR::Block& block) {
+ ValidateTypes(block);
+ ValidateUses(block);
+}
+
+} // namespace Shader::Optimization