]> git.ipfire.org Git - thirdparty/glibc.git/blob - scripts/gen-as-const.py
Replace gen-as-const.awk by gen-as-const.py.
[thirdparty/glibc.git] / scripts / gen-as-const.py
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()