code wiki / _hdl_build / nx_regalloc_calls.nx
nx_regalloc_calls.nx
buildroot/runtime/_hdl_build/nx_regalloc_calls.nx
about
nx_regalloc_calls.nx -- the G1 register allocator's KEYSTONE capability: correct
handling of values LIVE ACROSS A CALL. This is the exact bug the bootstrap compiler
has (project-knowngood-compiler-regpressure-miscompile): a value held live across a
call is left in a CALLER-SAVED register and clobbered by the call, so a later read
gets garbage and a comparison flips. The base linear scan (nx_regalloc_linscan) models
live intervals but knows nothing about calls or the caller/callee-saved ABI split --
so it can place a live-across-call value in a caller-saved register. That is the gap.
This closes it and PROVES it three ways:
(1) ra_linscan_calls -- a vreg that spans a call may take only a CALLEE-saved reg,
else it SPILLS (a spilled value is in memory and survives any call).
(2) ra_validate_calls -- STATIC proof: no live-across-call vreg sits in a caller-saved reg.
(3) rc_interp_alloc -- EXECUTION model where a call POISONS caller-saved registers,
so a wrong allocation actually computes a wrong answer (the bug, reproduced).
The differential (call-unaware diverges / call-aware matches the reference) is the proof.
x86-64 SysV ABI: caller-saved = rax,rcx,rdx,rsi,rdi,r8-r11 (clobbered by a call);
callee-saved = rbx,rbp,r12-r15 (survive a call). We model regs [0,ncaller) caller-saved,
[ncaller,nreg) callee-saved. license_tier: ORIGINAL
dependencies 1 imports · 1 importers
imports: nx_regalloc_interp.nx
imported by: nx_regalloc_calls_test.nx
structs
| none |
consts
| 23 | const ROP_CALL: i64 = 8 |
| 24 | const RC_POISON: i64 = 0 - 999999 // value a clobbered caller-saved register holds |
functions
| 28 | func rc_spans_call(v: i64, last_use: *i64, is_call: *i64, n: i64) -> i64 |
| 40 | func ra_linscan_calls(n: i64, nreg: i64, ncaller: i64, last_use: *i64, is_call: *i64, alloc: *i64) -> i64 |
| 79 | func ra_validate_calls(n: i64, last_use: *i64, is_call: *i64, alloc: *i64, ncaller: i64) -> i64 |
| 93 | func rc_interp_ref(n: i64, op: *i64, u0: *i64, u1: *i64, imm: *i64, is_call: *i64, |
| 117 | func rc_interp_alloc(n: i64, nreg: i64, ncaller: i64, op: *i64, u0: *i64, u1: *i64, imm: *i64, |