Handle aliases field in the grammar (#5799)

* Modify static table generation scripts to include alias lists
* Modify spv_opcode_desc_t and spv_operand_desc_t to include aliases
* Modify opcode and operand lookup by name to also search aliases

* update vim syntax generator
This commit is contained in:
alan-baker 2024-10-09 12:50:32 -04:00 committed by GitHub
parent 4310fd4eda
commit fcf994a619
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 127 additions and 21 deletions

2
DEPS
View File

@ -14,7 +14,7 @@ vars = {
're2_revision': '6dcd83d60f7944926bfd308cc13979fc53dd69ca',
'spirv_headers_revision': 'a62b032007b2e7a69f24a195cbfbd0cf22d31bb0',
'spirv_headers_revision': 'd92cf88c371424591115a87499009dfad41b669c',
}
deps = {

View File

@ -102,7 +102,7 @@ spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
const auto version = spvVersionForTargetEnv(env);
for (uint64_t opcodeIndex = 0; opcodeIndex < table->count; ++opcodeIndex) {
const spv_opcode_desc_t& entry = table->entries[opcodeIndex];
// We considers the current opcode as available as long as
// We consider the current opcode as available as long as
// 1. The target environment satisfies the minimal requirement of the
// opcode; or
// 2. There is at least one extension enabling this opcode.
@ -110,14 +110,35 @@ spv_result_t spvOpcodeTableNameLookup(spv_target_env env,
// Note that the second rule assumes the extension enabling this instruction
// is indeed requested in the SPIR-V code; checking that should be
// validator's work.
if (((version >= entry.minVersion && version <= entry.lastVersion) ||
entry.numExtensions > 0u || entry.numCapabilities > 0u) &&
nameLength == strlen(entry.name) &&
if ((version >= entry.minVersion && version <= entry.lastVersion) ||
entry.numExtensions > 0u || entry.numCapabilities > 0u) {
// Exact match case.
if (nameLength == strlen(entry.name) &&
!strncmp(name, entry.name, nameLength)) {
// NOTE: Found out Opcode!
*pEntry = &entry;
return SPV_SUCCESS;
}
// Lack of binary search really hurts here. There isn't an easy filter to
// apply before checking aliases since we need to handle promotion from
// vendor to KHR/EXT and KHR/EXT to core. It would require a sure-fire way
// of dropping suffices. Fortunately, most lookup are based on token
// value.
//
// If this was a binary search we could iterate between the lower and
// upper bounds.
if (entry.numAliases > 0) {
for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases;
aliasIndex++) {
// Skip Op prefix. Should this be encoded in the table instead?
const auto alias = entry.aliases[aliasIndex] + 2;
const size_t aliasLength = strlen(alias);
if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) {
*pEntry = &entry;
return SPV_SUCCESS;
}
}
}
}
}
return SPV_ERROR_INVALID_LOOKUP;
@ -133,8 +154,8 @@ spv_result_t spvOpcodeTableValueLookup(spv_target_env env,
const auto beg = table->entries;
const auto end = table->entries + table->count;
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {},
false, false, 0, nullptr, ~0u, ~0u};
spv_opcode_desc_t needle = {"", opcode, 0, nullptr, 0, {}, 0,
{}, false, false, 0, nullptr, ~0u, ~0u};
auto comp = [](const spv_opcode_desc_t& lhs, const spv_opcode_desc_t& rhs) {
return lhs.opcode < rhs.opcode;
@ -189,6 +210,7 @@ const char* spvOpcodeString(const uint32_t opcode) {
spv_opcode_desc_t needle = {"", static_cast<spv::Op>(opcode),
0, nullptr,
0, {},
0, {},
false, false,
0, nullptr,
~0u, ~0u};

View File

@ -64,11 +64,29 @@ spv_result_t spvOperandTableNameLookup(spv_target_env,
// We consider the current operand as available as long as
// it is in the grammar. It might not be *valid* to use,
// but that should be checked by the validator, not by parsing.
//
// Exact match case
if (nameLength == strlen(entry.name) &&
!strncmp(entry.name, name, nameLength)) {
*pEntry = &entry;
return SPV_SUCCESS;
}
// Check the aliases. Ideally we would have a version of the table sorted
// by name and then we could iterate between the lower and upper bounds to
// restrict the amount comparisons. Fortunately, name-based lookups are
// mostly restricted to the assembler.
if (entry.numAliases > 0) {
for (uint32_t aliasIndex = 0; aliasIndex < entry.numAliases;
aliasIndex++) {
const auto alias = entry.aliases[aliasIndex];
const size_t aliasLength = strlen(alias);
if (nameLength == aliasLength && !strncmp(name, alias, nameLength)) {
*pEntry = &entry;
return SPV_SUCCESS;
}
}
}
}
}
@ -83,7 +101,8 @@ spv_result_t spvOperandTableValueLookup(spv_target_env,
if (!table) return SPV_ERROR_INVALID_TABLE;
if (!pEntry) return SPV_ERROR_INVALID_POINTER;
spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr, {}, ~0u, ~0u};
spv_operand_desc_t needle = {"", value, 0, nullptr, 0, nullptr,
0, nullptr, {}, ~0u, ~0u};
auto comp = [](const spv_operand_desc_t& lhs, const spv_operand_desc_t& rhs) {
return lhs.value < rhs.value;

View File

@ -22,6 +22,8 @@
typedef struct spv_opcode_desc_t {
const char* name;
const spv::Op opcode;
const uint32_t numAliases;
const char** aliases;
const uint32_t numCapabilities;
const spv::Capability* capabilities;
// operandTypes[0..numTypes-1] describe logical operands for the instruction.
@ -47,6 +49,8 @@ typedef struct spv_opcode_desc_t {
typedef struct spv_operand_desc_t {
const char* name;
const uint32_t value;
const uint32_t numAliases;
const char** aliases;
const uint32_t numCapabilities;
const spv::Capability* capabilities;
// A set of extensions that enable this feature. If empty then this operand

View File

@ -70,6 +70,39 @@ def convert_max_required_version(version):
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.
@ -224,11 +257,12 @@ 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):
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
@ -238,6 +272,8 @@ class InstInitializer(object):
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)
@ -272,6 +308,7 @@ class InstInitializer(object):
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}',
@ -279,6 +316,8 @@ class InstInitializer(object):
'{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),
@ -336,6 +375,7 @@ def generate_instruction(inst, is_ext_inst):
"""
opname = inst.get('opname')
opcode = inst.get('opcode')
aliases = inst.get('aliases', [])
caps = inst.get('capabilities', [])
exts = inst.get('extensions', [])
operands = inst.get('operands', {})
@ -348,7 +388,7 @@ def generate_instruction(inst, is_ext_inst):
if is_ext_inst:
return str(ExtInstInitializer(opname, opcode, caps, operands))
else:
return str(InstInitializer(opname, caps, exts, operands, min_version, max_version))
return str(InstInitializer(opname, aliases, caps, exts, operands, min_version, max_version))
def generate_instruction_table(inst_table):
@ -364,6 +404,8 @@ def generate_instruction_table(inst_table):
"""
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(
@ -373,7 +415,7 @@ def generate_instruction_table(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))
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=""):
@ -405,12 +447,13 @@ def generate_extended_instruction_table(json_grammar, set_name, operand_kind_pre
class EnumerantInitializer(object):
"""Prints an enumerant as the initializer for spv_operand_desc_t."""
def __init__(self, enumerant, value, caps, exts, parameters, version, lastVersion):
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
@ -419,6 +462,8 @@ class EnumerantInitializer(object):
"""
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)
@ -428,13 +473,17 @@ class EnumerantInitializer(object):
self.lastVersion = convert_max_required_version(lastVersion)
def __str__(self):
template = ['{{"{enumerant}"', '{value}', '{num_caps}',
'{caps}', '{num_exts}', '{exts}',
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,
@ -456,6 +505,7 @@ def generate_enum_operand_kind_entry(entry, extension_map):
"""
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]
@ -471,7 +521,7 @@ def generate_enum_operand_kind_entry(entry, extension_map):
assert value is not None
return str(EnumerantInitializer(
enumerant, value, caps, exts, params, version, max_version))
enumerant, value, aliases, caps, exts, params, version, max_version))
def generate_enum_operand_kind(enum, synthetic_exts_list):
@ -516,7 +566,7 @@ def generate_enum_operand_kind(enum, synthetic_exts_list):
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, {}, SPV_SPIRV_VERSION_WORD(999,0), 0}']
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}', '}};']
@ -532,6 +582,11 @@ def generate_operand_kind_table(enums):
# 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', [])]
@ -566,7 +621,7 @@ def generate_operand_kind_table(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,))
return '\n\n'.join((aliases_arrays,) + (caps_arrays,) + (exts_arrays,) + enum_entries + (table,))
def get_extension_list(instructions, operand_kinds):

View File

@ -161,11 +161,17 @@ def main():
print('\n" Core instructions')
for inst in core["instructions"]:
EmitAsStatement(inst['opname'])
aliases = inst.get('aliases', [])
for alias in aliases:
EmitAsStatement(alias)
print('\n" Core operand enums')
for operand_kind in core["operand_kinds"]:
if 'enumerants' in operand_kind:
for e in operand_kind['enumerants']:
EmitAsEnumerant(e['enumerant'])
aliases = e.get('aliases', [])
for a in aliases:
EmitAsEnumerant(a)
if args.extinst_glsl_grammar is not None:
print('\n" GLSL.std.450 extended instructions')