diff options
| author | 2021-02-14 20:15:42 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:22 -0400 | |
| commit | cbfb7d182a4e90e4e263696d1fca35e47d3eabb4 (patch) | |
| tree | a8d384aa0daefdfafd9b61330e06b1cf7ac40ea6 /src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |
| parent | shader: Misc fixes (diff) | |
| download | yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.tar.gz yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.tar.xz yuzu-cbfb7d182a4e90e4e263696d1fca35e47d3eabb4.zip | |
shader: Support SSA loops on IR
Diffstat (limited to 'src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp')
| -rw-r--r-- | src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | 62 |
1 files changed, 49 insertions, 13 deletions
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index 7eaf719c4..13f9c914a 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -14,7 +14,13 @@ | |||
| 14 | // https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 | 14 | // https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6 |
| 15 | // | 15 | // |
| 16 | 16 | ||
| 17 | #include <ranges> | ||
| 18 | #include <span> | ||
| 19 | #include <variant> | ||
| 20 | #include <vector> | ||
| 21 | |||
| 17 | #include <boost/container/flat_map.hpp> | 22 | #include <boost/container/flat_map.hpp> |
| 23 | #include <boost/container/flat_set.hpp> | ||
| 18 | 24 | ||
| 19 | #include "shader_recompiler/frontend/ir/basic_block.h" | 25 | #include "shader_recompiler/frontend/ir/basic_block.h" |
| 20 | #include "shader_recompiler/frontend/ir/function.h" | 26 | #include "shader_recompiler/frontend/ir/function.h" |
| @@ -26,9 +32,9 @@ | |||
| 26 | 32 | ||
| 27 | namespace Shader::Optimization { | 33 | namespace Shader::Optimization { |
| 28 | namespace { | 34 | namespace { |
| 29 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | 35 | struct FlagTag { |
| 30 | 36 | auto operator<=>(const FlagTag&) const noexcept = default; | |
| 31 | struct FlagTag {}; | 37 | }; |
| 32 | struct ZeroFlagTag : FlagTag {}; | 38 | struct ZeroFlagTag : FlagTag {}; |
| 33 | struct SignFlagTag : FlagTag {}; | 39 | struct SignFlagTag : FlagTag {}; |
| 34 | struct CarryFlagTag : FlagTag {}; | 40 | struct CarryFlagTag : FlagTag {}; |
| @@ -38,9 +44,15 @@ struct GotoVariable : FlagTag { | |||
| 38 | GotoVariable() = default; | 44 | GotoVariable() = default; |
| 39 | explicit GotoVariable(u32 index_) : index{index_} {} | 45 | explicit GotoVariable(u32 index_) : index{index_} {} |
| 40 | 46 | ||
| 47 | auto operator<=>(const GotoVariable&) const noexcept = default; | ||
| 48 | |||
| 41 | u32 index; | 49 | u32 index; |
| 42 | }; | 50 | }; |
| 43 | 51 | ||
| 52 | using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, | ||
| 53 | OverflowFlagTag, GotoVariable>; | ||
| 54 | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value, std::less<IR::Block*>>; | ||
| 55 | |||
| 44 | struct DefTable { | 56 | struct DefTable { |
| 45 | [[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept { | 57 | [[nodiscard]] ValueMap& operator[](IR::Reg variable) noexcept { |
| 46 | return regs[IR::RegIndex(variable)]; | 58 | return regs[IR::RegIndex(variable)]; |
| @@ -102,19 +114,35 @@ public: | |||
| 102 | } | 114 | } |
| 103 | 115 | ||
| 104 | IR::Value ReadVariable(auto variable, IR::Block* block) { | 116 | IR::Value ReadVariable(auto variable, IR::Block* block) { |
| 105 | auto& def{current_def[variable]}; | 117 | const ValueMap& def{current_def[variable]}; |
| 106 | if (const auto it{def.find(block)}; it != def.end()) { | 118 | if (const auto it{def.find(block)}; it != def.end()) { |
| 107 | return it->second; | 119 | return it->second; |
| 108 | } | 120 | } |
| 109 | return ReadVariableRecursive(variable, block); | 121 | return ReadVariableRecursive(variable, block); |
| 110 | } | 122 | } |
| 111 | 123 | ||
| 124 | void SealBlock(IR::Block* block) { | ||
| 125 | const auto it{incomplete_phis.find(block)}; | ||
| 126 | if (it != incomplete_phis.end()) { | ||
| 127 | for (auto& [variant, phi] : it->second) { | ||
| 128 | std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); | ||
| 129 | } | ||
| 130 | } | ||
| 131 | sealed_blocks.insert(block); | ||
| 132 | } | ||
| 133 | |||
| 112 | private: | 134 | private: |
| 113 | IR::Value ReadVariableRecursive(auto variable, IR::Block* block) { | 135 | IR::Value ReadVariableRecursive(auto variable, IR::Block* block) { |
| 114 | IR::Value val; | 136 | IR::Value val; |
| 115 | if (const std::span preds{block->ImmediatePredecessors()}; preds.size() == 1) { | 137 | if (!sealed_blocks.contains(block)) { |
| 138 | // Incomplete CFG | ||
| 139 | IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 140 | incomplete_phis[block].insert_or_assign(variable, phi); | ||
| 141 | val = IR::Value{&*phi}; | ||
| 142 | } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | ||
| 143 | imm_preds.size() == 1) { | ||
| 116 | // Optimize the common case of one predecessor: no phi needed | 144 | // Optimize the common case of one predecessor: no phi needed |
| 117 | val = ReadVariable(variable, preds.front()); | 145 | val = ReadVariable(variable, imm_preds.front()); |
| 118 | } else { | 146 | } else { |
| 119 | // Break potential cycles with operandless phi | 147 | // Break potential cycles with operandless phi |
| 120 | IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | 148 | IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; |
| @@ -127,8 +155,8 @@ private: | |||
| 127 | } | 155 | } |
| 128 | 156 | ||
| 129 | IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) { | 157 | IR::Value AddPhiOperands(auto variable, IR::Inst& phi, IR::Block* block) { |
| 130 | for (IR::Block* const pred : block->ImmediatePredecessors()) { | 158 | for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { |
| 131 | phi.AddPhiOperand(pred, ReadVariable(variable, pred)); | 159 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); |
| 132 | } | 160 | } |
| 133 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); | 161 | return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); |
| 134 | } | 162 | } |
| @@ -159,6 +187,9 @@ private: | |||
| 159 | return same; | 187 | return same; |
| 160 | } | 188 | } |
| 161 | 189 | ||
| 190 | boost::container::flat_set<IR::Block*> sealed_blocks; | ||
| 191 | boost::container::flat_map<IR::Block*, boost::container::flat_map<Variant, IR::Inst*>> | ||
| 192 | incomplete_phis; | ||
| 162 | DefTable current_def; | 193 | DefTable current_def; |
| 163 | }; | 194 | }; |
| 164 | 195 | ||
| @@ -218,14 +249,19 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | |||
| 218 | break; | 249 | break; |
| 219 | } | 250 | } |
| 220 | } | 251 | } |
| 252 | |||
| 253 | void VisitBlock(Pass& pass, IR::Block* block) { | ||
| 254 | for (IR::Inst& inst : block->Instructions()) { | ||
| 255 | VisitInst(pass, block, inst); | ||
| 256 | } | ||
| 257 | pass.SealBlock(block); | ||
| 258 | } | ||
| 221 | } // Anonymous namespace | 259 | } // Anonymous namespace |
| 222 | 260 | ||
| 223 | void SsaRewritePass(IR::Function& function) { | 261 | void SsaRewritePass(std::span<IR::Block* const> post_order_blocks) { |
| 224 | Pass pass; | 262 | Pass pass; |
| 225 | for (IR::Block* const block : function.blocks) { | 263 | for (IR::Block* const block : post_order_blocks | std::views::reverse) { |
| 226 | for (IR::Inst& inst : block->Instructions()) { | 264 | VisitBlock(pass, block); |
| 227 | VisitInst(pass, block, inst); | ||
| 228 | } | ||
| 229 | } | 265 | } |
| 230 | } | 266 | } |
| 231 | 267 | ||