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