Home | History | Annotate | Line # | Download | only in ppc
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