| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | // Copyright 2021 yuzu Emulator Project
 | 
					
						
							|  |  |  | // Licensed under GPLv2 or any later version
 | 
					
						
							|  |  |  | // Refer to the license.txt file included.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // This file implements the SSA rewriting algorithm proposed in
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //      Simple and Efficient Construction of Static Single Assignment Form.
 | 
					
						
							| 
									
										
										
										
											2021-07-09 17:11:47 -03:00
										 |  |  | //      Braun M., Buchwald S., Hack S., Leiba R., Mallon C., Zwinkau A. (2013)
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | //      In: Jhala R., De Bosschere K. (eds)
 | 
					
						
							|  |  |  | //      Compiler Construction. CC 2013.
 | 
					
						
							|  |  |  | //      Lecture Notes in Computer Science, vol 7791.
 | 
					
						
							|  |  |  | //      Springer, Berlin, Heidelberg
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | //      https://link.springer.com/chapter/10.1007/978-3-642-37051-9_6
 | 
					
						
							|  |  |  | //
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  | #include <ranges>
 | 
					
						
							|  |  |  | #include <span>
 | 
					
						
							|  |  |  | #include <variant>
 | 
					
						
							|  |  |  | #include <vector>
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | #include <boost/container/flat_map.hpp>
 | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  | #include <boost/container/flat_set.hpp>
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | #include "shader_recompiler/frontend/ir/basic_block.h"
 | 
					
						
							| 
									
										
										
										
											2021-02-05 23:11:23 -03:00
										 |  |  | #include "shader_recompiler/frontend/ir/opcodes.h"
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | #include "shader_recompiler/frontend/ir/pred.h"
 | 
					
						
							|  |  |  | #include "shader_recompiler/frontend/ir/reg.h"
 | 
					
						
							| 
									
										
										
										
											2021-04-21 00:35:47 -03:00
										 |  |  | #include "shader_recompiler/frontend/ir/value.h"
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | #include "shader_recompiler/ir_opt/passes.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | namespace Shader::Optimization { | 
					
						
							|  |  |  | namespace { | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  | struct FlagTag { | 
					
						
							|  |  |  |     auto operator<=>(const FlagTag&) const noexcept = default; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  | struct ZeroFlagTag : FlagTag {}; | 
					
						
							|  |  |  | struct SignFlagTag : FlagTag {}; | 
					
						
							|  |  |  | struct CarryFlagTag : FlagTag {}; | 
					
						
							|  |  |  | struct OverflowFlagTag : FlagTag {}; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  | struct GotoVariable : FlagTag { | 
					
						
							|  |  |  |     GotoVariable() = default; | 
					
						
							|  |  |  |     explicit GotoVariable(u32 index_) : index{index_} {} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |     auto operator<=>(const GotoVariable&) const noexcept = default; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     u32 index; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  | struct LoopSafetyVariable { | 
					
						
							|  |  |  |     LoopSafetyVariable() = default; | 
					
						
							|  |  |  |     explicit LoopSafetyVariable(u32 index_) : index{index_} {} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     auto operator<=>(const LoopSafetyVariable&) const noexcept = default; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     u32 index; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  | struct IndirectBranchVariable { | 
					
						
							|  |  |  |     auto operator<=>(const IndirectBranchVariable&) const noexcept = default; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  | using Variant = | 
					
						
							|  |  |  |     std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, OverflowFlagTag, | 
					
						
							|  |  |  |                  GotoVariable, LoopSafetyVariable, IndirectBranchVariable>; | 
					
						
							|  |  |  | using ValueMap = boost::container::flat_map<IR::Block*, IR::Value>; | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | struct DefTable { | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, IR::Reg variable) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return block->SsaRegValue(variable); | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, IR::Reg variable, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         block->SetSsaRegValue(variable, value); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, IR::Pred variable) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return preds[IR::PredIndex(variable)][block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, IR::Pred variable, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         preds[IR::PredIndex(variable)].insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, GotoVariable variable) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return goto_vars[variable.index][block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, GotoVariable variable, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         goto_vars[variable.index].insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, LoopSafetyVariable variable) { | 
					
						
							|  |  |  |         return loop_safety_vars[variable.index][block]; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     void SetDef(IR::Block* block, LoopSafetyVariable variable, const IR::Value& value) { | 
					
						
							|  |  |  |         loop_safety_vars[variable.index].insert_or_assign(block, value); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     const IR::Value& Def(IR::Block* block, IndirectBranchVariable) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return indirect_branch_var[block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, IndirectBranchVariable, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         indirect_branch_var.insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, ZeroFlagTag) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return zero_flag[block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, ZeroFlagTag, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         zero_flag.insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, SignFlagTag) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return sign_flag[block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, SignFlagTag, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         sign_flag.insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, CarryFlagTag) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return carry_flag[block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, CarryFlagTag, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         carry_flag.insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     const IR::Value& Def(IR::Block* block, OverflowFlagTag) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         return overflow_flag[block]; | 
					
						
							|  |  |  |     } | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     void SetDef(IR::Block* block, OverflowFlagTag, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         overflow_flag.insert_or_assign(block, value); | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     std::array<ValueMap, IR::NUM_USER_PREDS> preds; | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     boost::container::flat_map<u32, ValueMap> goto_vars; | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     boost::container::flat_map<u32, ValueMap> loop_safety_vars; | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  |     ValueMap indirect_branch_var; | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  |     ValueMap zero_flag; | 
					
						
							|  |  |  |     ValueMap sign_flag; | 
					
						
							|  |  |  |     ValueMap carry_flag; | 
					
						
							|  |  |  |     ValueMap overflow_flag; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | IR::Opcode UndefOpcode(IR::Reg) noexcept { | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     return IR::Opcode::UndefU32; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | IR::Opcode UndefOpcode(IR::Pred) noexcept { | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     return IR::Opcode::UndefU1; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  | IR::Opcode UndefOpcode(const FlagTag&) noexcept { | 
					
						
							| 
									
										
										
										
											2021-02-11 16:39:06 -03:00
										 |  |  |     return IR::Opcode::UndefU1; | 
					
						
							| 
									
										
										
										
											2021-02-05 05:58:02 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  | IR::Opcode UndefOpcode(const LoopSafetyVariable&) noexcept { | 
					
						
							|  |  |  |     return IR::Opcode::UndefU32; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  | IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept { | 
					
						
							|  |  |  |     return IR::Opcode::UndefU32; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  | 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}; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | class Pass { | 
					
						
							|  |  |  | public: | 
					
						
							| 
									
										
										
										
											2021-02-24 20:31:15 -05:00
										 |  |  |     template <typename Type> | 
					
						
							|  |  |  |     void WriteVariable(Type variable, IR::Block* block, const IR::Value& value) { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |         current_def.SetDef(block, variable, value); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-24 20:31:15 -05:00
										 |  |  |     template <typename Type> | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  |     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: { | 
					
						
							| 
									
										
										
										
											2021-04-21 00:27:55 -03:00
										 |  |  |                 if (const IR::Value& def = current_def.Def(block, variable); !def.IsEmpty()) { | 
					
						
							|  |  |  |                     stack.back().result = def; | 
					
						
							| 
									
										
										
										
											2021-04-21 04:58:23 -03:00
										 |  |  |                 } else if (!block->IsSsaSealed()) { | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  |                     // Incomplete CFG
 | 
					
						
							|  |  |  |                     IR::Inst* phi{&*block->PrependNewInst(block->begin(), IR::Opcode::Phi)}; | 
					
						
							| 
									
										
										
										
											2021-04-17 16:40:35 -03:00
										 |  |  |                     phi->SetFlags(IR::TypeOf(UndefOpcode(variable))); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  |                     incomplete_phis[block].insert_or_assign(variable, phi); | 
					
						
							|  |  |  |                     stack.back().result = IR::Value{&*phi}; | 
					
						
							| 
									
										
										
										
											2021-05-14 00:40:54 -03:00
										 |  |  |                 } else if (const std::span imm_preds = block->ImmPredecessors(); | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  |                            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)}; | 
					
						
							| 
									
										
										
										
											2021-04-17 16:40:35 -03:00
										 |  |  |                     phi->SetFlags(IR::TypeOf(UndefOpcode(variable))); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-04-05 19:10:55 -03:00
										 |  |  |                     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; | 
					
						
							|  |  |  |                 } | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |                 [[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; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |     void SealBlock(IR::Block* block) { | 
					
						
							|  |  |  |         const auto it{incomplete_phis.find(block)}; | 
					
						
							|  |  |  |         if (it != incomplete_phis.end()) { | 
					
						
							|  |  |  |             for (auto& [variant, phi] : it->second) { | 
					
						
							|  |  |  |                 std::visit([&](auto& variable) { AddPhiOperands(variable, *phi, block); }, variant); | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-04-21 04:58:23 -03:00
										 |  |  |         block->SsaSeal(); | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | private: | 
					
						
							| 
									
										
										
										
											2021-02-24 20:31:15 -05:00
										 |  |  |     template <typename Type> | 
					
						
							|  |  |  |     IR::Value AddPhiOperands(Type variable, IR::Inst& phi, IR::Block* block) { | 
					
						
							| 
									
										
										
										
											2021-05-14 00:40:54 -03:00
										 |  |  |         for (IR::Block* const imm_pred : block->ImmPredecessors()) { | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |             phi.AddPhiOperand(imm_pred, ReadVariable(variable, imm_pred)); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |         } | 
					
						
							|  |  |  |         return TryRemoveTrivialPhi(phi, block, UndefOpcode(variable)); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-06 02:38:22 -03:00
										 |  |  |     IR::Value TryRemoveTrivialPhi(IR::Inst& phi, IR::Block* block, IR::Opcode undef_opcode) { | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |         IR::Value same; | 
					
						
							| 
									
										
										
										
											2021-02-06 02:38:22 -03:00
										 |  |  |         const size_t num_args{phi.NumArgs()}; | 
					
						
							|  |  |  |         for (size_t arg_index = 0; arg_index < num_args; ++arg_index) { | 
					
						
							|  |  |  |             const IR::Value& op{phi.Arg(arg_index)}; | 
					
						
							| 
									
										
										
										
											2021-03-19 19:28:31 -03:00
										 |  |  |             if (op.Resolve() == same.Resolve() || op == IR::Value{&phi}) { | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |                 // Unique value or self-reference
 | 
					
						
							|  |  |  |                 continue; | 
					
						
							|  |  |  |             } | 
					
						
							|  |  |  |             if (!same.IsEmpty()) { | 
					
						
							|  |  |  |                 // The phi merges at least two values: not trivial
 | 
					
						
							| 
									
										
										
										
											2021-02-06 02:38:22 -03:00
										 |  |  |                 return IR::Value{&phi}; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |             } | 
					
						
							|  |  |  |             same = op; | 
					
						
							|  |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-04-22 18:33:49 -03:00
										 |  |  |         // Remove the phi node from the block, it will be reinserted
 | 
					
						
							|  |  |  |         IR::Block::InstructionList& list{block->Instructions()}; | 
					
						
							|  |  |  |         list.erase(IR::Block::InstructionList::s_iterator_to(phi)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         // Find the first non-phi instruction and use it as an insertion point
 | 
					
						
							| 
									
										
										
										
											2021-05-14 04:48:46 -03:00
										 |  |  |         IR::Block::iterator reinsert_point{std::ranges::find_if_not(list, IR::IsPhi)}; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |         if (same.IsEmpty()) { | 
					
						
							|  |  |  |             // The phi is unreachable or in the start block
 | 
					
						
							| 
									
										
										
										
											2021-04-22 18:33:49 -03:00
										 |  |  |             // Insert an undefined instruction and make it the phi node replacement
 | 
					
						
							|  |  |  |             // The "phi" node reinsertion point is specified after this instruction
 | 
					
						
							|  |  |  |             reinsert_point = block->PrependNewInst(reinsert_point, undef_opcode); | 
					
						
							|  |  |  |             same = IR::Value{&*reinsert_point}; | 
					
						
							|  |  |  |             ++reinsert_point; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |         } | 
					
						
							| 
									
										
										
										
											2021-04-22 18:33:49 -03:00
										 |  |  |         // Reinsert the phi node and reroute all its uses to the "same" value
 | 
					
						
							|  |  |  |         list.insert(reinsert_point, phi); | 
					
						
							| 
									
										
										
										
											2021-02-06 02:38:22 -03:00
										 |  |  |         phi.ReplaceUsesWith(same); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |         // TODO: Try to recursively remove all phi users, which might have become trivial
 | 
					
						
							|  |  |  |         return same; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |     boost::container::flat_map<IR::Block*, boost::container::flat_map<Variant, IR::Inst*>> | 
					
						
							|  |  |  |         incomplete_phis; | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     DefTable current_def; | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2021-02-14 01:24:32 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | 
					
						
							| 
									
										
										
										
											2021-04-05 22:25:22 -04:00
										 |  |  |     switch (inst.GetOpcode()) { | 
					
						
							| 
									
										
										
										
											2021-02-14 01:24:32 -03:00
										 |  |  |     case IR::Opcode::SetRegister: | 
					
						
							|  |  |  |         if (const IR::Reg reg{inst.Arg(0).Reg()}; reg != IR::Reg::RZ) { | 
					
						
							|  |  |  |             pass.WriteVariable(reg, block, inst.Arg(1)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::SetPred: | 
					
						
							|  |  |  |         if (const IR::Pred pred{inst.Arg(0).Pred()}; pred != IR::Pred::PT) { | 
					
						
							|  |  |  |             pass.WriteVariable(pred, block, inst.Arg(1)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::SetGotoVariable: | 
					
						
							|  |  |  |         pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     case IR::Opcode::SetLoopSafetyVariable: | 
					
						
							|  |  |  |         pass.WriteVariable(LoopSafetyVariable{inst.Arg(0).U32()}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  |     case IR::Opcode::SetIndirectBranchVariable: | 
					
						
							|  |  |  |         pass.WriteVariable(IndirectBranchVariable{}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-02-14 01:24:32 -03:00
										 |  |  |     case IR::Opcode::SetZFlag: | 
					
						
							|  |  |  |         pass.WriteVariable(ZeroFlagTag{}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::SetSFlag: | 
					
						
							|  |  |  |         pass.WriteVariable(SignFlagTag{}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::SetCFlag: | 
					
						
							|  |  |  |         pass.WriteVariable(CarryFlagTag{}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::SetOFlag: | 
					
						
							|  |  |  |         pass.WriteVariable(OverflowFlagTag{}, block, inst.Arg(0)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetRegister: | 
					
						
							|  |  |  |         if (const IR::Reg reg{inst.Arg(0).Reg()}; reg != IR::Reg::RZ) { | 
					
						
							|  |  |  |             inst.ReplaceUsesWith(pass.ReadVariable(reg, block)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetPred: | 
					
						
							|  |  |  |         if (const IR::Pred pred{inst.Arg(0).Pred()}; pred != IR::Pred::PT) { | 
					
						
							|  |  |  |             inst.ReplaceUsesWith(pass.ReadVariable(pred, block)); | 
					
						
							|  |  |  |         } | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetGotoVariable: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-07-08 17:22:31 -04:00
										 |  |  |     case IR::Opcode::GetLoopSafetyVariable: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(LoopSafetyVariable{inst.Arg(0).U32()}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-03-27 22:30:24 +01:00
										 |  |  |     case IR::Opcode::GetIndirectBranchVariable: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(IndirectBranchVariable{}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							| 
									
										
										
										
											2021-02-14 01:24:32 -03:00
										 |  |  |     case IR::Opcode::GetZFlag: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(ZeroFlagTag{}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetSFlag: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(SignFlagTag{}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetCFlag: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(CarryFlagTag{}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     case IR::Opcode::GetOFlag: | 
					
						
							|  |  |  |         inst.ReplaceUsesWith(pass.ReadVariable(OverflowFlagTag{}, block)); | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     default: | 
					
						
							|  |  |  |         break; | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | void VisitBlock(Pass& pass, IR::Block* block) { | 
					
						
							|  |  |  |     for (IR::Inst& inst : block->Instructions()) { | 
					
						
							|  |  |  |         VisitInst(pass, block, inst); | 
					
						
							|  |  |  |     } | 
					
						
							|  |  |  |     pass.SealBlock(block); | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  | } // Anonymous namespace
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-03-14 03:41:05 -03:00
										 |  |  | void SsaRewritePass(IR::Program& program) { | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     Pass pass; | 
					
						
							| 
									
										
										
										
											2021-03-14 03:41:05 -03:00
										 |  |  |     for (IR::Block* const block : program.post_order_blocks | std::views::reverse) { | 
					
						
							| 
									
										
										
										
											2021-02-14 20:15:42 -03:00
										 |  |  |         VisitBlock(pass, block); | 
					
						
							| 
									
										
										
										
											2021-02-02 21:07:00 -03:00
										 |  |  |     } | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } // namespace Shader::Optimization
 |