structured_control_flow.cpp 34 KB

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