code wiki / (root) / nx_regalloc_liveness_diag.nx

nx_regalloc_liveness_diag.nx source

↩ module page · 99 lines · 4147 B

1// nx_regalloc_liveness_diag.nx -- BITS-UP DIAGNOSTIC for the disabled 2// compute_liveness. Builds the exact CFG shape build_intervals under- 3// approximates: a value defined in the entry, USED in a loop HEADER, and 4// NOT used after the loop. The back-edge (body -> header) makes the value 5// live across the body, but its last LINEAR use is in the header (which 6// precedes the body in block order), so build_intervals' def-to-last-use 7// range stops too early -> linear-scan would think the register is free in 8// the body -> clobber. compute_liveness must extend the interval across the 9// body. This test EXECUTES the allocator (does not execute the built IR); 10// it isolates the correctness question with ZERO blast radius to the 11// self-hosting backend. 12// 13// Exit 0 = both facts confirmed (build_intervals too short; compute_liveness 14// correct). Nonzero = where it diverged (see codes). 15// license_tier: ORIGINAL 16 17import "nx_syscalls.nx" 18import "nx_types.nx" 19import "nx_ir.nx" 20import "nx_regalloc.nx" 21const K_MAGIC_4096: i64 = 4096 22 23func main() -> i64 { 24 let m_raw: *u8 = sys_mmap(256) 25 let m: *Module = m_raw as *Module 26 m.name = "livediag" as *u8 27 m.functions = 0 as *Function 28 m.n_functions = 0 29 30 let f: *Function = ir_function_new(m, "main" as *u8, 4, ir_type_i64()) 31 let entry: *BasicBlock = ir_block_new(f) // block 0 32 let header: *BasicBlock = ir_block_new(f) // block 1 33 let body: *BasicBlock = ir_block_new(f) // block 2 34 let exit: *BasicBlock = ir_block_new(f) // block 3 35 36 // entry: limit = 7 + 0 ; br header 37 let limit: i64 = ir_emit_binop(entry, OP_ADD, ir_const_i64(f, 7), ir_const_i64(f, 0), ir_type_i64()) 38 ir_emit_br(entry, header) 39 40 // header: cond = limit + 1 (USES limit) ; br_cond cond -> body, exit 41 let cond: i64 = ir_emit_binop(header, OP_ADD, limit, ir_const_i64(f, 1), ir_type_i64()) 42 ir_emit_br_cond(header, cond, body, exit) 43 44 // body: tmp = 2 + 3 (does NOT use limit) ; br header (BACK-EDGE) 45 let tmp: i64 = ir_emit_binop(body, OP_ADD, ir_const_i64(f, 2), ir_const_i64(f, 3), ir_type_i64()) 46 ir_emit_br(body, header) 47 48 // exit: return 0 (limit NOT used after the loop) 49 ir_emit_return(exit, ir_const_i64(f, 0)) 50 51 // ---- run build_intervals alone, capture limit's interval ---- 52 let n: i64 = f.n_values 53 let intv_raw: *u8 = sys_mmap(n * 48 + 16) 54 let intv: *Interval = intv_raw as *Interval 55 let calls_raw: *u8 = sys_mmap(K_MAGIC_4096) 56 let calls: *i64 = calls_raw as *i64 57 let nc_raw: *u8 = sys_mmap(16) 58 let nc: *i64 = nc_raw as *i64 59 *nc = 0 60 let bs_raw: *u8 = sys_mmap(f.n_blocks * 8 + 16) 61 let bb_start: *i64 = bs_raw as *i64 62 let be_raw: *u8 = sys_mmap(f.n_blocks * 8 + 16) 63 let bb_end: *i64 = be_raw as *i64 64 65 build_intervals(f, intv, calls, nc, bb_start, bb_end) 66 67 let body_start: i64 = bb_start[2] 68 let body_end: i64 = bb_end[2] 69 let iv_bi: *Interval = intv_at(intv, limit) 70 let bi_start: i64 = iv_bi.start 71 let bi_end: i64 = iv_bi.end 72 73 // The bug: build_intervals' interval for `limit` does NOT reach the body. 74 // (last linear use is in the header, block 1, before the body block 2.) 75 var bi_covers_body: i64 = 0 76 if bi_end >= body_start { bi_covers_body = 1 } 77 78 // ---- now run compute_liveness on the SAME intervals, recapture ---- 79 compute_liveness(f, intv, bb_start, bb_end) 80 let iv_cl: *Interval = intv_at(intv, limit) 81 let cl_end: i64 = iv_cl.end 82 var cl_covers_body: i64 = 0 83 if cl_end >= body_end { cl_covers_body = 1 } 84 85 // ---- report via exit code ---- 86 // Diagnostic prints (best-effort; harness reads exit code). 87 if bi_covers_body == 1 { 88 // build_intervals already covered the body -> this CFG didn't trigger 89 // the under-approximation; the test shape is wrong, not a PASS. 90 return 30 91 } 92 if cl_covers_body == 0 { 93 // compute_liveness FAILED to extend across the body -> it is itself 94 // buggy (the real root cause is in compute_liveness). 95 return 40 96 } 97 // build_intervals too short (bug reproduced) AND compute_liveness correct. 98 return 0 99}