From 808ef97a086e7cc58a3ceded1de516ad6a6be5d3 Mon Sep 17 00:00:00 2001
From: ReinUsesLisp <reinuseslisp@airmail.cc>
Date: Mon, 21 Jun 2021 01:07:10 -0300
Subject: shader: Move loop safety tests to code emission

---
 src/shader_recompiler/backend/glasm/emit_context.h    |  1 +
 src/shader_recompiler/backend/glasm/emit_glasm.cpp    | 17 +++++++++++++++++
 .../backend/glasm/emit_glasm_instructions.h           |  2 --
 .../backend/glasm/emit_glasm_not_implemented.cpp      |  8 --------
 src/shader_recompiler/backend/glsl/emit_context.h     |  2 ++
 src/shader_recompiler/backend/glsl/emit_glsl.cpp      | 11 ++++++++++-
 .../backend/glsl/emit_glsl_instructions.h             |  2 --
 .../backend/glsl/emit_glsl_not_implemented.cpp        |  8 --------
 src/shader_recompiler/backend/spirv/emit_spirv.cpp    | 19 ++++++++++++++++++-
 .../backend/spirv/emit_spirv_context_get_set.cpp      |  8 --------
 .../backend/spirv/emit_spirv_instructions.h           |  2 --
 11 files changed, 48 insertions(+), 32 deletions(-)

(limited to 'src/shader_recompiler/backend')

diff --git a/src/shader_recompiler/backend/glasm/emit_context.h b/src/shader_recompiler/backend/glasm/emit_context.h
index cd4213cb7b..9f86e55d33 100644
--- a/src/shader_recompiler/backend/glasm/emit_context.h
+++ b/src/shader_recompiler/backend/glasm/emit_context.h
@@ -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{};
 };
 
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm.cpp b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
index f39b02f77c..79314f1301 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm.cpp
@@ -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};";
     }
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
index fef9ff9be4..c9f4826ce3 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_instructions.h
@@ -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);
diff --git a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
index a487a07443..ff64c69247 100644
--- a/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glasm/emit_glasm_not_implemented.cpp
@@ -153,14 +153,6 @@ void EmitGetIndirectBranchVariable(EmitContext& ctx) {
     NotImplemented();
 }
 
-void EmitSetLoopSafetyVariable(EmitContext& ctx) {
-    NotImplemented();
-}
-
-void EmitGetLoopSafetyVariable(EmitContext& ctx) {
-    NotImplemented();
-}
-
 void EmitGetZFlag(EmitContext& ctx) {
     NotImplemented();
 }
diff --git a/src/shader_recompiler/backend/glsl/emit_context.h b/src/shader_recompiler/backend/glsl/emit_context.h
index 8fa87c02cb..ecdf6e5bc8 100644
--- a/src/shader_recompiler/backend/glsl/emit_context.h
+++ b/src/shader_recompiler/backend/glsl/emit_context.h
@@ -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{};
 
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl.cpp b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
index ff869923f4..32c4f1da21 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl.cpp
@@ -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:
-            ctx.Add("if(!{}){{break;}}}}", ctx.var_alloc.Consume(node.data.repeat.cond));
+            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
 
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
index df28036e46..6a30785bbc 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_instructions.h
@@ -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,
diff --git a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
index 0a28a1ffc4..f420fe388c 100644
--- a/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
+++ b/src/shader_recompiler/backend/glsl/emit_glsl_not_implemented.cpp
@@ -46,14 +46,6 @@ void EmitGetIndirectBranchVariable(EmitContext& ctx) {
     NotImplemented();
 }
 
-void EmitSetLoopSafetyVariable(EmitContext& ctx) {
-    NotImplemented();
-}
-
-void EmitGetLoopSafetyVariable(EmitContext& ctx) {
-    NotImplemented();
-}
-
 void EmitGetZFlag(EmitContext& ctx) {
     NotImplemented();
 }
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv.cpp b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
index fd59b4d0aa..278c262f8f 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv.cpp
@@ -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:
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
index 2e364baecd..85bd723895 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_context_get_set.cpp
@@ -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)};
diff --git a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
index e3e5b03fed..1181e7b4ff 100644
--- a/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
+++ b/src/shader_recompiler/backend/spirv/emit_spirv_instructions.h
@@ -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);
-- 
cgit v1.2.3-70-g09d2