spreg-gen.py revision 1.1.1.1 1 1.1 christos #!/usr/bin/env python3
2 1.1 christos # Copyright (C) 1994-1995 Andrew Cagney <cagney (at] highland.com.au>
3 1.1 christos # Copyright (C) 1996-2024 Free Software Foundation, Inc.
4 1.1 christos #
5 1.1 christos # This file is part of the GNU simulators.
6 1.1 christos #
7 1.1 christos # This program is free software; you can redistribute it and/or modify
8 1.1 christos # it under the terms of the GNU General Public License as published by
9 1.1 christos # the Free Software Foundation; either version 3 of the License, or
10 1.1 christos # (at your option) any later version.
11 1.1 christos #
12 1.1 christos # This program is distributed in the hope that it will be useful,
13 1.1 christos # but WITHOUT ANY WARRANTY; without even the implied warranty of
14 1.1 christos # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 1.1 christos # GNU General Public License for more details.
16 1.1 christos #
17 1.1 christos # You should have received a copy of the GNU General Public License
18 1.1 christos # along with this program. If not, see <http://www.gnu.org/licenses/>.
19 1.1 christos
20 1.1 christos """Helper to generate spreg.[ch] files."""
21 1.1 christos
22 1.1 christos import argparse
23 1.1 christos from pathlib import Path
24 1.1 christos import sys
25 1.1 christos from typing import NamedTuple, TextIO
26 1.1 christos
27 1.1 christos
28 1.1 christos FILE = Path(__file__).resolve()
29 1.1 christos DIR = FILE.parent
30 1.1 christos
31 1.1 christos
32 1.1 christos NR_OF_SPRS = 1024
33 1.1 christos
34 1.1 christos SPREG_ATTRIBUTES = (
35 1.1 christos "is_valid",
36 1.1 christos "is_readonly",
37 1.1 christos "name",
38 1.1 christos "index",
39 1.1 christos "length",
40 1.1 christos )
41 1.1 christos
42 1.1 christos
43 1.1 christos class Spreg(NamedTuple):
44 1.1 christos """A single spreg entry."""
45 1.1 christos
46 1.1 christos name: str
47 1.1 christos reg_nr: int
48 1.1 christos is_readonly: int
49 1.1 christos length: int
50 1.1 christos
51 1.1 christos
52 1.1 christos def load_table(source: Path) -> list[Spreg]:
53 1.1 christos """Load the spreg table & return all entries in it."""
54 1.1 christos ret = []
55 1.1 christos
56 1.1 christos with source.open("r", encoding="utf-8") as fp:
57 1.1 christos for i, line in enumerate(fp):
58 1.1 christos line = line.split("#", 1)[0].strip()
59 1.1 christos if not line:
60 1.1 christos # Skip blank & comment lines.
61 1.1 christos continue
62 1.1 christos
63 1.1 christos fields = line.split(":")
64 1.1 christos assert len(fields) == 4, f"{source}:{i}: bad line: {line}"
65 1.1 christos spreg = Spreg(
66 1.1 christos name=fields[0].lower(),
67 1.1 christos reg_nr=int(fields[1]),
68 1.1 christos is_readonly=int(fields[2]),
69 1.1 christos length=int(fields[3]),
70 1.1 christos )
71 1.1 christos ret.append(spreg)
72 1.1 christos
73 1.1 christos return sorted(ret, key=lambda x: x[1])
74 1.1 christos
75 1.1 christos
76 1.1 christos def print_copyleft(fp: TextIO) -> None:
77 1.1 christos """Write out the standard copyright & license file block."""
78 1.1 christos fp.write(
79 1.1 christos f"""\
80 1.1 christos /* DO NOT EDIT: GENERATED BY {FILE.name}.
81 1.1 christos
82 1.1 christos Copyright (C) 1994-1995 Andrew Cagney <cagney (at] highland.com.au>
83 1.1 christos Copyright (C) 1996-2024 Free Software Foundation, Inc.
84 1.1 christos
85 1.1 christos This file is part of the GNU simulators.
86 1.1 christos
87 1.1 christos This program is free software; you can redistribute it and/or modify
88 1.1 christos it under the terms of the GNU General Public License as published by
89 1.1 christos the Free Software Foundation; either version 3 of the License, or
90 1.1 christos (at your option) any later version.
91 1.1 christos
92 1.1 christos This program is distributed in the hope that it will be useful,
93 1.1 christos but WITHOUT ANY WARRANTY; without even the implied warranty of
94 1.1 christos MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
95 1.1 christos GNU General Public License for more details.
96 1.1 christos
97 1.1 christos You should have received a copy of the GNU General Public License
98 1.1 christos along with this program. If not, see <http://www.gnu.org/licenses/>. */
99 1.1 christos """
100 1.1 christos )
101 1.1 christos
102 1.1 christos
103 1.1 christos def gen_header(table: list[Spreg], output: Path) -> None:
104 1.1 christos """Write header to |output| from spreg |table|."""
105 1.1 christos with output.open("w", encoding="utf-8") as fp:
106 1.1 christos print_copyleft(fp)
107 1.1 christos fp.write(
108 1.1 christos """
109 1.1 christos #ifndef _SPREG_H_
110 1.1 christos #define _SPREG_H_
111 1.1 christos
112 1.1 christos typedef unsigned_word spreg;
113 1.1 christos
114 1.1 christos typedef enum {
115 1.1 christos """
116 1.1 christos )
117 1.1 christos
118 1.1 christos for spreg in table:
119 1.1 christos fp.write(f" spr_{spreg.name} = {spreg.reg_nr},\n")
120 1.1 christos
121 1.1 christos fp.write(
122 1.1 christos f"""\
123 1.1 christos nr_of_sprs = {NR_OF_SPRS}
124 1.1 christos }} sprs;
125 1.1 christos
126 1.1 christos """
127 1.1 christos )
128 1.1 christos
129 1.1 christos for attr in SPREG_ATTRIBUTES:
130 1.1 christos ret_type = "const char *" if attr == "name" else "int"
131 1.1 christos fp.write(f"INLINE_SPREG({ret_type}) spr_{attr}(sprs spr);\n")
132 1.1 christos
133 1.1 christos fp.write(
134 1.1 christos """
135 1.1 christos #endif /* _SPREG_H_ */
136 1.1 christos """
137 1.1 christos )
138 1.1 christos
139 1.1 christos
140 1.1 christos def gen_switch_is_valid(table: list[Spreg], fp: TextIO) -> None:
141 1.1 christos """Generate switch table for is_valid property."""
142 1.1 christos fp.write(" switch (spr) {\n")
143 1.1 christos # Output all the known registers. We'll return 1 for them.
144 1.1 christos for spreg in table:
145 1.1 christos fp.write(f" case {spreg.reg_nr}:\n")
146 1.1 christos # All other registers return 0.
147 1.1 christos fp.write(
148 1.1 christos """\
149 1.1 christos return 1;
150 1.1 christos }
151 1.1 christos return 0;
152 1.1 christos """
153 1.1 christos )
154 1.1 christos
155 1.1 christos
156 1.1 christos def gen_switch_is_readonly(table: list[Spreg], fp: TextIO) -> None:
157 1.1 christos """Generate switch table for is_readonly property."""
158 1.1 christos # Find any readonly registers and output a switch for them.
159 1.1 christos # If there aren't any, we can optimize this away to a single return.
160 1.1 christos output_switch = False
161 1.1 christos has_readonly = False
162 1.1 christos for spreg in table:
163 1.1 christos if spreg.is_readonly:
164 1.1 christos if not output_switch:
165 1.1 christos fp.write(" switch (spr) {\n")
166 1.1 christos output_switch = True
167 1.1 christos has_readonly = True
168 1.1 christos fp.write(f" case {spreg.reg_nr}:\n")
169 1.1 christos if has_readonly:
170 1.1 christos fp.write(" return 1;\n")
171 1.1 christos
172 1.1 christos if output_switch:
173 1.1 christos fp.write(" }\n")
174 1.1 christos fp.write(" return 0;\n")
175 1.1 christos
176 1.1 christos
177 1.1 christos def gen_switch_length(table: list[Spreg], fp: TextIO) -> None:
178 1.1 christos """Generate switch table for length property."""
179 1.1 christos # Find any registers with a length property and output a switch for them.
180 1.1 christos # If there aren't any, we can optimize this away to a single return.
181 1.1 christos output_switch = False
182 1.1 christos for spreg in table:
183 1.1 christos if spreg.length:
184 1.1 christos if not output_switch:
185 1.1 christos fp.write(" switch (spr) {\n")
186 1.1 christos output_switch = True
187 1.1 christos fp.write(f" case {spreg.reg_nr}:\n")
188 1.1 christos fp.write(f" return {spreg.length};\n")
189 1.1 christos
190 1.1 christos if output_switch:
191 1.1 christos fp.write(" }\n")
192 1.1 christos fp.write(" return 0;\n")
193 1.1 christos
194 1.1 christos
195 1.1 christos def gen_source(table: list[Spreg], output: Path) -> None:
196 1.1 christos """Write header to |output| from spreg |table|."""
197 1.1 christos with output.open("w", encoding="utf-8") as fp:
198 1.1 christos print_copyleft(fp)
199 1.1 christos fp.write(
200 1.1 christos """
201 1.1 christos #ifndef _SPREG_C_
202 1.1 christos #define _SPREG_C_
203 1.1 christos
204 1.1 christos #include "basics.h"
205 1.1 christos #include "spreg.h"
206 1.1 christos
207 1.1 christos typedef struct _spreg_info {
208 1.1 christos const char *name;
209 1.1 christos int is_valid;
210 1.1 christos int length;
211 1.1 christos int is_readonly;
212 1.1 christos int index;
213 1.1 christos } spreg_info;
214 1.1 christos
215 1.1 christos static const spreg_info spr_info[nr_of_sprs+1] = {
216 1.1 christos """
217 1.1 christos )
218 1.1 christos
219 1.1 christos entries = iter(table)
220 1.1 christos entry = next(entries)
221 1.1 christos for spreg_nr in range(0, NR_OF_SPRS + 1):
222 1.1 christos if entry is None or spreg_nr < entry.reg_nr:
223 1.1 christos fp.write(f" {{ 0, 0, 0, 0, {spreg_nr} }},\n")
224 1.1 christos else:
225 1.1 christos fp.write(
226 1.1 christos f' {{ "{entry.name}", 1, {entry.length}, {entry.is_readonly}, spr_{entry.name} /*{spreg_nr}*/ }},\n'
227 1.1 christos )
228 1.1 christos entry = next(entries, None)
229 1.1 christos fp.write("};\n")
230 1.1 christos
231 1.1 christos for attr in SPREG_ATTRIBUTES:
232 1.1 christos ret_type = "const char *" if attr == "name" else "int"
233 1.1 christos fp.write(
234 1.1 christos f"""
235 1.1 christos INLINE_SPREG({ret_type}) spr_{attr}(sprs spr)
236 1.1 christos {{
237 1.1 christos """
238 1.1 christos )
239 1.1 christos
240 1.1 christos if attr not in ("index", "name"):
241 1.1 christos fp.write(
242 1.1 christos """\
243 1.1 christos #ifdef WITH_SPREG_SWITCH_TABLE
244 1.1 christos """
245 1.1 christos )
246 1.1 christos if attr == "is_valid":
247 1.1 christos gen_switch_is_valid(table, fp)
248 1.1 christos elif attr == "is_readonly":
249 1.1 christos gen_switch_is_readonly(table, fp)
250 1.1 christos elif attr == "length":
251 1.1 christos gen_switch_length(table, fp)
252 1.1 christos else:
253 1.1 christos assert False, f"{attr}: Unknown attribute"
254 1.1 christos fp.write("#else\n")
255 1.1 christos fp.write(f" return spr_info[spr].{attr};\n")
256 1.1 christos if attr not in ("index", "name"):
257 1.1 christos fp.write("#endif\n")
258 1.1 christos fp.write("}\n")
259 1.1 christos
260 1.1 christos fp.write("\n#endif /* _SPREG_C_ */\n")
261 1.1 christos
262 1.1 christos
263 1.1 christos def get_parser() -> argparse.ArgumentParser:
264 1.1 christos """Get CLI parser."""
265 1.1 christos parser = argparse.ArgumentParser(
266 1.1 christos description=__doc__,
267 1.1 christos formatter_class=argparse.RawDescriptionHelpFormatter,
268 1.1 christos )
269 1.1 christos parser.add_argument(
270 1.1 christos "--table",
271 1.1 christos type=Path,
272 1.1 christos default=DIR / "ppc-spr-table",
273 1.1 christos help="path to source table",
274 1.1 christos )
275 1.1 christos parser.add_argument("--header", type=Path, help="path to header (.h) file")
276 1.1 christos parser.add_argument("--source", type=Path, help="path to source (.c) file")
277 1.1 christos return parser
278 1.1 christos
279 1.1 christos
280 1.1 christos def parse_args(argv: list[str]) -> argparse.Namespace:
281 1.1 christos """Process the command line & default options."""
282 1.1 christos parser = get_parser()
283 1.1 christos opts = parser.parse_args(argv)
284 1.1 christos
285 1.1 christos if not opts.header and not opts.source:
286 1.1 christos opts.header = DIR / "spreg.h"
287 1.1 christos opts.source = DIR / "spreg.c"
288 1.1 christos
289 1.1 christos return opts
290 1.1 christos
291 1.1 christos
292 1.1 christos def main(argv: list[str]) -> int:
293 1.1 christos """The main entry point for scripts."""
294 1.1 christos opts = parse_args(argv)
295 1.1 christos
296 1.1 christos table = load_table(opts.table)
297 1.1 christos if opts.header:
298 1.1 christos gen_header(table, opts.header)
299 1.1 christos if opts.source:
300 1.1 christos gen_source(table, opts.source)
301 1.1 christos return 0
302 1.1 christos
303 1.1 christos
304 1.1 christos if __name__ == "__main__":
305 1.1 christos sys.exit(main(sys.argv[1:]))
306