| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963 |
- #!/usr/bin/env python3
- # 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
- SPV_EXT_relaxed_printf_string_address_space
- """
- 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 get_alias_array_name(aliases):
- """Returns the name of the array containing all the given aliases.
- Arguments:
- - aliases: a sequence of alias names
- """
- if not aliases:
- return 'nullptr';
- return '{}_aliases_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(aliases))
- def compose_alias_list(aliases):
- """Returns a string containing a braced list of aliases.
- Arguments:
- - aliases: a sequence of alias names
- Returns:
- a string containing the braced list of char* named by aliases.
- """
- return '{' + ', '.join([('"{}"').format(a) for a in aliases]) + '}'
- def generate_aliases_arrays(aliases):
- """Returns the arrays of aliases
- Arguments:
- - aliases: a sequence of sequence of alias names
- """
- aliases = sorted(set([tuple(a) for a in aliases if a]))
- arrays = [
- 'static const char* {}[] = {};'.format(
- get_alias_array_name(a), compose_alias_list(a))
- for a in aliases]
- return '\n'.join(arrays)
- 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, aliases, caps, exts, operands, version, lastVersion):
- """Initialization.
- Arguments:
- - opname: opcode name (with the 'Op' prefix)
- - aliases: a sequence of aliases for the name of this opcode
- - 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_aliases = len(aliases);
- self.aliases_mask = get_alias_array_name(aliases)
- 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_aliases}', '{aliases_mask}',
- '{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_aliases=self.num_aliases,
- aliases_mask=self.aliases_mask,
- 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')
- aliases = inst.get('aliases', [])
- 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, aliases, 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']))
- aliases_arrays = generate_aliases_arrays(
- [inst.get('aliases', []) for inst in inst_table])
- 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{}\n\n{}'.format(aliases_arrays, 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, aliases, caps, exts, parameters, version, lastVersion):
- """Initialization.
- Arguments:
- - enumerant: enumerant name
- - value: enumerant value
- - aliases: a sequence of aliased capability names
- - 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_aliases = len(aliases)
- self.aliases = get_alias_array_name(aliases)
- 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_aliases}', '{aliases}',
- '{num_caps}', '{caps}',
- '{num_exts}', '{exts}',
- '{{{parameters}}}', '{min_version}',
- '{max_version}}}']
- return ', '.join(template).format(
- enumerant=self.enumerant,
- value=self.value,
- num_aliases=self.num_aliases,
- aliases=self.aliases,
- 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')
- aliases = entry.get('aliases', [])
- 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, aliases, 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]
- if len(entries) == 0:
- # Insert a dummy entry. Otherwise the array is empty and compilation
- # will fail in MSVC.
- entries = [' {"place holder", 0, 0, nullptr, 0, nullptr, 0, nullptr, {}, SPV_SPIRV_VERSION_WORD(999,0), 0}']
- 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']]
- aliases = [entry.get('aliases', [])
- for enum in enums
- for entry in enum.get('enumerants', [])]
- aliases_arrays = generate_aliases_arrays(aliases)
- 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', 'CooperativeMatrixOperands', 'MatrixMultiplyAccumulateOperands', 'RawAccessChainOperands', 'FPEncoding']
- 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((aliases_arrays,) + (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='<path>',
- type=str, required=False,
- help='input JSON grammar file for core SPIR-V '
- 'instructions')
- parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>',
- type=str, required=False, default=None,
- help='input JSON grammar file for DebugInfo extended '
- 'instruction set')
- parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>',
- 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='<path>',
- type=str, required=False, default=None,
- help='input JSON grammar file for GLSL extended '
- 'instruction set')
- parser.add_argument('--extinst-opencl-grammar', metavar='<path>',
- 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='<path>',
- type=str, required=False, default=None,
- help='output file for core SPIR-V instructions')
- parser.add_argument('--glsl-insts-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for GLSL extended instruction set')
- parser.add_argument('--opencl-insts-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for OpenCL extended instruction set')
- parser.add_argument('--operand-kinds-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for operand kinds')
- parser.add_argument('--extension-enum-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for extension enumeration')
- parser.add_argument('--enum-string-mapping-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for enum-string mappings')
- parser.add_argument('--extinst-vendor-grammar', metavar='<path>',
- type=str, required=False, default=None,
- help='input JSON grammar file for vendor extended '
- 'instruction set'),
- parser.add_argument('--vendor-insts-output', metavar='<path>',
- type=str, required=False, default=None,
- help='output file for vendor extended instruction set')
- parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>',
- 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()
|