generate_grammar_tables.py 37 KB

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