[Mesa-dev] [PATCH 12/12] spirv: Generate SPIR-V builder infrastructure

Ian Romanick idr at freedesktop.org
Tue Nov 21 01:24:10 UTC 2017


From: Ian Romanick <ian.d.romanick at intel.com>

v2: Don't try to automatically set SpvCapabilityGeometry or
SpvCapabilityTessellation.

v3: Move some changes from a later patch back into this patch.  They
were only in the later patch because of rebase failure.  Make version a
parameter to emit_SpvHeader.  Many Python style changes based on
feedback from Dylan.

Signed-off-by: Ian Romanick <ian.d.romanick at intel.com>
---
 src/compiler/Makefile.sources         |   2 +
 src/compiler/Makefile.spirv.am        |   8 +-
 src/compiler/glsl/meson.build         |   2 +-
 src/compiler/spirv/.gitignore         |   1 +
 src/compiler/spirv/meson.build        |   7 +
 src/compiler/spirv/spirv_builder.h    | 245 ++++++++++++++
 src/compiler/spirv/spirv_builder_h.py | 585 ++++++++++++++++++++++++++++++++++
 7 files changed, 847 insertions(+), 3 deletions(-)
 create mode 100644 src/compiler/spirv/spirv_builder.h
 create mode 100644 src/compiler/spirv/spirv_builder_h.py

diff --git a/src/compiler/Makefile.sources b/src/compiler/Makefile.sources
index 1d67cba..aea067f 100644
--- a/src/compiler/Makefile.sources
+++ b/src/compiler/Makefile.sources
@@ -287,6 +287,7 @@ NIR_FILES = \
 	nir/nir_worklist.h
 
 SPIRV_GENERATED_FILES = \
+	spirv/spirv_builder_functions.h \
 	spirv/spirv_capabilities.cpp \
 	spirv/spirv_capabilities.h \
 	spirv/spirv_info.c
@@ -295,6 +296,7 @@ SPIRV_FILES = \
 	spirv/GLSL.std.450.h \
 	spirv/nir_spirv.h \
 	spirv/spirv.h \
+	spirv/spirv_builder.h \
 	spirv/spirv_info.h \
 	spirv/spirv_to_nir.c \
 	spirv/vtn_alu.c \
diff --git a/src/compiler/Makefile.spirv.am b/src/compiler/Makefile.spirv.am
index 4bc684a..dc3c01c 100644
--- a/src/compiler/Makefile.spirv.am
+++ b/src/compiler/Makefile.spirv.am
@@ -20,13 +20,17 @@
 # FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
 # IN THE SOFTWARE.
 
+spirv/spirv_builder_functions.h: spirv/spirv_builder_h.py spirv/spirv.core.grammar.json
+	$(MKDIR_GEN)
+	$(PYTHON_GEN) $(srcdir)/spirv/spirv_builder_h.py $(srcdir)/spirv/spirv.core.grammar.json $@ || ($(RM) $@; false)
+
 spirv/spirv_capabilities.cpp: spirv/spirv_capabilities_h.py spirv/spirv.core.grammar.json
 	$(MKDIR_GEN)
-	$(PYTHON_GEN) $(srcdir)/spirv/spirv_capabilities_h.py $(srcdir)/spirv/spirv.core.grammar.json $@ || ($(RM) $@; false)
+	$(PYTHON_GEN) $(srcdir)/spirv/spirv_capabilities_h.py --gen-cpp $@ $(srcdir)/spirv/spirv.core.grammar.json || ($(RM) $@; false)
 
 spirv/spirv_capabilities.h: spirv/spirv_capabilities_h.py spirv/spirv.core.grammar.json
 	$(MKDIR_GEN)
-	$(PYTHON_GEN) $(srcdir)/spirv/spirv_capabilities_h.py $(srcdir)/spirv/spirv.core.grammar.json $@ || ($(RM) $@; false)
+	$(PYTHON_GEN) $(srcdir)/spirv/spirv_capabilities_h.py --gen-h $@ $(srcdir)/spirv/spirv.core.grammar.json || ($(RM) $@; false)
 
 spirv/spirv_info.c: spirv/spirv_info_c.py spirv/spirv.core.grammar.json
 	$(MKDIR_GEN)
diff --git a/src/compiler/glsl/meson.build b/src/compiler/glsl/meson.build
index 6e43f80..6e264ba 100644
--- a/src/compiler/glsl/meson.build
+++ b/src/compiler/glsl/meson.build
@@ -199,7 +199,7 @@ libglsl = static_library(
   'glsl',
   [files_libglsl, glsl_parser, glsl_lexer_cpp, ir_expression_operation_h,
    ir_expression_operation_strings_h, ir_expression_operation_constant_h,
-   spirv_capabilities_cpp, spirv_capabilities_h],
+   spirv_capabilities_cpp, spirv_capabilities_h, spirv_builder_functions_h],
   c_args : [c_vis_args, c_msvc_compat_args, no_override_init_args],
   cpp_args : [cpp_vis_args, cpp_msvc_compat_args],
   link_with : [libnir, libglcpp],
diff --git a/src/compiler/spirv/.gitignore b/src/compiler/spirv/.gitignore
index 6b5ef0a..a4753c8 100644
--- a/src/compiler/spirv/.gitignore
+++ b/src/compiler/spirv/.gitignore
@@ -1,3 +1,4 @@
+/spirv_builder_functions.h
 /spirv_capabilities.cpp
 /spirv_capabilities.h
 /spirv_info.c
diff --git a/src/compiler/spirv/meson.build b/src/compiler/spirv/meson.build
index 8b6071a..92c0a76 100644
--- a/src/compiler/spirv/meson.build
+++ b/src/compiler/spirv/meson.build
@@ -38,3 +38,10 @@ spirv_capabilities_h = custom_target(
   output : 'spirv_capabilities.h',
   command : [prog_python2, '@INPUT0@', '--gen-h', '@OUTPUT@', '@INPUT1@'],
 )
+
+spirv_builder_functions_h = custom_target(
+  'spirv_builder_functions.h',
+  input : files('spirv_builder_h.py', 'spirv.core.grammar.json'),
+  output : 'spirv_builder_functions.h',
+  command : [prog_python2, '@INPUT0@', '@INPUT1@', '@OUTPUT@'],
+)
diff --git a/src/compiler/spirv/spirv_builder.h b/src/compiler/spirv/spirv_builder.h
new file mode 100644
index 0000000..f82f00f
--- /dev/null
+++ b/src/compiler/spirv/spirv_builder.h
@@ -0,0 +1,245 @@
+/* -*- c++ -*- */
+/*
+ * Copyright (C) 2017 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.
+ */
+#ifndef SPIRV_BUILDER_H
+#define SPIRV_BUILDER_H
+
+#include <inttypes.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "spirv.h"
+#include "spirv_capabilities.h"
+
+#define INSTRUCTION_HEADER(op, size) (((size) << 16) | op)
+
+class spirv_program {
+public:
+   spirv_program(spirv_capability_set *_capabilities)
+      : capabilities(_capabilities), max_instruction(1024), instruction(0),
+        begin(~0)
+   {
+      memory = (uint32_t *) calloc(max_instruction, sizeof(uint32_t));
+   }
+
+   ~spirv_program()
+   {
+      free(memory);
+   }
+
+   unsigned size_in_bytes() const
+   {
+      assert(instruction <= max_instruction);
+
+      return instruction * sizeof(uint32_t);
+   }
+
+   void write_uint32(uint32_t word)
+   {
+      expand_memory(1);
+
+      assert(instruction < max_instruction);
+      memory[instruction++] = word;
+   }
+
+   void write_uint64(uint64_t data)
+   {
+      expand_memory(2);
+
+      assert(instruction < max_instruction);
+      memory[instruction++] = (uint32_t) data;
+      memory[instruction++] = (uint32_t) (data >> 32);
+   }
+
+   void write(const void *ptr, unsigned num_words)
+   {
+      const unsigned bytes_to_copy = num_words * sizeof(uint32_t);
+
+      expand_memory(num_words);
+
+      assert(instruction + num_words <= max_instruction);
+      memcpy(&memory[instruction], ptr, bytes_to_copy);
+      instruction += num_words;
+   }
+
+   void write_string(const char *str)
+   {
+      const unsigned string_bytes = strlen(str);
+
+      /* +1 for the NUL character.  +3 for rounding to the next word size. */
+      const unsigned instruction_bytes = (string_bytes + 1 + 3) & ~3;
+      const unsigned instruction_words = instruction_bytes / sizeof(uint32_t);
+
+      expand_memory(instruction_words);
+
+      assert(instruction + instruction_words <= max_instruction);
+
+      memset(&memory[instruction], 0, instruction_bytes);
+      memcpy(&memory[instruction], str, string_bytes);
+
+      instruction += instruction_words;
+   }
+
+   /**
+    * Read instruction words
+    *
+    * \param ptr        Location to store the data.
+    * \param num_words  Number of \c uint32_t instruction words to read.
+    *
+    * \return
+    * Number of \c uint32_t instruction words actually read.  This will be
+    * less than or equal to \c num_words.
+    */
+   unsigned read(uint32_t *ptr, unsigned num_words) const
+   {
+       const unsigned bytes_to_copy = MIN2(size_in_bytes(),
+                                           num_words * sizeof(uint32_t));
+
+       memcpy(ptr, memory, bytes_to_copy);
+       return bytes_to_copy / sizeof(uint32_t);
+   }
+
+   /**
+    * Start writing an instruction with a known size.
+    */
+   void start_instruction(SpvOp opcode, unsigned num_words)
+   {
+      assert(begin == uint32_t(~0));
+      assert(num_words >= 1);
+
+      begin = instruction;
+      write_uint32(INSTRUCTION_HEADER(opcode, num_words));
+   }
+
+   /**
+    * Start writing an instruction, but the size is not yet known.
+    */
+   void start_instruction(SpvOp opcode)
+   {
+      assert(begin == uint32_t(~0));
+
+      begin = instruction;
+      write_uint32(INSTRUCTION_HEADER(opcode, 0));
+   }
+
+   /**
+    * Finish writing an instruction.
+    *
+    * If the size of instruction was not known when \c start_instruction was
+    * called, the total number of words written will be patched into the size
+    * field.
+    *
+    * \param opcode  The \c SpvOp from the matching \c start_instruction.  This
+    *                \b must match.
+    */
+   void finish_instruction(SpvOp opcode)
+   {
+      assert(begin < instruction);
+
+      const unsigned size = memory[begin] >> 16;
+
+      assert(size == 0 || size == (instruction - begin));
+      assert(opcode == (memory[begin] & SpvOpCodeMask));
+
+      if (size == 0)
+         memory[begin] = INSTRUCTION_HEADER(opcode, (instruction - begin));
+
+      begin = ~0;
+   }
+
+   /**
+    * Set of capabilities associated with this program.
+    *
+    * The \c spirv_capability_set may be shared with other programs that will
+    * eventually be combined together to form a single SPIR-V binary.
+    */
+   spirv_capability_set *const capabilities;
+
+private:
+   void expand_memory(unsigned need)
+   {
+      if (instruction + need > max_instruction) {
+         /* Guarantee that the new allocation will be large enough for the new
+          * storage need.
+          */
+         const unsigned additional = MAX2(max_instruction, need);
+
+         uint32_t *const more_memory =
+            (uint32_t *) realloc(memory,
+                                 ((max_instruction + additional) *
+                                  sizeof(uint32_t)));
+
+         if (more_memory != NULL) {
+            memset(&more_memory[max_instruction],
+                   0,
+                   additional * sizeof(uint32_t));
+
+            memory = more_memory;
+            max_instruction += additional;
+         }
+      }
+   }
+
+   uint32_t *memory;
+   unsigned max_instruction;
+
+   /** Word offset of the next instruction word to write. */
+   unsigned instruction;
+
+   /** Word offset of start of the current instruction. */
+   unsigned begin;
+};
+
+static inline void
+emit_SpvData_uint32(spirv_program *prog, uint32_t data)
+{
+   prog->write_uint32(data);
+}
+
+static inline void
+emit_SpvData_uint64(spirv_program *prog, uint64_t data)
+{
+   prog->write_uint64(data);
+}
+
+static inline void
+emit_SpvData_float32(spirv_program *prog, float data)
+{
+   union { float f; uint32_t u; } d;
+
+   d.f = data;
+   prog->write_uint32(d.u);
+}
+
+static inline void
+emit_SpvData_float64(spirv_program *prog, double data)
+{
+   union { double d; uint64_t u64; } d;
+
+   d.d = data;
+   prog->write_uint64(d.u64);
+}
+
+
+#include "spirv_builder_functions.h"
+
+#endif /* SPIRV_BUILDER_H */
diff --git a/src/compiler/spirv/spirv_builder_h.py b/src/compiler/spirv/spirv_builder_h.py
new file mode 100644
index 0000000..bd2f545
--- /dev/null
+++ b/src/compiler/spirv/spirv_builder_h.py
@@ -0,0 +1,585 @@
+COPYRIGHT = """\
+/*
+ * Copyright (C) 2017 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.
+ */
+"""
+
+import argparse
+import json
+import textwrap
+from sys import stdout
+from sys import exit
+from mako.template import Template
+
+def parse_args():
+    p = argparse.ArgumentParser()
+    p.add_argument("json")
+    p.add_argument("out")
+    return p.parse_args()
+
+def declare_parameter(operand, all_enums, bit_enums):
+    # Handle this case specially so that we can eliminate the extra space
+    # between the "*" and the parameter name.
+    if operand["kind"] == "LiteralString":
+        return "const char *" + operand_name(operand)
+
+    base_decl = type_map[operand["kind"]] + " " + operand_name(operand)
+
+    if can_have_extra_operands(operand["kind"], all_enums):
+        extra_decl = ",\n   const void *{}_parameters".format(operand_name(operand))
+    else:
+        extra_decl = ""
+
+    if optional_parameter_flag(operand, bit_enums) is not None:
+        flag_decl = "bool " + optional_parameter_flag(operand, bit_enums) + ",\n   "
+    else:
+        flag_decl = ""
+
+    return flag_decl + base_decl + extra_decl
+
+def can_have_extra_operands(name, enums):
+    """Determine whether an enum can have extra operands.
+
+    Some enums, like SpvDecoration, can have extra operands with some values.
+    Other enums, like SpvMemorySemantics, cannot.
+    """
+
+    if name not in enums:
+        return False
+
+    return any(p[2] for p in enums[name][1])
+
+def prototype_for_instruction_emit(inst, all_enums, bit_enums):
+    parameters = ["spirv_program *prog"]
+
+    if "operands" in inst:
+        parameters = parameters + [declare_parameter(operand, all_enums, bit_enums) for operand in inst["operands"]]
+
+    return textwrap.dedent("""\
+    static inline void
+    emit_Spv{name}({parameters})""".format(name=inst["opname"],
+                                           parameters=",\n        ".join(parameters)))
+
+def instruction_size(instruction, enums):
+    """Determine the size of an instruction based on its operands
+
+    Instructions that have only operands without ? or * quantifiers are sized
+    by the number of operands.  In addition, instructions that have certain
+    BitEnum and ValueEnum parameters also have varying sizes.
+    """
+
+    if "operands" not in instruction:
+        # Instructions like OpNop that have no operands.  Handle these with a
+        # trivial special case.
+        return 1
+
+    for operand in instruction["operands"]:
+        if "quantifier" in operand:
+            return 0
+        elif operand["kind"] == "LiteralString":
+            return 0
+        elif operand["kind"] in enums and enums[operand["kind"]][0]:
+            return 0
+
+    return len(instruction["operands"]) + 1
+
+def optional_parameter_flag(operand, bit_enums):
+    """Return the name of the flag for an optional parameter, None otherwise
+
+    Optional parameters are noted in the JSON with a "?" quantifier.  For some
+    types, it is possible to infer that a value does not exist by examining
+    the value.  For example, if a optional LiteralString parameter is NULL, it
+    is not included.  The same applies for some BitEnum kinds such as
+    ImageOperands. For cases where the existence cannot be infered from the
+    value, a "has_foo" flag is added to the parameter list.
+
+    This function returns either the has_foo name as a string or None.
+    """
+
+    if operand["kind"] == "LiteralString":
+        return None
+
+    if operand["kind"] == "ImageOperands":
+        return None
+
+    if operand["kind"] in bit_enums:
+        return None
+
+    if "quantifier" not in operand or operand["quantifier"] != "?":
+        return None
+
+    return "has_" + operand_name(operand)
+
+def operand_name(operand):
+    if operand["kind"] == "IdResult":
+        return "result"
+    elif operand["kind"] == "IdResultType":
+        return "result_type"
+    elif "name" in operand:
+        if "quantifier" in operand and "+" in operand["name"]:
+            return operand["kind"]
+        elif operand["name"] == "'D~ref~'":
+            return "deref"
+        else:
+            name=operand["name"].replace("'", "").replace(" ", "_").replace(".", "").replace("~","_").lower()
+            return name if name not in ["default"] else "_" + name
+    else:
+        return operand["kind"]
+
+def list_is_same(a, b):
+    """Determine whether two lists have the same contents without regard for
+    order.
+
+    The list==list comparison requires that the elements in each list have the
+    same order.  This function does not.
+
+    """
+    if len(a) != len(b):
+        return False
+
+    for x in a:
+        if x not in b:
+            return False
+
+    return True
+
+def capabilities_are_same(enums):
+    for parameter in enums[1:]:
+        if not list_is_same(enums[0][1], parameter[1]):
+            return False
+
+    return True
+
+def render_enable_capability(capability_list, indent="   "):
+    if len(capability_list) == 0:
+        return ""
+
+    execution_modes = ["Kernel", "Shader", "Geometry", "Tessellation"]
+
+    if set(capability_list) <= set(execution_modes):
+        # If all of the capabilities are execution mode capabilities, then one
+        # of the must already be set.
+
+        if len(capability_list) == 1:
+            text = "assert(prog->capabilities->is_enabled(SpvCapability{}));".format(capability_list[0])
+        else:
+            template = Template("""\
+            assert(prog->capabilities->is_enabled(SpvCapability${capability_list[0]}) ||
+            % for cap in capability_list[1:-1]:
+                   prog->capabilities->is_enabled(SpvCapability${cap}) ||
+            % endfor
+                   prog->capabilities->is_enabled(SpvCapability${capability_list[-1]}));""")
+
+            text = template.render(capability_list=capability_list)
+    elif len(capability_list) == 2 and capability_list[0] in execution_modes:
+        # If there are two capabilities listed and one is either
+        # SpvCapabilityKernel or SpvCapabilityShader, then enable the other
+        # capability if the non-SpvCapabilityKernel-or-SpvCapabilityShader
+        # capablity is not enabled.
+
+        text = """\
+        if (!prog->capabilities->is_enabled(SpvCapability{}))
+           prog->capabilities->enable(SpvCapability{});""".format(capability_list[0],
+                                                                  capability_list[1])
+    elif len(capability_list) == 1:
+        # If there is a single capability listed that is neither
+        # SpvCapabilityKernel nor SpvCapabilityShader, then enable it.
+
+        text = "prog->capabilities->enable(SpvCapability{});".format(capability_list[0])
+    else:
+        # If there are multiple capabilities listed, things become
+        # complicated.  There is generally not enough information available to
+        # know which capability should be enabled.
+
+        template = Template("""\
+        if (!prog->capabilities->is_enabled(SpvCapability${capability_list[0]}) &&
+        % for cap in capability_list[1:-1]:
+            !prog->capabilities->is_enabled(SpvCapability${cap}) &&
+        % endfor
+            !prog->capabilities->is_enabled(SpvCapability${capability_list[-1]})) {
+           // FINISHME: Do something smart.
+        }""")
+
+        text = template.render(capability_list=capability_list)
+
+    text = textwrap.dedent(text)
+    return "\n".join([(indent + l).rstrip() for l in text.splitlines()])
+
+TEMPLATE = Template("""\
+/* DO NOT EDIT - This file is generated automatically by spirv_builder_h.py script */
+
+""" + COPYRIGHT + """\
+#ifndef SPIRV_BUILDER_FUNCTIONS_H
+#define SPIRV_BUILDER_FUNCTIONS_H
+
+% for name in bit_enums:
+
+static inline void
+Spv${name}_validate_and_set_capabilities(MAYBE_UNUSED spirv_program *prog, MAYBE_UNUSED ${type_map[name]} bits${", MAYBE_UNUSED const uint32_t *parameters" if bit_enums[name][0] else ""})
+{
+   assert((bits & ~(${bit_enums[name][1][0][0]} |
+    % for parameter in bit_enums[name][1][1:]:
+                    ${parameter[0]}${" |" if parameter != bit_enums[name][1][-1] else ")) == 0);"}
+    % endfor
+    % for parameter in bit_enums[name][1]:
+        % if len(parameter[1]) == 1 and parameter[1][0] in ["Shader", "Kernel"]:
+   assert((bits & ${parameter[0]}) == 0 ||
+          prog->capabilities->is_enabled(SpvCapability${parameter[1][0]}));
+        % elif len(parameter[1]) > 0:
+
+   if ((bits & ${parameter[0]}))
+      prog->capabilities->enable(SpvCapability${parameter[1][0]});
+        % endif
+    % endfor
+}
+    % if bit_enums[name][0]:
+
+static inline unsigned
+${type_map[name]}_parameter_count(${type_map[name]} bits)
+{
+   unsigned count = 0;
+    % for parameter in bit_enums[name][1]:
+        % if len(parameter[2]) > 0:
+
+   if ((bits & ${parameter[0]}) != 0)
+      count += ${len(parameter[2])};
+        % endif
+    % endfor
+
+   return count;
+}
+    % endif
+% endfor
+% for name in value_enums:
+
+static inline void
+Spv${name}_validate_and_set_capabilities(MAYBE_UNUSED spirv_program *prog, ${type_map[name]} value${", MAYBE_UNUSED const uint32_t *parameters" if value_enums[name][0] else ""})
+{
+   switch (value) {
+    % if name == "Capability":
+        % for parameter in value_enums[name][1]:
+   case ${type_map[name]}${parameter[0]}:
+        % endfor
+      break;
+    % else:
+        % for parameter in value_enums[name][1]:
+   case ${type_map[name]}${parameter[0]}:
+            % for i in range(len(parameter[2])):
+                % if parameter[2][i]["kind"] in all_enums:
+      Spv${parameter[2][i]["kind"]}_validate_and_set_capabilities(prog, (${type_map[parameter[2][i]["kind"]]}) parameters[${i}]);
+                % endif
+            % endfor
+            % if not capabilities_are_same(value_enums[name][1]) or parameter == value_enums[name][1][-1]:
+                % if len(parameter[1]) > 0:
+${render_enable_capability(parameter[1], "      ")}
+                % endif
+      break;
+            % endif
+        % endfor
+    % endif
+   default:
+      unreachable("Invalid ${type_map[name]} value.");
+   }
+}
+    % if value_enums[name][0]:
+
+static inline unsigned
+Spv${name}_parameter_count(${type_map[name]} value)
+{
+   switch (value) {
+        % for parameter in value_enums[name][1]:
+   case ${type_map[name]}${parameter[0]}: return ${len(parameter[2])};
+        % endfor
+   default: unreachable("Invalid ${type_map[name]} value.");
+   }
+}
+    % endif
+% endfor
+
+static inline void
+emit_SpvHeader(spirv_program *prog, uint32_t version, uint32_t magic, uint32_t id_bound)
+{
+   prog->write_uint32(SpvMagicNumber);
+   prog->write_uint32(version);
+   prog->write_uint32(magic);
+   prog->write_uint32(id_bound);
+   prog->write_uint32(0);
+}
+% for inst in instructions:
+
+    % if "operands" in inst and "quantifier" in inst["operands"][-1] and inst["operands"][-1]["quantifier"] == "*":
+static inline void
+begin_Spv${inst["opname"]}(
+   ${",    ".join(["spirv_program *prog"] + [declare_parameter(operand, all_enums, bit_enums) for operand in inst["operands"][:-1]])})
+{
+        % if "capabilities" in inst:
+${render_enable_capability(inst["capabilities"], "   ")}
+
+        % endif
+
+       % if len(inst["operands"]) < 2:
+   /* Write the instruction header now, even though it will be rewritten in
+    * end_Spv${inst["opname"]}, to aid debugging.  Having something in the
+    * output stream can be helpful.
+    */
+        % endif
+   prog->start_instruction(Spv${inst["opname"]});
+        % for operand in inst["operands"][:-1]:
+            % if operand["kind"] == "LiteralString":
+   prog->write_string(${operand_name(operand)});
+            % else:
+   prog->write_uint32(${operand_name(operand)});
+            % endif
+        % endfor
+}
+
+static inline void
+end_Spv${inst["opname"]}(spirv_program *prog)
+{
+   prog->finish_instruction(Spv${inst["opname"]});
+}
+    % elif instruction_size(inst, all_enums) == 0:
+${prototype_for_instruction_emit(inst, all_enums, bit_enums)}
+{
+        % if "capabilities" in inst:
+${render_enable_capability(inst["capabilities"], "   ")}
+
+        % endif
+        % if "operands" in inst:
+            % for operand in inst["operands"]:
+                % if operand["kind"] in all_enums:
+                    % if optional_parameter_flag(operand, bit_enums) is not None:
+   if (${optional_parameter_flag(operand, bit_enums)})
+      Spv${operand["kind"]}_validate_and_set_capabilities(prog, ${operand_name(operand)}${", (const uint32_t *){}_parameters".format(operand_name(operand)) if can_have_extra_operands(operand["kind"], all_enums) else ""});
+                    % else:
+   Spv${operand["kind"]}_validate_and_set_capabilities(prog, ${operand_name(operand)}${", (const uint32_t *){}_parameters".format(operand_name(operand)) if can_have_extra_operands(operand["kind"], all_enums) else ""});
+                    % endif
+                % endif
+            % endfor
+
+        % endif
+        % if instruction_size(inst, all_enums) == 0:
+   prog->start_instruction(Spv${inst["opname"]});
+        % else:
+   prog->start_instruction(Spv${inst["opname"]}, ${instruction_size(inst, all_enums)});
+        % endif
+        % if "operands" in inst:
+            % for operand in inst["operands"]:
+                % if optional_parameter_flag(operand, bit_enums) is not None:
+
+   if (${optional_parameter_flag(operand, bit_enums)})
+   prog->write_uint32(${operand_name(operand)});
+                % elif operand["kind"] == "LiteralString" and "quantifier" in operand and operand["quantifier"] == "?":
+
+   if (${operand_name(operand)} != NULL)
+      prog->write_string(${operand_name(operand)});
+                % elif operand["kind"] == "LiteralString":
+   prog->write_string(${operand_name(operand)});
+                % elif operand["kind"] in bit_enums and bit_enums[operand["kind"]][0]:
+                    % if "quantifier" in operand and operand["quantifier"] == "?":
+
+   if (${operand_name(operand)} != ${type_map[operand["kind"]]}None) {
+      prog->write_uint32(${operand_name(operand)});
+      prog->write(${operand_name(operand)}_parameters,
+                  ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)}));
+   }
+                    % else:
+
+   prog->write_uint32(${operand_name(operand)});
+                        % if operand["kind"] == "ImageOperands":
+   assert(${operand_name(operand)} != ${type_map[operand["kind"]]}None);
+
+   prog->write(${operand_name(operand)}_parameters,
+               ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)}));
+                        % else:
+   if (${operand_name(operand)} != ${type_map[operand["kind"]]}None) {
+      prog->write(${operand_name(operand)}_parameters,
+                  ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)}));
+   }
+                        % endif
+                    % endif
+                % elif operand["kind"] in value_enums and value_enums[operand["kind"]][0]:
+   prog->write_uint32(${operand_name(operand)});
+   prog->write(${operand_name(operand)}_parameters,
+               ${type_map[operand["kind"]]}_parameter_count(${operand_name(operand)}));
+                % else:
+   prog->write_uint32(${operand_name(operand)});
+                % endif
+            % endfor
+        % endif
+
+   /* Fix the size based on what was actually emitted. */
+   prog->finish_instruction(Spv${inst["opname"]});
+}
+    % else:
+${prototype_for_instruction_emit(inst, all_enums, bit_enums)}
+{
+        % if "capabilities" in inst:
+${render_enable_capability(inst["capabilities"], "   ")}
+
+        % endif
+        % if inst["opname"] == "OpTypeInt":
+   switch (width) {
+   case 8:
+      assert(prog->capabilities->is_enabled(SpvCapabilityKernel));
+      prog->capabilities->enable(SpvCapabilityInt8);
+      break;
+   case 16:
+      prog->capabilities->enable(SpvCapabilityInt16);
+      break;
+   case 32:
+      break;
+   case 64:
+      prog->capabilities->enable(SpvCapabilityInt64);
+      break;
+   default:
+      assert(!"Invalid integer width.");
+   }
+
+        % elif inst["opname"] == "OpTypeFloat":
+   switch (width) {
+   case 16:
+      prog->capabilities->enable(SpvCapabilityFloat16);
+      break;
+   case 32:
+      break;
+   case 64:
+      prog->capabilities->enable(SpvCapabilityFloat64);
+      break;
+   default:
+      assert(!"Invalid float width.");
+   }
+
+        % endif
+        % if "operands" in inst:
+            % for operand in inst["operands"]:
+                % if operand["kind"] in all_enums:
+   Spv${operand["kind"]}_validate_and_set_capabilities(prog, ${operand_name(operand)}${", (const uint32_t *){}_parameters".format(operand_name(operand)) if can_have_extra_operands(operand["kind"], all_enums) else ""});
+                % endif
+            % endfor
+
+        % endif
+   prog->start_instruction(Spv${inst["opname"]}, ${instruction_size(inst, all_enums)});
+        % if "operands" in inst:
+            % for operand in inst["operands"]:
+   prog->write_uint32(${operand_name(operand)});
+            % endfor
+        % endif
+   prog->finish_instruction(Spv${inst["opname"]});
+}
+    % endif
+% endfor
+
+#endif /* SPIRV_BUILDER_FUNCTIONS_H */
+""")
+
+if __name__ == "__main__":
+    pargs = parse_args()
+
+    spirv_info = json.JSONDecoder().decode(open(pargs.json, "r").read())
+
+    type_map = {"LiteralContextDependentNumber": "int",
+                "LiteralExtInstInteger": "int",
+                "LiteralInteger": "int",
+                "LiteralString": "const char *",
+                "LiteralSpecConstantOpInteger": "int"
+    }
+
+    composite_types = {}
+    bit_enums = {}
+    value_enums = {}
+    for operand_kind in spirv_info["operand_kinds"]:
+        kind = operand_kind["kind"]
+
+        if operand_kind["category"] == "BitEnum":
+            type_map[kind] = "Spv" + kind + "Mask"
+
+            values = []
+            has_parameters = False
+            for enum in operand_kind["enumerants"]:
+                # For some reason, spirv.h does not include names for 0-values
+                # of BitEnums that are not None.  For example, there is no
+                # SpvMemorySemanticsRelaxedMask even though it appears in the
+                # JSON.
+                if int(enum["value"], 16) == 0 and enum["enumerant"] != "None":
+                    continue
+
+                capabilities = enum.get("capabilities", [])
+                parameters = enum.get("parameters", [])
+
+                if enum["enumerant"] == "None":
+                    enum_name = "Spv" + kind + "MaskNone"
+                else:
+                    enum_name = "Spv" + kind + enum["enumerant"] + "Mask"
+
+                values.append((enum_name, capabilities, parameters))
+
+                if parameters:
+                    has_parameters = True
+
+            bit_enums[kind] = (has_parameters, values)
+        elif operand_kind["category"] == "ValueEnum":
+            type_map[kind] = "Spv" + kind
+
+            values = []
+            has_parameters = False
+            last_value = -1
+            for enum in operand_kind["enumerants"]:
+                if enum["value"] == last_value:
+                    continue
+
+                last_value = enum["value"]
+                capabilities = enum.get("capabilities", [])
+                parameters = enum.get("parameters", [])
+                values.append((enum["enumerant"], capabilities, parameters))
+
+                if parameters:
+                    has_parameters = True
+
+            value_enums[kind] = (has_parameters, values)
+        elif operand_kind["category"] == "Id":
+            type_map[kind] = "SpvId"
+        elif operand_kind["category"] == "Composite":
+            type_map[kind] = "struct Spv" + kind
+            composite_types[kind] = operand_kind["bases"]
+        elif operand_kind["category"] != "Literal" or kind not in type_map:
+            print("Don't know how to handle {} in category {}".format(kind, operand_kind["category"]))
+            exit(1)
+
+    all_enums = dict(value_enums);
+    all_enums.update(bit_enums);
+
+    with open(pargs.out, 'w') as f:
+        f.write(TEMPLATE.render(instructions=spirv_info["instructions"],
+                                composite_types=composite_types,
+                                type_map=type_map,
+                                operand_name=operand_name,
+                                instruction_size=instruction_size,
+                                optional_parameter_flag=optional_parameter_flag,
+                                declare_parameter=declare_parameter,
+                                can_have_extra_operands=can_have_extra_operands,
+                                prototype_for_instruction_emit=prototype_for_instruction_emit,
+                                bit_enums=bit_enums,
+                                value_enums=value_enums,
+                                all_enums=all_enums,
+                                capabilities_are_same=capabilities_are_same,
+                                render_enable_capability=render_enable_capability))
-- 
2.9.5



More information about the mesa-dev mailing list