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}