diff options
| author | 2021-03-27 22:30:24 +0100 | |
|---|---|---|
| committer | 2021-07-22 21:51:25 -0400 | |
| commit | 34aba9627a8fad20b3b173180e2f3d679dd32293 (patch) | |
| tree | a4f2faec67a793e8b44493532a683908dcefb4d8 /src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |
| parent | shader: Fix alignment checks on RZ (diff) | |
| download | yuzu-34aba9627a8fad20b3b173180e2f3d679dd32293.tar.gz yuzu-34aba9627a8fad20b3b173180e2f3d679dd32293.tar.xz yuzu-34aba9627a8fad20b3b173180e2f3d679dd32293.zip | |
shader: Implement BRX
Diffstat (limited to 'src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp')
| -rw-r--r-- | src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | 57 |
1 files changed, 57 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 index 9d4688390..a6e55f61e 100644 --- a/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp +++ b/src/shader_recompiler/frontend/maxwell/structured_control_flow.cpp | |||
| @@ -17,6 +17,7 @@ | |||
| 17 | #include "shader_recompiler/environment.h" | 17 | #include "shader_recompiler/environment.h" |
| 18 | #include "shader_recompiler/frontend/ir/basic_block.h" | 18 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" | 19 | #include "shader_recompiler/frontend/ir/ir_emitter.h" |
| 20 | #include "shader_recompiler/frontend/maxwell/decode.h" | ||
| 20 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" | 21 | #include "shader_recompiler/frontend/maxwell/structured_control_flow.h" |
| 21 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" | 22 | #include "shader_recompiler/frontend/maxwell/translate/translate.h" |
| 22 | #include "shader_recompiler/object_pool.h" | 23 | #include "shader_recompiler/object_pool.h" |
| @@ -46,12 +47,15 @@ enum class StatementType { | |||
| 46 | Break, | 47 | Break, |
| 47 | Return, | 48 | Return, |
| 48 | Kill, | 49 | Kill, |
| 50 | Unreachable, | ||
| 49 | Function, | 51 | Function, |
| 50 | Identity, | 52 | Identity, |
| 51 | Not, | 53 | Not, |
| 52 | Or, | 54 | Or, |
| 53 | SetVariable, | 55 | SetVariable, |
| 56 | SetIndirectBranchVariable, | ||
| 54 | Variable, | 57 | Variable, |
| 58 | IndirectBranchCond, | ||
| 55 | }; | 59 | }; |
| 56 | 60 | ||
| 57 | bool HasChildren(StatementType type) { | 61 | bool HasChildren(StatementType type) { |
| @@ -72,12 +76,15 @@ struct Loop {}; | |||
| 72 | struct Break {}; | 76 | struct Break {}; |
| 73 | struct Return {}; | 77 | struct Return {}; |
| 74 | struct Kill {}; | 78 | struct Kill {}; |
| 79 | struct Unreachable {}; | ||
| 75 | struct FunctionTag {}; | 80 | struct FunctionTag {}; |
| 76 | struct Identity {}; | 81 | struct Identity {}; |
| 77 | struct Not {}; | 82 | struct Not {}; |
| 78 | struct Or {}; | 83 | struct Or {}; |
| 79 | struct SetVariable {}; | 84 | struct SetVariable {}; |
| 85 | struct SetIndirectBranchVariable {}; | ||
| 80 | struct Variable {}; | 86 | struct Variable {}; |
| 87 | struct IndirectBranchCond {}; | ||
| 81 | 88 | ||
| 82 | #ifdef _MSC_VER | 89 | #ifdef _MSC_VER |
| 83 | #pragma warning(push) | 90 | #pragma warning(push) |
| @@ -96,6 +103,7 @@ struct Statement : ListBaseHook { | |||
| 96 | : cond{cond_}, up{up_}, type{StatementType::Break} {} | 103 | : cond{cond_}, up{up_}, type{StatementType::Break} {} |
| 97 | Statement(Return) : type{StatementType::Return} {} | 104 | Statement(Return) : type{StatementType::Return} {} |
| 98 | Statement(Kill) : type{StatementType::Kill} {} | 105 | Statement(Kill) : type{StatementType::Kill} {} |
| 106 | Statement(Unreachable) : type{StatementType::Unreachable} {} | ||
| 99 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} | 107 | Statement(FunctionTag) : children{}, type{StatementType::Function} {} |
| 100 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} | 108 | Statement(Identity, IR::Condition cond_) : guest_cond{cond_}, type{StatementType::Identity} {} |
| 101 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} | 109 | Statement(Not, Statement* op_) : op{op_}, type{StatementType::Not} {} |
| @@ -103,7 +111,12 @@ struct Statement : ListBaseHook { | |||
| 103 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} | 111 | : op_a{op_a_}, op_b{op_b_}, type{StatementType::Or} {} |
| 104 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) | 112 | Statement(SetVariable, u32 id_, Statement* op_, Statement* up_) |
| 105 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} | 113 | : op{op_}, id{id_}, up{up_}, type{StatementType::SetVariable} {} |
| 114 | Statement(SetIndirectBranchVariable, IR::Reg branch_reg_, s32 branch_offset_) | ||
| 115 | : branch_offset{branch_offset_}, | ||
| 116 | branch_reg{branch_reg_}, type{StatementType::SetIndirectBranchVariable} {} | ||
| 106 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} | 117 | Statement(Variable, u32 id_) : id{id_}, type{StatementType::Variable} {} |
| 118 | Statement(IndirectBranchCond, u32 location_) | ||
| 119 | : location{location_}, type{StatementType::IndirectBranchCond} {} | ||
| 107 | 120 | ||
| 108 | ~Statement() { | 121 | ~Statement() { |
| 109 | if (HasChildren(type)) { | 122 | if (HasChildren(type)) { |
| @@ -118,11 +131,14 @@ struct Statement : ListBaseHook { | |||
| 118 | IR::Condition guest_cond; | 131 | IR::Condition guest_cond; |
| 119 | Statement* op; | 132 | Statement* op; |
| 120 | Statement* op_a; | 133 | Statement* op_a; |
| 134 | u32 location; | ||
| 135 | s32 branch_offset; | ||
| 121 | }; | 136 | }; |
| 122 | union { | 137 | union { |
| 123 | Statement* cond; | 138 | Statement* cond; |
| 124 | Statement* op_b; | 139 | Statement* op_b; |
| 125 | u32 id; | 140 | u32 id; |
| 141 | IR::Reg branch_reg; | ||
| 126 | }; | 142 | }; |
| 127 | Statement* up{}; | 143 | Statement* up{}; |
| 128 | StatementType type; | 144 | StatementType type; |
| @@ -141,6 +157,8 @@ std::string DumpExpr(const Statement* stmt) { | |||
| 141 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); | 157 | return fmt::format("{} || {}", DumpExpr(stmt->op_a), DumpExpr(stmt->op_b)); |
| 142 | case StatementType::Variable: | 158 | case StatementType::Variable: |
| 143 | return fmt::format("goto_L{}", stmt->id); | 159 | return fmt::format("goto_L{}", stmt->id); |
| 160 | case StatementType::IndirectBranchCond: | ||
| 161 | return fmt::format("(indirect_branch == {:x})", stmt->location); | ||
| 144 | default: | 162 | default: |
| 145 | return "<invalid type>"; | 163 | return "<invalid type>"; |
| 146 | } | 164 | } |
| @@ -182,14 +200,22 @@ std::string DumpTree(const Tree& tree, u32 indentation = 0) { | |||
| 182 | case StatementType::Kill: | 200 | case StatementType::Kill: |
| 183 | ret += fmt::format("{} kill;\n", indent); | 201 | ret += fmt::format("{} kill;\n", indent); |
| 184 | break; | 202 | break; |
| 203 | case StatementType::Unreachable: | ||
| 204 | ret += fmt::format("{} unreachable;\n", indent); | ||
| 205 | break; | ||
| 185 | case StatementType::SetVariable: | 206 | case StatementType::SetVariable: |
| 186 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); | 207 | ret += fmt::format("{} goto_L{} = {};\n", indent, stmt->id, DumpExpr(stmt->op)); |
| 187 | break; | 208 | break; |
| 209 | case StatementType::SetIndirectBranchVariable: | ||
| 210 | ret += fmt::format("{} indirect_branch = {} + {};\n", indent, stmt->branch_reg, | ||
| 211 | stmt->branch_offset); | ||
| 212 | break; | ||
| 188 | case StatementType::Function: | 213 | case StatementType::Function: |
| 189 | case StatementType::Identity: | 214 | case StatementType::Identity: |
| 190 | case StatementType::Not: | 215 | case StatementType::Not: |
| 191 | case StatementType::Or: | 216 | case StatementType::Or: |
| 192 | case StatementType::Variable: | 217 | case StatementType::Variable: |
| 218 | case StatementType::IndirectBranchCond: | ||
| 193 | throw LogicError("Statement can't be printed"); | 219 | throw LogicError("Statement can't be printed"); |
| 194 | } | 220 | } |
| 195 | } | 221 | } |
| @@ -417,6 +443,17 @@ private: | |||
| 417 | } | 443 | } |
| 418 | break; | 444 | break; |
| 419 | } | 445 | } |
| 446 | case Flow::EndClass::IndirectBranch: | ||
| 447 | root.insert(ip, *pool.Create(SetIndirectBranchVariable{}, block.branch_reg, | ||
| 448 | block.branch_offset)); | ||
| 449 | for (Flow::Block* const branch : block.indirect_branches) { | ||
| 450 | const Node indirect_label{local_labels.at(branch)}; | ||
| 451 | Statement* cond{pool.Create(IndirectBranchCond{}, branch->begin.Offset())}; | ||
| 452 | Statement* goto_stmt{pool.Create(Goto{}, cond, indirect_label, &root_stmt)}; | ||
| 453 | gotos.push_back(root.insert(ip, *goto_stmt)); | ||
| 454 | } | ||
| 455 | root.insert(ip, *pool.Create(Unreachable{})); | ||
| 456 | break; | ||
| 420 | case Flow::EndClass::Call: { | 457 | case Flow::EndClass::Call: { |
| 421 | Flow::Function& call{cfg.Functions()[block.function_call]}; | 458 | Flow::Function& call{cfg.Functions()[block.function_call]}; |
| 422 | const Node call_return_label{local_labels.at(block.return_block)}; | 459 | const Node call_return_label{local_labels.at(block.return_block)}; |
| @@ -623,6 +660,8 @@ IR::Block* TryFindForwardBlock(const Statement& stmt) { | |||
| 623 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); | 660 | return ir.LogicalOr(VisitExpr(ir, *stmt.op_a), VisitExpr(ir, *stmt.op_b)); |
| 624 | case StatementType::Variable: | 661 | case StatementType::Variable: |
| 625 | return ir.GetGotoVariable(stmt.id); | 662 | return ir.GetGotoVariable(stmt.id); |
| 663 | case StatementType::IndirectBranchCond: | ||
| 664 | return ir.IEqual(ir.GetIndirectBranchVariable(), ir.Imm32(stmt.location)); | ||
| 626 | default: | 665 | default: |
| 627 | throw NotImplementedException("Statement type {}", stmt.type); | 666 | throw NotImplementedException("Statement type {}", stmt.type); |
| 628 | } | 667 | } |
| @@ -670,6 +709,15 @@ private: | |||
| 670 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); | 709 | ir.SetGotoVariable(stmt.id, VisitExpr(ir, *stmt.op)); |
| 671 | break; | 710 | break; |
| 672 | } | 711 | } |
| 712 | case StatementType::SetIndirectBranchVariable: { | ||
| 713 | if (!current_block) { | ||
| 714 | current_block = MergeBlock(parent, stmt); | ||
| 715 | } | ||
| 716 | IR::IREmitter ir{*current_block}; | ||
| 717 | IR::U32 address{ir.IAdd(ir.GetReg(stmt.branch_reg), ir.Imm32(stmt.branch_offset))}; | ||
| 718 | ir.SetIndirectBranchVariable(address); | ||
| 719 | break; | ||
| 720 | } | ||
| 673 | case StatementType::If: { | 721 | case StatementType::If: { |
| 674 | if (!current_block) { | 722 | if (!current_block) { |
| 675 | current_block = block_pool.Create(inst_pool); | 723 | current_block = block_pool.Create(inst_pool); |
| @@ -756,6 +804,15 @@ private: | |||
| 756 | current_block = demote_block; | 804 | current_block = demote_block; |
| 757 | break; | 805 | break; |
| 758 | } | 806 | } |
| 807 | case StatementType::Unreachable: { | ||
| 808 | if (!current_block) { | ||
| 809 | current_block = block_pool.Create(inst_pool); | ||
| 810 | block_list.push_back(current_block); | ||
| 811 | } | ||
| 812 | IR::IREmitter{*current_block}.Unreachable(); | ||
| 813 | current_block = nullptr; | ||
| 814 | break; | ||
| 815 | } | ||
| 759 | default: | 816 | default: |
| 760 | throw NotImplementedException("Statement type {}", stmt.type); | 817 | throw NotImplementedException("Statement type {}", stmt.type); |
| 761 | } | 818 | } |