--- /dev/null
+/*
+ * 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));
+}
--- /dev/null
+/*
+ * 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);
+}