diff options
| author | 2021-04-05 19:10:55 -0300 | |
|---|---|---|
| committer | 2021-07-22 21:51:26 -0400 | |
| commit | 417fb5d385daa0fb40329709e6b4a53937580989 (patch) | |
| tree | 8ce5696b557b127e79a983957f0a5d77afe5aec4 /src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |
| parent | shader: Fix ShadowCube declaration type, set number of pipeline threads based... (diff) | |
| download | yuzu-417fb5d385daa0fb40329709e6b4a53937580989.tar.gz yuzu-417fb5d385daa0fb40329709e6b4a53937580989.tar.xz yuzu-417fb5d385daa0fb40329709e6b4a53937580989.zip | |
shader: Move recursive SSA rewrite to the heap
Diffstat (limited to 'src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp')
| -rw-r--r-- | src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | 118 |
1 files changed, 89 insertions, 29 deletions
diff --git a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp index 259233746..ca36253d1 100644 --- a/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp +++ b/src/shader_recompiler/ir_opt/ssa_rewrite_pass.cpp | |||
| @@ -119,6 +119,26 @@ IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept { | |||
| 119 | return inst.Opcode() == IR::Opcode::Phi; | 119 | return inst.Opcode() == IR::Opcode::Phi; |
| 120 | } | 120 | } |
| 121 | 121 | ||
| 122 | enum class Status { | ||
| 123 | Start, | ||
| 124 | SetValue, | ||
| 125 | PreparePhiArgument, | ||
| 126 | PushPhiArgument, | ||
| 127 | }; | ||
| 128 | |||
| 129 | template <typename Type> | ||
| 130 | struct ReadState { | ||
| 131 | ReadState(IR::Block* block_) : block{block_} {} | ||
| 132 | ReadState() = default; | ||
| 133 | |||
| 134 | IR::Block* block{}; | ||
| 135 | IR::Value result{}; | ||
| 136 | IR::Inst* phi{}; | ||
| 137 | IR::Block* const* pred_it{}; | ||
| 138 | IR::Block* const* pred_end{}; | ||
| 139 | Status pc{Status::Start}; | ||
| 140 | }; | ||
| 141 | |||
| 122 | class Pass { | 142 | class Pass { |
| 123 | public: | 143 | public: |
| 124 | template <typename Type> | 144 | template <typename Type> |
| @@ -127,12 +147,75 @@ public: | |||
| 127 | } | 147 | } |
| 128 | 148 | ||
| 129 | template <typename Type> | 149 | template <typename Type> |
| 130 | IR::Value ReadVariable(Type variable, IR::Block* block) { | 150 | IR::Value ReadVariable(Type variable, IR::Block* root_block) { |
| 131 | const ValueMap& def{current_def[variable]}; | 151 | boost::container::small_vector<ReadState<Type>, 64> stack{ |
| 132 | if (const auto it{def.find(block)}; it != def.end()) { | 152 | ReadState<Type>(nullptr), |
| 133 | return it->second; | 153 | ReadState<Type>(root_block), |
| 134 | } | 154 | }; |
| 135 | return ReadVariableRecursive(variable, block); | 155 | const auto prepare_phi_operand{[&] { |
| 156 | if (stack.back().pred_it == stack.back().pred_end) { | ||
| 157 | IR::Inst* const phi{stack.back().phi}; | ||
| 158 | IR::Block* const block{stack.back().block}; | ||
| 159 | const IR::Value result{TryRemoveTrivialPhi(*phi, block, UndefOpcode(variable))}; | ||
| 160 | stack.pop_back(); | ||
| 161 | stack.back().result = result; | ||
| 162 | WriteVariable(variable, block, result); | ||
| 163 | } else { | ||
| 164 | IR::Block* const imm_pred{*stack.back().pred_it}; | ||
| 165 | stack.back().pc = Status::PushPhiArgument; | ||
| 166 | stack.emplace_back(imm_pred); | ||
| 167 | } | ||
| 168 | }}; | ||
| 169 | do { | ||
| 170 | IR::Block* const block{stack.back().block}; | ||
| 171 | switch (stack.back().pc) { | ||
| 172 | case Status::Start: { | ||
| 173 | const ValueMap& def{current_def[variable]}; | ||
| 174 | if (const auto it{def.find(block)}; it != def.end()) { | ||
| 175 | stack.back().result = it->second; | ||
| 176 | } else if (!sealed_blocks.contains(block)) { | ||
| 177 | // Incomplete CFG | ||
| 178 | IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 179 | incomplete_phis[block].insert_or_assign(variable, phi); | ||
| 180 | stack.back().result = IR::Value{&*phi}; | ||
| 181 | } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | ||
| 182 | imm_preds.size() == 1) { | ||
| 183 | // Optimize the common case of one predecessor: no phi needed | ||
| 184 | stack.back().pc = Status::SetValue; | ||
| 185 | stack.emplace_back(imm_preds.front()); | ||
| 186 | break; | ||
| 187 | } else { | ||
| 188 | // Break potential cycles with operandless phi | ||
| 189 | IR::Inst* const phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 190 | WriteVariable(variable, block, IR::Value{phi}); | ||
| 191 | |||
| 192 | stack.back().phi = phi; | ||
| 193 | stack.back().pred_it = imm_preds.data(); | ||
| 194 | stack.back().pred_end = imm_preds.data() + imm_preds.size(); | ||
| 195 | prepare_phi_operand(); | ||
| 196 | break; | ||
| 197 | } | ||
| 198 | } | ||
| 199 | [[fallthrough]]; | ||
| 200 | case Status::SetValue: { | ||
| 201 | const IR::Value result{stack.back().result}; | ||
| 202 | WriteVariable(variable, block, result); | ||
| 203 | stack.pop_back(); | ||
| 204 | stack.back().result = result; | ||
| 205 | break; | ||
| 206 | } | ||
| 207 | case Status::PushPhiArgument: { | ||
| 208 | IR::Inst* const phi{stack.back().phi}; | ||
| 209 | phi->AddPhiOperand(*stack.back().pred_it, stack.back().result); | ||
| 210 | ++stack.back().pred_it; | ||
| 211 | } | ||
| 212 | [[fallthrough]]; | ||
| 213 | case Status::PreparePhiArgument: | ||
| 214 | prepare_phi_operand(); | ||
| 215 | break; | ||
| 216 | } | ||
| 217 | } while (stack.size() > 1); | ||
| 218 | return stack.back().result; | ||
| 136 | } | 219 | } |
| 137 | 220 | ||
| 138 | void SealBlock(IR::Block* block) { | 221 | void SealBlock(IR::Block* block) { |
| @@ -147,29 +230,6 @@ public: | |||
| 147 | 230 | ||
| 148 | private: | 231 | private: |
| 149 | template <typename Type> | 232 | template <typename Type> |
| 150 | IR::Value ReadVariableRecursive(Type variable, IR::Block* block) { | ||
| 151 | IR::Value val; | ||
| 152 | if (!sealed_blocks.contains(block)) { | ||
| 153 | // Incomplete CFG | ||
| 154 | IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 155 | incomplete_phis[block].insert_or_assign(variable, phi); | ||
| 156 | val = IR::Value{&*phi}; | ||
| 157 | } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | ||
| 158 | imm_preds.size() == 1) { | ||
| 159 | // Optimize the common case of one predecessor: no phi needed | ||
| 160 | val = ReadVariable(variable, imm_preds.front()); | ||
| 161 | } else { | ||
| 162 | // Break potential cycles with operandless phi | ||
| 163 | IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||
| 164 | val = IR::Value{&phi_inst}; | ||
| 165 | WriteVariable(variable, block, val); | ||
| 166 | val = AddPhiOperands(variable, phi_inst, block); | ||
| 167 | } | ||
| 168 | WriteVariable(variable, block, val); | ||
| 169 | return val; | ||
| 170 | } | ||
| 171 | |||
| 172 | template <typename Type> | ||
| 173 | IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { | 233 | IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { |
| 174 | for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { | 234 | for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { |
| 175 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); | 235 | phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); |