diff options
| author | 2021-02-02 21:07:00 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:21 -0400 | |
| commit | 6c4cc0cd062fbbba5349da1108d3c23cb330ca8a (patch) | |
| tree | 544291931da8a85fafcea71964c77d9278ec7f29 /src | |
| parent | shader: Initial recompiler work (diff) | |
| download | yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.gz yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.xz yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.zip | |
shader: SSA and dominance
Diffstat (limited to 'src')
24 files changed, 570 insertions, 77 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index c65846bc4..36a61f21a 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -11,6 +11,8 @@ add_executable(shader_recompiler | |||
| 11 | frontend/ir/condition.h | 11 | frontend/ir/condition.h |
| 12 | frontend/ir/flow_test.cpp | 12 | frontend/ir/flow_test.cpp |
| 13 | frontend/ir/flow_test.h | 13 | frontend/ir/flow_test.h |
| 14 | frontend/ir/function.cpp | ||
| 15 | frontend/ir/function.h | ||
| 14 | frontend/ir/ir_emitter.cpp | 16 | frontend/ir/ir_emitter.cpp |
| 15 | frontend/ir/ir_emitter.h | 17 | frontend/ir/ir_emitter.h |
| 16 | frontend/ir/microinstruction.cpp | 18 | frontend/ir/microinstruction.cpp |
| @@ -51,6 +53,7 @@ add_executable(shader_recompiler | |||
| 51 | ir_opt/get_set_elimination_pass.cpp | 53 | ir_opt/get_set_elimination_pass.cpp |
| 52 | ir_opt/identity_removal_pass.cpp | 54 | ir_opt/identity_removal_pass.cpp |
| 53 | ir_opt/passes.h | 55 | ir_opt/passes.h |
| 56 | ir_opt/ssa_rewrite_pass.cpp | ||
| 54 | ir_opt/verification_pass.cpp | 57 | ir_opt/verification_pass.cpp |
| 55 | main.cpp | 58 | main.cpp |
| 56 | ) | 59 | ) |
diff --git a/src/shader_recompiler/frontend/ir/basic_block.cpp b/src/shader_recompiler/frontend/ir/basic_block.cpp index 0406726ad..e795618fc 100644 --- a/src/shader_recompiler/frontend/ir/basic_block.cpp +++ b/src/shader_recompiler/frontend/ir/basic_block.cpp | |||
| @@ -37,6 +37,10 @@ Block::iterator Block::PrependNewInst(iterator insertion_point, Opcode op, | |||
| 37 | return result_it; | 37 | return result_it; |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | void Block::AddImmediatePredecessor(IR::Block* immediate_predecessor) { | ||
| 41 | imm_predecessors.push_back(immediate_predecessor); | ||
| 42 | } | ||
| 43 | |||
| 40 | u32 Block::LocationBegin() const noexcept { | 44 | u32 Block::LocationBegin() const noexcept { |
| 41 | return location_begin; | 45 | return location_begin; |
| 42 | } | 46 | } |
| @@ -53,6 +57,18 @@ const Block::InstructionList& Block::Instructions() const noexcept { | |||
| 53 | return instructions; | 57 | return instructions; |
| 54 | } | 58 | } |
| 55 | 59 | ||
| 60 | std::span<IR::Block* const> Block::ImmediatePredecessors() const noexcept { | ||
| 61 | return imm_predecessors; | ||
| 62 | } | ||
| 63 | |||
| 64 | static std::string BlockToIndex(const std::map<const Block*, size_t>& block_to_index, | ||
| 65 | Block* block) { | ||
| 66 | if (const auto it{block_to_index.find(block)}; it != block_to_index.end()) { | ||
| 67 | return fmt::format("{{Block ${}}}", it->second); | ||
| 68 | } | ||
| 69 | return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(block)); | ||
| 70 | } | ||
| 71 | |||
| 56 | static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index, | 72 | static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_index, |
| 57 | const std::map<const Inst*, size_t>& inst_to_index, | 73 | const std::map<const Inst*, size_t>& inst_to_index, |
| 58 | const Value& arg) { | 74 | const Value& arg) { |
| @@ -60,10 +76,7 @@ static std::string ArgToIndex(const std::map<const Block*, size_t>& block_to_ind | |||
| 60 | return "<null>"; | 76 | return "<null>"; |
| 61 | } | 77 | } |
| 62 | if (arg.IsLabel()) { | 78 | if (arg.IsLabel()) { |
| 63 | if (const auto it{block_to_index.find(arg.Label())}; it != block_to_index.end()) { | 79 | return BlockToIndex(block_to_index, arg.Label()); |
| 64 | return fmt::format("{{Block ${}}}", it->second); | ||
| 65 | } | ||
| 66 | return fmt::format("$<unknown block {:016x}>", reinterpret_cast<u64>(arg.Label())); | ||
| 67 | } | 80 | } |
| 68 | if (!arg.IsImmediate()) { | 81 | if (!arg.IsImmediate()) { |
| 69 | if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) { | 82 | if (const auto it{inst_to_index.find(arg.Inst())}; it != inst_to_index.end()) { |
| @@ -115,16 +128,26 @@ std::string DumpBlock(const Block& block, const std::map<const Block*, size_t>& | |||
| 115 | } else { | 128 | } else { |
| 116 | ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces | 129 | ret += fmt::format(" {}", op); // '%00000 = ' -> 1 + 5 + 3 = 9 spaces |
| 117 | } | 130 | } |
| 118 | const size_t arg_count{NumArgsOf(op)}; | 131 | if (op == Opcode::Phi) { |
| 119 | for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { | 132 | size_t val_index{0}; |
| 120 | const Value arg{inst.Arg(arg_index)}; | 133 | for (const auto& [phi_block, phi_val] : inst.PhiOperands()) { |
| 121 | ret += arg_index != 0 ? ", " : " "; | 134 | ret += val_index != 0 ? ", " : " "; |
| 122 | ret += ArgToIndex(block_to_index, inst_to_index, arg); | 135 | ret += fmt::format("[ {}, {} ]", ArgToIndex(block_to_index, inst_to_index, phi_val), |
| 123 | 136 | BlockToIndex(block_to_index, phi_block)); | |
| 124 | const Type actual_type{arg.Type()}; | 137 | ++val_index; |
| 125 | const Type expected_type{ArgTypeOf(op, arg_index)}; | 138 | } |
| 126 | if (!AreTypesCompatible(actual_type, expected_type)) { | 139 | } else { |
| 127 | ret += fmt::format("<type error: {} != {}>", actual_type, expected_type); | 140 | const size_t arg_count{NumArgsOf(op)}; |
| 141 | for (size_t arg_index = 0; arg_index < arg_count; ++arg_index) { | ||
| 142 | const Value arg{inst.Arg(arg_index)}; | ||
| 143 | ret += arg_index != 0 ? ", " : " "; | ||
| 144 | ret += ArgToIndex(block_to_index, inst_to_index, arg); | ||
| 145 | |||
| 146 | const Type actual_type{arg.Type()}; | ||
| 147 | const Type expected_type{ArgTypeOf(op, arg_index)}; | ||
| 148 | if (!AreTypesCompatible(actual_type, expected_type)) { | ||
| 149 | ret += fmt::format("<type error: {} != {}>", actual_type, expected_type); | ||
| 150 | } | ||
| 128 | } | 151 | } |
| 129 | } | 152 | } |
| 130 | if (TypeOf(op) != Type::Void) { | 153 | if (TypeOf(op) != Type::Void) { |
diff --git a/src/shader_recompiler/frontend/ir/basic_block.h b/src/shader_recompiler/frontend/ir/basic_block.h index 3ed2eb957..4b6b80c4b 100644 --- a/src/shader_recompiler/frontend/ir/basic_block.h +++ b/src/shader_recompiler/frontend/ir/basic_block.h | |||
| @@ -6,6 +6,8 @@ | |||
| 6 | 6 | ||
| 7 | #include <initializer_list> | 7 | #include <initializer_list> |
| 8 | #include <map> | 8 | #include <map> |
| 9 | #include <span> | ||
| 10 | #include <vector> | ||
| 9 | 11 | ||
| 10 | #include <boost/intrusive/list.hpp> | 12 | #include <boost/intrusive/list.hpp> |
| 11 | #include <boost/pool/pool_alloc.hpp> | 13 | #include <boost/pool/pool_alloc.hpp> |
| @@ -36,7 +38,11 @@ public: | |||
| 36 | void AppendNewInst(Opcode op, std::initializer_list<Value> args); | 38 | void AppendNewInst(Opcode op, std::initializer_list<Value> args); |
| 37 | 39 | ||
| 38 | /// Prepends a new instruction to this basic block before the insertion point. | 40 | /// Prepends a new instruction to this basic block before the insertion point. |
| 39 | iterator PrependNewInst(iterator insertion_point, Opcode op, std::initializer_list<Value> args); | 41 | iterator PrependNewInst(iterator insertion_point, Opcode op, |
| 42 | std::initializer_list<Value> args = {}); | ||
| 43 | |||
| 44 | /// Adds a new immediate predecessor to the basic block. | ||
| 45 | void AddImmediatePredecessor(IR::Block* immediate_predecessor); | ||
| 40 | 46 | ||
| 41 | /// Gets the starting location of this basic block. | 47 | /// Gets the starting location of this basic block. |
| 42 | [[nodiscard]] u32 LocationBegin() const noexcept; | 48 | [[nodiscard]] u32 LocationBegin() const noexcept; |
| @@ -44,9 +50,12 @@ public: | |||
| 44 | [[nodiscard]] u32 LocationEnd() const noexcept; | 50 | [[nodiscard]] u32 LocationEnd() const noexcept; |
| 45 | 51 | ||
| 46 | /// Gets a mutable reference to the instruction list for this basic block. | 52 | /// Gets a mutable reference to the instruction list for this basic block. |
| 47 | InstructionList& Instructions() noexcept; | 53 | [[nodiscard]] InstructionList& Instructions() noexcept; |
| 48 | /// Gets an immutable reference to the instruction list for this basic block. | 54 | /// Gets an immutable reference to the instruction list for this basic block. |
| 49 | const InstructionList& Instructions() const noexcept; | 55 | [[nodiscard]] const InstructionList& Instructions() const noexcept; |
| 56 | |||
| 57 | /// Gets an immutable span to the immediate predecessors. | ||
| 58 | [[nodiscard]] std::span<IR::Block* const> ImmediatePredecessors() const noexcept; | ||
| 50 | 59 | ||
| 51 | [[nodiscard]] bool empty() const { | 60 | [[nodiscard]] bool empty() const { |
| 52 | return instructions.empty(); | 61 | return instructions.empty(); |
| @@ -115,13 +124,16 @@ private: | |||
| 115 | /// End location of this block | 124 | /// End location of this block |
| 116 | u32 location_end; | 125 | u32 location_end; |
| 117 | 126 | ||
| 118 | /// List of instructions in this block. | 127 | /// List of instructions in this block |
| 119 | InstructionList instructions; | 128 | InstructionList instructions; |
| 120 | 129 | ||
| 121 | /// Memory pool for instruction list | 130 | /// Memory pool for instruction list |
| 122 | boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free, | 131 | boost::fast_pool_allocator<Inst, boost::default_user_allocator_malloc_free, |
| 123 | boost::details::pool::null_mutex> | 132 | boost::details::pool::null_mutex> |
| 124 | instruction_alloc_pool; | 133 | instruction_alloc_pool; |
| 134 | |||
| 135 | /// Block immediate predecessors | ||
| 136 | std::vector<IR::Block*> imm_predecessors; | ||
| 125 | }; | 137 | }; |
| 126 | 138 | ||
| 127 | [[nodiscard]] std::string DumpBlock(const Block& block); | 139 | [[nodiscard]] std::string DumpBlock(const Block& block); |
diff --git a/src/shader_recompiler/frontend/ir/function.cpp b/src/shader_recompiler/frontend/ir/function.cpp new file mode 100644 index 000000000..d1fc9461d --- /dev/null +++ b/src/shader_recompiler/frontend/ir/function.cpp | |||
| @@ -0,0 +1,5 @@ | |||
| 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 "shader_recompiler/frontend/ir/function.h" | ||
diff --git a/src/shader_recompiler/frontend/ir/function.h b/src/shader_recompiler/frontend/ir/function.h new file mode 100644 index 000000000..2d4dc5b98 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/function.h | |||
| @@ -0,0 +1,25 @@ | |||
| 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 <memory> | ||
| 8 | #include <vector> | ||
| 9 | |||
| 10 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 11 | |||
| 12 | namespace Shader::IR { | ||
| 13 | |||
| 14 | struct Function { | ||
| 15 | struct InplaceDelete { | ||
| 16 | void operator()(IR::Block* block) const noexcept { | ||
| 17 | std::destroy_at(block); | ||
| 18 | } | ||
| 19 | }; | ||
| 20 | using UniqueBlock = std::unique_ptr<IR::Block, InplaceDelete>; | ||
| 21 | |||
| 22 | std::vector<UniqueBlock> blocks; | ||
| 23 | }; | ||
| 24 | |||
| 25 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.cpp b/src/shader_recompiler/frontend/ir/microinstruction.cpp index 553fec3b7..ecf76e23d 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.cpp +++ b/src/shader_recompiler/frontend/ir/microinstruction.cpp | |||
| @@ -30,6 +30,11 @@ static void RemovePseudoInstruction(IR::Inst*& inst, IR::Opcode expected_opcode) | |||
| 30 | 30 | ||
| 31 | bool Inst::MayHaveSideEffects() const noexcept { | 31 | bool Inst::MayHaveSideEffects() const noexcept { |
| 32 | switch (op) { | 32 | switch (op) { |
| 33 | case Opcode::Branch: | ||
| 34 | case Opcode::BranchConditional: | ||
| 35 | case Opcode::Exit: | ||
| 36 | case Opcode::Return: | ||
| 37 | case Opcode::Unreachable: | ||
| 33 | case Opcode::SetAttribute: | 38 | case Opcode::SetAttribute: |
| 34 | case Opcode::SetAttributeIndexed: | 39 | case Opcode::SetAttributeIndexed: |
| 35 | case Opcode::WriteGlobalU8: | 40 | case Opcode::WriteGlobalU8: |
| @@ -113,6 +118,17 @@ void Inst::SetArg(size_t index, Value value) { | |||
| 113 | args[index] = value; | 118 | args[index] = value; |
| 114 | } | 119 | } |
| 115 | 120 | ||
| 121 | std::span<const std::pair<Block*, Value>> Inst::PhiOperands() const noexcept { | ||
| 122 | return phi_operands; | ||
| 123 | } | ||
| 124 | |||
| 125 | void Inst::AddPhiOperand(Block* predecessor, const Value& value) { | ||
| 126 | if (!value.IsImmediate()) { | ||
| 127 | Use(value); | ||
| 128 | } | ||
| 129 | phi_operands.emplace_back(predecessor, value); | ||
| 130 | } | ||
| 131 | |||
| 116 | void Inst::Invalidate() { | 132 | void Inst::Invalidate() { |
| 117 | ClearArgs(); | 133 | ClearArgs(); |
| 118 | op = Opcode::Void; | 134 | op = Opcode::Void; |
| @@ -125,6 +141,12 @@ void Inst::ClearArgs() { | |||
| 125 | } | 141 | } |
| 126 | value = {}; | 142 | value = {}; |
| 127 | } | 143 | } |
| 144 | for (auto& [phi_block, phi_op] : phi_operands) { | ||
| 145 | if (!phi_op.IsImmediate()) { | ||
| 146 | UndoUse(phi_op); | ||
| 147 | } | ||
| 148 | } | ||
| 149 | phi_operands.clear(); | ||
| 128 | } | 150 | } |
| 129 | 151 | ||
| 130 | void Inst::ReplaceUsesWith(Value replacement) { | 152 | void Inst::ReplaceUsesWith(Value replacement) { |
diff --git a/src/shader_recompiler/frontend/ir/microinstruction.h b/src/shader_recompiler/frontend/ir/microinstruction.h index 43460b950..7f1ed6710 100644 --- a/src/shader_recompiler/frontend/ir/microinstruction.h +++ b/src/shader_recompiler/frontend/ir/microinstruction.h | |||
| @@ -5,6 +5,8 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <array> | 7 | #include <array> |
| 8 | #include <span> | ||
| 9 | #include <vector> | ||
| 8 | 10 | ||
| 9 | #include <boost/intrusive/list.hpp> | 11 | #include <boost/intrusive/list.hpp> |
| 10 | 12 | ||
| @@ -15,6 +17,8 @@ | |||
| 15 | 17 | ||
| 16 | namespace Shader::IR { | 18 | namespace Shader::IR { |
| 17 | 19 | ||
| 20 | class Block; | ||
| 21 | |||
| 18 | constexpr size_t MAX_ARG_COUNT = 4; | 22 | constexpr size_t MAX_ARG_COUNT = 4; |
| 19 | 23 | ||
| 20 | class Inst : public boost::intrusive::list_base_hook<> { | 24 | class Inst : public boost::intrusive::list_base_hook<> { |
| @@ -59,6 +63,11 @@ public: | |||
| 59 | /// Set the value of a given argument index. | 63 | /// Set the value of a given argument index. |
| 60 | void SetArg(size_t index, Value value); | 64 | void SetArg(size_t index, Value value); |
| 61 | 65 | ||
| 66 | /// Get an immutable span to the phi operands. | ||
| 67 | [[nodiscard]] std::span<const std::pair<Block*, Value>> PhiOperands() const noexcept; | ||
| 68 | /// Add phi operand to a phi instruction. | ||
| 69 | void AddPhiOperand(Block* predecessor, const Value& value); | ||
| 70 | |||
| 62 | void Invalidate(); | 71 | void Invalidate(); |
| 63 | void ClearArgs(); | 72 | void ClearArgs(); |
| 64 | 73 | ||
| @@ -76,6 +85,7 @@ private: | |||
| 76 | Inst* carry_inst{}; | 85 | Inst* carry_inst{}; |
| 77 | Inst* overflow_inst{}; | 86 | Inst* overflow_inst{}; |
| 78 | Inst* zsco_inst{}; | 87 | Inst* zsco_inst{}; |
| 88 | std::vector<std::pair<Block*, Value>> phi_operands; | ||
| 79 | u64 flags{}; | 89 | u64 flags{}; |
| 80 | }; | 90 | }; |
| 81 | 91 | ||
diff --git a/src/shader_recompiler/frontend/ir/opcode.inc b/src/shader_recompiler/frontend/ir/opcode.inc index 371064bf3..40759e96a 100644 --- a/src/shader_recompiler/frontend/ir/opcode.inc +++ b/src/shader_recompiler/frontend/ir/opcode.inc | |||
| @@ -5,6 +5,7 @@ | |||
| 5 | // opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ... | 5 | // opcode name, return type, arg1 type, arg2 type, arg3 type, arg4 type, ... |
| 6 | OPCODE(Void, Void, ) | 6 | OPCODE(Void, Void, ) |
| 7 | OPCODE(Identity, Opaque, Opaque, ) | 7 | OPCODE(Identity, Opaque, Opaque, ) |
| 8 | OPCODE(Phi, Opaque, /*todo*/ ) | ||
| 8 | 9 | ||
| 9 | // Control flow | 10 | // Control flow |
| 10 | OPCODE(Branch, Void, Label, ) | 11 | OPCODE(Branch, Void, Label, ) |
| @@ -35,6 +36,13 @@ OPCODE(SetSFlag, Void, U1, | |||
| 35 | OPCODE(SetCFlag, Void, U1, ) | 36 | OPCODE(SetCFlag, Void, U1, ) |
| 36 | OPCODE(SetOFlag, Void, U1, ) | 37 | OPCODE(SetOFlag, Void, U1, ) |
| 37 | 38 | ||
| 39 | // Undefined | ||
| 40 | OPCODE(Undef1, U1, ) | ||
| 41 | OPCODE(Undef8, U8, ) | ||
| 42 | OPCODE(Undef16, U16, ) | ||
| 43 | OPCODE(Undef32, U32, ) | ||
| 44 | OPCODE(Undef64, U64, ) | ||
| 45 | |||
| 38 | // Memory operations | 46 | // Memory operations |
| 39 | OPCODE(WriteGlobalU8, Void, U64, U32, ) | 47 | OPCODE(WriteGlobalU8, Void, U64, U32, ) |
| 40 | OPCODE(WriteGlobalS8, Void, U64, U32, ) | 48 | OPCODE(WriteGlobalS8, Void, U64, U32, ) |
diff --git a/src/shader_recompiler/frontend/ir/pred.h b/src/shader_recompiler/frontend/ir/pred.h index 37cc53006..daf23193f 100644 --- a/src/shader_recompiler/frontend/ir/pred.h +++ b/src/shader_recompiler/frontend/ir/pred.h | |||
| @@ -10,6 +10,13 @@ namespace Shader::IR { | |||
| 10 | 10 | ||
| 11 | enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT }; | 11 | enum class Pred { P0, P1, P2, P3, P4, P5, P6, PT }; |
| 12 | 12 | ||
| 13 | constexpr size_t NUM_USER_PREDS = 6; | ||
| 14 | constexpr size_t NUM_PREDS = 7; | ||
| 15 | |||
| 16 | [[nodiscard]] constexpr size_t PredIndex(Pred pred) noexcept { | ||
| 17 | return static_cast<size_t>(pred); | ||
| 18 | } | ||
| 19 | |||
| 13 | } // namespace Shader::IR | 20 | } // namespace Shader::IR |
| 14 | 21 | ||
| 15 | template <> | 22 | template <> |
diff --git a/src/shader_recompiler/frontend/ir/reg.h b/src/shader_recompiler/frontend/ir/reg.h index 316fc4be8..771094eb9 100644 --- a/src/shader_recompiler/frontend/ir/reg.h +++ b/src/shader_recompiler/frontend/ir/reg.h | |||
| @@ -271,6 +271,9 @@ enum class Reg : u64 { | |||
| 271 | }; | 271 | }; |
| 272 | static_assert(static_cast<int>(Reg::RZ) == 255); | 272 | static_assert(static_cast<int>(Reg::RZ) == 255); |
| 273 | 273 | ||
| 274 | constexpr size_t NUM_USER_REGS = 255; | ||
| 275 | constexpr size_t NUM_REGS = 256; | ||
| 276 | |||
| 274 | [[nodiscard]] constexpr Reg operator+(Reg reg, int num) { | 277 | [[nodiscard]] constexpr Reg operator+(Reg reg, int num) { |
| 275 | if (reg == Reg::RZ) { | 278 | if (reg == Reg::RZ) { |
| 276 | // Adding or subtracting registers from RZ yields RZ | 279 | // Adding or subtracting registers from RZ yields RZ |
| @@ -290,8 +293,12 @@ static_assert(static_cast<int>(Reg::RZ) == 255); | |||
| 290 | return reg + (-num); | 293 | return reg + (-num); |
| 291 | } | 294 | } |
| 292 | 295 | ||
| 296 | [[nodiscard]] constexpr size_t RegIndex(Reg reg) noexcept { | ||
| 297 | return static_cast<size_t>(reg); | ||
| 298 | } | ||
| 299 | |||
| 293 | [[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) { | 300 | [[nodiscard]] constexpr bool IsAligned(Reg reg, size_t align) { |
| 294 | return (static_cast<size_t>(reg) / align) * align == static_cast<size_t>(reg); | 301 | return (RegIndex(reg) / align) * align == RegIndex(reg); |
| 295 | } | 302 | } |
| 296 | 303 | ||
| 297 | } // namespace Shader::IR | 304 | } // namespace Shader::IR |
diff --git a/src/shader_recompiler/frontend/ir/value.cpp b/src/shader_recompiler/frontend/ir/value.cpp index 7b5b35d6c..1e974e88c 100644 --- a/src/shader_recompiler/frontend/ir/value.cpp +++ b/src/shader_recompiler/frontend/ir/value.cpp | |||
| @@ -115,6 +115,43 @@ u64 Value::U64() const { | |||
| 115 | return imm_u64; | 115 | return imm_u64; |
| 116 | } | 116 | } |
| 117 | 117 | ||
| 118 | bool Value::operator==(const Value& other) const { | ||
| 119 | if (type != other.type) { | ||
| 120 | return false; | ||
| 121 | } | ||
| 122 | switch (type) { | ||
| 123 | case Type::Void: | ||
| 124 | return true; | ||
| 125 | case Type::Opaque: | ||
| 126 | return inst == other.inst; | ||
| 127 | case Type::Label: | ||
| 128 | return label == other.label; | ||
| 129 | case Type::Reg: | ||
| 130 | return reg == other.reg; | ||
| 131 | case Type::Pred: | ||
| 132 | return pred == other.pred; | ||
| 133 | case Type::Attribute: | ||
| 134 | return attribute == other.attribute; | ||
| 135 | case Type::U1: | ||
| 136 | return imm_u1 == other.imm_u1; | ||
| 137 | case Type::U8: | ||
| 138 | return imm_u8 == other.imm_u8; | ||
| 139 | case Type::U16: | ||
| 140 | return imm_u16 == other.imm_u16; | ||
| 141 | case Type::U32: | ||
| 142 | return imm_u32 == other.imm_u32; | ||
| 143 | case Type::U64: | ||
| 144 | return imm_u64 == other.imm_u64; | ||
| 145 | case Type::ZSCO: | ||
| 146 | throw NotImplementedException("ZSCO comparison"); | ||
| 147 | } | ||
| 148 | throw LogicError("Invalid type {}", type); | ||
| 149 | } | ||
| 150 | |||
| 151 | bool Value::operator!=(const Value& other) const { | ||
| 152 | return !operator==(other); | ||
| 153 | } | ||
| 154 | |||
| 118 | void Value::ValidateAccess(IR::Type expected) const { | 155 | void Value::ValidateAccess(IR::Type expected) const { |
| 119 | if (type != expected) { | 156 | if (type != expected) { |
| 120 | throw LogicError("Reading {} out of {}", expected, type); | 157 | throw LogicError("Reading {} out of {}", expected, type); |
diff --git a/src/shader_recompiler/frontend/ir/value.h b/src/shader_recompiler/frontend/ir/value.h index 664dacf9d..368119921 100644 --- a/src/shader_recompiler/frontend/ir/value.h +++ b/src/shader_recompiler/frontend/ir/value.h | |||
| @@ -48,6 +48,9 @@ public: | |||
| 48 | [[nodiscard]] u32 U32() const; | 48 | [[nodiscard]] u32 U32() const; |
| 49 | [[nodiscard]] u64 U64() const; | 49 | [[nodiscard]] u64 U64() const; |
| 50 | 50 | ||
| 51 | [[nodiscard]] bool operator==(const Value& other) const; | ||
| 52 | [[nodiscard]] bool operator!=(const Value& other) const; | ||
| 53 | |||
| 51 | private: | 54 | private: |
| 52 | void ValidateAccess(IR::Type expected) const; | 55 | void ValidateAccess(IR::Type expected) const; |
| 53 | 56 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index fc4dba826..21ee98137 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp | |||
| @@ -36,6 +36,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) { | |||
| 36 | .cond{true}, | 36 | .cond{true}, |
| 37 | .branch_true{new_id}, | 37 | .branch_true{new_id}, |
| 38 | .branch_false{UNREACHABLE_BLOCK_ID}, | 38 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 39 | .imm_predecessors{}, | ||
| 39 | }, | 40 | }, |
| 40 | Block{ | 41 | Block{ |
| 41 | .begin{pc}, | 42 | .begin{pc}, |
| @@ -46,6 +47,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) { | |||
| 46 | .cond{block.cond}, | 47 | .cond{block.cond}, |
| 47 | .branch_true{block.branch_true}, | 48 | .branch_true{block.branch_true}, |
| 48 | .branch_false{block.branch_false}, | 49 | .branch_false{block.branch_false}, |
| 50 | .imm_predecessors{}, | ||
| 49 | }, | 51 | }, |
| 50 | }; | 52 | }; |
| 51 | } | 53 | } |
| @@ -108,7 +110,7 @@ static bool HasFlowTest(Opcode opcode) { | |||
| 108 | } | 110 | } |
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | static std::string Name(const Block& block) { | 113 | static std::string NameOf(const Block& block) { |
| 112 | if (block.begin.IsVirtual()) { | 114 | if (block.begin.IsVirtual()) { |
| 113 | return fmt::format("\"Virtual {}\"", block.id); | 115 | return fmt::format("\"Virtual {}\"", block.id); |
| 114 | } else { | 116 | } else { |
| @@ -154,13 +156,127 @@ bool Block::Contains(Location pc) const noexcept { | |||
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | Function::Function(Location start_address) | 158 | Function::Function(Location start_address) |
| 157 | : entrypoint{start_address}, labels{Label{ | 159 | : entrypoint{start_address}, labels{{ |
| 158 | .address{start_address}, | 160 | .address{start_address}, |
| 159 | .block_id{0}, | 161 | .block_id{0}, |
| 160 | .stack{}, | 162 | .stack{}, |
| 161 | }} {} | 163 | }} {} |
| 162 | 164 | ||
| 165 | void Function::BuildBlocksMap() { | ||
| 166 | const size_t num_blocks{NumBlocks()}; | ||
| 167 | blocks_map.resize(num_blocks); | ||
| 168 | for (size_t block_index = 0; block_index < num_blocks; ++block_index) { | ||
| 169 | Block& block{blocks_data[block_index]}; | ||
| 170 | blocks_map[block.id] = █ | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | void Function::BuildImmediatePredecessors() { | ||
| 175 | for (const Block& block : blocks_data) { | ||
| 176 | if (block.branch_true != UNREACHABLE_BLOCK_ID) { | ||
| 177 | blocks_map[block.branch_true]->imm_predecessors.push_back(block.id); | ||
| 178 | } | ||
| 179 | if (block.branch_false != UNREACHABLE_BLOCK_ID) { | ||
| 180 | blocks_map[block.branch_false]->imm_predecessors.push_back(block.id); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | void Function::BuildPostOrder() { | ||
| 186 | boost::container::small_vector<BlockId, 0x110> block_stack; | ||
| 187 | post_order_map.resize(NumBlocks()); | ||
| 188 | |||
| 189 | Block& first_block{blocks_data[blocks.front()]}; | ||
| 190 | first_block.post_order_visited = true; | ||
| 191 | block_stack.push_back(first_block.id); | ||
| 192 | |||
| 193 | const auto visit_branch = [&](BlockId block_id, BlockId branch_id) { | ||
| 194 | if (branch_id == UNREACHABLE_BLOCK_ID) { | ||
| 195 | return false; | ||
| 196 | } | ||
| 197 | if (blocks_map[branch_id]->post_order_visited) { | ||
| 198 | return false; | ||
| 199 | } | ||
| 200 | blocks_map[branch_id]->post_order_visited = true; | ||
| 201 | |||
| 202 | // Calling push_back twice is faster than insert on msvc | ||
| 203 | block_stack.push_back(block_id); | ||
| 204 | block_stack.push_back(branch_id); | ||
| 205 | return true; | ||
| 206 | }; | ||
| 207 | while (!block_stack.empty()) { | ||
| 208 | const Block* const block{blocks_map[block_stack.back()]}; | ||
| 209 | block_stack.pop_back(); | ||
| 210 | |||
| 211 | if (!visit_branch(block->id, block->branch_true) && | ||
| 212 | !visit_branch(block->id, block->branch_false)) { | ||
| 213 | post_order_map[block->id] = static_cast<u32>(post_order_blocks.size()); | ||
| 214 | post_order_blocks.push_back(block->id); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | void Function::BuildImmediateDominators() { | ||
| 220 | auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })}; | ||
| 221 | auto reverse_order_but_first{std::views::reverse | std::views::drop(1) | transform_block_id}; | ||
| 222 | auto has_idom{std::views::filter([](Block* block) { return block->imm_dominator; })}; | ||
| 223 | auto intersect{[this](Block* finger1, Block* finger2) { | ||
| 224 | while (finger1 != finger2) { | ||
| 225 | while (post_order_map[finger1->id] < post_order_map[finger2->id]) { | ||
| 226 | finger1 = finger1->imm_dominator; | ||
| 227 | } | ||
| 228 | while (post_order_map[finger2->id] < post_order_map[finger1->id]) { | ||
| 229 | finger2 = finger2->imm_dominator; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | return finger1; | ||
| 233 | }}; | ||
| 234 | for (Block& block : blocks_data) { | ||
| 235 | block.imm_dominator = nullptr; | ||
| 236 | } | ||
| 237 | Block* const start_block{&blocks_data[blocks.front()]}; | ||
| 238 | start_block->imm_dominator = start_block; | ||
| 239 | |||
| 240 | bool changed{true}; | ||
| 241 | while (changed) { | ||
| 242 | changed = false; | ||
| 243 | for (Block* const block : post_order_blocks | reverse_order_but_first) { | ||
| 244 | Block* new_idom{}; | ||
| 245 | for (Block* predecessor : block->imm_predecessors | transform_block_id | has_idom) { | ||
| 246 | new_idom = new_idom ? intersect(predecessor, new_idom) : predecessor; | ||
| 247 | } | ||
| 248 | changed |= block->imm_dominator != new_idom; | ||
| 249 | block->imm_dominator = new_idom; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | void Function::BuildDominanceFrontier() { | ||
| 255 | auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })}; | ||
| 256 | auto has_enough_predecessors{[](Block& block) { return block.imm_predecessors.size() >= 2; }}; | ||
| 257 | for (Block& block : blocks_data | std::views::filter(has_enough_predecessors)) { | ||
| 258 | for (Block* current : block.imm_predecessors | transform_block_id) { | ||
| 259 | while (current != block.imm_dominator) { | ||
| 260 | current->dominance_frontiers.push_back(current->id); | ||
| 261 | current = current->imm_dominator; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 163 | CFG::CFG(Environment& env_, Location start_address) : env{env_} { | 267 | CFG::CFG(Environment& env_, Location start_address) : env{env_} { |
| 268 | VisitFunctions(start_address); | ||
| 269 | |||
| 270 | for (Function& function : functions) { | ||
| 271 | function.BuildBlocksMap(); | ||
| 272 | function.BuildImmediatePredecessors(); | ||
| 273 | function.BuildPostOrder(); | ||
| 274 | function.BuildImmediateDominators(); | ||
| 275 | function.BuildDominanceFrontier(); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | void CFG::VisitFunctions(Location start_address) { | ||
| 164 | functions.emplace_back(start_address); | 280 | functions.emplace_back(start_address); |
| 165 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { | 281 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { |
| 166 | while (!functions[function_id].labels.empty()) { | 282 | while (!functions[function_id].labels.empty()) { |
| @@ -202,6 +318,7 @@ void CFG::AnalyzeLabel(FunctionId function_id, Label& label) { | |||
| 202 | .cond{true}, | 318 | .cond{true}, |
| 203 | .branch_true{UNREACHABLE_BLOCK_ID}, | 319 | .branch_true{UNREACHABLE_BLOCK_ID}, |
| 204 | .branch_false{UNREACHABLE_BLOCK_ID}, | 320 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 321 | .imm_predecessors{}, | ||
| 205 | }; | 322 | }; |
| 206 | // Analyze instructions until it reaches an already visited block or there's a branch | 323 | // Analyze instructions until it reaches an already visited block or there's a branch |
| 207 | bool is_branch{false}; | 324 | bool is_branch{false}; |
| @@ -310,7 +427,7 @@ CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Locati | |||
| 310 | // Technically CAL pushes into PRET, but that's implicit in the function call for us | 427 | // Technically CAL pushes into PRET, but that's implicit in the function call for us |
| 311 | // Insert the function into the list if it doesn't exist | 428 | // Insert the function into the list if it doesn't exist |
| 312 | if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { | 429 | if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { |
| 313 | functions.push_back(cal_pc); | 430 | functions.emplace_back(cal_pc); |
| 314 | } | 431 | } |
| 315 | // Handle CAL like a regular instruction | 432 | // Handle CAL like a regular instruction |
| 316 | break; | 433 | break; |
| @@ -352,6 +469,7 @@ void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, | |||
| 352 | .cond{cond}, | 469 | .cond{cond}, |
| 353 | .branch_true{conditional_block_id}, | 470 | .branch_true{conditional_block_id}, |
| 354 | .branch_false{UNREACHABLE_BLOCK_ID}, | 471 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 472 | .imm_predecessors{}, | ||
| 355 | })}; | 473 | })}; |
| 356 | // Set the end properties of the conditional instruction and give it a new identity | 474 | // Set the end properties of the conditional instruction and give it a new identity |
| 357 | Block& conditional_block{block}; | 475 | Block& conditional_block{block}; |
| @@ -465,14 +583,14 @@ std::string CFG::Dot() const { | |||
| 465 | dot += fmt::format("\t\tnode [style=filled];\n"); | 583 | dot += fmt::format("\t\tnode [style=filled];\n"); |
| 466 | for (const u32 block_index : function.blocks) { | 584 | for (const u32 block_index : function.blocks) { |
| 467 | const Block& block{function.blocks_data[block_index]}; | 585 | const Block& block{function.blocks_data[block_index]}; |
| 468 | const std::string name{Name(block)}; | 586 | const std::string name{NameOf(block)}; |
| 469 | const auto add_branch = [&](BlockId branch_id, bool add_label) { | 587 | const auto add_branch = [&](BlockId branch_id, bool add_label) { |
| 470 | const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; | 588 | const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; |
| 471 | dot += fmt::format("\t\t{}->", name); | 589 | dot += fmt::format("\t\t{}->", name); |
| 472 | if (it == function.blocks_data.end()) { | 590 | if (it == function.blocks_data.end()) { |
| 473 | dot += fmt::format("\"Unknown label {}\"", branch_id); | 591 | dot += fmt::format("\"Unknown label {}\"", branch_id); |
| 474 | } else { | 592 | } else { |
| 475 | dot += Name(*it); | 593 | dot += NameOf(*it); |
| 476 | }; | 594 | }; |
| 477 | if (add_label && block.cond != true && block.cond != false) { | 595 | if (add_label && block.cond != true && block.cond != false) { |
| 478 | dot += fmt::format(" [label=\"{}\"]", block.cond); | 596 | dot += fmt::format(" [label=\"{}\"]", block.cond); |
| @@ -520,7 +638,7 @@ std::string CFG::Dot() const { | |||
| 520 | if (functions.front().blocks.empty()) { | 638 | if (functions.front().blocks.empty()) { |
| 521 | dot += "Start;\n"; | 639 | dot += "Start;\n"; |
| 522 | } else { | 640 | } else { |
| 523 | dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front())); | 641 | dot += fmt::format("\tStart -> {};\n", NameOf(functions.front().blocks_data.front())); |
| 524 | } | 642 | } |
| 525 | dot += fmt::format("\tStart [shape=diamond];\n"); | 643 | dot += fmt::format("\tStart [shape=diamond];\n"); |
| 526 | } | 644 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h index b2ab0cdc3..20ada8afd 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/control_flow.h | |||
| @@ -70,6 +70,12 @@ struct Block { | |||
| 70 | IR::Condition cond; | 70 | IR::Condition cond; |
| 71 | BlockId branch_true; | 71 | BlockId branch_true; |
| 72 | BlockId branch_false; | 72 | BlockId branch_false; |
| 73 | boost::container::small_vector<BlockId, 4> imm_predecessors; | ||
| 74 | boost::container::small_vector<BlockId, 8> dominance_frontiers; | ||
| 75 | union { | ||
| 76 | bool post_order_visited{false}; | ||
| 77 | Block* imm_dominator; | ||
| 78 | }; | ||
| 73 | }; | 79 | }; |
| 74 | 80 | ||
| 75 | struct Label { | 81 | struct Label { |
| @@ -81,11 +87,30 @@ struct Label { | |||
| 81 | struct Function { | 87 | struct Function { |
| 82 | Function(Location start_address); | 88 | Function(Location start_address); |
| 83 | 89 | ||
| 90 | void BuildBlocksMap(); | ||
| 91 | |||
| 92 | void BuildImmediatePredecessors(); | ||
| 93 | |||
| 94 | void BuildPostOrder(); | ||
| 95 | |||
| 96 | void BuildImmediateDominators(); | ||
| 97 | |||
| 98 | void BuildDominanceFrontier(); | ||
| 99 | |||
| 100 | [[nodiscard]] size_t NumBlocks() const noexcept { | ||
| 101 | return static_cast<size_t>(current_block_id) + 1; | ||
| 102 | } | ||
| 103 | |||
| 84 | Location entrypoint; | 104 | Location entrypoint; |
| 85 | BlockId current_block_id{0}; | 105 | BlockId current_block_id{0}; |
| 86 | boost::container::small_vector<Label, 16> labels; | 106 | boost::container::small_vector<Label, 16> labels; |
| 87 | boost::container::small_vector<u32, 0x130> blocks; | 107 | boost::container::small_vector<u32, 0x130> blocks; |
| 88 | boost::container::small_vector<Block, 0x130> blocks_data; | 108 | boost::container::small_vector<Block, 0x130> blocks_data; |
| 109 | // Translates from BlockId to block index | ||
| 110 | boost::container::small_vector<Block*, 0x130> blocks_map; | ||
| 111 | |||
| 112 | boost::container::small_vector<u32, 0x130> post_order_blocks; | ||
| 113 | boost::container::small_vector<BlockId, 0x130> post_order_map; | ||
| 89 | }; | 114 | }; |
| 90 | 115 | ||
| 91 | class CFG { | 116 | class CFG { |
| @@ -97,6 +122,12 @@ class CFG { | |||
| 97 | public: | 122 | public: |
| 98 | explicit CFG(Environment& env, Location start_address); | 123 | explicit CFG(Environment& env, Location start_address); |
| 99 | 124 | ||
| 125 | CFG& operator=(const CFG&) = delete; | ||
| 126 | CFG(const CFG&) = delete; | ||
| 127 | |||
| 128 | CFG& operator=(CFG&&) = delete; | ||
| 129 | CFG(CFG&&) = delete; | ||
| 130 | |||
| 100 | [[nodiscard]] std::string Dot() const; | 131 | [[nodiscard]] std::string Dot() const; |
| 101 | 132 | ||
| 102 | [[nodiscard]] std::span<const Function> Functions() const noexcept { | 133 | [[nodiscard]] std::span<const Function> Functions() const noexcept { |
| @@ -104,20 +135,22 @@ public: | |||
| 104 | } | 135 | } |
| 105 | 136 | ||
| 106 | private: | 137 | private: |
| 138 | void VisitFunctions(Location start_address); | ||
| 139 | |||
| 107 | void AnalyzeLabel(FunctionId function_id, Label& label); | 140 | void AnalyzeLabel(FunctionId function_id, Label& label); |
| 108 | 141 | ||
| 109 | /// Inspect already visited blocks. | 142 | /// Inspect already visited blocks. |
| 110 | /// Return true when the block has already been visited | 143 | /// Return true when the block has already been visited |
| 111 | [[nodiscard]] bool InspectVisitedBlocks(FunctionId function_id, const Label& label); | 144 | bool InspectVisitedBlocks(FunctionId function_id, const Label& label); |
| 112 | 145 | ||
| 113 | [[nodiscard]] AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc); | 146 | AnalysisState AnalyzeInst(Block& block, FunctionId function_id, Location pc); |
| 114 | 147 | ||
| 115 | void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class, | 148 | void AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, EndClass insn_end_class, |
| 116 | IR::Condition cond); | 149 | IR::Condition cond); |
| 117 | 150 | ||
| 118 | /// Return true when the branch instruction is confirmed to be a branch | 151 | /// Return true when the branch instruction is confirmed to be a branch |
| 119 | [[nodiscard]] bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, | 152 | bool AnalyzeBranch(Block& block, FunctionId function_id, Location pc, Instruction inst, |
| 120 | Instruction inst, Opcode opcode); | 153 | Opcode opcode); |
| 121 | 154 | ||
| 122 | void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, | 155 | void AnalyzeBRA(Block& block, FunctionId function_id, Location pc, Instruction inst, |
| 123 | bool is_absolute); | 156 | bool is_absolute); |
| @@ -126,8 +159,7 @@ private: | |||
| 126 | AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst); | 159 | AnalysisState AnalyzeEXIT(Block& block, FunctionId function_id, Location pc, Instruction inst); |
| 127 | 160 | ||
| 128 | /// Return the branch target block id | 161 | /// Return the branch target block id |
| 129 | [[nodiscard]] BlockId AddLabel(const Block& block, Stack stack, Location pc, | 162 | BlockId AddLabel(const Block& block, Stack stack, Location pc, FunctionId function_id); |
| 130 | FunctionId function_id); | ||
| 131 | 163 | ||
| 132 | Environment& env; | 164 | Environment& env; |
| 133 | boost::container::small_vector<Function, 1> functions; | 165 | boost::container::small_vector<Function, 1> functions; |
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp index 67a98ba57..49d1f4bfb 100644 --- a/src/shader_recompiler/frontend/maxwell/program.cpp +++ b/src/shader_recompiler/frontend/maxwell/program.cpp | |||
| @@ -8,40 +8,53 @@ | |||
| 8 | #include "shader_recompiler/frontend/maxwell/program.h" | 8 | #include "shader_recompiler/frontend/maxwell/program.h" |
| 9 | #include "shader_recompiler/frontend/maxwell/termination_code.h" | 9 | #include "shader_recompiler/frontend/maxwell/termination_code.h" |
| 10 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 10 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| 11 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 11 | 12 | ||
| 12 | namespace Shader::Maxwell { | 13 | namespace Shader::Maxwell { |
| 14 | namespace { | ||
| 15 | void TranslateCode(Environment& env, const Flow::Function& cfg_function, IR::Function& function, | ||
| 16 | std::span<IR::Block*> block_map, IR::Block* block_memory) { | ||
| 17 | const size_t num_blocks{cfg_function.blocks.size()}; | ||
| 18 | function.blocks.reserve(num_blocks); | ||
| 13 | 19 | ||
| 14 | Program::Function::~Function() { | 20 | for (const Flow::BlockId block_id : cfg_function.blocks) { |
| 15 | std::ranges::for_each(blocks, &std::destroy_at<IR::Block>); | 21 | const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; |
| 16 | } | ||
| 17 | |||
| 18 | Program::Program(Environment& env, const Flow::CFG& cfg) { | ||
| 19 | std::vector<IR::Block*> block_map; | ||
| 20 | functions.reserve(cfg.Functions().size()); | ||
| 21 | 22 | ||
| 22 | for (const Flow::Function& cfg_function : cfg.Functions()) { | 23 | function.blocks.emplace_back(std::construct_at(block_memory, Translate(env, flow_block))); |
| 23 | Function& function{functions.emplace_back()}; | 24 | block_map[flow_block.id] = function.blocks.back().get(); |
| 25 | ++block_memory; | ||
| 26 | } | ||
| 27 | } | ||
| 24 | 28 | ||
| 25 | const size_t num_blocks{cfg_function.blocks.size()}; | 29 | void EmitTerminationInsts(const Flow::Function& cfg_function, |
| 26 | IR::Block* block_memory{block_alloc_pool.allocate(num_blocks)}; | 30 | std::span<IR::Block* const> block_map) { |
| 27 | function.blocks.reserve(num_blocks); | 31 | for (const Flow::BlockId block_id : cfg_function.blocks) { |
| 32 | const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; | ||
| 33 | EmitTerminationCode(flow_block, block_map); | ||
| 34 | } | ||
| 35 | } | ||
| 28 | 36 | ||
| 29 | block_map.resize(cfg_function.blocks_data.size()); | 37 | void TranslateFunction(Environment& env, const Flow::Function& cfg_function, IR::Function& function, |
| 38 | IR::Block* block_memory) { | ||
| 39 | std::vector<IR::Block*> block_map; | ||
| 40 | block_map.resize(cfg_function.blocks_data.size()); | ||
| 30 | 41 | ||
| 31 | // Visit the instructions of all blocks | 42 | TranslateCode(env, cfg_function, function, block_map, block_memory); |
| 32 | for (const Flow::BlockId block_id : cfg_function.blocks) { | 43 | EmitTerminationInsts(cfg_function, block_map); |
| 33 | const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; | 44 | } |
| 45 | } // Anonymous namespace | ||
| 34 | 46 | ||
| 35 | IR::Block* const block{std::construct_at(block_memory, Translate(env, flow_block))}; | 47 | Program::Program(Environment& env, const Flow::CFG& cfg) { |
| 36 | ++block_memory; | 48 | functions.reserve(cfg.Functions().size()); |
| 37 | function.blocks.push_back(block); | 49 | for (const Flow::Function& cfg_function : cfg.Functions()) { |
| 38 | block_map[flow_block.id] = block; | 50 | TranslateFunction(env, cfg_function, functions.emplace_back(), |
| 39 | } | 51 | block_alloc_pool.allocate(cfg_function.blocks.size())); |
| 40 | // Now that all blocks are defined, emit the termination instructions | 52 | } |
| 41 | for (const Flow::BlockId block_id : cfg_function.blocks) { | 53 | std::ranges::for_each(functions, Optimization::SsaRewritePass); |
| 42 | const Flow::Block& flow_block{cfg_function.blocks_data[block_id]}; | 54 | for (IR::Function& function : functions) { |
| 43 | EmitTerminationCode(flow_block, block_map); | 55 | Optimization::Invoke(Optimization::DeadCodeEliminationPass, function); |
| 44 | } | 56 | Optimization::Invoke(Optimization::IdentityRemovalPass, function); |
| 57 | // Optimization::Invoke(Optimization::VerificationPass, function); | ||
| 45 | } | 58 | } |
| 46 | } | 59 | } |
| 47 | 60 | ||
| @@ -50,16 +63,16 @@ std::string DumpProgram(const Program& program) { | |||
| 50 | std::map<const IR::Inst*, size_t> inst_to_index; | 63 | std::map<const IR::Inst*, size_t> inst_to_index; |
| 51 | std::map<const IR::Block*, size_t> block_to_index; | 64 | std::map<const IR::Block*, size_t> block_to_index; |
| 52 | 65 | ||
| 53 | for (const Program::Function& function : program.functions) { | 66 | for (const IR::Function& function : program.functions) { |
| 54 | for (const IR::Block* const block : function.blocks) { | 67 | for (const auto& block : function.blocks) { |
| 55 | block_to_index.emplace(block, index); | 68 | block_to_index.emplace(block.get(), index); |
| 56 | ++index; | 69 | ++index; |
| 57 | } | 70 | } |
| 58 | } | 71 | } |
| 59 | std::string ret; | 72 | std::string ret; |
| 60 | for (const Program::Function& function : program.functions) { | 73 | for (const IR::Function& function : program.functions) { |
| 61 | ret += fmt::format("Function\n"); | 74 | ret += fmt::format("Function\n"); |
| 62 | for (const IR::Block* const block : function.blocks) { | 75 | for (const auto& block : function.blocks) { |
| 63 | ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; | 76 | ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; |
| 64 | } | 77 | } |
| 65 | } | 78 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/program.h b/src/shader_recompiler/frontend/maxwell/program.h index 7814b2c01..36e678a9e 100644 --- a/src/shader_recompiler/frontend/maxwell/program.h +++ b/src/shader_recompiler/frontend/maxwell/program.h | |||
| @@ -4,13 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <memory> | ||
| 7 | #include <string> | 8 | #include <string> |
| 8 | #include <vector> | 9 | #include <vector> |
| 9 | 10 | ||
| 11 | #include <boost/container/small_vector.hpp> | ||
| 10 | #include <boost/pool/pool_alloc.hpp> | 12 | #include <boost/pool/pool_alloc.hpp> |
| 11 | 13 | ||
| 12 | #include "shader_recompiler/environment.h" | 14 | #include "shader_recompiler/environment.h" |
| 13 | #include "shader_recompiler/frontend/ir/basic_block.h" | 15 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 16 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 14 | #include "shader_recompiler/frontend/maxwell/control_flow.h" | 17 | #include "shader_recompiler/frontend/maxwell/control_flow.h" |
| 15 | 18 | ||
| 16 | namespace Shader::Maxwell { | 19 | namespace Shader::Maxwell { |
| @@ -22,16 +25,10 @@ public: | |||
| 22 | explicit Program(Environment& env, const Flow::CFG& cfg); | 25 | explicit Program(Environment& env, const Flow::CFG& cfg); |
| 23 | 26 | ||
| 24 | private: | 27 | private: |
| 25 | struct Function { | ||
| 26 | ~Function(); | ||
| 27 | |||
| 28 | std::vector<IR::Block*> blocks; | ||
| 29 | }; | ||
| 30 | |||
| 31 | boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete, | 28 | boost::pool_allocator<IR::Block, boost::default_user_allocator_new_delete, |
| 32 | boost::details::pool::null_mutex> | 29 | boost::details::pool::null_mutex> |
| 33 | block_alloc_pool; | 30 | block_alloc_pool; |
| 34 | std::vector<Function> functions; | 31 | boost::container::small_vector<IR::Function, 1> functions; |
| 35 | }; | 32 | }; |
| 36 | 33 | ||
| 37 | [[nodiscard]] std::string DumpProgram(const Program& program); | 34 | [[nodiscard]] std::string DumpProgram(const Program& program); |
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.cpp b/src/shader_recompiler/frontend/maxwell/termination_code.cpp index a4ea5c5e3..ed5137f20 100644 --- a/src/shader_recompiler/frontend/maxwell/termination_code.cpp +++ b/src/shader_recompiler/frontend/maxwell/termination_code.cpp | |||
| @@ -47,12 +47,19 @@ static IR::U1 GetCond(IR::Condition cond, IR::IREmitter& ir) { | |||
| 47 | 47 | ||
| 48 | static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map, | 48 | static void EmitBranch(const Flow::Block& flow_block, std::span<IR::Block* const> block_map, |
| 49 | IR::IREmitter& ir) { | 49 | IR::IREmitter& ir) { |
| 50 | const auto add_immediate_predecessor = [&](Flow::BlockId label) { | ||
| 51 | block_map[label]->AddImmediatePredecessor(&ir.block); | ||
| 52 | }; | ||
| 50 | if (flow_block.cond == true) { | 53 | if (flow_block.cond == true) { |
| 54 | add_immediate_predecessor(flow_block.branch_true); | ||
| 51 | return ir.Branch(block_map[flow_block.branch_true]); | 55 | return ir.Branch(block_map[flow_block.branch_true]); |
| 52 | } | 56 | } |
| 53 | if (flow_block.cond == false) { | 57 | if (flow_block.cond == false) { |
| 58 | add_immediate_predecessor(flow_block.branch_false); | ||
| 54 | return ir.Branch(block_map[flow_block.branch_false]); | 59 | return ir.Branch(block_map[flow_block.branch_false]); |
| 55 | } | 60 | } |
| 61 | add_immediate_predecessor(flow_block.branch_true); | ||
| 62 | add_immediate_predecessor(flow_block.branch_false); | ||
| 56 | return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true], | 63 | return ir.BranchConditional(GetCond(flow_block.cond, ir), block_map[flow_block.branch_true], |
| 57 | block_map[flow_block.branch_false]); | 64 | block_map[flow_block.branch_false]); |
| 58 | } | 65 | } |
diff --git a/src/shader_recompiler/frontend/maxwell/termination_code.h b/src/shader_recompiler/frontend/maxwell/termination_code.h index b0d667942..04e044534 100644 --- a/src/shader_recompiler/frontend/maxwell/termination_code.h +++ b/src/shader_recompiler/frontend/maxwell/termination_code.h | |||
| @@ -11,6 +11,7 @@ | |||
| 11 | 11 | ||
| 12 | namespace Shader::Maxwell { | 12 | namespace Shader::Maxwell { |
| 13 | 13 | ||
| 14 | /// Emit termination instructions and collect immediate predecessors | ||
| 14 | void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map); | 15 | void EmitTerminationCode(const Flow::Block& flow_block, std::span<IR::Block* const> block_map); |
| 15 | 16 | ||
| 16 | } // namespace Shader::Maxwell | 17 | } // namespace Shader::Maxwell |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h index bc607b002..8be7d6ff1 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h | |||
| @@ -208,7 +208,7 @@ public: | |||
| 208 | void P2R_reg(u64 insn); | 208 | void P2R_reg(u64 insn); |
| 209 | void P2R_cbuf(u64 insn); | 209 | void P2R_cbuf(u64 insn); |
| 210 | void P2R_imm(u64 insn); | 210 | void P2R_imm(u64 insn); |
| 211 | void PBK(u64 insn); | 211 | void PBK(); |
| 212 | void PCNT(u64 insn); | 212 | void PCNT(u64 insn); |
| 213 | void PEXIT(u64 insn); | 213 | void PEXIT(u64 insn); |
| 214 | void PIXLD(u64 insn); | 214 | void PIXLD(u64 insn); |
| @@ -252,7 +252,7 @@ public: | |||
| 252 | void SHR_reg(u64 insn); | 252 | void SHR_reg(u64 insn); |
| 253 | void SHR_cbuf(u64 insn); | 253 | void SHR_cbuf(u64 insn); |
| 254 | void SHR_imm(u64 insn); | 254 | void SHR_imm(u64 insn); |
| 255 | void SSY(u64 insn); | 255 | void SSY(); |
| 256 | void ST(u64 insn); | 256 | void ST(u64 insn); |
| 257 | void STG(u64 insn); | 257 | void STG(u64 insn); |
| 258 | void STL(u64 insn); | 258 | void STL(u64 insn); |
diff --git a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp index c907c1ffb..0f52696d1 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp | |||
| @@ -762,7 +762,7 @@ void TranslatorVisitor::P2R_imm(u64) { | |||
| 762 | ThrowNotImplemented(Opcode::P2R_imm); | 762 | ThrowNotImplemented(Opcode::P2R_imm); |
| 763 | } | 763 | } |
| 764 | 764 | ||
| 765 | void TranslatorVisitor::PBK(u64) { | 765 | void TranslatorVisitor::PBK() { |
| 766 | // PBK is a no-op | 766 | // PBK is a no-op |
| 767 | } | 767 | } |
| 768 | 768 | ||
| @@ -938,8 +938,8 @@ void TranslatorVisitor::SHR_imm(u64) { | |||
| 938 | ThrowNotImplemented(Opcode::SHR_imm); | 938 | ThrowNotImplemented(Opcode::SHR_imm); |
| 939 | } | 939 | } |
| 940 | 940 | ||
| 941 | void TranslatorVisitor::SSY(u64) { | 941 | void TranslatorVisitor::SSY() { |
| 942 | ThrowNotImplemented(Opcode::SSY); | 942 | // SSY is a no-op |
| 943 | } | 943 | } |
| 944 | 944 | ||
| 945 | void TranslatorVisitor::ST(u64) { | 945 | void TranslatorVisitor::ST(u64) { |
diff --git a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp index f9bb063fb..7f8500087 100644 --- a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp +++ b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp | |||
| @@ -28,7 +28,6 @@ void IdentityRemovalPass(IR::Block& block) { | |||
| 28 | ++inst; | 28 | ++inst; |
| 29 | } | 29 | } |
| 30 | } | 30 | } |
| 31 | |||
| 32 | for (IR::Inst* const inst : to_invalidate) { | 31 | for (IR::Inst* const inst : to_invalidate) { |
| 33 | inst->Invalidate(); | 32 | inst->Invalidate(); |
| 34 | } | 33 | } |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index fe5454e9a..83f094d73 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -5,12 +5,21 @@ | |||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | 7 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 8 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 8 | 9 | ||
| 9 | namespace Shader::Optimization { | 10 | namespace Shader::Optimization { |
| 10 | 11 | ||
| 12 | template <typename Func> | ||
| 13 | void Invoke(Func&& func, IR::Function& function) { | ||
| 14 | for (const auto& block : function.blocks) { | ||
| 15 | func(*block); | ||
| 16 | } | ||
| 17 | } | ||
| 18 | |||
| 11 | void DeadCodeEliminationPass(IR::Block& block); | 19 | void DeadCodeEliminationPass(IR::Block& block); |
| 12 | void GetSetElimination(IR::Block& block); | 20 | void GetSetElimination(IR::Block& block); |
| 13 | void IdentityRemovalPass(IR::Block& block); | 21 | void IdentityRemovalPass(IR::Block& block); |
| 22 | void SsaRewritePass(IR::Function& function); | ||
| 14 | void VerificationPass(const IR::Block& block); | 23 | void VerificationPass(const IR::Block& block); |
| 15 | 24 | ||
| 16 | } // namespace Shader::Optimization | 25 | } // namespace Shader::Optimization |
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp new file mode 100644 index 000000000..a4b256a40 --- /dev/null +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -0,0 +1,155 @@ | |||
| 1 | // Copyright 2021 yuzu Emulator Project | ||
| 2 | // Licensed under GPLv2 or any later version | ||
| 3 | // Refer to the license.txt file included. | ||
| 4 | |||
| 5 | // This file implements the SSA rewriting algorithm proposed in | ||
| 6 | // | ||
| 7 | // Simple and Efficient Construction of Static Single Assignment Form. | ||
| 8 | // Braun M., Buchwald S., Hack S., Leißa R., Mallon C., Zwinkau A. (2013) | ||
| 9 | // In: Jhala R., De Bosschere K. (eds) | ||
| 10 | // Compiler Construction. CC 2013. | ||
| 11 | // Lecture Notes in Computer Science, vol 7791. | ||
| 12 | // Springer, Berlin, Heidelberg | ||
| 13 | // | ||
| 14 | // https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 | ||
| 15 | // | ||
| 16 | |||
| 17 | #include <map> | ||
| 18 | |||
| 19 | #include <boost/container/flat_map.hpp> | ||
| 20 | |||
| 21 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 22 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 23 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 24 | #include "shader_recompiler/frontend/ir/opcode.h" | ||
| 25 | #include "shader_recompiler/frontend/ir/pred.h" | ||
| 26 | #include "shader_recompiler/frontend/ir/reg.h" | ||
| 27 | #include "shader_recompiler/ir_opt/passes.h" | ||
| 28 | |||
| 29 | namespace Shader::Optimization { | ||
| 30 | namespace { | ||
| 31 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | ||
| 32 | |||
| 33 | struct DefTable { | ||
| 34 | [[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept { | ||
| 35 | return regs[IR::RegIndex(variable)]; | ||
| 36 | } | ||
| 37 | |||
| 38 | [[nodiscard]] ValueMap& operator[](IR::Pred variable) noexcept { | ||
| 39 | return preds[IR::PredIndex(variable)]; | ||
| 40 | } | ||
| 41 | |||
| 42 | std::array<ValueMap, IR::NUM_USER_REGS> regs; | ||
| 43 | std::array<ValueMap, IR::NUM_USER_PREDS> preds; | ||
| 44 | }; | ||
| 45 | |||
| 46 | IR::Opcode UndefOpcode(IR::Reg) noexcept { | ||
| 47 | return IR::Opcode::Undef32; | ||
| 48 | } | ||
| 49 | |||
| 50 | IR::Opcode UndefOpcode(IR::Pred) noexcept { | ||
| 51 | return IR::Opcode::Undef1; | ||
| 52 | } | ||
| 53 | |||
| 54 | [[nodiscard]] bool IsPhi(const IR::Inst& inst) noexcept { | ||
| 55 | return inst.Opcode() == IR::Opcode::Phi; | ||
| 56 | } | ||
| 57 | |||
| 58 | class Pass { | ||
| 59 | public: | ||
| 60 | void WriteVariable(auto variable, IR::Block* block, const IR::Value& value) { | ||
| 61 | current_def[variable].insert_or_assign(block, value); | ||
| 62 | } | ||
| 63 | |||
| 64 | IR::Value ReadVariable(auto variable, IR::Block* block) { | ||
| 65 | auto& def{current_def[variable]}; | ||
| 66 | if (const auto it{def.find(block)}; it != def.end()) { | ||
| 67 | return it->second; | ||
| 68 | } | ||
| 69 | return ReadVariableRecursive(variable, block); | ||
| 70 | } | ||
| 71 | |||
| 72 | private: | ||
| 73 | IR::Value ReadVariableRecursive(auto variable, IR::Block* block) { | ||
| 74 | IR::Value val; | ||
| 75 | if (const std::span preds{block->ImmediatePredecessors()}; preds.size() == 1) { | ||
| 76 | val = ReadVariable(variable, preds.front()); | ||
| 77 | } else { | ||
| 78 | // Break potential cycles with operandless phi | ||
| 79 | val = IR::Value{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 80 | WriteVariable(variable, block, val); | ||
| 81 | val = AddPhiOperands(variable, val, block); | ||
| 82 | } | ||
| 83 | WriteVariable(variable, block, val); | ||
| 84 | return val; | ||
| 85 | } | ||
| 86 | |||
| 87 | IR::Value AddPhiOperands(auto variable, const IR::Value& phi, IR::Block* block) { | ||
| 88 | for (IR::Block* const pred : block->ImmediatePredecessors()) { | ||
| 89 | phi.Inst()->AddPhiOperand(pred, ReadVariable(variable, pred)); | ||
| 90 | } | ||
| 91 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); | ||
| 92 | } | ||
| 93 | |||
| 94 | IR::Value TryRemoveTrivialPhi(const IR::Value& phi, IR::Block* block, IR::Opcode undef_opcode) { | ||
| 95 | IR::Value same; | ||
| 96 | for (const auto& pair : phi.Inst()->PhiOperands()) { | ||
| 97 | const IR::Value& op{pair.second}; | ||
| 98 | if (op == same || op == phi) { | ||
| 99 | // Unique value or self-reference | ||
| 100 | continue; | ||
| 101 | } | ||
| 102 | if (!same.IsEmpty()) { | ||
| 103 | // The phi merges at least two values: not trivial | ||
| 104 | return phi; | ||
| 105 | } | ||
| 106 | same = op; | ||
| 107 | } | ||
| 108 | if (same.IsEmpty()) { | ||
| 109 | // The phi is unreachable or in the start block | ||
| 110 | const auto first_not_phi{std::ranges::find_if_not(block->Instructions(), IsPhi)}; | ||
| 111 | same = IR::Value{&*block->PrependNewInst(first_not_phi, undef_opcode)}; | ||
| 112 | } | ||
| 113 | // Reroute all uses of phi to same and remove phi | ||
| 114 | phi.Inst()->ReplaceUsesWith(same); | ||
| 115 | // TODO: Try to recursively remove all phi users, which might have become trivial | ||
| 116 | return same; | ||
| 117 | } | ||
| 118 | |||
| 119 | DefTable current_def; | ||
| 120 | }; | ||
| 121 | } // Anonymous namespace | ||
| 122 | |||
| 123 | void SsaRewritePass(IR::Function& function) { | ||
| 124 | Pass pass; | ||
| 125 | for (const auto& block : function.blocks) { | ||
| 126 | for (IR::Inst& inst : block->Instructions()) { | ||
| 127 | switch (inst.Opcode()) { | ||
| 128 | case IR::Opcode::SetRegister: | ||
| 129 | if (const IR::Reg reg{inst.Arg(0).Reg()}; reg != IR::Reg::RZ) { | ||
| 130 | pass.WriteVariable(reg, block.get(), inst.Arg(1)); | ||
| 131 | } | ||
| 132 | break; | ||
| 133 | case IR::Opcode::SetPred: | ||
| 134 | if (const IR::Pred pred{inst.Arg(0).Pred()}; pred != IR::Pred::PT) { | ||
| 135 | pass.WriteVariable(pred, block.get(), inst.Arg(1)); | ||
| 136 | } | ||
| 137 | break; | ||
| 138 | case IR::Opcode::GetRegister: | ||
| 139 | if (const IR::Reg reg{inst.Arg(0).Reg()}; reg != IR::Reg::RZ) { | ||
| 140 | inst.ReplaceUsesWith(pass.ReadVariable(reg, block.get())); | ||
| 141 | } | ||
| 142 | break; | ||
| 143 | case IR::Opcode::GetPred: | ||
| 144 | if (const IR::Pred pred{inst.Arg(0).Pred()}; pred != IR::Pred::PT) { | ||
| 145 | inst.ReplaceUsesWith(pass.ReadVariable(pred, block.get())); | ||
| 146 | } | ||
| 147 | break; | ||
| 148 | default: | ||
| 149 | break; | ||
| 150 | } | ||
| 151 | } | ||
| 152 | } | ||
| 153 | } | ||
| 154 | |||
| 155 | } // namespace Shader::Optimization | ||
diff --git a/src/shader_recompiler/main.cpp b/src/shader_recompiler/main.cpp index 39f0bf333..e3c9ad6e8 100644 --- a/src/shader_recompiler/main.cpp +++ b/src/shader_recompiler/main.cpp | |||
| @@ -35,12 +35,12 @@ void RunDatabase() { | |||
| 35 | ForEachFile("D:\\Shaders\\Database", [&](const std::filesystem::path& path) { | 35 | ForEachFile("D:\\Shaders\\Database", [&](const std::filesystem::path& path) { |
| 36 | map.emplace_back(std::make_unique<FileEnvironment>(path.string().c_str())); | 36 | map.emplace_back(std::make_unique<FileEnvironment>(path.string().c_str())); |
| 37 | }); | 37 | }); |
| 38 | for (int i = 0; i < 1; ++i) { | 38 | for (int i = 0; i < 300; ++i) { |
| 39 | for (auto& env : map) { | 39 | for (auto& env : map) { |
| 40 | // fmt::print(stdout, "Decoding {}\n", path.string()); | 40 | // fmt::print(stdout, "Decoding {}\n", path.string()); |
| 41 | const Location start_address{0}; | 41 | const Location start_address{0}; |
| 42 | auto cfg{std::make_unique<Flow::CFG>(*env, start_address)}; | 42 | auto cfg{std::make_unique<Flow::CFG>(*env, start_address)}; |
| 43 | // fmt::print(stdout, "{}\n", cfg.Dot()); | 43 | // fmt::print(stdout, "{}\n", cfg->Dot()); |
| 44 | // IR::Program program{env, cfg}; | 44 | // IR::Program program{env, cfg}; |
| 45 | // Optimize(program); | 45 | // Optimize(program); |
| 46 | // const std::string code{EmitGLASM(program)}; | 46 | // const std::string code{EmitGLASM(program)}; |