code wiki / _hdl_build / nx_regalloc_race_kernel_test.nx
nx_regalloc_race_kernel_test.nx
buildroot/runtime/_hdl_build/nx_regalloc_race_kernel_test.nx
about
nx_regalloc_race_kernel_test.nx -- point the proven allocator at the EXACT hot
loop we lose ~1.9x to C on (nx_race_kernel), to quantify the lever concretely.
The race kernel's loop body, as a straight-line IR (each line defines one vreg):
c_in, acc_in, i_in live-ins (already in registers)
t0 = c_in * K (3)
c1 = t0 + A (4)
t1 = c1 >> 31 (5)
c2 = c1 ^ t1 (6) <- next iteration's c (live-out)
t2 = c2 & 65535 (7)
acc1 = acc_in + t2 (8) <- next iteration's acc (live-out)
i1 = i_in + 1 (9) <- next iteration's i (live-out)
cmp = i1 < K (10)
Pressure stays low (~4-5 live), so on x86-64's ~8+ usable GP registers the WHOLE
body fits in registers: ZERO memory traffic. The stack-resident codegen instead
loads each operand + stores each result every iteration -- run 20,000,000 times,
that is the ~1.9x. This shows the allocator turns the hot loop memory-free, the
direct path to closing the gap.
Known answer: 0 spills (fits in registers) AND regalloc memory traffic = 0 AND
the stack baseline is large -> exit 0.
dependencies 1 imports · 0 importers
imports: nx_regalloc_linscan.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 37 | const RKB_N: i64 = 11 |
| 38 | const RKB_NREG: i64 = 8 // x86-64 has ~14 GP regs; 8 is conservative |
| 39 | const RKB_ITERS: i64 = 20000000 // the race kernel's iteration count |
functions
| 26 | func rk_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } |
| 27 | func rk_emit(name: *u8, v: i64) -> i64 |
| 41 | func main() -> i64 |