diff options
| author | 2021-02-02 21:07:00 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:21 -0400 | |
| commit | 6c4cc0cd062fbbba5349da1108d3c23cb330ca8a (patch) | |
| tree | 544291931da8a85fafcea71964c77d9278ec7f29 /src/shader_recompiler/frontend/maxwell/control_flow.cpp | |
| parent | shader: Initial recompiler work (diff) | |
| download | yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.gz yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.tar.xz yuzu-6c4cc0cd062fbbba5349da1108d3c23cb330ca8a.zip | |
shader: SSA and dominance
Diffstat (limited to 'src/shader_recompiler/frontend/maxwell/control_flow.cpp')
| -rw-r--r-- | src/shader_recompiler/frontend/maxwell/control_flow.cpp | 130 |
1 files changed, 124 insertions, 6 deletions
diff --git a/src/shader_recompiler/frontend/maxwell/control_flow.cpp b/src/shader_recompiler/frontend/maxwell/control_flow.cpp index fc4dba826..21ee98137 100644 --- a/src/shader_recompiler/frontend/maxwell/control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/control_flow.cpp | |||
| @@ -36,6 +36,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) { | |||
| 36 | .cond{true}, | 36 | .cond{true}, |
| 37 | .branch_true{new_id}, | 37 | .branch_true{new_id}, |
| 38 | .branch_false{UNREACHABLE_BLOCK_ID}, | 38 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 39 | .imm_predecessors{}, | ||
| 39 | }, | 40 | }, |
| 40 | Block{ | 41 | Block{ |
| 41 | .begin{pc}, | 42 | .begin{pc}, |
| @@ -46,6 +47,7 @@ static std::array<Block, 2> Split(Block&& block, Location pc, BlockId new_id) { | |||
| 46 | .cond{block.cond}, | 47 | .cond{block.cond}, |
| 47 | .branch_true{block.branch_true}, | 48 | .branch_true{block.branch_true}, |
| 48 | .branch_false{block.branch_false}, | 49 | .branch_false{block.branch_false}, |
| 50 | .imm_predecessors{}, | ||
| 49 | }, | 51 | }, |
| 50 | }; | 52 | }; |
| 51 | } | 53 | } |
| @@ -108,7 +110,7 @@ static bool HasFlowTest(Opcode opcode) { | |||
| 108 | } | 110 | } |
| 109 | } | 111 | } |
| 110 | 112 | ||
| 111 | static std::string Name(const Block& block) { | 113 | static std::string NameOf(const Block& block) { |
| 112 | if (block.begin.IsVirtual()) { | 114 | if (block.begin.IsVirtual()) { |
| 113 | return fmt::format("\"Virtual {}\"", block.id); | 115 | return fmt::format("\"Virtual {}\"", block.id); |
| 114 | } else { | 116 | } else { |
| @@ -154,13 +156,127 @@ bool Block::Contains(Location pc) const noexcept { | |||
| 154 | } | 156 | } |
| 155 | 157 | ||
| 156 | Function::Function(Location start_address) | 158 | Function::Function(Location start_address) |
| 157 | : entrypoint{start_address}, labels{Label{ | 159 | : entrypoint{start_address}, labels{{ |
| 158 | .address{start_address}, | 160 | .address{start_address}, |
| 159 | .block_id{0}, | 161 | .block_id{0}, |
| 160 | .stack{}, | 162 | .stack{}, |
| 161 | }} {} | 163 | }} {} |
| 162 | 164 | ||
| 165 | void Function::BuildBlocksMap() { | ||
| 166 | const size_t num_blocks{NumBlocks()}; | ||
| 167 | blocks_map.resize(num_blocks); | ||
| 168 | for (size_t block_index = 0; block_index < num_blocks; ++block_index) { | ||
| 169 | Block& block{blocks_data[block_index]}; | ||
| 170 | blocks_map[block.id] = █ | ||
| 171 | } | ||
| 172 | } | ||
| 173 | |||
| 174 | void Function::BuildImmediatePredecessors() { | ||
| 175 | for (const Block& block : blocks_data) { | ||
| 176 | if (block.branch_true != UNREACHABLE_BLOCK_ID) { | ||
| 177 | blocks_map[block.branch_true]->imm_predecessors.push_back(block.id); | ||
| 178 | } | ||
| 179 | if (block.branch_false != UNREACHABLE_BLOCK_ID) { | ||
| 180 | blocks_map[block.branch_false]->imm_predecessors.push_back(block.id); | ||
| 181 | } | ||
| 182 | } | ||
| 183 | } | ||
| 184 | |||
| 185 | void Function::BuildPostOrder() { | ||
| 186 | boost::container::small_vector<BlockId, 0x110> block_stack; | ||
| 187 | post_order_map.resize(NumBlocks()); | ||
| 188 | |||
| 189 | Block& first_block{blocks_data[blocks.front()]}; | ||
| 190 | first_block.post_order_visited = true; | ||
| 191 | block_stack.push_back(first_block.id); | ||
| 192 | |||
| 193 | const auto visit_branch = [&](BlockId block_id, BlockId branch_id) { | ||
| 194 | if (branch_id == UNREACHABLE_BLOCK_ID) { | ||
| 195 | return false; | ||
| 196 | } | ||
| 197 | if (blocks_map[branch_id]->post_order_visited) { | ||
| 198 | return false; | ||
| 199 | } | ||
| 200 | blocks_map[branch_id]->post_order_visited = true; | ||
| 201 | |||
| 202 | // Calling push_back twice is faster than insert on msvc | ||
| 203 | block_stack.push_back(block_id); | ||
| 204 | block_stack.push_back(branch_id); | ||
| 205 | return true; | ||
| 206 | }; | ||
| 207 | while (!block_stack.empty()) { | ||
| 208 | const Block* const block{blocks_map[block_stack.back()]}; | ||
| 209 | block_stack.pop_back(); | ||
| 210 | |||
| 211 | if (!visit_branch(block->id, block->branch_true) && | ||
| 212 | !visit_branch(block->id, block->branch_false)) { | ||
| 213 | post_order_map[block->id] = static_cast<u32>(post_order_blocks.size()); | ||
| 214 | post_order_blocks.push_back(block->id); | ||
| 215 | } | ||
| 216 | } | ||
| 217 | } | ||
| 218 | |||
| 219 | void Function::BuildImmediateDominators() { | ||
| 220 | auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })}; | ||
| 221 | auto reverse_order_but_first{std::views::reverse | std::views::drop(1) | transform_block_id}; | ||
| 222 | auto has_idom{std::views::filter([](Block* block) { return block->imm_dominator; })}; | ||
| 223 | auto intersect{[this](Block* finger1, Block* finger2) { | ||
| 224 | while (finger1 != finger2) { | ||
| 225 | while (post_order_map[finger1->id] < post_order_map[finger2->id]) { | ||
| 226 | finger1 = finger1->imm_dominator; | ||
| 227 | } | ||
| 228 | while (post_order_map[finger2->id] < post_order_map[finger1->id]) { | ||
| 229 | finger2 = finger2->imm_dominator; | ||
| 230 | } | ||
| 231 | } | ||
| 232 | return finger1; | ||
| 233 | }}; | ||
| 234 | for (Block& block : blocks_data) { | ||
| 235 | block.imm_dominator = nullptr; | ||
| 236 | } | ||
| 237 | Block* const start_block{&blocks_data[blocks.front()]}; | ||
| 238 | start_block->imm_dominator = start_block; | ||
| 239 | |||
| 240 | bool changed{true}; | ||
| 241 | while (changed) { | ||
| 242 | changed = false; | ||
| 243 | for (Block* const block : post_order_blocks | reverse_order_but_first) { | ||
| 244 | Block* new_idom{}; | ||
| 245 | for (Block* predecessor : block->imm_predecessors | transform_block_id | has_idom) { | ||
| 246 | new_idom = new_idom ? intersect(predecessor, new_idom) : predecessor; | ||
| 247 | } | ||
| 248 | changed |= block->imm_dominator != new_idom; | ||
| 249 | block->imm_dominator = new_idom; | ||
| 250 | } | ||
| 251 | } | ||
| 252 | } | ||
| 253 | |||
| 254 | void Function::BuildDominanceFrontier() { | ||
| 255 | auto transform_block_id{std::views::transform([this](BlockId id) { return blocks_map[id]; })}; | ||
| 256 | auto has_enough_predecessors{[](Block& block) { return block.imm_predecessors.size() >= 2; }}; | ||
| 257 | for (Block& block : blocks_data | std::views::filter(has_enough_predecessors)) { | ||
| 258 | for (Block* current : block.imm_predecessors | transform_block_id) { | ||
| 259 | while (current != block.imm_dominator) { | ||
| 260 | current->dominance_frontiers.push_back(current->id); | ||
| 261 | current = current->imm_dominator; | ||
| 262 | } | ||
| 263 | } | ||
| 264 | } | ||
| 265 | } | ||
| 266 | |||
| 163 | CFG::CFG(Environment& env_, Location start_address) : env{env_} { | 267 | CFG::CFG(Environment& env_, Location start_address) : env{env_} { |
| 268 | VisitFunctions(start_address); | ||
| 269 | |||
| 270 | for (Function& function : functions) { | ||
| 271 | function.BuildBlocksMap(); | ||
| 272 | function.BuildImmediatePredecessors(); | ||
| 273 | function.BuildPostOrder(); | ||
| 274 | function.BuildImmediateDominators(); | ||
| 275 | function.BuildDominanceFrontier(); | ||
| 276 | } | ||
| 277 | } | ||
| 278 | |||
| 279 | void CFG::VisitFunctions(Location start_address) { | ||
| 164 | functions.emplace_back(start_address); | 280 | functions.emplace_back(start_address); |
| 165 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { | 281 | for (FunctionId function_id = 0; function_id < functions.size(); ++function_id) { |
| 166 | while (!functions[function_id].labels.empty()) { | 282 | while (!functions[function_id].labels.empty()) { |
| @@ -202,6 +318,7 @@ void CFG::AnalyzeLabel(FunctionId function_id, Label& label) { | |||
| 202 | .cond{true}, | 318 | .cond{true}, |
| 203 | .branch_true{UNREACHABLE_BLOCK_ID}, | 319 | .branch_true{UNREACHABLE_BLOCK_ID}, |
| 204 | .branch_false{UNREACHABLE_BLOCK_ID}, | 320 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 321 | .imm_predecessors{}, | ||
| 205 | }; | 322 | }; |
| 206 | // Analyze instructions until it reaches an already visited block or there's a branch | 323 | // Analyze instructions until it reaches an already visited block or there's a branch |
| 207 | bool is_branch{false}; | 324 | bool is_branch{false}; |
| @@ -310,7 +427,7 @@ CFG::AnalysisState CFG::AnalyzeInst(Block& block, FunctionId function_id, Locati | |||
| 310 | // Technically CAL pushes into PRET, but that's implicit in the function call for us | 427 | // Technically CAL pushes into PRET, but that's implicit in the function call for us |
| 311 | // Insert the function into the list if it doesn't exist | 428 | // Insert the function into the list if it doesn't exist |
| 312 | if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { | 429 | if (std::ranges::find(functions, cal_pc, &Function::entrypoint) == functions.end()) { |
| 313 | functions.push_back(cal_pc); | 430 | functions.emplace_back(cal_pc); |
| 314 | } | 431 | } |
| 315 | // Handle CAL like a regular instruction | 432 | // Handle CAL like a regular instruction |
| 316 | break; | 433 | break; |
| @@ -352,6 +469,7 @@ void CFG::AnalyzeCondInst(Block& block, FunctionId function_id, Location pc, | |||
| 352 | .cond{cond}, | 469 | .cond{cond}, |
| 353 | .branch_true{conditional_block_id}, | 470 | .branch_true{conditional_block_id}, |
| 354 | .branch_false{UNREACHABLE_BLOCK_ID}, | 471 | .branch_false{UNREACHABLE_BLOCK_ID}, |
| 472 | .imm_predecessors{}, | ||
| 355 | })}; | 473 | })}; |
| 356 | // Set the end properties of the conditional instruction and give it a new identity | 474 | // Set the end properties of the conditional instruction and give it a new identity |
| 357 | Block& conditional_block{block}; | 475 | Block& conditional_block{block}; |
| @@ -465,14 +583,14 @@ std::string CFG::Dot() const { | |||
| 465 | dot += fmt::format("\t\tnode [style=filled];\n"); | 583 | dot += fmt::format("\t\tnode [style=filled];\n"); |
| 466 | for (const u32 block_index : function.blocks) { | 584 | for (const u32 block_index : function.blocks) { |
| 467 | const Block& block{function.blocks_data[block_index]}; | 585 | const Block& block{function.blocks_data[block_index]}; |
| 468 | const std::string name{Name(block)}; | 586 | const std::string name{NameOf(block)}; |
| 469 | const auto add_branch = [&](BlockId branch_id, bool add_label) { | 587 | const auto add_branch = [&](BlockId branch_id, bool add_label) { |
| 470 | const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; | 588 | const auto it{std::ranges::find(function.blocks_data, branch_id, &Block::id)}; |
| 471 | dot += fmt::format("\t\t{}->", name); | 589 | dot += fmt::format("\t\t{}->", name); |
| 472 | if (it == function.blocks_data.end()) { | 590 | if (it == function.blocks_data.end()) { |
| 473 | dot += fmt::format("\"Unknown label {}\"", branch_id); | 591 | dot += fmt::format("\"Unknown label {}\"", branch_id); |
| 474 | } else { | 592 | } else { |
| 475 | dot += Name(*it); | 593 | dot += NameOf(*it); |
| 476 | }; | 594 | }; |
| 477 | if (add_label && block.cond != true && block.cond != false) { | 595 | if (add_label && block.cond != true && block.cond != false) { |
| 478 | dot += fmt::format(" [label=\"{}\"]", block.cond); | 596 | dot += fmt::format(" [label=\"{}\"]", block.cond); |
| @@ -520,7 +638,7 @@ std::string CFG::Dot() const { | |||
| 520 | if (functions.front().blocks.empty()) { | 638 | if (functions.front().blocks.empty()) { |
| 521 | dot += "Start;\n"; | 639 | dot += "Start;\n"; |
| 522 | } else { | 640 | } else { |
| 523 | dot += fmt::format("\tStart -> {};\n", Name(functions.front().blocks_data.front())); | 641 | dot += fmt::format("\tStart -> {};\n", NameOf(functions.front().blocks_data.front())); |
| 524 | } | 642 | } |
| 525 | dot += fmt::format("\tStart [shape=diamond];\n"); | 643 | dot += fmt::format("\tStart [shape=diamond];\n"); |
| 526 | } | 644 | } |