shader: Move recursive SSA rewrite to the heap
This commit is contained in:
		| @@ -119,6 +119,26 @@ IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept { | |||||||
|     return inst.Opcode() == IR::Opcode::Phi; |     return inst.Opcode() == IR::Opcode::Phi; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | enum class Status { | ||||||
|  |     Start, | ||||||
|  |     SetValue, | ||||||
|  |     PreparePhiArgument, | ||||||
|  |     PushPhiArgument, | ||||||
|  | }; | ||||||
|  |  | ||||||
|  | template <typename Type> | ||||||
|  | struct ReadState { | ||||||
|  |     ReadState(IR::Block* block_) : block{block_} {} | ||||||
|  |     ReadState() = default; | ||||||
|  |  | ||||||
|  |     IR::Block* block{}; | ||||||
|  |     IR::Value result{}; | ||||||
|  |     IR::Inst* phi{}; | ||||||
|  |     IR::Block* const* pred_it{}; | ||||||
|  |     IR::Block* const* pred_end{}; | ||||||
|  |     Status pc{Status::Start}; | ||||||
|  | }; | ||||||
|  |  | ||||||
| class Pass { | class Pass { | ||||||
| public: | public: | ||||||
|     template <typename Type> |     template <typename Type> | ||||||
| @@ -127,12 +147,75 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     template <typename Type> |     template <typename Type> | ||||||
|     IR::Value ReadVariable(Type variable, IR::Block* block) { |     IR::Value ReadVariable(Type variable, IR::Block* root_block) { | ||||||
|  |         boost::container::small_vector<ReadState<Type>, 64> stack{ | ||||||
|  |             ReadState<Type>(nullptr), | ||||||
|  |             ReadState<Type>(root_block), | ||||||
|  |         }; | ||||||
|  |         const auto prepare_phi_operand{[&] { | ||||||
|  |             if (stack.back().pred_it == stack.back().pred_end) { | ||||||
|  |                 IR::Inst* const phi{stack.back().phi}; | ||||||
|  |                 IR::Block* const block{stack.back().block}; | ||||||
|  |                 const IR::Value result{TryRemoveTrivialPhi(*phi, block, UndefOpcode(variable))}; | ||||||
|  |                 stack.pop_back(); | ||||||
|  |                 stack.back().result = result; | ||||||
|  |                 WriteVariable(variable, block, result); | ||||||
|  |             } else { | ||||||
|  |                 IR::Block* const imm_pred{*stack.back().pred_it}; | ||||||
|  |                 stack.back().pc = Status::PushPhiArgument; | ||||||
|  |                 stack.emplace_back(imm_pred); | ||||||
|  |             } | ||||||
|  |         }}; | ||||||
|  |         do { | ||||||
|  |             IR::Block* const block{stack.back().block}; | ||||||
|  |             switch (stack.back().pc) { | ||||||
|  |             case Status::Start: { | ||||||
|                 const ValueMap& def{current_def[variable]}; |                 const ValueMap& def{current_def[variable]}; | ||||||
|                 if (const auto it{def.find(block)}; it != def.end()) { |                 if (const auto it{def.find(block)}; it != def.end()) { | ||||||
|             return it->second; |                     stack.back().result = it->second; | ||||||
|  |                 } else if (!sealed_blocks.contains(block)) { | ||||||
|  |                     // Incomplete CFG | ||||||
|  |                     IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||||||
|  |                     incomplete_phis[block].insert_or_assign(variable, phi); | ||||||
|  |                     stack.back().result = IR::Value{&*phi}; | ||||||
|  |                 } else if (const std::span imm_preds{block->ImmediatePredecessors()}; | ||||||
|  |                            imm_preds.size() == 1) { | ||||||
|  |                     // Optimize the common case of one predecessor: no phi needed | ||||||
|  |                     stack.back().pc = Status::SetValue; | ||||||
|  |                     stack.emplace_back(imm_preds.front()); | ||||||
|  |                     break; | ||||||
|  |                 } else { | ||||||
|  |                     // Break potential cycles with operandless phi | ||||||
|  |                     IR::Inst* const phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | ||||||
|  |                     WriteVariable(variable, block, IR::Value{phi}); | ||||||
|  |  | ||||||
|  |                     stack.back().phi = phi; | ||||||
|  |                     stack.back().pred_it = imm_preds.data(); | ||||||
|  |                     stack.back().pred_end = imm_preds.data() + imm_preds.size(); | ||||||
|  |                     prepare_phi_operand(); | ||||||
|  |                     break; | ||||||
|                 } |                 } | ||||||
|         return ReadVariableRecursive(variable, block); |             } | ||||||
|  |                 [[fallthrough]]; | ||||||
|  |             case Status::SetValue: { | ||||||
|  |                 const IR::Value result{stack.back().result}; | ||||||
|  |                 WriteVariable(variable, block, result); | ||||||
|  |                 stack.pop_back(); | ||||||
|  |                 stack.back().result = result; | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |             case Status::PushPhiArgument: { | ||||||
|  |                 IR::Inst* const phi{stack.back().phi}; | ||||||
|  |                 phi->AddPhiOperand(*stack.back().pred_it, stack.back().result); | ||||||
|  |                 ++stack.back().pred_it; | ||||||
|  |             } | ||||||
|  |                 [[fallthrough]]; | ||||||
|  |             case Status::PreparePhiArgument: | ||||||
|  |                 prepare_phi_operand(); | ||||||
|  |                 break; | ||||||
|  |             } | ||||||
|  |         } while (stack.size() > 1); | ||||||
|  |         return stack.back().result; | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     void SealBlock(IR::Block* block) { |     void SealBlock(IR::Block* block) { | ||||||
| @@ -146,29 +229,6 @@ public: | |||||||
|     } |     } | ||||||
|  |  | ||||||
| private: | private: | ||||||
|     template <typename Type> |  | ||||||
|     IR::Value ReadVariableRecursive(Type variable, IR::Block* block) { |  | ||||||
|         IR::Value val; |  | ||||||
|         if (!sealed_blocks.contains(block)) { |  | ||||||
|             // Incomplete CFG |  | ||||||
|             IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; |  | ||||||
|             incomplete_phis[block].insert_or_assign(variable, phi); |  | ||||||
|             val = IR::Value{&*phi}; |  | ||||||
|         } else if (const std::span imm_preds{block->ImmediatePredecessors()}; |  | ||||||
|                    imm_preds.size() == 1) { |  | ||||||
|             // Optimize the common case of one predecessor: no phi needed |  | ||||||
|             val = ReadVariable(variable, imm_preds.front()); |  | ||||||
|         } else { |  | ||||||
|             // Break potential cycles with operandless phi |  | ||||||
|             IR::Inst& phi_inst{*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; |  | ||||||
|             val = IR::Value{&phi_inst}; |  | ||||||
|             WriteVariable(variable, block, val); |  | ||||||
|             val = AddPhiOperands(variable, phi_inst, block); |  | ||||||
|         } |  | ||||||
|         WriteVariable(variable, block, val); |  | ||||||
|         return val; |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     template <typename Type> |     template <typename Type> | ||||||
|     IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { |     IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { | ||||||
|         for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { |         for (IR::Block* const imm_pred : block->ImmediatePredecessors()) { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user