spirv: fix OpBitcast when the src and dst bitsize are different (v3)
authorConnor Abbott <cwabbott0@gmail.com>
Wed, 7 Jun 2017 21:12:05 +0000 (14:12 -0700)
committerConnor Abbott <cwabbott0@gmail.com>
Mon, 3 Jul 2017 18:58:50 +0000 (11:58 -0700)
Before, we were just implementing it with a move, which is incorrect
when the source and destination have different bitsizes. To implement
it properly, we need to use the 64-bit pack/unpack opcodes. Since
glslang uses OpBitcast to implement packInt2x32 and unpackInt2x32, this
should fix them on anv (and radv once we enable the int64 capability).

v2: make supporting non-32/64 bit easier (Jason)
v3: add another assert (Jason)

Fixes: b3135c3c ("anv: Advertise shaderInt64 on Broadwell and above")
Signed-off-by: Connor Abbott <cwabbott0@gmail.com>
Reviewed-by: Jason Ekstrand <jason@jlekstrand.net>
src/compiler/spirv/vtn_alu.c

index 9e4beedf926676a5ef09cee59d19b668ebd1052b..ecf9cbc34d678ddaa2710298f688d6cafeffe44f 100644 (file)
@@ -210,6 +210,68 @@ vtn_handle_matrix_alu(struct vtn_builder *b, SpvOp opcode,
    }
 }
 
+static void
+vtn_handle_bitcast(struct vtn_builder *b, struct vtn_ssa_value *dest,
+                   struct nir_ssa_def *src)
+{
+   if (glsl_get_vector_elements(dest->type) == src->num_components) {
+      /* From the definition of OpBitcast in the SPIR-V 1.2 spec:
+       *
+       * "If Result Type has the same number of components as Operand, they
+       * must also have the same component width, and results are computed per
+       * component."
+       */
+      dest->def = nir_imov(&b->nb, src);
+      return;
+   }
+
+   /* From the definition of OpBitcast in the SPIR-V 1.2 spec:
+    *
+    * "If Result Type has a different number of components than Operand, the
+    * total number of bits in Result Type must equal the total number of bits
+    * in Operand. Let L be the type, either Result Type or Operand’s type, that
+    * has the larger number of components. Let S be the other type, with the
+    * smaller number of components. The number of components in L must be an
+    * integer multiple of the number of components in S. The first component
+    * (that is, the only or lowest-numbered component) of S maps to the first
+    * components of L, and so on, up to the last component of S mapping to the
+    * last components of L. Within this mapping, any single component of S
+    * (mapping to multiple components of L) maps its lower-ordered bits to the
+    * lower-numbered components of L."
+    */
+   unsigned src_bit_size = src->bit_size;
+   unsigned dest_bit_size = glsl_get_bit_size(dest->type);
+   unsigned src_components = src->num_components;
+   unsigned dest_components = glsl_get_vector_elements(dest->type);
+   assert(src_bit_size * src_components == dest_bit_size * dest_components);
+
+   nir_ssa_def *dest_chan[4];
+   if (src_bit_size > dest_bit_size) {
+      assert(src_bit_size % dest_bit_size == 0);
+      unsigned divisor = src_bit_size / dest_bit_size;
+      for (unsigned comp = 0; comp < src_components; comp++) {
+         assert(src_bit_size == 64);
+         assert(dest_bit_size == 32);
+         nir_ssa_def *split =
+            nir_unpack_64_2x32(&b->nb, nir_channel(&b->nb, src, comp));
+         for (unsigned i = 0; i < divisor; i++)
+            dest_chan[divisor * comp + i] = nir_channel(&b->nb, split, i);
+      }
+   } else {
+      assert(dest_bit_size % src_bit_size == 0);
+      unsigned divisor = dest_bit_size / src_bit_size;
+      for (unsigned comp = 0; comp < dest_components; comp++) {
+         unsigned channels = ((1 << divisor) - 1) << (comp * divisor);
+         nir_ssa_def *src_chan =
+            nir_channels(&b->nb, src, channels);
+         assert(dest_bit_size == 64);
+         assert(src_bit_size == 32);
+         dest_chan[comp] = nir_pack_64_2x32(&b->nb, src_chan);
+      }
+   }
+   dest->def = nir_vec(&b->nb, dest_chan, dest_components);
+}
+
 nir_op
 vtn_nir_alu_op_for_spirv_opcode(SpvOp opcode, bool *swap,
                                 nir_alu_type src, nir_alu_type dst)
@@ -285,7 +347,6 @@ vtn_nir_alu_op_for_spirv_opcode(SpvOp opcode, bool *swap,
    case SpvOpFUnordGreaterThanEqual:               return nir_op_fge;
 
    /* Conversions: */
-   case SpvOpBitcast:               return nir_op_imov;
    case SpvOpQuantizeToF16:         return nir_op_fquantize2f16;
    case SpvOpUConvert:
    case SpvOpConvertFToU:
@@ -503,6 +564,10 @@ vtn_handle_alu(struct vtn_builder *b, SpvOp opcode,
       break;
    }
 
+   case SpvOpBitcast:
+      vtn_handle_bitcast(b, val->ssa, src[0]);
+      break;
+
    default: {
       bool swap;
       nir_alu_type src_alu_type = nir_get_nir_type_for_glsl_type(vtn_src[0]->type);