pan/va: Add FAU validation
authorAlyssa Rosenzweig <alyssa@collabora.com>
Fri, 23 Jul 2021 15:21:29 +0000 (11:21 -0400)
committerMarge Bot <emma+marge@anholt.net>
Fri, 25 Mar 2022 19:00:13 +0000 (19:00 +0000)
Signed-off-by: Alyssa Rosenzweig <alyssa@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15223>

src/panfrost/bifrost/meson.build
src/panfrost/bifrost/valhall/test/test-validate-fau.cpp [new file with mode: 0644]
src/panfrost/bifrost/valhall/va_compiler.h
src/panfrost/bifrost/valhall/va_validate.c [new file with mode: 0644]

index 914bd53..81eb621 100644 (file)
@@ -47,6 +47,7 @@ libpanfrost_bifrost_files = files(
   'bifrost_compile.c',
   'valhall/va_optimize.c',
   'valhall/va_pack.c',
+  'valhall/va_validate.c',
 )
 
 bifrost_gen_disasm_c = custom_target(
@@ -161,6 +162,7 @@ if with_tests
        'test/test-packing.cpp',
        'test/test-scheduler-predicates.cpp',
         'valhall/test/test-add-imm.cpp',
+        'valhall/test/test-validate-fau.cpp',
       ),
       c_args : [c_msvc_compat_args, no_override_init_args],
       gnu_symbol_visibility : 'hidden',
diff --git a/src/panfrost/bifrost/valhall/test/test-validate-fau.cpp b/src/panfrost/bifrost/valhall/test/test-validate-fau.cpp
new file mode 100644 (file)
index 0000000..4275d03
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2021 Collabora, Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "va_compiler.h"
+#include "bi_test.h"
+#include "bi_builder.h"
+
+#include <gtest/gtest.h>
+
+#define CASE(instr, expected) do { \
+   if (va_validate_fau(instr) != expected) { \
+      fprintf(stderr, "Incorrect validation for:\n"); \
+      bi_print_instr(instr, stderr); \
+      fprintf(stderr, "\n"); \
+      ADD_FAILURE(); \
+   } \
+} while(0)
+
+#define VALID(instr) CASE(instr, true)
+#define INVALID(instr) CASE(instr, false)
+
+class ValidateFau : public testing::Test {
+protected:
+   ValidateFau() {
+      mem_ctx = ralloc_context(NULL);
+      b = bit_builder(mem_ctx);
+
+      zero = bi_fau((enum bir_fau) (BIR_FAU_IMMEDIATE | 0), false);
+      imm1 = bi_fau((enum bir_fau) (BIR_FAU_IMMEDIATE | 1), false);
+      imm2 = bi_fau((enum bir_fau) (BIR_FAU_IMMEDIATE | 2), false);
+      unif = bi_fau((enum bir_fau) (BIR_FAU_UNIFORM | 5), false);
+      unif2 = bi_fau((enum bir_fau) (BIR_FAU_UNIFORM | 6), false);
+      core_id = bi_fau(BIR_FAU_CORE_ID, false);
+      lane_id = bi_fau(BIR_FAU_LANE_ID, false);
+   }
+
+   ~ValidateFau() {
+      ralloc_free(mem_ctx);
+   }
+
+   void *mem_ctx;
+   bi_builder *b;
+   bi_index zero, imm1, imm2, unif, unif2, core_id, lane_id;
+};
+
+TEST_F(ValidateFau, One64BitUniformSlot)
+{
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_register(3),
+            unif, BI_ROUND_NONE));
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_word(unif, 1),
+            unif, BI_ROUND_NONE));
+   VALID(bi_fma_f32_to(b, bi_register(1), unif, unif, bi_word(unif, 1),
+            BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), unif, unif2, bi_register(1),
+            BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), unif, unif2, bi_word(unif, 1),
+            BI_ROUND_NONE));
+
+   /* Crafted case that appears correct at first glance and was erronously
+    * marked as valid in early versions of the validator.
+    */
+   INVALID(bi_fma_f32_to(b, bi_register(1), bi_register(2),
+                         bi_fau((enum bir_fau) (BIR_FAU_UNIFORM | 0), false),
+                         bi_fau((enum bir_fau) (BIR_FAU_UNIFORM | 1), true),
+                         BI_ROUND_NONE));
+}
+
+TEST_F(ValidateFau, Combined64BitUniformsConstants)
+{
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_word(unif, 1),
+            unif, BI_ROUND_NONE));
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), zero,
+            unif, BI_ROUND_NONE));
+   VALID(bi_fma_f32_to(b, bi_register(1), zero, imm1, imm1, BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), zero, bi_word(unif, 1),
+            unif, BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), zero, imm1, imm2, BI_ROUND_NONE));
+}
+
+TEST_F(ValidateFau, UniformsOnlyInDefaultMode)
+{
+   INVALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_word(unif, 1),
+            lane_id, BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_word(unif, 1),
+            core_id, BI_ROUND_NONE));
+}
+
+TEST_F(ValidateFau, SingleSpecialImmediate)
+{
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_register(2),
+            lane_id, BI_ROUND_NONE));
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), bi_register(2),
+            core_id, BI_ROUND_NONE));
+   INVALID(bi_fma_f32_to(b, bi_register(1), bi_register(2), lane_id,
+            core_id, BI_ROUND_NONE));
+}
+
+TEST_F(ValidateFau, SmokeTests)
+{
+   VALID(bi_mov_i32_to(b, bi_register(1), bi_register(2)));
+   VALID(bi_mov_i32_to(b, bi_register(1), unif));
+   VALID(bi_fma_f32_to(b, bi_register(1), bi_discard(bi_register(1)),
+                        unif, bi_neg(zero), BI_ROUND_NONE));
+}
index d7207cd..1d2a76a 100644 (file)
@@ -34,6 +34,9 @@
 extern "C" {
 #endif
 
+bool va_validate_fau(bi_instr *I);
+void va_validate(FILE *fp, bi_context *ctx);
+void va_repair_fau(bi_builder *b, bi_instr *I);
 void va_fuse_add_imm(bi_instr *I);
 uint64_t va_pack_instr(const bi_instr *I, unsigned flow);
 
diff --git a/src/panfrost/bifrost/valhall/va_validate.c b/src/panfrost/bifrost/valhall/va_validate.c
new file mode 100644 (file)
index 0000000..e404d74
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * Copyright (C) 2021 Collabora Ltd.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice (including the next
+ * paragraph) shall be included in all copies or substantial portions of the
+ * Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+ * SOFTWARE.
+ */
+
+#include "va_compiler.h"
+#include "valhall.h"
+#include "bi_builder.h"
+
+/* Valhall has limits on access to fast-access uniforms:
+ *
+ *   An instruction may access no more than a single 64-bit uniform slot.
+ *   An instruction may access no more than 64-bits of combined uniforms and constants.
+ *   An instruction may access no more than a single special immediate (e.g. lane_id).
+ *
+ * We validate these constraints.
+ *
+ * An instruction may only access a single page of (special or uniform) FAU.
+ * This constraint does not need explicit validation: since FAU slots are
+ * naturally aligned, they never cross page boundaries, so this condition is
+ * implied by only acesssing a single 64-bit slot.
+ */
+
+struct fau_state {
+   signed uniform_slot;
+   bi_index buffer[2];
+};
+
+static bool
+fau_state_buffer(struct fau_state *fau, bi_index idx)
+{
+   for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
+      if (bi_is_word_equiv(fau->buffer[i], idx))
+         return true;
+      else if (bi_is_null(fau->buffer[i])) {
+         fau->buffer[i] = idx;
+         return true;
+      }
+   }
+
+   return false;
+}
+
+static bool
+fau_state_uniform(struct fau_state *fau, bi_index idx)
+{
+   /* Each slot is 64-bits. The low/high half is encoded as the offset of the
+    * bi_index, which we want to ignore.
+    */
+   unsigned slot = (idx.value & 63);
+
+   if (fau->uniform_slot < 0)
+      fau->uniform_slot = slot;
+
+   return fau->uniform_slot == slot;
+}
+
+static bool
+fau_is_special(enum bir_fau fau)
+{
+   return !(fau & (BIR_FAU_UNIFORM | BIR_FAU_IMMEDIATE));
+}
+
+static bool
+fau_state_special(struct fau_state *fau, bi_index idx)
+{
+   for (unsigned i = 0; i < ARRAY_SIZE(fau->buffer); ++i) {
+      bi_index buf = fau->buffer[i];
+      bool special = !bi_is_null(buf) && fau_is_special(buf.value);
+
+      if (special && !bi_is_equiv(buf, idx))
+         return false;
+   }
+
+   return true;
+}
+
+static bool
+valid_src(struct fau_state *fau, unsigned fau_page, bi_index src)
+{
+   if (src.type != BI_INDEX_FAU)
+      return true;
+
+   bool valid = (fau_page == va_fau_page(src.value));
+   valid &= fau_state_buffer(fau, src);
+
+   if (src.value & BIR_FAU_UNIFORM)
+      valid &= fau_state_uniform(fau, src);
+   else if (fau_is_special(src.value))
+      valid &= fau_state_special(fau, src);
+
+   return valid;
+}
+
+bool
+va_validate_fau(bi_instr *I)
+{
+   bool valid = true;
+   struct fau_state fau = { .uniform_slot = -1 };
+   unsigned fau_page = va_select_fau_page(I);
+
+   bi_foreach_src(I, s) {
+      valid &= valid_src(&fau, fau_page, I->src[s]);
+   }
+
+   return valid;
+}
+
+void
+va_repair_fau(bi_builder *b, bi_instr *I)
+{
+   struct fau_state fau = { .uniform_slot = -1 };
+   unsigned fau_page = va_select_fau_page(I);
+
+   bi_foreach_src(I, s) {
+      struct fau_state push = fau;
+      bi_index src = I->src[s];
+
+      if (!valid_src(&fau, fau_page, src)) {
+         bi_index copy = bi_mov_i32(b, bi_strip_index(src));
+         I->src[s] = bi_replace_index(src, copy);
+
+         /* Rollback update. Since the replacement move doesn't affect FAU
+          * state, there is no need to call valid_src again.
+          */
+         fau = push;
+      }
+   }
+}
+
+void
+va_validate(FILE *fp, bi_context *ctx)
+{
+   bool errors = false;
+
+   bi_foreach_instr_global(ctx, I) {
+      if (!va_validate_fau(I)) {
+         if (!errors) {
+            fprintf(fp, "Validation failed, this is a bug. Shader:\n\n");
+            bi_print_shader(ctx, fp);
+            fprintf(fp, "Offending code:\n");
+         }
+
+         bi_print_instr(I, fp);
+         fprintf(fp, "\n");
+         errors = true;
+      }
+   }
+
+   if (errors)
+      exit(1);
+}