about summary refs log tree commit diff
path: root/scripts/gen-as-const.py
blob: cabf401ed15e836713fd8a0403797776d1f026ae (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/python3
# Produce headers of assembly constants from C expressions.
# Copyright (C) 2018 Free Software Foundation, Inc.
# This file is part of the GNU C Library.
#
# The GNU C Library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2.1 of the License, or (at your option) any later version.
#
# The GNU C Library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with the GNU C Library; if not, see
# <http://www.gnu.org/licenses/>.

# The input to this script looks like:
#       #cpp-directive ...
#       NAME1
#       NAME2 expression ...
# A line giving just a name implies an expression consisting of just that name.

import argparse
import os.path
import re
import subprocess
import tempfile


def compute_c_consts(sym_data, cc):
    """Compute the values of some C constants.

    The first argument is a list whose elements are either strings
    (preprocessor directives) or pairs of strings (a name and a C
    expression for the corresponding value).  Preprocessor directives
    in the middle of the list may be used to select which constants
    end up being evaluated using which expressions.

    """
    out_lines = []
    started = False
    for arg in sym_data:
        if isinstance(arg, str):
            out_lines.append(arg)
            continue
        name = arg[0]
        value = arg[1]
        if not started:
            out_lines.append('void\ndummy (void)\n{')
            started = True
        out_lines.append('asm ("@@@name@@@%s@@@value@@@%%0@@@end@@@" '
                         ': : \"i\" ((long int) (%s)));'
                         % (name, value))
    if started:
        out_lines.append('}')
    out_lines.append('')
    out_text = '\n'.join(out_lines)
    with tempfile.TemporaryDirectory() as temp_dir:
        c_file_name = os.path.join(temp_dir, 'test.c')
        s_file_name = os.path.join(temp_dir, 'test.s')
        with open(c_file_name, 'w') as c_file:
            c_file.write(out_text)
        # Compilation has to be from stdin to avoid the temporary file
        # name being written into the generated dependencies.
        cmd = ('%s -S -o %s -x c - < %s' % (cc, s_file_name, c_file_name))
        subprocess.check_call(cmd, shell=True)
        consts = {}
        with open(s_file_name, 'r') as s_file:
            for line in s_file:
                match = re.search('@@@name@@@([^@]*)'
                                  '@@@value@@@[^0-9Xxa-fA-F-]*'
                                  '([0-9Xxa-fA-F-]+).*@@@end@@@', line)
                if match:
                    if (match.group(1) in consts
                        and match.group(2) != consts[match.group(1)]):
                        raise ValueError('duplicate constant %s'
                                         % match.group(1))
                    consts[match.group(1)] = match.group(2)
        return consts


def gen_test(sym_data):
    """Generate a test for the values of some C constants.

    The first argument is as for compute_c_consts.

    """
    out_lines = []
    started = False
    for arg in sym_data:
        if isinstance(arg, str):
            out_lines.append(arg)
            continue
        name = arg[0]
        value = arg[1]
        if not started:
            out_lines.append('#include <stdint.h>\n'
                             '#include <stdio.h>\n'
                             '#include <bits/wordsize.h>\n'
                             '#if __WORDSIZE == 64\n'
                             'typedef uint64_t c_t;\n'
                             '# define U(n) UINT64_C (n)\n'
                             '#else\n'
                             'typedef uint32_t c_t;\n'
                             '# define U(n) UINT32_C (n)\n'
                             '#endif\n'
                             'static int\n'
                             'do_test (void)\n'
                             '{\n'
                             # Compilation test only, using static assertions.
                             '  return 0;\n'
                             '}\n'
                             '#include <support/test-driver.c>')
            started = True
        out_lines.append('_Static_assert (U (asconst_%s) == (c_t) (%s), '
                         '"value of %s");'
                         % (name, value, name))
    return '\n'.join(out_lines)


def main():
    """The main entry point."""
    parser = argparse.ArgumentParser(
        description='Produce headers of assembly constants.')
    parser.add_argument('--cc', metavar='CC',
                        help='C compiler (including options) to use')
    parser.add_argument('--test', action='store_true',
                        help='Generate test case instead of header')
    parser.add_argument('sym_file',
                        help='.sym file to process')
    args = parser.parse_args()
    sym_data = []
    with open(args.sym_file, 'r') as sym_file:
        for line in sym_file:
            line = line.strip()
            if line == '':
                continue
            # Pass preprocessor directives through.
            if line.startswith('#'):
                sym_data.append(line)
                continue
            words = line.split(maxsplit=1)
            # Separator.
            if words[0] == '--':
                continue
            name = words[0]
            value = words[1] if len(words) > 1 else words[0]
            sym_data.append((name, value))
    if args.test:
        print(gen_test(sym_data))
    else:
        consts = compute_c_consts(sym_data, args.cc)
        print(''.join('#define %s %s\n' % c for c in sorted(consts.items())), end='')

if __name__ == '__main__':
    main()