diff options
| author | 2021-02-14 20:15:42 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:22 -0400 | |
| commit | cbfb7d182a4e90e4e263696d1fca35e47d3eabb4 (patch) | |
| tree | a8d384aa0daefdfafd9b61330e06b1cf7ac40ea6 /src/shader_recompiler | |
| parent | shader: Misc fixes (diff) | |
| download | yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.tar.gz yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.tar.xz yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.zip | |
shader: Support SSA loops on IR
Diffstat (limited to 'src/shader_recompiler')
| -rw-r--r-- | src/shader_recompiler/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv.h | 12 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | 8 | ||||
| -rw-r--r-- | src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp | 10 | ||||
| -rw-r--r-- | src/shader_recompiler/frontend/ir/function.h | 1 | ||||
| -rw-r--r-- | src/shader_recompiler/frontend/ir/post_order.cpp | 48 | ||||
| -rw-r--r-- | src/shader_recompiler/frontend/ir/post_order.h | 13 | ||||
| -rw-r--r-- | src/shader_recompiler/frontend/maxwell/program.cpp | 12 | ||||
| -rw-r--r-- | src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | 2 | ||||
| -rw-r--r-- | src/shader_recompiler/ir_opt/passes.h | 8 | ||||
| -rw-r--r-- | src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | 62 | ||||
| -rw-r--r-- | src/shader_recompiler/main.cpp | 18 |
12 files changed, 150 insertions, 46 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 27fc79e21..e1f4276a1 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -32,6 +32,8 @@ add_executable(shader_recompiler | |||
| 32 | frontend/ir/opcodes.cpp | 32 | frontend/ir/opcodes.cpp |
| 33 | frontend/ir/opcodes.h | 33 | frontend/ir/opcodes.h |
| 34 | frontend/ir/opcodes.inc | 34 | frontend/ir/opcodes.inc |
| 35 | frontend/ir/post_order.cpp | ||
| 36 | frontend/ir/post_order.h | ||
| 35 | frontend/ir/pred.h | 37 | frontend/ir/pred.h |
| 36 | frontend/ir/program.cpp | 38 | frontend/ir/program.cpp |
| 37 | frontend/ir/program.h | 39 | frontend/ir/program.h |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.h b/src/shader_recompiler/backend/spirv/emit_spirv.h index 46ec7a1bb..6b09757d1 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.h +++ b/src/shader_recompiler/backend/spirv/emit_spirv.h | |||
| @@ -159,10 +159,10 @@ private: | |||
| 159 | Id EmitWorkgroupId(EmitContext& ctx); | 159 | Id EmitWorkgroupId(EmitContext& ctx); |
| 160 | Id EmitLocalInvocationId(EmitContext& ctx); | 160 | Id EmitLocalInvocationId(EmitContext& ctx); |
| 161 | Id EmitUndefU1(EmitContext& ctx); | 161 | Id EmitUndefU1(EmitContext& ctx); |
| 162 | void EmitUndefU8(EmitContext& ctx); | 162 | Id EmitUndefU8(EmitContext& ctx); |
| 163 | void EmitUndefU16(EmitContext& ctx); | 163 | Id EmitUndefU16(EmitContext& ctx); |
| 164 | void EmitUndefU32(EmitContext& ctx); | 164 | Id EmitUndefU32(EmitContext& ctx); |
| 165 | void EmitUndefU64(EmitContext& ctx); | 165 | Id EmitUndefU64(EmitContext& ctx); |
| 166 | void EmitLoadGlobalU8(EmitContext& ctx); | 166 | void EmitLoadGlobalU8(EmitContext& ctx); |
| 167 | void EmitLoadGlobalS8(EmitContext& ctx); | 167 | void EmitLoadGlobalS8(EmitContext& ctx); |
| 168 | void EmitLoadGlobalU16(EmitContext& ctx); | 168 | void EmitLoadGlobalU16(EmitContext& ctx); |
| @@ -297,12 +297,12 @@ private: | |||
| 297 | void EmitBitFieldInsert(EmitContext& ctx); | 297 | void EmitBitFieldInsert(EmitContext& ctx); |
| 298 | void EmitBitFieldSExtract(EmitContext& ctx); | 298 | void EmitBitFieldSExtract(EmitContext& ctx); |
| 299 | Id EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count); | 299 | Id EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id count); |
| 300 | void EmitSLessThan(EmitContext& ctx); | 300 | Id EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs); |
| 301 | void EmitULessThan(EmitContext& ctx); | 301 | void EmitULessThan(EmitContext& ctx); |
| 302 | void EmitIEqual(EmitContext& ctx); | 302 | void EmitIEqual(EmitContext& ctx); |
| 303 | void EmitSLessThanEqual(EmitContext& ctx); | 303 | void EmitSLessThanEqual(EmitContext& ctx); |
| 304 | void EmitULessThanEqual(EmitContext& ctx); | 304 | void EmitULessThanEqual(EmitContext& ctx); |
| 305 | void EmitSGreaterThan(EmitContext& ctx); | 305 | Id EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs); |
| 306 | void EmitUGreaterThan(EmitContext& ctx); | 306 | void EmitUGreaterThan(EmitContext& ctx); |
| 307 | void EmitINotEqual(EmitContext& ctx); | 307 | void EmitINotEqual(EmitContext& ctx); |
| 308 | void EmitSGreaterThanEqual(EmitContext& ctx); | 308 | void EmitSGreaterThanEqual(EmitContext& ctx); |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp index 3ef4f3d78..e811a63ab 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_integer.cpp | |||
| @@ -73,8 +73,8 @@ Id EmitSPIRV::EmitBitFieldUExtract(EmitContext& ctx, Id base, Id offset, Id coun | |||
| 73 | return ctx.OpBitFieldUExtract(ctx.u32[1], base, offset, count); | 73 | return ctx.OpBitFieldUExtract(ctx.u32[1], base, offset, count); |
| 74 | } | 74 | } |
| 75 | 75 | ||
| 76 | void EmitSPIRV::EmitSLessThan(EmitContext&) { | 76 | Id EmitSPIRV::EmitSLessThan(EmitContext& ctx, Id lhs, Id rhs) { |
| 77 | throw NotImplementedException("SPIR-V Instruction"); | 77 | return ctx.OpSLessThan(ctx.u1, lhs, rhs); |
| 78 | } | 78 | } |
| 79 | 79 | ||
| 80 | void EmitSPIRV::EmitULessThan(EmitContext&) { | 80 | void EmitSPIRV::EmitULessThan(EmitContext&) { |
| @@ -93,8 +93,8 @@ void EmitSPIRV::EmitULessThanEqual(EmitContext&) { | |||
| 93 | throw NotImplementedException("SPIR-V Instruction"); | 93 | throw NotImplementedException("SPIR-V Instruction"); |
| 94 | } | 94 | } |
| 95 | 95 | ||
| 96 | void EmitSPIRV::EmitSGreaterThan(EmitContext&) { | 96 | Id EmitSPIRV::EmitSGreaterThan(EmitContext& ctx, Id lhs, Id rhs) { |
| 97 | throw NotImplementedException("SPIR-V Instruction"); | 97 | return ctx.OpSGreaterThan(ctx.u1, lhs, rhs); |
| 98 | } | 98 | } |
| 99 | 99 | ||
| 100 | void EmitSPIRV::EmitUGreaterThan(EmitContext&) { | 100 | void EmitSPIRV::EmitUGreaterThan(EmitContext&) { |
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp index 859b60a95..a6f542360 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv_undefined.cpp | |||
| @@ -10,19 +10,19 @@ Id EmitSPIRV::EmitUndefU1(EmitContext& ctx) { | |||
| 10 | return ctx.OpUndef(ctx.u1); | 10 | return ctx.OpUndef(ctx.u1); |
| 11 | } | 11 | } |
| 12 | 12 | ||
| 13 | void EmitSPIRV::EmitUndefU8(EmitContext&) { | 13 | Id EmitSPIRV::EmitUndefU8(EmitContext&) { |
| 14 | throw NotImplementedException("SPIR-V Instruction"); | 14 | throw NotImplementedException("SPIR-V Instruction"); |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | void EmitSPIRV::EmitUndefU16(EmitContext&) { | 17 | Id EmitSPIRV::EmitUndefU16(EmitContext&) { |
| 18 | throw NotImplementedException("SPIR-V Instruction"); | 18 | throw NotImplementedException("SPIR-V Instruction"); |
| 19 | } | 19 | } |
| 20 | 20 | ||
| 21 | void EmitSPIRV::EmitUndefU32(EmitContext&) { | 21 | Id EmitSPIRV::EmitUndefU32(EmitContext& ctx) { |
| 22 | throw NotImplementedException("SPIR-V Instruction"); | 22 | return ctx.OpUndef(ctx.u32[1]); |
| 23 | } | 23 | } |
| 24 | 24 | ||
| 25 | void EmitSPIRV::EmitUndefU64(EmitContext&) { | 25 | Id EmitSPIRV::EmitUndefU64(EmitContext&) { |
| 26 | throw NotImplementedException("SPIR-V Instruction"); | 26 | throw NotImplementedException("SPIR-V Instruction"); |
| 27 | } | 27 | } |
| 28 | 28 | ||
diff --git a/src/shader_recompiler/frontend/ir/function.h b/src/shader_recompiler/frontend/ir/function.h index fd7d56419..d1f061146 100644 --- a/src/shader_recompiler/frontend/ir/function.h +++ b/src/shader_recompiler/frontend/ir/function.h | |||
| @@ -12,6 +12,7 @@ namespace Shader::IR { | |||
| 12 | 12 | ||
| 13 | struct Function { | 13 | struct Function { |
| 14 | BlockList blocks; | 14 | BlockList blocks; |
| 15 | BlockList post_order_blocks; | ||
| 15 | }; | 16 | }; |
| 16 | 17 | ||
| 17 | } // namespace Shader::IR | 18 | } // namespace Shader::IR |
diff --git a/src/shader_recompiler/frontend/ir/post_order.cpp b/src/shader_recompiler/frontend/ir/post_order.cpp new file mode 100644 index 000000000..a48b8dec5 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/post_order.cpp | |||
| @@ -0,0 +1,48 @@ | |||
| 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 <boost/container/flat_set.hpp> | ||
| 6 | #include <boost/container/small_vector.hpp> | ||
| 7 | |||
| 8 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 9 | #include "shader_recompiler/frontend/ir/post_order.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | BlockList PostOrder(const BlockList& blocks) { | ||
| 14 | boost::container::small_vector<Block*, 16> block_stack; | ||
| 15 | boost::container::flat_set<Block*> visited; | ||
| 16 | |||
| 17 | BlockList post_order_blocks; | ||
| 18 | post_order_blocks.reserve(blocks.size()); | ||
| 19 | |||
| 20 | Block* const first_block{blocks.front()}; | ||
| 21 | visited.insert(first_block); | ||
| 22 | block_stack.push_back(first_block); | ||
| 23 | |||
| 24 | const auto visit_branch = [&](Block* block, Block* branch) { | ||
| 25 | if (!branch) { | ||
| 26 | return false; | ||
| 27 | } | ||
| 28 | if (!visited.insert(branch).second) { | ||
| 29 | return false; | ||
| 30 | } | ||
| 31 | // Calling push_back twice is faster than insert on msvc | ||
| 32 | block_stack.push_back(block); | ||
| 33 | block_stack.push_back(branch); | ||
| 34 | return true; | ||
| 35 | }; | ||
| 36 | while (!block_stack.empty()) { | ||
| 37 | Block* const block{block_stack.back()}; | ||
| 38 | block_stack.pop_back(); | ||
| 39 | |||
| 40 | if (!visit_branch(block, block->TrueBranch()) && | ||
| 41 | !visit_branch(block, block->FalseBranch())) { | ||
| 42 | post_order_blocks.push_back(block); | ||
| 43 | } | ||
| 44 | } | ||
| 45 | return post_order_blocks; | ||
| 46 | } | ||
| 47 | |||
| 48 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/post_order.h b/src/shader_recompiler/frontend/ir/post_order.h new file mode 100644 index 000000000..30137ff57 --- /dev/null +++ b/src/shader_recompiler/frontend/ir/post_order.h | |||
| @@ -0,0 +1,13 @@ | |||
| 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::IR { | ||
| 10 | |||
| 11 | BlockList PostOrder(const BlockList& blocks); | ||
| 12 | |||
| 13 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp index 9fa912ed8..dab6d68c0 100644 --- a/src/shader_recompiler/frontend/maxwell/program.cpp +++ b/src/shader_recompiler/frontend/maxwell/program.cpp | |||
| @@ -7,6 +7,7 @@ | |||
| 7 | #include <vector> | 7 | #include <vector> |
| 8 | 8 | ||
| 9 | #include "shader_recompiler/frontend/ir/basic_block.h" | 9 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 10 | #include "shader_recompiler/frontend/ir/post_order.h" | ||
| 10 | #include "shader_recompiler/frontend/ir/structured_control_flow.h" | 11 | #include "shader_recompiler/frontend/ir/structured_control_flow.h" |
| 11 | #include "shader_recompiler/frontend/maxwell/program.h" | 12 | #include "shader_recompiler/frontend/maxwell/program.h" |
| 12 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 13 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| @@ -56,11 +57,14 @@ IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Blo | |||
| 56 | } | 57 | } |
| 57 | 58 | ||
| 58 | fmt::print(stdout, "No optimizations: {}", IR::DumpProgram(program)); | 59 | fmt::print(stdout, "No optimizations: {}", IR::DumpProgram(program)); |
| 59 | std::ranges::for_each(functions, Optimization::SsaRewritePass); | ||
| 60 | for (IR::Function& function : functions) { | 60 | for (IR::Function& function : functions) { |
| 61 | Optimization::Invoke(Optimization::GlobalMemoryToStorageBufferPass, function); | 61 | function.post_order_blocks = PostOrder(function.blocks); |
| 62 | Optimization::Invoke(Optimization::ConstantPropagationPass, function); | 62 | Optimization::SsaRewritePass(function.post_order_blocks); |
| 63 | Optimization::Invoke(Optimization::DeadCodeEliminationPass, function); | 63 | } |
| 64 | for (IR::Function& function : functions) { | ||
| 65 | Optimization::PostOrderInvoke(Optimization::GlobalMemoryToStorageBufferPass, function); | ||
| 66 | Optimization::PostOrderInvoke(Optimization::ConstantPropagationPass, function); | ||
| 67 | Optimization::PostOrderInvoke(Optimization::DeadCodeEliminationPass, function); | ||
| 64 | Optimization::IdentityRemovalPass(function); | 68 | Optimization::IdentityRemovalPass(function); |
| 65 | Optimization::VerificationPass(function); | 69 | Optimization::VerificationPass(function); |
| 66 | } | 70 | } |
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 bbaa412f6..132b2012a 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | |||
| @@ -13,7 +13,7 @@ namespace Shader::Optimization { | |||
| 13 | void DeadCodeEliminationPass(IR::Block& block) { | 13 | void DeadCodeEliminationPass(IR::Block& block) { |
| 14 | // We iterate over the instructions in reverse order. | 14 | // We iterate over the instructions in reverse order. |
| 15 | // This is because removing an instruction reduces the number of uses for earlier instructions. | 15 | // This is because removing an instruction reduces the number of uses for earlier instructions. |
| 16 | for (IR::Inst& inst : std::views::reverse(block)) { | 16 | for (IR::Inst& inst : block | std::views::reverse) { |
| 17 | if (!inst.HasUses() && !inst.MayHaveSideEffects()) { | 17 | if (!inst.HasUses() && !inst.MayHaveSideEffects()) { |
| 18 | inst.Invalidate(); | 18 | inst.Invalidate(); |
| 19 | } | 19 | } |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 578a24d89..30eb31588 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -4,14 +4,16 @@ | |||
| 4 | 4 | ||
| 5 | #pragma once | 5 | #pragma once |
| 6 | 6 | ||
| 7 | #include <span> | ||
| 8 | |||
| 7 | #include "shader_recompiler/frontend/ir/basic_block.h" | 9 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 8 | #include "shader_recompiler/frontend/ir/function.h" | 10 | #include "shader_recompiler/frontend/ir/function.h" |
| 9 | 11 | ||
| 10 | namespace Shader::Optimization { | 12 | namespace Shader::Optimization { |
| 11 | 13 | ||
| 12 | template <typename Func> | 14 | template <typename Func> |
| 13 | void Invoke(Func&& func, IR::Function& function) { | 15 | void PostOrderInvoke(Func&& func, IR::Function& function) { |
| 14 | for (const auto& block : function.blocks) { | 16 | for (const auto& block : function.post_order_blocks) { |
| 15 | func(*block); | 17 | func(*block); |
| 16 | } | 18 | } |
| 17 | } | 19 | } |
| @@ -20,7 +22,7 @@ void ConstantPropagationPass(IR::Block& block); | |||
| 20 | void DeadCodeEliminationPass(IR::Block& block); | 22 | void DeadCodeEliminationPass(IR::Block& block); |
| 21 | void GlobalMemoryToStorageBufferPass(IR::Block& block); | 23 | void GlobalMemoryToStorageBufferPass(IR::Block& block); |
| 22 | void IdentityRemovalPass(IR::Function& function); | 24 | void IdentityRemovalPass(IR::Function& function); |
| 23 | void SsaRewritePass(IR::Function& function); | 25 | void SsaRewritePass(std::span<IR::Block* const> post_order_blocks); |
| 24 | void VerificationPass(const IR::Function& function); | 26 | void VerificationPass(const IR::Function& function); |
| 25 | 27 | ||
| 26 | } // namespace Shader::Optimization | 28 | } // 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 index 7eaf719c4..13f9c914a 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -14,7 +14,13 @@ | |||
| 14 | // https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 | 14 | // https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 |
| 15 | // | 15 | // |
| 16 | 16 | ||
| 17 | #include <ranges> | ||
| 18 | #include <span> | ||
| 19 | #include <variant> | ||
| 20 | #include <vector> | ||
| 21 | |||
| 17 | #include <boost/container/flat_map.hpp> | 22 | #include <boost/container/flat_map.hpp> |
| 23 | #include <boost/container/flat_set.hpp> | ||
| 18 | 24 | ||
| 19 | #include "shader_recompiler/frontend/ir/basic_block.h" | 25 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 20 | #include "shader_recompiler/frontend/ir/function.h" | 26 | #include "shader_recompiler/frontend/ir/function.h" |
| @@ -26,9 +32,9 @@ | |||
| 26 | 32 | ||
| 27 | namespace Shader::Optimization { | 33 | namespace Shader::Optimization { |
| 28 | namespace { | 34 | namespace { |
| 29 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | 35 | struct FlagTag { |
| 30 | 36 | auto operator<=>(const FlagTag&) const noexcept = default; | |
| 31 | struct FlagTag {}; | 37 | }; |
| 32 | struct ZeroFlagTag : FlagTag {}; | 38 | struct ZeroFlagTag : FlagTag {}; |
| 33 | struct SignFlagTag : FlagTag {}; | 39 | struct SignFlagTag : FlagTag {}; |
| 34 | struct CarryFlagTag : FlagTag {}; | 40 | struct CarryFlagTag : FlagTag {}; |
| @@ -38,9 +44,15 @@ struct GotoVariable : FlagTag { | |||
| 38 | GotoVariable() = default; | 44 | GotoVariable() = default; |
| 39 | explicit GotoVariable(u32 index_) : index{index_} {} | 45 | explicit GotoVariable(u32 index_) : index{index_} {} |
| 40 | 46 | ||
| 47 | auto operator<=>(const GotoVariable&) const noexcept = default; | ||
| 48 | |||
| 41 | u32 index; | 49 | u32 index; |
| 42 | }; | 50 | }; |
| 43 | 51 | ||
| 52 | using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, | ||
| 53 | OverflowFlagTag, GotoVariable>; | ||
| 54 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | ||
| 55 | |||
| 44 | struct DefTable { | 56 | struct DefTable { |
| 45 | [[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept { | 57 | [[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept { |
| 46 | return regs[IR::RegIndex(variable)]; | 58 | return regs[IR::RegIndex(variable)]; |
| @@ -102,19 +114,35 @@ public: | |||
| 102 | } | 114 | } |
| 103 | 115 | ||
| 104 | IR::Value ReadVariable(auto variable, IR::Block* block) { | 116 | IR::Value ReadVariable(auto variable, IR::Block* block) { |
| 105 | auto& def{current_def[variable]}; | 117 | const ValueMap& def{current_def[variable]}; |
| 106 | if (const auto it{def.find(block)}; it != def.end()) { | 118 | if (const auto it{def.find(block)}; it != def.end()) { |
| 107 | return it->second; | 119 | return it->second; |
| 108 | } | 120 | } |
| 109 | return ReadVariableRecursive(variable, block); | 121 | return ReadVariableRecursive(variable, block); |
| 110 | } | 122 | } |
| 111 | 123 | ||
| 124 | void SealBlock(IR::Block* block) { | ||
| 125 | const auto it{incomplete_phis.find(block)}; | ||
| 126 | if (it != incomplete_phis.end()) { | ||
| 127 | for (auto& [variant, phi] : it->second) { | ||
| 128 | std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | sealed_blocks.insert(block); | ||
| 132 | } | ||
| 133 | |||
| 112 | private: | 134 | private: |
| 113 | IR::Value ReadVariableRecursive(auto variable, IR::Block* block) { | 135 | IR::Value ReadVariableRecursive(auto variable, IR::Block* block) { |
| 114 | IR::Value val; | 136 | IR::Value val; |
| 115 | if (const std::span preds{block->ImmediatePredecessors()}; preds.size() == 1) { | 137 | if (!sealed_blocks.contains(block)) { |
| 138 | // Incomplete CFG | ||
| 139 | IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 140 | incomplete_phis[block].insert_or_assign(variable, phi); | ||
| 141 | val = IR::Value{&*phi}; | ||
| 142 | } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | ||
| 143 | imm_preds.size() == 1) { | ||
| 116 | // Optimize the common case of one predecessor: no phi needed | 144 | // Optimize the common case of one predecessor: no phi needed |
| 117 | val = ReadVariable(variable, preds.front()); | 145 | val = ReadVariable(variable, imm_preds.front()); |
| 118 | } else { | 146 | } else { |
| 119 | // Break potential cycles with operandless phi | 147 | // Break potential cycles with operandless phi |
| 120 | IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | 148 | IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; |
| @@ -127,8 +155,8 @@ private: | |||
| 127 | } | 155 | } |
| 128 | 156 | ||
| 129 | IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) { | 157 | IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) { |
| 130 | for (IR::Block* const pred : block->ImmediatePredecessors()) { | 158 | for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { |
| 131 | phi.AddPhiOperand(pred, ReadVariable(variable, pred)); | 159 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); |
| 132 | } | 160 | } |
| 133 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); | 161 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); |
| 134 | } | 162 | } |
| @@ -159,6 +187,9 @@ private: | |||
| 159 | return same; | 187 | return same; |
| 160 | } | 188 | } |
| 161 | 189 | ||
| 190 | boost::container::flat_set<IR::Block*> sealed_blocks; | ||
| 191 | boost::container::flat_map<IR::Block*, boost::container::flat_map<Variant, IR::Inst*>> | ||
| 192 | incomplete_phis; | ||
| 162 | DefTable current_def; | 193 | DefTable current_def; |
| 163 | }; | 194 | }; |
| 164 | 195 | ||
| @@ -218,14 +249,19 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | |||
| 218 | break; | 249 | break; |
| 219 | } | 250 | } |
| 220 | } | 251 | } |
| 252 | |||
| 253 | void VisitBlock(Pass& pass, IR::Block* block) { | ||
| 254 | for (IR::Inst& inst : block->Instructions()) { | ||
| 255 | VisitInst(pass, block, inst); | ||
| 256 | } | ||
| 257 | pass.SealBlock(block); | ||
| 258 | } | ||
| 221 | } // Anonymous namespace | 259 | } // Anonymous namespace |
| 222 | 260 | ||
| 223 | void SsaRewritePass(IR::Function& function) { | 261 | void SsaRewritePass(std::span<IR::Block* const> post_order_blocks) { |
| 224 | Pass pass; | 262 | Pass pass; |
| 225 | for (IR::Block* const block : function.blocks) { | 263 | for (IR::Block* const block : post_order_blocks | std::views::reverse) { |
| 226 | for (IR::Inst& inst : block->Instructions()) { | 264 | VisitBlock(pass, block); |
| 227 | VisitInst(pass, block, inst); | ||
| 228 | } | ||
| 229 | } | 265 | } |
| 230 | } | 266 | } |
| 231 | 267 | ||
diff --git a/src/shader_recompiler/main.cpp b/src/shader_recompiler/main.cpp index 92358232c..29f65966c 100644 --- a/src/shader_recompiler/main.cpp +++ b/src/shader_recompiler/main.cpp | |||
| @@ -69,14 +69,12 @@ int main() { | |||
| 69 | 69 | ||
| 70 | // FileEnvironment env{"D:\\Shaders\\Database\\Oninaki\\CS8F146B41DB6BD826.bin"}; | 70 | // FileEnvironment env{"D:\\Shaders\\Database\\Oninaki\\CS8F146B41DB6BD826.bin"}; |
| 71 | FileEnvironment env{"D:\\Shaders\\shader.bin"}; | 71 | FileEnvironment env{"D:\\Shaders\\shader.bin"}; |
| 72 | for (int i = 0; i < 1; ++i) { | 72 | block_pool->ReleaseContents(); |
| 73 | block_pool->ReleaseContents(); | 73 | inst_pool->ReleaseContents(); |
| 74 | inst_pool->ReleaseContents(); | 74 | flow_block_pool->ReleaseContents(); |
| 75 | flow_block_pool->ReleaseContents(); | 75 | Flow::CFG cfg{env, *flow_block_pool, 0}; |
| 76 | Flow::CFG cfg{env, *flow_block_pool, 0}; | 76 | fmt::print(stdout, "{}\n", cfg.Dot()); |
| 77 | fmt::print(stdout, "{}\n", cfg.Dot()); | 77 | IR::Program program{TranslateProgram(*inst_pool, *block_pool, env, cfg)}; |
| 78 | IR::Program program{TranslateProgram(*inst_pool, *block_pool, env, cfg)}; | 78 | fmt::print(stdout, "{}\n", IR::DumpProgram(program)); |
| 79 | fmt::print(stdout, "{}\n", IR::DumpProgram(program)); | 79 | // Backend::SPIRV::EmitSPIRV spirv{program}; |
| 80 | Backend::SPIRV::EmitSPIRV spirv{program}; | ||
| 81 | } | ||
| 82 | } | 80 | } |