[RFC,02/17] eir: add legalization

Submitted by Christian Gmeiner on May 10, 2019, 9:09 a.m.

Details

Message ID 20190510090915.2739-3-christian.gmeiner@gmail.com
State New
Headers show
Series "An other look at nir" ( rev: 1 ) in Mesa

Not browsing as part of any series.

Commit Message

Christian Gmeiner May 10, 2019, 9:09 a.m.
- if shader is empty add a NOP instruction
- avoid multiple uniform src for alu ops
- resolve jump target

Signed-off-by: Christian Gmeiner <christian.gmeiner@gmail.com>
---
 src/etnaviv/compiler/eir.h                  |   3 +
 src/etnaviv/compiler/eir_legalize.c         | 177 ++++++++++++++++++++
 src/etnaviv/compiler/meson.build            |   1 +
 src/etnaviv/compiler/tests/eir_legalize.cpp | 136 +++++++++++++++
 src/etnaviv/compiler/tests/meson.build      |  10 ++
 5 files changed, 327 insertions(+)
 create mode 100644 src/etnaviv/compiler/eir_legalize.c
 create mode 100644 src/etnaviv/compiler/tests/eir_legalize.cpp

Patch hide | download patch | download mbox

diff --git a/src/etnaviv/compiler/eir.h b/src/etnaviv/compiler/eir.h
index e2185b004f1..a05b12de94b 100644
--- a/src/etnaviv/compiler/eir.h
+++ b/src/etnaviv/compiler/eir.h
@@ -282,6 +282,9 @@  eir_assign_output(struct eir *ir, unsigned idx, unsigned slot, unsigned ncomp)
    ir->num_outputs = MAX2(ir->num_outputs, idx + 1);
 }
 
+void
+eir_legalize(struct eir *ir);
+
 void
 eir_calculate_live_intervals(struct eir *ir);
 
diff --git a/src/etnaviv/compiler/eir_legalize.c b/src/etnaviv/compiler/eir_legalize.c
new file mode 100644
index 00000000000..94f5c2bd12b
--- /dev/null
+++ b/src/etnaviv/compiler/eir_legalize.c
@@ -0,0 +1,177 @@ 
+/*
+ * Copyright (c) 2018 Etnaviv Project
+ * Copyright (C) 2018 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include "eir.h"
+#include "etnaviv/gc/gc_instr.h"
+
+static int
+invalid_uniform_usage(const struct eir_instruction *inst)
+{
+   const struct gc_instr *gc = &inst->gc;
+   int invalid = 0;
+   bool first_uniform = true;
+   int index;
+
+   if (gc->type != GC_OP_TYPE_ALU)
+      return 0;
+
+   for (unsigned i = 0; i < gc_op_num_src(gc->opcode); i++) {
+      const struct eir_register *src = &inst->src[i];
+
+      if (src->type != EIR_REG_UNIFORM)
+         continue;
+
+      if (first_uniform) {
+         index = src->index;
+         first_uniform = false;
+         continue;
+      }
+
+      if (src->index == index)
+         continue;
+
+      invalid |= 1 << i;
+   }
+
+   return invalid;
+}
+
+static void
+legalize_uniform_usage(struct eir_block *block, struct eir_instruction *inst)
+{
+   /*
+   * The hardware does not allow two or more different uniform registers to be used as
+   * sources in the same ALU instruction. Emit mov instructions to temporary registers
+   * for all but one uniform register in this case.
+   */
+   int mask = invalid_uniform_usage(inst);
+
+   while (mask) {
+      const int i = ffs(mask) - 1;
+      struct eir_register *src = &inst->src[i];
+      struct eir_register tmp = eir_temp_register(block->ir, 4);
+
+      tmp.writemask = 0xf; /* TODO */
+
+      eir_MOV(block, &tmp, src);
+      src->type = EIR_REG_TEMP;
+      src->index = tmp.index;
+
+      mask &= ~(1 << i);
+   }
+}
+
+static void
+legalize_block(struct eir_block *block)
+{
+   struct list_head instr_list;
+
+   /*
+    * Remove all the instructions from the list, we'll be adding
+    * them back in as we go
+    */
+   list_replace(&block->instr_list, &instr_list);
+   list_inithead(&block->instr_list);
+
+   list_for_each_entry_safe (struct eir_instruction, inst, &instr_list, node) {
+      legalize_uniform_usage(block, inst);
+      list_addtail(&inst->node, &block->instr_list);
+   }
+}
+
+struct block_data {
+   unsigned start_ip;
+   unsigned end_ip;
+};
+
+static void
+resolve_jumps(struct eir *ir)
+{
+   void *mem_ctx = ralloc_context(NULL);
+   unsigned ip = 0;
+   assert(mem_ctx);
+
+   eir_for_each_block(block, ir) {
+      struct block_data *bd = rzalloc(mem_ctx, struct block_data);
+
+      assert(bd);
+      assert(!block->data);
+      block->data = bd;
+
+      /* determine start and end IP for this block */
+      bd->start_ip = ip;
+      eir_for_each_inst(inst, block) {
+         ip++;
+      }
+      bd->end_ip = ip;
+   }
+
+   eir_for_each_block(block, ir) {
+      const struct block_data *bd;
+
+      /* the end block of the program has no branch */
+      if (!block->successors[0])
+         continue;
+
+      bd = block->successors[0]->data;
+
+      eir_for_each_inst(inst, block) {
+         if (inst->gc.type != GC_OP_TYPE_BRANCH)
+            continue;
+
+         inst->gc.branch.imm = bd->start_ip;
+
+         /*
+          * if there is a empty block at the end of the shader an
+          * extra NOP should be generated as jump target
+          */
+         if (list_empty(&block->successors[0]->instr_list))
+            eir_NOP(block->successors[0]);
+      }
+   }
+
+   ralloc_free(mem_ctx);
+   eir_for_each_block(block, ir)
+      block->data = NULL;
+}
+
+void
+eir_legalize(struct eir *ir)
+{
+   eir_for_each_block(block, ir)
+      legalize_block(block);
+
+   resolve_jumps(ir);
+
+   /* add NOP if the only block has no instructions */
+   if (ir->blocks == 1) {
+      struct eir_block *block = list_first_entry(&ir->block_list, struct eir_block, node);
+
+      if (list_empty(&block->instr_list))
+         eir_NOP(block);
+   }
+}
diff --git a/src/etnaviv/compiler/meson.build b/src/etnaviv/compiler/meson.build
index 8affe6ebd48..c83399d5297 100644
--- a/src/etnaviv/compiler/meson.build
+++ b/src/etnaviv/compiler/meson.build
@@ -23,6 +23,7 @@ 
 libetnaviv_compiler_files = files(
   'eir.c',
   'eir.h',
+  'eir_legalize.c',
   'eir_uniform.c',
 )
 
diff --git a/src/etnaviv/compiler/tests/eir_legalize.cpp b/src/etnaviv/compiler/tests/eir_legalize.cpp
new file mode 100644
index 00000000000..7968ccc9783
--- /dev/null
+++ b/src/etnaviv/compiler/tests/eir_legalize.cpp
@@ -0,0 +1,136 @@ 
+/*
+ * Copyright (c) 2017 Etnaviv Project
+ * Copyright (C) 2017 Zodiac Inflight Innovations
+ *
+ * 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, sub license,
+ * 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 NON-INFRINGEMENT. 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.
+ *
+ * Authors:
+ *    Christian Gmeiner <christian.gmeiner@gmail.com>
+ */
+
+#include <gtest/gtest.h>
+#include "etnaviv/compiler/eir.h"
+
+TEST (LegalizeTest, EmptyShader)
+{
+   struct eir *ir = eir_create();
+   struct eir_block *block = eir_block_create(ir);
+
+   eir_legalize(ir);
+
+   ASSERT_EQ(list_length(&block->instr_list), 1);
+   struct eir_instruction *nop = list_first_entry(&block->instr_list, struct eir_instruction, node);
+   ASSERT_EQ(nop->gc.opcode, GC_NOP);
+
+   eir_destroy(ir);
+}
+
+TEST (LegalizeTest, UniformsTwoSame)
+{
+   struct eir *ir = eir_create();
+   struct eir_block *block = eir_block_create(ir);
+   struct eir_register dst = eir_temp_register(ir, 4);
+   struct eir_register src = eir_uniform_register_ui(ir, 1);
+
+   dst.writemask = 0xf;
+
+   eir_ADD(block, &dst, &src, &src);
+
+   eir_legalize(ir);
+
+   ASSERT_EQ(list_length(&block->instr_list), 1);
+   struct eir_instruction *add = list_first_entry(&block->instr_list, struct eir_instruction, node);
+   ASSERT_EQ(add->gc.opcode, GC_ADD);
+
+   eir_destroy(ir);
+}
+
+TEST (LegalizeTest, UniformsThreeSame)
+{
+   struct eir *ir = eir_create();
+   struct eir_block *block = eir_block_create(ir);
+   struct eir_register dst = eir_temp_register(ir, 4);
+   struct eir_register src = eir_uniform_register_ui(ir, 1);
+
+   dst.writemask = 0xf;
+
+   eir_MAD(block, &dst, &src, &src, &src);
+
+   eir_legalize(ir);
+
+   ASSERT_EQ(list_length(&block->instr_list), 1);
+   struct eir_instruction *mad = list_first_entry(&block->instr_list, struct eir_instruction, node);
+   ASSERT_EQ(mad->gc.opcode, GC_MAD);
+
+   eir_destroy(ir);
+}
+
+
+TEST (LegalizeTest, UniformsTwoDifferent)
+{
+   static const uint32_t val0[] = { 0, 1, 2, 3 };
+   static const uint32_t val1[] = { 4, 5, 6, 7 };
+   struct eir *ir = eir_create();
+   struct eir_block *block = eir_block_create(ir);
+   struct eir_register dst = eir_temp_register(ir, 4);
+   struct eir_register src0 = eir_uniform_register_vec4_ui(ir, 4, val0);
+   struct eir_register src1 = eir_uniform_register_vec4_ui(ir, 4, val1);
+
+   dst.writemask = 0xf;
+
+   eir_ADD(block, &dst, &src0, &src1);
+
+   eir_legalize(ir);
+
+   ASSERT_EQ(list_length(&block->instr_list), 2);
+   struct eir_instruction *mov = list_first_entry(&block->instr_list, struct eir_instruction, node);
+   ASSERT_EQ(mov->gc.opcode, GC_MOV);
+   ASSERT_EQ(mov->src[0].index, 1);
+   ASSERT_EQ(mov->src[0].type, eir_register::EIR_REG_UNIFORM);
+
+   eir_destroy(ir);
+}
+
+TEST (LegalizeTest, UniformsThreeDifferent)
+{
+   static const uint32_t val0[] = { 0, 1, 2, 3 };
+   static const uint32_t val1[] = { 4, 5, 6, 7 };
+   static const uint32_t val2[] = { 8, 9, 10, 11 };
+   struct eir *ir = eir_create();
+   struct eir_block *block = eir_block_create(ir);
+   struct eir_register dst = eir_temp_register(ir, 4);
+   struct eir_register src0 = eir_uniform_register_vec4_ui(ir, 4, val0);
+   struct eir_register src1 = eir_uniform_register_vec4_ui(ir, 4, val1);
+   struct eir_register src2 = eir_uniform_register_vec4_ui(ir, 4, val2);
+
+   dst.writemask = 0xf;
+
+   eir_MAD(block, &dst, &src0, &src1, &src2);
+
+   eir_legalize(ir);
+
+   ASSERT_EQ(list_length(&block->instr_list), 3);
+   struct eir_instruction *mov = list_first_entry(&block->instr_list, struct eir_instruction, node);
+   ASSERT_EQ(mov->gc.opcode, GC_MOV);
+   ASSERT_EQ(mov->src[0].index, 1);
+   ASSERT_EQ(mov->src[0].type, eir_register::EIR_REG_UNIFORM);
+
+   eir_destroy(ir);
+}
diff --git a/src/etnaviv/compiler/tests/meson.build b/src/etnaviv/compiler/tests/meson.build
index c7506f04ed5..f82acae5f1a 100644
--- a/src/etnaviv/compiler/tests/meson.build
+++ b/src/etnaviv/compiler/tests/meson.build
@@ -42,3 +42,13 @@  test(
   )
 )
 
+test(
+  'eir_legalize',
+  executable(
+    'eir_legalize', 'eir_legalize.cpp',
+    cpp_args : [cpp_vis_args, cpp_msvc_compat_args],
+    link_with: [libetnaviv_gc, libetnaviv_compiler, libmesa_util],
+    include_directories: [inc_common, inc_etnaviv],
+    dependencies : [dep_clock, dep_thread, idep_gtest],
+  )
+)