boot2

Playing with the boostrap
git clone https://git.ryansepassi.com/git/boot2.git
Log | Files | Refs | README

p1_gen.py (8140B)


      1 #!/usr/bin/env python3
      2 """Generate P1 v2 DEFINE tables.
      3 
      4 This is a fresh generator for docs/P1v2.md. The ISA surface is described by
      5 plain namedtuple rows, and each backend registers a simple row-type -> encoder
      6 mapping. The emitted immediate/offset domains are still curated tables rather
      7 than the full theoretical spec space, so extending coverage is a one-line data
      8 edit instead of an architecture rewrite.
      9 
     10 Usage:
     11     python3 p1/p1_gen.py [--arch ARCH] [build-root]
     12     python3 p1/p1_gen.py --check [--arch ARCH] [build-root]
     13     python3 p1/p1_gen.py --list-archs
     14 """
     15 
     16 import os
     17 import sys
     18 from itertools import product
     19 
     20 from common import (
     21     AddI,
     22     Banner,
     23     BranchReg,
     24     CondB,
     25     CondBZ,
     26     Enter,
     27     La,
     28     LaBr,
     29     LdArg,
     30     Li,
     31     Literal,
     32     LogI,
     33     Mem,
     34     Mov,
     35     Nullary,
     36     Rrr,
     37     ShiftI,
     38     get_arch,
     39     registered_arches,
     40     word_hex,
     41 )
     42 
     43 import aarch64  # noqa: F401 - imported for arch registration side effects
     44 
     45 
     46 P1_GPRS = ('a0', 'a1', 'a2', 'a3', 't0', 't1', 't2', 's0', 's1', 's2', 's3')
     47 P1_BASES = P1_GPRS + ('sp',)
     48 
     49 RRR_OPS = ('ADD', 'SUB', 'AND', 'OR', 'XOR', 'SHL', 'SHR', 'SAR', 'MUL', 'DIV', 'REM')
     50 LOGI_OPS = ('ANDI', 'ORI')
     51 SHIFT_OPS = ('SHLI', 'SHRI', 'SARI')
     52 MEM_OPS = ('LD', 'ST', 'LB', 'SB')
     53 CONDB_OPS = ('BEQ', 'BNE', 'BLT', 'BLTU')
     54 CONDBZ_OPS = ('BEQZ', 'BNEZ', 'BLTZ')
     55 
     56 ADDI_IMMS = (
     57     -2048, -1024, -256, -128, -64, -48, -32, -24, -16, -12, -8, -7, -6,
     58     -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 12, 15, 16, 24, 32, 40,
     59     48, 63, 64, 127, 128, 255, 256, 512, 1024, 2047,
     60 )
     61 
     62 LOGI_IMMS = (
     63     -1, 0, 1, 2, 3, 4, 6, 7, 8, 15, 16, 31, 32, 63, 64, 127, 255, 511, 1023,
     64     2047,
     65 )
     66 
     67 SHIFT_IMMS = tuple(range(64))
     68 
     69 MEM_OFFS = (
     70     -256, -128, -64, -48, -32, -24, -16, -8, -1, 0, 1, 2, 3, 4, 5, 6, 7, 8,
     71     15, 16, 24, 32, 40, 48, 56, 64, 128, 255,
     72 )
     73 
     74 LDARG_SLOTS = tuple(range(32))
     75 ENTER_SIZES = tuple(range(0, 129))
     76 
     77 
     78 HEADER = """## p1_{arch}.M1 — GENERATED by p1/p1_gen.py. Do not edit by hand.
     79 ##
     80 ## This table targets the P1 v2 ISA described in docs/P1v2.md.
     81 ## Row shapes are shared; per-arch lowering lives in p1/<arch>.py.
     82 """
     83 
     84 
     85 def imm_suffix(imm):
     86     return f'NEG{-imm}' if imm < 0 else str(imm)
     87 
     88 
     89 def rows(arch):
     90     out = []
     91 
     92     out.append(Banner('Materialization'))
     93     for rd in P1_GPRS:
     94         out.append(Li(name=f'LI_{rd.upper()}', rd=rd))
     95     for rd in P1_GPRS:
     96         out.append(La(name=f'LA_{rd.upper()}', rd=rd))
     97     out.append(LaBr(name='LA_BR'))
     98 
     99     out.append(Banner('Moves'))
    100     for rd, rs in product(P1_GPRS, P1_GPRS):
    101         out.append(Mov(name=f'MOV_{rd.upper()}_{rs.upper()}', rd=rd, rs=rs))
    102     for rd in P1_GPRS:
    103         out.append(Mov(name=f'MOV_{rd.upper()}_SP', rd=rd, rs='sp'))
    104 
    105     out.append(Banner('Register Arithmetic'))
    106     for op, rd, ra, rb in product(RRR_OPS, P1_GPRS, P1_GPRS, P1_GPRS):
    107         out.append(Rrr(name=f'{op}_{rd.upper()}_{ra.upper()}_{rb.upper()}',
    108                        op=op, rd=rd, ra=ra, rb=rb))
    109 
    110     out.append(Banner('Immediate Arithmetic'))
    111     for rd, ra, imm in product(P1_GPRS, P1_GPRS, ADDI_IMMS):
    112         out.append(AddI(name=f'ADDI_{rd.upper()}_{ra.upper()}_{imm_suffix(imm)}',
    113                         rd=rd, ra=ra, imm=imm))
    114     for op, rd, ra, imm in product(LOGI_OPS, P1_GPRS, P1_GPRS, LOGI_IMMS):
    115         out.append(LogI(name=f'{op}_{rd.upper()}_{ra.upper()}_{imm_suffix(imm)}',
    116                         op=op, rd=rd, ra=ra, imm=imm))
    117     for op, rd, ra, imm in product(SHIFT_OPS, P1_GPRS, P1_GPRS, SHIFT_IMMS):
    118         out.append(ShiftI(name=f'{op}_{rd.upper()}_{ra.upper()}_{imm}',
    119                           op=op, rd=rd, ra=ra, imm=imm))
    120 
    121     out.append(Banner('Memory'))
    122     for op, rt, rn, off in product(MEM_OPS, P1_GPRS, P1_BASES, MEM_OFFS):
    123         out.append(Mem(name=f'{op}_{rt.upper()}_{rn.upper()}_{imm_suffix(off)}',
    124                        op=op, rt=rt, rn=rn, off=off))
    125 
    126     out.append(Banner('ABI Access'))
    127     for rd, slot in product(P1_GPRS, LDARG_SLOTS):
    128         out.append(LdArg(name=f'LDARG_{rd.upper()}_{slot}', rd=rd, slot=slot))
    129 
    130     out.append(Banner('Branches'))
    131     out.append(Nullary(name='B', kind='B'))
    132     for rs in P1_GPRS:
    133         out.append(BranchReg(name=f'BR_{rs.upper()}', kind='BR', rs=rs))
    134     for op, ra, rb in product(CONDB_OPS, P1_GPRS, P1_GPRS):
    135         out.append(CondB(name=f'{op}_{ra.upper()}_{rb.upper()}', op=op, ra=ra, rb=rb))
    136     for op, ra in product(CONDBZ_OPS, P1_GPRS):
    137         out.append(CondBZ(name=f'{op}_{ra.upper()}', op=op, ra=ra))
    138 
    139     out.append(Banner('Calls And Returns'))
    140     out.append(Nullary(name='CALL', kind='CALL'))
    141     out.append(Nullary(name='RET', kind='RET'))
    142     out.append(Nullary(name='ERET', kind='ERET'))
    143     out.append(Nullary(name='TAIL', kind='TAIL'))
    144     for rs in P1_GPRS:
    145         out.append(BranchReg(name=f'CALLR_{rs.upper()}', kind='CALLR', rs=rs))
    146     for rs in P1_GPRS:
    147         out.append(BranchReg(name=f'TAILR_{rs.upper()}', kind='TAILR', rs=rs))
    148 
    149     out.append(Banner('Frame Management'))
    150     for size in ENTER_SIZES:
    151         out.append(Enter(name=f'ENTER_{size}', size=size))
    152 
    153     out.append(Banner('System'))
    154     out.append(Nullary(name='SYSCALL', kind='SYSCALL'))
    155     for name, number in sorted(arch.syscall_numbers.items()):
    156         out.append(Literal(name=name, hex_by_arch={arch.name: word_hex(arch.word_bytes, number)}))
    157 
    158     return out
    159 
    160 
    161 def lower_name(name):
    162     low = name.lower()
    163     head, sep, rest = low.partition('_')
    164     if not sep:
    165         return low
    166     if '_' not in rest:
    167         return low
    168     return f'{head}_{rest.replace("_", ",")}'
    169 
    170 
    171 def encode_row(arch, row):
    172     if isinstance(row, Literal):
    173         return row.hex_by_arch[arch.name]
    174     encoder = arch.encoders[type(row)]
    175     return encoder(arch, row)
    176 
    177 
    178 def emit(arch_name):
    179     arch = get_arch(arch_name)
    180     out = [HEADER.format(arch=arch.name).rstrip(), '']
    181     seen = set()
    182     for row in rows(arch):
    183         if isinstance(row, Banner):
    184             out.append('')
    185             out.append(f'## ---- {row.text}')
    186             continue
    187         name = lower_name(row.name)
    188         if name in seen:
    189             raise RuntimeError(f'duplicate DEFINE: {name}')
    190         seen.add(name)
    191         out.append(f'DEFINE {name} {encode_row(arch, row)}')
    192     out.append('')
    193     out.append('## ---- Program Entry')
    194     out.append('## Backend-owned :_start stub per docs/P1.md §Program Entry.')
    195     out.append('## Calls p1_main under the one-word direct-result convention')
    196     out.append("## (a0=argc, a1=argv) and sys_exits its return value.")
    197     out.extend(arch.start_stub())
    198     out.append('')
    199     return '\n'.join(out)
    200 
    201 
    202 def parse_args(argv):
    203     check = False
    204     archs = []
    205     positional = []
    206     i = 0
    207     while i < len(argv):
    208         arg = argv[i]
    209         if arg == '--check':
    210             check = True
    211         elif arg == '--list-archs':
    212             print('\n'.join(registered_arches()))
    213             sys.exit(0)
    214         elif arg == '--arch':
    215             i += 1
    216             if i >= len(argv):
    217                 raise SystemExit('--arch requires a value')
    218             archs.append(argv[i])
    219         else:
    220             positional.append(arg)
    221         i += 1
    222     build_root = positional[0] if positional else os.path.join('build', 'p1v2')
    223     if not archs:
    224         archs = list(registered_arches())
    225     return check, archs, build_root
    226 
    227 
    228 def main(argv=None):
    229     check, archs, build_root = parse_args(argv or sys.argv[1:])
    230     had_diff = False
    231 
    232     for arch_name in archs:
    233         arch = get_arch(arch_name)
    234         dest_dir = os.path.join(build_root, arch.name)
    235         path = os.path.join(dest_dir, f'p1_{arch.name}.M1')
    236         content = emit(arch.name)
    237         if check:
    238             try:
    239                 with open(path) as f:
    240                     existing = f.read()
    241             except FileNotFoundError:
    242                 existing = ''
    243             if existing != content:
    244                 sys.stderr.write(f'DIFF: {path}\n')
    245                 had_diff = True
    246             continue
    247         os.makedirs(dest_dir, exist_ok=True)
    248         with open(path, 'w') as f:
    249             f.write(content)
    250         print(f'wrote {path} ({len(content)} bytes)')
    251 
    252     if check and had_diff:
    253         sys.exit(1)
    254 
    255 
    256 if __name__ == '__main__':
    257     main()