code wiki / _hdl_build / nx_regalloc_linscan.nx
nx_regalloc_linscan.nx
buildroot/runtime/_hdl_build/nx_regalloc_linscan.nx
about
nx_regalloc_linscan.nx -- linear-scan register allocation, bits-up + PROVABLE.
This is the keystone lever the ONGOING race vs C keeps pointing at: Nishi's
stack/memory-resident codegen loads every operand and stores every result (the
~2x speed + ~6x code-density tax the race measured). Register allocation keeps
live values in registers; only SPILLS touch memory. Built STANDALONE + proven
first (NOT hand-patched into the protected self-hosting compiler -- a prior
"g1 regalloc" attempt silently miscompiled high-pressure functions; we prove
soundness here, then the loop absorbs it into codegen under governance).
Algorithm: Poletto & Sarkar, "Linear Scan Register Allocation," ACM TOPLAS 1999
(the alternative is Chaitin graph-coloring, SIGPLAN 1982; linear scan is faster
and good enough -- the right fit for a spore that re-derives its stack on deploy).
Model IR: a straight-line block of n instructions; instruction i DEFINES virtual
register i and USES up to two prior vregs (u0[i], u1[i]; -1 = none). The live
interval of vreg v is [v (its def), last_use[v]]. Linear scan allocates nreg
physical registers across the intervals, spilling the farthest-future interval
when it runs out (the standard heuristic).
dependencies 1 imports · 3 importers
imports: nx_syscalls.nx
imported by: nx_regalloc_interp.nxnx_regalloc_linscan_test.nxnx_regalloc_race_kernel_test.nx
structs
| none |
consts
| 23 | const RA_SPILL: i64 = 0 - 1 |
functions
| 26 | func ra_compute_last_use(n: i64, u0: *i64, u1: *i64, last_use: *i64) -> i64 |
| 41 | func ra_linscan(n: i64, nreg: i64, last_use: *i64, alloc: *i64) -> i64 |
| 97 | func ra_validate(n: i64, last_use: *i64, alloc: *i64, nreg: i64) -> i64 |
| 120 | func ra_baseline_mem(n: i64, u0: *i64, u1: *i64) -> i64 |
| 133 | func ra_regalloc_mem(n: i64, u0: *i64, u1: *i64, alloc: *i64) -> i64 |