| #!/usr/bin/env python3 | |
| # Copyright (c) 2017 Google Inc. | |
| # Licensed under the Apache License, Version 2.0 (the "License"); | |
| # you may not use this file except in compliance with the License. | |
| # You may obtain a copy of the License at | |
| # | |
| # http://www.apache.org/licenses/LICENSE-2.0 | |
| # | |
| # Unless required by applicable law or agreed to in writing, software | |
| # distributed under the License is distributed on an "AS IS" BASIS, | |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
| # See the License for the specific language governing permissions and | |
| # limitations under the License. | |
| """Generates language headers from a JSON grammar file""" | |
| import errno | |
| import json | |
| import os.path | |
| import re | |
| 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, copyright, instructions, operand_kinds, version = None, revision = None): | |
| self.name = 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_EXTINST_{}_H_'.format(grammar.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)) | |
| 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}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format( | |
| '{', prefix, var, value, '}') | |
| def enum_prefix(self, prefix, name): | |
| return 'enum {}{} {}'.format(prefix, name, '{') | |
| def enum_end(self, prefix, enum): | |
| return ' {}{}Max = 0x7ffffff\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-grammar', metavar='<path>', | |
| type=str, required=True, | |
| help='input JSON grammar file for extended instruction set') | |
| parser.add_argument('--extinst-output-path', metavar='<path>', | |
| type=str, required=True, | |
| help='Path 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()) | |
| grammar_name = os.path.splitext(os.path.basename(args.extinst_output_path))[0] | |
| grammar = ExtInstGrammar(name = grammar_name, | |
| copyright = grammar_json['copyright'], | |
| instructions = grammar_json['instructions'], | |
| operand_kinds = grammar_json['operand_kinds'], | |
| version = grammar_json['version'], | |
| revision = grammar_json['revision']) | |
| make_path_to_file(args.extinst_output_path) | |
| with open(args.extinst_output_path, 'w') as f: | |
| f.write(CGenerator().generate(grammar)) | |
| if __name__ == '__main__': | |
| main() |