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