shader: Move loop safety tests to code emission
This commit is contained in:
		| @@ -71,6 +71,7 @@ public: | ||||
|     std::string_view stage_name = "invalid"; | ||||
|     std::string_view attrib_name = "invalid"; | ||||
|  | ||||
|     u32 num_safety_loop_vars{}; | ||||
|     bool uses_y_direction{}; | ||||
| }; | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,8 @@ | ||||
| #include <string> | ||||
| #include <tuple> | ||||
|  | ||||
| #include "common/div_ceil.h" | ||||
| #include "common/settings.h" | ||||
| #include "shader_recompiler/backend/bindings.h" | ||||
| #include "shader_recompiler/backend/glasm/emit_context.h" | ||||
| #include "shader_recompiler/backend/glasm/emit_glasm.h" | ||||
| @@ -222,6 +224,14 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) { | ||||
|             ctx.Add("REP;"); | ||||
|             break; | ||||
|         case IR::AbstractSyntaxNode::Type::Repeat: | ||||
|             if (!Settings::values.disable_shader_loop_safety_checks) { | ||||
|                 const u32 loop_index{ctx.num_safety_loop_vars++}; | ||||
|                 const u32 vector_index{loop_index / 4}; | ||||
|                 const char component{"xyzw"[loop_index % 4]}; | ||||
|                 ctx.Add("SUB.S.CC loop{}.{},loop{}.{},1;" | ||||
|                         "BRK(LT.{});", | ||||
|                         vector_index, component, vector_index, component, component); | ||||
|             } | ||||
|             if (node.data.repeat.cond.IsImmediate()) { | ||||
|                 if (node.data.repeat.cond.U1()) { | ||||
|                     ctx.Add("ENDREP;"); | ||||
| @@ -425,6 +435,10 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I | ||||
|     if (program.info.uses_fswzadd) { | ||||
|         header += "FSWZA[4],FSWZB[4],"; | ||||
|     } | ||||
|     const u32 num_safety_loop_vectors{Common::DivCeil(ctx.num_safety_loop_vars, 4u)}; | ||||
|     for (u32 index = 0; index < num_safety_loop_vectors; ++index) { | ||||
|         header += fmt::format("loop{},", index); | ||||
|     } | ||||
|     header += "RC;" | ||||
|               "LONG TEMP "; | ||||
|     for (size_t index = 0; index < ctx.reg_alloc.NumUsedLongRegisters(); ++index) { | ||||
| @@ -441,6 +455,9 @@ std::string EmitGLASM(const Profile& profile, const RuntimeInfo& runtime_info, I | ||||
|                   "MOV.F FSWZB[2],1;" | ||||
|                   "MOV.F FSWZB[3],-1;"; | ||||
|     } | ||||
|     for (u32 index = 0; index < num_safety_loop_vectors; ++index) { | ||||
|         header += fmt::format("MOV.S loop{},{{0x2000,0x2000,0x2000,0x2000}};", index); | ||||
|     } | ||||
|     if (ctx.uses_y_direction) { | ||||
|         header += "PARAM y_direction[1]={state.material.front.ambient};"; | ||||
|     } | ||||
|   | ||||
| @@ -42,8 +42,6 @@ void EmitSetGotoVariable(EmitContext& ctx); | ||||
| void EmitGetGotoVariable(EmitContext& ctx); | ||||
| void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitSetLoopSafetyVariable(EmitContext& ctx); | ||||
| void EmitGetLoopSafetyVariable(EmitContext& ctx); | ||||
| void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset); | ||||
| void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset); | ||||
| void EmitGetCbufU16(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, ScalarU32 offset); | ||||
|   | ||||
| @@ -153,14 +153,6 @@ void EmitGetIndirectBranchVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitSetLoopSafetyVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitGetLoopSafetyVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitGetZFlag(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|   | ||||
| @@ -153,6 +153,8 @@ public: | ||||
|     std::vector<TextureImageDefinition> images; | ||||
|     std::array<std::array<GenericElementInfo, 4>, 32> output_generics{}; | ||||
|  | ||||
|     u32 num_safety_loop_vars{}; | ||||
|  | ||||
|     bool uses_y_direction{}; | ||||
|     bool uses_cc_carry{}; | ||||
|  | ||||
|   | ||||
| @@ -6,6 +6,7 @@ | ||||
| #include <string> | ||||
|  | ||||
| #include "common/alignment.h" | ||||
| #include "common/settings.h" | ||||
| #include "shader_recompiler/backend/glsl/emit_context.h" | ||||
| #include "shader_recompiler/backend/glsl/emit_glsl.h" | ||||
| #include "shader_recompiler/backend/glsl/emit_glsl_instructions.h" | ||||
| @@ -156,7 +157,12 @@ void EmitCode(EmitContext& ctx, const IR::Program& program) { | ||||
|             ctx.Add("for(;;){{"); | ||||
|             break; | ||||
|         case IR::AbstractSyntaxNode::Type::Repeat: | ||||
|             if (Settings::values.disable_shader_loop_safety_checks) { | ||||
|                 ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond)); | ||||
|             } else { | ||||
|                 ctx.Add("if(--loop{}<0 || !{}){{break;}}}}", ctx.num_safety_loop_vars++, | ||||
|                         ctx.var_alloc.Consume(node.data.repeat.cond)); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             throw NotImplementedException("AbstractSyntaxNode Type {}", node.type); | ||||
| @@ -198,6 +204,9 @@ void DefineVariables(const EmitContext& ctx, std::string& header) { | ||||
|                                   ctx.var_alloc.Representation(index, type), type_name); | ||||
|         } | ||||
|     } | ||||
|     for (u32 i = 0; i < ctx.num_safety_loop_vars; ++i) { | ||||
|         header += fmt::format("int loop{}=0x2000;", i); | ||||
|     } | ||||
| } | ||||
| } // Anonymous namespace | ||||
|  | ||||
|   | ||||
| @@ -44,8 +44,6 @@ void EmitSetGotoVariable(EmitContext& ctx); | ||||
| void EmitGetGotoVariable(EmitContext& ctx); | ||||
| void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitSetLoopSafetyVariable(EmitContext& ctx); | ||||
| void EmitGetLoopSafetyVariable(EmitContext& ctx); | ||||
| void EmitGetCbufU8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||
|                    const IR::Value& offset); | ||||
| void EmitGetCbufS8(EmitContext& ctx, IR::Inst& inst, const IR::Value& binding, | ||||
|   | ||||
| @@ -46,14 +46,6 @@ void EmitGetIndirectBranchVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitSetLoopSafetyVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitGetLoopSafetyVariable(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|  | ||||
| void EmitGetZFlag(EmitContext& ctx) { | ||||
|     NotImplemented(); | ||||
| } | ||||
|   | ||||
| @@ -8,6 +8,7 @@ | ||||
| #include <utility> | ||||
| #include <vector> | ||||
|  | ||||
| #include "common/settings.h" | ||||
| #include "shader_recompiler/backend/spirv/emit_spirv.h" | ||||
| #include "shader_recompiler/backend/spirv/emit_spirv_instructions.h" | ||||
| #include "shader_recompiler/frontend/ir/basic_block.h" | ||||
| @@ -151,9 +152,25 @@ void Traverse(EmitContext& ctx, IR::Program& program) { | ||||
|             } | ||||
|             break; | ||||
|         case IR::AbstractSyntaxNode::Type::Repeat: { | ||||
|             Id cond{ctx.Def(node.data.repeat.cond)}; | ||||
|             if (!Settings::values.disable_shader_loop_safety_checks) { | ||||
|                 const Id pointer_type{ctx.TypePointer(spv::StorageClass::Private, ctx.U32[1])}; | ||||
|                 const Id safety_counter{ctx.AddGlobalVariable( | ||||
|                     pointer_type, spv::StorageClass::Private, ctx.Const(0x2000u))}; | ||||
|                 if (ctx.profile.supported_spirv >= 0x00010400) { | ||||
|                     ctx.interfaces.push_back(safety_counter); | ||||
|                 } | ||||
|                 const Id old_counter{ctx.OpLoad(ctx.U32[1], safety_counter)}; | ||||
|                 const Id new_counter{ctx.OpISub(ctx.U32[1], old_counter, ctx.Const(1u))}; | ||||
|                 ctx.OpStore(safety_counter, new_counter); | ||||
|  | ||||
|                 const Id safety_cond{ | ||||
|                     ctx.OpSGreaterThanEqual(ctx.U1, new_counter, ctx.u32_zero_value)}; | ||||
|                 cond = ctx.OpLogicalAnd(ctx.U1, cond, safety_cond); | ||||
|             } | ||||
|             const Id loop_header_label{node.data.repeat.loop_header->Definition<Id>()}; | ||||
|             const Id merge_label{node.data.repeat.merge->Definition<Id>()}; | ||||
|             ctx.OpBranchConditional(ctx.Def(node.data.repeat.cond), loop_header_label, merge_label); | ||||
|             ctx.OpBranchConditional(cond, loop_header_label, merge_label); | ||||
|             break; | ||||
|         } | ||||
|         case IR::AbstractSyntaxNode::Type::Return: | ||||
|   | ||||
| @@ -198,14 +198,6 @@ void EmitGetIndirectBranchVariable(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| void EmitSetLoopSafetyVariable(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| void EmitGetLoopSafetyVariable(EmitContext&) { | ||||
|     throw LogicError("Unreachable instruction"); | ||||
| } | ||||
|  | ||||
| Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset) { | ||||
|     if (ctx.profile.support_descriptor_aliasing && ctx.profile.support_int8) { | ||||
|         const Id load{GetCbuf(ctx, ctx.U8, &UniformDefinitions::U8, sizeof(u8), binding, offset)}; | ||||
|   | ||||
| @@ -43,8 +43,6 @@ void EmitSetGotoVariable(EmitContext& ctx); | ||||
| void EmitGetGotoVariable(EmitContext& ctx); | ||||
| void EmitSetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitGetIndirectBranchVariable(EmitContext& ctx); | ||||
| void EmitSetLoopSafetyVariable(EmitContext& ctx); | ||||
| void EmitGetLoopSafetyVariable(EmitContext& ctx); | ||||
| Id EmitGetCbufU8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||||
| Id EmitGetCbufS8(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||||
| Id EmitGetCbufU16(EmitContext& ctx, const IR::Value& binding, const IR::Value& offset); | ||||
|   | ||||
| @@ -147,14 +147,6 @@ void IREmitter::SetIndirectBranchVariable(const U32& value) { | ||||
|     Inst(Opcode::SetIndirectBranchVariable, value); | ||||
| } | ||||
|  | ||||
| U32 IREmitter::GetLoopSafetyVariable(u32 id) { | ||||
|     return Inst<U32>(Opcode::GetLoopSafetyVariable, id); | ||||
| } | ||||
|  | ||||
| void IREmitter::SetLoopSafetyVariable(u32 id, const U32& counter) { | ||||
|     Inst(Opcode::SetLoopSafetyVariable, id, counter); | ||||
| } | ||||
|  | ||||
| U32 IREmitter::GetCbuf(const U32& binding, const U32& byte_offset) { | ||||
|     return Inst<U32>(Opcode::GetCbufU32, binding, byte_offset); | ||||
| } | ||||
|   | ||||
| @@ -55,9 +55,6 @@ public: | ||||
|     [[nodiscard]] U32 GetIndirectBranchVariable(); | ||||
|     void SetIndirectBranchVariable(const U32& value); | ||||
|  | ||||
|     [[nodiscard]] U32 GetLoopSafetyVariable(u32 id); | ||||
|     void SetLoopSafetyVariable(u32 id, const U32& counter); | ||||
|  | ||||
|     [[nodiscard]] U32 GetCbuf(const U32& binding, const U32& byte_offset); | ||||
|     [[nodiscard]] Value GetCbuf(const U32& binding, const U32& byte_offset, size_t bitsize, | ||||
|                                 bool is_signed); | ||||
|   | ||||
| @@ -32,8 +32,6 @@ OPCODE(GetGotoVariable,                                     U1,             U32, | ||||
| OPCODE(SetGotoVariable,                                     Void,           U32,            U1,                                                             ) | ||||
| OPCODE(GetIndirectBranchVariable,                           U32,                                                                                            ) | ||||
| OPCODE(SetIndirectBranchVariable,                           Void,           U32,                                                                            ) | ||||
| OPCODE(GetLoopSafetyVariable,                               U32,            U32,                                                                            ) | ||||
| OPCODE(SetLoopSafetyVariable,                               Void,           U32,            U32,                                                            ) | ||||
| OPCODE(GetCbufU8,                                           U32,            U32,            U32,                                                            ) | ||||
| OPCODE(GetCbufS8,                                           U32,            U32,            U32,                                                            ) | ||||
| OPCODE(GetCbufU16,                                          U32,            U32,            U32,                                                            ) | ||||
|   | ||||
| @@ -15,7 +15,6 @@ | ||||
|  | ||||
| #include <boost/intrusive/list.hpp> | ||||
|  | ||||
| #include "common/settings.h" | ||||
| #include "shader_recompiler/environment.h" | ||||
| #include "shader_recompiler/frontend/ir/basic_block.h" | ||||
| #include "shader_recompiler/frontend/ir/ir_emitter.h" | ||||
| @@ -663,7 +662,7 @@ public: | ||||
|         Visit(root_stmt, nullptr, nullptr); | ||||
|  | ||||
|         IR::Block& first_block{*syntax_list.front().data.block}; | ||||
|         IR::IREmitter ir = IR::IREmitter(first_block, first_block.begin()); | ||||
|         IR::IREmitter ir(first_block, first_block.begin()); | ||||
|         ir.Prologue(); | ||||
|     } | ||||
|  | ||||
| @@ -741,28 +740,9 @@ private: | ||||
|             } | ||||
|             case StatementType::Loop: { | ||||
|                 IR::Block* const loop_header_block{block_pool.Create(inst_pool)}; | ||||
|                 const u32 this_loop_id{loop_id++}; | ||||
|  | ||||
|                 if (Settings::values.disable_shader_loop_safety_checks) { | ||||
|                 if (current_block) { | ||||
|                     current_block->AddBranch(loop_header_block); | ||||
|                 } | ||||
|                 } else { | ||||
|                     IR::Block* const init_block{block_pool.Create(inst_pool)}; | ||||
|                     IR::IREmitter ir{*init_block}; | ||||
|  | ||||
|                     static constexpr u32 SAFETY_THRESHOLD = 0x1000; | ||||
|                     ir.SetLoopSafetyVariable(this_loop_id, ir.Imm32(SAFETY_THRESHOLD)); | ||||
|  | ||||
|                     if (current_block) { | ||||
|                         current_block->AddBranch(init_block); | ||||
|                     } | ||||
|                     init_block->AddBranch(loop_header_block); | ||||
|  | ||||
|                     auto& init_node{syntax_list.emplace_back()}; | ||||
|                     init_node.type = IR::AbstractSyntaxNode::Type::Block; | ||||
|                     init_node.data.block = init_block; | ||||
|                 } | ||||
|                 auto& header_node{syntax_list.emplace_back()}; | ||||
|                 header_node.type = IR::AbstractSyntaxNode::Type::Block; | ||||
|                 header_node.data.block = loop_header_block; | ||||
| @@ -779,16 +759,7 @@ private: | ||||
|  | ||||
|                 // The continue block is located at the end of the loop | ||||
|                 IR::IREmitter ir{*continue_block}; | ||||
|                 IR::U1 cond{VisitExpr(ir, *stmt.cond)}; | ||||
|                 if (!Settings::values.disable_shader_loop_safety_checks) { | ||||
|                     const IR::U32 old_counter{ir.GetLoopSafetyVariable(this_loop_id)}; | ||||
|                     const IR::U32 new_counter{ir.ISub(old_counter, ir.Imm32(1))}; | ||||
|                     ir.SetLoopSafetyVariable(this_loop_id, new_counter); | ||||
|  | ||||
|                     const IR::U1 safety_cond{ir.INotEqual(new_counter, ir.Imm32(0))}; | ||||
|                     cond = ir.LogicalAnd(cond, safety_cond); | ||||
|                 } | ||||
|                 cond = ir.ConditionRef(cond); | ||||
|                 const IR::U1 cond{ir.ConditionRef(VisitExpr(ir, *stmt.cond))}; | ||||
|  | ||||
|                 IR::Block* const body_block{syntax_list.at(body_block_index).data.block}; | ||||
|                 loop_header_block->AddBranch(body_block); | ||||
|   | ||||
| @@ -48,22 +48,12 @@ struct GotoVariable : FlagTag { | ||||
|     u32 index; | ||||
| }; | ||||
|  | ||||
| struct LoopSafetyVariable { | ||||
|     LoopSafetyVariable() = default; | ||||
|     explicit LoopSafetyVariable(u32 index_) : index{index_} {} | ||||
|  | ||||
|     auto operator<=>(const LoopSafetyVariable&) const noexcept = default; | ||||
|  | ||||
|     u32 index; | ||||
| }; | ||||
|  | ||||
| struct IndirectBranchVariable { | ||||
|     auto operator<=>(const IndirectBranchVariable&) const noexcept = default; | ||||
| }; | ||||
|  | ||||
| using Variant = | ||||
|     std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, OverflowFlagTag, | ||||
|                  GotoVariable, LoopSafetyVariable, IndirectBranchVariable>; | ||||
| using Variant = std::variant<IR::Reg, IR::Pred, ZeroFlagTag, SignFlagTag, CarryFlagTag, | ||||
|                              OverflowFlagTag, GotoVariable, IndirectBranchVariable>; | ||||
| using ValueMap = boost::container::flat_map<IR::Block*, IR::Value>; | ||||
|  | ||||
| struct DefTable { | ||||
| @@ -88,13 +78,6 @@ struct DefTable { | ||||
|         goto_vars[variable.index].insert_or_assign(block, value); | ||||
|     } | ||||
|  | ||||
|     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) { | ||||
|         return indirect_branch_var[block]; | ||||
|     } | ||||
| @@ -132,7 +115,6 @@ struct DefTable { | ||||
|  | ||||
|     std::array<ValueMap, IR::NUM_USER_PREDS> preds; | ||||
|     boost::container::flat_map<u32, ValueMap> goto_vars; | ||||
|     boost::container::flat_map<u32, ValueMap> loop_safety_vars; | ||||
|     ValueMap indirect_branch_var; | ||||
|     ValueMap zero_flag; | ||||
|     ValueMap sign_flag; | ||||
| @@ -152,10 +134,6 @@ IR::Opcode UndefOpcode(const FlagTag&) noexcept { | ||||
|     return IR::Opcode::UndefU1; | ||||
| } | ||||
|  | ||||
| IR::Opcode UndefOpcode(const LoopSafetyVariable&) noexcept { | ||||
|     return IR::Opcode::UndefU32; | ||||
| } | ||||
|  | ||||
| IR::Opcode UndefOpcode(IndirectBranchVariable) noexcept { | ||||
|     return IR::Opcode::UndefU32; | ||||
| } | ||||
| @@ -337,9 +315,6 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | ||||
|     case IR::Opcode::SetGotoVariable: | ||||
|         pass.WriteVariable(GotoVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); | ||||
|         break; | ||||
|     case IR::Opcode::SetLoopSafetyVariable: | ||||
|         pass.WriteVariable(LoopSafetyVariable{inst.Arg(0).U32()}, block, inst.Arg(1)); | ||||
|         break; | ||||
|     case IR::Opcode::SetIndirectBranchVariable: | ||||
|         pass.WriteVariable(IndirectBranchVariable{}, block, inst.Arg(0)); | ||||
|         break; | ||||
| @@ -368,9 +343,6 @@ void VisitInst(Pass& pass, IR::Block* block, IR::Inst& inst) { | ||||
|     case IR::Opcode::GetGotoVariable: | ||||
|         inst.ReplaceUsesWith(pass.ReadVariable(GotoVariable{inst.Arg(0).U32()}, block)); | ||||
|         break; | ||||
|     case IR::Opcode::GetLoopSafetyVariable: | ||||
|         inst.ReplaceUsesWith(pass.ReadVariable(LoopSafetyVariable{inst.Arg(0).U32()}, block)); | ||||
|         break; | ||||
|     case IR::Opcode::GetIndirectBranchVariable: | ||||
|         inst.ReplaceUsesWith(pass.ReadVariable(IndirectBranchVariable{}, block)); | ||||
|         break; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user