[Mesa-dev] [PATCH v4 002/129] nir: Add a deref instruction type

Jason Ekstrand jason at jlekstrand.net
Fri Jun 1 05:01:45 UTC 2018


This commit adds a new instruction type to NIR for handling derefs.
Nothing uses it yet but this adds the data structure as well as all of
the code to validate, print, clone, and [de]serialize them.

Reviewed-by: Caio Marcelo de Oliveira Filho <caio.oliveira at intel.com>
---
 src/compiler/nir/nir.c                    |  50 +++++++++++
 src/compiler/nir/nir.h                    |  58 ++++++++++++-
 src/compiler/nir/nir_clone.c              |  42 ++++++++++
 src/compiler/nir/nir_instr_set.c          |  78 ++++++++++++++++++
 src/compiler/nir/nir_opt_copy_propagate.c |  62 +++++++++++---
 src/compiler/nir/nir_opt_dce.c            |   7 ++
 src/compiler/nir/nir_print.c              | 132 +++++++++++++++++++++++++++++-
 src/compiler/nir/nir_serialize.c          |  81 ++++++++++++++++++
 src/compiler/nir/nir_validate.c           |  83 +++++++++++++++++++
 9 files changed, 580 insertions(+), 13 deletions(-)

diff --git a/src/compiler/nir/nir.c b/src/compiler/nir/nir.c
index dc1c560..44f4752 100644
--- a/src/compiler/nir/nir.c
+++ b/src/compiler/nir/nir.c
@@ -473,6 +473,26 @@ nir_alu_instr_create(nir_shader *shader, nir_op op)
    return instr;
 }
 
+nir_deref_instr *
+nir_deref_instr_create(nir_shader *shader, nir_deref_type deref_type)
+{
+   nir_deref_instr *instr =
+      rzalloc_size(shader, sizeof(nir_deref_instr));
+
+   instr_init(&instr->instr, nir_instr_type_deref);
+
+   instr->deref_type = deref_type;
+   if (deref_type != nir_deref_type_var)
+      src_init(&instr->parent);
+
+   if (deref_type == nir_deref_type_array)
+      src_init(&instr->arr.index);
+
+   dest_init(&instr->dest);
+
+   return instr;
+}
+
 nir_jump_instr *
 nir_jump_instr_create(nir_shader *shader, nir_jump_type type)
 {
@@ -1202,6 +1222,12 @@ visit_alu_dest(nir_alu_instr *instr, nir_foreach_dest_cb cb, void *state)
 }
 
 static bool
+visit_deref_dest(nir_deref_instr *instr, nir_foreach_dest_cb cb, void *state)
+{
+   return cb(&instr->dest, state);
+}
+
+static bool
 visit_intrinsic_dest(nir_intrinsic_instr *instr, nir_foreach_dest_cb cb,
                      void *state)
 {
@@ -1242,6 +1268,8 @@ nir_foreach_dest(nir_instr *instr, nir_foreach_dest_cb cb, void *state)
    switch (instr->type) {
    case nir_instr_type_alu:
       return visit_alu_dest(nir_instr_as_alu(instr), cb, state);
+   case nir_instr_type_deref:
+      return visit_deref_dest(nir_instr_as_deref(instr), cb, state);
    case nir_instr_type_intrinsic:
       return visit_intrinsic_dest(nir_instr_as_intrinsic(instr), cb, state);
    case nir_instr_type_tex:
@@ -1287,6 +1315,7 @@ nir_foreach_ssa_def(nir_instr *instr, nir_foreach_ssa_def_cb cb, void *state)
 {
    switch (instr->type) {
    case nir_instr_type_alu:
+   case nir_instr_type_deref:
    case nir_instr_type_tex:
    case nir_instr_type_intrinsic:
    case nir_instr_type_phi:
@@ -1353,6 +1382,23 @@ visit_alu_src(nir_alu_instr *instr, nir_foreach_src_cb cb, void *state)
 }
 
 static bool
+visit_deref_instr_src(nir_deref_instr *instr,
+                      nir_foreach_src_cb cb, void *state)
+{
+   if (instr->deref_type != nir_deref_type_var) {
+      if (!visit_src(&instr->parent, cb, state))
+         return false;
+   }
+
+   if (instr->deref_type == nir_deref_type_array) {
+      if (!visit_src(&instr->arr.index, cb, state))
+         return false;
+   }
+
+   return true;
+}
+
+static bool
 visit_tex_src(nir_tex_instr *instr, nir_foreach_src_cb cb, void *state)
 {
    for (unsigned i = 0; i < instr->num_srcs; i++) {
@@ -1440,6 +1486,10 @@ nir_foreach_src(nir_instr *instr, nir_foreach_src_cb cb, void *state)
       if (!visit_alu_src(nir_instr_as_alu(instr), cb, state))
          return false;
       break;
+   case nir_instr_type_deref:
+      if (!visit_deref_instr_src(nir_instr_as_deref(instr), cb, state))
+         return false;
+      break;
    case nir_instr_type_intrinsic:
       if (!visit_intrinsic_src(nir_instr_as_intrinsic(instr), cb, state))
          return false;
diff --git a/src/compiler/nir/nir.h b/src/compiler/nir/nir.h
index f6086bd..187d3b4 100644
--- a/src/compiler/nir/nir.h
+++ b/src/compiler/nir/nir.h
@@ -424,6 +424,7 @@ typedef struct nir_register {
 
 typedef enum {
    nir_instr_type_alu,
+   nir_instr_type_deref,
    nir_instr_type_call,
    nir_instr_type_tex,
    nir_instr_type_intrinsic,
@@ -891,7 +892,9 @@ bool nir_alu_srcs_equal(const nir_alu_instr *alu1, const nir_alu_instr *alu2,
 typedef enum {
    nir_deref_type_var,
    nir_deref_type_array,
-   nir_deref_type_struct
+   nir_deref_type_array_wildcard,
+   nir_deref_type_struct,
+   nir_deref_type_cast,
 } nir_deref_type;
 
 typedef struct nir_deref {
@@ -953,6 +956,56 @@ nir_deref_tail(nir_deref *deref)
 typedef struct {
    nir_instr instr;
 
+   /** The type of this deref instruction */
+   nir_deref_type deref_type;
+
+   /** The mode of the underlying variable */
+   nir_variable_mode mode;
+
+   /** The dereferenced type of the resulting pointer value */
+   const struct glsl_type *type;
+
+   union {
+      /** Variable being dereferenced if deref_type is a deref_var */
+      nir_variable *var;
+
+      /** Parent deref if deref_type is not deref_var */
+      nir_src parent;
+   };
+
+   /** Additional deref parameters */
+   union {
+      struct {
+         nir_src index;
+      } arr;
+
+      struct {
+         unsigned index;
+      } strct;
+   };
+
+   /** Destination to store the resulting "pointer" */
+   nir_dest dest;
+} nir_deref_instr;
+
+NIR_DEFINE_CAST(nir_instr_as_deref, nir_instr, nir_deref_instr, instr,
+                type, nir_instr_type_deref)
+
+static inline nir_deref_instr *
+nir_src_as_deref(nir_src src)
+{
+   if (!src.is_ssa)
+      return NULL;
+
+   if (src.ssa->parent_instr->type != nir_instr_type_deref)
+      return NULL;
+
+   return nir_instr_as_deref(src.ssa->parent_instr);
+}
+
+typedef struct {
+   nir_instr instr;
+
    unsigned num_params;
    nir_deref_var **params;
    nir_deref_var *return_deref;
@@ -2072,6 +2125,9 @@ void nir_metadata_preserve(nir_function_impl *impl, nir_metadata preserved);
 /** creates an instruction with default swizzle/writemask/etc. with NULL registers */
 nir_alu_instr *nir_alu_instr_create(nir_shader *shader, nir_op op);
 
+nir_deref_instr *nir_deref_instr_create(nir_shader *shader,
+                                        nir_deref_type deref_type);
+
 nir_jump_instr *nir_jump_instr_create(nir_shader *shader, nir_jump_type type);
 
 nir_load_const_instr *nir_load_const_instr_create(nir_shader *shader,
diff --git a/src/compiler/nir/nir_clone.c b/src/compiler/nir/nir_clone.c
index bcfdaa7..20eaaff 100644
--- a/src/compiler/nir/nir_clone.c
+++ b/src/compiler/nir/nir_clone.c
@@ -346,6 +346,46 @@ clone_alu(clone_state *state, const nir_alu_instr *alu)
    return nalu;
 }
 
+static nir_deref_instr *
+clone_deref_instr(clone_state *state, const nir_deref_instr *deref)
+{
+   nir_deref_instr *nderef =
+      nir_deref_instr_create(state->ns, deref->deref_type);
+
+   __clone_dst(state, &nderef->instr, &nderef->dest, &deref->dest);
+
+   nderef->mode = deref->mode;
+   nderef->type = deref->type;
+
+   if (deref->deref_type == nir_deref_type_var) {
+      nderef->var = remap_var(state, deref->var);
+      return nderef;
+   }
+
+   __clone_src(state, &nderef->instr, &nderef->parent, &deref->parent);
+
+   switch (deref->deref_type) {
+   case nir_deref_type_struct:
+      nderef->strct.index = deref->strct.index;
+      break;
+
+   case nir_deref_type_array:
+      __clone_src(state, &nderef->instr,
+                  &nderef->arr.index, &deref->arr.index);
+      break;
+
+   case nir_deref_type_array_wildcard:
+   case nir_deref_type_cast:
+      /* Nothing to do */
+      break;
+
+   default:
+      unreachable("Invalid instruction deref type");
+   }
+
+   return nderef;
+}
+
 static nir_intrinsic_instr *
 clone_intrinsic(clone_state *state, const nir_intrinsic_instr *itr)
 {
@@ -502,6 +542,8 @@ clone_instr(clone_state *state, const nir_instr *instr)
    switch (instr->type) {
    case nir_instr_type_alu:
       return &clone_alu(state, nir_instr_as_alu(instr))->instr;
+   case nir_instr_type_deref:
+      return &clone_deref_instr(state, nir_instr_as_deref(instr))->instr;
    case nir_instr_type_intrinsic:
       return &clone_intrinsic(state, nir_instr_as_intrinsic(instr))->instr;
    case nir_instr_type_load_const:
diff --git a/src/compiler/nir/nir_instr_set.c b/src/compiler/nir/nir_instr_set.c
index 9cb9ed4..939ddcc 100644
--- a/src/compiler/nir/nir_instr_set.c
+++ b/src/compiler/nir/nir_instr_set.c
@@ -79,6 +79,40 @@ hash_alu(uint32_t hash, const nir_alu_instr *instr)
 }
 
 static uint32_t
+hash_deref(uint32_t hash, const nir_deref_instr *instr)
+{
+   hash = HASH(hash, instr->deref_type);
+   hash = HASH(hash, instr->mode);
+   hash = HASH(hash, instr->type);
+
+   if (instr->deref_type == nir_deref_type_var)
+      return HASH(hash, instr->var);
+
+   hash = hash_src(hash, &instr->parent);
+
+   switch (instr->deref_type) {
+   case nir_deref_type_struct:
+      hash = HASH(hash, instr->strct.index);
+      break;
+
+   case nir_deref_type_array:
+      hash = hash_src(hash, &instr->arr.index);
+      break;
+
+   case nir_deref_type_var:
+   case nir_deref_type_array_wildcard:
+   case nir_deref_type_cast:
+      /* Nothing to do */
+      break;
+
+   default:
+      unreachable("Invalid instruction deref type");
+   }
+
+   return hash;
+}
+
+static uint32_t
 hash_load_const(uint32_t hash, const nir_load_const_instr *instr)
 {
    hash = HASH(hash, instr->def.num_components);
@@ -182,6 +216,9 @@ hash_instr(const void *data)
    case nir_instr_type_alu:
       hash = hash_alu(hash, nir_instr_as_alu(instr));
       break;
+   case nir_instr_type_deref:
+      hash = hash_deref(hash, nir_instr_as_deref(instr));
+      break;
    case nir_instr_type_load_const:
       hash = hash_load_const(hash, nir_instr_as_load_const(instr));
       break;
@@ -289,6 +326,43 @@ nir_instrs_equal(const nir_instr *instr1, const nir_instr *instr2)
       }
       return true;
    }
+   case nir_instr_type_deref: {
+      nir_deref_instr *deref1 = nir_instr_as_deref(instr1);
+      nir_deref_instr *deref2 = nir_instr_as_deref(instr2);
+
+      if (deref1->deref_type != deref2->deref_type ||
+          deref1->mode != deref2->mode ||
+          deref1->type != deref2->type)
+         return false;
+
+      if (deref1->deref_type == nir_deref_type_var)
+         return deref1->var == deref2->var;
+
+      if (!nir_srcs_equal(deref1->parent, deref2->parent))
+         return false;
+
+      switch (deref1->deref_type) {
+      case nir_deref_type_struct:
+         if (deref1->strct.index != deref2->strct.index)
+            return false;
+         break;
+
+      case nir_deref_type_array:
+         if (!nir_srcs_equal(deref1->arr.index, deref2->arr.index))
+            return false;
+         break;
+
+      case nir_deref_type_var:
+      case nir_deref_type_array_wildcard:
+      case nir_deref_type_cast:
+         /* Nothing to do */
+         break;
+
+      default:
+         unreachable("Invalid instruction deref type");
+      }
+      break;
+   }
    case nir_instr_type_tex: {
       nir_tex_instr *tex1 = nir_instr_as_tex(instr1);
       nir_tex_instr *tex2 = nir_instr_as_tex(instr2);
@@ -430,6 +504,7 @@ instr_can_rewrite(nir_instr *instr)
 
    switch (instr->type) {
    case nir_instr_type_alu:
+   case nir_instr_type_deref:
    case nir_instr_type_load_const:
    case nir_instr_type_phi:
       return true;
@@ -468,6 +543,9 @@ nir_instr_get_dest_ssa_def(nir_instr *instr)
    case nir_instr_type_alu:
       assert(nir_instr_as_alu(instr)->dest.dest.is_ssa);
       return &nir_instr_as_alu(instr)->dest.dest.ssa;
+   case nir_instr_type_deref:
+      assert(nir_instr_as_deref(instr)->dest.is_ssa);
+      return &nir_instr_as_deref(instr)->dest.ssa;
    case nir_instr_type_load_const:
       return &nir_instr_as_load_const(instr)->def;
    case nir_instr_type_phi:
diff --git a/src/compiler/nir/nir_opt_copy_propagate.c b/src/compiler/nir/nir_opt_copy_propagate.c
index 3cd476a..c35e8e1 100644
--- a/src/compiler/nir/nir_opt_copy_propagate.c
+++ b/src/compiler/nir/nir_opt_copy_propagate.c
@@ -99,6 +99,22 @@ is_swizzleless_move(nir_alu_instr *instr)
 }
 
 static bool
+is_trivial_deref_cast(nir_deref_instr *cast)
+{
+   nir_deref_instr *parent = nir_src_as_deref(cast->parent);
+   if (!parent)
+      return false;
+
+   if (cast->deref_type != nir_deref_type_cast)
+      return false;
+
+   return cast->mode == parent->mode &&
+          cast->type == parent->type &&
+          cast->dest.ssa.num_components == parent->dest.ssa.num_components &&
+          cast->dest.ssa.bit_size == parent->dest.ssa.bit_size;
+}
+
+static bool
 copy_prop_src(nir_src *src, nir_instr *parent_instr, nir_if *parent_if,
               unsigned num_components)
 {
@@ -109,23 +125,31 @@ copy_prop_src(nir_src *src, nir_instr *parent_instr, nir_if *parent_if,
    }
 
    nir_instr *src_instr = src->ssa->parent_instr;
-   if (src_instr->type != nir_instr_type_alu)
-      return false;
+   nir_ssa_def *copy_def;
+   if (src_instr->type == nir_instr_type_alu) {
+      nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
+      if (!is_swizzleless_move(alu_instr))
+         return false;
 
-   nir_alu_instr *alu_instr = nir_instr_as_alu(src_instr);
-   if (!is_swizzleless_move(alu_instr))
-      return false;
+      if (alu_instr->src[0].src.ssa->num_components != num_components)
+         return false;
 
-   if (alu_instr->src[0].src.ssa->num_components != num_components)
+      copy_def= alu_instr->src[0].src.ssa;
+   } else if (src_instr->type == nir_instr_type_deref) {
+      nir_deref_instr *deref_instr = nir_instr_as_deref(src_instr);
+      if (!is_trivial_deref_cast(deref_instr))
+         return false;
+
+      copy_def = deref_instr->parent.ssa;
+   } else {
       return false;
+   }
 
    if (parent_instr) {
-      nir_instr_rewrite_src(parent_instr, src,
-                            nir_src_for_ssa(alu_instr->src[0].src.ssa));
+      nir_instr_rewrite_src(parent_instr, src, nir_src_for_ssa(copy_def));
    } else {
       assert(src == &parent_if->condition);
-      nir_if_rewrite_condition(parent_if,
-                               nir_src_for_ssa(alu_instr->src[0].src.ssa));
+      nir_if_rewrite_condition(parent_if, nir_src_for_ssa(copy_def));
    }
 
    return true;
@@ -234,6 +258,24 @@ copy_prop_instr(nir_instr *instr)
       return progress;
    }
 
+   case nir_instr_type_deref: {
+      nir_deref_instr *deref = nir_instr_as_deref(instr);
+
+      if (deref->deref_type != nir_deref_type_var) {
+         assert(deref->dest.is_ssa);
+         const unsigned comps = deref->dest.ssa.num_components;
+         while (copy_prop_src(&deref->parent, instr, NULL, comps))
+            progress = true;
+      }
+
+      if (deref->deref_type == nir_deref_type_array) {
+         while (copy_prop_src(&deref->arr.index, instr, NULL, 1))
+            progress = true;
+      }
+
+      return progress;
+   }
+
    case nir_instr_type_tex: {
       nir_tex_instr *tex = nir_instr_as_tex(instr);
       for (unsigned i = 0; i < tex->num_srcs; i++) {
diff --git a/src/compiler/nir/nir_opt_dce.c b/src/compiler/nir/nir_opt_dce.c
index 570e430..c9b3388 100644
--- a/src/compiler/nir/nir_opt_dce.c
+++ b/src/compiler/nir/nir_opt_dce.c
@@ -52,6 +52,7 @@ static void
 init_instr(nir_instr *instr, nir_instr_worklist *worklist)
 {
    nir_alu_instr *alu_instr;
+   nir_deref_instr *deref_instr;
    nir_intrinsic_instr *intrin_instr;
    nir_tex_instr *tex_instr;
 
@@ -73,6 +74,12 @@ init_instr(nir_instr *instr, nir_instr_worklist *worklist)
          mark_and_push(worklist, instr);
       break;
 
+   case nir_instr_type_deref:
+      deref_instr = nir_instr_as_deref(instr);
+      if (!deref_instr->dest.is_ssa)
+         mark_and_push(worklist, instr);
+      break;
+
    case nir_instr_type_intrinsic:
       intrin_instr = nir_instr_as_intrinsic(instr);
       if (nir_intrinsic_infos[intrin_instr->intrinsic].flags &
diff --git a/src/compiler/nir/nir_print.c b/src/compiler/nir/nir_print.c
index fad274e..37dd7c2 100644
--- a/src/compiler/nir/nir_print.c
+++ b/src/compiler/nir/nir_print.c
@@ -395,7 +395,7 @@ print_constant(nir_constant *c, const struct glsl_type *type, print_state *state
 }
 
 static const char *
-get_variable_mode_str(nir_variable_mode mode)
+get_variable_mode_str(nir_variable_mode mode, bool want_local_global_mode)
 {
    switch (mode) {
    case nir_var_shader_in:
@@ -412,7 +412,9 @@ get_variable_mode_str(nir_variable_mode mode)
       return "shared";
    case nir_var_param:
    case nir_var_global:
+      return want_local_global_mode ? "global" : "";
    case nir_var_local:
+      return want_local_global_mode ? "local" : "";
    default:
       return "";
    }
@@ -430,7 +432,7 @@ print_var_decl(nir_variable *var, print_state *state)
    const char *const patch = (var->data.patch) ? "patch " : "";
    const char *const inv = (var->data.invariant) ? "invariant " : "";
    fprintf(fp, "%s%s%s%s%s %s ",
-           cent, samp, patch, inv, get_variable_mode_str(var->data.mode),
+           cent, samp, patch, inv, get_variable_mode_str(var->data.mode, false),
            glsl_interp_mode_name(var->data.interpolation));
 
    const char *const coher = (var->data.image.coherent) ? "coherent " : "";
@@ -520,6 +522,128 @@ print_var_decl(nir_variable *var, print_state *state)
 }
 
 static void
+print_deref_link(nir_deref_instr *instr, bool whole_chain, print_state *state)
+{
+   FILE *fp = state->fp;
+
+   if (instr->deref_type == nir_deref_type_var) {
+      fprintf(fp, "%s", get_var_name(instr->var, state));
+      return;
+   } else if (instr->deref_type == nir_deref_type_cast) {
+      fprintf(fp, "(%s *)", glsl_get_type_name(instr->type));
+      print_src(&instr->parent, state);
+      return;
+   }
+
+   assert(instr->parent.is_ssa);
+   nir_deref_instr *parent =
+      nir_instr_as_deref(instr->parent.ssa->parent_instr);
+
+   /* Is the parent we're going to print a bare cast? */
+   const bool is_parent_cast =
+      whole_chain && parent->deref_type == nir_deref_type_cast;
+
+   /* If we're not printing the whole chain, the parent we print will be a SSA
+    * value that represents a pointer.  The only deref type that naturally
+    * gives a pointer is a cast.
+    */
+   const bool is_parent_pointer =
+      !whole_chain || parent->deref_type == nir_deref_type_cast;
+
+   /* Struct derefs have a nice syntax that works on pointers, arrays derefs
+    * do not.
+    */
+   const bool need_deref =
+      is_parent_pointer && instr->deref_type != nir_deref_type_struct;
+
+   /* Cast need extra parens and so * dereferences */
+   if (is_parent_cast || need_deref)
+      fprintf(fp, "(");
+
+   if (need_deref)
+      fprintf(fp, "*");
+
+   if (whole_chain) {
+      print_deref_link(parent, whole_chain, state);
+   } else {
+      print_src(&instr->parent, state);
+   }
+
+   if (is_parent_cast || need_deref)
+      fprintf(fp, ")");
+
+   switch (instr->deref_type) {
+   case nir_deref_type_struct:
+      fprintf(fp, "%s%s", is_parent_pointer ? "->" : ".",
+              glsl_get_struct_elem_name(parent->type, instr->strct.index));
+      break;
+
+   case nir_deref_type_array: {
+      nir_const_value *const_index = nir_src_as_const_value(instr->arr.index);
+      if (const_index) {
+         fprintf(fp, "[%u]", const_index->u32[0]);
+      } else {
+         fprintf(fp, "[");
+         print_src(&instr->arr.index, state);
+         fprintf(fp, "]");
+      }
+      break;
+   }
+
+   case nir_deref_type_array_wildcard:
+      fprintf(fp, "[*]");
+      break;
+
+   default:
+      unreachable("Invalid deref instruction type");
+   }
+}
+
+static void
+print_deref_instr(nir_deref_instr *instr, print_state *state)
+{
+   FILE *fp = state->fp;
+
+   print_dest(&instr->dest, state);
+
+   switch (instr->deref_type) {
+   case nir_deref_type_var:
+      fprintf(fp, " = deref_var ");
+      break;
+   case nir_deref_type_array:
+   case nir_deref_type_array_wildcard:
+      fprintf(fp, " = deref_array ");
+      break;
+   case nir_deref_type_struct:
+      fprintf(fp, " = deref_struct ");
+      break;
+   case nir_deref_type_cast:
+      fprintf(fp, " = deref_cast ");
+      break;
+   default:
+      unreachable("Invalid deref instruction type");
+   }
+
+   /* Only casts naturally return a pointer type */
+   if (instr->deref_type != nir_deref_type_cast)
+      fprintf(fp, "&");
+
+   print_deref_link(instr, false, state);
+
+   fprintf(fp, " (%s %s) ",
+           get_variable_mode_str(instr->mode, true),
+           glsl_get_type_name(instr->type));
+
+   if (instr->deref_type != nir_deref_type_var &&
+       instr->deref_type != nir_deref_type_cast) {
+      /* Print the entire chain as a comment */
+      fprintf(fp, "/* &");
+      print_deref_link(instr, true, state);
+      fprintf(fp, " */");
+   }
+}
+
+static void
 print_var(nir_variable *var, print_state *state)
 {
    FILE *fp = state->fp;
@@ -967,6 +1091,10 @@ print_instr(const nir_instr *instr, print_state *state, unsigned tabs)
       print_alu_instr(nir_instr_as_alu(instr), state);
       break;
 
+   case nir_instr_type_deref:
+      print_deref_instr(nir_instr_as_deref(instr), state);
+      break;
+
    case nir_instr_type_call:
       print_call_instr(nir_instr_as_call(instr), state);
       break;
diff --git a/src/compiler/nir/nir_serialize.c b/src/compiler/nir/nir_serialize.c
index 00df49c..834a65b 100644
--- a/src/compiler/nir/nir_serialize.c
+++ b/src/compiler/nir/nir_serialize.c
@@ -479,6 +479,81 @@ read_alu(read_ctx *ctx)
 }
 
 static void
+write_deref(write_ctx *ctx, const nir_deref_instr *deref)
+{
+   blob_write_uint32(ctx->blob, deref->deref_type);
+
+   blob_write_uint32(ctx->blob, deref->mode);
+   encode_type_to_blob(ctx->blob, deref->type);
+
+   write_dest(ctx, &deref->dest);
+
+   if (deref->deref_type == nir_deref_type_var) {
+      write_object(ctx, deref->var);
+      return;
+   }
+
+   write_src(ctx, &deref->parent);
+
+   switch (deref->deref_type) {
+   case nir_deref_type_struct:
+      blob_write_uint32(ctx->blob, deref->strct.index);
+      break;
+
+   case nir_deref_type_array:
+      write_src(ctx, &deref->arr.index);
+      break;
+
+   case nir_deref_type_array_wildcard:
+   case nir_deref_type_cast:
+      /* Nothing to do */
+      break;
+
+   default:
+      unreachable("Invalid deref type");
+   }
+}
+
+static nir_deref_instr *
+read_deref(read_ctx *ctx)
+{
+   nir_deref_type deref_type = blob_read_uint32(ctx->blob);
+   nir_deref_instr *deref = nir_deref_instr_create(ctx->nir, deref_type);
+
+   deref->mode = blob_read_uint32(ctx->blob);
+   deref->type = decode_type_from_blob(ctx->blob);
+
+   read_dest(ctx, &deref->dest, &deref->instr);
+
+   if (deref_type == nir_deref_type_var) {
+      deref->var = read_object(ctx);
+      return deref;
+   }
+
+   read_src(ctx, &deref->parent, &deref->instr);
+
+   switch (deref->deref_type) {
+   case nir_deref_type_struct:
+      deref->strct.index = blob_read_uint32(ctx->blob);
+      break;
+
+   case nir_deref_type_array:
+      read_src(ctx, &deref->arr.index, &deref->instr);
+      break;
+
+   case nir_deref_type_array_wildcard:
+   case nir_deref_type_cast:
+      /* Nothing to do */
+      break;
+
+   default:
+      unreachable("Invalid deref type");
+   }
+
+   return deref;
+}
+
+static void
 write_intrinsic(write_ctx *ctx, const nir_intrinsic_instr *intrin)
 {
    blob_write_uint32(ctx->blob, intrin->intrinsic);
@@ -803,6 +878,9 @@ write_instr(write_ctx *ctx, const nir_instr *instr)
    case nir_instr_type_alu:
       write_alu(ctx, nir_instr_as_alu(instr));
       break;
+   case nir_instr_type_deref:
+      write_deref(ctx, nir_instr_as_deref(instr));
+      break;
    case nir_instr_type_intrinsic:
       write_intrinsic(ctx, nir_instr_as_intrinsic(instr));
       break;
@@ -840,6 +918,9 @@ read_instr(read_ctx *ctx, nir_block *block)
    case nir_instr_type_alu:
       instr = &read_alu(ctx)->instr;
       break;
+   case nir_instr_type_deref:
+      instr = &read_deref(ctx)->instr;
+      break;
    case nir_instr_type_intrinsic:
       instr = &read_intrinsic(ctx)->instr;
       break;
diff --git a/src/compiler/nir/nir_validate.c b/src/compiler/nir/nir_validate.c
index 490f0b5..07af71d 100644
--- a/src/compiler/nir/nir_validate.c
+++ b/src/compiler/nir/nir_validate.c
@@ -471,6 +471,85 @@ validate_deref_var(void *parent_mem_ctx, nir_deref_var *deref, validate_state *s
 }
 
 static void
+validate_deref_instr(nir_deref_instr *instr, validate_state *state)
+{
+   if (instr->deref_type == nir_deref_type_var) {
+      /* Variable dereferences are stupid simple. */
+      validate_assert(state, instr->mode == instr->var->data.mode);
+      validate_assert(state, instr->type == instr->var->type);
+      validate_var_use(instr->var, state);
+   } else if (instr->deref_type == nir_deref_type_cast) {
+      /* For cast, we simply have to trust the instruction.  It's up to
+       * lowering passes and front/back-ends to make them sane.
+       */
+      validate_src(&instr->parent, state, 0, 0);
+
+      /* We just validate that the type and mode are there */
+      validate_assert(state, instr->mode);
+      validate_assert(state, instr->type);
+   } else {
+      /* We require the parent to be SSA.  This may be lifted in the future */
+      validate_assert(state, instr->parent.is_ssa);
+
+      /* The parent pointer value must have the same number of components
+       * as the destination.
+       */
+      validate_src(&instr->parent, state, nir_dest_bit_size(instr->dest),
+                   nir_dest_num_components(instr->dest));
+
+      nir_instr *parent_instr = instr->parent.ssa->parent_instr;
+
+      /* The parent must come from another deref instruction */
+      validate_assert(state, parent_instr->type == nir_instr_type_deref);
+
+      nir_deref_instr *parent = nir_instr_as_deref(parent_instr);
+
+      validate_assert(state, instr->mode == parent->mode);
+
+      switch (instr->deref_type) {
+      case nir_deref_type_struct:
+         validate_assert(state, glsl_type_is_struct(parent->type));
+         validate_assert(state,
+            instr->strct.index < glsl_get_length(parent->type));
+         validate_assert(state, instr->type ==
+            glsl_get_struct_field(parent->type, instr->strct.index));
+         break;
+
+      case nir_deref_type_array:
+      case nir_deref_type_array_wildcard:
+         if (instr->mode == nir_var_shared) {
+            /* Shared variables have a bit more relaxed rules because we need
+             * to be able to handle array derefs on vectors.  Fortunately,
+             * nir_lower_io handles these just fine.
+             */
+            validate_assert(state, glsl_type_is_array(parent->type) ||
+                                   glsl_type_is_matrix(parent->type) ||
+                                   glsl_type_is_vector(parent->type));
+         } else {
+            /* Most of NIR cannot handle array derefs on vectors */
+            validate_assert(state, glsl_type_is_array(parent->type) ||
+                                   glsl_type_is_matrix(parent->type));
+         }
+         validate_assert(state,
+            instr->type == glsl_get_array_element(parent->type));
+
+         if (instr->deref_type == nir_deref_type_array)
+            validate_src(&instr->arr.index, state, 32, 1);
+         break;
+
+      default:
+         unreachable("Invalid deref instruction type");
+      }
+   }
+
+   /* We intentionally don't validate the size of the destination because we
+    * want to let other compiler components such as SPIR-V decide how big
+    * pointers should be.
+    */
+   validate_dest(&instr->dest, state, 0, 0);
+}
+
+static void
 validate_intrinsic_instr(nir_intrinsic_instr *instr, validate_state *state)
 {
    unsigned dest_bit_size = 0;
@@ -618,6 +697,10 @@ validate_instr(nir_instr *instr, validate_state *state)
       validate_alu_instr(nir_instr_as_alu(instr), state);
       break;
 
+   case nir_instr_type_deref:
+      validate_deref_instr(nir_instr_as_deref(instr), state);
+      break;
+
    case nir_instr_type_call:
       validate_call_instr(nir_instr_as_call(instr), state);
       break;
-- 
2.5.0.400.gff86faf



More information about the mesa-dev mailing list