mirror of
https://github.com/KhronosGroup/SPIRV-Tools
synced 2024-12-31 20:21:06 +00:00
f3a10470d3
unordered_map is not POD. Using it as static may cause problems when operator new() and operator delete() is customized. Also changed some function signatures to use const char* instead of std::string, which will give caller the flexibility to avoid creating a std::string.
756 lines
29 KiB
Python
Executable File
756 lines
29 KiB
Python
Executable File
#!/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."""
|
|
|
|
from __future__ import print_function
|
|
|
|
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 grammar. Get this list from the SPIR-V registery web page.
|
|
EXTENSIONS_FROM_SPIRV_REGISTRY = """
|
|
SPV_AMD_shader_explicit_vertex_parameter
|
|
SPV_AMD_shader_trinary_minmax
|
|
SPV_AMD_gcn_shader
|
|
SPV_KHR_shader_ballot
|
|
SPV_AMD_shader_ballot
|
|
SPV_AMD_gpu_shader_half_float
|
|
SPV_KHR_shader_draw_parameters
|
|
SPV_KHR_subgroup_vote
|
|
SPV_KHR_16bit_storage
|
|
SPV_KHR_device_group
|
|
SPV_KHR_multiview
|
|
SPV_NVX_multiview_per_view_attributes
|
|
SPV_NV_viewport_array2
|
|
SPV_NV_stereo_view_rendering
|
|
SPV_NV_sample_mask_override_coverage
|
|
SPV_NV_geometry_shader_passthrough
|
|
SPV_AMD_texture_gather_bias_lod
|
|
SPV_KHR_storage_buffer_storage_class
|
|
SPV_KHR_variable_pointers
|
|
SPV_AMD_gpu_shader_int16
|
|
SPV_KHR_post_depth_coverage
|
|
SPV_KHR_shader_atomic_counter_ops
|
|
SPV_EXT_shader_stencil_export
|
|
SPV_EXT_shader_viewport_index_layer
|
|
SPV_AMD_shader_image_load_store_lod
|
|
SPV_AMD_shader_fragment_mask
|
|
"""
|
|
|
|
|
|
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 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* enums named by caps.
|
|
"""
|
|
return "{" + ", ".join(['SpvCapability{}'.format(c) for c in caps]) + "}"
|
|
|
|
|
|
def get_capability_array_name(caps, version):
|
|
"""Returns the name of the array containing all the given capabilities.
|
|
|
|
Args:
|
|
- caps: a sequence of capability names
|
|
"""
|
|
if not caps:
|
|
return 'nullptr'
|
|
else:
|
|
return '{}_caps_{}_{}'.format(
|
|
PYGEN_VARIABLE_PREFIX, ''.join(caps), version)
|
|
|
|
|
|
def generate_capability_arrays(caps, version):
|
|
"""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]))
|
|
arrays = [
|
|
'static const SpvCapability {}[] = {};'.format(
|
|
get_capability_array_name(c, version), 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(
|
|
['libspirv::Extension::k{}'.format(e) for e in exts]) + "}"
|
|
|
|
|
|
def get_extension_array_name(extensions, version):
|
|
"""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), version)
|
|
|
|
|
|
def generate_extension_arrays(extensions, version):
|
|
"""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 libspirv::Extension {}[] = {};'.format(
|
|
get_extension_array_name(e, version), 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, operands, version):
|
|
"""Initialization.
|
|
|
|
Arguments:
|
|
- opname: opcode name (with the 'Op' prefix)
|
|
- caps: a sequence of capability names required by this opcode
|
|
- operands: a sequence of (operand-kind, operand-quantifier) tuples
|
|
- version: version of the instruction set
|
|
"""
|
|
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, version)
|
|
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
|
|
|
|
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):
|
|
template = ['{{"{opname}"', 'SpvOp{opname}',
|
|
'{num_caps}', '{caps_mask}',
|
|
'{num_operands}', '{{{operands}}}',
|
|
'{def_result_id}', '{ref_type_id}}}']
|
|
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))
|
|
|
|
|
|
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, version):
|
|
"""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
|
|
- version: version of the extended instruction set
|
|
"""
|
|
self.opname = opname
|
|
self.opcode = opcode
|
|
self.num_caps = len(caps)
|
|
self.caps_mask = get_capability_array_name(caps, version)
|
|
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, version, 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', [])
|
|
operands = inst.get('operands', {})
|
|
operands = [(o['kind'], o.get('quantifier', '')) for o in operands]
|
|
|
|
assert opname is not None
|
|
|
|
if is_ext_inst:
|
|
return str(ExtInstInitializer(opname, opcode, caps, operands, version))
|
|
else:
|
|
return str(InstInitializer(opname, caps, operands, version))
|
|
|
|
|
|
def generate_instruction_table(inst_table, version):
|
|
"""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.
|
|
- vesion: SPIR-V version.
|
|
"""
|
|
inst_table = sorted(inst_table, key=lambda k: k['opcode'])
|
|
caps_arrays = generate_capability_arrays(
|
|
[inst.get('capabilities', []) for inst in inst_table], version)
|
|
insts = [generate_instruction(inst, version, False) for inst in inst_table]
|
|
insts = ['static const spv_opcode_desc_t kOpcodeTableEntries_{}[] = {{\n'
|
|
' {}\n}};'.format(version, ',\n '.join(insts))]
|
|
|
|
return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts))
|
|
|
|
|
|
def generate_extended_instruction_table(inst_table, set_name, version):
|
|
"""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.
|
|
"""
|
|
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, version)
|
|
insts = [generate_instruction(inst, version, 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):
|
|
"""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
|
|
"""
|
|
self.enumerant = enumerant
|
|
self.value = value
|
|
self.num_caps = len(caps)
|
|
self.caps = get_capability_array_name(caps, version)
|
|
self.num_exts = len(exts)
|
|
self.exts = get_extension_array_name(exts, version)
|
|
self.parameters = [convert_operand_kind(p) for p in parameters]
|
|
|
|
def __str__(self):
|
|
template = ['{{"{enumerant}"', '{value}', '{num_caps}',
|
|
'{caps}', '{num_exts}', '{exts}', '{{{parameters}}}}}']
|
|
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))
|
|
|
|
|
|
def generate_enum_operand_kind_entry(entry, version):
|
|
"""Returns the C initializer for the given operand enum entry.
|
|
|
|
Arguments:
|
|
- entry: a dict containing information about an enum entry
|
|
|
|
Returns:
|
|
a string containing the C initializer for spv_operand_desc_t
|
|
"""
|
|
enumerant = entry.get('enumerant')
|
|
value = entry.get('value')
|
|
caps = entry.get('capabilities', [])
|
|
exts = entry.get('extensions', [])
|
|
params = entry.get('parameters', [])
|
|
params = [p.get('kind') for p in params]
|
|
params = zip(params, [''] * len(params))
|
|
|
|
assert enumerant is not None
|
|
assert value is not None
|
|
|
|
return str(EnumerantInitializer(
|
|
enumerant, value, caps, exts, params, version))
|
|
|
|
|
|
def generate_enum_operand_kind(enum, version):
|
|
"""Returns the C definition for the given operand kind."""
|
|
kind = enum.get('kind')
|
|
assert kind is not None
|
|
|
|
name = '{}_{}Entries_{}'.format(PYGEN_VARIABLE_PREFIX, kind, version)
|
|
entries = [' {}'.format(generate_enum_operand_kind_entry(e, version))
|
|
for e in enum.get('enumerants', [])]
|
|
|
|
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, version):
|
|
"""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, version)
|
|
|
|
exts = [entry.get('extensions', [])
|
|
for enum in enums
|
|
for entry in enum.get('enumerants', [])]
|
|
exts_arrays = generate_extension_arrays(exts, version)
|
|
|
|
enums = [generate_enum_operand_kind(e, version) for e in enums]
|
|
# We have three operand kinds that requires their optional counterpart to
|
|
# exist in the operand info table.
|
|
three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess']
|
|
three_optional_enums = [e for e in enums if e[0] in three_optional_enums]
|
|
enums.extend(three_optional_enums)
|
|
|
|
enum_kinds, enum_names, enum_entries = zip(*enums)
|
|
# Mark the last three as optional ones.
|
|
enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3
|
|
# And we don't want redefinition of them.
|
|
enum_entries = enum_entries[:-3]
|
|
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_{v}[] = {{',
|
|
'{enums}', '}};']
|
|
table = '\n'.join(template).format(
|
|
p=PYGEN_VARIABLE_PREFIX, v=version, enums=',\n'.join(table_entries))
|
|
|
|
return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,))
|
|
|
|
|
|
def get_extension_list(operands):
|
|
"""Returns extensions as an alphabetically sorted list of strings."""
|
|
enumerants = sum([item.get('enumerants', []) for item in operands
|
|
if item.get('category') in ['ValueEnum']], [])
|
|
|
|
extensions = sum([item.get('extensions', []) for item in enumerants
|
|
if item.get('extensions')], [])
|
|
|
|
extensions.extend(EXTENSIONS_FROM_SPIRV_REGISTRY.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(operands):
|
|
"""Returns capabilities as a list of JSON objects, in order of
|
|
appearance.
|
|
"""
|
|
enumerants = sum([item.get('enumerants', []) for item in operands
|
|
if item.get('kind') in ['Capability']], [])
|
|
return enumerants
|
|
|
|
|
|
def generate_extension_enum(operands):
|
|
"""Returns enumeration containing extensions declared in the grammar."""
|
|
extensions = get_extension_list(operands)
|
|
return ',\n'.join(['k' + extension for extension in extensions])
|
|
|
|
|
|
def generate_extension_to_string_table(operands):
|
|
"""Returns extension to string mapping table."""
|
|
extensions = get_extension_list(operands)
|
|
entry_template = ' {{Extension::k{extension},\n "{extension}"}}'
|
|
table_entries = [entry_template.format(extension=extension)
|
|
for extension in extensions]
|
|
table_template = '{{\n{enums}\n}}'
|
|
return table_template.format(enums=',\n'.join(table_entries))
|
|
|
|
|
|
def generate_string_to_extension_table(operands):
|
|
"""Returns string to extension mapping table."""
|
|
extensions = get_extension_list(operands)
|
|
entry_template = ' {{"{extension}",\n Extension::k{extension}}}'
|
|
table_entries = [entry_template.format(extension=extension)
|
|
for extension in extensions]
|
|
table_template = '{{\n{enums}\n}}'
|
|
return table_template.format(enums=',\n'.join(table_entries))
|
|
|
|
|
|
def generate_capability_to_string_table(operands):
|
|
"""Returns capability to string mapping table."""
|
|
capabilities = [item.get('enumerant')
|
|
for item in get_capabilities(operands)]
|
|
entry_template = ' {{SpvCapability{capability},\n "{capability}"}}'
|
|
table_entries = [entry_template.format(capability=capability)
|
|
for capability in capabilities]
|
|
table_template = '{{\n{enums}\n}}'
|
|
return table_template.format(enums=',\n'.join(table_entries))
|
|
|
|
|
|
def generate_extension_to_string_mapping(operands):
|
|
"""Returns mapping function from extensions to corresponding strings."""
|
|
extensions = get_extension_list(operands)
|
|
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(operands):
|
|
"""Returns mapping function from strings to corresponding extensions."""
|
|
extensions = get_extension_list(operands) # Already sorted
|
|
|
|
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(operands):
|
|
"""Returns mapping function from capabilities to corresponding strings.
|
|
We take care to avoid emitting duplicate values.
|
|
"""
|
|
function = 'const char* CapabilityToString(SpvCapability capability) {\n'
|
|
function += ' switch (capability) {\n'
|
|
template = ' case SpvCapability{capability}:\n' \
|
|
' return "{capability}";\n'
|
|
emitted = set() # The values of capabilities we already have emitted
|
|
for capability in get_capabilities(operands):
|
|
value = capability.get('value')
|
|
if value not in emitted:
|
|
emitted.add(value)
|
|
function += template.format(capability=capability.get('enumerant'))
|
|
function += ' case SpvCapabilityMax:\n' \
|
|
' assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \
|
|
' return "";\n'
|
|
function += ' };\n\n return "";\n}'
|
|
return function
|
|
|
|
|
|
def generate_all_string_enum_mappings(operands):
|
|
"""Returns all string-to-enum / enum-to-string mapping tables."""
|
|
tables = []
|
|
tables.append(generate_extension_to_string_mapping(operands))
|
|
tables.append(generate_string_to_extension_mapping(operands))
|
|
tables.append(generate_capability_to_string_mapping(operands))
|
|
return '\n\n'.join(tables)
|
|
|
|
|
|
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-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('--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')
|
|
args = parser.parse_args()
|
|
|
|
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):
|
|
print('error: --operand-kinds-output requires --spirv-core-grammar '
|
|
'and --exinst-debuginfo-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:
|
|
grammar = json.loads(json_file.read())
|
|
with open(args.extinst_debuginfo_grammar) as debuginfo_json_file:
|
|
debuginfo_grammar = json.loads(debuginfo_json_file.read())
|
|
operand_kinds = grammar['operand_kinds']
|
|
operand_kinds.extend(debuginfo_grammar['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)
|
|
version = '{}_{}'.format(grammar['major_version'],
|
|
grammar['minor_version'])
|
|
print(generate_instruction_table(
|
|
grammar['instructions'], version),
|
|
file=open(args.core_insts_output, 'w'))
|
|
print(generate_operand_kind_table(operand_kinds, version),
|
|
file=open(args.operand_kinds_output, 'w'))
|
|
if args.extension_enum_output is not None:
|
|
make_path_to_file(args.extension_enum_output)
|
|
print(generate_extension_enum(grammar['operand_kinds']),
|
|
file=open(args.extension_enum_output, 'w'))
|
|
if args.enum_string_mapping_output is not None:
|
|
make_path_to_file(args.enum_string_mapping_output)
|
|
print(generate_all_string_enum_mappings(operand_kinds),
|
|
file=open(args.enum_string_mapping_output, 'w'))
|
|
|
|
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)
|
|
print(generate_extended_instruction_table(
|
|
grammar['instructions'], "glsl", "1_0"),
|
|
file=open(args.glsl_insts_output, 'w'))
|
|
|
|
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)
|
|
print(generate_extended_instruction_table(
|
|
grammar['instructions'], "opencl", "1_0"),
|
|
file=open(args.opencl_insts_output, 'w'))
|
|
|
|
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("-", "_")
|
|
print(generate_extended_instruction_table(
|
|
grammar['instructions'], name, "1_0"),
|
|
file=open(args.vendor_insts_output, 'w'))
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|