code wiki / _hdl_build / nx_regalloc_calls.nx

nx_regalloc_calls.nx

buildroot/runtime/_hdl_build/nx_regalloc_calls.nx

6392 B142 linesdepth 4pulls 4 transitivereach 1 importersview sourcekind librarytopic regalloc
docsdependenciesstructsconstsfunctions

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

nx_regalloc_interp.nx nx_regalloc_calls.nx nx_regalloc_calls_test.nx

imports: nx_regalloc_interp.nx

imported by: nx_regalloc_calls_test.nx

structs

none

consts

23const ROP_CALL: i64 = 8
24const RC_POISON: i64 = 0 - 999999 // value a clobbered caller-saved register holds

functions

28func rc_spans_call(v: i64, last_use: *i64, is_call: *i64, n: i64) -> i64
40func ra_linscan_calls(n: i64, nreg: i64, ncaller: i64, last_use: *i64, is_call: *i64, alloc: *i64) -> i64
called by 1: main calls 1: rc_spans_call
79func ra_validate_calls(n: i64, last_use: *i64, is_call: *i64, alloc: *i64, ncaller: i64) -> i64
called by 1: main calls 1: rc_spans_call
93func rc_interp_ref(n: i64, op: *i64, u0: *i64, u1: *i64, imm: *i64, is_call: *i64,
called by 1: run_trials calls 1: ri_op_eval
117func rc_interp_alloc(n: i64, nreg: i64, ncaller: i64, op: *i64, u0: *i64, u1: *i64, imm: *i64,
called by 1: run_trials calls 2: ri_getri_op_eval