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