From: Ian Romanick <ian.d.roman...@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.roman...@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 _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev