structured_control_flow.cpp 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793
  1. // Copyright 2021 yuzu Emulator Project
  2. // Licensed under GPLv2 or any later version
  3. // Refer to the license.txt file included.
  4. #include <algorithm>
  5. #include <memory>
  6. #include <ranges>
  7. #include <string>
  8. #include <unordered_map>
  9. #include <utility>
  10. #include <vector>
  11. #include <fmt/format.h>
  12. #include <boost/intrusive/list.hpp>
  13. #include "shader_recompiler/environment.h"
  14. #include "shader_recompiler/frontend/ir/basic_block.h"
  15. #include "shader_recompiler/frontend/ir/ir_emitter.h"
  16. #include "shader_recompiler/frontend/maxwell/structured_control_flow.h"
  17. #include "shader_recompiler/frontend/maxwell/translate/translate.h"
  18. #include "shader_recompiler/object_pool.h"
  19. namespace Shader::Maxwell {
  20. namespace {
  21. struct Statement;
  22. // Use normal_link because we are not guaranteed to destroy the tree in order
  23. using ListBaseHook =
  24. boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>;
  25. using Tree = boost::intrusive::list<Statement,
  26. // Allow using Statement without a definition
  27. boost::intrusive::base_hook<ListBaseHook>,
  28. // Avoid linear complexity on splice, size is never called
  29. boost::intrusive::constant_time_size<false>>;
  30. using Node = Tree::iterator;
  31. using ConstNode = Tree::const_iterator;
  32. enum class StatementType {
  33. Code,
  34. Goto,
  35. Label,
  36. If,
  37. Loop,
  38. Break,
  39. Return,
  40. Kill,
  41. Function,
  42. Identity,
  43. Not,
  44. Or,
  45. SetVariable,
  46. Variable,
  47. };
  48. bool HasChildren(StatementType type) {
  49. switch (type) {
  50. case StatementType::If:
  51. case StatementType::Loop:
  52. case StatementType::Function:
  53. return true;
  54. default:
  55. return false;
  56. }
  57. }
  58. struct Goto {};
  59. struct Label {};
  60. struct If {};
  61. struct Loop {};
  62. struct Break {};
  63. struct Return {};
  64. struct Kill {};
  65. struct FunctionTag {};
  66. struct Identity {};
  67. struct Not {};
  68. struct Or {};
  69. struct SetVariable {};
  70. struct Variable {};
  71. #ifdef _MSC_VER
  72. #pragma warning(push)
  73. #pragma warning(disable : 26495) // Always initialize a member variable, expected in Statement
  74. #endif
  75. struct Statement : ListBaseHook {
  76. Statement(IR::Block* code_, Statement* up_) : code{code_}, up{up_}, type{StatementType::Code} {}
  77. Statement(Goto, Statement* cond_, Node label_, Statement* up_)
  78. : label{label_}, cond{cond_}, up{up_}, type{StatementType::Goto} {}
  79. Statement(Label, u32 id_, Statement* up_) : id{id_}, up{up_}, type{StatementType::Label} {}
  80. Statement(If, Statement* cond_, Tree&& children_, Statement* up_)
  81. : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::If} {}
  82. Statement(Loop, Statement* cond_, Tree&& children_, Statement* up_)
  83. : children{std::move(children_)}, cond{cond_}, up{up_}, type{StatementType::Loop} {}
  84. Statement(Break, Statement* cond_, Statement* up_)
  85. : cond{cond_}, up{up_}, type{StatementType::Break} {}
  86. Statement(Return) : type{StatementType::Return} {}
  87. Statement(Kill) : type{StatementType::Kill} {}
  88. Statement(FunctionTag) : children{}, type{StatementType::Function} {}
  89. Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {}
  90. Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {}
  91. Statement(Or, Statement* op_a_, Statement* op_b_)
  92. : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {}
  93. Statement(SetVariable, u32 id_, Statement* op_, Statement* up_)
  94. : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {}
  95. Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {}
  96. ~Statement() {
  97. if (HasChildren(type)) {
  98. std::destroy_at(&children);
  99. }
  100. }
  101. union {
  102. IR::Block* code;
  103. Node label;
  104. Tree children;
  105. IR::Condition guest_cond;
  106. Statement* op;
  107. Statement* op_a;
  108. };
  109. union {
  110. Statement* cond;
  111. Statement* op_b;
  112. u32 id;
  113. };
  114. Statement* up{};
  115. StatementType type;
  116. };
  117. #ifdef _MSC_VER
  118. #pragma warning(pop)
  119. #endif
  120. std::string DumpExpr(const Statement* stmt) {
  121. switch (stmt->type) {
  122. case StatementType::Identity:
  123. return fmt::format("{}", stmt->guest_cond);
  124. case StatementType::Not:
  125. return fmt::format("!{}", DumpExpr(stmt->op));
  126. case StatementType::Or:
  127. return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b));
  128. case StatementType::Variable:
  129. return fmt::format("goto_L{}", stmt->id);
  130. default:
  131. return "<invalid type>";
  132. }
  133. }
  134. std::string DumpTree(const Tree& tree, u32 indentation = 0) {
  135. std::string ret;
  136. std::string indent(indentation, ' ');
  137. for (auto stmt = tree.begin(); stmt != tree.end(); ++stmt) {
  138. switch (stmt->type) {
  139. case StatementType::Code:
  140. ret += fmt::format("{} Block {:04x};\n", indent, stmt->code->LocationBegin());
  141. break;
  142. case StatementType::Goto:
  143. ret += fmt::format("{} if ({}) goto L{};\n", indent, DumpExpr(stmt->cond),
  144. stmt->label->id);
  145. break;
  146. case StatementType::Label:
  147. ret += fmt::format("{}L{}:\n", indent, stmt->id);
  148. break;
  149. case StatementType::If:
  150. ret += fmt::format("{} if ({}) {{\n", indent, DumpExpr(stmt->cond));
  151. ret += DumpTree(stmt->children, indentation + 4);
  152. ret += fmt::format("{} }}\n", indent);
  153. break;
  154. case StatementType::Loop:
  155. ret += fmt::format("{} do {{\n", indent);
  156. ret += DumpTree(stmt->children, indentation + 4);
  157. ret += fmt::format("{} }} while ({});\n", indent, DumpExpr(stmt->cond));
  158. break;
  159. case StatementType::Break:
  160. ret += fmt::format("{} if ({}) break;\n", indent, DumpExpr(stmt->cond));
  161. break;
  162. case StatementType::Return:
  163. ret += fmt::format("{} return;\n", indent);
  164. break;
  165. case StatementType::Kill:
  166. ret += fmt::format("{} kill;\n", indent);
  167. break;
  168. case StatementType::SetVariable:
  169. ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op));
  170. break;
  171. case StatementType::Function:
  172. case StatementType::Identity:
  173. case StatementType::Not:
  174. case StatementType::Or:
  175. case StatementType::Variable:
  176. throw LogicError("Statement can't be printed");
  177. }
  178. }
  179. return ret;
  180. }
  181. bool HasNode(const Tree& tree, ConstNode stmt) {
  182. const auto end{tree.end()};
  183. for (auto it = tree.begin(); it != end; ++it) {
  184. if (it == stmt || (HasChildren(it->type) && HasNode(it->children, stmt))) {
  185. return true;
  186. }
  187. }
  188. return false;
  189. }
  190. Node FindStatementWithLabel(Tree& tree, ConstNode goto_stmt) {
  191. const ConstNode label_stmt{goto_stmt->label};
  192. const ConstNode end{tree.end()};
  193. for (auto it = tree.begin(); it != end; ++it) {
  194. if (it == label_stmt || (HasChildren(it->type) && HasNode(it->children, label_stmt))) {
  195. return it;
  196. }
  197. }
  198. throw LogicError("Lift label not in tree");
  199. }
  200. void SanitizeNoBreaks(const Tree& tree) {
  201. if (std::ranges::find(tree, StatementType::Break, &Statement::type) != tree.end()) {
  202. throw NotImplementedException("Capturing statement with break nodes");
  203. }
  204. }
  205. size_t Level(Node stmt) {
  206. size_t level{0};
  207. Statement* node{stmt->up};
  208. while (node) {
  209. ++level;
  210. node = node->up;
  211. }
  212. return level;
  213. }
  214. bool IsDirectlyRelated(Node goto_stmt, Node label_stmt) {
  215. const size_t goto_level{Level(goto_stmt)};
  216. const size_t label_level{Level(label_stmt)};
  217. size_t min_level;
  218. size_t max_level;
  219. Node min;
  220. Node max;
  221. if (label_level < goto_level) {
  222. min_level = label_level;
  223. max_level = goto_level;
  224. min = label_stmt;
  225. max = goto_stmt;
  226. } else { // goto_level < label_level
  227. min_level = goto_level;
  228. max_level = label_level;
  229. min = goto_stmt;
  230. max = label_stmt;
  231. }
  232. while (max_level > min_level) {
  233. --max_level;
  234. max = max->up;
  235. }
  236. return min->up == max->up;
  237. }
  238. bool IsIndirectlyRelated(Node goto_stmt, Node label_stmt) {
  239. return goto_stmt->up != label_stmt->up && !IsDirectlyRelated(goto_stmt, label_stmt);
  240. }
  241. bool SearchNode(const Tree& tree, ConstNode stmt, size_t& offset) {
  242. ++offset;
  243. const auto end = tree.end();
  244. for (ConstNode it = tree.begin(); it != end; ++it) {
  245. ++offset;
  246. if (stmt == it) {
  247. return true;
  248. }
  249. if (HasChildren(it->type) && SearchNode(it->children, stmt, offset)) {
  250. return true;
  251. }
  252. }
  253. return false;
  254. }
  255. class GotoPass {
  256. public:
  257. explicit GotoPass(Flow::CFG& cfg, ObjectPool<IR::Inst>& inst_pool_,
  258. ObjectPool<IR::Block>& block_pool_, ObjectPool<Statement>& stmt_pool)
  259. : inst_pool{inst_pool_}, block_pool{block_pool_}, pool{stmt_pool} {
  260. std::vector gotos{BuildTree(cfg)};
  261. for (const Node& goto_stmt : gotos | std::views::reverse) {
  262. RemoveGoto(goto_stmt);
  263. }
  264. }
  265. Statement& RootStatement() noexcept {
  266. return root_stmt;
  267. }
  268. private:
  269. void RemoveGoto(Node goto_stmt) {
  270. // Force goto_stmt and label_stmt to be directly related
  271. const Node label_stmt{goto_stmt->label};
  272. if (IsIndirectlyRelated(goto_stmt, label_stmt)) {
  273. // Move goto_stmt out using outward-movement transformation until it becomes
  274. // directly related to label_stmt
  275. while (!IsDirectlyRelated(goto_stmt, label_stmt)) {
  276. goto_stmt = MoveOutward(goto_stmt);
  277. }
  278. }
  279. // Force goto_stmt and label_stmt to be siblings
  280. if (IsDirectlyRelated(goto_stmt, label_stmt)) {
  281. const size_t label_level{Level(label_stmt)};
  282. size_t goto_level{Level(goto_stmt)};
  283. if (goto_level > label_level) {
  284. // Move goto_stmt out of its level using outward-movement transformations
  285. while (goto_level > label_level) {
  286. goto_stmt = MoveOutward(goto_stmt);
  287. --goto_level;
  288. }
  289. } else { // Level(goto_stmt) < Level(label_stmt)
  290. if (Offset(goto_stmt) > Offset(label_stmt)) {
  291. // Lift goto_stmt to above stmt containing label_stmt using goto-lifting
  292. // transformations
  293. goto_stmt = Lift(goto_stmt);
  294. }
  295. // Move goto_stmt into label_stmt's level using inward-movement transformation
  296. while (goto_level < label_level) {
  297. goto_stmt = MoveInward(goto_stmt);
  298. ++goto_level;
  299. }
  300. }
  301. }
  302. // TODO: Remove this
  303. {
  304. Node it{goto_stmt};
  305. bool sibling{false};
  306. do {
  307. sibling |= it == label_stmt;
  308. --it;
  309. } while (it != goto_stmt->up->children.begin());
  310. while (it != goto_stmt->up->children.end()) {
  311. sibling |= it == label_stmt;
  312. ++it;
  313. }
  314. if (!sibling) {
  315. throw LogicError("Not siblings");
  316. }
  317. }
  318. // goto_stmt and label_stmt are guaranteed to be siblings, eliminate
  319. if (std::next(goto_stmt) == label_stmt) {
  320. // Simply eliminate the goto if the label is next to it
  321. goto_stmt->up->children.erase(goto_stmt);
  322. } else if (Offset(goto_stmt) < Offset(label_stmt)) {
  323. // Eliminate goto_stmt with a conditional
  324. EliminateAsConditional(goto_stmt, label_stmt);
  325. } else {
  326. // Eliminate goto_stmt with a loop
  327. EliminateAsLoop(goto_stmt, label_stmt);
  328. }
  329. }
  330. std::vector<Node> BuildTree(Flow::CFG& cfg) {
  331. u32 label_id{0};
  332. std::vector<Node> gotos;
  333. Flow::Function& first_function{cfg.Functions().front()};
  334. BuildTree(cfg, first_function, label_id, gotos, root_stmt.children.end(), std::nullopt);
  335. return gotos;
  336. }
  337. void BuildTree(Flow::CFG& cfg, Flow::Function& function, u32& label_id,
  338. std::vector<Node>& gotos, Node function_insert_point,
  339. std::optional<Node> return_label) {
  340. Statement* const false_stmt{pool.Create(Identity{}, IR::Condition{false})};
  341. Tree& root{root_stmt.children};
  342. std::unordered_map<Flow::Block*, Node> local_labels;
  343. local_labels.reserve(function.blocks.size());
  344. for (Flow::Block& block : function.blocks) {
  345. Statement* const label{pool.Create(Label{}, label_id, &root_stmt)};
  346. const Node label_it{root.insert(function_insert_point, *label)};
  347. local_labels.emplace(&block, label_it);
  348. ++label_id;
  349. }
  350. for (Flow::Block& block : function.blocks) {
  351. const Node label{local_labels.at(&block)};
  352. // Insertion point
  353. const Node ip{std::next(label)};
  354. // Reset goto variables before the first block and after its respective label
  355. const auto make_reset_variable{[&]() -> Statement& {
  356. return *pool.Create(SetVariable{}, label->id, false_stmt, &root_stmt);
  357. }};
  358. root.push_front(make_reset_variable());
  359. root.insert(ip, make_reset_variable());
  360. const u32 begin_offset{block.begin.Offset()};
  361. const u32 end_offset{block.end.Offset()};
  362. IR::Block* const ir_block{block_pool.Create(inst_pool, begin_offset, end_offset)};
  363. root.insert(ip, *pool.Create(ir_block, &root_stmt));
  364. switch (block.end_class) {
  365. case Flow::EndClass::Branch: {
  366. Statement* const always_cond{pool.Create(Identity{}, IR::Condition{true})};
  367. if (block.cond == IR::Condition{true}) {
  368. const Node true_label{local_labels.at(block.branch_true)};
  369. gotos.push_back(
  370. root.insert(ip, *pool.Create(Goto{}, always_cond, true_label, &root_stmt)));
  371. } else if (block.cond == IR::Condition{false}) {
  372. const Node false_label{local_labels.at(block.branch_false)};
  373. gotos.push_back(root.insert(
  374. ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
  375. } else {
  376. const Node true_label{local_labels.at(block.branch_true)};
  377. const Node false_label{local_labels.at(block.branch_false)};
  378. Statement* const true_cond{pool.Create(Identity{}, block.cond)};
  379. gotos.push_back(
  380. root.insert(ip, *pool.Create(Goto{}, true_cond, true_label, &root_stmt)));
  381. gotos.push_back(root.insert(
  382. ip, *pool.Create(Goto{}, always_cond, false_label, &root_stmt)));
  383. }
  384. break;
  385. }
  386. case Flow::EndClass::Call: {
  387. Flow::Function& call{cfg.Functions()[block.function_call]};
  388. const Node call_return_label{local_labels.at(block.return_block)};
  389. BuildTree(cfg, call, label_id, gotos, ip, call_return_label);
  390. break;
  391. }
  392. case Flow::EndClass::Exit:
  393. root.insert(ip, *pool.Create(Return{}));
  394. break;
  395. case Flow::EndClass::Return: {
  396. Statement* const always_cond{pool.Create(Identity{}, block.cond)};
  397. auto goto_stmt{pool.Create(Goto{}, always_cond, return_label.value(), &root_stmt)};
  398. gotos.push_back(root.insert(ip, *goto_stmt));
  399. break;
  400. }
  401. case Flow::EndClass::Kill:
  402. root.insert(ip, *pool.Create(Kill{}));
  403. break;
  404. }
  405. }
  406. }
  407. void UpdateTreeUp(Statement* tree) {
  408. for (Statement& stmt : tree->children) {
  409. stmt.up = tree;
  410. }
  411. }
  412. void EliminateAsConditional(Node goto_stmt, Node label_stmt) {
  413. Tree& body{goto_stmt->up->children};
  414. Tree if_body;
  415. if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_stmt);
  416. Statement* const cond{pool.Create(Not{}, goto_stmt->cond)};
  417. Statement* const if_stmt{pool.Create(If{}, cond, std::move(if_body), goto_stmt->up)};
  418. UpdateTreeUp(if_stmt);
  419. body.insert(goto_stmt, *if_stmt);
  420. body.erase(goto_stmt);
  421. }
  422. void EliminateAsLoop(Node goto_stmt, Node label_stmt) {
  423. Tree& body{goto_stmt->up->children};
  424. Tree loop_body;
  425. loop_body.splice(loop_body.begin(), body, label_stmt, goto_stmt);
  426. Statement* const cond{goto_stmt->cond};
  427. Statement* const loop{pool.Create(Loop{}, cond, std::move(loop_body), goto_stmt->up)};
  428. UpdateTreeUp(loop);
  429. body.insert(goto_stmt, *loop);
  430. body.erase(goto_stmt);
  431. }
  432. [[nodiscard]] Node MoveOutward(Node goto_stmt) {
  433. switch (goto_stmt->up->type) {
  434. case StatementType::If:
  435. return MoveOutwardIf(goto_stmt);
  436. case StatementType::Loop:
  437. return MoveOutwardLoop(goto_stmt);
  438. default:
  439. throw LogicError("Invalid outward movement");
  440. }
  441. }
  442. [[nodiscard]] Node MoveInward(Node goto_stmt) {
  443. Statement* const parent{goto_stmt->up};
  444. Tree& body{parent->children};
  445. const Node label_nested_stmt{FindStatementWithLabel(body, goto_stmt)};
  446. const Node label{goto_stmt->label};
  447. const u32 label_id{label->id};
  448. Statement* const goto_cond{goto_stmt->cond};
  449. Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
  450. body.insert(goto_stmt, *set_var);
  451. Tree if_body;
  452. if_body.splice(if_body.begin(), body, std::next(goto_stmt), label_nested_stmt);
  453. Statement* const variable{pool.Create(Variable{}, label_id)};
  454. Statement* const neg_var{pool.Create(Not{}, variable)};
  455. if (!if_body.empty()) {
  456. Statement* const if_stmt{pool.Create(If{}, neg_var, std::move(if_body), parent)};
  457. UpdateTreeUp(if_stmt);
  458. body.insert(goto_stmt, *if_stmt);
  459. }
  460. body.erase(goto_stmt);
  461. switch (label_nested_stmt->type) {
  462. case StatementType::If:
  463. // Update nested if condition
  464. label_nested_stmt->cond = pool.Create(Or{}, variable, label_nested_stmt->cond);
  465. break;
  466. case StatementType::Loop:
  467. break;
  468. default:
  469. throw LogicError("Invalid inward movement");
  470. }
  471. Tree& nested_tree{label_nested_stmt->children};
  472. Statement* const new_goto{pool.Create(Goto{}, variable, label, &*label_nested_stmt)};
  473. return nested_tree.insert(nested_tree.begin(), *new_goto);
  474. }
  475. [[nodiscard]] Node Lift(Node goto_stmt) {
  476. Statement* const parent{goto_stmt->up};
  477. Tree& body{parent->children};
  478. const Node label{goto_stmt->label};
  479. const u32 label_id{label->id};
  480. const Node label_nested_stmt{FindStatementWithLabel(body, goto_stmt)};
  481. const auto type{label_nested_stmt->type};
  482. Tree loop_body;
  483. loop_body.splice(loop_body.begin(), body, label_nested_stmt, goto_stmt);
  484. SanitizeNoBreaks(loop_body);
  485. Statement* const variable{pool.Create(Variable{}, label_id)};
  486. Statement* const loop_stmt{pool.Create(Loop{}, variable, std::move(loop_body), parent)};
  487. UpdateTreeUp(loop_stmt);
  488. const Node loop_node{body.insert(goto_stmt, *loop_stmt)};
  489. Statement* const new_goto{pool.Create(Goto{}, variable, label, loop_stmt)};
  490. loop_stmt->children.push_front(*new_goto);
  491. const Node new_goto_node{loop_stmt->children.begin()};
  492. Statement* const set_var{pool.Create(SetVariable{}, label_id, goto_stmt->cond, loop_stmt)};
  493. loop_stmt->children.push_back(*set_var);
  494. body.erase(goto_stmt);
  495. return new_goto_node;
  496. }
  497. Node MoveOutwardIf(Node goto_stmt) {
  498. const Node parent{Tree::s_iterator_to(*goto_stmt->up)};
  499. Tree& body{parent->children};
  500. const u32 label_id{goto_stmt->label->id};
  501. Statement* const goto_cond{goto_stmt->cond};
  502. Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, &*parent)};
  503. body.insert(goto_stmt, *set_goto_var);
  504. Tree if_body;
  505. if_body.splice(if_body.begin(), body, std::next(goto_stmt), body.end());
  506. if_body.pop_front();
  507. Statement* const cond{pool.Create(Variable{}, label_id)};
  508. Statement* const neg_cond{pool.Create(Not{}, cond)};
  509. Statement* const if_stmt{pool.Create(If{}, neg_cond, std::move(if_body), &*parent)};
  510. UpdateTreeUp(if_stmt);
  511. body.insert(goto_stmt, *if_stmt);
  512. body.erase(goto_stmt);
  513. Statement* const new_cond{pool.Create(Variable{}, label_id)};
  514. Statement* const new_goto{pool.Create(Goto{}, new_cond, goto_stmt->label, parent->up)};
  515. Tree& parent_tree{parent->up->children};
  516. return parent_tree.insert(std::next(parent), *new_goto);
  517. }
  518. Node MoveOutwardLoop(Node goto_stmt) {
  519. Statement* const parent{goto_stmt->up};
  520. Tree& body{parent->children};
  521. const u32 label_id{goto_stmt->label->id};
  522. Statement* const goto_cond{goto_stmt->cond};
  523. Statement* const set_goto_var{pool.Create(SetVariable{}, label_id, goto_cond, parent)};
  524. Statement* const cond{pool.Create(Variable{}, label_id)};
  525. Statement* const break_stmt{pool.Create(Break{}, cond, parent)};
  526. body.insert(goto_stmt, *set_goto_var);
  527. body.insert(goto_stmt, *break_stmt);
  528. body.erase(goto_stmt);
  529. const Node loop{Tree::s_iterator_to(*goto_stmt->up)};
  530. Statement* const new_goto_cond{pool.Create(Variable{}, label_id)};
  531. Statement* const new_goto{pool.Create(Goto{}, new_goto_cond, goto_stmt->label, loop->up)};
  532. Tree& parent_tree{loop->up->children};
  533. return parent_tree.insert(std::next(loop), *new_goto);
  534. }
  535. size_t Offset(ConstNode stmt) const {
  536. size_t offset{0};
  537. if (!SearchNode(root_stmt.children, stmt, offset)) {
  538. throw LogicError("Node not found in tree");
  539. }
  540. return offset;
  541. }
  542. ObjectPool<IR::Inst>& inst_pool;
  543. ObjectPool<IR::Block>& block_pool;
  544. ObjectPool<Statement>& pool;
  545. Statement root_stmt{FunctionTag{}};
  546. };
  547. IR::Block* TryFindForwardBlock(const Statement& stmt) {
  548. const Tree& tree{stmt.up->children};
  549. const ConstNode end{tree.cend()};
  550. ConstNode forward_node{std::next(Tree::s_iterator_to(stmt))};
  551. while (forward_node != end && !HasChildren(forward_node->type)) {
  552. if (forward_node->type == StatementType::Code) {
  553. return forward_node->code;
  554. }
  555. ++forward_node;
  556. }
  557. return nullptr;
  558. }
  559. [[nodiscard]] IR::U1 VisitExpr(IR::IREmitter& ir, const Statement& stmt) {
  560. switch (stmt.type) {
  561. case StatementType::Identity:
  562. return ir.Condition(stmt.guest_cond);
  563. case StatementType::Not:
  564. return ir.LogicalNot(IR::U1{VisitExpr(ir, *stmt.op)});
  565. case StatementType::Or:
  566. return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b));
  567. case StatementType::Variable:
  568. return ir.GetGotoVariable(stmt.id);
  569. default:
  570. throw NotImplementedException("Statement type {}", stmt.type);
  571. }
  572. }
  573. class TranslatePass {
  574. public:
  575. TranslatePass(ObjectPool<IR::Inst>& inst_pool_, ObjectPool<IR::Block>& block_pool_,
  576. ObjectPool<Statement>& stmt_pool_, Environment& env_, Statement& root_stmt,
  577. IR::BlockList& block_list_)
  578. : stmt_pool{stmt_pool_}, inst_pool{inst_pool_}, block_pool{block_pool_}, env{env_},
  579. block_list{block_list_} {
  580. Visit(root_stmt, nullptr, nullptr);
  581. IR::IREmitter ir{*block_list.front()};
  582. ir.Prologue();
  583. }
  584. private:
  585. void Visit(Statement& parent, IR::Block* continue_block, IR::Block* break_block) {
  586. Tree& tree{parent.children};
  587. IR::Block* current_block{nullptr};
  588. for (auto it = tree.begin(); it != tree.end(); ++it) {
  589. Statement& stmt{*it};
  590. switch (stmt.type) {
  591. case StatementType::Label:
  592. // Labels can be ignored
  593. break;
  594. case StatementType::Code: {
  595. if (current_block && current_block != stmt.code) {
  596. IR::IREmitter{*current_block}.Branch(stmt.code);
  597. }
  598. current_block = stmt.code;
  599. Translate(env, stmt.code);
  600. block_list.push_back(stmt.code);
  601. break;
  602. }
  603. case StatementType::SetVariable: {
  604. if (!current_block) {
  605. current_block = MergeBlock(parent, stmt);
  606. }
  607. IR::IREmitter ir{*current_block};
  608. ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op));
  609. break;
  610. }
  611. case StatementType::If: {
  612. if (!current_block) {
  613. current_block = block_pool.Create(inst_pool);
  614. block_list.push_back(current_block);
  615. }
  616. IR::Block* const merge_block{MergeBlock(parent, stmt)};
  617. // Visit children
  618. const size_t first_block_index{block_list.size()};
  619. Visit(stmt, merge_block, break_block);
  620. // Implement if header block
  621. IR::Block* const first_if_block{block_list.at(first_block_index)};
  622. IR::IREmitter ir{*current_block};
  623. const IR::U1 cond{VisitExpr(ir, *stmt.cond)};
  624. ir.SelectionMerge(merge_block);
  625. ir.BranchConditional(cond, first_if_block, merge_block);
  626. current_block = merge_block;
  627. break;
  628. }
  629. case StatementType::Loop: {
  630. IR::Block* const loop_header_block{block_pool.Create(inst_pool)};
  631. if (current_block) {
  632. IR::IREmitter{*current_block}.Branch(loop_header_block);
  633. }
  634. block_list.push_back(loop_header_block);
  635. IR::Block* const new_continue_block{block_pool.Create(inst_pool)};
  636. IR::Block* const merge_block{MergeBlock(parent, stmt)};
  637. // Visit children
  638. const size_t first_block_index{block_list.size()};
  639. Visit(stmt, new_continue_block, merge_block);
  640. // The continue block is located at the end of the loop
  641. block_list.push_back(new_continue_block);
  642. // Implement loop header block
  643. IR::Block* const first_loop_block{block_list.at(first_block_index)};
  644. IR::IREmitter ir{*loop_header_block};
  645. ir.LoopMerge(merge_block, new_continue_block);
  646. ir.Branch(first_loop_block);
  647. // Implement continue block
  648. IR::IREmitter continue_ir{*new_continue_block};
  649. const IR::U1 continue_cond{VisitExpr(continue_ir, *stmt.cond)};
  650. continue_ir.BranchConditional(continue_cond, ir.block, merge_block);
  651. current_block = merge_block;
  652. break;
  653. }
  654. case StatementType::Break: {
  655. if (!current_block) {
  656. current_block = block_pool.Create(inst_pool);
  657. block_list.push_back(current_block);
  658. }
  659. IR::Block* const skip_block{MergeBlock(parent, stmt)};
  660. IR::IREmitter ir{*current_block};
  661. ir.BranchConditional(VisitExpr(ir, *stmt.cond), break_block, skip_block);
  662. current_block = skip_block;
  663. break;
  664. }
  665. case StatementType::Return: {
  666. if (!current_block) {
  667. current_block = block_pool.Create(inst_pool);
  668. block_list.push_back(current_block);
  669. }
  670. IR::IREmitter ir{*current_block};
  671. ir.Epilogue();
  672. ir.Return();
  673. current_block = nullptr;
  674. break;
  675. }
  676. case StatementType::Kill: {
  677. if (!current_block) {
  678. current_block = block_pool.Create(inst_pool);
  679. block_list.push_back(current_block);
  680. }
  681. IR::IREmitter{*current_block}.DemoteToHelperInvocation(continue_block);
  682. current_block = nullptr;
  683. break;
  684. }
  685. default:
  686. throw NotImplementedException("Statement type {}", stmt.type);
  687. }
  688. }
  689. if (current_block && continue_block) {
  690. IR::IREmitter{*current_block}.Branch(continue_block);
  691. }
  692. }
  693. IR::Block* MergeBlock(Statement& parent, Statement& stmt) {
  694. if (IR::Block* const block{TryFindForwardBlock(stmt)}) {
  695. return block;
  696. }
  697. // Create a merge block we can visit later
  698. IR::Block* const block{block_pool.Create(inst_pool)};
  699. Statement* const merge_stmt{stmt_pool.Create(block, &parent)};
  700. parent.children.insert(std::next(Tree::s_iterator_to(stmt)), *merge_stmt);
  701. return block;
  702. }
  703. ObjectPool<Statement>& stmt_pool;
  704. ObjectPool<IR::Inst>& inst_pool;
  705. ObjectPool<IR::Block>& block_pool;
  706. Environment& env;
  707. IR::BlockList& block_list;
  708. };
  709. } // Anonymous namespace
  710. IR::BlockList VisitAST(ObjectPool<IR::Inst>& inst_pool, ObjectPool<IR::Block>& block_pool,
  711. Environment& env, Flow::CFG& cfg) {
  712. ObjectPool<Statement> stmt_pool{64};
  713. GotoPass goto_pass{cfg, inst_pool, block_pool, stmt_pool};
  714. Statement& root{goto_pass.RootStatement()};
  715. IR::BlockList block_list;
  716. TranslatePass{inst_pool, block_pool, stmt_pool, env, root, block_list};
  717. return block_list;
  718. }
  719. } // namespace Shader::Maxwell