boot2

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

refactor-m1pp-bss.py (7354B)


      1 #!/usr/bin/env python3
      2 """One-shot M1pp.P1 BSS refactor.
      3 
      4 Identifies labels in M1pp/M1pp.P1 whose body is one or more ZERO32 lines
      5 (the big BSS-style buffers). Moves them out of the file into BSS:
      6 
      7 * Removes the label declarations + their ZERO32 padding.
      8 * Adds an OFFSET DEFINE for each buffer (offset from BSS_BASE, the byte
      9   past ELF_end).
     10 * Adds a per-buffer 8-byte pointer slot in the in-file data area
     11   (input_buf_ptr, output_buf_ptr, ...).
     12 * Adds initialization code at the top of p1_main that computes each base
     13   pointer once and stores it into its slot.
     14 * Rewrites every `la_<reg> &<buf>` in code to `la_<reg> &<buf>_ptr;
     15   ld_<reg>,<reg>,0` (cached-pointer indirection — same dest reg, no
     16   scratch needed).
     17 
     18 Run: python3 scripts/refactor-m1pp-bss.py
     19 """
     20 
     21 import re
     22 import sys
     23 from pathlib import Path
     24 
     25 SRC = Path('M1pp/M1pp.P1')
     26 text = SRC.read_text()
     27 lines = text.split('\n')
     28 
     29 # --- Pass 1: identify BSS buffers (label followed by ZERO32 lines). ----------
     30 
     31 buffers = []  # (name, start_idx, end_idx_exclusive, zero32_count)
     32 i = 0
     33 while i < len(lines):
     34     line = lines[i].strip()
     35     m = re.match(r':([A-Za-z_][A-Za-z0-9_]*)$', line)
     36     if m:
     37         label = m.group(1)
     38         # scan forward to find ZERO32 lines (allowing intermediate blank/comment)
     39         j = i + 1
     40         zero32_count = 0
     41         while j < len(lines):
     42             stripped = lines[j].strip()
     43             if stripped.startswith('ZERO32'):
     44                 zero32_count += stripped.split().count('ZERO32')
     45                 j += 1
     46             elif stripped == '' or stripped.startswith('#') or stripped.startswith(';'):
     47                 # Comments/blank — peek further
     48                 k = j + 1
     49                 while k < len(lines) and (lines[k].strip() == '' or lines[k].strip().startswith('#')):
     50                     k += 1
     51                 if k < len(lines) and lines[k].strip().startswith('ZERO32'):
     52                     j = k
     53                     continue
     54                 break
     55             else:
     56                 break
     57         if zero32_count > 0:
     58             buffers.append((label, i, j, zero32_count))
     59             i = j
     60             continue
     61     i += 1
     62 
     63 # --- Pass 2: compute layout. -------------------------------------------------
     64 
     65 layout = []
     66 offset = 0
     67 for name, _, _, count in buffers:
     68     size = count * 32
     69     layout.append((name, offset, size))
     70     offset += size
     71 
     72 bss_total = offset
     73 print(f"Found {len(buffers)} BSS buffers, total {bss_total} bytes ({bss_total/1024:.1f} KB)")
     74 for name, off, size in layout:
     75     print(f"  {name:25s} off=0x{off:08x}  size={size:9d}")
     76 
     77 # --- Pass 3: build the rewrite. ----------------------------------------------
     78 
     79 def le_u64(value: int) -> str:
     80     """Encode a non-negative int as 8 bytes little-endian hex."""
     81     assert 0 <= value < (1 << 64)
     82     return ''.join(f'{(value >> (8*k)) & 0xff:02x}' for k in range(8)).upper()
     83 
     84 # 3a. Generate OFFSET DEFINEs.
     85 offset_defines = ['## --- BSS layout (offsets from ELF_end) -------------------------------------']
     86 for name, off, _ in layout:
     87     offset_defines.append(f'DEFINE OFF_{name} {le_u64(off)}')
     88 offset_defines.append('')
     89 
     90 # 3b. Generate pointer slots (8 bytes each, ZERO8).
     91 ptr_slots = ['## --- BSS pointer slots (set by p1_main; one per BSS buffer) -----------------']
     92 for name, _, _ in layout:
     93     ptr_slots.append(f':{name}_ptr')
     94     ptr_slots.append('ZERO8')
     95 ptr_slots.append('')
     96 
     97 # 3c. Generate init code (computes ELF_end + offset, stores into each slot).
     98 init_code = ['    # --- init BSS pointer slots from ELF_end ---------------------------------']
     99 init_code.append('    la_t0 &ELF_end')
    100 for name, off, _ in layout:
    101     if off == 0:
    102         init_code.append(f'    la_t1 &{name}_ptr')
    103         init_code.append(f'    st_t0,t1,0    # {name}_ptr = ELF_end')
    104     else:
    105         init_code.append(f'    li_t1 OFF_{name}')
    106         init_code.append(f'    add_t1,t0,t1')
    107         init_code.append(f'    la_t2 &{name}_ptr')
    108         init_code.append(f'    st_t1,t2,0    # {name}_ptr = ELF_end + OFF_{name}')
    109 init_code.append('    # --- end BSS init -------------------------------------------------------')
    110 init_code.append('')
    111 
    112 # --- Pass 4: rewrite the file. -----------------------------------------------
    113 
    114 # Build a mask of lines to delete (the BSS buffer blocks).
    115 delete_mask = [False] * len(lines)
    116 for _, start, end in [(n, s, e) for (n, s, e, _) in buffers]:
    117     for k in range(start, end):
    118         delete_mask[k] = True
    119 
    120 # Find p1_main and the line right after enter_0 (insertion point for init code).
    121 p1_main_idx = None
    122 for k, ln in enumerate(lines):
    123     if ln.strip() == ':p1_main':
    124         p1_main_idx = k
    125         break
    126 assert p1_main_idx is not None, 'no :p1_main label'
    127 # Find first non-blank/non-comment line after enter_0.
    128 init_insert_idx = None
    129 for k in range(p1_main_idx + 1, len(lines)):
    130     if 'enter_0' in lines[k]:
    131         init_insert_idx = k + 1
    132         break
    133 assert init_insert_idx is not None, 'no enter_0 after :p1_main'
    134 
    135 # Find :ELF_end (insertion point for ptr_slots — just before).
    136 elf_end_idx = None
    137 for k, ln in enumerate(lines):
    138     if ln.strip() == ':ELF_end':
    139         elf_end_idx = k
    140         break
    141 assert elf_end_idx is not None
    142 
    143 # Find the constants section to insert OFFSET DEFINEs (after the last sizing
    144 # DEFINE, which I'll detect as the last DEFINE EXPR_INVALID).
    145 defines_insert_idx = None
    146 for k, ln in enumerate(lines):
    147     if ln.strip() == 'DEFINE EXPR_INVALID 1200000000000000':
    148         defines_insert_idx = k + 1
    149         break
    150 assert defines_insert_idx is not None
    151 
    152 # Build the new line list. Process in reverse so insert indices stay valid.
    153 out = list(lines)
    154 
    155 # Insert ptr_slots just before :ELF_end.
    156 out[elf_end_idx:elf_end_idx] = ptr_slots
    157 
    158 # Then we need to delete buffer blocks. But the delete mask refers to the
    159 # original `lines` indices; ptr_slots insertion shifted later indices. To
    160 # avoid that, do all index-based work on the original `lines`, then assemble
    161 # at the end.
    162 
    163 # Restart with a clean approach.
    164 out = []
    165 buffer_names = {b[0] for b in buffers}
    166 for k, ln in enumerate(lines):
    167     if delete_mask[k]:
    168         continue
    169     out.append(ln)
    170     if k == defines_insert_idx - 1:
    171         out.extend(offset_defines)
    172     if k == init_insert_idx - 1:
    173         out.extend(init_code)
    174     if k == elf_end_idx - 1:
    175         out.extend(ptr_slots)
    176 
    177 # --- Pass 5: rewrite all `la_<reg> &<buf>` references. -----------------------
    178 
    179 # Pattern: optional indent, "la_<reg> &<buf>", optional comment.
    180 # Replace with two lines: la_<reg> &<buf>_ptr ; ld_<reg>,<reg>,0
    181 la_re = re.compile(r'^(\s*)la_([a-z][a-z0-9]+) &(' + '|'.join(re.escape(n) for n in buffer_names) + r')\b(.*)$')
    182 
    183 rewritten = []
    184 ref_count = 0
    185 for ln in out:
    186     m = la_re.match(ln)
    187     if m:
    188         indent, reg, name, tail = m.group(1), m.group(2), m.group(3), m.group(4)
    189         rewritten.append(f'{indent}la_{reg} &{name}_ptr{tail}')
    190         rewritten.append(f'{indent}ld_{reg},{reg},0')
    191         ref_count += 1
    192     else:
    193         rewritten.append(ln)
    194 
    195 print(f"Rewrote {ref_count} la_<reg> &<buf> references.")
    196 
    197 # --- Pass 6: write back. -----------------------------------------------------
    198 
    199 new_text = '\n'.join(rewritten)
    200 SRC.write_text(new_text)
    201 
    202 old_lines = len(lines)
    203 new_lines = len(rewritten)
    204 print(f"M1pp.P1 lines: {old_lines} -> {new_lines}  ({new_lines-old_lines:+d})")
    205 print(f"M1pp.P1 bytes: {len(text)} -> {len(new_text)}  ({len(new_text)-len(text):+d})")