#!/usr/bin/env python # Copyright (c) 2016 Google Inc. # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Generates various info tables from SPIR-V JSON grammar.""" import errno import json import os.path import re # Prefix for all C variables generated by this script. PYGEN_VARIABLE_PREFIX = 'pygen_variable' # Extensions to recognize, but which don't necessarily come from the SPIR-V # core or KHR grammar files. Get this list from the SPIR-V registry web page. # NOTE: Only put things on this list if it is not in those grammar files. EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ SPV_AMD_gcn_shader SPV_AMD_gpu_shader_half_float SPV_AMD_gpu_shader_int16 SPV_AMD_shader_trinary_minmax SPV_KHR_non_semantic_info """ OUTPUT_LANGUAGE = 'c' def make_path_to_file(f): """Makes all ancestor directories to the given file, if they don't yet exist. Arguments: f: The file whose ancestor directories are to be created. """ dir = os.path.dirname(os.path.abspath(f)) try: os.makedirs(dir) except OSError as e: if e.errno == errno.EEXIST and os.path.isdir(dir): pass else: raise def convert_min_required_version(version): """Converts the minimal required SPIR-V version encoded in the grammar to the symbol in SPIRV-Tools.""" if version is None: return 'SPV_SPIRV_VERSION_WORD(1, 0)' if version == 'None': return '0xffffffffu' return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) def convert_max_required_version(version): """Converts the maximum required SPIR-V version encoded in the grammar to the symbol in SPIRV-Tools.""" if version is None: return '0xffffffffu' return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) def compose_capability_list(caps): """Returns a string containing a braced list of capabilities as enums. Arguments: - caps: a sequence of capability names Returns: a string containing the braced list of SpvCapability* or spv::Capability:: enums named by caps. """ base_string = 'SpvCapability' global OUTPUT_LANGUAGE if OUTPUT_LANGUAGE == 'c++': base_string = 'spv::Capability::' return '{' + ', '.join([(base_string + '{}').format(c) for c in caps]) + '}' def get_capability_array_name(caps): """Returns the name of the array containing all the given capabilities. Args: - caps: a sequence of capability names """ if not caps: return 'nullptr' return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps)) def generate_capability_arrays(caps): """Returns the arrays of capabilities. Arguments: - caps: a sequence of sequence of capability names """ caps = sorted(set([tuple(c) for c in caps if c])) cap_str = 'SpvCapability' global OUTPUT_LANGUAGE if OUTPUT_LANGUAGE == 'c++': cap_str = 'spv::Capability' arrays = [ 'static const ' + cap_str + ' {}[] = {};'.format( get_capability_array_name(c), compose_capability_list(c)) for c in caps] return '\n'.join(arrays) def compose_extension_list(exts): """Returns a string containing a braced list of extensions as enums. Arguments: - exts: a sequence of extension names Returns: a string containing the braced list of extensions named by exts. """ return '{' + ', '.join( ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}' def get_extension_array_name(extensions): """Returns the name of the array containing all the given extensions. Args: - extensions: a sequence of extension names """ if not extensions: return 'nullptr' else: return '{}_exts_{}'.format( PYGEN_VARIABLE_PREFIX, ''.join(extensions)) def generate_extension_arrays(extensions): """Returns the arrays of extensions. Arguments: - caps: a sequence of sequence of extension names """ extensions = sorted(set([tuple(e) for e in extensions if e])) arrays = [ 'static const spvtools::Extension {}[] = {};'.format( get_extension_array_name(e), compose_extension_list(e)) for e in extensions] return '\n'.join(arrays) def convert_operand_kind(operand_tuple): """Returns the corresponding operand type used in spirv-tools for the given operand kind and quantifier used in the JSON grammar. Arguments: - operand_tuple: a tuple of two elements: - operand kind: used in the JSON grammar - quantifier: '', '?', or '*' Returns: a string of the enumerant name in spv_operand_type_t """ kind, quantifier = operand_tuple # The following cases are where we differ between the JSON grammar and # spirv-tools. if kind == 'IdResultType': kind = 'TypeId' elif kind == 'IdResult': kind = 'ResultId' elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': kind = 'MemorySemanticsId' elif kind == 'IdScope' or kind == 'Scope': kind = 'ScopeId' elif kind == 'IdRef': kind = 'Id' elif kind == 'ImageOperands': kind = 'Image' elif kind == 'Dim': kind = 'Dimensionality' elif kind == 'ImageFormat': kind = 'SamplerImageFormat' elif kind == 'KernelEnqueueFlags': kind = 'KernelEnqFlags' elif kind == 'LiteralExtInstInteger': kind = 'ExtensionInstructionNumber' elif kind == 'LiteralSpecConstantOpInteger': kind = 'SpecConstantOpNumber' elif kind == 'LiteralContextDependentNumber': kind = 'TypedLiteralNumber' elif kind == 'PairLiteralIntegerIdRef': kind = 'LiteralIntegerId' elif kind == 'PairIdRefLiteralInteger': kind = 'IdLiteralInteger' elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar kind = 'Id' if kind == 'FPRoundingMode': kind = 'FpRoundingMode' elif kind == 'FPFastMathMode': kind = 'FpFastMathMode' if quantifier == '?': kind = 'Optional{}'.format(kind) elif quantifier == '*': kind = 'Variable{}'.format(kind) return 'SPV_OPERAND_TYPE_{}'.format( re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) class InstInitializer(object): """Instances holds a SPIR-V instruction suitable for printing as the initializer for spv_opcode_desc_t.""" def __init__(self, opname, caps, exts, operands, version, lastVersion): """Initialization. Arguments: - opname: opcode name (with the 'Op' prefix) - caps: a sequence of capability names required by this opcode - exts: a sequence of names of extensions enabling this enumerant - operands: a sequence of (operand-kind, operand-quantifier) tuples - version: minimal SPIR-V version required for this opcode - lastVersion: last version of SPIR-V that includes this opcode """ assert opname.startswith('Op') self.opname = opname[2:] # Remove the "Op" prefix. self.num_caps = len(caps) self.caps_mask = get_capability_array_name(caps) self.num_exts = len(exts) self.exts = get_extension_array_name(exts) self.operands = [convert_operand_kind(o) for o in operands] self.fix_syntax() operands = [o[0] for o in operands] self.ref_type_id = 'IdResultType' in operands self.def_result_id = 'IdResult' in operands self.version = convert_min_required_version(version) self.lastVersion = convert_max_required_version(lastVersion) def fix_syntax(self): """Fix an instruction's syntax, adjusting for differences between the officially released grammar and how SPIRV-Tools uses the grammar. Fixes: - ExtInst should not end with SPV_OPERAND_VARIABLE_ID. https://github.com/KhronosGroup/SPIRV-Tools/issues/233 """ if (self.opname == 'ExtInst' and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'): self.operands.pop() def __str__(self): global OUTPUT_LANGUAGE base_str = 'SpvOp' if OUTPUT_LANGUAGE == 'c++': base_str = 'spv::Op::Op' template = ['{{"{opname}"', base_str + '{opname}', '{num_caps}', '{caps_mask}', '{num_operands}', '{{{operands}}}', '{def_result_id}', '{ref_type_id}', '{num_exts}', '{exts}', '{min_version}', '{max_version}}}'] return ', '.join(template).format( opname=self.opname, num_caps=self.num_caps, caps_mask=self.caps_mask, num_operands=len(self.operands), operands=', '.join(self.operands), def_result_id=(1 if self.def_result_id else 0), ref_type_id=(1 if self.ref_type_id else 0), num_exts=self.num_exts, exts=self.exts, min_version=self.version, max_version=self.lastVersion) class ExtInstInitializer(object): """Instances holds a SPIR-V extended instruction suitable for printing as the initializer for spv_ext_inst_desc_t.""" def __init__(self, opname, opcode, caps, operands): """Initialization. Arguments: - opname: opcode name - opcode: enumerant value for this opcode - caps: a sequence of capability names required by this opcode - operands: a sequence of (operand-kind, operand-quantifier) tuples """ self.opname = opname self.opcode = opcode self.num_caps = len(caps) self.caps_mask = get_capability_array_name(caps) self.operands = [convert_operand_kind(o) for o in operands] self.operands.append('SPV_OPERAND_TYPE_NONE') def __str__(self): template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', '{{{operands}}}}}'] return ', '.join(template).format( opname=self.opname, opcode=self.opcode, num_caps=self.num_caps, caps_mask=self.caps_mask, operands=', '.join(self.operands)) def generate_instruction(inst, is_ext_inst): """Returns the C initializer for the given SPIR-V instruction. Arguments: - inst: a dict containing information about a SPIR-V instruction - is_ext_inst: a bool indicating whether |inst| is an extended instruction. Returns: a string containing the C initializer for spv_opcode_desc_t or spv_ext_inst_desc_t """ opname = inst.get('opname') opcode = inst.get('opcode') caps = inst.get('capabilities', []) exts = inst.get('extensions', []) operands = inst.get('operands', {}) operands = [(o['kind'], o.get('quantifier', '')) for o in operands] min_version = inst.get('version', None) max_version = inst.get('lastVersion', None) assert opname is not None if is_ext_inst: return str(ExtInstInitializer(opname, opcode, caps, operands)) else: return str(InstInitializer(opname, caps, exts, operands, min_version, max_version)) def generate_instruction_table(inst_table): """Returns the info table containing all SPIR-V instructions, sorted by opcode, and prefixed by capability arrays. Note: - the built-in sorted() function is guaranteed to be stable. https://docs.python.org/3/library/functions.html#sorted Arguments: - inst_table: a list containing all SPIR-V instructions. """ inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname'])) caps_arrays = generate_capability_arrays( [inst.get('capabilities', []) for inst in inst_table]) exts_arrays = generate_extension_arrays( [inst.get('extensions', []) for inst in inst_table]) insts = [generate_instruction(inst, False) for inst in inst_table] insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n' ' {}\n}};'.format(',\n '.join(insts))] return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts)) def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): """Returns the info table containing all SPIR-V extended instructions, sorted by opcode, and prefixed by capability arrays. Arguments: - inst_table: a list containing all SPIR-V instructions. - set_name: the name of the extended instruction set. - operand_kind_prefix: the prefix, if any, to add to the front of operand kind names. """ if operand_kind_prefix: prefix_operand_kind_names(operand_kind_prefix, json_grammar) inst_table = json_grammar["instructions"] set_name = set_name.replace(".", "_") inst_table = sorted(inst_table, key=lambda k: k['opcode']) caps = [inst.get('capabilities', []) for inst in inst_table] caps_arrays = generate_capability_arrays(caps) insts = [generate_instruction(inst, True) for inst in inst_table] insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' ' {}\n}};'.format(set_name, ',\n '.join(insts))] return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) class EnumerantInitializer(object): """Prints an enumerant as the initializer for spv_operand_desc_t.""" def __init__(self, enumerant, value, caps, exts, parameters, version, lastVersion): """Initialization. Arguments: - enumerant: enumerant name - value: enumerant value - caps: a sequence of capability names required by this enumerant - exts: a sequence of names of extensions enabling this enumerant - parameters: a sequence of (operand-kind, operand-quantifier) tuples - version: minimal SPIR-V version required for this opcode - lastVersion: last SPIR-V version this opode appears """ self.enumerant = enumerant self.value = value self.num_caps = len(caps) self.caps = get_capability_array_name(caps) self.num_exts = len(exts) self.exts = get_extension_array_name(exts) self.parameters = [convert_operand_kind(p) for p in parameters] self.version = convert_min_required_version(version) self.lastVersion = convert_max_required_version(lastVersion) def __str__(self): template = ['{{"{enumerant}"', '{value}', '{num_caps}', '{caps}', '{num_exts}', '{exts}', '{{{parameters}}}', '{min_version}', '{max_version}}}'] return ', '.join(template).format( enumerant=self.enumerant, value=self.value, num_caps=self.num_caps, caps=self.caps, num_exts=self.num_exts, exts=self.exts, parameters=', '.join(self.parameters), min_version=self.version, max_version=self.lastVersion) def generate_enum_operand_kind_entry(entry, extension_map): """Returns the C initializer for the given operand enum entry. Arguments: - entry: a dict containing information about an enum entry - extension_map: a dict mapping enum value to list of extensions Returns: a string containing the C initializer for spv_operand_desc_t """ enumerant = entry.get('enumerant') value = entry.get('value') caps = entry.get('capabilities', []) if value in extension_map: exts = extension_map[value] else: exts = [] params = entry.get('parameters', []) params = [p.get('kind') for p in params] params = zip(params, [''] * len(params)) version = entry.get('version', None) max_version = entry.get('lastVersion', None) assert enumerant is not None assert value is not None return str(EnumerantInitializer( enumerant, value, caps, exts, params, version, max_version)) def generate_enum_operand_kind(enum, synthetic_exts_list): """Returns the C definition for the given operand kind. It's a static const named array of spv_operand_desc_t. Also appends to |synthetic_exts_list| a list of extension lists used. """ kind = enum.get('kind') assert kind is not None # Sort all enumerants according to their values, but otherwise # preserve their order so the first name listed in the grammar # as the preferred name for disassembly. if enum.get('category') == 'ValueEnum': def functor(k): return (k['value']) else: def functor(k): return (int(k['value'], 16)) entries = sorted(enum.get('enumerants', []), key=functor) # SubgroupEqMask and SubgroupEqMaskKHR are the same number with # same semantics, but one has no extension list while the other # does. Both should have the extension list. # So create a mapping from enum value to the union of the extensions # across all those grammar entries. Preserve order. extension_map = {} for e in entries: value = e.get('value') extension_map[value] = [] for e in entries: value = e.get('value') exts = e.get('extensions', []) for ext in exts: if ext not in extension_map[value]: extension_map[value].append(ext) synthetic_exts_list.extend(extension_map.values()) name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind) entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map)) for e in entries] template = ['static const spv_operand_desc_t {name}[] = {{', '{entries}', '}};'] entries = '\n'.join(template).format( name=name, entries=',\n'.join(entries)) return kind, name, entries def generate_operand_kind_table(enums): """Returns the info table containing all SPIR-V operand kinds.""" # We only need to output info tables for those operand kinds that are enums. enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] caps = [entry.get('capabilities', []) for enum in enums for entry in enum.get('enumerants', [])] caps_arrays = generate_capability_arrays(caps) exts = [entry.get('extensions', []) for enum in enums for entry in enum.get('enumerants', [])] enums = [generate_enum_operand_kind(e, exts) for e in enums] exts_arrays = generate_extension_arrays(exts) # We have a few operand kinds that require their optional counterpart to # exist in the operand info table. optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess', 'PackedVectorFormat'] optional_enums = [e for e in enums if e[0] in optional_enums] enums.extend(optional_enums) enum_kinds, enum_names, enum_entries = zip(*enums) # Mark the last few as optional ones. enum_quantifiers = [''] * (len(enums) - len(optional_enums)) + ['?'] * len(optional_enums) # And we don't want redefinition of them. enum_entries = enum_entries[:-len(optional_enums)] enum_kinds = [convert_operand_kind(e) for e in zip(enum_kinds, enum_quantifiers)] table_entries = zip(enum_kinds, enum_names, enum_names) table_entries = [' {{{}, ARRAY_SIZE({}), {}}}'.format(*e) for e in table_entries] template = [ 'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{', '{enums}', '}};'] table = '\n'.join(template).format( p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries)) return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) def get_extension_list(instructions, operand_kinds): """Returns extensions as an alphabetically sorted list of strings.""" things_with_an_extensions_field = [item for item in instructions] enumerants = sum([item.get('enumerants', []) for item in operand_kinds], []) things_with_an_extensions_field.extend(enumerants) extensions = sum([item.get('extensions', []) for item in things_with_an_extensions_field if item.get('extensions')], []) for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): # If it's already listed in a grammar, then don't put it in the # special exceptions list. assert item not in extensions, 'Extension %s is already in a grammar file' % item extensions.extend( EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) # Validator would ignore type declaration unique check. Should only be used # for legacy autogenerated test files containing multiple instances of the # same type declaration, if fixing the test by other methods is too # difficult. Shouldn't be used for any other reasons. extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') return sorted(set(extensions)) def get_capabilities(operand_kinds): """Returns capabilities as a list of JSON objects, in order of appearance.""" enumerants = sum([item.get('enumerants', []) for item in operand_kinds if item.get('kind') in ['Capability']], []) return enumerants def generate_extension_enum(extensions): """Returns enumeration containing extensions declared in the grammar.""" return ',\n'.join(['k' + extension for extension in extensions]) def generate_extension_to_string_mapping(extensions): """Returns mapping function from extensions to corresponding strings.""" function = 'const char* ExtensionToString(Extension extension) {\n' function += ' switch (extension) {\n' template = ' case Extension::k{extension}:\n' \ ' return "{extension}";\n' function += ''.join([template.format(extension=extension) for extension in extensions]) function += ' }\n\n return "";\n}' return function def generate_string_to_extension_mapping(extensions): """Returns mapping function from strings to corresponding extensions.""" function = ''' bool GetExtensionFromString(const char* str, Extension* extension) {{ static const char* known_ext_strs[] = {{ {strs} }}; static const Extension known_ext_ids[] = {{ {ids} }}; const auto b = std::begin(known_ext_strs); const auto e = std::end(known_ext_strs); const auto found = std::equal_range( b, e, str, [](const char* str1, const char* str2) {{ return std::strcmp(str1, str2) < 0; }}); if (found.first == e || found.first == found.second) return false; *extension = known_ext_ids[found.first - b]; return true; }} '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]), ids=', '.join(['Extension::k{}'.format(e) for e in extensions])) return function def generate_capability_to_string_mapping(operand_kinds): """Returns mapping function from capabilities to corresponding strings. We take care to avoid emitting duplicate values. """ cap_str = 'SpvCapability' cap_join = '' global OUTPUT_LANGUAGE if OUTPUT_LANGUAGE == 'c++': cap_str = 'spv::Capability' cap_join = '::' function = 'const char* CapabilityToString(' + cap_str + ' capability) {\n' function += ' switch (capability) {\n' template = ' case ' + cap_str + cap_join + '{capability}:\n' \ ' return "{capability}";\n' emitted = set() # The values of capabilities we already have emitted for capability in get_capabilities(operand_kinds): value = capability.get('value') if value not in emitted: emitted.add(value) function += template.format(capability=capability.get('enumerant')) function += ' case ' + cap_str + cap_join + 'Max:\n' \ ' assert(0 && "Attempting to convert ' + cap_str + cap_join + 'Max to string");\n' \ ' return "";\n' function += ' }\n\n return "";\n}' return function def generate_all_string_enum_mappings(extensions, operand_kinds): """Returns all string-to-enum / enum-to-string mapping tables.""" tables = [] tables.append(generate_extension_to_string_mapping(extensions)) tables.append(generate_string_to_extension_mapping(extensions)) tables.append(generate_capability_to_string_mapping(operand_kinds)) return '\n\n'.join(tables) def precondition_operand_kinds(operand_kinds): """For operand kinds that have the same number, make sure they all have the same extension list.""" # Map operand kind and value to list of the union of extensions # for same-valued enumerants. exts = {} for kind_entry in operand_kinds: kind = kind_entry.get('kind') for enum_entry in kind_entry.get('enumerants', []): value = enum_entry.get('value') key = kind + '.' + str(value) if key in exts: exts[key].extend(enum_entry.get('extensions', [])) else: exts[key] = enum_entry.get('extensions', []) exts[key] = sorted(set(exts[key])) # Now make each entry the same list. for kind_entry in operand_kinds: kind = kind_entry.get('kind') for enum_entry in kind_entry.get('enumerants', []): value = enum_entry.get('value') key = kind + '.' + str(value) if len(exts[key]) > 0: enum_entry['extensions'] = exts[key] return operand_kinds def prefix_operand_kind_names(prefix, json_dict): """Modifies json_dict, by prefixing all the operand kind names with the given prefix. Also modifies their uses in the instructions to match. """ old_to_new = {} for operand_kind in json_dict["operand_kinds"]: old_name = operand_kind["kind"] new_name = prefix + old_name operand_kind["kind"] = new_name old_to_new[old_name] = new_name for instruction in json_dict["instructions"]: for operand in instruction.get("operands", []): replacement = old_to_new.get(operand["kind"]) if replacement is not None: operand["kind"] = replacement def main(): import argparse parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') parser.add_argument('--spirv-core-grammar', metavar='', type=str, required=False, help='input JSON grammar file for core SPIR-V ' 'instructions') parser.add_argument('--extinst-debuginfo-grammar', metavar='', type=str, required=False, default=None, help='input JSON grammar file for DebugInfo extended ' 'instruction set') parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='', type=str, required=False, default=None, help='input JSON grammar file for OpenCL.DebugInfo.100 ' 'extended instruction set') parser.add_argument('--extinst-glsl-grammar', metavar='', type=str, required=False, default=None, help='input JSON grammar file for GLSL extended ' 'instruction set') parser.add_argument('--extinst-opencl-grammar', metavar='', type=str, required=False, default=None, help='input JSON grammar file for OpenCL extended ' 'instruction set') parser.add_argument('--output-language', type=str, required=False, default='c', choices=['c','c++'], help='specify output language type') parser.add_argument('--core-insts-output', metavar='', type=str, required=False, default=None, help='output file for core SPIR-V instructions') parser.add_argument('--glsl-insts-output', metavar='', type=str, required=False, default=None, help='output file for GLSL extended instruction set') parser.add_argument('--opencl-insts-output', metavar='', type=str, required=False, default=None, help='output file for OpenCL extended instruction set') parser.add_argument('--operand-kinds-output', metavar='', type=str, required=False, default=None, help='output file for operand kinds') parser.add_argument('--extension-enum-output', metavar='', type=str, required=False, default=None, help='output file for extension enumeration') parser.add_argument('--enum-string-mapping-output', metavar='', type=str, required=False, default=None, help='output file for enum-string mappings') parser.add_argument('--extinst-vendor-grammar', metavar='', type=str, required=False, default=None, help='input JSON grammar file for vendor extended ' 'instruction set'), parser.add_argument('--vendor-insts-output', metavar='', type=str, required=False, default=None, help='output file for vendor extended instruction set') parser.add_argument('--vendor-operand-kind-prefix', metavar='', type=str, required=False, default=None, help='prefix for operand kinds (to disambiguate operand type enums)') args = parser.parse_args() global OUTPUT_LANGUAGE OUTPUT_LANGUAGE = args.output_language # The GN build system needs this because it doesn't handle quoting # empty string arguments well. if args.vendor_operand_kind_prefix == "...nil...": args.vendor_operand_kind_prefix = "" if (args.core_insts_output is None) != \ (args.operand_kinds_output is None): print('error: --core-insts-output and --operand-kinds-output ' 'should be specified together.') exit(1) if args.operand_kinds_output and not (args.spirv_core_grammar and args.extinst_debuginfo_grammar and args.extinst_cldebuginfo100_grammar): print('error: --operand-kinds-output requires --spirv-core-grammar ' 'and --extinst-debuginfo-grammar ' 'and --extinst-cldebuginfo100-grammar') exit(1) if (args.glsl_insts_output is None) != \ (args.extinst_glsl_grammar is None): print('error: --glsl-insts-output and --extinst-glsl-grammar ' 'should be specified together.') exit(1) if (args.opencl_insts_output is None) != \ (args.extinst_opencl_grammar is None): print('error: --opencl-insts-output and --extinst-opencl-grammar ' 'should be specified together.') exit(1) if (args.vendor_insts_output is None) != \ (args.extinst_vendor_grammar is None): print('error: --vendor-insts-output and ' '--extinst-vendor-grammar should be specified together.') exit(1) if all([args.core_insts_output is None, args.glsl_insts_output is None, args.opencl_insts_output is None, args.vendor_insts_output is None, args.extension_enum_output is None, args.enum_string_mapping_output is None]): print('error: at least one output should be specified.') exit(1) if args.spirv_core_grammar is not None: with open(args.spirv_core_grammar) as json_file: core_grammar = json.loads(json_file.read()) with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: debuginfo_grammar = json.loads(debuginfo_json_file.read()) with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) instructions = [] instructions.extend(core_grammar['instructions']) instructions.extend(debuginfo_grammar['instructions']) instructions.extend(cldebuginfo100_grammar['instructions']) operand_kinds = [] operand_kinds.extend(core_grammar['operand_kinds']) operand_kinds.extend(debuginfo_grammar['operand_kinds']) operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) extensions = get_extension_list(instructions, operand_kinds) operand_kinds = precondition_operand_kinds(operand_kinds) if args.core_insts_output is not None: make_path_to_file(args.core_insts_output) make_path_to_file(args.operand_kinds_output) with open(args.core_insts_output, 'w') as f: f.write(generate_instruction_table( core_grammar['instructions'])) with open(args.operand_kinds_output, 'w') as f: f.write(generate_operand_kind_table(operand_kinds)) if args.extension_enum_output is not None: make_path_to_file(args.extension_enum_output) with open(args.extension_enum_output, 'w') as f: f.write(generate_extension_enum(extensions)) if args.enum_string_mapping_output is not None: make_path_to_file(args.enum_string_mapping_output) with open(args.enum_string_mapping_output, 'w') as f: f.write(generate_all_string_enum_mappings( extensions, operand_kinds)) if args.extinst_glsl_grammar is not None: with open(args.extinst_glsl_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.glsl_insts_output) with open(args.glsl_insts_output, 'w') as f: f.write(generate_extended_instruction_table( grammar, 'glsl')) if args.extinst_opencl_grammar is not None: with open(args.extinst_opencl_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.opencl_insts_output) with open(args.opencl_insts_output, 'w') as f: f.write(generate_extended_instruction_table( grammar, 'opencl')) if args.extinst_vendor_grammar is not None: with open(args.extinst_vendor_grammar) as json_file: grammar = json.loads(json_file.read()) make_path_to_file(args.vendor_insts_output) name = args.extinst_vendor_grammar start = name.find('extinst.') + len('extinst.') name = name[start:-len('.grammar.json')].replace('-', '_') with open(args.vendor_insts_output, 'w') as f: f.write(generate_extended_instruction_table( grammar, name, args.vendor_operand_kind_prefix)) if __name__ == '__main__': main()