nir/serialize: remove up to 3 consecutive equal ALU instruction headers
[mesa.git] / src / compiler / nir / nir_serialize.c
index 8290cd82d2147b693d94043445faca84cd335e4f..85db23beef8e98b185dab67c96772fd7ecf596ae 100644 (file)
@@ -56,6 +56,10 @@ typedef struct {
    const struct glsl_type *last_interface_type;
    struct nir_variable_data last_var_data;
 
+   /* For skipping equal ALU headers (typical after scalarization). */
+   nir_instr_type last_instr_type;
+   uintptr_t last_alu_header_offset;
+
    /* Don't write optional data such as variable names. */
    bool strip;
 } write_ctx;
@@ -99,12 +103,6 @@ write_lookup_object(write_ctx *ctx, const void *obj)
    return (uint32_t)(uintptr_t) entry->data;
 }
 
-static void
-write_object(write_ctx *ctx, const void *obj)
-{
-   blob_write_uint32(ctx->blob, write_lookup_object(ctx, obj));
-}
-
 static void
 read_add_object(read_ctx *ctx, void *obj)
 {
@@ -574,6 +572,21 @@ union packed_dest {
    } reg;
 };
 
+enum intrinsic_const_indices_encoding {
+   /* Use the 6 bits of packed_const_indices to store 1-6 indices.
+    * 1 6-bit index, or 2 3-bit indices, or 3 2-bit indices, or
+    * 4-6 1-bit indices.
+    *
+    * The common case for load_ubo is 0, 0, 0, which is trivially represented.
+    * The common cases for load_interpolated_input also fit here, e.g.: 7, 3
+    */
+   const_indices_6bit_all_combined,
+
+   const_indices_8bit,  /* 8 bits per element */
+   const_indices_16bit, /* 16 bits per element */
+   const_indices_32bit, /* 32 bits per element */
+};
+
 enum load_const_packing {
    /* Constants are not packed and are stored in following dwords. */
    load_const_full,
@@ -602,21 +615,33 @@ union packed_instr {
       unsigned saturate:1;
       unsigned writemask:4;
       unsigned op:9;
-      unsigned _pad:3;
+      unsigned packed_src_ssa_16bit:1;
+      /* Scalarized ALUs always have the same header. */
+      unsigned num_followup_alu_sharing_header:2;
       unsigned dest:8;
    } alu;
    struct {
       unsigned instr_type:4;
       unsigned deref_type:3;
-      unsigned mode:10;
-      unsigned _pad:7;
+      unsigned cast_type_same_as_last:1;
+      unsigned mode:10; /* deref_var redefines this */
+      unsigned packed_src_ssa_16bit:1; /* deref_var redefines this */
+      unsigned _pad:5;  /* deref_var redefines this */
       unsigned dest:8;
    } deref;
+   struct {
+      unsigned instr_type:4;
+      unsigned deref_type:3;
+      unsigned _pad:1;
+      unsigned object_idx:16; /* if 0, the object ID is a separate uint32 */
+      unsigned dest:8;
+   } deref_var;
    struct {
       unsigned instr_type:4;
       unsigned intrinsic:9;
       unsigned num_components:3;
-      unsigned _pad:8;
+      unsigned const_indices_encoding:2;
+      unsigned packed_const_indices:6;
       unsigned dest:8;
    } intrinsic;
    struct {
@@ -653,7 +678,8 @@ union packed_instr {
 
 /* Write "lo24" as low 24 bits in the first uint32. */
 static void
-write_dest(write_ctx *ctx, const nir_dest *dst, union packed_instr header)
+write_dest(write_ctx *ctx, const nir_dest *dst, union packed_instr header,
+           nir_instr_type instr_type)
 {
    STATIC_ASSERT(sizeof(union packed_dest) == 1);
    union packed_dest dest;
@@ -668,9 +694,43 @@ write_dest(write_ctx *ctx, const nir_dest *dst, union packed_instr header)
    } else {
       dest.reg.is_indirect = !!(dst->reg.indirect);
    }
-
    header.any.dest = dest.u8;
-   blob_write_uint32(ctx->blob, header.u32);
+
+   /* Check if the current ALU instruction has the same header as the previous
+    * instruction that is also ALU. If it is, we don't have to write
+    * the current header. This is a typical occurence after scalarization.
+    */
+   if (instr_type == nir_instr_type_alu) {
+      bool equal_header = false;
+
+      if (ctx->last_instr_type == nir_instr_type_alu) {
+         assert(ctx->last_alu_header_offset);
+         union packed_instr *last_header =
+            (union packed_instr *)(ctx->blob->data +
+                                   ctx->last_alu_header_offset);
+
+         /* Clear the field that counts ALUs with equal headers. */
+         union packed_instr clean_header;
+         clean_header.u32 = last_header->u32;
+         clean_header.alu.num_followup_alu_sharing_header = 0;
+
+         /* There can be at most 4 consecutive ALU instructions
+          * sharing the same header.
+          */
+         if (last_header->alu.num_followup_alu_sharing_header < 3 &&
+             header.u32 == clean_header.u32) {
+            last_header->alu.num_followup_alu_sharing_header++;
+            equal_header = true;
+         }
+      }
+
+      if (!equal_header) {
+         ctx->last_alu_header_offset = ctx->blob->size;
+         blob_write_uint32(ctx->blob, header.u32);
+      }
+   } else {
+      blob_write_uint32(ctx->blob, header.u32);
+   }
 
    if (dst->is_ssa) {
       write_add_object(ctx, &dst->ssa);
@@ -708,9 +768,37 @@ read_dest(read_ctx *ctx, nir_dest *dst, nir_instr *instr,
    }
 }
 
+static bool
+are_object_ids_16bit(write_ctx *ctx)
+{
+   /* Check the highest object ID, because they are monotonic. */
+   return ctx->next_idx < (1 << 16);
+}
+
+static bool
+is_alu_src_ssa_16bit(write_ctx *ctx, const nir_alu_instr *alu)
+{
+   unsigned num_srcs = nir_op_infos[alu->op].num_inputs;
+
+   for (unsigned i = 0; i < num_srcs; i++) {
+      if (!alu->src[i].src.is_ssa || alu->src[i].abs || alu->src[i].negate)
+         return false;
+
+      unsigned src_components = nir_ssa_alu_instr_src_components(alu, i);
+
+      for (unsigned chan = 0; chan < src_components; chan++) {
+         if (alu->src[i].swizzle[chan] != chan)
+            return false;
+      }
+   }
+
+   return are_object_ids_16bit(ctx);
+}
+
 static void
 write_alu(write_ctx *ctx, const nir_alu_instr *alu)
 {
+   unsigned num_srcs = nir_op_infos[alu->op].num_inputs;
    /* 9 bits for nir_op */
    STATIC_ASSERT(nir_num_opcodes <= 512);
    union packed_instr header;
@@ -723,27 +811,38 @@ write_alu(write_ctx *ctx, const nir_alu_instr *alu)
    header.alu.saturate = alu->dest.saturate;
    header.alu.writemask = alu->dest.write_mask;
    header.alu.op = alu->op;
+   header.alu.packed_src_ssa_16bit = is_alu_src_ssa_16bit(ctx, alu);
 
-   write_dest(ctx, &alu->dest.dest, header);
+   write_dest(ctx, &alu->dest.dest, header, alu->instr.type);
 
-   for (unsigned i = 0; i < nir_op_infos[alu->op].num_inputs; i++) {
-      union packed_src src;
-      src.u32 = 0;
-
-      src.alu.negate = alu->src[i].negate;
-      src.alu.abs = alu->src[i].abs;
-      src.alu.swizzle_x = alu->src[i].swizzle[0];
-      src.alu.swizzle_y = alu->src[i].swizzle[1];
-      src.alu.swizzle_z = alu->src[i].swizzle[2];
-      src.alu.swizzle_w = alu->src[i].swizzle[3];
-
-      write_src_full(ctx, &alu->src[i].src, src);
+   if (header.alu.packed_src_ssa_16bit) {
+      for (unsigned i = 0; i < num_srcs; i++) {
+         assert(alu->src[i].src.is_ssa);
+         unsigned idx = write_lookup_object(ctx, alu->src[i].src.ssa);
+         assert(idx < (1 << 16));
+         blob_write_uint16(ctx->blob, idx);
+      }
+   } else {
+      for (unsigned i = 0; i < num_srcs; i++) {
+         union packed_src src;
+         src.u32 = 0;
+
+         src.alu.negate = alu->src[i].negate;
+         src.alu.abs = alu->src[i].abs;
+         src.alu.swizzle_x = alu->src[i].swizzle[0];
+         src.alu.swizzle_y = alu->src[i].swizzle[1];
+         src.alu.swizzle_z = alu->src[i].swizzle[2];
+         src.alu.swizzle_w = alu->src[i].swizzle[3];
+
+         write_src_full(ctx, &alu->src[i].src, src);
+      }
    }
 }
 
 static nir_alu_instr *
 read_alu(read_ctx *ctx, union packed_instr header)
 {
+   unsigned num_srcs = nir_op_infos[header.alu.op].num_inputs;
    nir_alu_instr *alu = nir_alu_instr_create(ctx->nir, header.alu.op);
 
    alu->exact = header.alu.exact;
@@ -754,15 +853,30 @@ read_alu(read_ctx *ctx, union packed_instr header)
 
    read_dest(ctx, &alu->dest.dest, &alu->instr, header);
 
-   for (unsigned i = 0; i < nir_op_infos[header.alu.op].num_inputs; i++) {
-      union packed_src src = read_src(ctx, &alu->src[i].src, &alu->instr);
+   if (header.alu.packed_src_ssa_16bit) {
+      for (unsigned i = 0; i < num_srcs; i++) {
+         nir_alu_src *src = &alu->src[i];
+         src->src.is_ssa = true;
+         src->src.ssa = read_lookup_object(ctx, blob_read_uint16(ctx->blob));
+
+         memset(&src->swizzle, 0, sizeof(src->swizzle));
+
+         unsigned src_components = nir_ssa_alu_instr_src_components(alu, i);
 
-      alu->src[i].negate = src.alu.negate;
-      alu->src[i].abs = src.alu.abs;
-      alu->src[i].swizzle[0] = src.alu.swizzle_x;
-      alu->src[i].swizzle[1] = src.alu.swizzle_y;
-      alu->src[i].swizzle[2] = src.alu.swizzle_z;
-      alu->src[i].swizzle[3] = src.alu.swizzle_w;
+         for (unsigned chan = 0; chan < src_components; chan++)
+            src->swizzle[chan] = chan;
+      }
+   } else {
+      for (unsigned i = 0; i < num_srcs; i++) {
+         union packed_src src = read_src(ctx, &alu->src[i].src, &alu->instr);
+
+         alu->src[i].negate = src.alu.negate;
+         alu->src[i].abs = src.alu.abs;
+         alu->src[i].swizzle[0] = src.alu.swizzle_x;
+         alu->src[i].swizzle[1] = src.alu.swizzle_y;
+         alu->src[i].swizzle[2] = src.alu.swizzle_z;
+         alu->src[i].swizzle[3] = src.alu.swizzle_w;
+      }
    }
 
    return alu;
@@ -779,34 +893,63 @@ write_deref(write_ctx *ctx, const nir_deref_instr *deref)
 
    header.deref.instr_type = deref->instr.type;
    header.deref.deref_type = deref->deref_type;
-   header.deref.mode = deref->mode;
 
-   write_dest(ctx, &deref->dest, header);
-   encode_type_to_blob(ctx->blob, deref->type);
+   if (deref->deref_type == nir_deref_type_cast) {
+      header.deref.mode = deref->mode;
+      header.deref.cast_type_same_as_last = deref->type == ctx->last_type;
+   }
 
+   unsigned var_idx = 0;
    if (deref->deref_type == nir_deref_type_var) {
-      write_object(ctx, deref->var);
-      return;
+      var_idx = write_lookup_object(ctx, deref->var);
+      if (var_idx && var_idx < (1 << 16))
+         header.deref_var.object_idx = var_idx;
+   }
+
+   if (deref->deref_type == nir_deref_type_array ||
+       deref->deref_type == nir_deref_type_ptr_as_array) {
+      header.deref.packed_src_ssa_16bit =
+         deref->parent.is_ssa && deref->arr.index.is_ssa &&
+         are_object_ids_16bit(ctx);
    }
 
-   write_src(ctx, &deref->parent);
+   write_dest(ctx, &deref->dest, header, deref->instr.type);
 
    switch (deref->deref_type) {
+   case nir_deref_type_var:
+      if (!header.deref_var.object_idx)
+         blob_write_uint32(ctx->blob, var_idx);
+      break;
+
    case nir_deref_type_struct:
+      write_src(ctx, &deref->parent);
       blob_write_uint32(ctx->blob, deref->strct.index);
       break;
 
    case nir_deref_type_array:
    case nir_deref_type_ptr_as_array:
-      write_src(ctx, &deref->arr.index);
+      if (header.deref.packed_src_ssa_16bit) {
+         blob_write_uint16(ctx->blob,
+                           write_lookup_object(ctx, deref->parent.ssa));
+         blob_write_uint16(ctx->blob,
+                           write_lookup_object(ctx, deref->arr.index.ssa));
+      } else {
+         write_src(ctx, &deref->parent);
+         write_src(ctx, &deref->arr.index);
+      }
       break;
 
    case nir_deref_type_cast:
+      write_src(ctx, &deref->parent);
       blob_write_uint32(ctx->blob, deref->cast.ptr_stride);
+      if (!header.deref.cast_type_same_as_last) {
+         encode_type_to_blob(ctx->blob, deref->type);
+         ctx->last_type = deref->type;
+      }
       break;
 
    case nir_deref_type_array_wildcard:
-      /* Nothing to do */
+      write_src(ctx, &deref->parent);
       break;
 
    default:
@@ -822,38 +965,74 @@ read_deref(read_ctx *ctx, union packed_instr header)
 
    read_dest(ctx, &deref->dest, &deref->instr, header);
 
-   deref->mode = header.deref.mode;
-   deref->type = decode_type_from_blob(ctx->blob);
+   nir_deref_instr *parent;
 
-   if (deref_type == nir_deref_type_var) {
-      deref->var = read_object(ctx);
-      return deref;
-   }
+   switch (deref->deref_type) {
+   case nir_deref_type_var:
+      if (header.deref_var.object_idx)
+         deref->var = read_lookup_object(ctx, header.deref_var.object_idx);
+      else
+         deref->var = read_object(ctx);
 
-   read_src(ctx, &deref->parent, &deref->instr);
+      deref->type = deref->var->type;
+      break;
 
-   switch (deref->deref_type) {
    case nir_deref_type_struct:
+      read_src(ctx, &deref->parent, &deref->instr);
+      parent = nir_src_as_deref(deref->parent);
       deref->strct.index = blob_read_uint32(ctx->blob);
+      deref->type = glsl_get_struct_field(parent->type, deref->strct.index);
       break;
 
    case nir_deref_type_array:
    case nir_deref_type_ptr_as_array:
-      read_src(ctx, &deref->arr.index, &deref->instr);
+      if (header.deref.packed_src_ssa_16bit) {
+         deref->parent.is_ssa = true;
+         deref->parent.ssa = read_lookup_object(ctx, blob_read_uint16(ctx->blob));
+         deref->arr.index.is_ssa = true;
+         deref->arr.index.ssa = read_lookup_object(ctx, blob_read_uint16(ctx->blob));
+      } else {
+         read_src(ctx, &deref->parent, &deref->instr);
+         read_src(ctx, &deref->arr.index, &deref->instr);
+      }
+
+      parent = nir_src_as_deref(deref->parent);
+      if (deref->deref_type == nir_deref_type_array)
+         deref->type = glsl_get_array_element(parent->type);
+      else
+         deref->type = parent->type;
       break;
 
    case nir_deref_type_cast:
+      read_src(ctx, &deref->parent, &deref->instr);
       deref->cast.ptr_stride = blob_read_uint32(ctx->blob);
+      if (header.deref.cast_type_same_as_last) {
+         deref->type = ctx->last_type;
+      } else {
+         deref->type = decode_type_from_blob(ctx->blob);
+         ctx->last_type = deref->type;
+      }
       break;
 
    case nir_deref_type_array_wildcard:
-      /* Nothing to do */
+      read_src(ctx, &deref->parent, &deref->instr);
+      parent = nir_src_as_deref(deref->parent);
+      deref->type = glsl_get_array_element(parent->type);
       break;
 
    default:
       unreachable("Invalid deref type");
    }
 
+   if (deref_type == nir_deref_type_var) {
+      deref->mode = deref->var->data.mode;
+   } else if (deref->deref_type == nir_deref_type_cast) {
+      deref->mode = header.deref.mode;
+   } else {
+      assert(deref->parent.is_ssa);
+      deref->mode = nir_instr_as_deref(deref->parent.ssa->parent_instr)->mode;
+   }
+
    return deref;
 }
 
@@ -874,16 +1053,55 @@ write_intrinsic(write_ctx *ctx, const nir_intrinsic_instr *intrin)
    header.intrinsic.num_components =
       encode_num_components_in_3bits(intrin->num_components);
 
+   /* Analyze constant indices to decide how to encode them. */
+   if (num_indices) {
+      unsigned max_bits = 0;
+      for (unsigned i = 0; i < num_indices; i++) {
+         unsigned max = util_last_bit(intrin->const_index[i]);
+         max_bits = MAX2(max_bits, max);
+      }
+
+      if (max_bits * num_indices <= 6) {
+         header.intrinsic.const_indices_encoding = const_indices_6bit_all_combined;
+
+         /* Pack all const indices into 6 bits. */
+         unsigned bit_size = 6 / num_indices;
+         for (unsigned i = 0; i < num_indices; i++) {
+            header.intrinsic.packed_const_indices |=
+               intrin->const_index[i] << (i * bit_size);
+         }
+      } else if (max_bits <= 8)
+         header.intrinsic.const_indices_encoding = const_indices_8bit;
+      else if (max_bits <= 16)
+         header.intrinsic.const_indices_encoding = const_indices_16bit;
+      else
+         header.intrinsic.const_indices_encoding = const_indices_32bit;
+   }
+
    if (nir_intrinsic_infos[intrin->intrinsic].has_dest)
-      write_dest(ctx, &intrin->dest, header);
+      write_dest(ctx, &intrin->dest, header, intrin->instr.type);
    else
       blob_write_uint32(ctx->blob, header.u32);
 
    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]);
+   if (num_indices) {
+      switch (header.intrinsic.const_indices_encoding) {
+      case const_indices_8bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            blob_write_uint8(ctx->blob, intrin->const_index[i]);
+         break;
+      case const_indices_16bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            blob_write_uint16(ctx->blob, intrin->const_index[i]);
+         break;
+      case const_indices_32bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            blob_write_uint32(ctx->blob, intrin->const_index[i]);
+         break;
+      }
+   }
 }
 
 static nir_intrinsic_instr *
@@ -904,8 +1122,32 @@ read_intrinsic(read_ctx *ctx, union packed_instr header)
    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);
+   if (num_indices) {
+      switch (header.intrinsic.const_indices_encoding) {
+      case const_indices_6bit_all_combined: {
+         unsigned bit_size = 6 / num_indices;
+         unsigned bit_mask = u_bit_consecutive(0, bit_size);
+         for (unsigned i = 0; i < num_indices; i++) {
+            intrin->const_index[i] =
+               (header.intrinsic.packed_const_indices >> (i * bit_size)) &
+               bit_mask;
+         }
+         break;
+      }
+      case const_indices_8bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            intrin->const_index[i] = blob_read_uint8(ctx->blob);
+         break;
+      case const_indices_16bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            intrin->const_index[i] = blob_read_uint16(ctx->blob);
+         break;
+      case const_indices_32bit:
+         for (unsigned i = 0; i < num_indices; i++)
+            intrin->const_index[i] = blob_read_uint32(ctx->blob);
+         break;
+      }
+   }
 
    return intrin;
 }
@@ -1122,7 +1364,7 @@ write_tex(write_ctx *ctx, const nir_tex_instr *tex)
    header.tex.op = tex->op;
    header.tex.texture_array_size = tex->texture_array_size;
 
-   write_dest(ctx, &tex->dest, header);
+   write_dest(ctx, &tex->dest, header, tex->instr.type);
 
    blob_write_uint32(ctx->blob, tex->texture_index);
    blob_write_uint32(ctx->blob, tex->sampler_index);
@@ -1195,7 +1437,7 @@ write_phi(write_ctx *ctx, const nir_phi_instr *phi)
     * and then store enough information so that a later fixup pass can fill
     * them in correctly.
     */
-   write_dest(ctx, &phi->dest, header);
+   write_dest(ctx, &phi->dest, header, phi->instr.type);
 
    nir_foreach_phi_src(src, phi) {
       assert(src->src.is_ssa);
@@ -1363,7 +1605,8 @@ write_instr(write_ctx *ctx, const nir_instr *instr)
    }
 }
 
-static void
+/* Return the number of instructions read. */
+static unsigned
 read_instr(read_ctx *ctx, nir_block *block)
 {
    STATIC_ASSERT(sizeof(union packed_instr) == 4);
@@ -1373,8 +1616,9 @@ read_instr(read_ctx *ctx, nir_block *block)
 
    switch (header.any.instr_type) {
    case nir_instr_type_alu:
-      instr = &read_alu(ctx, header)->instr;
-      break;
+      for (unsigned i = 0; i <= header.alu.num_followup_alu_sharing_header; i++)
+         nir_instr_insert_after_block(block, &read_alu(ctx, header)->instr);
+      return header.alu.num_followup_alu_sharing_header + 1;
    case nir_instr_type_deref:
       instr = &read_deref(ctx, header)->instr;
       break;
@@ -1397,7 +1641,7 @@ read_instr(read_ctx *ctx, nir_block *block)
        * are read so that we can set their sources up.
        */
       read_phi(ctx, block, header);
-      return;
+      return 1;
    case nir_instr_type_jump:
       instr = &read_jump(ctx, header)->instr;
       break;
@@ -1411,6 +1655,7 @@ read_instr(read_ctx *ctx, nir_block *block)
    }
 
    nir_instr_insert_after_block(block, instr);
+   return 1;
 }
 
 static void
@@ -1418,8 +1663,14 @@ 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)
+
+   ctx->last_instr_type = ~0;
+   ctx->last_alu_header_offset = 0;
+
+   nir_foreach_instr(instr, block) {
       write_instr(ctx, instr);
+      ctx->last_instr_type = instr->type;
+   }
 }
 
 static void
@@ -1434,8 +1685,8 @@ read_block(read_ctx *ctx, struct exec_list *cf_list)
 
    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);
+   for (unsigned i = 0; i < num_instrs;) {
+      i += read_instr(ctx, block);
    }
 }