blob: 0c5bc8298e0e6c0757fded0e3730583d629a8d4b [file]
#!/usr/bin/env python3
# Copyright: 2017-2024 Google LLC
# SPDX-License-Identifier: MIT
#
# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
# https://www.khronos.org/registry/
"""Generates a C language headers from a SPIR-V JSON grammar file"""
import errno
import json
import os.path
import re
DEFAULT_COPYRIGHT="""SPDX-FileCopyrightText: 2020-2024 The Khronos Group Inc.
SPDX-License-Identifier: MIT
MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
https://www.khronos.org/registry/
""".split('\n')
def make_path_to_file(f):
"""Makes all ancestor directories to the given file, if they
don't yet exist.
Arguments:
f: The file whose ancestor directories are to be created.
"""
dir = os.path.dirname(os.path.abspath(f))
try:
os.makedirs(dir)
except OSError as e:
if e.errno == errno.EEXIST and os.path.isdir(dir):
pass
else:
raise
class ExtInstGrammar:
"""The grammar for an extended instruction set"""
def __init__(self, name, guard_name, copyright, instructions, operand_kinds, version = None, revision = None):
self.name = name
self.guard_name = guard_name
self.copyright = copyright
self.instructions = instructions
self.operand_kinds = operand_kinds
self.version = version
self.revision = revision
class LangGenerator:
"""A language-specific generator"""
def __init__(self):
self.upper_case_initial = re.compile('^[A-Z]')
pass
def comment_prefix(self):
return ""
def namespace_prefix(self):
return ""
def uses_guards(self):
return False
def cpp_guard_preamble(self):
return ""
def cpp_guard_postamble(self):
return ""
def enum_value(self, prefix, name, value):
if self.upper_case_initial.match(name):
use_name = name
else:
use_name = '_' + name
return " {}{} = {},".format(prefix, use_name, value)
def generate(self, grammar):
"""Returns a string that is the language-specific header for the given grammar"""
parts = []
if grammar.copyright:
parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright])
parts.append('')
guard = 'SPIRV_UNIFIED1_{}_H_'.format(grammar.guard_name)
if self.uses_guards:
parts.append('#ifndef {}'.format(guard))
parts.append('#define {}'.format(guard))
parts.append('')
parts.append(self.cpp_guard_preamble())
if grammar.version:
parts.append(self.const_definition(grammar.name, 'Version', grammar.version))
if grammar.revision is not None:
parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision))
parts.append('')
if grammar.instructions:
parts.append(self.enum_prefix(grammar.name, 'Instructions'))
for inst in grammar.instructions:
parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode']))
parts.append(self.enum_end(grammar.name, 'Instructions'))
parts.append('')
if grammar.operand_kinds:
for kind in grammar.operand_kinds:
parts.append(self.enum_prefix(grammar.name, kind['kind']))
for e in kind['enumerants']:
parts.append(self.enum_value(grammar.name, e['enumerant'], e['value']))
parts.append(self.enum_end(grammar.name, kind['kind']))
parts.append('')
parts.append(self.cpp_guard_postamble())
if self.uses_guards:
parts.append('#endif // {}'.format(guard))
# Ensre the file ends in an end of line
parts.append('')
return '\n'.join(parts)
class CLikeGenerator(LangGenerator):
def uses_guards(self):
return True
def comment_prefix(self):
return "// "
def const_definition(self, prefix, var, value):
# Use an anonymous enum. Don't use a static const int variable because
# that can bloat binary size.
return 'enum {0}{1}{2}{3} = {4},{1}{2}{3}_BitWidthPadding = 0x7fffffff{5};'.format(
'{', '\n ', prefix, var, value, '\n}')
def enum_prefix(self, prefix, name):
return 'enum {}{} {}'.format(prefix, name, '{')
def enum_end(self, prefix, enum):
return ' {}{}Max = 0x7fffffff\n{};\n'.format(prefix, enum, '}')
def cpp_guard_preamble(self):
return '#ifdef __cplusplus\nextern "C" {\n#endif\n'
def cpp_guard_postamble(self):
return '#ifdef __cplusplus\n}\n#endif\n'
class CGenerator(CLikeGenerator):
pass
def main():
import argparse
parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
parser.add_argument('--extinst-name',
type=str, required=True,
help='The name to use in tokens')
parser.add_argument('--extinst-grammar', metavar='<path>',
type=str, required=True,
help='input JSON grammar file for extended instruction set')
parser.add_argument('--extinst-output-base', metavar='<path>',
type=str, required=True,
help='Basename of the language-specific output file.')
args = parser.parse_args()
with open(args.extinst_grammar) as json_file:
grammar_json = json.loads(json_file.read())
if 'copyright' in grammar_json:
copyright = grammar_json['copyright']
else:
copyright = DEFAULT_COPYRIGHT
if 'version' in grammar_json:
version = grammar_json['version']
else:
version = 0
if 'operand_kinds' in grammar_json:
operand_kinds = grammar_json['operand_kinds']
else:
operand_kinds = []
sanitized_guard_name = args.extinst_output_base.replace('.', '_')
grammar = ExtInstGrammar(name = args.extinst_name,
guard_name = sanitized_guard_name,
copyright = copyright,
instructions = grammar_json['instructions'],
operand_kinds = operand_kinds,
version = version,
revision = grammar_json['revision'])
make_path_to_file(args.extinst_output_base)
with open(args.extinst_output_base + '.h', 'w') as f:
f.write(CGenerator().generate(grammar))
if __name__ == '__main__':
main()