nir: Add the start of a SPIR-V to NIR translator
authorJason Ekstrand <jason.ekstrand@intel.com>
Wed, 29 Apr 2015 00:43:16 +0000 (17:43 -0700)
committerJason Ekstrand <jason.ekstrand@intel.com>
Mon, 31 Aug 2015 23:58:20 +0000 (16:58 -0700)
At the moment, it can handle the very basics of strings and can ignore
debug instructions.  It also has basic support for decorations.

src/glsl/Makefile.sources
src/glsl/nir/nir_spirv.h [new file with mode: 0644]
src/glsl/nir/spirv_to_nir.c [new file with mode: 0644]

index 05a12bc0721e3a0accfc45cea991477afb325e62..c6a89362988ff9c0c0f0a5346d6dbe7836cd451c 100644 (file)
@@ -64,6 +64,7 @@ NIR_FILES = \
        nir/nir_remove_dead_variables.c \
        nir/nir_search.c \
        nir/nir_search.h \
+       nir/nir_spirv.h \
        nir/nir_split_var_copies.c \
        nir/nir_sweep.c \
        nir/nir_to_ssa.c \
@@ -73,6 +74,7 @@ NIR_FILES = \
        nir/nir_worklist.c \
        nir/nir_worklist.h \
        nir/nir_types.cpp \
+       nir/spirv_to_nir.c \
        $(NIR_GENERATED_FILES)
 
 # libglsl
diff --git a/src/glsl/nir/nir_spirv.h b/src/glsl/nir/nir_spirv.h
new file mode 100644 (file)
index 0000000..5f4140d
--- /dev/null
@@ -0,0 +1,39 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * 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.
+ *
+ * Authors:
+ *    Jason Ekstrand (jason@jlekstrand.net)
+ *
+ */
+
+#pragma once
+
+#ifndef _NIR_SPIRV_H_
+#define _NIR_SPIRV_H_
+
+#include "nir.h"
+
+nir_shader *spirv_to_nir(const uint32_t *words, size_t word_count,
+                         gl_shader_stage stage,
+                         const nir_shader_compiler_options *options);
+
+#endif /* _NIR_SPIRV_H_ */
diff --git a/src/glsl/nir/spirv_to_nir.c b/src/glsl/nir/spirv_to_nir.c
new file mode 100644 (file)
index 0000000..3a49ad9
--- /dev/null
@@ -0,0 +1,454 @@
+/*
+ * Copyright © 2015 Intel Corporation
+ *
+ * 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.
+ *
+ * Authors:
+ *    Jason Ekstrand (jason@jlekstrand.net)
+ *
+ */
+
+#include "nir_spirv.h"
+#include "spirv.h"
+
+struct vtn_decoration;
+
+enum vtn_value_type {
+   vtn_value_type_invalid = 0,
+   vtn_value_type_undef,
+   vtn_value_type_string,
+   vtn_value_type_decoration_group,
+   vtn_value_type_ssa,
+   vtn_value_type_deref,
+};
+
+struct vtn_value {
+   enum vtn_value_type value_type;
+   const char *name;
+   struct vtn_decoration *decoration;
+   union {
+      void *ptr;
+      char *str;
+      nir_ssa_def *ssa;
+      nir_deref_var *deref;
+   };
+};
+
+struct vtn_decoration {
+   struct vtn_decoration *next;
+   const uint32_t *literals;
+   struct vtn_value *group;
+   SpvDecoration decoration;
+};
+
+struct vtn_builder {
+   nir_shader *shader;
+   nir_function_impl *impl;
+
+   unsigned value_id_bound;
+   struct vtn_value *values;
+};
+
+static void
+vtn_push_value(struct vtn_builder *b, uint32_t value_id,
+               enum vtn_value_type value_type, void *ptr)
+{
+   assert(value_id < b->value_id_bound);
+   assert(b->values[value_id].value_type == vtn_value_type_invalid);
+
+   b->values[value_id].value_type = value_type;
+   b->values[value_id].ptr = ptr;
+}
+
+static void
+vtn_push_token(struct vtn_builder *b, uint32_t value_id,
+               enum vtn_value_type value_type)
+{
+   vtn_push_value(b, value_id, value_type, NULL);
+}
+
+static char *
+vtn_string_literal(struct vtn_builder *b, const uint32_t *words,
+                   unsigned word_count)
+{
+   return ralloc_strndup(b, (char *)words, (word_count - 2) * sizeof(*words));
+}
+
+typedef void (*decoration_foreach_cb)(struct vtn_builder *,
+                                      struct vtn_value *,
+                                      const struct vtn_decoration *,
+                                      void *);
+
+static void
+_foreach_decoration_helper(struct vtn_builder *b,
+                           struct vtn_value *base_value,
+                           struct vtn_value *value,
+                           decoration_foreach_cb cb, void *data)
+{
+   for (struct vtn_decoration *dec = value->decoration; dec; dec = dec->next) {
+      if (dec->group) {
+         assert(dec->group->value_type == vtn_value_type_decoration_group);
+         _foreach_decoration_helper(b, base_value, dec->group, cb, data);
+      } else {
+         cb(b, base_value, dec, data);
+      }
+   }
+}
+
+/** Iterates (recursively if needed) over all of the decorations on a value
+ *
+ * This function iterates over all of the decorations applied to a given
+ * value.  If it encounters a decoration group, it recurses into the group
+ * and iterates over all of those decorations as well.
+ */
+static void
+vtn_foreach_decoration(struct vtn_builder *b, struct vtn_value *value,
+                       decoration_foreach_cb cb, void *data)
+{
+   _foreach_decoration_helper(b, value, value, cb, data);
+}
+
+static void
+vtn_handle_decoration(struct vtn_builder *b, SpvOp opcode,
+                      const uint32_t *w, unsigned count)
+{
+   switch (opcode) {
+   case SpvOpDecorationGroup:
+      vtn_push_token(b, w[1], vtn_value_type_undef);
+      break;
+
+   case SpvOpDecorate: {
+      struct vtn_value *val = &b->values[w[1]];
+
+      struct vtn_decoration *dec = rzalloc(b, struct vtn_decoration);
+      dec->decoration = w[2];
+      dec->literals = &w[3];
+
+      /* Link into the list */
+      dec->next = val->decoration;
+      val->decoration = dec;
+      break;
+   }
+
+   case SpvOpGroupDecorate: {
+      struct vtn_value *group = &b->values[w[1]];
+      assert(group->value_type == vtn_value_type_decoration_group);
+
+      for (unsigned i = 2; i < count; i++) {
+         struct vtn_value *val = &b->values[w[i]];
+         struct vtn_decoration *dec = rzalloc(b, struct vtn_decoration);
+         dec->group = group;
+
+         /* Link into the list */
+         dec->next = val->decoration;
+         val->decoration = dec;
+      }
+      break;
+   }
+
+   case SpvOpGroupMemberDecorate:
+      assert(!"Bad instruction.  Khronos Bug #13513");
+      break;
+
+   default:
+      unreachable("Unhandled opcode");
+   }
+}
+
+static void
+vtn_handle_type(struct vtn_builder *b, SpvOp opcode,
+                const uint32_t *w, unsigned count)
+{
+   unreachable("Unhandled opcode");
+}
+
+static void
+vtn_handle_constant(struct vtn_builder *b, SpvOp opcode,
+                    const uint32_t *w, unsigned count)
+{
+   unreachable("Unhandled opcode");
+}
+
+static void
+vtn_handle_variables(struct vtn_builder *b, SpvOp opcode,
+                     const uint32_t *w, unsigned count)
+{
+   unreachable("Unhandled opcode");
+}
+
+static void
+vtn_handle_texture(struct vtn_builder *b, SpvOp opcode,
+                   const uint32_t *w, unsigned count)
+{
+   unreachable("Unhandled opcode");
+}
+
+static void
+vtn_handle_alu(struct vtn_builder *b, SpvOp opcode,
+               const uint32_t *w, unsigned count)
+{
+   unreachable("Unhandled opcode");
+}
+
+static void
+vtn_handle_instruction(struct vtn_builder *b, SpvOp opcode,
+                       const uint32_t *w, unsigned count)
+{
+   switch (opcode) {
+   case SpvOpSource:
+   case SpvOpSourceExtension:
+   case SpvOpMemberName:
+   case SpvOpLine:
+      /* Unhandled, but these are for debug so that's ok. */
+      break;
+
+   case SpvOpName:
+      b->values[w[1]].name = vtn_string_literal(b, &w[2], count - 2);
+      break;
+
+   case SpvOpString:
+      vtn_push_value(b, w[1], vtn_value_type_string,
+                     vtn_string_literal(b, &w[2], count - 2));
+      break;
+
+   case SpvOpUndef:
+      vtn_push_token(b, w[2], vtn_value_type_undef);
+      break;
+
+   case SpvOpTypeVoid:
+   case SpvOpTypeBool:
+   case SpvOpTypeInt:
+   case SpvOpTypeFloat:
+   case SpvOpTypeVector:
+   case SpvOpTypeMatrix:
+   case SpvOpTypeSampler:
+   case SpvOpTypeArray:
+   case SpvOpTypeRuntimeArray:
+   case SpvOpTypeStruct:
+   case SpvOpTypeOpaque:
+   case SpvOpTypePointer:
+   case SpvOpTypeFunction:
+   case SpvOpTypeEvent:
+   case SpvOpTypeDeviceEvent:
+   case SpvOpTypeReserveId:
+   case SpvOpTypeQueue:
+   case SpvOpTypePipe:
+      vtn_handle_type(b, opcode, w, count);
+      break;
+
+   case SpvOpConstantTrue:
+   case SpvOpConstantFalse:
+   case SpvOpConstant:
+   case SpvOpConstantComposite:
+   case SpvOpConstantSampler:
+   case SpvOpConstantNullPointer:
+   case SpvOpConstantNullObject:
+   case SpvOpSpecConstantTrue:
+   case SpvOpSpecConstantFalse:
+   case SpvOpSpecConstant:
+   case SpvOpSpecConstantComposite:
+      vtn_handle_constant(b, opcode, w, count);
+      break;
+
+   case SpvOpVariable:
+   case SpvOpVariableArray:
+   case SpvOpLoad:
+   case SpvOpStore:
+   case SpvOpCopyMemory:
+   case SpvOpCopyMemorySized:
+   case SpvOpAccessChain:
+   case SpvOpInBoundsAccessChain:
+   case SpvOpArrayLength:
+   case SpvOpImagePointer:
+      vtn_handle_variables(b, opcode, w, count);
+      break;
+
+   case SpvOpDecorationGroup:
+   case SpvOpDecorate:
+   case SpvOpMemberDecorate:
+   case SpvOpGroupDecorate:
+   case SpvOpGroupMemberDecorate:
+      vtn_handle_decoration(b, opcode, w, count);
+      break;
+
+   case SpvOpTextureSample:
+   case SpvOpTextureSampleDref:
+   case SpvOpTextureSampleLod:
+   case SpvOpTextureSampleProj:
+   case SpvOpTextureSampleGrad:
+   case SpvOpTextureSampleOffset:
+   case SpvOpTextureSampleProjLod:
+   case SpvOpTextureSampleProjGrad:
+   case SpvOpTextureSampleLodOffset:
+   case SpvOpTextureSampleProjOffset:
+   case SpvOpTextureSampleGradOffset:
+   case SpvOpTextureSampleProjLodOffset:
+   case SpvOpTextureSampleProjGradOffset:
+   case SpvOpTextureFetchTexelLod:
+   case SpvOpTextureFetchTexelOffset:
+   case SpvOpTextureFetchSample:
+   case SpvOpTextureFetchTexel:
+   case SpvOpTextureGather:
+   case SpvOpTextureGatherOffset:
+   case SpvOpTextureGatherOffsets:
+   case SpvOpTextureQuerySizeLod:
+   case SpvOpTextureQuerySize:
+   case SpvOpTextureQueryLod:
+   case SpvOpTextureQueryLevels:
+   case SpvOpTextureQuerySamples:
+      vtn_handle_texture(b, opcode, w, count);
+      break;
+
+   case SpvOpSNegate:
+   case SpvOpFNegate:
+   case SpvOpNot:
+   case SpvOpAny:
+   case SpvOpAll:
+   case SpvOpConvertFToU:
+   case SpvOpConvertFToS:
+   case SpvOpConvertSToF:
+   case SpvOpConvertUToF:
+   case SpvOpUConvert:
+   case SpvOpSConvert:
+   case SpvOpFConvert:
+   case SpvOpConvertPtrToU:
+   case SpvOpConvertUToPtr:
+   case SpvOpPtrCastToGeneric:
+   case SpvOpGenericCastToPtr:
+   case SpvOpBitcast:
+   case SpvOpTranspose:
+   case SpvOpIsNan:
+   case SpvOpIsInf:
+   case SpvOpIsFinite:
+   case SpvOpIsNormal:
+   case SpvOpSignBitSet:
+   case SpvOpLessOrGreater:
+   case SpvOpOrdered:
+   case SpvOpUnordered:
+   case SpvOpIAdd:
+   case SpvOpFAdd:
+   case SpvOpISub:
+   case SpvOpFSub:
+   case SpvOpIMul:
+   case SpvOpFMul:
+   case SpvOpUDiv:
+   case SpvOpSDiv:
+   case SpvOpFDiv:
+   case SpvOpUMod:
+   case SpvOpSRem:
+   case SpvOpSMod:
+   case SpvOpFRem:
+   case SpvOpFMod:
+   case SpvOpVectorTimesScalar:
+   case SpvOpMatrixTimesScalar:
+   case SpvOpVectorTimesMatrix:
+   case SpvOpMatrixTimesVector:
+   case SpvOpMatrixTimesMatrix:
+   case SpvOpOuterProduct:
+   case SpvOpDot:
+   case SpvOpShiftRightLogical:
+   case SpvOpShiftRightArithmetic:
+   case SpvOpShiftLeftLogical:
+   case SpvOpLogicalOr:
+   case SpvOpLogicalXor:
+   case SpvOpLogicalAnd:
+   case SpvOpBitwiseOr:
+   case SpvOpBitwiseXor:
+   case SpvOpBitwiseAnd:
+   case SpvOpSelect:
+   case SpvOpIEqual:
+   case SpvOpFOrdEqual:
+   case SpvOpFUnordEqual:
+   case SpvOpINotEqual:
+   case SpvOpFOrdNotEqual:
+   case SpvOpFUnordNotEqual:
+   case SpvOpULessThan:
+   case SpvOpSLessThan:
+   case SpvOpFOrdLessThan:
+   case SpvOpFUnordLessThan:
+   case SpvOpUGreaterThan:
+   case SpvOpSGreaterThan:
+   case SpvOpFOrdGreaterThan:
+   case SpvOpFUnordGreaterThan:
+   case SpvOpULessThanEqual:
+   case SpvOpSLessThanEqual:
+   case SpvOpFOrdLessThanEqual:
+   case SpvOpFUnordLessThanEqual:
+   case SpvOpUGreaterThanEqual:
+   case SpvOpSGreaterThanEqual:
+   case SpvOpFOrdGreaterThanEqual:
+   case SpvOpFUnordGreaterThanEqual:
+   case SpvOpDPdx:
+   case SpvOpDPdy:
+   case SpvOpFwidth:
+   case SpvOpDPdxFine:
+   case SpvOpDPdyFine:
+   case SpvOpFwidthFine:
+   case SpvOpDPdxCoarse:
+   case SpvOpDPdyCoarse:
+   case SpvOpFwidthCoarse:
+      vtn_handle_alu(b, opcode, w, count);
+      break;
+
+   default:
+      unreachable("Unhandled opcode");
+   }
+}
+
+nir_shader *
+spirv_to_nir(const uint32_t *words, size_t word_count,
+             gl_shader_stage stage,
+             const nir_shader_compiler_options *options)
+{
+   /* Handle the SPIR-V header (first 4 dwords)  */
+   assert(word_count > 5);
+
+   assert(words[0] == SpvMagicNumber);
+   assert(words[1] == 99);
+   /* words[2] == generator magic */
+   unsigned value_id_bound = words[3];
+   assert(words[4] == 0);
+
+   words+= 5;
+
+   nir_shader *shader = nir_shader_create(NULL, stage, options);
+
+   /* Initialize the stn_builder object */
+   struct vtn_builder *b = rzalloc(NULL, struct vtn_builder);
+   b->shader = shader;
+   b->value_id_bound = value_id_bound;
+   b->values = ralloc_array(b, struct vtn_value, value_id_bound);
+
+   /* Start handling instructions */
+   const uint32_t *word_end = words + word_count;
+   while (words < word_end) {
+      SpvOp opcode = words[0] & SpvOpCodeMask;
+      unsigned count = words[0] >> SpvWordCountShift;
+      assert(words + count <= word_end);
+
+      vtn_handle_instruction(b, opcode, words, count);
+
+      words += count;
+   }
+
+   ralloc_free(b);
+
+   return shader;
+}