#!/usr/bin/env python3 # Copyright (C) 1994-1995 Andrew Cagney # Copyright (C) 1996-2023 Free Software Foundation, Inc. # # This file is part of the GNU simulators. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program. If not, see . """Helper to generate spreg.[ch] files.""" import argparse from pathlib import Path import sys from typing import NamedTuple, TextIO FILE = Path(__file__).resolve() DIR = FILE.parent NR_OF_SPRS = 1024 SPREG_ATTRIBUTES = ( "is_valid", "is_readonly", "name", "index", "length", ) class Spreg(NamedTuple): """A single spreg entry.""" name: str reg_nr: int is_readonly: int length: int def load_table(source: Path) -> list[Spreg]: """Load the spreg table & return all entries in it.""" ret = [] with source.open("r", encoding="utf-8") as fp: for i, line in enumerate(fp): line = line.split("#", 1)[0].strip() if not line: # Skip blank & comment lines. continue fields = line.split(":") assert len(fields) == 4, f"{source}:{i}: bad line: {line}" spreg = Spreg( name=fields[0].lower(), reg_nr=int(fields[1]), is_readonly=int(fields[2]), length=int(fields[3]), ) ret.append(spreg) return sorted(ret, key=lambda x: x[1]) def print_copyleft(fp: TextIO) -> None: """Write out the standard copyright & license file block.""" fp.write( f"""\ /* DO NOT EDIT: GENERATED BY {FILE.name}. Copyright (C) 1994-1995 Andrew Cagney Copyright (C) 1996-2023 Free Software Foundation, Inc. This file is part of the GNU simulators. This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ """ ) def gen_header(table: list[Spreg], output: Path) -> None: """Write header to |output| from spreg |table|.""" with output.open("w", encoding="utf-8") as fp: print_copyleft(fp) fp.write( """ #ifndef _SPREG_H_ #define _SPREG_H_ typedef unsigned_word spreg; typedef enum { """ ) for spreg in table: fp.write(f" spr_{spreg.name} = {spreg.reg_nr},\n") fp.write( f"""\ nr_of_sprs = {NR_OF_SPRS} }} sprs; """ ) for attr in SPREG_ATTRIBUTES: ret_type = "const char *" if attr == "name" else "int" fp.write(f"INLINE_SPREG({ret_type}) spr_{attr}(sprs spr);\n") fp.write( """ #endif /* _SPREG_H_ */ """ ) def gen_switch_is_valid(table: list[Spreg], fp: TextIO) -> None: """Generate switch table for is_valid property.""" fp.write(" switch (spr) {\n") # Output all the known registers. We'll return 1 for them. for spreg in table: fp.write(f" case {spreg.reg_nr}:\n") # All other registers return 0. fp.write( """\ return 1; } return 0; """ ) def gen_switch_is_readonly(table: list[Spreg], fp: TextIO) -> None: """Generate switch table for is_readonly property.""" # Find any readonly registers and output a switch for them. # If there aren't any, we can optimize this away to a single return. output_switch = False has_readonly = False for spreg in table: if spreg.is_readonly: if not output_switch: fp.write(" switch (spr) {\n") output_switch = True has_readonly = True fp.write(f" case {spreg.reg_nr}:\n") if has_readonly: fp.write(" return 1;\n") if output_switch: fp.write(" }\n") fp.write(" return 0;\n") def gen_switch_length(table: list[Spreg], fp: TextIO) -> None: """Generate switch table for length property.""" # Find any registers with a length property and output a switch for them. # If there aren't any, we can optimize this away to a single return. output_switch = False for spreg in table: if spreg.length: if not output_switch: fp.write(" switch (spr) {\n") output_switch = True fp.write(f" case {spreg.reg_nr}:\n") fp.write(f" return {spreg.length};\n") if output_switch: fp.write(" }\n") fp.write(" return 0;\n") def gen_source(table: list[Spreg], output: Path) -> None: """Write header to |output| from spreg |table|.""" with output.open("w", encoding="utf-8") as fp: print_copyleft(fp) fp.write( """ #ifndef _SPREG_C_ #define _SPREG_C_ #include "basics.h" #include "spreg.h" typedef struct _spreg_info { const char *name; int is_valid; int length; int is_readonly; int index; } spreg_info; static const spreg_info spr_info[nr_of_sprs+1] = { """ ) entries = iter(table) entry = next(entries) for spreg_nr in range(0, NR_OF_SPRS + 1): if entry is None or spreg_nr < entry.reg_nr: fp.write(f" {{ 0, 0, 0, 0, {spreg_nr} }},\n") else: fp.write( f' {{ "{entry.name}", 1, {entry.length}, {entry.is_readonly}, spr_{entry.name} /*{spreg_nr}*/ }},\n' ) entry = next(entries, None) fp.write("};\n") for attr in SPREG_ATTRIBUTES: ret_type = "const char *" if attr == "name" else "int" fp.write( f""" INLINE_SPREG({ret_type}) spr_{attr}(sprs spr) {{ """ ) if attr not in ("index", "name"): fp.write( """\ #ifdef WITH_SPREG_SWITCH_TABLE """ ) if attr == "is_valid": gen_switch_is_valid(table, fp) elif attr == "is_readonly": gen_switch_is_readonly(table, fp) elif attr == "length": gen_switch_length(table, fp) else: assert False, f"{attr}: Unknown attribute" fp.write("#else\n") fp.write(f" return spr_info[spr].{attr};\n") if attr not in ("index", "name"): fp.write("#endif\n") fp.write("}\n") fp.write("\n#endif /* _SPREG_C_ */\n") def get_parser() -> argparse.ArgumentParser: """Get CLI parser.""" parser = argparse.ArgumentParser( description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( "--table", type=Path, default=DIR / "ppc-spr-table", help="path to source table", ) parser.add_argument("--header", type=Path, help="path to header (.h) file") parser.add_argument("--source", type=Path, help="path to source (.c) file") return parser def parse_args(argv: list[str]) -> argparse.Namespace: """Process the command line & default options.""" parser = get_parser() opts = parser.parse_args(argv) if not opts.header and not opts.source: opts.header = DIR / "spreg.h" opts.source = DIR / "spreg.c" return opts def main(argv: list[str]) -> int: """The main entry point for scripts.""" opts = parse_args(argv) table = load_table(opts.table) if opts.header: gen_header(table, opts.header) if opts.source: gen_source(table, opts.source) return 0 if __name__ == "__main__": sys.exit(main(sys.argv[1:]))