diff --git a/DEPS b/DEPS index fc50180f7..da4846c75 100644 --- a/DEPS +++ b/DEPS @@ -14,7 +14,7 @@ vars = { 're2_revision': '6dcd83d60f7944926bfd308cc13979fc53dd69ca', - 'spirv_headers_revision': 'a62b032007b2e7a69f24a195cbfbd0cf22d31bb0', + 'spirv_headers_revision': 'd92cf88c371424591115a87499009dfad41b669c', } deps = { diff --git a/source/opcode.cpp b/source/opcode.cpp index 32fa10d15..ea03bd671 100644 --- a/source/opcode.cpp +++ b/source/opcode.cpp @@ -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,13 +110,34 @@ 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) && - !strncmp(name, entry.name, nameLength)) { - // NOTE: Found out Opcode! - *pEntry = &entry; - return SPV_SUCCESS; + 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)) { + *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; + } + } + } } } @@ -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(opcode), 0, nullptr, 0, {}, + 0, {}, false, false, 0, nullptr, ~0u, ~0u}; diff --git a/source/operand.cpp b/source/operand.cpp index 508a5d1d0..9bee647fc 100644 --- a/source/operand.cpp +++ b/source/operand.cpp @@ -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; diff --git a/source/table.h b/source/table.h index 4f1dc1f84..47625c5fb 100644 --- a/source/table.h +++ b/source/table.h @@ -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 diff --git a/utils/generate_grammar_tables.py b/utils/generate_grammar_tables.py index fd0bcabba..2b695b731 100755 --- a/utils/generate_grammar_tables.py +++ b/utils/generate_grammar_tables.py @@ -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): diff --git a/utils/generate_vim_syntax.py b/utils/generate_vim_syntax.py index 5c9c6b21a..1d156a0cd 100755 --- a/utils/generate_vim_syntax.py +++ b/utils/generate_vim_syntax.py @@ -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')