[Mesa-dev] [PATCH 12/12] spirv: Generate SPIR-V builder infrastructure
Eric Engestrom
eric.engestrom at imgtec.com
Tue Nov 21 11:56:51 UTC 2017
On Monday, 2017-11-20 17:24:10 -0800, Ian Romanick wrote:
> 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)
Oh, I see you fixed it already, the hunk just ended up in the wrong
commit (:
>
> 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