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/frontend/maxwell/control_flow.cpp | |
| 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/frontend/maxwell/control_flow.cpp')
| -rw-r--r-- | src/shader_recompiler/frontend/maxwell/control_flow.cpp | 78 |
1 files changed, 38 insertions, 40 deletions
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", |