1#!/usr/bin/env python 2# Copyright (c) 2017 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 language headers from a JSON grammar file""" 16 17from __future__ import print_function 18 19import errno 20import json 21import os.path 22import re 23 24 25def make_path_to_file(f): 26 """Makes all ancestor directories to the given file, if they 27 don't yet exist. 28 29 Arguments: 30 f: The file whose ancestor directories are to be created. 31 """ 32 dir = os.path.dirname(os.path.abspath(f)) 33 try: 34 os.makedirs(dir) 35 except OSError as e: 36 if e.errno == errno.EEXIST and os.path.isdir(dir): 37 pass 38 else: 39 raise 40 41class ExtInstGrammar: 42 """The grammar for an extended instruction set""" 43 44 def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None): 45 self.name = name 46 self.copyright = copyright 47 self.instructions = instructions 48 self.operand_kinds = operand_kinds 49 self.version = version 50 self.revision = revision 51 52 53class LangGenerator: 54 """A language-specific generator""" 55 56 def __init__(self): 57 self.upper_case_initial = re.compile('^[A-Z]') 58 pass 59 60 def comment_prefix(self): 61 return "" 62 63 def namespace_prefix(self): 64 return "" 65 66 def uses_guards(self): 67 return False 68 69 def cpp_guard_preamble(self): 70 return "" 71 72 def cpp_guard_postamble(self): 73 return "" 74 75 def enum_value(self, prefix, name, value): 76 if self.upper_case_initial.match(name): 77 use_name = name 78 else: 79 use_name = '_' + name 80 81 return " {}{} = {},".format(prefix, use_name, value) 82 83 def generate(self, grammar): 84 """Returns a string that is the language-specific header for the given grammar""" 85 86 parts = [] 87 if grammar.copyright: 88 parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright]) 89 parts.append('') 90 91 guard = 'SPIRV_EXTINST_{}_H_'.format(grammar.name) 92 if self.uses_guards: 93 parts.append('#ifndef {}'.format(guard)) 94 parts.append('#define {}'.format(guard)) 95 parts.append('') 96 97 parts.append(self.cpp_guard_preamble()) 98 99 if grammar.version: 100 parts.append(self.const_definition(grammar.name, 'Version', grammar.version)) 101 102 if grammar.revision is not None: 103 parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision)) 104 105 parts.append('') 106 107 if grammar.instructions: 108 parts.append(self.enum_prefix(grammar.name, 'Instructions')) 109 for inst in grammar.instructions: 110 parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode'])) 111 parts.append(self.enum_end(grammar.name, 'Instructions')) 112 parts.append('') 113 114 if grammar.operand_kinds: 115 for kind in grammar.operand_kinds: 116 parts.append(self.enum_prefix(grammar.name, kind['kind'])) 117 for e in kind['enumerants']: 118 parts.append(self.enum_value(grammar.name, e['enumerant'], e['value'])) 119 parts.append(self.enum_end(grammar.name, kind['kind'])) 120 parts.append('') 121 122 parts.append(self.cpp_guard_postamble()) 123 124 if self.uses_guards: 125 parts.append('#endif // {}'.format(guard)) 126 127 return '\n'.join(parts) 128 129 130class CLikeGenerator(LangGenerator): 131 def uses_guards(self): 132 return True 133 134 def comment_prefix(self): 135 return "// " 136 137 def const_definition(self, prefix, var, value): 138 # Use an anonymous enum. Don't use a static const int variable because 139 # that can bloat binary size. 140 return 'enum {0} {1}{2} = {3}, {1}{2}_BitWidthPadding = 0x7fffffff {4};'.format( 141 '{', prefix, var, value, '}') 142 143 def enum_prefix(self, prefix, name): 144 return 'enum {}{} {}'.format(prefix, name, '{') 145 146 def enum_end(self, prefix, enum): 147 return ' {}{}Max = 0x7ffffff\n{};\n'.format(prefix, enum, '}') 148 149 def cpp_guard_preamble(self): 150 return '#ifdef __cplusplus\nextern "C" {\n#endif\n' 151 152 def cpp_guard_postamble(self): 153 return '#ifdef __cplusplus\n}\n#endif\n' 154 155 156class CGenerator(CLikeGenerator): 157 pass 158 159 160def main(): 161 import argparse 162 parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar') 163 164 parser.add_argument('--extinst-name', 165 type=str, required=True, 166 help='The name to use in tokens') 167 parser.add_argument('--extinst-grammar', metavar='<path>', 168 type=str, required=True, 169 help='input JSON grammar file for extended instruction set') 170 parser.add_argument('--extinst-output-base', metavar='<path>', 171 type=str, required=True, 172 help='Basename of the language-specific output file.') 173 args = parser.parse_args() 174 175 with open(args.extinst_grammar) as json_file: 176 grammar_json = json.loads(json_file.read()) 177 grammar = ExtInstGrammar(name = args.extinst_name, 178 copyright = grammar_json['copyright'], 179 instructions = grammar_json['instructions'], 180 operand_kinds = grammar_json['operand_kinds'], 181 version = grammar_json['version'], 182 revision = grammar_json['revision']) 183 make_path_to_file(args.extinst_output_base) 184 print(CGenerator().generate(grammar), file=open(args.extinst_output_base + '.h', 'w')) 185 186 187if __name__ == '__main__': 188 main() 189