code wiki / (root) / nx_cc_splice_bisect.nx

nx_cc_splice_bisect.nx source

↩ module page · 305 lines · 10794 B

1// nx_cc_splice_bisect.nx -- PER-FUNCTION COMPILER-CODEGEN BISECTOR. 2// 3// Team organ (Doctor's localizer, sibling of nx_cc_equiv_gate): when 4// a challenger compiler's output misbehaves, this finds WHICH 5// function's codegen carries the defect -- no awk, no shell loops, 6// pure NishiLang file IO + fork/execve/wait4 (sovereign judging law). 7// 8// Method (asm splice): for every function whose body DIFFERS between 9// the two asm files, build a FRANKEN image = base.s with only that 10// one function's body replaced by the donor's, assemble+link it, and 11// run the franken compiler on a probe source. A function is a 12// BREAKER iff its splice alone flips the probe verdict. Functions 13// are self-contained ABI units, so single-function splices are valid 14// experiment isolation. 15// 16// Born 2026-06-10 bisecting the awakened-LICM self-host breakage 17// (ledger T#opt-dom-licm-dormant-paths): corpus rows all GREEN, 18// self-host RED -- the defect lives in specific big-function hoists, 19// and this organ names them. 20// 21// Usage: nx_cc_splice_bisect <base.s> <donor.s> [probe.nx] 22// base.s asm of the BLESSED compiler compiling some source 23// donor.s asm of the CHALLENGER compiling the SAME source 24// probe.nx source the franken compiler must still compile 25// (default runtime/_derefcast_minrepro.nx; compile rc 0 26// + non-empty asm = behaves) 27// Exit 0 iff NO breaker found (prints/logs the per-function table 28// to knowledge/status/cc_splice_bisect.log either way). 29// 30// license_tier: ORIGINAL 31 32import "nx_syscalls.nx" 33 34const SB_MAX_FNS: i64 = 4096 35 36func sb_puts2(logfd: i64, s: *u8) -> i64 { 37 var n: i64 = 0 38 while s[n] != (0 as u8) { n = n + 1 } 39 sys_write(1, s, n) 40 if logfd > 0 { sys_write(logfd, s, n) } 41 return 0 42} 43 44func sb_putn2(logfd: i64, v: i64) -> i64 { 45 let t: *u8 = sys_mmap(28) 46 let o: *u8 = sys_mmap(28) 47 var m: i64 = v 48 var neg: i64 = 0 49 if m < 0 { neg = 1; m = 0 - m } 50 var k: i64 = 0 51 if m == 0 { t[0] = 48 as u8; k = 1 } 52 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 53 var w: i64 = 0 54 if neg == 1 { o[0] = 45 as u8; w = 1 } 55 var i: i64 = 0 56 while i < k { o[w + i] = t[k - 1 - i]; i = i + 1 } 57 sys_write(1, o, w + k) 58 if logfd > 0 { sys_write(logfd, o, w + k) } 59 return 0 60} 61 62func sb_putsn2(logfd: i64, s: *u8, n: i64) -> i64 { 63 sys_write(1, s, n) 64 if logfd > 0 { sys_write(logfd, s, n) } 65 return 0 66} 67 68// Is buf[off..] the start of a top-level function label line? 69// Shape: ^[A-Za-z_][A-Za-z0-9_]*:\n (directives are indented or 70// dot-prefixed; .L local labels are dot-prefixed -- both excluded.) 71// Returns the name length, or 0. 72func sb_label_len(buf: *u8, off: i64, end: i64) -> i64 { 73 var c: i64 = buf[off] & 0xff 74 var alpha: i64 = 0 75 if c >= 65 { if c <= 90 { alpha = 1 } } 76 if c >= 97 { if c <= 122 { alpha = 1 } } 77 if c == 95 { alpha = 1 } 78 if alpha == 0 { return 0 } 79 var p: i64 = off 80 while p < end { 81 c = buf[p] & 0xff 82 if c == 58 { // ':' 83 if p + 1 < end { 84 if (buf[p + 1] & 0xff) != 10 { return 0 } 85 } 86 return p - off 87 } 88 var ok: i64 = 0 89 if c >= 65 { if c <= 90 { ok = 1 } } 90 if c >= 97 { if c <= 122 { ok = 1 } } 91 if c >= 48 { if c <= 57 { ok = 1 } } 92 if c == 95 { ok = 1 } 93 if ok == 0 { return 0 } 94 p = p + 1 95 } 96 return 0 97} 98 99// Scan an asm buffer into parallel fn tables. 100// tab layout per entry: name_off, name_len, body_off, body_end. 101// body = from the label line through the byte before the next 102// top-level label (carries .size/.globl of the next fn's preamble -- 103// harmless: names are identical on both sides of every splice). 104// Returns the number of functions found. 105func sb_scan(buf: *u8, n: i64, tab: *i64) -> i64 { 106 var cnt: i64 = 0 107 var off: i64 = 0 108 var at_line_start: i64 = 1 109 while off < n { 110 if at_line_start == 1 { 111 let ll: i64 = sb_label_len(buf, off, n) 112 if ll > 0 { 113 if cnt > 0 { 114 tab[(cnt - 1) * 4 + 3] = off // close previous body 115 } 116 if cnt < SB_MAX_FNS { 117 tab[cnt * 4] = off 118 tab[cnt * 4 + 1] = ll 119 tab[cnt * 4 + 2] = off 120 tab[cnt * 4 + 3] = n 121 cnt = cnt + 1 122 } 123 } 124 } 125 if (buf[off] & 0xff) == 10 { at_line_start = 1 } 126 if (buf[off] & 0xff) != 10 { at_line_start = 0 } 127 off = off + 1 128 } 129 return cnt 130} 131 132func sb_names_eq(a: *u8, ao: i64, al: i64, b: *u8, bo: i64, bl: i64) -> i64 { 133 if al != bl { return 0 } 134 var i: i64 = 0 135 while i < al { 136 if a[ao + i] != b[bo + i] { return 0 } 137 i = i + 1 138 } 139 return 1 140} 141 142func sb_bodies_eq(a: *u8, as_: i64, ae: i64, b: *u8, bs: i64, be: i64) -> i64 { 143 if ae - as_ != be - bs { return 0 } 144 var i: i64 = 0 145 let len: i64 = ae - as_ 146 while i < len { 147 if a[as_ + i] != b[bs + i] { return 0 } 148 i = i + 1 149 } 150 return 1 151} 152 153// fork + redirects + execve; parent waits; returns RAW wait status. 154func sb_run(path: *u8, argv: *i64, envp: *i64, redir_out: i64, redir_err: i64) -> i64 { 155 let pid: i64 = sys_fork() 156 if pid == 0 { 157 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) } 158 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) } 159 sys_execve(path, argv, envp) 160 sys_exit(127) 161 } 162 let st: *i64 = sys_mmap(16) as *i64 163 sys_wait4(pid, st, 0) 164 return st[0] 165} 166 167// Write base with one fn body swapped for the donor's -> /tmp/sb_frank.s 168func sb_write_franken(base: *u8, base_n: i64, bs: i64, be: i64, 169 donor: *u8, ds: i64, de: i64) -> i64 { 170 let fd: i64 = sys_openat_wr("/tmp/sb_frank.s\x00" as *u8, 0x1a4) 171 if fd < 0 { return 0 - 1 } 172 sys_write(fd, base, bs) 173 sys_write(fd, donor + ds, de - ds) 174 sys_write(fd, base + be, base_n - be) 175 sys_close(fd) 176 return 0 177} 178 179// as + ld + run franken compiler on probe. Returns: 180// 0 behaves (probe compiles, rc 0, non-empty asm) 181// 1 as-fail, 2 ld-fail, else the RAW probe wait status. 182func sb_try_franken(probe: *u8, envp: *i64, devnull: i64) -> i64 { 183 let asbin: *u8 = "/usr/bin/as\x00" 184 let a2: *i64 = sys_mmap(8 * 6) as *i64 185 a2[0] = asbin as i64 186 a2[1] = "/tmp/sb_frank.s\x00" as *u8 as i64 187 a2[2] = "-o\x00" as *u8 as i64 188 a2[3] = "/tmp/sb_frank.o\x00" as *u8 as i64 189 a2[4] = 0 190 if sb_run(asbin, a2, envp, 0 - 1, devnull) != 0 { return 1 } 191 192 let ldbin: *u8 = "/usr/bin/ld\x00" 193 let a3: *i64 = sys_mmap(8 * 6) as *i64 194 a3[0] = ldbin as i64 195 a3[1] = "/tmp/sb_frank.o\x00" as *u8 as i64 196 a3[2] = "-o\x00" as *u8 as i64 197 a3[3] = "/tmp/sb_frank.elf\x00" as *u8 as i64 198 a3[4] = 0 199 if sb_run(ldbin, a3, envp, 0 - 1, devnull) != 0 { return 2 } 200 201 let frank: *u8 = "/tmp/sb_frank.elf\x00" 202 let a4: *i64 = sys_mmap(8 * 4) as *i64 203 a4[0] = frank as i64 204 a4[1] = probe as i64 205 a4[2] = 0 206 let outfd: i64 = sys_openat_wr("/tmp/sb_probe.s\x00" as *u8, 0x1a4) 207 let st: i64 = sb_run(frank, a4, envp, outfd, devnull) 208 sys_close(outfd) 209 if st != 0 { return st } 210 // Probe asm must be non-empty (a 0-byte success is a lie). 211 let pf: i64 = sys_openat_rd("/tmp/sb_probe.s\x00" as *u8) 212 if pf < 0 { return 3 } 213 let sz: i64 = sys_lseek(pf, 0, 2) 214 sys_close(pf) 215 if sz < 64 { return 3 } 216 return 0 217} 218 219func main(argc: i64, argv: *i64) -> i64 { 220 if argc < 3 { 221 sb_puts2(0 - 1, "usage: nx_cc_splice_bisect <base.s> <donor.s> [probe.nx]\n\x00" as *u8) 222 return 2 223 } 224 let base_path: *u8 = argv[1] as *u8 225 let donor_path: *u8 = argv[2] as *u8 226 var probe: *u8 = "runtime/_derefcast_minrepro.nx\x00" 227 if argc >= 4 { probe = argv[3] as *u8 } 228 229 let bl_p: *i64 = sys_mmap(16) as *i64 230 let base: *u8 = sys_read_file(base_path, bl_p) 231 let base_n: i64 = bl_p[0] 232 let dl_p: *i64 = sys_mmap(16) as *i64 233 let donor: *u8 = sys_read_file(donor_path, dl_p) 234 let donor_n: i64 = dl_p[0] 235 if base_n < 1 { return 3 } 236 if donor_n < 1 { return 3 } 237 238 let btab: *i64 = sys_mmap(SB_MAX_FNS * 32 + 64) as *i64 239 let dtab: *i64 = sys_mmap(SB_MAX_FNS * 32 + 64) as *i64 240 let bn: i64 = sb_scan(base, base_n, btab) 241 let dn: i64 = sb_scan(donor, donor_n, dtab) 242 243 let envp: *i64 = sys_mmap(8 * 2) as *i64 244 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64 245 envp[1] = 0 246 let devnull: i64 = sys_openat_wr("/dev/null\x00" as *u8, 0x1a4) 247 let logfd: i64 = sys_openat_append("knowledge/status/cc_splice_bisect.log\x00" as *u8, 0x1a4) 248 249 sb_puts2(logfd, "SPLICE-BISECT epoch=\x00" as *u8) 250 sb_putn2(logfd, sys_now_realtime_sec()) 251 sb_puts2(logfd, " base_fns=\x00" as *u8) 252 sb_putn2(logfd, bn) 253 sb_puts2(logfd, " donor_fns=\x00" as *u8) 254 sb_putn2(logfd, dn) 255 sb_puts2(logfd, "\n\x00" as *u8) 256 257 var differing: i64 = 0 258 var breakers: i64 = 0 259 var bi: i64 = 0 260 while bi < bn { 261 let b_no: i64 = btab[bi * 4] 262 let b_nl: i64 = btab[bi * 4 + 1] 263 let b_bs: i64 = btab[bi * 4 + 2] 264 let b_be: i64 = btab[bi * 4 + 3] 265 // find donor fn with same name 266 var di: i64 = 0 267 var found: i64 = 0 - 1 268 while di < dn { 269 if sb_names_eq(base, b_no, b_nl, donor, dtab[di * 4], dtab[di * 4 + 1]) == 1 { 270 found = di 271 di = dn 272 } 273 di = di + 1 274 } 275 if found >= 0 { 276 let d_bs: i64 = dtab[found * 4 + 2] 277 let d_be: i64 = dtab[found * 4 + 3] 278 if sb_bodies_eq(base, b_bs, b_be, donor, d_bs, d_be) == 0 { 279 differing = differing + 1 280 sb_write_franken(base, base_n, b_bs, b_be, donor, d_bs, d_be) 281 let rc: i64 = sb_try_franken(probe, envp, devnull) 282 sb_puts2(logfd, "SPLICE-BISECT fn=\x00" as *u8) 283 sb_putsn2(logfd, base + b_no, b_nl) 284 sb_puts2(logfd, " probe_st=\x00" as *u8) 285 sb_putn2(logfd, rc) 286 if rc == 0 { sb_puts2(logfd, " verdict=BEHAVES\n\x00" as *u8) } 287 if rc != 0 { 288 sb_puts2(logfd, " verdict=BREAKER\n\x00" as *u8) 289 breakers = breakers + 1 290 } 291 } 292 } 293 bi = bi + 1 294 } 295 296 sb_puts2(logfd, "SPLICE-BISECT differing=\x00" as *u8) 297 sb_putn2(logfd, differing) 298 sb_puts2(logfd, " breakers=\x00" as *u8) 299 sb_putn2(logfd, breakers) 300 if breakers == 0 { sb_puts2(logfd, " verdict=CLEAN\n\x00" as *u8) } 301 if breakers != 0 { sb_puts2(logfd, " verdict=LOCALIZED\n\x00" as *u8) } 302 if logfd > 0 { sys_close(logfd) } 303 if breakers == 0 { return 0 } 304 return 1 305}