boot2

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

p1_gen.py (8068B)


      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 --out PATH
     12     python3 p1/gen/p1_gen.py --check --arch ARCH --out PATH
     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     arch = None
    207     out = None
    208     i = 0
    209     while i < len(argv):
    210         a = argv[i]
    211         if a == '--check':
    212             check = True
    213         elif a == '--list-archs':
    214             print('\n'.join(sorted(ARCHES)))
    215             sys.exit(0)
    216         elif a == '--arch':
    217             i += 1
    218             if i >= len(argv):
    219                 raise SystemExit('--arch requires a value')
    220             arch = argv[i]
    221         elif a == '--out':
    222             i += 1
    223             if i >= len(argv):
    224                 raise SystemExit('--out requires a value')
    225             out = argv[i]
    226         else:
    227             raise SystemExit(f'unexpected argument: {a}')
    228         i += 1
    229     if arch is None:
    230         raise SystemExit('--arch is required')
    231     if out is None:
    232         raise SystemExit('--out is required')
    233     if arch not in ARCHES:
    234         raise SystemExit(f'unknown arch: {arch}')
    235     return check, arch, out
    236 
    237 
    238 def main(argv=None):
    239     check, arch_name, path = parse_args(argv or sys.argv[1:])
    240     content = emit(arch_name)
    241     if check:
    242         try:
    243             with open(path) as f:
    244                 existing = f.read()
    245         except FileNotFoundError:
    246             existing = ''
    247         if existing != content:
    248             sys.stderr.write(f'DIFF: {path}\n')
    249             sys.exit(1)
    250         return
    251     os.makedirs(os.path.dirname(path) or '.', exist_ok=True)
    252     with open(path, 'w') as f:
    253         f.write(content)
    254     print(f'wrote {path} ({len(content)} bytes)')
    255 
    256 
    257 if __name__ == '__main__':
    258     main()