boot2

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

p1_gen.py (8080B)


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