| #!/usr/bin/env python3 |
| # Copyright (C) 1994-1995 Andrew Cagney <cagney@highland.com.au> |
| # Copyright (C) 1996-2024 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 <http://www.gnu.org/licenses/>. |
| |
| """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 <cagney@highland.com.au> |
| Copyright (C) 1996-2024 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 <http://www.gnu.org/licenses/>. */ |
| """ |
| ) |
| |
| |
| 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:])) |