diff options
| author | 2021-03-14 03:41:05 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:23 -0400 | |
| commit | 71f96fa6366dc6dd306a953bca1b958fb32bc55a (patch) | |
| tree | 12e13f9502e4b9510446c967a831e5d4bacb729e /src/shader_recompiler | |
| parent | spirv: Add SignedZeroInfNanPreserve logic (diff) | |
| download | yuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.tar.gz yuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.tar.xz yuzu-71f96fa6366dc6dd306a953bca1b958fb32bc55a.zip | |
shader: Implement CAL inlining function calls
Diffstat (limited to 'src/shader_recompiler')
24 files changed, 286 insertions, 330 deletions
diff --git a/src/shader_recompiler/CMakeLists.txt b/src/shader_recompiler/CMakeLists.txt index 171fdd321..20409e09a 100644 --- a/src/shader_recompiler/CMakeLists.txt +++ b/src/shader_recompiler/CMakeLists.txt | |||
| @@ -27,8 +27,6 @@ add_library(shader_recompiler STATIC | |||
| 27 | frontend/ir/condition.h | 27 | frontend/ir/condition.h |
| 28 | frontend/ir/flow_test.cpp | 28 | frontend/ir/flow_test.cpp |
| 29 | frontend/ir/flow_test.h | 29 | frontend/ir/flow_test.h |
| 30 | frontend/ir/function.cpp | ||
| 31 | frontend/ir/function.h | ||
| 32 | frontend/ir/ir_emitter.cpp | 30 | frontend/ir/ir_emitter.cpp |
| 33 | frontend/ir/ir_emitter.h | 31 | frontend/ir/ir_emitter.h |
| 34 | frontend/ir/microinstruction.cpp | 32 | frontend/ir/microinstruction.cpp |
| @@ -43,8 +41,6 @@ add_library(shader_recompiler STATIC | |||
| 43 | frontend/ir/program.cpp | 41 | frontend/ir/program.cpp |
| 44 | frontend/ir/program.h | 42 | frontend/ir/program.h |
| 45 | frontend/ir/reg.h | 43 | frontend/ir/reg.h |
| 46 | frontend/ir/structured_control_flow.cpp | ||
| 47 | frontend/ir/structured_control_flow.h | ||
| 48 | frontend/ir/type.cpp | 44 | frontend/ir/type.cpp |
| 49 | frontend/ir/type.h | 45 | frontend/ir/type.h |
| 50 | frontend/ir/value.cpp | 46 | frontend/ir/value.cpp |
| @@ -60,6 +56,8 @@ add_library(shader_recompiler STATIC | |||
| 60 | frontend/maxwell/opcodes.h | 56 | frontend/maxwell/opcodes.h |
| 61 | frontend/maxwell/program.cpp | 57 | frontend/maxwell/program.cpp |
| 62 | frontend/maxwell/program.h | 58 | frontend/maxwell/program.h |
| 59 | frontend/maxwell/structured_control_flow.cpp | ||
| 60 | frontend/maxwell/structured_control_flow.h | ||
| 63 | frontend/maxwell/translate/impl/bitfield_extract.cpp | 61 | frontend/maxwell/translate/impl/bitfield_extract.cpp |
| 64 | frontend/maxwell/translate/impl/bitfield_insert.cpp | 62 | frontend/maxwell/translate/impl/bitfield_insert.cpp |
| 65 | frontend/maxwell/translate/impl/common_encoding.h | 63 | frontend/maxwell/translate/impl/common_encoding.h |
diff --git a/src/shader_recompiler/backend/spirv/emit_context.cpp b/src/shader_recompiler/backend/spirv/emit_context.cpp index 278b26b50..f848c6175 100644 --- a/src/shader_recompiler/backend/spirv/emit_context.cpp +++ b/src/shader_recompiler/backend/spirv/emit_context.cpp | |||
| @@ -262,10 +262,8 @@ void EmitContext::DefineTextures(const Info& info, u32& binding) { | |||
| 262 | } | 262 | } |
| 263 | 263 | ||
| 264 | void EmitContext::DefineLabels(IR::Program& program) { | 264 | void EmitContext::DefineLabels(IR::Program& program) { |
| 265 | for (const IR::Function& function : program.functions) { | 265 | for (IR::Block* const block : program.blocks) { |
| 266 | for (IR::Block* const block : function.blocks) { | 266 | block->SetDefinition(OpLabel()); |
| 267 | block->SetDefinition(OpLabel()); | ||
| 268 | } | ||
| 269 | } | 267 | } |
| 270 | } | 268 | } |
| 271 | 269 | ||
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp index c7cba6279..7e7db9161 100644 --- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp +++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp | |||
| @@ -10,7 +10,6 @@ | |||
| 10 | 10 | ||
| 11 | #include "shader_recompiler/backend/spirv/emit_spirv.h" | 11 | #include "shader_recompiler/backend/spirv/emit_spirv.h" |
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | 12 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 14 | #include "shader_recompiler/frontend/ir/microinstruction.h" | 13 | #include "shader_recompiler/frontend/ir/microinstruction.h" |
| 15 | #include "shader_recompiler/frontend/ir/program.h" | 14 | #include "shader_recompiler/frontend/ir/program.h" |
| 16 | 15 | ||
| @@ -199,18 +198,14 @@ Id PhiArgDef(EmitContext& ctx, IR::Inst* inst, size_t index) { | |||
| 199 | std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program) { | 198 | std::vector<u32> EmitSPIRV(const Profile& profile, Environment& env, IR::Program& program) { |
| 200 | EmitContext ctx{profile, program}; | 199 | EmitContext ctx{profile, program}; |
| 201 | const Id void_function{ctx.TypeFunction(ctx.void_id)}; | 200 | const Id void_function{ctx.TypeFunction(ctx.void_id)}; |
| 202 | // FIXME: Forward declare functions (needs sirit support) | 201 | const Id func{ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function)}; |
| 203 | Id func{}; | 202 | for (IR::Block* const block : program.blocks) { |
| 204 | for (IR::Function& function : program.functions) { | 203 | ctx.AddLabel(block->Definition<Id>()); |
| 205 | func = ctx.OpFunction(ctx.void_id, spv::FunctionControlMask::MaskNone, void_function); | 204 | for (IR::Inst& inst : block->Instructions()) { |
| 206 | for (IR::Block* const block : function.blocks) { | 205 | EmitInst(ctx, &inst); |
| 207 | ctx.AddLabel(block->Definition<Id>()); | ||
| 208 | for (IR::Inst& inst : block->Instructions()) { | ||
| 209 | EmitInst(ctx, &inst); | ||
| 210 | } | ||
| 211 | } | 206 | } |
| 212 | ctx.OpFunctionEnd(); | ||
| 213 | } | 207 | } |
| 208 | ctx.OpFunctionEnd(); | ||
| 214 | boost::container::small_vector<Id, 32> interfaces; | 209 | boost::container::small_vector<Id, 32> interfaces; |
| 215 | const Info& info{program.info}; | 210 | const Info& info{program.info}; |
| 216 | if (info.uses_workgroup_id) { | 211 | if (info.uses_workgroup_id) { |
diff --git a/src/shader_recompiler/frontend/ir/function.cpp b/src/shader_recompiler/frontend/ir/function.cpp deleted file mode 100644 index d1fc9461d..000000000 --- a/src/shader_recompiler/frontend/ir/function.cpp +++ /dev/null | |||
| @@ -1,5 +0,0 @@ | |||
| 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 deleted file mode 100644 index d1f061146..000000000 --- a/src/shader_recompiler/frontend/ir/function.h +++ /dev/null | |||
| @@ -1,18 +0,0 @@ | |||
| 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 <boost/container/small_vector.hpp> | ||
| 8 | |||
| 9 | #include "shader_recompiler/frontend/ir/basic_block.h" | ||
| 10 | |||
| 11 | namespace Shader::IR { | ||
| 12 | |||
| 13 | struct Function { | ||
| 14 | BlockList blocks; | ||
| 15 | BlockList post_order_blocks; | ||
| 16 | }; | ||
| 17 | |||
| 18 | } // namespace Shader::IR | ||
diff --git a/src/shader_recompiler/frontend/ir/program.cpp b/src/shader_recompiler/frontend/ir/program.cpp index 8c301c3a1..5f51aeb5f 100644 --- a/src/shader_recompiler/frontend/ir/program.cpp +++ b/src/shader_recompiler/frontend/ir/program.cpp | |||
| @@ -9,7 +9,8 @@ | |||
| 9 | 9 | ||
| 10 | #include <fmt/format.h> | 10 | #include <fmt/format.h> |
| 11 | 11 | ||
| 12 | #include "shader_recompiler/frontend/ir/function.h" | 12 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/frontend/ir/microinstruction.h" | ||
| 13 | #include "shader_recompiler/frontend/ir/program.h" | 14 | #include "shader_recompiler/frontend/ir/program.h" |
| 14 | 15 | ||
| 15 | namespace Shader::IR { | 16 | namespace Shader::IR { |
| @@ -19,18 +20,13 @@ std::string DumpProgram(const Program& program) { | |||
| 19 | std::map<const IR::Inst*, size_t> inst_to_index; | 20 | std::map<const IR::Inst*, size_t> inst_to_index; |
| 20 | std::map<const IR::Block*, size_t> block_to_index; | 21 | std::map<const IR::Block*, size_t> block_to_index; |
| 21 | 22 | ||
| 22 | for (const IR::Function& function : program.functions) { | 23 | for (const IR::Block* const block : program.blocks) { |
| 23 | for (const IR::Block* const block : function.blocks) { | 24 | block_to_index.emplace(block, index); |
| 24 | block_to_index.emplace(block, index); | 25 | ++index; |
| 25 | ++index; | ||
| 26 | } | ||
| 27 | } | 26 | } |
| 28 | std::string ret; | 27 | std::string ret; |
| 29 | for (const IR::Function& function : program.functions) { | 28 | for (const auto& block : program.blocks) { |
| 30 | ret += fmt::format("Function\n"); | 29 | ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; |
| 31 | for (const auto& block : function.blocks) { | ||
| 32 | ret += IR::DumpBlock(*block, block_to_index, inst_to_index, index) + '\n'; | ||
| 33 | } | ||
| 34 | } | 30 | } |
| 35 | return ret; | 31 | return ret; |
| 36 | } | 32 | } |
diff --git a/src/shader_recompiler/frontend/ir/program.h b/src/shader_recompiler/frontend/ir/program.h index 98aab2dc6..bce8b19b3 100644 --- a/src/shader_recompiler/frontend/ir/program.h +++ b/src/shader_recompiler/frontend/ir/program.h | |||
| @@ -8,13 +8,14 @@ | |||
| 8 | 8 | ||
| 9 | #include <boost/container/small_vector.hpp> | 9 | #include <boost/container/small_vector.hpp> |
| 10 | 10 | ||
| 11 | #include "shader_recompiler/frontend/ir/function.h" | 11 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 12 | #include "shader_recompiler/shader_info.h" | 12 | #include "shader_recompiler/shader_info.h" |
| 13 | 13 | ||
| 14 | namespace Shader::IR { | 14 | namespace Shader::IR { |
| 15 | 15 | ||
| 16 | struct Program { | 16 | struct Program { |
| 17 | boost::container::small_vector<Function, 1> functions; | 17 | BlockList blocks; |
| 18 | BlockList post_order_blocks; | ||
| 18 | Info info; | 19 | Info info; |
| 19 | }; | 20 | }; |
| 20 | 21 | ||
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index d0dc66330..715c0e92d 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp | |||
| @@ -31,13 +31,12 @@ struct Compare { | |||
| 31 | return lhs.begin < rhs.begin; | 31 | return lhs.begin < rhs.begin; |
| 32 | } | 32 | } |
| 33 | }; | 33 | }; |
| 34 | } // Anonymous namespace | ||
| 35 | 34 | ||
| 36 | static u32 BranchOffset(Location pc, Instruction inst) { | 35 | u32 BranchOffset(Location pc, Instruction inst) { |
| 37 | return pc.Offset() + inst.branch.Offset() + 8; | 36 | return pc.Offset() + inst.branch.Offset() + 8; |
| 38 | } | 37 | } |
| 39 | 38 | ||
| 40 | static void Split(Block* old_block, Block* new_block, Location pc) { | 39 | void Split(Block* old_block, Block* new_block, Location pc) { |
| 41 | if (pc <= old_block->begin || pc >= old_block->end) { | 40 | if (pc <= old_block->begin || pc >= old_block->end) { |
| 42 | throw InvalidArgument("Invalid address to split={}", pc); | 41 | throw InvalidArgument("Invalid address to split={}", pc); |
| 43 | } | 42 | } |
| @@ -49,21 +48,19 @@ static void Split(Block* old_block, Block* new_block, Location pc) { | |||
| 49 | .cond{old_block->cond}, | 48 | .cond{old_block->cond}, |
| 50 | .branch_true{old_block->branch_true}, | 49 | .branch_true{old_block->branch_true}, |
| 51 | .branch_false{old_block->branch_false}, | 50 | .branch_false{old_block->branch_false}, |
| 52 | .ir{nullptr}, | ||
| 53 | }; | 51 | }; |
| 54 | *old_block = Block{ | 52 | *old_block = Block{ |
| 55 | .begin{old_block->begin}, | 53 | .begin{old_block->begin}, |
| 56 | .end{pc}, | 54 | .end{pc}, |
| 57 | .end_class{EndClass::Branch}, | 55 | .end_class{EndClass::Branch}, |
| 58 | .stack{std::move(old_block->stack)}, | 56 | .stack{std::move(old_block->stack)}, |
| 59 | .cond{IR::Condition{true}}, | 57 | .cond{true}, |
| 60 | .branch_true{new_block}, | 58 | .branch_true{new_block}, |
| 61 | .branch_false{nullptr}, | 59 | .branch_false{nullptr}, |
| 62 | .ir{nullptr}, | ||
| 63 | }; | 60 | }; |
| 64 | } | 61 | } |
| 65 | 62 | ||
| 66 | static Token OpcodeToken(Opcode opcode) { | 63 | Token OpcodeToken(Opcode opcode) { |
| 67 | switch (opcode) { | 64 | switch (opcode) { |
| 68 | case Opcode::PBK: | 65 | case Opcode::PBK: |
| 69 | case Opcode::BRK: | 66 | case Opcode::BRK: |
| @@ -89,7 +86,7 @@ static Token OpcodeToken(Opcode opcode) { | |||
| 89 | } | 86 | } |
| 90 | } | 87 | } |
| 91 | 88 | ||
| 92 | static bool IsAbsoluteJump(Opcode opcode) { | 89 | bool IsAbsoluteJump(Opcode opcode) { |
| 93 | switch (opcode) { | 90 | switch (opcode) { |
| 94 | case Opcode::JCAL: | 91 | case Opcode::JCAL: |
| 95 | case Opcode::JMP: | 92 | case Opcode::JMP: |
| @@ -100,7 +97,7 @@ static bool IsAbsoluteJump(Opcode opcode) { | |||
| 100 | } | 97 | } |
| 101 | } | 98 | } |
| 102 | 99 | ||
| 103 | static bool HasFlowTest(Opcode opcode) { | 100 | bool HasFlowTest(Opcode opcode) { |
| 104 | switch (opcode) { | 101 | switch (opcode) { |
| 105 | case Opcode::BRA: | 102 | case Opcode::BRA: |
| 106 | case Opcode::BRX: | 103 | case Opcode::BRX: |
| @@ -121,13 +118,14 @@ static bool HasFlowTest(Opcode opcode) { | |||
| 121 | } | 118 | } |
| 122 | } | 119 | } |
| 123 | 120 | ||
| 124 | static std::string NameOf(const Block& block) { | 121 | std::string NameOf(const Block& block) { |
| 125 | if (block.begin.IsVirtual()) { | 122 | if (block.begin.IsVirtual()) { |
| 126 | return fmt::format("\"Virtual {}\"", block.begin); | 123 | return fmt::format("\"Virtual {}\"", block.begin); |
| 127 | } else { | 124 | } else { |
| 128 | return fmt::format("\"{}\"", block.begin); | 125 | return fmt::format("\"{}\"", block.begin); |
| 129 | } | 126 | } |
| 130 | } | 127 | } |
| 128 | } // Anonymous namespace | ||
| 131 | 129 | ||
| 132 | void Stack::Push(Token token, Location target) { | 130 | void Stack::Push(Token token, Location target) { |
| 133 | entries.push_back({ | 131 | entries.push_back({ |
| @@ -166,26 +164,24 @@ bool Block::Contains(Location pc) const noexcept { | |||
| 166 | return pc >= begin && pc < end; | 164 | return pc >= begin && pc < end; |
| 167 | } | 165 | } |
| 168 | 166 | ||
| 169 | Function::Function(Location start_address) | 167 | Function::Function(ObjectPool<Block>& block_pool, Location start_address) |
| 170 | : entrypoint{start_address}, labels{{ | 168 | : entrypoint{start_address}, labels{{ |
| 171 | .address{start_address}, | 169 | .address{start_address}, |
| 172 | .block{nullptr}, | 170 | .block{block_pool.Create(Block{ |
| 171 | .begin{start_address}, | ||
| 172 | .end{start_address}, | ||
| 173 | .end_class{EndClass::Branch}, | ||
| 174 | .stack{}, | ||
| 175 | .cond{true}, | ||
| 176 | .branch_true{nullptr}, | ||
| 177 | .branch_false{nullptr}, | ||
| 178 | })}, | ||
| 173 | .stack{}, | 179 | .stack{}, |
| 174 | }} {} | 180 | }} {} |
| 175 | 181 | ||
| 176 | CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address) | 182 | CFG::CFG(Environment& env_, ObjectPool<Block>& block_pool_, Location start_address) |
| 177 | : env{env_}, block_pool{block_pool_} { | 183 | : env{env_}, block_pool{block_pool_} { |
| 178 | functions.emplace_back(start_address); | 184 | functions.emplace_back(block_pool, start_address); |
| 179 | functions.back().labels.back().block = block_pool.Create(Block{ | ||
| 180 | .begin{start_address}, | ||
| 181 | .end{start_address}, | ||
| 182 | .end_class{EndClass::Branch}, | ||
| 183 | .stack{}, | ||
| 184 | .cond{IR::Condition{true}}, | ||
| 185 | .branch_true{nullptr}, | ||
| 186 | .branch_false{nullptr}, | ||
| 187 | .ir{nullptr}, | ||
| 188 | }); | ||
| 189 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { | 185 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { |
| 190 | while (!functions[function_id].labels.empty()) { | 186 | while (!functions[function_id].labels.empty()) { |
| 191 | Function& function{functions[function_id]}; | 187 | Function& function{functions[function_id]}; |
| @@ -308,11 +304,17 @@ CFG::AnalysisState CFG::AnalyzeInst(Block* block, FunctionId function_id, Locati | |||
| 308 | const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; | 304 | const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; |
| 309 | // Technically CAL pushes into PRET, but that's implicit in the function call for us | 305 | // Technically CAL pushes into PRET, but that's implicit in the function call for us |
| 310 | // Insert the function into the list if it doesn't exist | 306 | // Insert the function into the list if it doesn't exist |
| 311 | if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { | 307 | const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)}; |
| 312 | functions.emplace_back(cal_pc); | 308 | const bool exists{it != functions.end()}; |
| 309 | const FunctionId call_id{exists ? std::distance(functions.begin(), it) : functions.size()}; | ||
| 310 | if (!exists) { | ||
| 311 | functions.emplace_back(block_pool, cal_pc); | ||
| 313 | } | 312 | } |
| 314 | // Handle CAL like a regular instruction | 313 | block->end_class = EndClass::Call; |
| 315 | break; | 314 | block->function_call = call_id; |
| 315 | block->return_block = AddLabel(block, block->stack, pc + 1, function_id); | ||
| 316 | block->end = pc; | ||
| 317 | return AnalysisState::Branch; | ||
| 316 | } | 318 | } |
| 317 | default: | 319 | default: |
| 318 | break; | 320 | break; |
| @@ -348,7 +350,6 @@ void CFG::AnalyzeCondInst(Block* block, FunctionId function_id, Location pc, | |||
| 348 | .cond{cond}, | 350 | .cond{cond}, |
| 349 | .branch_true{conditional_block}, | 351 | .branch_true{conditional_block}, |
| 350 | .branch_false{nullptr}, | 352 | .branch_false{nullptr}, |
| 351 | .ir{nullptr}, | ||
| 352 | }; | 353 | }; |
| 353 | // Save the contents of the visited block in the conditional block | 354 | // Save the contents of the visited block in the conditional block |
| 354 | *conditional_block = std::move(*block); | 355 | *conditional_block = std::move(*block); |
| @@ -401,16 +402,6 @@ void CFG::AnalyzeBRX(Block*, Location, Instruction, bool is_absolute) { | |||
| 401 | throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); | 402 | throw NotImplementedException("{}", is_absolute ? "JMX" : "BRX"); |
| 402 | } | 403 | } |
| 403 | 404 | ||
| 404 | void CFG::AnalyzeCAL(Location pc, Instruction inst, bool is_absolute) { | ||
| 405 | const Location cal_pc{is_absolute ? inst.branch.Absolute() : BranchOffset(pc, inst)}; | ||
| 406 | // Technically CAL pushes into PRET, but that's implicit in the function call for us | ||
| 407 | // Insert the function to the function list if it doesn't exist | ||
| 408 | const auto it{std::ranges::find(functions, cal_pc, &Function::entrypoint)}; | ||
| 409 | if (it == functions.end()) { | ||
| 410 | functions.emplace_back(cal_pc); | ||
| 411 | } | ||
| 412 | } | ||
| 413 | |||
| 414 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, | 405 | CFG::AnalysisState CFG::AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, |
| 415 | Instruction inst) { | 406 | Instruction inst) { |
| 416 | const IR::FlowTest flow_test{inst.branch.flow_test}; | 407 | const IR::FlowTest flow_test{inst.branch.flow_test}; |
| @@ -455,10 +446,9 @@ Block* CFG::AddLabel(Block* block, Stack stack, Location pc, FunctionId function | |||
| 455 | .end{pc}, | 446 | .end{pc}, |
| 456 | .end_class{EndClass::Branch}, | 447 | .end_class{EndClass::Branch}, |
| 457 | .stack{stack}, | 448 | .stack{stack}, |
| 458 | .cond{IR::Condition{true}}, | 449 | .cond{true}, |
| 459 | .branch_true{nullptr}, | 450 | .branch_true{nullptr}, |
| 460 | .branch_false{nullptr}, | 451 | .branch_false{nullptr}, |
| 461 | .ir{nullptr}, | ||
| 462 | })}; | 452 | })}; |
| 463 | function.labels.push_back(Label{ | 453 | function.labels.push_back(Label{ |
| 464 | .address{pc}, | 454 | .address{pc}, |
| @@ -495,6 +485,14 @@ std::string CFG::Dot() const { | |||
| 495 | add_branch(block.branch_false, false); | 485 | add_branch(block.branch_false, false); |
| 496 | } | 486 | } |
| 497 | break; | 487 | break; |
| 488 | case EndClass::Call: | ||
| 489 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | ||
| 490 | dot += fmt::format("\t\tN{}->{};\n", node_uid, NameOf(*block.return_block)); | ||
| 491 | dot += fmt::format("\t\tN{} [label=\"Call {}\"][shape=square][style=stripped];\n", | ||
| 492 | node_uid, block.function_call); | ||
| 493 | dot += '\n'; | ||
| 494 | ++node_uid; | ||
| 495 | break; | ||
| 498 | case EndClass::Exit: | 496 | case EndClass::Exit: |
| 499 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); | 497 | dot += fmt::format("\t\t{}->N{};\n", name, node_uid); |
| 500 | dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n", | 498 | dot += fmt::format("\t\tN{} [label=\"Exit\"][shape=square][style=stripped];\n", |
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.h b/src/shader_recompiler/frontend/maxwell/control_flow.h index 209c9e551..fe74f210f 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/control_flow.h | |||
| @@ -20,16 +20,13 @@ | |||
| 20 | #include "shader_recompiler/frontend/maxwell/opcodes.h" | 20 | #include "shader_recompiler/frontend/maxwell/opcodes.h" |
| 21 | #include "shader_recompiler/object_pool.h" | 21 | #include "shader_recompiler/object_pool.h" |
| 22 | 22 | ||
| 23 | namespace Shader::IR { | ||
| 24 | class Block; | ||
| 25 | } | ||
| 26 | |||
| 27 | namespace Shader::Maxwell::Flow { | 23 | namespace Shader::Maxwell::Flow { |
| 28 | 24 | ||
| 29 | using FunctionId = size_t; | 25 | using FunctionId = size_t; |
| 30 | 26 | ||
| 31 | enum class EndClass { | 27 | enum class EndClass { |
| 32 | Branch, | 28 | Branch, |
| 29 | Call, | ||
| 33 | Exit, | 30 | Exit, |
| 34 | Return, | 31 | Return, |
| 35 | }; | 32 | }; |
| @@ -75,9 +72,14 @@ struct Block : boost::intrusive::set_base_hook< | |||
| 75 | EndClass end_class; | 72 | EndClass end_class; |
| 76 | Stack stack; | 73 | Stack stack; |
| 77 | IR::Condition cond; | 74 | IR::Condition cond; |
| 78 | Block* branch_true; | 75 | union { |
| 79 | Block* branch_false; | 76 | Block* branch_true; |
| 80 | IR::Block* ir; | 77 | FunctionId function_call; |
| 78 | }; | ||
| 79 | union { | ||
| 80 | Block* branch_false; | ||
| 81 | Block* return_block; | ||
| 82 | }; | ||
| 81 | }; | 83 | }; |
| 82 | 84 | ||
| 83 | struct Label { | 85 | struct Label { |
| @@ -87,7 +89,7 @@ struct Label { | |||
| 87 | }; | 89 | }; |
| 88 | 90 | ||
| 89 | struct Function { | 91 | struct Function { |
| 90 | Function(Location start_address); | 92 | explicit Function(ObjectPool<Block>& block_pool, Location start_address); |
| 91 | 93 | ||
| 92 | Location entrypoint; | 94 | Location entrypoint; |
| 93 | boost::container::small_vector<Label, 16> labels; | 95 | boost::container::small_vector<Label, 16> labels; |
| @@ -137,7 +139,6 @@ private: | |||
| 137 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, | 139 | void AnalyzeBRA(Block* block, FunctionId function_id, Location pc, Instruction inst, |
| 138 | bool is_absolute); | 140 | bool is_absolute); |
| 139 | void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute); | 141 | void AnalyzeBRX(Block* block, Location pc, Instruction inst, bool is_absolute); |
| 140 | void AnalyzeCAL(Location pc, Instruction inst, bool is_absolute); | ||
| 141 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); | 142 | AnalysisState AnalyzeEXIT(Block* block, FunctionId function_id, Location pc, Instruction inst); |
| 142 | 143 | ||
| 143 | /// Return the branch target block id | 144 | /// Return the branch target block id |
diff --git a/src/shader_recompiler/frontend/maxwell/program.cpp b/src/shader_recompiler/frontend/maxwell/program.cpp index b270bbccd..8bfa64326 100644 --- a/src/shader_recompiler/frontend/maxwell/program.cpp +++ b/src/shader_recompiler/frontend/maxwell/program.cpp | |||
| @@ -8,67 +8,44 @@ | |||
| 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/post_order.h" |
| 11 | #include "shader_recompiler/frontend/ir/structured_control_flow.h" | ||
| 12 | #include "shader_recompiler/frontend/maxwell/program.h" | 11 | #include "shader_recompiler/frontend/maxwell/program.h" |
| 12 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" | ||
| 13 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 13 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| 14 | #include "shader_recompiler/ir_opt/passes.h" | 14 | #include "shader_recompiler/ir_opt/passes.h" |
| 15 | 15 | ||
| 16 | namespace Shader::Maxwell { | 16 | namespace Shader::Maxwell { |
| 17 | namespace { | 17 | |
| 18 | IR::BlockList TranslateCode(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | 18 | static void RemoveUnreachableBlocks(IR::Program& program) { |
| 19 | Environment& env, Flow::Function& cfg_function) { | 19 | // Some blocks might be unreachable if a function call exists unconditionally |
| 20 | const size_t num_blocks{cfg_function.blocks.size()}; | 20 | // If this happens the number of blocks and post order blocks will mismatch |
| 21 | std::vector<IR::Block*> blocks(cfg_function.blocks.size()); | 21 | if (program.blocks.size() == program.post_order_blocks.size()) { |
| 22 | std::ranges::for_each(cfg_function.blocks, [&, i = size_t{0}](auto& cfg_block) mutable { | 22 | return; |
| 23 | const u32 begin{cfg_block.begin.Offset()}; | 23 | } |
| 24 | const u32 end{cfg_block.end.Offset()}; | 24 | const IR::BlockList& post_order{program.post_order_blocks}; |
| 25 | blocks[i] = block_pool.Create(inst_pool, begin, end); | 25 | std::erase_if(program.blocks, [&](IR::Block* block) { |
| 26 | cfg_block.ir = blocks[i]; | 26 | return std::ranges::find(post_order, block) == post_order.end(); |
| 27 | ++i; | ||
| 28 | }); | ||
| 29 | std::ranges::for_each(cfg_function.blocks, [&, i = size_t{0}](auto& cfg_block) mutable { | ||
| 30 | IR::Block* const block{blocks[i]}; | ||
| 31 | ++i; | ||
| 32 | if (cfg_block.end_class != Flow::EndClass::Branch) { | ||
| 33 | block->SetReturn(); | ||
| 34 | } else if (cfg_block.cond == IR::Condition{true}) { | ||
| 35 | block->SetBranch(cfg_block.branch_true->ir); | ||
| 36 | } else if (cfg_block.cond == IR::Condition{false}) { | ||
| 37 | block->SetBranch(cfg_block.branch_false->ir); | ||
| 38 | } else { | ||
| 39 | block->SetBranches(cfg_block.cond, cfg_block.branch_true->ir, | ||
| 40 | cfg_block.branch_false->ir); | ||
| 41 | } | ||
| 42 | }); | 27 | }); |
| 43 | return IR::VisitAST(inst_pool, block_pool, blocks, | ||
| 44 | [&](IR::Block* block) { Translate(env, block); }); | ||
| 45 | } | 28 | } |
| 46 | } // Anonymous namespace | ||
| 47 | 29 | ||
| 48 | IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, | 30 | IR::Program TranslateProgram(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, |
| 49 | Environment& env, Flow::CFG& cfg) { | 31 | Environment& env, Flow::CFG& cfg) { |
| 50 | IR::Program program; | 32 | IR::Program program; |
| 51 | auto& functions{program.functions}; | 33 | program.blocks = VisitAST(inst_pool, block_pool, env, cfg); |
| 52 | functions.reserve(cfg.Functions().size()); | 34 | program.post_order_blocks = PostOrder(program.blocks); |
| 53 | for (Flow::Function& cfg_function : cfg.Functions()) { | 35 | RemoveUnreachableBlocks(program); |
| 54 | functions.push_back(IR::Function{ | 36 | |
| 55 | .blocks{TranslateCode(inst_pool, block_pool, env, cfg_function)}, | 37 | // Replace instructions before the SSA rewrite |
| 56 | .post_order_blocks{}, | ||
| 57 | }); | ||
| 58 | } | ||
| 59 | Optimization::LowerFp16ToFp32(program); | 38 | Optimization::LowerFp16ToFp32(program); |
| 60 | for (IR::Function& function : functions) { | 39 | |
| 61 | function.post_order_blocks = PostOrder(function.blocks); | 40 | Optimization::SsaRewritePass(program); |
| 62 | Optimization::SsaRewritePass(function.post_order_blocks); | 41 | |
| 63 | } | ||
| 64 | Optimization::GlobalMemoryToStorageBufferPass(program); | 42 | Optimization::GlobalMemoryToStorageBufferPass(program); |
| 65 | Optimization::TexturePass(env, program); | 43 | Optimization::TexturePass(env, program); |
| 66 | for (IR::Function& function : functions) { | 44 | |
| 67 | Optimization::PostOrderInvoke(Optimization::ConstantPropagationPass, function); | 45 | Optimization::ConstantPropagationPass(program); |
| 68 | Optimization::PostOrderInvoke(Optimization::DeadCodeEliminationPass, function); | 46 | Optimization::DeadCodeEliminationPass(program); |
| 69 | Optimization::IdentityRemovalPass(function); | 47 | Optimization::IdentityRemovalPass(program); |
| 70 | Optimization::VerificationPass(function); | 48 | Optimization::VerificationPass(program); |
| 71 | } | ||
| 72 | Optimization::CollectShaderInfoPass(program); | 49 | Optimization::CollectShaderInfoPass(program); |
| 73 | return program; | 50 | return program; |
| 74 | } | 51 | } |
diff --git a/src/shader_recompiler/frontend/ir/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp index bfba55a7e..5f5d9cf17 100644 --- a/src/shader_recompiler/frontend/ir/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |||
| @@ -14,11 +14,14 @@ | |||
| 14 | 14 | ||
| 15 | #include <boost/intrusive/list.hpp> | 15 | #include <boost/intrusive/list.hpp> |
| 16 | 16 | ||
| 17 | #include "shader_recompiler/environment.h" | ||
| 17 | #include "shader_recompiler/frontend/ir/basic_block.h" | 18 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 18 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 20 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" | ||
| 21 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | ||
| 19 | #include "shader_recompiler/object_pool.h" | 22 | #include "shader_recompiler/object_pool.h" |
| 20 | 23 | ||
| 21 | namespace Shader::IR { | 24 | namespace Shader::Maxwell { |
| 22 | namespace { | 25 | namespace { |
| 23 | struct Statement; | 26 | struct Statement; |
| 24 | 27 | ||
| @@ -79,7 +82,7 @@ struct Variable {}; | |||
| 79 | #pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement | 82 | #pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement |
| 80 | #endif | 83 | #endif |
| 81 | struct Statement : ListBaseHook { | 84 | struct Statement : ListBaseHook { |
| 82 | Statement(Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {} | 85 | Statement(IR::Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {} |
| 83 | Statement(Goto, Statement* cond_, Node label_, Statement* up_) | 86 | Statement(Goto, Statement* cond_, Node label_, Statement* up_) |
| 84 | : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {} | 87 | : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {} |
| 85 | Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {} | 88 | Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {} |
| @@ -91,7 +94,7 @@ struct Statement : ListBaseHook { | |||
| 91 | : cond{cond_}, up{up_}, type{StatementType::Break} {} | 94 | : cond{cond_}, up{up_}, type{StatementType::Break} {} |
| 92 | Statement(Return) : type{StatementType::Return} {} | 95 | Statement(Return) : type{StatementType::Return} {} |
| 93 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} | 96 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} |
| 94 | Statement(Identity, Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} | 97 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} |
| 95 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} | 98 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} |
| 96 | Statement(Or, Statement* op_a_, Statement* op_b_) | 99 | Statement(Or, Statement* op_a_, Statement* op_b_) |
| 97 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} | 100 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} |
| @@ -106,10 +109,10 @@ struct Statement : ListBaseHook { | |||
| 106 | } | 109 | } |
| 107 | 110 | ||
| 108 | union { | 111 | union { |
| 109 | Block* code; | 112 | IR::Block* code; |
| 110 | Node label; | 113 | Node label; |
| 111 | Tree children; | 114 | Tree children; |
| 112 | Condition guest_cond; | 115 | IR::Condition guest_cond; |
| 113 | Statement* op; | 116 | Statement* op; |
| 114 | Statement* op_a; | 117 | Statement* op_a; |
| 115 | }; | 118 | }; |
| @@ -269,9 +272,10 @@ bool SearchNode(const Tree& tree, ConstNode stmt, size_t& offset) { | |||
| 269 | 272 | ||
| 270 | class GotoPass { | 273 | class GotoPass { |
| 271 | public: | 274 | public: |
| 272 | explicit GotoPass(std::span<Block* const> blocks, ObjectPool<Statement>& stmt_pool) | 275 | explicit GotoPass(Flow::CFG& cfg, ObjectPool<IR::Inst>& inst_pool_, |
| 273 | : pool{stmt_pool} { | 276 | ObjectPool<IR::Block>& block_pool_, ObjectPool<Statement>& stmt_pool) |
| 274 | std::vector gotos{BuildUnorderedTreeGetGotos(blocks)}; | 277 | : inst_pool{inst_pool_}, block_pool{block_pool_}, pool{stmt_pool} { |
| 278 | std::vector gotos{BuildTree(cfg)}; | ||
| 275 | for (const Node& goto_stmt : gotos | std::views::reverse) { | 279 | for (const Node& goto_stmt : gotos | std::views::reverse) { |
| 276 | RemoveGoto(goto_stmt); | 280 | RemoveGoto(goto_stmt); |
| 277 | } | 281 | } |
| @@ -316,18 +320,20 @@ private: | |||
| 316 | } | 320 | } |
| 317 | } | 321 | } |
| 318 | // TODO: Remove this | 322 | // TODO: Remove this |
| 319 | Node it{goto_stmt}; | 323 | { |
| 320 | bool sibling{false}; | 324 | Node it{goto_stmt}; |
| 321 | do { | 325 | bool sibling{false}; |
| 322 | sibling |= it == label_stmt; | 326 | do { |
| 323 | --it; | 327 | sibling |= it == label_stmt; |
| 324 | } while (it != goto_stmt->up->children.begin()); | 328 | --it; |
| 325 | while (it != goto_stmt->up->children.end()) { | 329 | } while (it != goto_stmt->up->children.begin()); |
| 326 | sibling |= it == label_stmt; | 330 | while (it != goto_stmt->up->children.end()) { |
| 327 | ++it; | 331 | sibling |= it == label_stmt; |
| 328 | } | 332 | ++it; |
| 329 | if (!sibling) { | 333 | } |
| 330 | throw LogicError("Not siblings"); | 334 | if (!sibling) { |
| 335 | throw LogicError("Not siblings"); | ||
| 336 | } | ||
| 331 | } | 337 | } |
| 332 | // goto_stmt and label_stmt are guaranteed to be siblings, eliminate | 338 | // goto_stmt and label_stmt are guaranteed to be siblings, eliminate |
| 333 | if (std::next(goto_stmt) == label_stmt) { | 339 | if (std::next(goto_stmt) == label_stmt) { |
| @@ -342,63 +348,84 @@ private: | |||
| 342 | } | 348 | } |
| 343 | } | 349 | } |
| 344 | 350 | ||
| 345 | std::vector<Node> BuildUnorderedTreeGetGotos(std::span<Block* const> blocks) { | 351 | std::vector<Node> BuildTree(Flow::CFG& cfg) { |
| 346 | // Assume all blocks have two branches | 352 | u32 label_id{0}; |
| 347 | std::vector<Node> gotos; | 353 | std::vector<Node> gotos; |
| 348 | gotos.reserve(blocks.size() * 2); | 354 | Flow::Function& first_function{cfg.Functions().front()}; |
| 349 | 355 | BuildTree(cfg, first_function, label_id, gotos, root_stmt.children.end(), std::nullopt); | |
| 350 | const std::unordered_map labels_map{BuildLabels(blocks)}; | ||
| 351 | Tree& root{root_stmt.children}; | ||
| 352 | auto insert_point{root.begin()}; | ||
| 353 | // Skip all goto variables zero-initialization | ||
| 354 | std::advance(insert_point, labels_map.size()); | ||
| 355 | |||
| 356 | for (Block* const block : blocks) { | ||
| 357 | // Skip label | ||
| 358 | ++insert_point; | ||
| 359 | // Skip set variable | ||
| 360 | ++insert_point; | ||
| 361 | root.insert(insert_point, *pool.Create(block, &root_stmt)); | ||
| 362 | |||
| 363 | if (block->IsTerminationBlock()) { | ||
| 364 | root.insert(insert_point, *pool.Create(Return{})); | ||
| 365 | continue; | ||
| 366 | } | ||
| 367 | const Condition cond{block->BranchCondition()}; | ||
| 368 | Statement* const true_cond{pool.Create(Identity{}, Condition{true})}; | ||
| 369 | if (cond == Condition{true} || cond == Condition{false}) { | ||
| 370 | const bool is_true{cond == Condition{true}}; | ||
| 371 | const Block* const branch{is_true ? block->TrueBranch() : block->FalseBranch()}; | ||
| 372 | const Node label{labels_map.at(branch)}; | ||
| 373 | Statement* const goto_stmt{pool.Create(Goto{}, true_cond, label, &root_stmt)}; | ||
| 374 | gotos.push_back(root.insert(insert_point, *goto_stmt)); | ||
| 375 | } else { | ||
| 376 | Statement* const ident_cond{pool.Create(Identity{}, cond)}; | ||
| 377 | const Node true_label{labels_map.at(block->TrueBranch())}; | ||
| 378 | const Node false_label{labels_map.at(block->FalseBranch())}; | ||
| 379 | Statement* goto_true{pool.Create(Goto{}, ident_cond, true_label, &root_stmt)}; | ||
| 380 | Statement* goto_false{pool.Create(Goto{}, true_cond, false_label, &root_stmt)}; | ||
| 381 | gotos.push_back(root.insert(insert_point, *goto_true)); | ||
| 382 | gotos.push_back(root.insert(insert_point, *goto_false)); | ||
| 383 | } | ||
| 384 | } | ||
| 385 | return gotos; | 356 | return gotos; |
| 386 | } | 357 | } |
| 387 | 358 | ||
| 388 | std::unordered_map<const Block*, Node> BuildLabels(std::span<Block* const> blocks) { | 359 | void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id, |
| 389 | // TODO: Consider storing labels intrusively inside the block | 360 | std::vector<Node>& gotos, Node function_insert_point, |
| 390 | std::unordered_map<const Block*, Node> labels_map; | 361 | std::optional<Node> return_label) { |
| 362 | Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false})}; | ||
| 391 | Tree& root{root_stmt.children}; | 363 | Tree& root{root_stmt.children}; |
| 392 | u32 label_id{0}; | 364 | std::unordered_map<Flow::Block*, Node> local_labels; |
| 393 | for (const Block* const block : blocks) { | 365 | local_labels.reserve(function.blocks.size()); |
| 366 | |||
| 367 | for (Flow::Block& block : function.blocks) { | ||
| 394 | Statement* const label{pool.Create(Label{}, label_id, &root_stmt)}; | 368 | Statement* const label{pool.Create(Label{}, label_id, &root_stmt)}; |
| 395 | labels_map.emplace(block, root.insert(root.end(), *label)); | 369 | const Node label_it{root.insert(function_insert_point, *label)}; |
| 396 | Statement* const false_stmt{pool.Create(Identity{}, Condition{false})}; | 370 | local_labels.emplace(&block, label_it); |
| 397 | root.push_back(*pool.Create(SetVariable{}, label_id, false_stmt, &root_stmt)); | ||
| 398 | root.push_front(*pool.Create(SetVariable{}, label_id, false_stmt, &root_stmt)); | ||
| 399 | ++label_id; | 371 | ++label_id; |
| 400 | } | 372 | } |
| 401 | return labels_map; | 373 | for (Flow::Block& block : function.blocks) { |
| 374 | const Node label{local_labels.at(&block)}; | ||
| 375 | // Insertion point | ||
| 376 | const Node ip{std::next(label)}; | ||
| 377 | |||
| 378 | // Reset goto variables before the first block and after its respective label | ||
| 379 | const auto make_reset_variable{[&]() -> Statement& { | ||
| 380 | return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt); | ||
| 381 | }}; | ||
| 382 | root.push_front(make_reset_variable()); | ||
| 383 | root.insert(ip, make_reset_variable()); | ||
| 384 | |||
| 385 | const u32 begin_offset{block.begin.Offset()}; | ||
| 386 | const u32 end_offset{block.end.Offset()}; | ||
| 387 | IR::Block* const ir_block{block_pool.Create(inst_pool, begin_offset, end_offset)}; | ||
| 388 | root.insert(ip, *pool.Create(ir_block, &root_stmt)); | ||
| 389 | |||
| 390 | switch (block.end_class) { | ||
| 391 | case Flow::EndClass::Branch: { | ||
| 392 | Statement* const always_cond{pool.Create(Identity{}, IR::Condition{true})}; | ||
| 393 | if (block.cond == IR::Condition{true}) { | ||
| 394 | const Node true_label{local_labels.at(block.branch_true)}; | ||
| 395 | gotos.push_back( | ||
| 396 | root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt))); | ||
| 397 | } else if (block.cond == IR::Condition{false}) { | ||
| 398 | const Node false_label{local_labels.at(block.branch_false)}; | ||
| 399 | gotos.push_back(root.insert( | ||
| 400 | ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt))); | ||
| 401 | } else { | ||
| 402 | const Node true_label{local_labels.at(block.branch_true)}; | ||
| 403 | const Node false_label{local_labels.at(block.branch_false)}; | ||
| 404 | Statement* const true_cond{pool.Create(Identity{}, block.cond)}; | ||
| 405 | gotos.push_back( | ||
| 406 | root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt))); | ||
| 407 | gotos.push_back(root.insert( | ||
| 408 | ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt))); | ||
| 409 | } | ||
| 410 | break; | ||
| 411 | } | ||
| 412 | case Flow::EndClass::Call: { | ||
| 413 | Flow::Function& call{cfg.Functions()[block.function_call]}; | ||
| 414 | const Node call_return_label{local_labels.at(block.return_block)}; | ||
| 415 | BuildTree(cfg, call, label_id, gotos, ip, call_return_label); | ||
| 416 | break; | ||
| 417 | } | ||
| 418 | case Flow::EndClass::Exit: | ||
| 419 | root.insert(ip, *pool.Create(Return{})); | ||
| 420 | break; | ||
| 421 | case Flow::EndClass::Return: { | ||
| 422 | Statement* const always_cond{pool.Create(Identity{}, block.cond)}; | ||
| 423 | auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)}; | ||
| 424 | gotos.push_back(root.insert(ip, *goto_stmt)); | ||
| 425 | break; | ||
| 426 | } | ||
| 427 | } | ||
| 428 | } | ||
| 402 | } | 429 | } |
| 403 | 430 | ||
| 404 | void UpdateTreeUp(Statement* tree) { | 431 | void UpdateTreeUp(Statement* tree) { |
| @@ -556,11 +583,13 @@ private: | |||
| 556 | return offset; | 583 | return offset; |
| 557 | } | 584 | } |
| 558 | 585 | ||
| 586 | ObjectPool<IR::Inst>& inst_pool; | ||
| 587 | ObjectPool<IR::Block>& block_pool; | ||
| 559 | ObjectPool<Statement>& pool; | 588 | ObjectPool<Statement>& pool; |
| 560 | Statement root_stmt{FunctionTag{}}; | 589 | Statement root_stmt{FunctionTag{}}; |
| 561 | }; | 590 | }; |
| 562 | 591 | ||
| 563 | Block* TryFindForwardBlock(const Statement& stmt) { | 592 | IR::Block* TryFindForwardBlock(const Statement& stmt) { |
| 564 | const Tree& tree{stmt.up->children}; | 593 | const Tree& tree{stmt.up->children}; |
| 565 | const ConstNode end{tree.cend()}; | 594 | const ConstNode end{tree.cend()}; |
| 566 | ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))}; | 595 | ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))}; |
| @@ -573,12 +602,12 @@ Block* TryFindForwardBlock(const Statement& stmt) { | |||
| 573 | return nullptr; | 602 | return nullptr; |
| 574 | } | 603 | } |
| 575 | 604 | ||
| 576 | [[nodiscard]] U1 VisitExpr(IREmitter& ir, const Statement& stmt) { | 605 | [[nodiscard]] IR::U1 VisitExpr(IR::IREmitter& ir, const Statement& stmt) { |
| 577 | switch (stmt.type) { | 606 | switch (stmt.type) { |
| 578 | case StatementType::Identity: | 607 | case StatementType::Identity: |
| 579 | return ir.Condition(stmt.guest_cond); | 608 | return ir.Condition(stmt.guest_cond); |
| 580 | case StatementType::Not: | 609 | case StatementType::Not: |
| 581 | return ir.LogicalNot(U1{VisitExpr(ir, *stmt.op)}); | 610 | return ir.LogicalNot(IR::U1{VisitExpr(ir, *stmt.op)}); |
| 582 | case StatementType::Or: | 611 | case StatementType::Or: |
| 583 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); | 612 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); |
| 584 | case StatementType::Variable: | 613 | case StatementType::Variable: |
| @@ -590,18 +619,18 @@ Block* TryFindForwardBlock(const Statement& stmt) { | |||
| 590 | 619 | ||
| 591 | class TranslatePass { | 620 | class TranslatePass { |
| 592 | public: | 621 | public: |
| 593 | TranslatePass(ObjectPool<Inst>& inst_pool_, ObjectPool<Block>& block_pool_, | 622 | TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_, |
| 594 | ObjectPool<Statement>& stmt_pool_, Statement& root_stmt, | 623 | ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt, |
| 595 | const std::function<void(IR::Block*)>& func_, BlockList& block_list_) | 624 | IR::BlockList& block_list_) |
| 596 | : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, func{func_}, | 625 | : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_}, |
| 597 | block_list{block_list_} { | 626 | block_list{block_list_} { |
| 598 | Visit(root_stmt, nullptr, nullptr); | 627 | Visit(root_stmt, nullptr, nullptr); |
| 599 | } | 628 | } |
| 600 | 629 | ||
| 601 | private: | 630 | private: |
| 602 | void Visit(Statement& parent, Block* continue_block, Block* break_block) { | 631 | void Visit(Statement& parent, IR::Block* continue_block, IR::Block* break_block) { |
| 603 | Tree& tree{parent.children}; | 632 | Tree& tree{parent.children}; |
| 604 | Block* current_block{nullptr}; | 633 | IR::Block* current_block{nullptr}; |
| 605 | 634 | ||
| 606 | for (auto it = tree.begin(); it != tree.end(); ++it) { | 635 | for (auto it = tree.begin(); it != tree.end(); ++it) { |
| 607 | Statement& stmt{*it}; | 636 | Statement& stmt{*it}; |
| @@ -611,11 +640,10 @@ private: | |||
| 611 | break; | 640 | break; |
| 612 | case StatementType::Code: { | 641 | case StatementType::Code: { |
| 613 | if (current_block && current_block != stmt.code) { | 642 | if (current_block && current_block != stmt.code) { |
| 614 | IREmitter ir{*current_block}; | 643 | IR::IREmitter{*current_block}.Branch(stmt.code); |
| 615 | ir.Branch(stmt.code); | ||
| 616 | } | 644 | } |
| 617 | current_block = stmt.code; | 645 | current_block = stmt.code; |
| 618 | func(stmt.code); | 646 | Translate(env, stmt.code); |
| 619 | block_list.push_back(stmt.code); | 647 | block_list.push_back(stmt.code); |
| 620 | break; | 648 | break; |
| 621 | } | 649 | } |
| @@ -623,7 +651,7 @@ private: | |||
| 623 | if (!current_block) { | 651 | if (!current_block) { |
| 624 | current_block = MergeBlock(parent, stmt); | 652 | current_block = MergeBlock(parent, stmt); |
| 625 | } | 653 | } |
| 626 | IREmitter ir{*current_block}; | 654 | IR::IREmitter ir{*current_block}; |
| 627 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); | 655 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); |
| 628 | break; | 656 | break; |
| 629 | } | 657 | } |
| @@ -632,16 +660,16 @@ private: | |||
| 632 | current_block = block_pool.Create(inst_pool); | 660 | current_block = block_pool.Create(inst_pool); |
| 633 | block_list.push_back(current_block); | 661 | block_list.push_back(current_block); |
| 634 | } | 662 | } |
| 635 | Block* const merge_block{MergeBlock(parent, stmt)}; | 663 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; |
| 636 | 664 | ||
| 637 | // Visit children | 665 | // Visit children |
| 638 | const size_t first_block_index{block_list.size()}; | 666 | const size_t first_block_index{block_list.size()}; |
| 639 | Visit(stmt, merge_block, break_block); | 667 | Visit(stmt, merge_block, break_block); |
| 640 | 668 | ||
| 641 | // Implement if header block | 669 | // Implement if header block |
| 642 | Block* const first_if_block{block_list.at(first_block_index)}; | 670 | IR::Block* const first_if_block{block_list.at(first_block_index)}; |
| 643 | IREmitter ir{*current_block}; | 671 | IR::IREmitter ir{*current_block}; |
| 644 | const U1 cond{VisitExpr(ir, *stmt.cond)}; | 672 | const IR::U1 cond{VisitExpr(ir, *stmt.cond)}; |
| 645 | ir.SelectionMerge(merge_block); | 673 | ir.SelectionMerge(merge_block); |
| 646 | ir.BranchConditional(cond, first_if_block, merge_block); | 674 | ir.BranchConditional(cond, first_if_block, merge_block); |
| 647 | 675 | ||
| @@ -649,14 +677,14 @@ private: | |||
| 649 | break; | 677 | break; |
| 650 | } | 678 | } |
| 651 | case StatementType::Loop: { | 679 | case StatementType::Loop: { |
| 652 | Block* const loop_header_block{block_pool.Create(inst_pool)}; | 680 | IR::Block* const loop_header_block{block_pool.Create(inst_pool)}; |
| 653 | if (current_block) { | 681 | if (current_block) { |
| 654 | IREmitter{*current_block}.Branch(loop_header_block); | 682 | IR::IREmitter{*current_block}.Branch(loop_header_block); |
| 655 | } | 683 | } |
| 656 | block_list.push_back(loop_header_block); | 684 | block_list.push_back(loop_header_block); |
| 657 | 685 | ||
| 658 | Block* const new_continue_block{block_pool.Create(inst_pool)}; | 686 | IR::Block* const new_continue_block{block_pool.Create(inst_pool)}; |
| 659 | Block* const merge_block{MergeBlock(parent, stmt)}; | 687 | IR::Block* const merge_block{MergeBlock(parent, stmt)}; |
| 660 | 688 | ||
| 661 | // Visit children | 689 | // Visit children |
| 662 | const size_t first_block_index{block_list.size()}; | 690 | const size_t first_block_index{block_list.size()}; |
| @@ -666,14 +694,14 @@ private: | |||
| 666 | block_list.push_back(new_continue_block); | 694 | block_list.push_back(new_continue_block); |
| 667 | 695 | ||
| 668 | // Implement loop header block | 696 | // Implement loop header block |
| 669 | Block* const first_loop_block{block_list.at(first_block_index)}; | 697 | IR::Block* const first_loop_block{block_list.at(first_block_index)}; |
| 670 | IREmitter ir{*loop_header_block}; | 698 | IR::IREmitter ir{*loop_header_block}; |
| 671 | ir.LoopMerge(merge_block, new_continue_block); | 699 | ir.LoopMerge(merge_block, new_continue_block); |
| 672 | ir.Branch(first_loop_block); | 700 | ir.Branch(first_loop_block); |
| 673 | 701 | ||
| 674 | // Implement continue block | 702 | // Implement continue block |
| 675 | IREmitter continue_ir{*new_continue_block}; | 703 | IR::IREmitter continue_ir{*new_continue_block}; |
| 676 | const U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)}; | 704 | const IR::U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)}; |
| 677 | continue_ir.BranchConditional(continue_cond, ir.block, merge_block); | 705 | continue_ir.BranchConditional(continue_cond, ir.block, merge_block); |
| 678 | 706 | ||
| 679 | current_block = merge_block; | 707 | current_block = merge_block; |
| @@ -684,9 +712,9 @@ private: | |||
| 684 | current_block = block_pool.Create(inst_pool); | 712 | current_block = block_pool.Create(inst_pool); |
| 685 | block_list.push_back(current_block); | 713 | block_list.push_back(current_block); |
| 686 | } | 714 | } |
| 687 | Block* const skip_block{MergeBlock(parent, stmt)}; | 715 | IR::Block* const skip_block{MergeBlock(parent, stmt)}; |
| 688 | 716 | ||
| 689 | IREmitter ir{*current_block}; | 717 | IR::IREmitter ir{*current_block}; |
| 690 | ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block); | 718 | ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block); |
| 691 | 719 | ||
| 692 | current_block = skip_block; | 720 | current_block = skip_block; |
| @@ -697,7 +725,7 @@ private: | |||
| 697 | current_block = block_pool.Create(inst_pool); | 725 | current_block = block_pool.Create(inst_pool); |
| 698 | block_list.push_back(current_block); | 726 | block_list.push_back(current_block); |
| 699 | } | 727 | } |
| 700 | IREmitter{*current_block}.Return(); | 728 | IR::IREmitter{*current_block}.Return(); |
| 701 | current_block = nullptr; | 729 | current_block = nullptr; |
| 702 | break; | 730 | break; |
| 703 | } | 731 | } |
| @@ -706,39 +734,37 @@ private: | |||
| 706 | } | 734 | } |
| 707 | } | 735 | } |
| 708 | if (current_block && continue_block) { | 736 | if (current_block && continue_block) { |
| 709 | IREmitter ir{*current_block}; | 737 | IR::IREmitter{*current_block}.Branch(continue_block); |
| 710 | ir.Branch(continue_block); | ||
| 711 | } | 738 | } |
| 712 | } | 739 | } |
| 713 | 740 | ||
| 714 | Block* MergeBlock(Statement& parent, Statement& stmt) { | 741 | IR::Block* MergeBlock(Statement& parent, Statement& stmt) { |
| 715 | if (Block* const block{TryFindForwardBlock(stmt)}) { | 742 | if (IR::Block* const block{TryFindForwardBlock(stmt)}) { |
| 716 | return block; | 743 | return block; |
| 717 | } | 744 | } |
| 718 | // Create a merge block we can visit later | 745 | // Create a merge block we can visit later |
| 719 | Block* const block{block_pool.Create(inst_pool)}; | 746 | IR::Block* const block{block_pool.Create(inst_pool)}; |
| 720 | Statement* const merge_stmt{stmt_pool.Create(block, &parent)}; | 747 | Statement* const merge_stmt{stmt_pool.Create(block, &parent)}; |
| 721 | parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt); | 748 | parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt); |
| 722 | return block; | 749 | return block; |
| 723 | } | 750 | } |
| 724 | 751 | ||
| 725 | ObjectPool<Statement>& stmt_pool; | 752 | ObjectPool<Statement>& stmt_pool; |
| 726 | ObjectPool<Inst>& inst_pool; | 753 | ObjectPool<IR::Inst>& inst_pool; |
| 727 | ObjectPool<Block>& block_pool; | 754 | ObjectPool<IR::Block>& block_pool; |
| 728 | const std::function<void(IR::Block*)>& func; | 755 | Environment& env; |
| 729 | BlockList& block_list; | 756 | IR::BlockList& block_list; |
| 730 | }; | 757 | }; |
| 731 | } // Anonymous namespace | 758 | } // Anonymous namespace |
| 732 | 759 | ||
| 733 | BlockList VisitAST(ObjectPool<Inst>& inst_pool, ObjectPool<Block>& block_pool, | 760 | IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool, |
| 734 | std::span<Block* const> unordered_blocks, | 761 | Environment& env, Flow::CFG& cfg) { |
| 735 | const std::function<void(Block*)>& func) { | ||
| 736 | ObjectPool<Statement> stmt_pool{64}; | 762 | ObjectPool<Statement> stmt_pool{64}; |
| 737 | GotoPass goto_pass{unordered_blocks, stmt_pool}; | 763 | GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool}; |
| 738 | BlockList block_list; | 764 | Statement& root{goto_pass.RootStatement()}; |
| 739 | TranslatePass translate_pass{inst_pool, block_pool, stmt_pool, goto_pass.RootStatement(), | 765 | IR::BlockList block_list; |
| 740 | func, block_list}; | 766 | TranslatePass{inst_pool, block_pool, stmt_pool, env, root, block_list}; |
| 741 | return block_list; | 767 | return block_list; |
| 742 | } | 768 | } |
| 743 | 769 | ||
| 744 | } // namespace Shader::IR | 770 | } // namespace Shader::Maxwell |
diff --git a/src/shader_recompiler/frontend/ir/structured_control_flow.h b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h index a574c24f7..e4797291e 100644 --- a/src/shader_recompiler/frontend/ir/structured_control_flow.h +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.h | |||
| @@ -9,14 +9,16 @@ | |||
| 9 | 9 | ||
| 10 | #include <boost/intrusive/list.hpp> | 10 | #include <boost/intrusive/list.hpp> |
| 11 | 11 | ||
| 12 | #include "shader_recompiler/environment.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/basic_block.h" | 13 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 13 | #include "shader_recompiler/frontend/ir/microinstruction.h" | 14 | #include "shader_recompiler/frontend/ir/microinstruction.h" |
| 15 | #include "shader_recompiler/frontend/maxwell/control_flow.h" | ||
| 14 | #include "shader_recompiler/object_pool.h" | 16 | #include "shader_recompiler/object_pool.h" |
| 15 | 17 | ||
| 16 | namespace Shader::IR { | 18 | namespace Shader::Maxwell { |
| 17 | 19 | ||
| 18 | [[nodiscard]] BlockList VisitAST(ObjectPool<Inst>& inst_pool, ObjectPool<Block>& block_pool, | 20 | [[nodiscard]] IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, |
| 19 | std::span<Block* const> unordered_blocks, | 21 | ObjectPool<IR::Block>& block_pool, Environment& env, |
| 20 | const std::function<void(Block*)>& func); | 22 | Flow::CFG& cfg); |
| 21 | 23 | ||
| 22 | } // namespace Shader::IR | 24 | } // 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 c6253c40c..45d6f5e06 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/impl.h | |||
| @@ -62,7 +62,7 @@ public: | |||
| 62 | void BRA(u64 insn); | 62 | void BRA(u64 insn); |
| 63 | void BRK(u64 insn); | 63 | void BRK(u64 insn); |
| 64 | void BRX(u64 insn); | 64 | void BRX(u64 insn); |
| 65 | void CAL(u64 insn); | 65 | void CAL(); |
| 66 | void CCTL(u64 insn); | 66 | void CCTL(u64 insn); |
| 67 | void CCTLL(u64 insn); | 67 | void CCTLL(u64 insn); |
| 68 | void CONT(u64 insn); | 68 | void CONT(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 01ecbb4cc..92da5c7e8 100644 --- a/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp +++ b/src/shader_recompiler/frontend/maxwell/translate/impl/not_implemented.cpp | |||
| @@ -65,8 +65,8 @@ void TranslatorVisitor::BRX(u64) { | |||
| 65 | ThrowNotImplemented(Opcode::BRX); | 65 | ThrowNotImplemented(Opcode::BRX); |
| 66 | } | 66 | } |
| 67 | 67 | ||
| 68 | void TranslatorVisitor::CAL(u64) { | 68 | void TranslatorVisitor::CAL() { |
| 69 | ThrowNotImplemented(Opcode::CAL); | 69 | // CAL is a no-op |
| 70 | } | 70 | } |
| 71 | 71 | ||
| 72 | void TranslatorVisitor::CCTL(u64) { | 72 | void TranslatorVisitor::CCTL(u64) { |
diff --git a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp index 70d75ad6c..708b6b267 100644 --- a/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp +++ b/src/shader_recompiler/ir_opt/collect_shader_info_pass.cpp | |||
| @@ -296,11 +296,9 @@ void Visit(Info& info, IR::Inst& inst) { | |||
| 296 | 296 | ||
| 297 | void CollectShaderInfoPass(IR::Program& program) { | 297 | void CollectShaderInfoPass(IR::Program& program) { |
| 298 | Info& info{program.info}; | 298 | Info& info{program.info}; |
| 299 | for (IR::Function& function : program.functions) { | 299 | for (IR::Block* const block : program.post_order_blocks) { |
| 300 | for (IR::Block* const block : function.post_order_blocks) { | 300 | for (IR::Inst& inst : block->Instructions()) { |
| 301 | for (IR::Inst& inst : block->Instructions()) { | 301 | Visit(info, inst); |
| 302 | Visit(info, inst); | ||
| 303 | } | ||
| 304 | } | 302 | } |
| 305 | } | 303 | } |
| 306 | } | 304 | } |
diff --git a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp index 7ba9ebe9b..a39db2bf1 100644 --- a/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp +++ b/src/shader_recompiler/ir_opt/constant_propagation_pass.cpp | |||
| @@ -371,9 +371,11 @@ void ConstantPropagation(IR::Block& block, IR::Inst& inst) { | |||
| 371 | } | 371 | } |
| 372 | } // Anonymous namespace | 372 | } // Anonymous namespace |
| 373 | 373 | ||
| 374 | void ConstantPropagationPass(IR::Block& block) { | 374 | void ConstantPropagationPass(IR::Program& program) { |
| 375 | for (IR::Inst& inst : block) { | 375 | for (IR::Block* const block : program.post_order_blocks) { |
| 376 | ConstantPropagation(block, inst); | 376 | for (IR::Inst& inst : block->Instructions()) { |
| 377 | ConstantPropagation(*block, inst); | ||
| 378 | } | ||
| 377 | } | 379 | } |
| 378 | } | 380 | } |
| 379 | 381 | ||
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 132b2012a..8ad59f42e 100644 --- a/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp +++ b/src/shader_recompiler/ir_opt/dead_code_elimination_pass.cpp | |||
| @@ -10,12 +10,14 @@ | |||
| 10 | 10 | ||
| 11 | namespace Shader::Optimization { | 11 | namespace Shader::Optimization { |
| 12 | 12 | ||
| 13 | void DeadCodeEliminationPass(IR::Block& block) { | 13 | void DeadCodeEliminationPass(IR::Program& program) { |
| 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 : block | std::views::reverse) { | 16 | for (IR::Block* const block : program.post_order_blocks) { |
| 17 | if (!inst.HasUses() && !inst.MayHaveSideEffects()) { | 17 | for (IR::Inst& inst : block->Instructions() | std::views::reverse) { |
| 18 | inst.Invalidate(); | 18 | if (!inst.HasUses() && !inst.MayHaveSideEffects()) { |
| 19 | inst.Invalidate(); | ||
| 20 | } | ||
| 19 | } | 21 | } |
| 20 | } | 22 | } |
| 21 | } | 23 | } |
diff --git a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp index 5d98d278e..1faa1ec88 100644 --- a/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp +++ b/src/shader_recompiler/ir_opt/global_memory_to_storage_buffer_pass.cpp | |||
| @@ -351,14 +351,12 @@ void GlobalMemoryToStorageBufferPass(IR::Program& program) { | |||
| 351 | StorageBufferSet storage_buffers; | 351 | StorageBufferSet storage_buffers; |
| 352 | StorageInstVector to_replace; | 352 | StorageInstVector to_replace; |
| 353 | 353 | ||
| 354 | for (IR::Function& function : program.functions) { | 354 | for (IR::Block* const block : program.post_order_blocks) { |
| 355 | for (IR::Block* const block : function.post_order_blocks) { | 355 | for (IR::Inst& inst : block->Instructions()) { |
| 356 | for (IR::Inst& inst : block->Instructions()) { | 356 | if (!IsGlobalMemory(inst)) { |
| 357 | if (!IsGlobalMemory(inst)) { | 357 | continue; |
| 358 | continue; | ||
| 359 | } | ||
| 360 | CollectStorageBuffers(*block, inst, storage_buffers, to_replace); | ||
| 361 | } | 358 | } |
| 359 | CollectStorageBuffers(*block, inst, storage_buffers, to_replace); | ||
| 362 | } | 360 | } |
| 363 | } | 361 | } |
| 364 | Info& info{program.info}; | 362 | Info& info{program.info}; |
diff --git a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp index 593efde39..8790b48f2 100644 --- a/src/shader_recompiler/ir_opt/identity_removal_pass.cpp +++ b/src/shader_recompiler/ir_opt/identity_removal_pass.cpp | |||
| @@ -10,10 +10,10 @@ | |||
| 10 | 10 | ||
| 11 | namespace Shader::Optimization { | 11 | namespace Shader::Optimization { |
| 12 | 12 | ||
| 13 | void IdentityRemovalPass(IR::Function& function) { | 13 | void IdentityRemovalPass(IR::Program& program) { |
| 14 | std::vector<IR::Inst*> to_invalidate; | 14 | std::vector<IR::Inst*> to_invalidate; |
| 15 | 15 | ||
| 16 | for (IR::Block* const block : function.blocks) { | 16 | for (IR::Block* const block : program.blocks) { |
| 17 | for (auto inst = block->begin(); inst != block->end();) { | 17 | for (auto inst = block->begin(); inst != block->end();) { |
| 18 | const size_t num_args{inst->NumArgs()}; | 18 | const size_t num_args{inst->NumArgs()}; |
| 19 | for (size_t i = 0; i < num_args; ++i) { | 19 | for (size_t i = 0; i < num_args; ++i) { |
diff --git a/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp b/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp index 14a5cb50f..74acb8bb6 100644 --- a/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp +++ b/src/shader_recompiler/ir_opt/lower_fp16_to_fp32.cpp | |||
| @@ -77,11 +77,9 @@ IR::Opcode Replace(IR::Opcode op) { | |||
| 77 | } // Anonymous namespace | 77 | } // Anonymous namespace |
| 78 | 78 | ||
| 79 | void LowerFp16ToFp32(IR::Program& program) { | 79 | void LowerFp16ToFp32(IR::Program& program) { |
| 80 | for (IR::Function& function : program.functions) { | 80 | for (IR::Block* const block : program.blocks) { |
| 81 | for (IR::Block* const block : function.blocks) { | 81 | for (IR::Inst& inst : block->Instructions()) { |
| 82 | for (IR::Inst& inst : block->Instructions()) { | 82 | inst.ReplaceOpcode(Replace(inst.Opcode())); |
| 83 | inst.ReplaceOpcode(Replace(inst.Opcode())); | ||
| 84 | } | ||
| 85 | } | 83 | } |
| 86 | } | 84 | } |
| 87 | } | 85 | } |
diff --git a/src/shader_recompiler/ir_opt/passes.h b/src/shader_recompiler/ir_opt/passes.h index 3b7e7306b..5c1fc166c 100644 --- a/src/shader_recompiler/ir_opt/passes.h +++ b/src/shader_recompiler/ir_opt/passes.h | |||
| @@ -8,26 +8,18 @@ | |||
| 8 | 8 | ||
| 9 | #include "shader_recompiler/environment.h" | 9 | #include "shader_recompiler/environment.h" |
| 10 | #include "shader_recompiler/frontend/ir/basic_block.h" | 10 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 11 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 12 | #include "shader_recompiler/frontend/ir/program.h" | 11 | #include "shader_recompiler/frontend/ir/program.h" |
| 13 | 12 | ||
| 14 | namespace Shader::Optimization { | 13 | namespace Shader::Optimization { |
| 15 | 14 | ||
| 16 | template <typename Func> | ||
| 17 | void PostOrderInvoke(Func&& func, IR::Function& function) { | ||
| 18 | for (const auto& block : function.post_order_blocks) { | ||
| 19 | func(*block); | ||
| 20 | } | ||
| 21 | } | ||
| 22 | |||
| 23 | void CollectShaderInfoPass(IR::Program& program); | 15 | void CollectShaderInfoPass(IR::Program& program); |
| 24 | void ConstantPropagationPass(IR::Block& block); | 16 | void ConstantPropagationPass(IR::Program& program); |
| 25 | void DeadCodeEliminationPass(IR::Block& block); | 17 | void DeadCodeEliminationPass(IR::Program& program); |
| 26 | void GlobalMemoryToStorageBufferPass(IR::Program& program); | 18 | void GlobalMemoryToStorageBufferPass(IR::Program& program); |
| 27 | void IdentityRemovalPass(IR::Function& function); | 19 | void IdentityRemovalPass(IR::Program& program); |
| 28 | void LowerFp16ToFp32(IR::Program& program); | 20 | void LowerFp16ToFp32(IR::Program& program); |
| 29 | void SsaRewritePass(std::span<IR::Block* const> post_order_blocks); | 21 | void SsaRewritePass(IR::Program& program); |
| 30 | void TexturePass(Environment& env, IR::Program& program); | 22 | void TexturePass(Environment& env, IR::Program& program); |
| 31 | void VerificationPass(const IR::Function& function); | 23 | void VerificationPass(const IR::Program& program); |
| 32 | 24 | ||
| 33 | } // 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 index 19d35b1f8..f89fd51c8 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -23,7 +23,6 @@ | |||
| 23 | #include <boost/container/flat_set.hpp> | 23 | #include <boost/container/flat_set.hpp> |
| 24 | 24 | ||
| 25 | #include "shader_recompiler/frontend/ir/basic_block.h" | 25 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 26 | #include "shader_recompiler/frontend/ir/function.h" | ||
| 27 | #include "shader_recompiler/frontend/ir/microinstruction.h" | 26 | #include "shader_recompiler/frontend/ir/microinstruction.h" |
| 28 | #include "shader_recompiler/frontend/ir/opcodes.h" | 27 | #include "shader_recompiler/frontend/ir/opcodes.h" |
| 29 | #include "shader_recompiler/frontend/ir/pred.h" | 28 | #include "shader_recompiler/frontend/ir/pred.h" |
| @@ -262,9 +261,9 @@ void VisitBlock(Pass& pass, IR::Block* block) { | |||
| 262 | } | 261 | } |
| 263 | } // Anonymous namespace | 262 | } // Anonymous namespace |
| 264 | 263 | ||
| 265 | void SsaRewritePass(std::span<IR::Block* const> post_order_blocks) { | 264 | void SsaRewritePass(IR::Program& program) { |
| 266 | Pass pass; | 265 | Pass pass; |
| 267 | for (IR::Block* const block : post_order_blocks | std::views::reverse) { | 266 | for (IR::Block* const block : program.post_order_blocks | std::views::reverse) { |
| 268 | VisitBlock(pass, block); | 267 | VisitBlock(pass, block); |
| 269 | } | 268 | } |
| 270 | } | 269 | } |
diff --git a/src/shader_recompiler/ir_opt/texture_pass.cpp b/src/shader_recompiler/ir_opt/texture_pass.cpp index ec802e02c..de9d633e2 100644 --- a/src/shader_recompiler/ir_opt/texture_pass.cpp +++ b/src/shader_recompiler/ir_opt/texture_pass.cpp | |||
| @@ -164,14 +164,12 @@ private: | |||
| 164 | 164 | ||
| 165 | void TexturePass(Environment& env, IR::Program& program) { | 165 | void TexturePass(Environment& env, IR::Program& program) { |
| 166 | TextureInstVector to_replace; | 166 | TextureInstVector to_replace; |
| 167 | for (IR::Function& function : program.functions) { | 167 | for (IR::Block* const block : program.post_order_blocks) { |
| 168 | for (IR::Block* const block : function.post_order_blocks) { | 168 | for (IR::Inst& inst : block->Instructions()) { |
| 169 | for (IR::Inst& inst : block->Instructions()) { | 169 | if (!IsTextureInstruction(inst)) { |
| 170 | if (!IsTextureInstruction(inst)) { | 170 | continue; |
| 171 | continue; | ||
| 172 | } | ||
| 173 | to_replace.push_back(MakeInst(env, block, inst)); | ||
| 174 | } | 171 | } |
| 172 | to_replace.push_back(MakeInst(env, block, inst)); | ||
| 175 | } | 173 | } |
| 176 | } | 174 | } |
| 177 | // Sort instructions to visit textures by constant buffer index, then by offset | 175 | // Sort instructions to visit textures by constant buffer index, then by offset |
diff --git a/src/shader_recompiler/ir_opt/verification_pass.cpp b/src/shader_recompiler/ir_opt/verification_pass.cpp index 32b56eb57..4080b37cc 100644 --- a/src/shader_recompiler/ir_opt/verification_pass.cpp +++ b/src/shader_recompiler/ir_opt/verification_pass.cpp | |||
| @@ -11,8 +11,8 @@ | |||
| 11 | 11 | ||
| 12 | namespace Shader::Optimization { | 12 | namespace Shader::Optimization { |
| 13 | 13 | ||
| 14 | static void ValidateTypes(const IR::Function& function) { | 14 | static void ValidateTypes(const IR::Program& program) { |
| 15 | for (const auto& block : function.blocks) { | 15 | for (const auto& block : program.blocks) { |
| 16 | for (const IR::Inst& inst : *block) { | 16 | for (const IR::Inst& inst : *block) { |
| 17 | if (inst.Opcode() == IR::Opcode::Phi) { | 17 | if (inst.Opcode() == IR::Opcode::Phi) { |
| 18 | // Skip validation on phi nodes | 18 | // Skip validation on phi nodes |
| @@ -30,9 +30,9 @@ static void ValidateTypes(const IR::Function& function) { | |||
| 30 | } | 30 | } |
| 31 | } | 31 | } |
| 32 | 32 | ||
| 33 | static void ValidateUses(const IR::Function& function) { | 33 | static void ValidateUses(const IR::Program& program) { |
| 34 | std::map<IR::Inst*, int> actual_uses; | 34 | std::map<IR::Inst*, int> actual_uses; |
| 35 | for (const auto& block : function.blocks) { | 35 | for (const auto& block : program.blocks) { |
| 36 | for (const IR::Inst& inst : *block) { | 36 | for (const IR::Inst& inst : *block) { |
| 37 | const size_t num_args{inst.NumArgs()}; | 37 | const size_t num_args{inst.NumArgs()}; |
| 38 | for (size_t i = 0; i < num_args; ++i) { | 38 | for (size_t i = 0; i < num_args; ++i) { |
| @@ -45,14 +45,14 @@ static void ValidateUses(const IR::Function& function) { | |||
| 45 | } | 45 | } |
| 46 | for (const auto [inst, uses] : actual_uses) { | 46 | for (const auto [inst, uses] : actual_uses) { |
| 47 | if (inst->UseCount() != uses) { | 47 | if (inst->UseCount() != uses) { |
| 48 | throw LogicError("Invalid uses in block:" /*, IR::DumpFunction(function)*/); | 48 | throw LogicError("Invalid uses in block: {}", IR::DumpProgram(program)); |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | } | 51 | } |
| 52 | 52 | ||
| 53 | void VerificationPass(const IR::Function& function) { | 53 | void VerificationPass(const IR::Program& program) { |
| 54 | ValidateTypes(function); | 54 | ValidateTypes(program); |
| 55 | ValidateUses(function); | 55 | ValidateUses(program); |
| 56 | } | 56 | } |
| 57 | 57 | ||
| 58 | } // namespace Shader::Optimization | 58 | } // namespace Shader::Optimization |