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