summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/maxwell/control_flow.cpp
diff options
context:
space:
mode:
authorGravatar ReinUsesLisp2021-02-02 21:07:00 -0300
committerGravatar ameerj2021-07-22 21:51:21 -0400
commit6c4cc0cd062fbbba5349da1108d3c23cb330ca8a (patch)
tree544291931da8a85fafcea71964c77d9278ec7f29 /src/shader_recompiler/frontend/maxwell/control_flow.cpp
parentshader: Initial recompiler work (diff)
downloadyuzu-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.cpp130
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
111static std::string Name(const Block& block) { 113static 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
156Function::Function(Location start_address) 158Function::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
165void 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] = &block;
171 }
172}
173
174void 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
185void 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
219void 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
254void 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
163CFG::CFG(Environment& env_, Location start_address) : env{env_} { 267CFG::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
279void 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 }