summaryrefslogtreecommitdiff
path: root/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
diff options
context:
space:
mode:
authorGravatar bunnei2021-07-25 11:39:04 -0700
committerGravatar GitHub2021-07-25 11:39:04 -0700
commit98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f (patch)
tree816faa96c2c4d291825063433331a8ea4b3d08f1 /src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
parentMerge pull request #6699 from lat9nq/common-threads (diff)
parentshader: Support out of bound local memory reads and immediate writes (diff)
downloadyuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.gz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.tar.xz
yuzu-98b26b6e126d4775fdf3f773fe8a8ac808a8ff8f.zip
Merge pull request #6585 from ameerj/hades
Shader Decompiler Rewrite
Diffstat (limited to 'src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp')
-rw-r--r--src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp883
1 files changed, 883 insertions, 0 deletions
diff --git a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
new file mode 100644
index 000000000..8b3e0a15c
--- /dev/null
+++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp
@@ -0,0 +1,883 @@
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 <algorithm>
6#include <memory>
7#include <string>
8#include <unordered_map>
9#include <utility>
10#include <vector>
11#include <version>
12
13#include <fmt/format.h>
14
15#include <boost/intrusive/list.hpp>
16
17#include "shader_recompiler/environment.h"
18#include "shader_recompiler/frontend/ir/basic_block.h"
19#include "shader_recompiler/frontend/ir/ir_emitter.h"
20#include "shader_recompiler/frontend/maxwell/decode.h"
21#include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
22#include "shader_recompiler/frontend/maxwell/translate/translate.h"
23#include "shader_recompiler/object_pool.h"
24
25namespace Shader::Maxwell {
26namespace {
27struct Statement;
28
29// Use normal_link because we are not guaranteed to destroy the tree in order
30using ListBaseHook =
31 boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
32
33using Tree = boost::intrusive::list<Statement,
34 // Allow using Statement without a definition
35 boost::intrusive::base_hook<ListBaseHook>,
36 // Avoid linear complexity on splice, size is never called
37 boost::intrusive::constant_time_size<false>>;
38using Node = Tree::iterator;
39
40enum class StatementType {
41 Code,
42 Goto,
43 Label,
44 If,
45 Loop,
46 Break,
47 Return,
48 Kill,
49 Unreachable,
50 Function,
51 Identity,
52 Not,
53 Or,
54 SetVariable,
55 SetIndirectBranchVariable,
56 Variable,
57 IndirectBranchCond,
58};
59
60bool HasChildren(StatementType type) {
61 switch (type) {
62 case StatementType::If:
63 case StatementType::Loop:
64 case StatementType::Function:
65 return true;
66 default:
67 return false;
68 }
69}
70
71struct Goto {};
72struct Label {};
73struct If {};
74struct Loop {};
75struct Break {};
76struct Return {};
77struct Kill {};
78struct Unreachable {};
79struct FunctionTag {};
80struct Identity {};
81struct Not {};
82struct Or {};
83struct SetVariable {};
84struct SetIndirectBranchVariable {};
85struct Variable {};
86struct IndirectBranchCond {};
87
88#ifdef _MSC_VER
89#pragma warning(push)
90#pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
91#endif
92struct Statement : ListBaseHook {
93 Statement(const Flow::Block* block_, Statement* up_)
94 : block{block_}, up{up_}, type{StatementType::Code} {}
95 Statement(Goto, Statement* cond_, Node label_, Statement* up_)
96 : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
97 Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
98 Statement(If, Statement* cond_, Tree&& children_, Statement* up_)
99 : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
100 Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_)
101 : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
102 Statement(Break, Statement* cond_, Statement* up_)
103 : cond{cond_}, up{up_}, type{StatementType::Break} {}
104 Statement(Return, Statement* up_) : up{up_}, type{StatementType::Return} {}
105 Statement(Kill, Statement* up_) : up{up_}, type{StatementType::Kill} {}
106 Statement(Unreachable, Statement* up_) : up{up_}, type{StatementType::Unreachable} {}
107 Statement(FunctionTag) : children{}, type{StatementType::Function} {}
108 Statement(Identity, IR::Condition cond_, Statement* up_)
109 : guest_cond{cond_}, up{up_}, type{StatementType::Identity} {}
110 Statement(Not, Statement* op_, Statement* up_) : op{op_}, up{up_}, type{StatementType::Not} {}
111 Statement(Or, Statement* op_a_, Statement* op_b_, Statement* up_)
112 : op_a{op_a_}, op_b{op_b_}, up{up_}, type{StatementType::Or} {}
113 Statement(SetVariable, u32 id_, Statement* op_, Statement* up_)
114 : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
115 Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_, Statement* up_)
116 : branch_offset{branch_offset_},
117 branch_reg{branch_reg_}, up{up_}, type{StatementType::SetIndirectBranchVariable} {}
118 Statement(Variable, u32 id_, Statement* up_)
119 : id{id_}, up{up_}, type{StatementType::Variable} {}
120 Statement(IndirectBranchCond, u32 location_, Statement* up_)
121 : location{location_}, up{up_}, type{StatementType::IndirectBranchCond} {}
122
123 ~Statement() {
124 if (HasChildren(type)) {
125 std::destroy_at(&children);
126 }
127 }
128
129 union {
130 const Flow::Block* block;
131 Node label;
132 Tree children;
133 IR::Condition guest_cond;
134 Statement* op;
135 Statement* op_a;
136 u32 location;
137 s32 branch_offset;
138 };
139 union {
140 Statement* cond;
141 Statement* op_b;
142 u32 id;
143 IR::Reg branch_reg;
144 };
145 Statement* up{};
146 StatementType type;
147};
148#ifdef _MSC_VER
149#pragma warning(pop)
150#endif
151
152std::string DumpExpr(const Statement* stmt) {
153 switch (stmt->type) {
154 case StatementType::Identity:
155 return fmt::format("{}", stmt->guest_cond);
156 case StatementType::Not:
157 return fmt::format("!{}", DumpExpr(stmt->op));
158 case StatementType::Or:
159 return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b));
160 case StatementType::Variable:
161 return fmt::format("goto_L{}", stmt->id);
162 case StatementType::IndirectBranchCond:
163 return fmt::format("(indirect_branch == {:x})", stmt->location);
164 default:
165 return "<invalid type>";
166 }
167}
168
169[[maybe_unused]] std::string DumpTree(const Tree& tree, u32 indentation = 0) {
170 std::string ret;
171 std::string indent(indentation, ' ');
172 for (auto stmt = tree.begin(); stmt != tree.end(); ++stmt) {
173 switch (stmt->type) {
174 case StatementType::Code:
175 ret += fmt::format("{} Block {:04x} -> {:04x} (0x{:016x});\n", indent,
176 stmt->block->begin.Offset(), stmt->block->end.Offset(),
177 reinterpret_cast<uintptr_t>(stmt->block));
178 break;
179 case StatementType::Goto:
180 ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond),
181 stmt->label->id);
182 break;
183 case StatementType::Label:
184 ret += fmt::format("{}L{}:\n", indent, stmt->id);
185 break;
186 case StatementType::If:
187 ret += fmt::format("{} if ({}) {{\n", indent, DumpExpr(stmt->cond));
188 ret += DumpTree(stmt->children, indentation + 4);
189 ret += fmt::format("{} }}\n", indent);
190 break;
191 case StatementType::Loop:
192 ret += fmt::format("{} do {{\n", indent);
193 ret += DumpTree(stmt->children, indentation + 4);
194 ret += fmt::format("{} }} while ({});\n", indent, DumpExpr(stmt->cond));
195 break;
196 case StatementType::Break:
197 ret += fmt::format("{} if ({}) break;\n", indent, DumpExpr(stmt->cond));
198 break;
199 case StatementType::Return:
200 ret += fmt::format("{} return;\n", indent);
201 break;
202 case StatementType::Kill:
203 ret += fmt::format("{} kill;\n", indent);
204 break;
205 case StatementType::Unreachable:
206 ret += fmt::format("{} unreachable;\n", indent);
207 break;
208 case StatementType::SetVariable:
209 ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op));
210 break;
211 case StatementType::SetIndirectBranchVariable:
212 ret += fmt::format("{} indirect_branch = {} + {};\n", indent, stmt->branch_reg,
213 stmt->branch_offset);
214 break;
215 case StatementType::Function:
216 case StatementType::Identity:
217 case StatementType::Not:
218 case StatementType::Or:
219 case StatementType::Variable:
220 case StatementType::IndirectBranchCond:
221 throw LogicError("Statement can't be printed");
222 }
223 }
224 return ret;
225}
226
227void SanitizeNoBreaks(const Tree& tree) {
228 if (std::ranges::find(tree, StatementType::Break, &Statement::type) != tree.end()) {
229 throw NotImplementedException("Capturing statement with break nodes");
230 }
231}
232
233size_t Level(Node stmt) {
234 size_t level{0};
235 Statement* node{stmt->up};
236 while (node) {
237 ++level;
238 node = node->up;
239 }
240 return level;
241}
242
243bool IsDirectlyRelated(Node goto_stmt, Node label_stmt) {
244 const size_t goto_level{Level(goto_stmt)};
245 const size_t label_level{Level(label_stmt)};
246 size_t min_level;
247 size_t max_level;
248 Node min;
249 Node max;
250 if (label_level < goto_level) {
251 min_level = label_level;
252 max_level = goto_level;
253 min = label_stmt;
254 max = goto_stmt;
255 } else { // goto_level < label_level
256 min_level = goto_level;
257 max_level = label_level;
258 min = goto_stmt;
259 max = label_stmt;
260 }
261 while (max_level > min_level) {
262 --max_level;
263 max = max->up;
264 }
265 return min->up == max->up;
266}
267
268bool IsIndirectlyRelated(Node goto_stmt, Node label_stmt) {
269 return goto_stmt->up != label_stmt->up && !IsDirectlyRelated(goto_stmt, label_stmt);
270}
271
272[[maybe_unused]] bool AreSiblings(Node goto_stmt, Node label_stmt) noexcept {
273 Node it{goto_stmt};
274 do {
275 if (it == label_stmt) {
276 return true;
277 }
278 --it;
279 } while (it != goto_stmt->up->children.begin());
280 while (it != goto_stmt->up->children.end()) {
281 if (it == label_stmt) {
282 return true;
283 }
284 ++it;
285 }
286 return false;
287}
288
289Node SiblingFromNephew(Node uncle, Node nephew) noexcept {
290 Statement* const parent{uncle->up};
291 Statement* it{&*nephew};
292 while (it->up != parent) {
293 it = it->up;
294 }
295 return Tree::s_iterator_to(*it);
296}
297
298bool AreOrdered(Node left_sibling, Node right_sibling) noexcept {
299 const Node end{right_sibling->up->children.end()};
300 for (auto it = right_sibling; it != end; ++it) {
301 if (it == left_sibling) {
302 return false;
303 }
304 }
305 return true;
306}
307
308bool NeedsLift(Node goto_stmt, Node label_stmt) noexcept {
309 const Node sibling{SiblingFromNephew(goto_stmt, label_stmt)};
310 return AreOrdered(sibling, goto_stmt);
311}
312
313class GotoPass {
314public:
315 explicit GotoPass(Flow::CFG& cfg, ObjectPool<Statement>& stmt_pool) : pool{stmt_pool} {
316 std::vector gotos{BuildTree(cfg)};
317 const auto end{gotos.rend()};
318 for (auto goto_stmt = gotos.rbegin(); goto_stmt != end; ++goto_stmt) {
319 RemoveGoto(*goto_stmt);
320 }
321 }
322
323 Statement& RootStatement() noexcept {
324 return root_stmt;
325 }
326
327private:
328 void RemoveGoto(Node goto_stmt) {
329 // Force goto_stmt and label_stmt to be directly related
330 const Node label_stmt{goto_stmt->label};
331 if (IsIndirectlyRelated(goto_stmt, label_stmt)) {
332 // Move goto_stmt out using outward-movement transformation until it becomes
333 // directly related to label_stmt
334 while (!IsDirectlyRelated(goto_stmt, label_stmt)) {
335 goto_stmt = MoveOutward(goto_stmt);
336 }
337 }
338 // Force goto_stmt and label_stmt to be siblings
339 if (IsDirectlyRelated(goto_stmt, label_stmt)) {
340 const size_t label_level{Level(label_stmt)};
341 size_t goto_level{Level(goto_stmt)};
342 if (goto_level > label_level) {
343 // Move goto_stmt out of its level using outward-movement transformations
344 while (goto_level > label_level) {
345 goto_stmt = MoveOutward(goto_stmt);
346 --goto_level;
347 }
348 } else { // Level(goto_stmt) < Level(label_stmt)
349 if (NeedsLift(goto_stmt, label_stmt)) {
350 // Lift goto_stmt to above stmt containing label_stmt using goto-lifting
351 // transformations
352 goto_stmt = Lift(goto_stmt);
353 }
354 // Move goto_stmt into label_stmt's level using inward-movement transformation
355 while (goto_level < label_level) {
356 goto_stmt = MoveInward(goto_stmt);
357 ++goto_level;
358 }
359 }
360 }
361 // Expensive operation:
362 // if (!AreSiblings(goto_stmt, label_stmt)) {
363 // throw LogicError("Goto is not a sibling with the label");
364 // }
365 // goto_stmt and label_stmt are guaranteed to be siblings, eliminate
366 if (std::next(goto_stmt) == label_stmt) {
367 // Simply eliminate the goto if the label is next to it
368 goto_stmt->up->children.erase(goto_stmt);
369 } else if (AreOrdered(goto_stmt, label_stmt)) {
370 // Eliminate goto_stmt with a conditional
371 EliminateAsConditional(goto_stmt, label_stmt);
372 } else {
373 // Eliminate goto_stmt with a loop
374 EliminateAsLoop(goto_stmt, label_stmt);
375 }
376 }
377
378 std::vector<Node> BuildTree(Flow::CFG& cfg) {
379 u32 label_id{0};
380 std::vector<Node> gotos;
381 Flow::Function& first_function{cfg.Functions().front()};
382 BuildTree(cfg, first_function, label_id, gotos, root_stmt.children.end(), std::nullopt);
383 return gotos;
384 }
385
386 void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id,
387 std::vector<Node>& gotos, Node function_insert_point,
388 std::optional<Node> return_label) {
389 Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false}, &root_stmt)};
390 Tree& root{root_stmt.children};
391 std::unordered_map<Flow::Block*, Node> local_labels;
392 local_labels.reserve(function.blocks.size());
393
394 for (Flow::Block& block : function.blocks) {
395 Statement* const label{pool.Create(Label{}, label_id, &root_stmt)};
396 const Node label_it{root.insert(function_insert_point, *label)};
397 local_labels.emplace(&block, label_it);
398 ++label_id;
399 }
400 for (Flow::Block& block : function.blocks) {
401 const Node label{local_labels.at(&block)};
402 // Insertion point
403 const Node ip{std::next(label)};
404
405 // Reset goto variables before the first block and after its respective label
406 const auto make_reset_variable{[&]() -> Statement& {
407 return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt);
408 }};
409 root.push_front(make_reset_variable());
410 root.insert(ip, make_reset_variable());
411 root.insert(ip, *pool.Create(&block, &root_stmt));
412
413 switch (block.end_class) {
414 case Flow::EndClass::Branch: {
415 Statement* const always_cond{
416 pool.Create(Identity{}, IR::Condition{true}, &root_stmt)};
417 if (block.cond == IR::Condition{true}) {
418 const Node true_label{local_labels.at(block.branch_true)};
419 gotos.push_back(
420 root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt)));
421 } else if (block.cond == IR::Condition{false}) {
422 const Node false_label{local_labels.at(block.branch_false)};
423 gotos.push_back(root.insert(
424 ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
425 } else {
426 const Node true_label{local_labels.at(block.branch_true)};
427 const Node false_label{local_labels.at(block.branch_false)};
428 Statement* const true_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
429 gotos.push_back(
430 root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt)));
431 gotos.push_back(root.insert(
432 ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
433 }
434 break;
435 }
436 case Flow::EndClass::IndirectBranch:
437 root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg,
438 block.branch_offset, &root_stmt));
439 for (const Flow::IndirectBranch& indirect : block.indirect_branches) {
440 const Node indirect_label{local_labels.at(indirect.block)};
441 Statement* cond{
442 pool.Create(IndirectBranchCond{}, indirect.address, &root_stmt)};
443 Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)};
444 gotos.push_back(root.insert(ip, *goto_stmt));
445 }
446 root.insert(ip, *pool.Create(Unreachable{}, &root_stmt));
447 break;
448 case Flow::EndClass::Call: {
449 Flow::Function& call{cfg.Functions()[block.function_call]};
450 const Node call_return_label{local_labels.at(block.return_block)};
451 BuildTree(cfg, call, label_id, gotos, ip, call_return_label);
452 break;
453 }
454 case Flow::EndClass::Exit:
455 root.insert(ip, *pool.Create(Return{}, &root_stmt));
456 break;
457 case Flow::EndClass::Return: {
458 Statement* const always_cond{pool.Create(Identity{}, block.cond, &root_stmt)};
459 auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)};
460 gotos.push_back(root.insert(ip, *goto_stmt));
461 break;
462 }
463 case Flow::EndClass::Kill:
464 root.insert(ip, *pool.Create(Kill{}, &root_stmt));
465 break;
466 }
467 }
468 }
469
470 void UpdateTreeUp(Statement* tree) {
471 for (Statement& stmt : tree->children) {
472 stmt.up = tree;
473 }
474 }
475
476 void EliminateAsConditional(Node goto_stmt, Node label_stmt) {
477 Tree& body{goto_stmt->up->children};
478 Tree if_body;
479 if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_stmt);
480 Statement* const cond{pool.Create(Not{}, goto_stmt->cond, &root_stmt)};
481 Statement* const if_stmt{pool.Create(If{}, cond, std::move(if_body), goto_stmt->up)};
482 UpdateTreeUp(if_stmt);
483 body.insert(goto_stmt, *if_stmt);
484 body.erase(goto_stmt);
485 }
486
487 void EliminateAsLoop(Node goto_stmt, Node label_stmt) {
488 Tree& body{goto_stmt->up->children};
489 Tree loop_body;
490 loop_body.splice(loop_body.begin(), body, label_stmt, goto_stmt);
491 Statement* const cond{goto_stmt->cond};
492 Statement* const loop{pool.Create(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
493 UpdateTreeUp(loop);
494 body.insert(goto_stmt, *loop);
495 body.erase(goto_stmt);
496 }
497
498 [[nodiscard]] Node MoveOutward(Node goto_stmt) {
499 switch (goto_stmt->up->type) {
500 case StatementType::If:
501 return MoveOutwardIf(goto_stmt);
502 case StatementType::Loop:
503 return MoveOutwardLoop(goto_stmt);
504 default:
505 throw LogicError("Invalid outward movement");
506 }
507 }
508
509 [[nodiscard]] Node MoveInward(Node goto_stmt) {
510 Statement* const parent{goto_stmt->up};
511 Tree& body{parent->children};
512 const Node label{goto_stmt->label};
513 const Node label_nested_stmt{SiblingFromNephew(goto_stmt, label)};
514 const u32 label_id{label->id};
515
516 Statement* const goto_cond{goto_stmt->cond};
517 Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
518 body.insert(goto_stmt, *set_var);
519
520 Tree if_body;
521 if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_nested_stmt);
522 Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
523 Statement* const neg_var{pool.Create(Not{}, variable, &root_stmt)};
524 if (!if_body.empty()) {
525 Statement* const if_stmt{pool.Create(If{}, neg_var, std::move(if_body), parent)};
526 UpdateTreeUp(if_stmt);
527 body.insert(goto_stmt, *if_stmt);
528 }
529 body.erase(goto_stmt);
530
531 switch (label_nested_stmt->type) {
532 case StatementType::If:
533 // Update nested if condition
534 label_nested_stmt->cond =
535 pool.Create(Or{}, variable, label_nested_stmt->cond, &root_stmt);
536 break;
537 case StatementType::Loop:
538 break;
539 default:
540 throw LogicError("Invalid inward movement");
541 }
542 Tree& nested_tree{label_nested_stmt->children};
543 Statement* const new_goto{pool.Create(Goto{}, variable, label, &*label_nested_stmt)};
544 return nested_tree.insert(nested_tree.begin(), *new_goto);
545 }
546
547 [[nodiscard]] Node Lift(Node goto_stmt) {
548 Statement* const parent{goto_stmt->up};
549 Tree& body{parent->children};
550 const Node label{goto_stmt->label};
551 const u32 label_id{label->id};
552 const Node label_nested_stmt{SiblingFromNephew(goto_stmt, label)};
553
554 Tree loop_body;
555 loop_body.splice(loop_body.begin(), body, label_nested_stmt, goto_stmt);
556 SanitizeNoBreaks(loop_body);
557 Statement* const variable{pool.Create(Variable{}, label_id, &root_stmt)};
558 Statement* const loop_stmt{pool.Create(Loop{}, variable, std::move(loop_body), parent)};
559 UpdateTreeUp(loop_stmt);
560 body.insert(goto_stmt, *loop_stmt);
561
562 Statement* const new_goto{pool.Create(Goto{}, variable, label, loop_stmt)};
563 loop_stmt->children.push_front(*new_goto);
564 const Node new_goto_node{loop_stmt->children.begin()};
565
566 Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
567 loop_stmt->children.push_back(*set_var);
568
569 body.erase(goto_stmt);
570 return new_goto_node;
571 }
572
573 Node MoveOutwardIf(Node goto_stmt) {
574 const Node parent{Tree::s_iterator_to(*goto_stmt->up)};
575 Tree& body{parent->children};
576 const u32 label_id{goto_stmt->label->id};
577 Statement* const goto_cond{goto_stmt->cond};
578 Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, &*parent)};
579 body.insert(goto_stmt, *set_goto_var);
580
581 Tree if_body;
582 if_body.splice(if_body.begin(), body, std::next(goto_stmt), body.end());
583 if_body.pop_front();
584 Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
585 Statement* const neg_cond{pool.Create(Not{}, cond, &root_stmt)};
586 Statement* const if_stmt{pool.Create(If{}, neg_cond, std::move(if_body), &*parent)};
587 UpdateTreeUp(if_stmt);
588 body.insert(goto_stmt, *if_stmt);
589
590 body.erase(goto_stmt);
591
592 Statement* const new_cond{pool.Create(Variable{}, label_id, &root_stmt)};
593 Statement* const new_goto{pool.Create(Goto{}, new_cond, goto_stmt->label, parent->up)};
594 Tree& parent_tree{parent->up->children};
595 return parent_tree.insert(std::next(parent), *new_goto);
596 }
597
598 Node MoveOutwardLoop(Node goto_stmt) {
599 Statement* const parent{goto_stmt->up};
600 Tree& body{parent->children};
601 const u32 label_id{goto_stmt->label->id};
602 Statement* const goto_cond{goto_stmt->cond};
603 Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
604 Statement* const cond{pool.Create(Variable{}, label_id, &root_stmt)};
605 Statement* const break_stmt{pool.Create(Break{}, cond, parent)};
606 body.insert(goto_stmt, *set_goto_var);
607 body.insert(goto_stmt, *break_stmt);
608 body.erase(goto_stmt);
609
610 const Node loop{Tree::s_iterator_to(*goto_stmt->up)};
611 Statement* const new_goto_cond{pool.Create(Variable{}, label_id, &root_stmt)};
612 Statement* const new_goto{pool.Create(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
613 Tree& parent_tree{loop->up->children};
614 return parent_tree.insert(std::next(loop), *new_goto);
615 }
616
617 ObjectPool<Statement>& pool;
618 Statement root_stmt{FunctionTag{}};
619};
620
621[[nodiscard]] Statement* TryFindForwardBlock(Statement& stmt) {
622 Tree& tree{stmt.up->children};
623 const Node end{tree.end()};
624 Node forward_node{std::next(Tree::s_iterator_to(stmt))};
625 while (forward_node != end && !HasChildren(forward_node->type)) {
626 if (forward_node->type == StatementType::Code) {
627 return &*forward_node;
628 }
629 ++forward_node;
630 }
631 return nullptr;
632}
633
634[[nodiscard]] IR::U1 VisitExpr(IR::IREmitter& ir, const Statement& stmt) {
635 switch (stmt.type) {
636 case StatementType::Identity:
637 return ir.Condition(stmt.guest_cond);
638 case StatementType::Not:
639 return ir.LogicalNot(IR::U1{VisitExpr(ir, *stmt.op)});
640 case StatementType::Or:
641 return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b));
642 case StatementType::Variable:
643 return ir.GetGotoVariable(stmt.id);
644 case StatementType::IndirectBranchCond:
645 return ir.IEqual(ir.GetIndirectBranchVariable(), ir.Imm32(stmt.location));
646 default:
647 throw NotImplementedException("Statement type {}", stmt.type);
648 }
649}
650
651class TranslatePass {
652public:
653 TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
654 ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
655 IR::AbstractSyntaxList& syntax_list_)
656 : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
657 syntax_list{syntax_list_} {
658 Visit(root_stmt, nullptr, nullptr);
659
660 IR::Block& first_block{*syntax_list.front().data.block};
661 IR::IREmitter ir(first_block, first_block.begin());
662 ir.Prologue();
663 }
664
665private:
666 void Visit(Statement& parent, IR::Block* break_block, IR::Block* fallthrough_block) {
667 IR::Block* current_block{};
668 const auto ensure_block{[&] {
669 if (current_block) {
670 return;
671 }
672 current_block = block_pool.Create(inst_pool);
673 auto& node{syntax_list.emplace_back()};
674 node.type = IR::AbstractSyntaxNode::Type::Block;
675 node.data.block = current_block;
676 }};
677 Tree& tree{parent.children};
678 for (auto it = tree.begin(); it != tree.end(); ++it) {
679 Statement& stmt{*it};
680 switch (stmt.type) {
681 case StatementType::Label:
682 // Labels can be ignored
683 break;
684 case StatementType::Code: {
685 ensure_block();
686 Translate(env, current_block, stmt.block->begin.Offset(), stmt.block->end.Offset());
687 break;
688 }
689 case StatementType::SetVariable: {
690 ensure_block();
691 IR::IREmitter ir{*current_block};
692 ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
693 break;
694 }
695 case StatementType::SetIndirectBranchVariable: {
696 ensure_block();
697 IR::IREmitter ir{*current_block};
698 IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))};
699 ir.SetIndirectBranchVariable(address);
700 break;
701 }
702 case StatementType::If: {
703 ensure_block();
704 IR::Block* const merge_block{MergeBlock(parent, stmt)};
705
706 // Implement if header block
707 IR::IREmitter ir{*current_block};
708 const IR::U1 cond{ir.ConditionRef(VisitExpr(ir, *stmt.cond))};
709
710 const size_t if_node_index{syntax_list.size()};
711 syntax_list.emplace_back();
712
713 // Visit children
714 const size_t then_block_index{syntax_list.size()};
715 Visit(stmt, break_block, merge_block);
716
717 IR::Block* const then_block{syntax_list.at(then_block_index).data.block};
718 current_block->AddBranch(then_block);
719 current_block->AddBranch(merge_block);
720 current_block = merge_block;
721
722 auto& if_node{syntax_list[if_node_index]};
723 if_node.type = IR::AbstractSyntaxNode::Type::If;
724 if_node.data.if_node.cond = cond;
725 if_node.data.if_node.body = then_block;
726 if_node.data.if_node.merge = merge_block;
727
728 auto& endif_node{syntax_list.emplace_back()};
729 endif_node.type = IR::AbstractSyntaxNode::Type::EndIf;
730 endif_node.data.end_if.merge = merge_block;
731
732 auto& merge{syntax_list.emplace_back()};
733 merge.type = IR::AbstractSyntaxNode::Type::Block;
734 merge.data.block = merge_block;
735 break;
736 }
737 case StatementType::Loop: {
738 IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
739 if (current_block) {
740 current_block->AddBranch(loop_header_block);
741 }
742 auto& header_node{syntax_list.emplace_back()};
743 header_node.type = IR::AbstractSyntaxNode::Type::Block;
744 header_node.data.block = loop_header_block;
745
746 IR::Block* const continue_block{block_pool.Create(inst_pool)};
747 IR::Block* const merge_block{MergeBlock(parent, stmt)};
748
749 const size_t loop_node_index{syntax_list.size()};
750 syntax_list.emplace_back();
751
752 // Visit children
753 const size_t body_block_index{syntax_list.size()};
754 Visit(stmt, merge_block, continue_block);
755
756 // The continue block is located at the end of the loop
757 IR::IREmitter ir{*continue_block};
758 const IR::U1 cond{ir.ConditionRef(VisitExpr(ir, *stmt.cond))};
759
760 IR::Block* const body_block{syntax_list.at(body_block_index).data.block};
761 loop_header_block->AddBranch(body_block);
762
763 continue_block->AddBranch(loop_header_block);
764 continue_block->AddBranch(merge_block);
765
766 current_block = merge_block;
767
768 auto& loop{syntax_list[loop_node_index]};
769 loop.type = IR::AbstractSyntaxNode::Type::Loop;
770 loop.data.loop.body = body_block;
771 loop.data.loop.continue_block = continue_block;
772 loop.data.loop.merge = merge_block;
773
774 auto& continue_block_node{syntax_list.emplace_back()};
775 continue_block_node.type = IR::AbstractSyntaxNode::Type::Block;
776 continue_block_node.data.block = continue_block;
777
778 auto& repeat{syntax_list.emplace_back()};
779 repeat.type = IR::AbstractSyntaxNode::Type::Repeat;
780 repeat.data.repeat.cond = cond;
781 repeat.data.repeat.loop_header = loop_header_block;
782 repeat.data.repeat.merge = merge_block;
783
784 auto& merge{syntax_list.emplace_back()};
785 merge.type = IR::AbstractSyntaxNode::Type::Block;
786 merge.data.block = merge_block;
787 break;
788 }
789 case StatementType::Break: {
790 ensure_block();
791 IR::Block* const skip_block{MergeBlock(parent, stmt)};
792
793 IR::IREmitter ir{*current_block};
794 const IR::U1 cond{ir.ConditionRef(VisitExpr(ir, *stmt.cond))};
795 current_block->AddBranch(break_block);
796 current_block->AddBranch(skip_block);
797 current_block = skip_block;
798
799 auto& break_node{syntax_list.emplace_back()};
800 break_node.type = IR::AbstractSyntaxNode::Type::Break;
801 break_node.data.break_node.cond = cond;
802 break_node.data.break_node.merge = break_block;
803 break_node.data.break_node.skip = skip_block;
804
805 auto& merge{syntax_list.emplace_back()};
806 merge.type = IR::AbstractSyntaxNode::Type::Block;
807 merge.data.block = skip_block;
808 break;
809 }
810 case StatementType::Return: {
811 ensure_block();
812 IR::IREmitter{*current_block}.Epilogue();
813 current_block = nullptr;
814 syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Return;
815 break;
816 }
817 case StatementType::Kill: {
818 ensure_block();
819 IR::Block* demote_block{MergeBlock(parent, stmt)};
820 IR::IREmitter{*current_block}.DemoteToHelperInvocation();
821 current_block->AddBranch(demote_block);
822 current_block = demote_block;
823
824 auto& merge{syntax_list.emplace_back()};
825 merge.type = IR::AbstractSyntaxNode::Type::Block;
826 merge.data.block = demote_block;
827 break;
828 }
829 case StatementType::Unreachable: {
830 ensure_block();
831 current_block = nullptr;
832 syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable;
833 break;
834 }
835 default:
836 throw NotImplementedException("Statement type {}", stmt.type);
837 }
838 }
839 if (current_block) {
840 if (fallthrough_block) {
841 current_block->AddBranch(fallthrough_block);
842 } else {
843 syntax_list.emplace_back().type = IR::AbstractSyntaxNode::Type::Unreachable;
844 }
845 }
846 }
847
848 IR::Block* MergeBlock(Statement& parent, Statement& stmt) {
849 Statement* merge_stmt{TryFindForwardBlock(stmt)};
850 if (!merge_stmt) {
851 // Create a merge block we can visit later
852 merge_stmt = stmt_pool.Create(&dummy_flow_block, &parent);
853 parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
854 }
855 return block_pool.Create(inst_pool);
856 }
857
858 ObjectPool<Statement>& stmt_pool;
859 ObjectPool<IR::Inst>& inst_pool;
860 ObjectPool<IR::Block>& block_pool;
861 Environment& env;
862 IR::AbstractSyntaxList& syntax_list;
863
864// TODO: C++20 Remove this when all compilers support constexpr std::vector
865#if __cpp_lib_constexpr_vector >= 201907
866 static constexpr Flow::Block dummy_flow_block;
867#else
868 const Flow::Block dummy_flow_block;
869#endif
870};
871} // Anonymous namespace
872
873IR::AbstractSyntaxList BuildASL(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
874 Environment& env, Flow::CFG& cfg) {
875 ObjectPool<Statement> stmt_pool{64};
876 GotoPass goto_pass{cfg, stmt_pool};
877 Statement& root{goto_pass.RootStatement()};
878 IR::AbstractSyntaxList syntax_list;
879 TranslatePass{inst_pool, block_pool, stmt_pool, env, root, syntax_list};
880 return syntax_list;
881}
882
883} // namespace Shader::Maxwell