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