1b2a28edaSopenharmony_ci#!/usr/bin/env python3
2b2a28edaSopenharmony_ci# Copyright (c) 2017-2020 Google LLC
3b2a28edaSopenharmony_ci#
4b2a28edaSopenharmony_ci# Permission is hereby granted, free of charge, to any person obtaining a
5b2a28edaSopenharmony_ci# copy of this software and/or associated documentation files (the
6b2a28edaSopenharmony_ci# "Materials"), to deal in the Materials without restriction, including
7b2a28edaSopenharmony_ci# without limitation the rights to use, copy, modify, merge, publish,
8b2a28edaSopenharmony_ci# distribute, sublicense, and/or sell copies of the Materials, and to
9b2a28edaSopenharmony_ci# permit persons to whom the Materials are furnished to do so, subject to
10b2a28edaSopenharmony_ci# the following conditions:
11b2a28edaSopenharmony_ci#
12b2a28edaSopenharmony_ci# The above copyright notice and this permission notice shall be included
13b2a28edaSopenharmony_ci# in all copies or substantial portions of the Materials.
14b2a28edaSopenharmony_ci#
15b2a28edaSopenharmony_ci# MODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
16b2a28edaSopenharmony_ci# KHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
17b2a28edaSopenharmony_ci# SPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
18b2a28edaSopenharmony_ci#    https://www.khronos.org/registry/
19b2a28edaSopenharmony_ci#
20b2a28edaSopenharmony_ci# THE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21b2a28edaSopenharmony_ci# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22b2a28edaSopenharmony_ci# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
23b2a28edaSopenharmony_ci# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
24b2a28edaSopenharmony_ci# CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
25b2a28edaSopenharmony_ci# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
26b2a28edaSopenharmony_ci# MATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
27b2a28edaSopenharmony_ci
28b2a28edaSopenharmony_ci"""Generates a C language headers from a SPIR-V JSON grammar file"""
29b2a28edaSopenharmony_ci
30b2a28edaSopenharmony_ciimport errno
31b2a28edaSopenharmony_ciimport json
32b2a28edaSopenharmony_ciimport os.path
33b2a28edaSopenharmony_ciimport re
34b2a28edaSopenharmony_ci
35b2a28edaSopenharmony_ciDEFAULT_COPYRIGHT="""Copyright (c) 2020 The Khronos Group Inc.
36b2a28edaSopenharmony_ci
37b2a28edaSopenharmony_ciPermission is hereby granted, free of charge, to any person obtaining a
38b2a28edaSopenharmony_cicopy of this software and/or associated documentation files (the
39b2a28edaSopenharmony_ci"Materials"), to deal in the Materials without restriction, including
40b2a28edaSopenharmony_ciwithout limitation the rights to use, copy, modify, merge, publish,
41b2a28edaSopenharmony_cidistribute, sublicense, and/or sell copies of the Materials, and to
42b2a28edaSopenharmony_cipermit persons to whom the Materials are furnished to do so, subject to
43b2a28edaSopenharmony_cithe following conditions:
44b2a28edaSopenharmony_ci
45b2a28edaSopenharmony_ciThe above copyright notice and this permission notice shall be included
46b2a28edaSopenharmony_ciin all copies or substantial portions of the Materials.
47b2a28edaSopenharmony_ci
48b2a28edaSopenharmony_ciMODIFICATIONS TO THIS FILE MAY MEAN IT NO LONGER ACCURATELY REFLECTS
49b2a28edaSopenharmony_ciKHRONOS STANDARDS. THE UNMODIFIED, NORMATIVE VERSIONS OF KHRONOS
50b2a28edaSopenharmony_ciSPECIFICATIONS AND HEADER INFORMATION ARE LOCATED AT
51b2a28edaSopenharmony_ci   https://www.khronos.org/registry/
52b2a28edaSopenharmony_ci
53b2a28edaSopenharmony_ciTHE MATERIALS ARE PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
54b2a28edaSopenharmony_ciEXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
55b2a28edaSopenharmony_ciMERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
56b2a28edaSopenharmony_ciIN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
57b2a28edaSopenharmony_ciCLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
58b2a28edaSopenharmony_ciTORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
59b2a28edaSopenharmony_ciMATERIALS OR THE USE OR OTHER DEALINGS IN THE MATERIALS.
60b2a28edaSopenharmony_ci""".split('\n')
61b2a28edaSopenharmony_ci
62b2a28edaSopenharmony_cidef make_path_to_file(f):
63b2a28edaSopenharmony_ci    """Makes all ancestor directories to the given file, if they
64b2a28edaSopenharmony_ci    don't yet exist.
65b2a28edaSopenharmony_ci
66b2a28edaSopenharmony_ci    Arguments:
67b2a28edaSopenharmony_ci        f: The file whose ancestor directories are to be created.
68b2a28edaSopenharmony_ci    """
69b2a28edaSopenharmony_ci    dir = os.path.dirname(os.path.abspath(f))
70b2a28edaSopenharmony_ci    try:
71b2a28edaSopenharmony_ci        os.makedirs(dir)
72b2a28edaSopenharmony_ci    except OSError as e:
73b2a28edaSopenharmony_ci        if e.errno == errno.EEXIST and os.path.isdir(dir):
74b2a28edaSopenharmony_ci            pass
75b2a28edaSopenharmony_ci        else:
76b2a28edaSopenharmony_ci            raise
77b2a28edaSopenharmony_ci
78b2a28edaSopenharmony_ciclass ExtInstGrammar:
79b2a28edaSopenharmony_ci    """The grammar for an extended instruction set"""
80b2a28edaSopenharmony_ci
81b2a28edaSopenharmony_ci    def __init__(self, name, copyright, instructions, operand_kinds, version = None, revision = None):
82b2a28edaSopenharmony_ci       self.name = name
83b2a28edaSopenharmony_ci       self.copyright = copyright
84b2a28edaSopenharmony_ci       self.instructions = instructions
85b2a28edaSopenharmony_ci       self.operand_kinds = operand_kinds
86b2a28edaSopenharmony_ci       self.version = version
87b2a28edaSopenharmony_ci       self.revision = revision
88b2a28edaSopenharmony_ci
89b2a28edaSopenharmony_ci
90b2a28edaSopenharmony_ciclass LangGenerator:
91b2a28edaSopenharmony_ci    """A language-specific generator"""
92b2a28edaSopenharmony_ci
93b2a28edaSopenharmony_ci    def __init__(self):
94b2a28edaSopenharmony_ci        self.upper_case_initial = re.compile('^[A-Z]')
95b2a28edaSopenharmony_ci        pass
96b2a28edaSopenharmony_ci
97b2a28edaSopenharmony_ci    def comment_prefix(self):
98b2a28edaSopenharmony_ci        return ""
99b2a28edaSopenharmony_ci
100b2a28edaSopenharmony_ci    def namespace_prefix(self):
101b2a28edaSopenharmony_ci        return ""
102b2a28edaSopenharmony_ci
103b2a28edaSopenharmony_ci    def uses_guards(self):
104b2a28edaSopenharmony_ci        return False
105b2a28edaSopenharmony_ci
106b2a28edaSopenharmony_ci    def cpp_guard_preamble(self):
107b2a28edaSopenharmony_ci        return ""
108b2a28edaSopenharmony_ci
109b2a28edaSopenharmony_ci    def cpp_guard_postamble(self):
110b2a28edaSopenharmony_ci        return ""
111b2a28edaSopenharmony_ci
112b2a28edaSopenharmony_ci    def enum_value(self, prefix, name, value):
113b2a28edaSopenharmony_ci        if self.upper_case_initial.match(name):
114b2a28edaSopenharmony_ci            use_name = name
115b2a28edaSopenharmony_ci        else:
116b2a28edaSopenharmony_ci            use_name = '_' + name
117b2a28edaSopenharmony_ci
118b2a28edaSopenharmony_ci        return "    {}{} = {},".format(prefix, use_name, value)
119b2a28edaSopenharmony_ci
120b2a28edaSopenharmony_ci    def generate(self, grammar):
121b2a28edaSopenharmony_ci        """Returns a string that is the language-specific header for the given grammar"""
122b2a28edaSopenharmony_ci
123b2a28edaSopenharmony_ci        parts = []
124b2a28edaSopenharmony_ci        if grammar.copyright:
125b2a28edaSopenharmony_ci            parts.extend(["{}{}".format(self.comment_prefix(), f) for f in grammar.copyright])
126b2a28edaSopenharmony_ci        parts.append('')
127b2a28edaSopenharmony_ci
128b2a28edaSopenharmony_ci        guard = 'SPIRV_UNIFIED1_{}_H_'.format(grammar.name)
129b2a28edaSopenharmony_ci        if self.uses_guards:
130b2a28edaSopenharmony_ci            parts.append('#ifndef {}'.format(guard))
131b2a28edaSopenharmony_ci            parts.append('#define {}'.format(guard))
132b2a28edaSopenharmony_ci        parts.append('')
133b2a28edaSopenharmony_ci
134b2a28edaSopenharmony_ci        parts.append(self.cpp_guard_preamble())
135b2a28edaSopenharmony_ci
136b2a28edaSopenharmony_ci        if grammar.version:
137b2a28edaSopenharmony_ci            parts.append(self.const_definition(grammar.name, 'Version', grammar.version))
138b2a28edaSopenharmony_ci
139b2a28edaSopenharmony_ci        if grammar.revision is not None:
140b2a28edaSopenharmony_ci            parts.append(self.const_definition(grammar.name, 'Revision', grammar.revision))
141b2a28edaSopenharmony_ci
142b2a28edaSopenharmony_ci        parts.append('')
143b2a28edaSopenharmony_ci
144b2a28edaSopenharmony_ci        if grammar.instructions:
145b2a28edaSopenharmony_ci            parts.append(self.enum_prefix(grammar.name, 'Instructions'))
146b2a28edaSopenharmony_ci            for inst in grammar.instructions:
147b2a28edaSopenharmony_ci                parts.append(self.enum_value(grammar.name, inst['opname'], inst['opcode']))
148b2a28edaSopenharmony_ci            parts.append(self.enum_end(grammar.name, 'Instructions'))
149b2a28edaSopenharmony_ci            parts.append('')
150b2a28edaSopenharmony_ci
151b2a28edaSopenharmony_ci        if grammar.operand_kinds:
152b2a28edaSopenharmony_ci            for kind in grammar.operand_kinds:
153b2a28edaSopenharmony_ci                parts.append(self.enum_prefix(grammar.name, kind['kind']))
154b2a28edaSopenharmony_ci                for e in kind['enumerants']:
155b2a28edaSopenharmony_ci                    parts.append(self.enum_value(grammar.name, e['enumerant'], e['value']))
156b2a28edaSopenharmony_ci                parts.append(self.enum_end(grammar.name, kind['kind']))
157b2a28edaSopenharmony_ci            parts.append('')
158b2a28edaSopenharmony_ci
159b2a28edaSopenharmony_ci        parts.append(self.cpp_guard_postamble())
160b2a28edaSopenharmony_ci
161b2a28edaSopenharmony_ci        if self.uses_guards:
162b2a28edaSopenharmony_ci            parts.append('#endif // {}'.format(guard))
163b2a28edaSopenharmony_ci
164b2a28edaSopenharmony_ci        # Ensre the file ends in an end of line
165b2a28edaSopenharmony_ci        parts.append('')
166b2a28edaSopenharmony_ci
167b2a28edaSopenharmony_ci        return '\n'.join(parts)
168b2a28edaSopenharmony_ci
169b2a28edaSopenharmony_ci
170b2a28edaSopenharmony_ciclass CLikeGenerator(LangGenerator):
171b2a28edaSopenharmony_ci    def uses_guards(self):
172b2a28edaSopenharmony_ci        return True
173b2a28edaSopenharmony_ci
174b2a28edaSopenharmony_ci    def comment_prefix(self):
175b2a28edaSopenharmony_ci        return "// "
176b2a28edaSopenharmony_ci
177b2a28edaSopenharmony_ci    def const_definition(self, prefix, var, value):
178b2a28edaSopenharmony_ci        # Use an anonymous enum.  Don't use a static const int variable because
179b2a28edaSopenharmony_ci        # that can bloat binary size.
180b2a28edaSopenharmony_ci        return 'enum {0}{1}{2}{3} = {4},{1}{2}{3}_BitWidthPadding = 0x7fffffff{5};'.format(
181b2a28edaSopenharmony_ci               '{', '\n    ', prefix, var, value, '\n}')
182b2a28edaSopenharmony_ci
183b2a28edaSopenharmony_ci    def enum_prefix(self, prefix, name):
184b2a28edaSopenharmony_ci        return 'enum {}{} {}'.format(prefix, name, '{')
185b2a28edaSopenharmony_ci
186b2a28edaSopenharmony_ci    def enum_end(self, prefix, enum):
187b2a28edaSopenharmony_ci        return '    {}{}Max = 0x7fffffff\n{};\n'.format(prefix, enum, '}')
188b2a28edaSopenharmony_ci
189b2a28edaSopenharmony_ci    def cpp_guard_preamble(self):
190b2a28edaSopenharmony_ci        return '#ifdef __cplusplus\nextern "C" {\n#endif\n'
191b2a28edaSopenharmony_ci
192b2a28edaSopenharmony_ci    def cpp_guard_postamble(self):
193b2a28edaSopenharmony_ci        return '#ifdef __cplusplus\n}\n#endif\n'
194b2a28edaSopenharmony_ci
195b2a28edaSopenharmony_ci
196b2a28edaSopenharmony_ciclass CGenerator(CLikeGenerator):
197b2a28edaSopenharmony_ci    pass
198b2a28edaSopenharmony_ci
199b2a28edaSopenharmony_ci
200b2a28edaSopenharmony_cidef main():
201b2a28edaSopenharmony_ci    import argparse
202b2a28edaSopenharmony_ci    parser = argparse.ArgumentParser(description='Generate language headers from a JSON grammar')
203b2a28edaSopenharmony_ci
204b2a28edaSopenharmony_ci    parser.add_argument('--extinst-name',
205b2a28edaSopenharmony_ci                        type=str, required=True,
206b2a28edaSopenharmony_ci                        help='The name to use in tokens')
207b2a28edaSopenharmony_ci    parser.add_argument('--extinst-grammar', metavar='<path>',
208b2a28edaSopenharmony_ci                        type=str, required=True,
209b2a28edaSopenharmony_ci                        help='input JSON grammar file for extended instruction set')
210b2a28edaSopenharmony_ci    parser.add_argument('--extinst-output-base', metavar='<path>',
211b2a28edaSopenharmony_ci                        type=str, required=True,
212b2a28edaSopenharmony_ci                        help='Basename of the language-specific output file.')
213b2a28edaSopenharmony_ci    args = parser.parse_args()
214b2a28edaSopenharmony_ci
215b2a28edaSopenharmony_ci    with open(args.extinst_grammar) as json_file:
216b2a28edaSopenharmony_ci        grammar_json = json.loads(json_file.read())
217b2a28edaSopenharmony_ci        if 'copyright' in grammar_json:
218b2a28edaSopenharmony_ci          copyright = grammar_json['copyright']
219b2a28edaSopenharmony_ci        else:
220b2a28edaSopenharmony_ci          copyright = DEFAULT_COPYRIGHT
221b2a28edaSopenharmony_ci        if 'version' in grammar_json:
222b2a28edaSopenharmony_ci          version = grammar_json['version']
223b2a28edaSopenharmony_ci        else:
224b2a28edaSopenharmony_ci          version = 0
225b2a28edaSopenharmony_ci        if 'operand_kinds' in grammar_json:
226b2a28edaSopenharmony_ci          operand_kinds = grammar_json['operand_kinds']
227b2a28edaSopenharmony_ci        else:
228b2a28edaSopenharmony_ci          operand_kinds = []
229b2a28edaSopenharmony_ci
230b2a28edaSopenharmony_ci        grammar = ExtInstGrammar(name = args.extinst_name,
231b2a28edaSopenharmony_ci                                 copyright = copyright,
232b2a28edaSopenharmony_ci                                 instructions = grammar_json['instructions'],
233b2a28edaSopenharmony_ci                                 operand_kinds = operand_kinds,
234b2a28edaSopenharmony_ci                                 version = version,
235b2a28edaSopenharmony_ci                                 revision = grammar_json['revision'])
236b2a28edaSopenharmony_ci        make_path_to_file(args.extinst_output_base)
237b2a28edaSopenharmony_ci        with open(args.extinst_output_base + '.h', 'w') as f:
238b2a28edaSopenharmony_ci            f.write(CGenerator().generate(grammar))
239b2a28edaSopenharmony_ci
240b2a28edaSopenharmony_ci
241b2a28edaSopenharmony_ciif __name__ == '__main__':
242b2a28edaSopenharmony_ci    main()
243