]>
Commit | Line | Data |
---|---|---|
7e1d4240 JM |
1 | #!/usr/bin/python3 |
2 | # Produce headers of assembly constants from C expressions. | |
3 | # Copyright (C) 2018 Free Software Foundation, Inc. | |
4 | # This file is part of the GNU C Library. | |
5 | # | |
6 | # The GNU C Library is free software; you can redistribute it and/or | |
7 | # modify it under the terms of the GNU Lesser General Public | |
8 | # License as published by the Free Software Foundation; either | |
9 | # version 2.1 of the License, or (at your option) any later version. | |
10 | # | |
11 | # The GNU C Library is distributed in the hope that it will be useful, | |
12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | # Lesser General Public License for more details. | |
15 | # | |
16 | # You should have received a copy of the GNU Lesser General Public | |
17 | # License along with the GNU C Library; if not, see | |
18 | # <http://www.gnu.org/licenses/>. | |
19 | ||
20 | # The input to this script looks like: | |
21 | # #cpp-directive ... | |
22 | # NAME1 | |
23 | # NAME2 expression ... | |
24 | # A line giving just a name implies an expression consisting of just that name. | |
25 | ||
26 | import argparse | |
27 | import os.path | |
28 | import re | |
29 | import subprocess | |
30 | import tempfile | |
31 | ||
32 | ||
33 | def compute_c_consts(sym_data, cc): | |
34 | """Compute the values of some C constants. | |
35 | ||
36 | The first argument is a list whose elements are either strings | |
37 | (preprocessor directives) or pairs of strings (a name and a C | |
38 | expression for the corresponding value). Preprocessor directives | |
39 | in the middle of the list may be used to select which constants | |
40 | end up being evaluated using which expressions. | |
41 | ||
42 | """ | |
43 | out_lines = [] | |
44 | started = False | |
45 | for arg in sym_data: | |
46 | if isinstance(arg, str): | |
47 | out_lines.append(arg) | |
48 | continue | |
49 | name = arg[0] | |
50 | value = arg[1] | |
51 | if not started: | |
52 | out_lines.append('void\ndummy (void)\n{') | |
53 | started = True | |
54 | out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" ' | |
55 | ': : \"i\" ((long int) (%s)));' | |
56 | % (name, value)) | |
57 | if started: | |
58 | out_lines.append('}') | |
59 | out_lines.append('') | |
60 | out_text = '\n'.join(out_lines) | |
61 | with tempfile.TemporaryDirectory() as temp_dir: | |
62 | c_file_name = os.path.join(temp_dir, 'test.c') | |
63 | s_file_name = os.path.join(temp_dir, 'test.s') | |
64 | with open(c_file_name, 'w') as c_file: | |
65 | c_file.write(out_text) | |
66 | # Compilation has to be from stdin to avoid the temporary file | |
67 | # name being written into the generated dependencies. | |
68 | cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name)) | |
69 | subprocess.check_call(cmd, shell=True) | |
70 | consts = {} | |
71 | with open(s_file_name, 'r') as s_file: | |
72 | for line in s_file: | |
73 | match = re.search('@@@name@@@([^@]*)' | |
74 | '@@@value@@@[^0-9Xxa-fA-F-]*' | |
75 | '([0-9Xxa-fA-F-]+).*@@@end@@@', line) | |
76 | if match: | |
77 | if (match.group(1) in consts | |
78 | and match.group(2) != consts[match.group(1)]): | |
79 | raise ValueError('duplicate constant %s' | |
80 | % match.group(1)) | |
81 | consts[match.group(1)] = match.group(2) | |
82 | return consts | |
83 | ||
84 | ||
85 | def gen_test(sym_data): | |
86 | """Generate a test for the values of some C constants. | |
87 | ||
88 | The first argument is as for compute_c_consts. | |
89 | ||
90 | """ | |
91 | out_lines = [] | |
92 | started = False | |
93 | for arg in sym_data: | |
94 | if isinstance(arg, str): | |
95 | out_lines.append(arg) | |
96 | continue | |
97 | name = arg[0] | |
98 | value = arg[1] | |
99 | if not started: | |
100 | out_lines.append('#include <stdint.h>\n' | |
101 | '#include <stdio.h>\n' | |
102 | '#include <bits/wordsize.h>\n' | |
103 | '#if __WORDSIZE == 64\n' | |
104 | 'typedef uint64_t c_t;\n' | |
105 | '# define U(n) UINT64_C (n)\n' | |
106 | '#else\n' | |
107 | 'typedef uint32_t c_t;\n' | |
108 | '# define U(n) UINT32_C (n)\n' | |
109 | '#endif\n' | |
110 | 'static int\n' | |
111 | 'do_test (void)\n' | |
112 | '{\n' | |
113 | # Compilation test only, using static assertions. | |
114 | ' return 0;\n' | |
115 | '}\n' | |
116 | '#include <support/test-driver.c>') | |
117 | started = True | |
118 | out_lines.append('_Static_assert (U (asconst_%s) == (c_t) (%s), ' | |
119 | '"value of %s");' | |
120 | % (name, value, name)) | |
121 | return '\n'.join(out_lines) | |
122 | ||
123 | ||
124 | def main(): | |
125 | """The main entry point.""" | |
126 | parser = argparse.ArgumentParser( | |
127 | description='Produce headers of assembly constants.') | |
128 | parser.add_argument('--cc', metavar='CC', | |
129 | help='C compiler (including options) to use') | |
130 | parser.add_argument('--test', action='store_true', | |
131 | help='Generate test case instead of header') | |
132 | parser.add_argument('sym_file', | |
133 | help='.sym file to process') | |
134 | args = parser.parse_args() | |
135 | sym_data = [] | |
136 | with open(args.sym_file, 'r') as sym_file: | |
137 | for line in sym_file: | |
138 | line = line.strip() | |
139 | if line == '': | |
140 | continue | |
141 | # Pass preprocessor directives through. | |
142 | if line.startswith('#'): | |
143 | sym_data.append(line) | |
144 | continue | |
145 | words = line.split(maxsplit=1) | |
146 | # Separator. | |
147 | if words[0] == '--': | |
148 | continue | |
149 | name = words[0] | |
150 | value = words[1] if len(words) > 1 else words[0] | |
151 | sym_data.append((name, value)) | |
152 | if args.test: | |
153 | print(gen_test(sym_data)) | |
154 | else: | |
155 | consts = compute_c_consts(sym_data, args.cc) | |
156 | print('\n'.join('#define %s %s' % c for c in sorted(consts.items()))) | |
157 | ||
158 | if __name__ == '__main__': | |
159 | main() |