nir: add serialization and deserialization
authorConnor Abbott <cwabbott0@gmail.com>
Wed, 13 Sep 2017 03:17:51 +0000 (23:17 -0400)
committerJordan Justen <jordan.l.justen@intel.com>
Wed, 1 Nov 2017 06:36:53 +0000 (23:36 -0700)
v2 (Jason Ekstrand):
 - Various whitespace cleanups
 - Add helpers for reading/writing objects
 - Rework derefs
 - [de]serialize nir_shader::num_*
 - Fix uses of blob_reserve_bytes
 - Use a bitfield struct for packing tex_instr data

v3:
 - Zero nir_variable struct on deserialization. (Jordan)
 - Allow nir_serialize.h to be included in C++. (Jordan)
 - Handle NULL info.name. (Jason)
 - Set info.name to NULL when name is NULL. (Jordan)

Acked-by: Timothy Arceri <tarceri@itsqueeze.com>
Reviewed-by: Jordan Justen <jordan.l.justen@intel.com>
Acked-by: Jason Ekstrand <jason@jlekstrand.net>
src/compiler/Makefile.sources
src/compiler/nir/meson.build
src/compiler/nir/nir_serialize.c [new file with mode: 0644]
src/compiler/nir/nir_serialize.h [new file with mode: 0644]

index 27cc33ab8356d7cca87c50735d0c2d4ddfa42cff..b80468c7a444451f3f6746f4267b940715b231b6 100644 (file)
@@ -277,6 +277,8 @@ NIR_FILES = \
        nir/nir_search.c \
        nir/nir_search.h \
        nir/nir_search_helpers.h \
+       nir/nir_serialize.c \
+       nir/nir_serialize.h \
        nir/nir_split_var_copies.c \
        nir/nir_sweep.c \
        nir/nir_to_lcssa.c \
index cb88effa6283898ecc32556d780a613225bc8ad8..bb9ac084480c4b79aee0124dfd90171dba0b9c10 100644 (file)
@@ -164,6 +164,8 @@ files_libnir = files(
   'nir_search.c',
   'nir_search.h',
   'nir_search_helpers.h',
+  'nir_serialize.c',
+  'nir_serialize.h',
   'nir_split_var_copies.c',
   'nir_sweep.c',
   'nir_to_lcssa.c',
diff --git a/src/compiler/nir/nir_serialize.c b/src/compiler/nir/nir_serialize.c
new file mode 100644 (file)
index 0000000..bb6a5d8
--- /dev/null
@@ -0,0 +1,1201 @@
+/*
+ * Copyright © 2017 Connor Abbott
+ *
+ * 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 "nir_serialize.h"
+#include "nir_control_flow.h"
+#include "util/u_dynarray.h"
+
+typedef struct {
+   size_t blob_offset;
+   nir_ssa_def *src;
+   nir_block *block;
+} write_phi_fixup;
+
+typedef struct {
+   const nir_shader *nir;
+
+   struct blob *blob;
+
+   /* maps pointer to index */
+   struct hash_table *remap_table;
+
+   /* the next index to assign to a NIR in-memory object */
+   uintptr_t next_idx;
+
+   /* Array of write_phi_fixup structs representing phi sources that need to
+    * be resolved in the second pass.
+    */
+   struct util_dynarray phi_fixups;
+} write_ctx;
+
+typedef struct {
+   nir_shader *nir;
+
+   struct blob_reader *blob;
+
+   /* the next index to assign to a NIR in-memory object */
+   uintptr_t next_idx;
+
+   /* The length of the index -> object table */
+   uintptr_t idx_table_len;
+
+   /* map from index to deserialized pointer */
+   void **idx_table;
+
+   /* List of phi sources. */
+   struct list_head phi_srcs;
+
+} read_ctx;
+
+static void
+write_add_object(write_ctx *ctx, const void *obj)
+{
+   uintptr_t index = ctx->next_idx++;
+   _mesa_hash_table_insert(ctx->remap_table, obj, (void *) index);
+}
+
+static uintptr_t
+write_lookup_object(write_ctx *ctx, const void *obj)
+{
+   struct hash_entry *entry = _mesa_hash_table_search(ctx->remap_table, obj);
+   assert(entry);
+   return (uintptr_t) entry->data;
+}
+
+static void
+write_object(write_ctx *ctx, const void *obj)
+{
+   blob_write_intptr(ctx->blob, write_lookup_object(ctx, obj));
+}
+
+static void
+read_add_object(read_ctx *ctx, void *obj)
+{
+   assert(ctx->next_idx < ctx->idx_table_len);
+   ctx->idx_table[ctx->next_idx++] = obj;
+}
+
+static void *
+read_lookup_object(read_ctx *ctx, uintptr_t idx)
+{
+   assert(idx < ctx->idx_table_len);
+   return ctx->idx_table[idx];
+}
+
+static void *
+read_object(read_ctx *ctx)
+{
+   return read_lookup_object(ctx, blob_read_intptr(ctx->blob));
+}
+
+static void
+write_constant(write_ctx *ctx, const nir_constant *c)
+{
+   blob_write_bytes(ctx->blob, c->values, sizeof(c->values));
+   blob_write_uint32(ctx->blob, c->num_elements);
+   for (unsigned i = 0; i < c->num_elements; i++)
+      write_constant(ctx, c->elements[i]);
+}
+
+static nir_constant *
+read_constant(read_ctx *ctx, nir_variable *nvar)
+{
+   nir_constant *c = ralloc(nvar, nir_constant);
+
+   blob_copy_bytes(ctx->blob, (uint8_t *)c->values, sizeof(c->values));
+   c->num_elements = blob_read_uint32(ctx->blob);
+   c->elements = ralloc_array(ctx->nir, nir_constant *, c->num_elements);
+   for (unsigned i = 0; i < c->num_elements; i++)
+      c->elements[i] = read_constant(ctx, nvar);
+
+   return c;
+}
+
+static void
+write_variable(write_ctx *ctx, const nir_variable *var)
+{
+   write_add_object(ctx, var);
+   encode_type_to_blob(ctx->blob, var->type);
+   blob_write_uint32(ctx->blob, !!(var->name));
+   blob_write_string(ctx->blob, var->name);
+   blob_write_bytes(ctx->blob, (uint8_t *) &var->data, sizeof(var->data));
+   blob_write_uint32(ctx->blob, var->num_state_slots);
+   blob_write_bytes(ctx->blob, (uint8_t *) var->state_slots,
+                    var->num_state_slots * sizeof(nir_state_slot));
+   blob_write_uint32(ctx->blob, !!(var->constant_initializer));
+   if (var->constant_initializer)
+      write_constant(ctx, var->constant_initializer);
+   blob_write_uint32(ctx->blob, !!(var->interface_type));
+   if (var->interface_type)
+      encode_type_to_blob(ctx->blob, var->interface_type);
+}
+
+static nir_variable *
+read_variable(read_ctx *ctx)
+{
+   nir_variable *var = rzalloc(ctx->nir, nir_variable);
+   read_add_object(ctx, var);
+
+   var->type = decode_type_from_blob(ctx->blob);
+   bool has_name = blob_read_uint32(ctx->blob);
+   if (has_name) {
+      const char *name = blob_read_string(ctx->blob);
+      var->name = ralloc_strdup(var, name);
+   } else {
+      var->name = NULL;
+   }
+   blob_copy_bytes(ctx->blob, (uint8_t *) &var->data, sizeof(var->data));
+   var->num_state_slots = blob_read_uint32(ctx->blob);
+   var->state_slots = ralloc_array(var, nir_state_slot, var->num_state_slots);
+   blob_copy_bytes(ctx->blob, (uint8_t *) var->state_slots,
+                   var->num_state_slots * sizeof(nir_state_slot));
+   bool has_const_initializer = blob_read_uint32(ctx->blob);
+   if (has_const_initializer)
+      var->constant_initializer = read_constant(ctx, var);
+   else
+      var->constant_initializer = NULL;
+   bool has_interface_type = blob_read_uint32(ctx->blob);
+   if (has_interface_type)
+      var->interface_type = decode_type_from_blob(ctx->blob);
+   else
+      var->interface_type = NULL;
+
+   return var;
+}
+
+static void
+write_var_list(write_ctx *ctx, const struct exec_list *src)
+{
+   blob_write_uint32(ctx->blob, exec_list_length(src));
+   foreach_list_typed(nir_variable, var, node, src) {
+      write_variable(ctx, var);
+   }
+}
+
+static void
+read_var_list(read_ctx *ctx, struct exec_list *dst)
+{
+   exec_list_make_empty(dst);
+   unsigned num_vars = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < num_vars; i++) {
+      nir_variable *var = read_variable(ctx);
+      exec_list_push_tail(dst, &var->node);
+   }
+}
+
+static void
+write_register(write_ctx *ctx, const nir_register *reg)
+{
+   write_add_object(ctx, reg);
+   blob_write_uint32(ctx->blob, reg->num_components);
+   blob_write_uint32(ctx->blob, reg->bit_size);
+   blob_write_uint32(ctx->blob, reg->num_array_elems);
+   blob_write_uint32(ctx->blob, reg->index);
+   blob_write_uint32(ctx->blob, !!(reg->name));
+   if (reg->name)
+      blob_write_string(ctx->blob, reg->name);
+   blob_write_uint32(ctx->blob, reg->is_global << 1 | reg->is_packed);
+}
+
+static nir_register *
+read_register(read_ctx *ctx)
+{
+   nir_register *reg = ralloc(ctx->nir, nir_register);
+   read_add_object(ctx, reg);
+   reg->num_components = blob_read_uint32(ctx->blob);
+   reg->bit_size = blob_read_uint32(ctx->blob);
+   reg->num_array_elems = blob_read_uint32(ctx->blob);
+   reg->index = blob_read_uint32(ctx->blob);
+   bool has_name = blob_read_uint32(ctx->blob);
+   if (has_name) {
+      const char *name = blob_read_string(ctx->blob);
+      reg->name = ralloc_strdup(reg, name);
+   } else {
+      reg->name = NULL;
+   }
+   unsigned flags = blob_read_uint32(ctx->blob);
+   reg->is_global = flags & 0x2;
+   reg->is_packed = flags & 0x1;
+
+   list_inithead(&reg->uses);
+   list_inithead(&reg->defs);
+   list_inithead(&reg->if_uses);
+
+   return reg;
+}
+
+static void
+write_reg_list(write_ctx *ctx, const struct exec_list *src)
+{
+   blob_write_uint32(ctx->blob, exec_list_length(src));
+   foreach_list_typed(nir_register, reg, node, src)
+      write_register(ctx, reg);
+}
+
+static void
+read_reg_list(read_ctx *ctx, struct exec_list *dst)
+{
+   exec_list_make_empty(dst);
+   unsigned num_regs = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < num_regs; i++) {
+      nir_register *reg = read_register(ctx);
+      exec_list_push_tail(dst, &reg->node);
+   }
+}
+
+static void
+write_src(write_ctx *ctx, const nir_src *src)
+{
+   /* Since sources are very frequent, we try to save some space when storing
+    * them. In particular, we store whether the source is a register and
+    * whether the register has an indirect index in the low two bits. We can
+    * assume that the high two bits of the index are zero, since otherwise our
+    * address space would've been exhausted allocating the remap table!
+    */
+   if (src->is_ssa) {
+      uintptr_t idx = write_lookup_object(ctx, src->ssa) << 2;
+      idx |= 1;
+      blob_write_intptr(ctx->blob, idx);
+   } else {
+      uintptr_t idx = write_lookup_object(ctx, src->reg.reg) << 2;
+      if (src->reg.indirect)
+         idx |= 2;
+      blob_write_intptr(ctx->blob, idx);
+      blob_write_uint32(ctx->blob, src->reg.base_offset);
+      if (src->reg.indirect) {
+         write_src(ctx, src->reg.indirect);
+      }
+   }
+}
+
+static void
+read_src(read_ctx *ctx, nir_src *src, void *mem_ctx)
+{
+   uintptr_t val = blob_read_intptr(ctx->blob);
+   uintptr_t idx = val >> 2;
+   src->is_ssa = val & 0x1;
+   if (src->is_ssa) {
+      src->ssa = read_lookup_object(ctx, idx);
+   } else {
+      bool is_indirect = val & 0x2;
+      src->reg.reg = read_lookup_object(ctx, idx);
+      src->reg.base_offset = blob_read_uint32(ctx->blob);
+      if (is_indirect) {
+         src->reg.indirect = ralloc(mem_ctx, nir_src);
+         read_src(ctx, src->reg.indirect, mem_ctx);
+      } else {
+         src->reg.indirect = NULL;
+      }
+   }
+}
+
+static void
+write_dest(write_ctx *ctx, const nir_dest *dst)
+{
+   uint32_t val = dst->is_ssa;
+   if (dst->is_ssa) {
+      val |= !!(dst->ssa.name) << 1;
+      val |= dst->ssa.num_components << 2;
+      val |= dst->ssa.bit_size << 5;
+   } else {
+      val |= !!(dst->reg.indirect) << 1;
+   }
+   blob_write_uint32(ctx->blob, val);
+   if (dst->is_ssa) {
+      write_add_object(ctx, &dst->ssa);
+      if (dst->ssa.name)
+         blob_write_string(ctx->blob, dst->ssa.name);
+   } else {
+      blob_write_intptr(ctx->blob, write_lookup_object(ctx, dst->reg.reg));
+      blob_write_uint32(ctx->blob, dst->reg.base_offset);
+      if (dst->reg.indirect)
+         write_src(ctx, dst->reg.indirect);
+   }
+}
+
+static void
+read_dest(read_ctx *ctx, nir_dest *dst, nir_instr *instr)
+{
+   uint32_t val = blob_read_uint32(ctx->blob);
+   bool is_ssa = val & 0x1;
+   if (is_ssa) {
+      bool has_name = val & 0x2;
+      unsigned num_components = (val >> 2) & 0x7;
+      unsigned bit_size = val >> 5;
+      char *name = has_name ? blob_read_string(ctx->blob) : NULL;
+      nir_ssa_dest_init(instr, dst, num_components, bit_size, name);
+      read_add_object(ctx, &dst->ssa);
+   } else {
+      bool is_indirect = val & 0x2;
+      dst->reg.reg = read_object(ctx);
+      dst->reg.base_offset = blob_read_uint32(ctx->blob);
+      if (is_indirect) {
+         dst->reg.indirect = ralloc(instr, nir_src);
+         read_src(ctx, dst->reg.indirect, instr);
+      }
+   }
+}
+
+static void
+write_deref_chain(write_ctx *ctx, const nir_deref_var *deref_var)
+{
+   write_object(ctx, deref_var->var);
+
+   uint32_t len = 0;
+   for (const nir_deref *d = deref_var->deref.child; d; d = d->child)
+      len++;
+   blob_write_uint32(ctx->blob, len);
+
+   for (const nir_deref *d = deref_var->deref.child; d; d = d->child) {
+      blob_write_uint32(ctx->blob, d->deref_type);
+      switch (d->deref_type) {
+      case nir_deref_type_array: {
+         const nir_deref_array *deref_array = nir_deref_as_array(d);
+         blob_write_uint32(ctx->blob, deref_array->deref_array_type);
+         blob_write_uint32(ctx->blob, deref_array->base_offset);
+         if (deref_array->deref_array_type == nir_deref_array_type_indirect)
+            write_src(ctx, &deref_array->indirect);
+         break;
+      }
+      case nir_deref_type_struct: {
+         const nir_deref_struct *deref_struct = nir_deref_as_struct(d);
+         blob_write_uint32(ctx->blob, deref_struct->index);
+         break;
+      }
+      case nir_deref_type_var:
+         unreachable("Invalid deref type");
+      }
+
+      encode_type_to_blob(ctx->blob, d->type);
+   }
+}
+
+static nir_deref_var *
+read_deref_chain(read_ctx *ctx, void *mem_ctx)
+{
+   nir_variable *var = read_object(ctx);
+   nir_deref_var *deref_var = nir_deref_var_create(mem_ctx, var);
+
+   uint32_t len = blob_read_uint32(ctx->blob);
+
+   nir_deref *tail = &deref_var->deref;
+   for (uint32_t i = 0; i < len; i++) {
+      nir_deref_type deref_type = blob_read_uint32(ctx->blob);
+      nir_deref *deref = NULL;
+      switch (deref_type) {
+      case nir_deref_type_array: {
+         nir_deref_array *deref_array = nir_deref_array_create(tail);
+         deref_array->deref_array_type = blob_read_uint32(ctx->blob);
+         deref_array->base_offset = blob_read_uint32(ctx->blob);
+         if (deref_array->deref_array_type == nir_deref_array_type_indirect)
+            read_src(ctx, &deref_array->indirect, mem_ctx);
+         deref = &deref_array->deref;
+         break;
+      }
+      case nir_deref_type_struct: {
+         uint32_t index = blob_read_uint32(ctx->blob);
+         nir_deref_struct *deref_struct = nir_deref_struct_create(tail, index);
+         deref = &deref_struct->deref;
+         break;
+      }
+      case nir_deref_type_var:
+         unreachable("Invalid deref type");
+      }
+
+      deref->type = decode_type_from_blob(ctx->blob);
+
+      tail->child = deref;
+      tail = deref;
+   }
+
+   return deref_var;
+}
+
+static void
+write_alu(write_ctx *ctx, const nir_alu_instr *alu)
+{
+   blob_write_uint32(ctx->blob, alu->op);
+   uint32_t flags = alu->exact;
+   flags |= alu->dest.saturate << 1;
+   flags |= alu->dest.write_mask << 2;
+   blob_write_uint32(ctx->blob, flags);
+
+   write_dest(ctx, &alu->dest.dest);
+
+   for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) {
+      write_src(ctx, &alu->src[i].src);
+      flags = alu->src[i].negate;
+      flags |= alu->src[i].abs << 1;
+      for (unsigned j = 0; j < 4; j++)
+         flags |= alu->src[i].swizzle[j] << (2 + 2 * j);
+      blob_write_uint32(ctx->blob, flags);
+   }
+}
+
+static nir_alu_instr *
+read_alu(read_ctx *ctx)
+{
+   nir_op op = blob_read_uint32(ctx->blob);
+   nir_alu_instr *alu = nir_alu_instr_create(ctx->nir, op);
+
+   uint32_t flags = blob_read_uint32(ctx->blob);
+   alu->exact = flags & 1;
+   alu->dest.saturate = flags & 2;
+   alu->dest.write_mask = flags >> 2;
+
+   read_dest(ctx, &alu->dest.dest, &alu->instr);
+
+   for (unsigned i = 0; i < nir_op_infos[op].num_inputs; i++) {
+      read_src(ctx, &alu->src[i].src, &alu->instr);
+      flags = blob_read_uint32(ctx->blob);
+      alu->src[i].negate = flags & 1;
+      alu->src[i].abs = flags & 2;
+      for (unsigned j = 0; j < 4; j++)
+         alu->src[i].swizzle[j] = (flags >> (2 * j + 2)) & 3;
+   }
+
+   return alu;
+}
+
+static void
+write_intrinsic(write_ctx *ctx, const nir_intrinsic_instr *intrin)
+{
+   blob_write_uint32(ctx->blob, intrin->intrinsic);
+
+   unsigned num_variables = nir_intrinsic_infos[intrin->intrinsic].num_variables;
+   unsigned num_srcs = nir_intrinsic_infos[intrin->intrinsic].num_srcs;
+   unsigned num_indices = nir_intrinsic_infos[intrin->intrinsic].num_indices;
+
+   blob_write_uint32(ctx->blob, intrin->num_components);
+
+   if (nir_intrinsic_infos[intrin->intrinsic].has_dest)
+      write_dest(ctx, &intrin->dest);
+
+   for (unsigned i = 0; i < num_variables; i++)
+      write_deref_chain(ctx, intrin->variables[i]);
+
+   for (unsigned i = 0; i < num_srcs; i++)
+      write_src(ctx, &intrin->src[i]);
+
+   for (unsigned i = 0; i < num_indices; i++)
+      blob_write_uint32(ctx->blob, intrin->const_index[i]);
+}
+
+static nir_intrinsic_instr *
+read_intrinsic(read_ctx *ctx)
+{
+   nir_intrinsic_op op = blob_read_uint32(ctx->blob);
+
+   nir_intrinsic_instr *intrin = nir_intrinsic_instr_create(ctx->nir, op);
+
+   unsigned num_variables = nir_intrinsic_infos[op].num_variables;
+   unsigned num_srcs = nir_intrinsic_infos[op].num_srcs;
+   unsigned num_indices = nir_intrinsic_infos[op].num_indices;
+
+   intrin->num_components = blob_read_uint32(ctx->blob);
+
+   if (nir_intrinsic_infos[op].has_dest)
+      read_dest(ctx, &intrin->dest, &intrin->instr);
+
+   for (unsigned i = 0; i < num_variables; i++)
+      intrin->variables[i] = read_deref_chain(ctx, &intrin->instr);
+
+   for (unsigned i = 0; i < num_srcs; i++)
+      read_src(ctx, &intrin->src[i], &intrin->instr);
+
+   for (unsigned i = 0; i < num_indices; i++)
+      intrin->const_index[i] = blob_read_uint32(ctx->blob);
+
+   return intrin;
+}
+
+static void
+write_load_const(write_ctx *ctx, const nir_load_const_instr *lc)
+{
+   uint32_t val = lc->def.num_components;
+   val |= lc->def.bit_size << 3;
+   blob_write_uint32(ctx->blob, val);
+   blob_write_bytes(ctx->blob, (uint8_t *) &lc->value, sizeof(lc->value));
+   write_add_object(ctx, &lc->def);
+}
+
+static nir_load_const_instr *
+read_load_const(read_ctx *ctx)
+{
+   uint32_t val = blob_read_uint32(ctx->blob);
+
+   nir_load_const_instr *lc =
+      nir_load_const_instr_create(ctx->nir, val & 0x7, val >> 3);
+
+   blob_copy_bytes(ctx->blob, (uint8_t *) &lc->value, sizeof(lc->value));
+   read_add_object(ctx, &lc->def);
+   return lc;
+}
+
+static void
+write_ssa_undef(write_ctx *ctx, const nir_ssa_undef_instr *undef)
+{
+   uint32_t val = undef->def.num_components;
+   val |= undef->def.bit_size << 3;
+   blob_write_uint32(ctx->blob, val);
+   write_add_object(ctx, &undef->def);
+}
+
+static nir_ssa_undef_instr *
+read_ssa_undef(read_ctx *ctx)
+{
+   uint32_t val = blob_read_uint32(ctx->blob);
+
+   nir_ssa_undef_instr *undef =
+      nir_ssa_undef_instr_create(ctx->nir, val & 0x7, val >> 3);
+
+   read_add_object(ctx, &undef->def);
+   return undef;
+}
+
+union packed_tex_data {
+   uint32_t u32;
+   struct {
+      enum glsl_sampler_dim sampler_dim:4;
+      nir_alu_type dest_type:8;
+      unsigned coord_components:3;
+      unsigned is_array:1;
+      unsigned is_shadow:1;
+      unsigned is_new_style_shadow:1;
+      unsigned component:2;
+      unsigned has_texture_deref:1;
+      unsigned has_sampler_deref:1;
+   };
+};
+
+static void
+write_tex(write_ctx *ctx, const nir_tex_instr *tex)
+{
+   blob_write_uint32(ctx->blob, tex->num_srcs);
+   blob_write_uint32(ctx->blob, tex->op);
+   blob_write_uint32(ctx->blob, tex->texture_index);
+   blob_write_uint32(ctx->blob, tex->texture_array_size);
+   blob_write_uint32(ctx->blob, tex->sampler_index);
+
+   STATIC_ASSERT(sizeof(union packed_tex_data) == sizeof(uint32_t));
+   union packed_tex_data packed = {
+      .sampler_dim = tex->sampler_dim,
+      .dest_type = tex->dest_type,
+      .coord_components = tex->coord_components,
+      .is_array = tex->is_array,
+      .is_shadow = tex->is_shadow,
+      .is_new_style_shadow = tex->is_new_style_shadow,
+      .component = tex->component,
+      .has_texture_deref = tex->texture != NULL,
+      .has_sampler_deref = tex->sampler != NULL,
+   };
+   blob_write_uint32(ctx->blob, packed.u32);
+
+   write_dest(ctx, &tex->dest);
+   for (unsigned i = 0; i < tex->num_srcs; i++) {
+      blob_write_uint32(ctx->blob, tex->src[i].src_type);
+      write_src(ctx, &tex->src[i].src);
+   }
+
+   if (tex->texture)
+      write_deref_chain(ctx, tex->texture);
+   if (tex->sampler)
+      write_deref_chain(ctx, tex->sampler);
+}
+
+static nir_tex_instr *
+read_tex(read_ctx *ctx)
+{
+   unsigned num_srcs = blob_read_uint32(ctx->blob);
+   nir_tex_instr *tex = nir_tex_instr_create(ctx->nir, num_srcs);
+
+   tex->op = blob_read_uint32(ctx->blob);
+   tex->texture_index = blob_read_uint32(ctx->blob);
+   tex->texture_array_size = blob_read_uint32(ctx->blob);
+   tex->sampler_index = blob_read_uint32(ctx->blob);
+
+   union packed_tex_data packed;
+   packed.u32 = blob_read_uint32(ctx->blob);
+   tex->sampler_dim = packed.sampler_dim;
+   tex->dest_type = packed.dest_type;
+   tex->coord_components = packed.coord_components;
+   tex->is_array = packed.is_array;
+   tex->is_shadow = packed.is_shadow;
+   tex->is_new_style_shadow = packed.is_new_style_shadow;
+   tex->component = packed.component;
+
+   read_dest(ctx, &tex->dest, &tex->instr);
+   for (unsigned i = 0; i < tex->num_srcs; i++) {
+      tex->src[i].src_type = blob_read_uint32(ctx->blob);
+      read_src(ctx, &tex->src[i].src, &tex->instr);
+   }
+
+   tex->texture = packed.has_texture_deref ?
+                  read_deref_chain(ctx, &tex->instr) : NULL;
+   tex->sampler = packed.has_sampler_deref ?
+                  read_deref_chain(ctx, &tex->instr) : NULL;
+
+   return tex;
+}
+
+static void
+write_phi(write_ctx *ctx, const nir_phi_instr *phi)
+{
+   /* Phi nodes are special, since they may reference SSA definitions and
+    * basic blocks that don't exist yet. We leave two empty uintptr_t's here,
+    * and then store enough information so that a later fixup pass can fill
+    * them in correctly.
+    */
+   write_dest(ctx, &phi->dest);
+
+   blob_write_uint32(ctx->blob, exec_list_length(&phi->srcs));
+
+   nir_foreach_phi_src(src, phi) {
+      assert(src->src.is_ssa);
+      size_t blob_offset = blob_reserve_intptr(ctx->blob);
+      MAYBE_UNUSED size_t blob_offset2 = blob_reserve_intptr(ctx->blob);
+      assert(blob_offset + sizeof(uintptr_t) == blob_offset2);
+      write_phi_fixup fixup = {
+         .blob_offset = blob_offset,
+         .src = src->src.ssa,
+         .block = src->pred,
+      };
+      util_dynarray_append(&ctx->phi_fixups, write_phi_fixup, fixup);
+   }
+}
+
+static void
+write_fixup_phis(write_ctx *ctx)
+{
+   util_dynarray_foreach(&ctx->phi_fixups, write_phi_fixup, fixup) {
+      uintptr_t *blob_ptr = (uintptr_t *)(ctx->blob->data + fixup->blob_offset);
+      blob_ptr[0] = write_lookup_object(ctx, fixup->src);
+      blob_ptr[1] = write_lookup_object(ctx, fixup->block);
+   }
+
+   util_dynarray_clear(&ctx->phi_fixups);
+}
+
+static nir_phi_instr *
+read_phi(read_ctx *ctx, nir_block *blk)
+{
+   nir_phi_instr *phi = nir_phi_instr_create(ctx->nir);
+
+   read_dest(ctx, &phi->dest, &phi->instr);
+
+   unsigned num_srcs = blob_read_uint32(ctx->blob);
+
+   /* For similar reasons as before, we just store the index directly into the
+    * pointer, and let a later pass resolve the phi sources.
+    *
+    * In order to ensure that the copied sources (which are just the indices
+    * from the blob for now) don't get inserted into the old shader's use-def
+    * lists, we have to add the phi instruction *before* we set up its
+    * sources.
+    */
+   nir_instr_insert_after_block(blk, &phi->instr);
+
+   for (unsigned i = 0; i < num_srcs; i++) {
+      nir_phi_src *src = ralloc(phi, nir_phi_src);
+
+      src->src.is_ssa = true;
+      src->src.ssa = (nir_ssa_def *) blob_read_intptr(ctx->blob);
+      src->pred = (nir_block *) blob_read_intptr(ctx->blob);
+
+      /* Since we're not letting nir_insert_instr handle use/def stuff for us,
+       * we have to set the parent_instr manually.  It doesn't really matter
+       * when we do it, so we might as well do it here.
+       */
+      src->src.parent_instr = &phi->instr;
+
+      /* Stash it in the list of phi sources.  We'll walk this list and fix up
+       * sources at the very end of read_function_impl.
+       */
+      list_add(&src->src.use_link, &ctx->phi_srcs);
+
+      exec_list_push_tail(&phi->srcs, &src->node);
+   }
+
+   return phi;
+}
+
+static void
+read_fixup_phis(read_ctx *ctx)
+{
+   list_for_each_entry_safe(nir_phi_src, src, &ctx->phi_srcs, src.use_link) {
+      src->pred = read_lookup_object(ctx, (uintptr_t)src->pred);
+      src->src.ssa = read_lookup_object(ctx, (uintptr_t)src->src.ssa);
+
+      /* Remove from this list */
+      list_del(&src->src.use_link);
+
+      list_addtail(&src->src.use_link, &src->src.ssa->uses);
+   }
+   assert(list_empty(&ctx->phi_srcs));
+}
+
+static void
+write_jump(write_ctx *ctx, const nir_jump_instr *jmp)
+{
+   blob_write_uint32(ctx->blob, jmp->type);
+}
+
+static nir_jump_instr *
+read_jump(read_ctx *ctx)
+{
+   nir_jump_type type = blob_read_uint32(ctx->blob);
+   nir_jump_instr *jmp = nir_jump_instr_create(ctx->nir, type);
+   return jmp;
+}
+
+static void
+write_call(write_ctx *ctx, const nir_call_instr *call)
+{
+   blob_write_intptr(ctx->blob, write_lookup_object(ctx, call->callee));
+
+   for (unsigned i = 0; i < call->num_params; i++)
+      write_deref_chain(ctx, call->params[i]);
+
+   write_deref_chain(ctx, call->return_deref);
+}
+
+static nir_call_instr *
+read_call(read_ctx *ctx)
+{
+   nir_function *callee = read_object(ctx);
+   nir_call_instr *call = nir_call_instr_create(ctx->nir, callee);
+
+   for (unsigned i = 0; i < call->num_params; i++)
+      call->params[i] = read_deref_chain(ctx, &call->instr);
+
+   call->return_deref = read_deref_chain(ctx, &call->instr);
+
+   return call;
+}
+
+static void
+write_instr(write_ctx *ctx, const nir_instr *instr)
+{
+   blob_write_uint32(ctx->blob, instr->type);
+   switch (instr->type) {
+   case nir_instr_type_alu:
+      write_alu(ctx, nir_instr_as_alu(instr));
+      break;
+   case nir_instr_type_intrinsic:
+      write_intrinsic(ctx, nir_instr_as_intrinsic(instr));
+      break;
+   case nir_instr_type_load_const:
+      write_load_const(ctx, nir_instr_as_load_const(instr));
+      break;
+   case nir_instr_type_ssa_undef:
+      write_ssa_undef(ctx, nir_instr_as_ssa_undef(instr));
+      break;
+   case nir_instr_type_tex:
+      write_tex(ctx, nir_instr_as_tex(instr));
+      break;
+   case nir_instr_type_phi:
+      write_phi(ctx, nir_instr_as_phi(instr));
+      break;
+   case nir_instr_type_jump:
+      write_jump(ctx, nir_instr_as_jump(instr));
+      break;
+   case nir_instr_type_call:
+      write_call(ctx, nir_instr_as_call(instr));
+      break;
+   case nir_instr_type_parallel_copy:
+      unreachable("Cannot write parallel copies");
+   default:
+      unreachable("bad instr type");
+   }
+}
+
+static void
+read_instr(read_ctx *ctx, nir_block *block)
+{
+   nir_instr_type type = blob_read_uint32(ctx->blob);
+   nir_instr *instr;
+   switch (type) {
+   case nir_instr_type_alu:
+      instr = &read_alu(ctx)->instr;
+      break;
+   case nir_instr_type_intrinsic:
+      instr = &read_intrinsic(ctx)->instr;
+      break;
+   case nir_instr_type_load_const:
+      instr = &read_load_const(ctx)->instr;
+      break;
+   case nir_instr_type_ssa_undef:
+      instr = &read_ssa_undef(ctx)->instr;
+      break;
+   case nir_instr_type_tex:
+      instr = &read_tex(ctx)->instr;
+      break;
+   case nir_instr_type_phi:
+      /* Phi instructions are a bit of a special case when reading because we
+       * don't want inserting the instruction to automatically handle use/defs
+       * for us.  Instead, we need to wait until all the blocks/instructions
+       * are read so that we can set their sources up.
+       */
+      read_phi(ctx, block);
+      return;
+   case nir_instr_type_jump:
+      instr = &read_jump(ctx)->instr;
+      break;
+   case nir_instr_type_call:
+      instr = &read_call(ctx)->instr;
+      break;
+   case nir_instr_type_parallel_copy:
+      unreachable("Cannot read parallel copies");
+   default:
+      unreachable("bad instr type");
+   }
+
+   nir_instr_insert_after_block(block, instr);
+}
+
+static void
+write_block(write_ctx *ctx, const nir_block *block)
+{
+   write_add_object(ctx, block);
+   blob_write_uint32(ctx->blob, exec_list_length(&block->instr_list));
+   nir_foreach_instr(instr, block)
+      write_instr(ctx, instr);
+}
+
+static void
+read_block(read_ctx *ctx, struct exec_list *cf_list)
+{
+   /* Don't actually create a new block.  Just use the one from the tail of
+    * the list.  NIR guarantees that the tail of the list is a block and that
+    * no two blocks are side-by-side in the IR;  It should be empty.
+    */
+   nir_block *block =
+      exec_node_data(nir_block, exec_list_get_tail(cf_list), cf_node.node);
+
+   read_add_object(ctx, block);
+   unsigned num_instrs = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < num_instrs; i++) {
+      read_instr(ctx, block);
+   }
+}
+
+static void
+write_cf_list(write_ctx *ctx, const struct exec_list *cf_list);
+
+static void
+read_cf_list(read_ctx *ctx, struct exec_list *cf_list);
+
+static void
+write_if(write_ctx *ctx, nir_if *nif)
+{
+   write_src(ctx, &nif->condition);
+
+   write_cf_list(ctx, &nif->then_list);
+   write_cf_list(ctx, &nif->else_list);
+}
+
+static void
+read_if(read_ctx *ctx, struct exec_list *cf_list)
+{
+   nir_if *nif = nir_if_create(ctx->nir);
+
+   read_src(ctx, &nif->condition, nif);
+
+   nir_cf_node_insert_end(cf_list, &nif->cf_node);
+
+   read_cf_list(ctx, &nif->then_list);
+   read_cf_list(ctx, &nif->else_list);
+}
+
+static void
+write_loop(write_ctx *ctx, nir_loop *loop)
+{
+   write_cf_list(ctx, &loop->body);
+}
+
+static void
+read_loop(read_ctx *ctx, struct exec_list *cf_list)
+{
+   nir_loop *loop = nir_loop_create(ctx->nir);
+
+   nir_cf_node_insert_end(cf_list, &loop->cf_node);
+
+   read_cf_list(ctx, &loop->body);
+}
+
+static void
+write_cf_node(write_ctx *ctx, nir_cf_node *cf)
+{
+   blob_write_uint32(ctx->blob, cf->type);
+
+   switch (cf->type) {
+   case nir_cf_node_block:
+      write_block(ctx, nir_cf_node_as_block(cf));
+      break;
+   case nir_cf_node_if:
+      write_if(ctx, nir_cf_node_as_if(cf));
+      break;
+   case nir_cf_node_loop:
+      write_loop(ctx, nir_cf_node_as_loop(cf));
+      break;
+   default:
+      unreachable("bad cf type");
+   }
+}
+
+static void
+read_cf_node(read_ctx *ctx, struct exec_list *list)
+{
+   nir_cf_node_type type = blob_read_uint32(ctx->blob);
+
+   switch (type) {
+   case nir_cf_node_block:
+      read_block(ctx, list);
+      break;
+   case nir_cf_node_if:
+      read_if(ctx, list);
+      break;
+   case nir_cf_node_loop:
+      read_loop(ctx, list);
+      break;
+   default:
+      unreachable("bad cf type");
+   }
+}
+
+static void
+write_cf_list(write_ctx *ctx, const struct exec_list *cf_list)
+{
+   blob_write_uint32(ctx->blob, exec_list_length(cf_list));
+   foreach_list_typed(nir_cf_node, cf, node, cf_list) {
+      write_cf_node(ctx, cf);
+   }
+}
+
+static void
+read_cf_list(read_ctx *ctx, struct exec_list *cf_list)
+{
+   uint32_t num_cf_nodes = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < num_cf_nodes; i++)
+      read_cf_node(ctx, cf_list);
+}
+
+static void
+write_function_impl(write_ctx *ctx, const nir_function_impl *fi)
+{
+   write_var_list(ctx, &fi->locals);
+   write_reg_list(ctx, &fi->registers);
+   blob_write_uint32(ctx->blob, fi->reg_alloc);
+
+   blob_write_uint32(ctx->blob, fi->num_params);
+   for (unsigned i = 0; i < fi->num_params; i++) {
+      write_variable(ctx, fi->params[i]);
+   }
+
+   blob_write_uint32(ctx->blob, !!(fi->return_var));
+   if (fi->return_var)
+      write_variable(ctx, fi->return_var);
+
+   write_cf_list(ctx, &fi->body);
+   write_fixup_phis(ctx);
+}
+
+static nir_function_impl *
+read_function_impl(read_ctx *ctx, nir_function *fxn)
+{
+   nir_function_impl *fi = nir_function_impl_create_bare(ctx->nir);
+   fi->function = fxn;
+
+   read_var_list(ctx, &fi->locals);
+   read_reg_list(ctx, &fi->registers);
+   fi->reg_alloc = blob_read_uint32(ctx->blob);
+
+   fi->num_params = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < fi->num_params; i++) {
+      fi->params[i] = read_variable(ctx);
+   }
+
+   bool has_return = blob_read_uint32(ctx->blob);
+   if (has_return)
+      fi->return_var = read_variable(ctx);
+   else
+      fi->return_var = NULL;
+
+   read_cf_list(ctx, &fi->body);
+   read_fixup_phis(ctx);
+
+   fi->valid_metadata = 0;
+
+   return fi;
+}
+
+static void
+write_function(write_ctx *ctx, const nir_function *fxn)
+{
+   blob_write_uint32(ctx->blob, !!(fxn->name));
+   if (fxn->name)
+      blob_write_string(ctx->blob, fxn->name);
+
+   write_add_object(ctx, fxn);
+
+   blob_write_uint32(ctx->blob, fxn->num_params);
+   for (unsigned i = 0; i < fxn->num_params; i++) {
+      blob_write_uint32(ctx->blob, fxn->params[i].param_type);
+      encode_type_to_blob(ctx->blob, fxn->params[i].type);
+   }
+
+   encode_type_to_blob(ctx->blob, fxn->return_type);
+
+   /* At first glance, it looks like we should write the function_impl here.
+    * However, call instructions need to be able to reference at least the
+    * function and those will get processed as we write the function_impls.
+    * We stop here and write function_impls as a second pass.
+    */
+}
+
+static void
+read_function(read_ctx *ctx)
+{
+   bool has_name = blob_read_uint32(ctx->blob);
+   char *name = has_name ? blob_read_string(ctx->blob) : NULL;
+
+   nir_function *fxn = nir_function_create(ctx->nir, name);
+
+   read_add_object(ctx, fxn);
+
+   fxn->num_params = blob_read_uint32(ctx->blob);
+   for (unsigned i = 0; i < fxn->num_params; i++) {
+      fxn->params[i].param_type = blob_read_uint32(ctx->blob);
+      fxn->params[i].type = decode_type_from_blob(ctx->blob);
+   }
+
+   fxn->return_type = decode_type_from_blob(ctx->blob);
+}
+
+void
+nir_serialize(struct blob *blob, const nir_shader *nir)
+{
+   write_ctx ctx;
+   ctx.remap_table = _mesa_hash_table_create(NULL, _mesa_hash_pointer,
+                                             _mesa_key_pointer_equal);
+   ctx.next_idx = 0;
+   ctx.blob = blob;
+   ctx.nir = nir;
+   util_dynarray_init(&ctx.phi_fixups, NULL);
+
+   size_t idx_size_offset = blob_reserve_intptr(blob);
+
+   struct shader_info info = nir->info;
+   uint32_t strings = 0;
+   if (info.name)
+      strings |= 0x1;
+   if (info.label)
+      strings |= 0x2;
+   blob_write_uint32(blob, strings);
+   if (info.name)
+      blob_write_string(blob, info.name);
+   if (info.label)
+      blob_write_string(blob, info.label);
+   info.name = info.label = NULL;
+   blob_write_bytes(blob, (uint8_t *) &info, sizeof(info));
+
+   write_var_list(&ctx, &nir->uniforms);
+   write_var_list(&ctx, &nir->inputs);
+   write_var_list(&ctx, &nir->outputs);
+   write_var_list(&ctx, &nir->shared);
+   write_var_list(&ctx, &nir->globals);
+   write_var_list(&ctx, &nir->system_values);
+
+   write_reg_list(&ctx, &nir->registers);
+   blob_write_uint32(blob, nir->reg_alloc);
+   blob_write_uint32(blob, nir->num_inputs);
+   blob_write_uint32(blob, nir->num_uniforms);
+   blob_write_uint32(blob, nir->num_outputs);
+   blob_write_uint32(blob, nir->num_shared);
+
+   blob_write_uint32(blob, exec_list_length(&nir->functions));
+   nir_foreach_function(fxn, nir) {
+      write_function(&ctx, fxn);
+   }
+
+   nir_foreach_function(fxn, nir) {
+      write_function_impl(&ctx, fxn->impl);
+   }
+
+   *(uintptr_t *)(blob->data + idx_size_offset) = ctx.next_idx;
+
+   _mesa_hash_table_destroy(ctx.remap_table, NULL);
+   util_dynarray_fini(&ctx.phi_fixups);
+}
+
+nir_shader *
+nir_deserialize(void *mem_ctx,
+                const struct nir_shader_compiler_options *options,
+                struct blob_reader *blob)
+{
+   read_ctx ctx;
+   ctx.blob = blob;
+   list_inithead(&ctx.phi_srcs);
+   ctx.idx_table_len = blob_read_intptr(blob);
+   ctx.idx_table = calloc(ctx.idx_table_len, sizeof(uintptr_t));
+   ctx.next_idx = 0;
+
+   uint32_t strings = blob_read_uint32(blob);
+   char *name = (strings & 0x1) ? blob_read_string(blob) : NULL;
+   char *label = (strings & 0x2) ? blob_read_string(blob) : NULL;
+
+   struct shader_info info;
+   blob_copy_bytes(blob, (uint8_t *) &info, sizeof(info));
+
+   ctx.nir = nir_shader_create(mem_ctx, info.stage, options, NULL);
+
+   info.name = name ? ralloc_strdup(ctx.nir, name) : NULL;
+   info.label = label ? ralloc_strdup(ctx.nir, label) : NULL;
+
+   ctx.nir->info = info;
+
+   read_var_list(&ctx, &ctx.nir->uniforms);
+   read_var_list(&ctx, &ctx.nir->inputs);
+   read_var_list(&ctx, &ctx.nir->outputs);
+   read_var_list(&ctx, &ctx.nir->shared);
+   read_var_list(&ctx, &ctx.nir->globals);
+   read_var_list(&ctx, &ctx.nir->system_values);
+
+   read_reg_list(&ctx, &ctx.nir->registers);
+   ctx.nir->reg_alloc = blob_read_uint32(blob);
+   ctx.nir->num_inputs = blob_read_uint32(blob);
+   ctx.nir->num_uniforms = blob_read_uint32(blob);
+   ctx.nir->num_outputs = blob_read_uint32(blob);
+   ctx.nir->num_shared = blob_read_uint32(blob);
+
+   unsigned num_functions = blob_read_uint32(blob);
+   for (unsigned i = 0; i < num_functions; i++)
+      read_function(&ctx);
+
+   nir_foreach_function(fxn, ctx.nir)
+      fxn->impl = read_function_impl(&ctx, fxn);
+
+   free(ctx.idx_table);
+
+   return ctx.nir;
+}
diff --git a/src/compiler/nir/nir_serialize.h b/src/compiler/nir/nir_serialize.h
new file mode 100644 (file)
index 0000000..f77d8e3
--- /dev/null
@@ -0,0 +1,43 @@
+/*
+ * Copyright © 2017 Connor Abbott
+ *
+ * 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.
+ */
+
+#ifndef _NIR_SERIALIZE_H
+#define _NIR_SERIALIZE_H
+
+#include "nir.h"
+#include "compiler/blob.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void nir_serialize(struct blob *blob, const nir_shader *nir);
+nir_shader *nir_deserialize(void *mem_ctx,
+                            const struct nir_shader_compiler_options *options,
+                            struct blob_reader *blob);
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#endif