On Monday, 2017-11-20 17:24:10 -0800, Ian Romanick wrote: > 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)
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 > _______________________________________________ mesa-dev mailing list mesa-dev@lists.freedesktop.org https://lists.freedesktop.org/mailman/listinfo/mesa-dev