1#!/usr/bin/env python 2# Copyright (c) 2016 Google Inc. 3 4# Licensed under the Apache License, Version 2.0 (the "License"); 5# you may not use this file except in compliance with the License. 6# You may obtain a copy of the License at 7# 8# http://www.apache.org/licenses/LICENSE-2.0 9# 10# Unless required by applicable law or agreed to in writing, software 11# distributed under the License is distributed on an "AS IS" BASIS, 12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13# See the License for the specific language governing permissions and 14# limitations under the License. 15"""Generates various info tables from SPIR-V JSON grammar.""" 16 17import errno 18import json 19import os.path 20import re 21 22# Prefix for all C variables generated by this script. 23PYGEN_VARIABLE_PREFIX = 'pygen_variable' 24 25# Extensions to recognize, but which don't necessarily come from the SPIR-V 26# core or KHR grammar files. Get this list from the SPIR-V registery web page. 27# NOTE: Only put things on this list if it is not in those grammar files. 28EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS = """ 29SPV_AMD_gcn_shader 30SPV_AMD_gpu_shader_half_float 31SPV_AMD_gpu_shader_int16 32SPV_AMD_shader_trinary_minmax 33SPV_KHR_non_semantic_info 34""" 35 36 37def make_path_to_file(f): 38 """Makes all ancestor directories to the given file, if they don't yet 39 exist. 40 41 Arguments: 42 f: The file whose ancestor directories are to be created. 43 """ 44 dir = os.path.dirname(os.path.abspath(f)) 45 try: 46 os.makedirs(dir) 47 except OSError as e: 48 if e.errno == errno.EEXIST and os.path.isdir(dir): 49 pass 50 else: 51 raise 52 53 54def convert_min_required_version(version): 55 """Converts the minimal required SPIR-V version encoded in the grammar to 56 the symbol in SPIRV-Tools.""" 57 if version is None: 58 return 'SPV_SPIRV_VERSION_WORD(1, 0)' 59 if version == 'None': 60 return '0xffffffffu' 61 return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) 62 63 64def convert_max_required_version(version): 65 """Converts the maximum required SPIR-V version encoded in the grammar to 66 the symbol in SPIRV-Tools.""" 67 if version is None: 68 return '0xffffffffu' 69 return 'SPV_SPIRV_VERSION_WORD({})'.format(version.replace('.', ',')) 70 71 72def compose_capability_list(caps): 73 """Returns a string containing a braced list of capabilities as enums. 74 75 Arguments: 76 - caps: a sequence of capability names 77 78 Returns: 79 a string containing the braced list of SpvCapability* enums named by caps. 80 """ 81 return '{' + ', '.join(['SpvCapability{}'.format(c) for c in caps]) + '}' 82 83 84def get_capability_array_name(caps): 85 """Returns the name of the array containing all the given capabilities. 86 87 Args: 88 - caps: a sequence of capability names 89 """ 90 if not caps: 91 return 'nullptr' 92 return '{}_caps_{}'.format(PYGEN_VARIABLE_PREFIX, ''.join(caps)) 93 94 95def generate_capability_arrays(caps): 96 """Returns the arrays of capabilities. 97 98 Arguments: 99 - caps: a sequence of sequence of capability names 100 """ 101 caps = sorted(set([tuple(c) for c in caps if c])) 102 arrays = [ 103 'static const SpvCapability {}[] = {};'.format( 104 get_capability_array_name(c), compose_capability_list(c)) 105 for c in caps] 106 return '\n'.join(arrays) 107 108 109def compose_extension_list(exts): 110 """Returns a string containing a braced list of extensions as enums. 111 112 Arguments: 113 - exts: a sequence of extension names 114 115 Returns: 116 a string containing the braced list of extensions named by exts. 117 """ 118 return '{' + ', '.join( 119 ['spvtools::Extension::k{}'.format(e) for e in exts]) + '}' 120 121 122def get_extension_array_name(extensions): 123 """Returns the name of the array containing all the given extensions. 124 125 Args: 126 - extensions: a sequence of extension names 127 """ 128 if not extensions: 129 return 'nullptr' 130 else: 131 return '{}_exts_{}'.format( 132 PYGEN_VARIABLE_PREFIX, ''.join(extensions)) 133 134 135def generate_extension_arrays(extensions): 136 """Returns the arrays of extensions. 137 138 Arguments: 139 - caps: a sequence of sequence of extension names 140 """ 141 extensions = sorted(set([tuple(e) for e in extensions if e])) 142 arrays = [ 143 'static const spvtools::Extension {}[] = {};'.format( 144 get_extension_array_name(e), compose_extension_list(e)) 145 for e in extensions] 146 return '\n'.join(arrays) 147 148 149def convert_operand_kind(operand_tuple): 150 """Returns the corresponding operand type used in spirv-tools for the given 151 operand kind and quantifier used in the JSON grammar. 152 153 Arguments: 154 - operand_tuple: a tuple of two elements: 155 - operand kind: used in the JSON grammar 156 - quantifier: '', '?', or '*' 157 158 Returns: 159 a string of the enumerant name in spv_operand_type_t 160 """ 161 kind, quantifier = operand_tuple 162 # The following cases are where we differ between the JSON grammar and 163 # spirv-tools. 164 if kind == 'IdResultType': 165 kind = 'TypeId' 166 elif kind == 'IdResult': 167 kind = 'ResultId' 168 elif kind == 'IdMemorySemantics' or kind == 'MemorySemantics': 169 kind = 'MemorySemanticsId' 170 elif kind == 'IdScope' or kind == 'Scope': 171 kind = 'ScopeId' 172 elif kind == 'IdRef': 173 kind = 'Id' 174 175 elif kind == 'ImageOperands': 176 kind = 'Image' 177 elif kind == 'Dim': 178 kind = 'Dimensionality' 179 elif kind == 'ImageFormat': 180 kind = 'SamplerImageFormat' 181 elif kind == 'KernelEnqueueFlags': 182 kind = 'KernelEnqFlags' 183 184 elif kind == 'LiteralExtInstInteger': 185 kind = 'ExtensionInstructionNumber' 186 elif kind == 'LiteralSpecConstantOpInteger': 187 kind = 'SpecConstantOpNumber' 188 elif kind == 'LiteralContextDependentNumber': 189 kind = 'TypedLiteralNumber' 190 191 elif kind == 'PairLiteralIntegerIdRef': 192 kind = 'LiteralIntegerId' 193 elif kind == 'PairIdRefLiteralInteger': 194 kind = 'IdLiteralInteger' 195 elif kind == 'PairIdRefIdRef': # Used by OpPhi in the grammar 196 kind = 'Id' 197 198 if kind == 'FPRoundingMode': 199 kind = 'FpRoundingMode' 200 elif kind == 'FPFastMathMode': 201 kind = 'FpFastMathMode' 202 203 if quantifier == '?': 204 kind = 'Optional{}'.format(kind) 205 elif quantifier == '*': 206 kind = 'Variable{}'.format(kind) 207 208 return 'SPV_OPERAND_TYPE_{}'.format( 209 re.sub(r'([a-z])([A-Z])', r'\1_\2', kind).upper()) 210 211 212class InstInitializer(object): 213 """Instances holds a SPIR-V instruction suitable for printing as the 214 initializer for spv_opcode_desc_t.""" 215 216 def __init__(self, opname, caps, exts, operands, version, lastVersion): 217 """Initialization. 218 219 Arguments: 220 - opname: opcode name (with the 'Op' prefix) 221 - caps: a sequence of capability names required by this opcode 222 - exts: a sequence of names of extensions enabling this enumerant 223 - operands: a sequence of (operand-kind, operand-quantifier) tuples 224 - version: minimal SPIR-V version required for this opcode 225 - lastVersion: last version of SPIR-V that includes this opcode 226 """ 227 228 assert opname.startswith('Op') 229 self.opname = opname[2:] # Remove the "Op" prefix. 230 self.num_caps = len(caps) 231 self.caps_mask = get_capability_array_name(caps) 232 self.num_exts = len(exts) 233 self.exts = get_extension_array_name(exts) 234 self.operands = [convert_operand_kind(o) for o in operands] 235 236 self.fix_syntax() 237 238 operands = [o[0] for o in operands] 239 self.ref_type_id = 'IdResultType' in operands 240 self.def_result_id = 'IdResult' in operands 241 242 self.version = convert_min_required_version(version) 243 self.lastVersion = convert_max_required_version(lastVersion) 244 245 def fix_syntax(self): 246 """Fix an instruction's syntax, adjusting for differences between the 247 officially released grammar and how SPIRV-Tools uses the grammar. 248 249 Fixes: 250 - ExtInst should not end with SPV_OPERAND_VARIABLE_ID. 251 https://github.com/KhronosGroup/SPIRV-Tools/issues/233 252 """ 253 if (self.opname == 'ExtInst' 254 and self.operands[-1] == 'SPV_OPERAND_TYPE_VARIABLE_ID'): 255 self.operands.pop() 256 257 def __str__(self): 258 template = ['{{"{opname}"', 'SpvOp{opname}', 259 '{num_caps}', '{caps_mask}', 260 '{num_operands}', '{{{operands}}}', 261 '{def_result_id}', '{ref_type_id}', 262 '{num_exts}', '{exts}', 263 '{min_version}', '{max_version}}}'] 264 return ', '.join(template).format( 265 opname=self.opname, 266 num_caps=self.num_caps, 267 caps_mask=self.caps_mask, 268 num_operands=len(self.operands), 269 operands=', '.join(self.operands), 270 def_result_id=(1 if self.def_result_id else 0), 271 ref_type_id=(1 if self.ref_type_id else 0), 272 num_exts=self.num_exts, 273 exts=self.exts, 274 min_version=self.version, 275 max_version=self.lastVersion) 276 277 278class ExtInstInitializer(object): 279 """Instances holds a SPIR-V extended instruction suitable for printing as 280 the initializer for spv_ext_inst_desc_t.""" 281 282 def __init__(self, opname, opcode, caps, operands): 283 """Initialization. 284 285 Arguments: 286 - opname: opcode name 287 - opcode: enumerant value for this opcode 288 - caps: a sequence of capability names required by this opcode 289 - operands: a sequence of (operand-kind, operand-quantifier) tuples 290 """ 291 self.opname = opname 292 self.opcode = opcode 293 self.num_caps = len(caps) 294 self.caps_mask = get_capability_array_name(caps) 295 self.operands = [convert_operand_kind(o) for o in operands] 296 self.operands.append('SPV_OPERAND_TYPE_NONE') 297 298 def __str__(self): 299 template = ['{{"{opname}"', '{opcode}', '{num_caps}', '{caps_mask}', 300 '{{{operands}}}}}'] 301 return ', '.join(template).format( 302 opname=self.opname, 303 opcode=self.opcode, 304 num_caps=self.num_caps, 305 caps_mask=self.caps_mask, 306 operands=', '.join(self.operands)) 307 308 309def generate_instruction(inst, is_ext_inst): 310 """Returns the C initializer for the given SPIR-V instruction. 311 312 Arguments: 313 - inst: a dict containing information about a SPIR-V instruction 314 - is_ext_inst: a bool indicating whether |inst| is an extended 315 instruction. 316 317 Returns: 318 a string containing the C initializer for spv_opcode_desc_t or 319 spv_ext_inst_desc_t 320 """ 321 opname = inst.get('opname') 322 opcode = inst.get('opcode') 323 caps = inst.get('capabilities', []) 324 exts = inst.get('extensions', []) 325 operands = inst.get('operands', {}) 326 operands = [(o['kind'], o.get('quantifier', '')) for o in operands] 327 min_version = inst.get('version', None) 328 max_version = inst.get('lastVersion', None) 329 330 assert opname is not None 331 332 if is_ext_inst: 333 return str(ExtInstInitializer(opname, opcode, caps, operands)) 334 else: 335 return str(InstInitializer(opname, caps, exts, operands, min_version, max_version)) 336 337 338def generate_instruction_table(inst_table): 339 """Returns the info table containing all SPIR-V instructions, sorted by 340 opcode, and prefixed by capability arrays. 341 342 Note: 343 - the built-in sorted() function is guaranteed to be stable. 344 https://docs.python.org/3/library/functions.html#sorted 345 346 Arguments: 347 - inst_table: a list containing all SPIR-V instructions. 348 """ 349 inst_table = sorted(inst_table, key=lambda k: (k['opcode'], k['opname'])) 350 351 caps_arrays = generate_capability_arrays( 352 [inst.get('capabilities', []) for inst in inst_table]) 353 exts_arrays = generate_extension_arrays( 354 [inst.get('extensions', []) for inst in inst_table]) 355 356 insts = [generate_instruction(inst, False) for inst in inst_table] 357 insts = ['static const spv_opcode_desc_t kOpcodeTableEntries[] = {{\n' 358 ' {}\n}};'.format(',\n '.join(insts))] 359 360 return '{}\n\n{}\n\n{}'.format(caps_arrays, exts_arrays, '\n'.join(insts)) 361 362 363def generate_extended_instruction_table(json_grammar, set_name, operand_kind_prefix=""): 364 """Returns the info table containing all SPIR-V extended instructions, 365 sorted by opcode, and prefixed by capability arrays. 366 367 Arguments: 368 - inst_table: a list containing all SPIR-V instructions. 369 - set_name: the name of the extended instruction set. 370 - operand_kind_prefix: the prefix, if any, to add to the front 371 of operand kind names. 372 """ 373 if operand_kind_prefix: 374 prefix_operand_kind_names(operand_kind_prefix, json_grammar) 375 376 inst_table = json_grammar["instructions"] 377 set_name = set_name.replace(".", "_") 378 379 inst_table = sorted(inst_table, key=lambda k: k['opcode']) 380 caps = [inst.get('capabilities', []) for inst in inst_table] 381 caps_arrays = generate_capability_arrays(caps) 382 insts = [generate_instruction(inst, True) for inst in inst_table] 383 insts = ['static const spv_ext_inst_desc_t {}_entries[] = {{\n' 384 ' {}\n}};'.format(set_name, ',\n '.join(insts))] 385 386 return '{}\n\n{}'.format(caps_arrays, '\n'.join(insts)) 387 388 389class EnumerantInitializer(object): 390 """Prints an enumerant as the initializer for spv_operand_desc_t.""" 391 392 def __init__(self, enumerant, value, caps, exts, parameters, version, lastVersion): 393 """Initialization. 394 395 Arguments: 396 - enumerant: enumerant name 397 - value: enumerant value 398 - caps: a sequence of capability names required by this enumerant 399 - exts: a sequence of names of extensions enabling this enumerant 400 - parameters: a sequence of (operand-kind, operand-quantifier) tuples 401 - version: minimal SPIR-V version required for this opcode 402 - lastVersion: last SPIR-V version this opode appears 403 """ 404 self.enumerant = enumerant 405 self.value = value 406 self.num_caps = len(caps) 407 self.caps = get_capability_array_name(caps) 408 self.num_exts = len(exts) 409 self.exts = get_extension_array_name(exts) 410 self.parameters = [convert_operand_kind(p) for p in parameters] 411 self.version = convert_min_required_version(version) 412 self.lastVersion = convert_max_required_version(lastVersion) 413 414 def __str__(self): 415 template = ['{{"{enumerant}"', '{value}', '{num_caps}', 416 '{caps}', '{num_exts}', '{exts}', 417 '{{{parameters}}}', '{min_version}', 418 '{max_version}}}'] 419 return ', '.join(template).format( 420 enumerant=self.enumerant, 421 value=self.value, 422 num_caps=self.num_caps, 423 caps=self.caps, 424 num_exts=self.num_exts, 425 exts=self.exts, 426 parameters=', '.join(self.parameters), 427 min_version=self.version, 428 max_version=self.lastVersion) 429 430 431def generate_enum_operand_kind_entry(entry, extension_map): 432 """Returns the C initializer for the given operand enum entry. 433 434 Arguments: 435 - entry: a dict containing information about an enum entry 436 - extension_map: a dict mapping enum value to list of extensions 437 438 Returns: 439 a string containing the C initializer for spv_operand_desc_t 440 """ 441 enumerant = entry.get('enumerant') 442 value = entry.get('value') 443 caps = entry.get('capabilities', []) 444 if value in extension_map: 445 exts = extension_map[value] 446 else: 447 exts = [] 448 params = entry.get('parameters', []) 449 params = [p.get('kind') for p in params] 450 params = zip(params, [''] * len(params)) 451 version = entry.get('version', None) 452 max_version = entry.get('lastVersion', None) 453 454 assert enumerant is not None 455 assert value is not None 456 457 return str(EnumerantInitializer( 458 enumerant, value, caps, exts, params, version, max_version)) 459 460 461def generate_enum_operand_kind(enum, synthetic_exts_list): 462 """Returns the C definition for the given operand kind. 463 It's a static const named array of spv_operand_desc_t. 464 465 Also appends to |synthetic_exts_list| a list of extension lists 466 used. 467 """ 468 kind = enum.get('kind') 469 assert kind is not None 470 471 # Sort all enumerants according to their values, but otherwise 472 # preserve their order so the first name listed in the grammar 473 # as the preferred name for disassembly. 474 if enum.get('category') == 'ValueEnum': 475 def functor(k): return (k['value']) 476 else: 477 def functor(k): return (int(k['value'], 16)) 478 entries = sorted(enum.get('enumerants', []), key=functor) 479 480 # SubgroupEqMask and SubgroupEqMaskKHR are the same number with 481 # same semantics, but one has no extension list while the other 482 # does. Both should have the extension list. 483 # So create a mapping from enum value to the union of the extensions 484 # across all those grammar entries. Preserve order. 485 extension_map = {} 486 for e in entries: 487 value = e.get('value') 488 extension_map[value] = [] 489 for e in entries: 490 value = e.get('value') 491 exts = e.get('extensions', []) 492 for ext in exts: 493 if ext not in extension_map[value]: 494 extension_map[value].append(ext) 495 synthetic_exts_list.extend(extension_map.values()) 496 497 name = '{}_{}Entries'.format(PYGEN_VARIABLE_PREFIX, kind) 498 entries = [' {}'.format(generate_enum_operand_kind_entry(e, extension_map)) 499 for e in entries] 500 501 template = ['static const spv_operand_desc_t {name}[] = {{', 502 '{entries}', '}};'] 503 entries = '\n'.join(template).format( 504 name=name, 505 entries=',\n'.join(entries)) 506 507 return kind, name, entries 508 509 510def generate_operand_kind_table(enums): 511 """Returns the info table containing all SPIR-V operand kinds.""" 512 # We only need to output info tables for those operand kinds that are enums. 513 enums = [e for e in enums if e.get('category') in ['ValueEnum', 'BitEnum']] 514 515 caps = [entry.get('capabilities', []) 516 for enum in enums 517 for entry in enum.get('enumerants', [])] 518 caps_arrays = generate_capability_arrays(caps) 519 520 exts = [entry.get('extensions', []) 521 for enum in enums 522 for entry in enum.get('enumerants', [])] 523 enums = [generate_enum_operand_kind(e, exts) for e in enums] 524 exts_arrays = generate_extension_arrays(exts) 525 526 # We have three operand kinds that requires their optional counterpart to 527 # exist in the operand info table. 528 three_optional_enums = ['ImageOperands', 'AccessQualifier', 'MemoryAccess'] 529 three_optional_enums = [e for e in enums if e[0] in three_optional_enums] 530 enums.extend(three_optional_enums) 531 532 enum_kinds, enum_names, enum_entries = zip(*enums) 533 # Mark the last three as optional ones. 534 enum_quantifiers = [''] * (len(enums) - 3) + ['?'] * 3 535 # And we don't want redefinition of them. 536 enum_entries = enum_entries[:-3] 537 enum_kinds = [convert_operand_kind(e) 538 for e in zip(enum_kinds, enum_quantifiers)] 539 table_entries = zip(enum_kinds, enum_names, enum_names) 540 table_entries = [' {{{}, ARRAY_SIZE({}), {}}}'.format(*e) 541 for e in table_entries] 542 543 template = [ 544 'static const spv_operand_desc_group_t {p}_OperandInfoTable[] = {{', 545 '{enums}', '}};'] 546 table = '\n'.join(template).format( 547 p=PYGEN_VARIABLE_PREFIX, enums=',\n'.join(table_entries)) 548 549 return '\n\n'.join((caps_arrays,) + (exts_arrays,) + enum_entries + (table,)) 550 551 552def get_extension_list(instructions, operand_kinds): 553 """Returns extensions as an alphabetically sorted list of strings.""" 554 555 things_with_an_extensions_field = [item for item in instructions] 556 557 enumerants = sum([item.get('enumerants', []) 558 for item in operand_kinds], []) 559 560 things_with_an_extensions_field.extend(enumerants) 561 562 extensions = sum([item.get('extensions', []) 563 for item in things_with_an_extensions_field 564 if item.get('extensions')], []) 565 566 for item in EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split(): 567 # If it's already listed in a grammar, then don't put it in the 568 # special exceptions list. 569 assert item not in extensions, 'Extension %s is already in a grammar file' % item 570 571 extensions.extend( 572 EXTENSIONS_FROM_SPIRV_REGISTRY_AND_NOT_FROM_GRAMMARS.split()) 573 574 # Validator would ignore type declaration unique check. Should only be used 575 # for legacy autogenerated test files containing multiple instances of the 576 # same type declaration, if fixing the test by other methods is too 577 # difficult. Shouldn't be used for any other reasons. 578 extensions.append('SPV_VALIDATOR_ignore_type_decl_unique') 579 580 return sorted(set(extensions)) 581 582 583def get_capabilities(operand_kinds): 584 """Returns capabilities as a list of JSON objects, in order of 585 appearance.""" 586 enumerants = sum([item.get('enumerants', []) for item in operand_kinds 587 if item.get('kind') in ['Capability']], []) 588 return enumerants 589 590 591def generate_extension_enum(extensions): 592 """Returns enumeration containing extensions declared in the grammar.""" 593 return ',\n'.join(['k' + extension for extension in extensions]) 594 595 596def generate_extension_to_string_mapping(extensions): 597 """Returns mapping function from extensions to corresponding strings.""" 598 function = 'const char* ExtensionToString(Extension extension) {\n' 599 function += ' switch (extension) {\n' 600 template = ' case Extension::k{extension}:\n' \ 601 ' return "{extension}";\n' 602 function += ''.join([template.format(extension=extension) 603 for extension in extensions]) 604 function += ' };\n\n return "";\n}' 605 return function 606 607 608def generate_string_to_extension_mapping(extensions): 609 """Returns mapping function from strings to corresponding extensions.""" 610 611 function = ''' 612 bool GetExtensionFromString(const char* str, Extension* extension) {{ 613 static const char* known_ext_strs[] = {{ {strs} }}; 614 static const Extension known_ext_ids[] = {{ {ids} }}; 615 const auto b = std::begin(known_ext_strs); 616 const auto e = std::end(known_ext_strs); 617 const auto found = std::equal_range( 618 b, e, str, [](const char* str1, const char* str2) {{ 619 return std::strcmp(str1, str2) < 0; 620 }}); 621 if (found.first == e || found.first == found.second) return false; 622 623 *extension = known_ext_ids[found.first - b]; 624 return true; 625 }} 626 '''.format(strs=', '.join(['"{}"'.format(e) for e in extensions]), 627 ids=', '.join(['Extension::k{}'.format(e) for e in extensions])) 628 629 return function 630 631 632def generate_capability_to_string_mapping(operand_kinds): 633 """Returns mapping function from capabilities to corresponding strings. 634 635 We take care to avoid emitting duplicate values. 636 """ 637 function = 'const char* CapabilityToString(SpvCapability capability) {\n' 638 function += ' switch (capability) {\n' 639 template = ' case SpvCapability{capability}:\n' \ 640 ' return "{capability}";\n' 641 emitted = set() # The values of capabilities we already have emitted 642 for capability in get_capabilities(operand_kinds): 643 value = capability.get('value') 644 if value not in emitted: 645 emitted.add(value) 646 function += template.format(capability=capability.get('enumerant')) 647 function += ' case SpvCapabilityMax:\n' \ 648 ' assert(0 && "Attempting to convert SpvCapabilityMax to string");\n' \ 649 ' return "";\n' 650 function += ' };\n\n return "";\n}' 651 return function 652 653 654def generate_all_string_enum_mappings(extensions, operand_kinds): 655 """Returns all string-to-enum / enum-to-string mapping tables.""" 656 tables = [] 657 tables.append(generate_extension_to_string_mapping(extensions)) 658 tables.append(generate_string_to_extension_mapping(extensions)) 659 tables.append(generate_capability_to_string_mapping(operand_kinds)) 660 return '\n\n'.join(tables) 661 662 663def precondition_operand_kinds(operand_kinds): 664 """For operand kinds that have the same number, make sure they all have the 665 same extension list.""" 666 667 # Map operand kind and value to list of the union of extensions 668 # for same-valued enumerants. 669 exts = {} 670 for kind_entry in operand_kinds: 671 kind = kind_entry.get('kind') 672 for enum_entry in kind_entry.get('enumerants', []): 673 value = enum_entry.get('value') 674 key = kind + '.' + str(value) 675 if key in exts: 676 exts[key].extend(enum_entry.get('extensions', [])) 677 else: 678 exts[key] = enum_entry.get('extensions', []) 679 exts[key] = sorted(set(exts[key])) 680 681 # Now make each entry the same list. 682 for kind_entry in operand_kinds: 683 kind = kind_entry.get('kind') 684 for enum_entry in kind_entry.get('enumerants', []): 685 value = enum_entry.get('value') 686 key = kind + '.' + str(value) 687 if len(exts[key]) > 0: 688 enum_entry['extensions'] = exts[key] 689 690 return operand_kinds 691 692 693def prefix_operand_kind_names(prefix, json_dict): 694 """Modifies json_dict, by prefixing all the operand kind names 695 with the given prefix. Also modifies their uses in the instructions 696 to match. 697 """ 698 699 old_to_new = {} 700 for operand_kind in json_dict["operand_kinds"]: 701 old_name = operand_kind["kind"] 702 new_name = prefix + old_name 703 operand_kind["kind"] = new_name 704 old_to_new[old_name] = new_name 705 706 for instruction in json_dict["instructions"]: 707 for operand in instruction.get("operands", []): 708 replacement = old_to_new.get(operand["kind"]) 709 if replacement is not None: 710 operand["kind"] = replacement 711 712 713def main(): 714 import argparse 715 parser = argparse.ArgumentParser(description='Generate SPIR-V info tables') 716 717 parser.add_argument('--spirv-core-grammar', metavar='<path>', 718 type=str, required=False, 719 help='input JSON grammar file for core SPIR-V ' 720 'instructions') 721 parser.add_argument('--extinst-debuginfo-grammar', metavar='<path>', 722 type=str, required=False, default=None, 723 help='input JSON grammar file for DebugInfo extended ' 724 'instruction set') 725 parser.add_argument('--extinst-cldebuginfo100-grammar', metavar='<path>', 726 type=str, required=False, default=None, 727 help='input JSON grammar file for OpenCL.DebugInfo.100 ' 728 'extended instruction set') 729 parser.add_argument('--extinst-glsl-grammar', metavar='<path>', 730 type=str, required=False, default=None, 731 help='input JSON grammar file for GLSL extended ' 732 'instruction set') 733 parser.add_argument('--extinst-opencl-grammar', metavar='<path>', 734 type=str, required=False, default=None, 735 help='input JSON grammar file for OpenCL extended ' 736 'instruction set') 737 738 parser.add_argument('--core-insts-output', metavar='<path>', 739 type=str, required=False, default=None, 740 help='output file for core SPIR-V instructions') 741 parser.add_argument('--glsl-insts-output', metavar='<path>', 742 type=str, required=False, default=None, 743 help='output file for GLSL extended instruction set') 744 parser.add_argument('--opencl-insts-output', metavar='<path>', 745 type=str, required=False, default=None, 746 help='output file for OpenCL extended instruction set') 747 parser.add_argument('--operand-kinds-output', metavar='<path>', 748 type=str, required=False, default=None, 749 help='output file for operand kinds') 750 parser.add_argument('--extension-enum-output', metavar='<path>', 751 type=str, required=False, default=None, 752 help='output file for extension enumeration') 753 parser.add_argument('--enum-string-mapping-output', metavar='<path>', 754 type=str, required=False, default=None, 755 help='output file for enum-string mappings') 756 parser.add_argument('--extinst-vendor-grammar', metavar='<path>', 757 type=str, required=False, default=None, 758 help='input JSON grammar file for vendor extended ' 759 'instruction set'), 760 parser.add_argument('--vendor-insts-output', metavar='<path>', 761 type=str, required=False, default=None, 762 help='output file for vendor extended instruction set') 763 parser.add_argument('--vendor-operand-kind-prefix', metavar='<string>', 764 type=str, required=False, default=None, 765 help='prefix for operand kinds (to disambiguate operand type enums)') 766 args = parser.parse_args() 767 768 # The GN build system needs this because it doesn't handle quoting 769 # empty string arguments well. 770 if args.vendor_operand_kind_prefix == "...nil...": 771 args.vendor_operand_kind_prefix = "" 772 773 if (args.core_insts_output is None) != \ 774 (args.operand_kinds_output is None): 775 print('error: --core-insts-output and --operand-kinds-output ' 776 'should be specified together.') 777 exit(1) 778 if args.operand_kinds_output and not (args.spirv_core_grammar and 779 args.extinst_debuginfo_grammar and 780 args.extinst_cldebuginfo100_grammar): 781 print('error: --operand-kinds-output requires --spirv-core-grammar ' 782 'and --extinst-debuginfo-grammar ' 783 'and --extinst-cldebuginfo100-grammar') 784 exit(1) 785 if (args.glsl_insts_output is None) != \ 786 (args.extinst_glsl_grammar is None): 787 print('error: --glsl-insts-output and --extinst-glsl-grammar ' 788 'should be specified together.') 789 exit(1) 790 if (args.opencl_insts_output is None) != \ 791 (args.extinst_opencl_grammar is None): 792 print('error: --opencl-insts-output and --extinst-opencl-grammar ' 793 'should be specified together.') 794 exit(1) 795 if (args.vendor_insts_output is None) != \ 796 (args.extinst_vendor_grammar is None): 797 print('error: --vendor-insts-output and ' 798 '--extinst-vendor-grammar should be specified together.') 799 exit(1) 800 if all([args.core_insts_output is None, 801 args.glsl_insts_output is None, 802 args.opencl_insts_output is None, 803 args.vendor_insts_output is None, 804 args.extension_enum_output is None, 805 args.enum_string_mapping_output is None]): 806 print('error: at least one output should be specified.') 807 exit(1) 808 809 if args.spirv_core_grammar is not None: 810 with open(args.spirv_core_grammar) as json_file: 811 core_grammar = json.loads(json_file.read()) 812 with open(args.extinst_debuginfo_grammar) as debuginfo_json_file: 813 debuginfo_grammar = json.loads(debuginfo_json_file.read()) 814 with open(args.extinst_cldebuginfo100_grammar) as cldebuginfo100_json_file: 815 cldebuginfo100_grammar = json.loads(cldebuginfo100_json_file.read()) 816 prefix_operand_kind_names("CLDEBUG100_", cldebuginfo100_grammar) 817 instructions = [] 818 instructions.extend(core_grammar['instructions']) 819 instructions.extend(debuginfo_grammar['instructions']) 820 instructions.extend(cldebuginfo100_grammar['instructions']) 821 operand_kinds = [] 822 operand_kinds.extend(core_grammar['operand_kinds']) 823 operand_kinds.extend(debuginfo_grammar['operand_kinds']) 824 operand_kinds.extend(cldebuginfo100_grammar['operand_kinds']) 825 extensions = get_extension_list(instructions, operand_kinds) 826 operand_kinds = precondition_operand_kinds(operand_kinds) 827 if args.core_insts_output is not None: 828 make_path_to_file(args.core_insts_output) 829 make_path_to_file(args.operand_kinds_output) 830 with open(args.core_insts_output, 'w') as f: 831 f.write(generate_instruction_table( 832 core_grammar['instructions'])) 833 with open(args.operand_kinds_output, 'w') as f: 834 f.write(generate_operand_kind_table(operand_kinds)) 835 if args.extension_enum_output is not None: 836 make_path_to_file(args.extension_enum_output) 837 with open(args.extension_enum_output, 'w') as f: 838 f.write(generate_extension_enum(extensions)) 839 if args.enum_string_mapping_output is not None: 840 make_path_to_file(args.enum_string_mapping_output) 841 with open(args.enum_string_mapping_output, 'w') as f: 842 f.write(generate_all_string_enum_mappings( 843 extensions, operand_kinds)) 844 845 if args.extinst_glsl_grammar is not None: 846 with open(args.extinst_glsl_grammar) as json_file: 847 grammar = json.loads(json_file.read()) 848 make_path_to_file(args.glsl_insts_output) 849 with open(args.glsl_insts_output, 'w') as f: 850 f.write(generate_extended_instruction_table( 851 grammar, 'glsl')) 852 853 if args.extinst_opencl_grammar is not None: 854 with open(args.extinst_opencl_grammar) as json_file: 855 grammar = json.loads(json_file.read()) 856 make_path_to_file(args.opencl_insts_output) 857 with open(args.opencl_insts_output, 'w') as f: 858 f.write(generate_extended_instruction_table( 859 grammar, 'opencl')) 860 861 if args.extinst_vendor_grammar is not None: 862 with open(args.extinst_vendor_grammar) as json_file: 863 grammar = json.loads(json_file.read()) 864 make_path_to_file(args.vendor_insts_output) 865 name = args.extinst_vendor_grammar 866 start = name.find('extinst.') + len('extinst.') 867 name = name[start:-len('.grammar.json')].replace('-', '_') 868 with open(args.vendor_insts_output, 'w') as f: 869 f.write(generate_extended_instruction_table( 870 grammar, name, args.vendor_operand_kind_prefix)) 871 872 873if __name__ == '__main__': 874 main() 875