Mesa (master): nir: update nir_gather_info to only mark used array/ matrix elements

Timothy Arceri tarceri at kemper.freedesktop.org
Thu Nov 10 22:25:21 UTC 2016


Module: Mesa
Branch: master
Commit: 7372d2153ab68eefec792a8cb094a6557bdb8b4a
URL:    http://cgit.freedesktop.org/mesa/mesa/commit/?id=7372d2153ab68eefec792a8cb094a6557bdb8b4a

Author: Timothy Arceri <timothy.arceri at collabora.com>
Date:   Thu Oct 27 12:21:52 2016 +1100

nir: update nir_gather_info to only mark used array/matrix elements

This is based on the code from the GLSL IR pass however unlike the GLSL IR
pass it also supports arrays of arrays.

As well as implementing the logic from the GLSL IR pass we add some
additional intrinsic cases to catch more system values.

Reviewed-by: Kenneth Graunke <kenneth at whitecape.org>

---

 src/compiler/nir/nir_gather_info.c | 260 +++++++++++++++++++++++++++++--------
 1 file changed, 207 insertions(+), 53 deletions(-)

diff --git a/src/compiler/nir/nir_gather_info.c b/src/compiler/nir/nir_gather_info.c
index 380140a..63c8a42 100644
--- a/src/compiler/nir/nir_gather_info.c
+++ b/src/compiler/nir/nir_gather_info.c
@@ -21,9 +21,189 @@
  * IN THE SOFTWARE.
  */
 
+#include "main/mtypes.h"
 #include "nir.h"
 
 static void
+set_io_mask(nir_shader *shader, nir_variable *var, int offset, int len)
+{
+   for (int i = 0; i < len; i++) {
+      assert(var->data.location != -1);
+
+      int idx = var->data.location + offset + i;
+      bool is_patch_generic = var->data.patch &&
+                              idx != VARYING_SLOT_TESS_LEVEL_INNER &&
+                              idx != VARYING_SLOT_TESS_LEVEL_OUTER &&
+                              idx != VARYING_SLOT_BOUNDING_BOX0 &&
+                              idx != VARYING_SLOT_BOUNDING_BOX1;
+      uint64_t bitfield;
+
+      if (is_patch_generic) {
+         assert(idx >= VARYING_SLOT_PATCH0 && idx < VARYING_SLOT_TESS_MAX);
+         bitfield = BITFIELD64_BIT(idx - VARYING_SLOT_PATCH0);
+      }
+      else {
+         assert(idx < VARYING_SLOT_MAX);
+         bitfield = BITFIELD64_BIT(idx);
+      }
+
+      if (var->data.mode == nir_var_shader_in) {
+         if (is_patch_generic)
+            shader->info->patch_inputs_read |= bitfield;
+         else
+            shader->info->inputs_read |= bitfield;
+
+         /* double inputs read is only for vertex inputs */
+         if (shader->stage == MESA_SHADER_VERTEX &&
+             glsl_type_is_dual_slot(glsl_without_array(var->type)))
+            shader->info->double_inputs_read |= bitfield;
+
+         if (shader->stage == MESA_SHADER_FRAGMENT) {
+            shader->info->fs.uses_sample_qualifier |= var->data.sample;
+         }
+      } else {
+         assert(var->data.mode == nir_var_shader_out);
+         if (is_patch_generic) {
+            shader->info->patch_outputs_written |= bitfield;
+         } else if (!var->data.read_only) {
+            shader->info->outputs_written |= bitfield;
+         }
+
+         if (var->data.fb_fetch_output)
+            shader->info->outputs_read |= bitfield;
+      }
+   }
+}
+
+/**
+ * Mark an entire variable as used.  Caller must ensure that the variable
+ * represents a shader input or output.
+ */
+static void
+mark_whole_variable(nir_shader *shader, nir_variable *var)
+{
+   const struct glsl_type *type = var->type;
+   bool is_vertex_input = false;
+
+   if (nir_is_per_vertex_io(var, shader->stage)) {
+      assert(glsl_type_is_array(type));
+      type = glsl_get_array_element(type);
+   }
+
+   if (shader->stage == MESA_SHADER_VERTEX &&
+       var->data.mode == nir_var_shader_in)
+      is_vertex_input = true;
+
+   set_io_mask(shader, var, 0,
+               glsl_count_attribute_slots(type, is_vertex_input));
+}
+
+static unsigned
+get_io_offset(nir_deref_var *deref, bool is_vertex_input)
+{
+   unsigned offset = 0;
+
+   nir_deref *tail = &deref->deref;
+   while (tail->child != NULL) {
+      tail = tail->child;
+
+      if (tail->deref_type == nir_deref_type_array) {
+         nir_deref_array *deref_array = nir_deref_as_array(tail);
+
+         if (deref_array->deref_array_type == nir_deref_array_type_indirect) {
+            return -1;
+         }
+
+         offset += glsl_count_attribute_slots(tail->type, is_vertex_input) *
+            deref_array->base_offset;
+      }
+      /* TODO: we can get the offset for structs here see nir_lower_io() */
+   }
+
+   return offset;
+}
+
+/**
+ * Try to mark a portion of the given varying as used.  Caller must ensure
+ * that the variable represents a shader input or output.
+ *
+ * If the index can't be interpreted as a constant, or some other problem
+ * occurs, then nothing will be marked and false will be returned.
+ */
+static bool
+try_mask_partial_io(nir_shader *shader, nir_deref_var *deref)
+{
+   nir_variable *var = deref->var;
+   const struct glsl_type *type = var->type;
+
+   if (nir_is_per_vertex_io(var, shader->stage)) {
+      assert(glsl_type_is_array(type));
+      type = glsl_get_array_element(type);
+   }
+
+   /* The code below only handles:
+    *
+    * - Indexing into matrices
+    * - Indexing into arrays of (arrays, matrices, vectors, or scalars)
+    *
+    * For now, we just give up if we see varying structs and arrays of structs
+    * here marking the entire variable as used.
+    */
+   if (!(glsl_type_is_matrix(type) ||
+         (glsl_type_is_array(type) &&
+          (glsl_type_is_numeric(glsl_without_array(type)) ||
+           glsl_type_is_boolean(glsl_without_array(type)))))) {
+
+      /* If we don't know how to handle this case, give up and let the
+       * caller mark the whole variable as used.
+       */
+      return false;
+   }
+
+   bool is_vertex_input = false;
+   if (shader->stage == MESA_SHADER_VERTEX &&
+       var->data.mode == nir_var_shader_in)
+      is_vertex_input = true;
+
+   unsigned offset = get_io_offset(deref, is_vertex_input);
+   if (offset == -1)
+      return false;
+
+   unsigned num_elems;
+   unsigned elem_width = 1;
+   unsigned mat_cols = 1;
+   if (glsl_type_is_array(type)) {
+      num_elems = glsl_get_aoa_size(type);
+      if (glsl_type_is_matrix(glsl_without_array(type)))
+         mat_cols = glsl_get_matrix_columns(glsl_without_array(type));
+   } else {
+      num_elems = glsl_get_matrix_columns(type);
+   }
+
+   /* double element width for double types that takes two slots */
+   if (!is_vertex_input &&
+       glsl_type_is_dual_slot(glsl_without_array(type))) {
+      elem_width *= 2;
+   }
+
+   if (offset >= num_elems * elem_width * mat_cols) {
+      /* Constant index outside the bounds of the matrix/array.  This could
+       * arise as a result of constant folding of a legal GLSL program.
+       *
+       * Even though the spec says that indexing outside the bounds of a
+       * matrix/array results in undefined behaviour, we don't want to pass
+       * out-of-range values to set_io_mask() (since this could result in
+       * slots that don't exist being marked as used), so just let the caller
+       * mark the whole variable as used.
+       */
+      return false;
+   }
+
+   set_io_mask(shader, var, offset, elem_width);
+   return true;
+}
+
+static void
 gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader)
 {
    switch (instr->intrinsic) {
@@ -33,10 +213,24 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader)
       shader->info->fs.uses_discard = true;
       break;
 
+   case nir_intrinsic_interp_var_at_centroid:
+   case nir_intrinsic_interp_var_at_sample:
+   case nir_intrinsic_interp_var_at_offset:
+   case nir_intrinsic_load_var:
+   case nir_intrinsic_store_var:
+      if (instr->variables[0]->var->data.mode == nir_var_shader_in ||
+          instr->variables[0]->var->data.mode == nir_var_shader_out) {
+         if (!try_mask_partial_io(shader, instr->variables[0]))
+            mark_whole_variable(shader, instr->variables[0]->var);
+      }
+      break;
+
+   case nir_intrinsic_load_draw_id:
    case nir_intrinsic_load_front_face:
    case nir_intrinsic_load_vertex_id:
    case nir_intrinsic_load_vertex_id_zero_base:
    case nir_intrinsic_load_base_vertex:
+   case nir_intrinsic_load_base_instance:
    case nir_intrinsic_load_instance_id:
    case nir_intrinsic_load_sample_id:
    case nir_intrinsic_load_sample_pos:
@@ -47,6 +241,9 @@ gather_intrinsic_info(nir_intrinsic_instr *instr, nir_shader *shader)
    case nir_intrinsic_load_local_invocation_index:
    case nir_intrinsic_load_work_group_id:
    case nir_intrinsic_load_num_work_groups:
+   case nir_intrinsic_load_tess_coord:
+   case nir_intrinsic_load_tess_level_outer:
+   case nir_intrinsic_load_tess_level_inner:
       shader->info->system_values_read |=
          (1 << nir_system_value_from_intrinsic(instr->intrinsic));
       break;
@@ -89,62 +286,9 @@ gather_info_block(nir_block *block, nir_shader *shader)
    }
 }
 
-/**
- * Returns the bits in the inputs_read, outputs_written, or
- * system_values_read bitfield corresponding to this variable.
- */
-static inline uint64_t
-get_io_mask(nir_variable *var, gl_shader_stage stage)
-{
-   assert(var->data.mode == nir_var_shader_in ||
-          var->data.mode == nir_var_shader_out ||
-          var->data.mode == nir_var_system_value);
-   assert(var->data.location >= 0);
-
-   const struct glsl_type *var_type = var->type;
-   if (stage == MESA_SHADER_GEOMETRY && var->data.mode == nir_var_shader_in) {
-      /* Most geometry shader inputs are per-vertex arrays */
-      if (var->data.location >= VARYING_SLOT_VAR0)
-         assert(glsl_type_is_array(var_type));
-
-      if (glsl_type_is_array(var_type))
-         var_type = glsl_get_array_element(var_type);
-   }
-
-   bool is_vertex_input = (var->data.mode == nir_var_shader_in &&
-                           stage == MESA_SHADER_VERTEX);
-   unsigned slots = glsl_count_attribute_slots(var_type, is_vertex_input);
-   return ((1ull << slots) - 1) << var->data.location;
-}
-
 void
 nir_shader_gather_info(nir_shader *shader, nir_function_impl *entrypoint)
 {
-   /* This pass does not yet support tessellation shaders */
-   assert(shader->stage == MESA_SHADER_VERTEX ||
-          shader->stage == MESA_SHADER_GEOMETRY ||
-          shader->stage == MESA_SHADER_FRAGMENT ||
-          shader->stage == MESA_SHADER_COMPUTE);
-
-   bool uses_sample_qualifier = false;
-   shader->info->inputs_read = 0;
-   foreach_list_typed(nir_variable, var, node, &shader->inputs) {
-      shader->info->inputs_read |= get_io_mask(var, shader->stage);
-      uses_sample_qualifier |= var->data.sample;
-   }
-
-   if (shader->stage == MESA_SHADER_FRAGMENT)
-      shader->info->fs.uses_sample_qualifier = uses_sample_qualifier;
-
-   /* TODO: Some day we may need to add stream support to NIR */
-   shader->info->outputs_written = 0;
-   foreach_list_typed(nir_variable, var, node, &shader->outputs)
-      shader->info->outputs_written |= get_io_mask(var, shader->stage);
-
-   shader->info->system_values_read = 0;
-   foreach_list_typed(nir_variable, var, node, &shader->system_values)
-      shader->info->system_values_read |= get_io_mask(var, shader->stage);
-
    shader->info->num_textures = 0;
    shader->info->num_images = 0;
    nir_foreach_variable(var, &shader->uniforms) {
@@ -162,6 +306,16 @@ nir_shader_gather_info(nir_shader *shader, nir_function_impl *entrypoint)
       }
    }
 
+   shader->info->inputs_read = 0;
+   shader->info->outputs_written = 0;
+   shader->info->outputs_read = 0;
+   shader->info->double_inputs_read = 0;
+   shader->info->patch_inputs_read = 0;
+   shader->info->patch_outputs_written = 0;
+   shader->info->system_values_read = 0;
+   if (shader->stage == MESA_SHADER_FRAGMENT) {
+      shader->info->fs.uses_sample_qualifier = false;
+   }
    nir_foreach_block(block, entrypoint) {
       gather_info_block(block, shader);
    }




More information about the mesa-commit mailing list