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}