• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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