code wiki / _hdl_build / nx_doctor_asm_fix.nx
nx_doctor_asm_fix.nx source
↩ module page · 88 lines · 3453 B
1// nx_doctor_asm_fix.nx -- the DOCTOR fixes the miscompile in the MACHINE CODE, from the
2// Engineer's artifact (which register, which call line). The fix is the textbook caller-
3// save proven correct by the G1 allocator model (nx_regalloc_calls): a value that must
4// survive a call is SPILLED across it. At the assembly level that is `pushq %reg` right
5// before the offending call and `popq %reg` right after -- the call's clobber of the
6// caller-saved register no longer matters, because the value is restored from the stack.
7// One emit pass, milliseconds. RACI: the Doctor only PRODUCES the patched candidate; the
8// Engineer re-verifies (re-scan + execute) and the Council admits. Depends only on
9// syscalls -- decoupled from the Engineer. license_tier: ORIGINAL
10
11import "nx_syscalls.nx"
12const K_MAGIC_4096: i64 = 4096
13
14func doc_regname(id: i64) -> *u8 {
15 if id == 0 { return "rax" as *u8 }
16 if id == 2 { return "rcx" as *u8 }
17 if id == 3 { return "rdx" as *u8 }
18 if id == 4 { return "rsi" as *u8 }
19 if id == 5 { return "rdi" as *u8 }
20 if id == 8 { return "r8" as *u8 }
21 if id == 9 { return "r9" as *u8 }
22 if id == 10 { return "r10" as *u8 }
23 if id == 11 { return "r11" as *u8 }
24 return "rax" as *u8
25}
26
27func doc_emit(out: *u8, oi: i64, s: *u8) -> i64 {
28 var j: i64 = 0
29 while s[j] != (0 as u8) { out[oi] = s[j]; oi = oi + 1; j = j + 1 }
30 return oi
31}
32
33func doc_write_file2(path: *u8, buf: *u8, len: i64) -> i64 {
34 let fd: i64 = sys_openat_wr(path, 0x1a4)
35 if fd < 0 { return 0 - 1 }
36 sys_write(fd, buf, len)
37 sys_close(fd)
38 return 0
39}
40
41// patch in_path -> out_path: wrap each flagged call line with pushq/popq of its register.
42// fl_line[k] = the call's line index; fl_reg[k] = the caller-saved register id to preserve.
43// Returns the number of (push+pop) wraps emitted.
44func doc_asm_fix(in_path: *u8, out_path: *u8, fl_reg: *i64, fl_line: *i64, nflags: i64) -> i64 {
45 let lenp: *i64 = sys_mmap(8) as *i64
46 let buf: *u8 = sys_read_file(in_path, lenp)
47 let blen: i64 = lenp[0]
48 let out: *u8 = sys_mmap(blen * 2 + K_MAGIC_4096)
49 var oi: i64 = 0
50 var wraps: i64 = 0
51 var line: i64 = 0
52 var i: i64 = 0
53 while i < blen {
54 var le: i64 = i
55 var d: i64 = 0
56 while d == 0 { if le >= blen { d = 1 } else { if buf[le] == (10 as u8) { d = 1 } else { le = le + 1 } } }
57
58 // pushq %reg before the line, for each flag on this line index (in order)
59 var f: i64 = 0
60 while f < nflags {
61 if fl_line[f] == line {
62 oi = doc_emit(out, oi, " pushq %" as *u8) // leading tab
63 oi = doc_emit(out, oi, doc_regname(fl_reg[f]))
64 oi = doc_emit(out, oi, "\n" as *u8)
65 wraps = wraps + 1
66 }
67 f = f + 1
68 }
69 // the original line + newline
70 var p: i64 = i
71 while p < le { out[oi] = buf[p]; oi = oi + 1; p = p + 1 }
72 out[oi] = 10 as u8; oi = oi + 1
73 // popq %reg after, in REVERSE (LIFO restore)
74 var g: i64 = nflags - 1
75 while g >= 0 {
76 if fl_line[g] == line {
77 oi = doc_emit(out, oi, " popq %" as *u8)
78 oi = doc_emit(out, oi, doc_regname(fl_reg[g]))
79 oi = doc_emit(out, oi, "\n" as *u8)
80 }
81 g = g - 1
82 }
83 i = le + 1
84 line = line + 1
85 }
86 doc_write_file2(out_path, out, oi)
87 return wraps
88}