code wiki / (root) / nx_cc_equiv_gate.nx

nx_cc_equiv_gate.nx source

↩ module page · 463 lines · 21703 B

1// nx_cc_equiv_gate.nx -- COMPILER DIFFERENTIAL-BEHAVIOR ORACLE: miscompile-detection for the sovereign toolchain (EMI-style baseline-vs-challenger build + stdout byte-equal + self-host stage). 2// 3// Team organ (Engineer-owned) born from the dom/licm dormant-paths 4// discovery (ledger T#opt-dom-licm-dormant-paths, 2026-06-10): the 5// deref-cast precedence bug had silently disabled 4 optimizer reads 6// since they were written, and AWAKENING them broke the self-host at 7// generation 3 -- a class of defect no single-binary test catches. 8// This gate judges ANY compiler mutation by BEHAVIOR, not by asm: 9// 10// For each corpus row (deterministic, network-free modules): 11// build with BASELINE cc -> run -> raw wait4 status + stdout bytes 12// build with CHALLENGER cc -> run -> same 13// row PASS iff both build, statuses EQUAL, stdout BYTE-EQUAL. 14// (Different codegen/hoisting is fine; different BEHAVIOR is not.) 15// 16// SELF-HOST stage (the gen3-class catch): 17// challenger compiles runtime/nx_compile_x86.nx -> gen2 binary; 18// gen2 must then compile a probe module whose binary runs exit 0. 19// This is exactly where the awakened-LICM breakage surfaced 20// (gen3: "unknown type name 'nx_size'"). 21// 22// Usage: nx_cc_equiv_gate [challenger_elf] 23// challenger default: /tmp/cc_challenger.elf 24// baseline (always): _offc/nx_compile_x86_native.elf 25// Run with CWD=nxc2/. Exit 0 iff all rows + self-host PASS. 26// Verdicts stream to stdout AND append to the durable log 27// knowledge/status/cc_equiv_gate.log (Archivist rule). 28// 29// ORCHESTRATION is pure NishiLang fork/dup3/execve/wait4 -- the 30// sovereign exit-judging law (no shell, no $?). 31// 32// license_tier: ORIGINAL 33 34import "nx_syscalls.nx" 35import "nx_gate_verdict.nx" 36 37const EQ_ROWS: i64 = 10 38 39func eq_puts2(logfd: i64, s: *u8) -> i64 { 40 var n: i64 = 0 41 while s[n] != (0 as u8) { n = n + 1 } 42 sys_write(1, s, n) 43 if logfd > 0 { sys_write(logfd, s, n) } 44 return 0 45} 46 47func eq_putn2(logfd: i64, v: i64) -> i64 { 48 let t: *u8 = sys_mmap(28) 49 let o: *u8 = sys_mmap(28) 50 var m: i64 = v 51 var neg: i64 = 0 52 if m < 0 { neg = 1; m = 0 - m } 53 var k: i64 = 0 54 if m == 0 { t[0] = 48 as u8; k = 1 } 55 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 56 var w: i64 = 0 57 if neg == 1 { o[0] = 45 as u8; w = 1 } 58 var i: i64 = 0 59 while i < k { o[w + i] = t[k - 1 - i]; i = i + 1 } 60 sys_write(1, o, w + k) 61 if logfd > 0 { sys_write(logfd, o, w + k) } 62 return 0 63} 64 65func eq_cat(dst: *u8, off: i64, s: *u8) -> i64 { 66 var i: i64 = 0 67 while s[i] != (0 as u8) { dst[off + i] = s[i]; i = i + 1 } 68 return off + i 69} 70 71// Decimal-append v into dst at off; returns new off (buffer sibling of eq_putn2). 72func eq_catn(dst: *u8, off: i64, v: i64) -> i64 { 73 let t: *u8 = sys_mmap(28) 74 var m: i64 = v 75 if m < 0 { m = 0 - m } 76 var k: i64 = 0 77 if m == 0 { t[0] = 48 as u8; k = 1 } 78 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 79 var i: i64 = 0 80 while i < k { dst[off + i] = t[k - 1 - i]; i = i + 1 } 81 return off + k 82} 83 84// "<pre><nonce><suf>" heap path. PER-RUN UNIQUE TEMPS (2026-08-05): every temp below used to 85// be one FIXED shared path, so two concurrent gate runs (two seats, or one transport retry -- 86// OBSERVED live: epochs 1785972850/1785972865, 15s apart) overwrote each other's products; 87// worst, both raced the SAME _build/_eq_asm_out.elf rename source, so run A could rename run 88// B's assembled binary into its own slot and compare cross-wired artifacts. Same fixed-shared- 89// capture-path class as the mgmt /api/build seq236 bug. A differential oracle whose two sides 90// can belong to DIFFERENT runs is not a measurement. 91func eq_uniq(pre: *u8, nonce: i64, suf: *u8) -> *u8 { 92 let b: *u8 = sys_mmap(128) 93 var o: i64 = eq_cat(b, 0, pre) 94 o = eq_catn(b, o, nonce) 95 o = eq_cat(b, o, suf) 96 b[o] = 0 as u8 97 return b 98} 99 100// Run nonce: 8 CSPRNG bytes -> positive decimal < 1e9; epoch-seconds fallback. Sub-second 101// double-fires (the transport-retry shape) get distinct nonces where epoch alone collides. 102func eq_nonce() -> i64 { 103 let fd: i64 = sys_openat_rd("/dev/urandom\x00" as *u8) 104 if fd < 0 { return sys_now_realtime_sec() } 105 let b: *u8 = sys_mmap(16) 106 let n: i64 = sys_read(fd, b, 8) 107 sys_close(fd) 108 if n < 8 { return sys_now_realtime_sec() } 109 var v: i64 = 0 110 var i: i64 = 0 111 while i < 8 { v = (v << 8) | (b[i] as i64); i = i + 1 } 112 if v < 0 { v = 0 - v } 113 if v < 0 { v = 0 } 114 return v % 1000000000 115} 116 117// fork + redirects + execve; parent waits; returns RAW wait status. 118func eq_run(path: *u8, argv: *i64, envp: *i64, redir_out: i64, redir_err: i64) -> i64 { 119 let pid: i64 = sys_fork() 120 if pid == 0 { 121 if redir_out >= 0 { sys_dup3(redir_out, 1, 0) } 122 if redir_err >= 0 { sys_dup3(redir_err, 2, 0) } 123 sys_execve(path, argv, envp) 124 sys_exit(127) 125 } 126 let st: *i64 = sys_mmap(16) as *i64 127 sys_wait4(pid, st, 0) 128 return st[0] 129} 130 131// Compile src_path with cc_path -> elf_out via as+ld. 132// Returns 0 ok, 1 compile-fail, 2 as-fail, 3 ld-fail. 133// -1 missing, else byte size. The 127 hunt: name WHICH stage produced nothing. 134func eq_fsize(path: *u8) -> i64 { 135 let fd: i64 = sys_openat_rd(path) 136 if fd < 0 { return 0 - 1 } 137 let sz: i64 = sys_lseek(fd, 0, 2) 138 sys_close(fd) 139 return sz 140} 141 142// ⚠THE `devnull` ARG IS THE COMPILER'S STDERR AND IT USED TO SWALLOW EVERY DIAGNOSTIC. 143// A row could report build_b=1 (challenger refused) with NO WAY to learn WHY -- the exact 144// nx_parse message that names the offending call/callee was written to /dev/null, so a RED 145// row said only THAT it failed. That turned a 30-second read into a multi-session hunt on the 146// call-arg type residue (2026-08-05). ★A GATE THAT REPORTS A FAILURE WITHOUT ITS DIAGNOSTIC 147// MAKES THE FAILURE UNACTIONABLE. Callers now pass a real log fd; the cc's stderr lands in 148// /tmp/eq_cc_a_<nonce>.log (baseline) and /tmp/eq_cc_b_<nonce>.log (challenger, per-run nonce'd 149// -- kept on RED, named in the CC-EQUIV cc-logs-kept line), and their sizes are reported 150// per row so a nonzero cclog is visible in the verdict line itself. 151func eq_build(cc_path: *u8, src_path: *u8, s_tmp: *u8, o_tmp: *u8, 152 elf_out: *u8, envp: *i64, devnull: i64, 153 asm_tmp: *u8, asm_log: *u8) -> i64 { 154 let a1: *i64 = sys_mmap(8 * 4) as *i64 155 a1[0] = cc_path as i64; a1[1] = src_path as i64; a1[2] = 0 156 let sfd: i64 = sys_openat_wr(s_tmp, 0x1a4) 157 let st1: i64 = eq_run(cc_path, a1, envp, sfd, devnull) 158 sys_close(sfd) 159 if st1 != 0 { return 1 } 160 161 // SOVEREIGN assemble+link with nxasm (NO gcc/binutils) -- re-parents the self-host PROOF 162 // itself to Nishi (X-SOV-EQUIV-NXASM; before this the proof ran THROUGH GNU as+ld, so the 163 // sovereignty proof was non-sovereign). nxasm emits the ELF directly (.s -> ELF, no .o). 164 // Output to a NEUTRAL /tmp temp then rename over elf_out -- the R1-T1-004 ctx-x-output-path 165 // workaround nx_sov_build_run uses (certain target strings exit 6 from sovereign parents). 166 let nxasm: *u8 = "_offc/nxasm_x86_main.elf\x00" 167 let tmpe: *u8 = asm_tmp 168 let a2: *i64 = sys_mmap(8 * 6) as *i64 169 a2[0] = nxasm as i64 170 a2[1] = s_tmp as i64 171 a2[2] = tmpe as i64 172 a2[3] = 0 173 let alog: i64 = sys_openat_wr(asm_log, 0x1a4) 174 let arc: i64 = eq_run(nxasm, a2, envp, alog, alog) 175 sys_close(alog) 176 if arc != 0 { return 2 } 177 // the rename result was DISCARDED -- a failed rename left elf_out missing while eq_build 178 // still returned 0, so the exec-127 downstream looked like a compiler fault. 179 if sys_renameat(tmpe, elf_out) != 0 { return 3 } 180 // nxasm writes 0644, so the product was NOT EXECUTABLE and every exec died 127. Both sides died 181 // identically, so out_eq compared two empty files and every row reported PASS -- a GREEN that 182 // measured nothing. ★TWO FAILURES THAT AGREE ARE NOT AN EQUIVALENCE. 183 sys_fchmodat(elf_out, 0x1ed) 184 return 0 185} 186 187// Run elf with stdout captured to out_path; returns RAW wait status. 188func eq_run_capture(elf: *u8, out_path: *u8, envp: *i64, devnull: i64) -> i64 { 189 let ofd: i64 = sys_openat_wr(out_path, 0x1a4) 190 let a: *i64 = sys_mmap(8 * 2) as *i64 191 a[0] = elf as i64; a[1] = 0 192 let st: i64 = eq_run(elf, a, envp, ofd, devnull) 193 sys_close(ofd) 194 return st 195} 196 197// Byte-compare two files. 1 equal, 0 different / unreadable. 198func eq_files_equal(pa: *u8, pb: *u8) -> i64 { 199 let fa: i64 = sys_openat_rd(pa) 200 let fb: i64 = sys_openat_rd(pb) 201 if fa < 0 { if fb < 0 { return 1 } } 202 if fa < 0 { sys_close(fb); return 0 } 203 if fb < 0 { sys_close(fa); return 0 } 204 let ba: *u8 = sys_mmap(65536) 205 let bb: *u8 = sys_mmap(65536) 206 var equal: i64 = 1 207 var more: i64 = 1 208 while more == 1 { 209 let na: i64 = sys_read(fa, ba, 65536) 210 let nb: i64 = sys_read(fb, bb, 65536) 211 if na != nb { equal = 0; more = 0 } 212 if more == 1 { 213 if na <= 0 { more = 0 } 214 var i: i64 = 0 215 while i < na { 216 if ba[i] != bb[i] { equal = 0; i = na; more = 0 } 217 i = i + 1 218 } 219 } 220 } 221 sys_close(fa) 222 sys_close(fb) 223 return equal 224} 225 226// A GATE WHOSE VERDICT DEPENDS ON THE CALLER'S WORKING DIRECTORY IS NOT A MEASUREMENT. 227// Every source path below is relative to the buildroot. Run from anywhere else -- e.g. nx_job_run, 228// which fixes cwd at ~/nishihost -- and EVERY build fails to FIND its source, so all rows report 229// build!=0 and the run-status sentinels (-1/-2) are never overwritten. That prints verdict=RED and 230// reads as a broken compiler when nothing is wrong at all. Anchor to the tree, THEN measure. 231func eq_probe(logfd: i64, tag: *u8, path: *u8) -> i64 { 232 let fd: i64 = sys_openat_rd(path) 233 eq_puts2(logfd, " " as *u8); eq_puts2(logfd, tag); eq_puts2(logfd, "=" as *u8) 234 if fd < 0 { eq_puts2(logfd, "NO" as *u8); return 0 } 235 sys_close(fd) 236 eq_puts2(logfd, "YES" as *u8) 237 return 1 238} 239 240func eq_anchor_root() -> i64 { 241 let fd: i64 = sys_openat_rd("buildroot/runtime/nx_compile_x86.nx\x00" as *u8) 242 if fd < 0 { return 0 } 243 sys_close(fd) 244 return sys_chdir("buildroot\x00" as *u8) 245} 246 247func main(argc: i64, argv: *i64) -> i64 { 248 // SIGPIPE IMMUNITY (2026-08-05). The gate streams every line to stdout AND the durable 249 // log. When the CALLER's transport drops (observed twice live: epochs 1785972850/65 and 250 // 1785975277/91 -- headers in the log, then silence), the next stdout write hits a closed 251 // pipe and SIGPIPE kills the run MID-MEASUREMENT, so the verdict never reaches the log 252 // either. A GATE WHOSE VERDICT DEPENDS ON ITS CALLER'S CONNECTION SURVIVING IS NOT A 253 // MEASUREMENT: ignore SIGPIPE so a dead caller costs the stream, never the verdict. 254 sys_ignore_sigpipe() 255 eq_anchor_root() 256 var challenger: *u8 = "/tmp/cc_challenger.elf\x00" 257 if argc >= 2 { challenger = argv[1] as *u8 } 258 let baseline: *u8 = "_offc/nx_cc_sovereign.elf\x00" // the LIVE toolchain; the old name has not existed for a long time 259 260 let names: *i64 = sys_mmap(8 * (EQ_ROWS + 1)) as *i64 261 names[0] = "_derefcast_minrepro\x00" as *u8 as i64 262 names[1] = "_arg9_delegate_probe\x00" as *u8 as i64 263 names[2] = "_arg7_minrepro\x00" as *u8 as i64 264 names[3] = "nx_tls13_kdf_test\x00" as *u8 as i64 265 names[4] = "nx_tls13_schedule_test\x00" as *u8 as i64 266 names[5] = "nx_jpeg_ascii_test\x00" as *u8 as i64 267 names[6] = "nx_tls13_ext_test\x00" as *u8 as i64 268 names[7] = "nx_tls13_client_session_recv_sh_test\x00" as *u8 as i64 269 names[8] = "nx_p256_keyshare_test\x00" as *u8 as i64 270 names[9] = "nx_tls13_p256_loopback_test\x00" as *u8 as i64 271 272 let envp: *i64 = sys_mmap(8 * 2) as *i64 273 envp[0] = "PATH=/usr/bin:/bin\x00" as *u8 as i64 274 envp[1] = 0 275 let devnull: i64 = sys_openat_wr("/dev/null\x00" as *u8, 0x1a4) 276 let logfd: i64 = sys_openat_append("knowledge/status/cc_equiv_gate.log\x00" as *u8, 0x1a4) 277 278 eq_puts2(logfd, "CC-EQUIV epoch=\x00" as *u8) 279 eq_putn2(logfd, sys_now_realtime_sec()) 280 eq_puts2(logfd, " challenger=\x00" as *u8) 281 eq_puts2(logfd, challenger) 282 eq_puts2(logfd, "\nCC-EQUIV cwdprobe" as *u8) 283 eq_probe(logfd, "runtime" as *u8, "runtime/nx_compile_x86.nx\x00" as *u8) 284 eq_probe(logfd, "br_runtime" as *u8, "buildroot/runtime/nx_compile_x86.nx\x00" as *u8) 285 eq_probe(logfd, "offc" as *u8, "_offc/nx_compile_x86_native.elf\x00" as *u8) 286 eq_probe(logfd, "br_offc" as *u8, "buildroot/_offc/nx_compile_x86_native.elf\x00" as *u8) 287 eq_puts2(logfd, "\n" as *u8) 288 289 // CHALLENGER PREFLIGHT (2026-08-05). A challenger path that does not resolve from the 290 // gate's own CWD (buildroot -- eq_anchor_root chdirs there) used to run all 10 rows 291 // against a binary that NEVER EXECUTED: RED on every row, empty challenger logs 292 // (cclogb=0), identical eb bytes on every row -- two failures agreeing with each other, 293 // wearing the shape of an equivalence verdict. A gate whose instrument is missing must 294 // refuse to run: fail LOUD, name the path AND the fix, exit a distinct code (4). 295 let cpre: i64 = sys_openat_rd(challenger) 296 if cpre < 0 { 297 eq_puts2(logfd, "CC-EQUIV verdict=CHALLENGER-UNREADABLE path=\x00" as *u8) 298 eq_puts2(logfd, challenger) 299 eq_puts2(logfd, "\n gate CWD is buildroot; /api/build stages into the nishihost ROOT -- pass ../<target>.sov.elf.new\n\x00" as *u8) 300 sys_exit(4) 301 } 302 sys_close(cpre) 303 304 // PER-RUN UNIQUE TEMPS -- see eq_uniq. One nonce names every temp this run owns. 305 let uq: i64 = eq_nonce() 306 let p_as: *u8 = eq_uniq("/tmp/eqa_\x00" as *u8, uq, ".s\x00" as *u8) 307 let p_ao: *u8 = eq_uniq("/tmp/eqa_\x00" as *u8, uq, ".o\x00" as *u8) 308 let p_ae: *u8 = eq_uniq("_build/eqa_\x00" as *u8, uq, ".elf\x00" as *u8) 309 let p_aout: *u8 = eq_uniq("/tmp/eqa_\x00" as *u8, uq, ".out\x00" as *u8) 310 let p_alog: *u8 = eq_uniq("/tmp/eq_cc_a_\x00" as *u8, uq, ".log\x00" as *u8) 311 let p_bs: *u8 = eq_uniq("/tmp/eqb_\x00" as *u8, uq, ".s\x00" as *u8) 312 let p_bo: *u8 = eq_uniq("/tmp/eqb_\x00" as *u8, uq, ".o\x00" as *u8) 313 let p_be: *u8 = eq_uniq("_build/eqb_\x00" as *u8, uq, ".elf\x00" as *u8) 314 let p_bout: *u8 = eq_uniq("/tmp/eqb_\x00" as *u8, uq, ".out\x00" as *u8) 315 let p_blog: *u8 = eq_uniq("/tmp/eq_cc_b_\x00" as *u8, uq, ".log\x00" as *u8) 316 let p_asm: *u8 = eq_uniq("_build/_eq_asm_\x00" as *u8, uq, ".elf\x00" as *u8) 317 let p_nxl: *u8 = eq_uniq("/tmp/eq_nxasm_\x00" as *u8, uq, ".log\x00" as *u8) 318 let p_g2s: *u8 = eq_uniq("/tmp/eqg2_\x00" as *u8, uq, ".s\x00" as *u8) 319 let p_g2o: *u8 = eq_uniq("/tmp/eqg2_\x00" as *u8, uq, ".o\x00" as *u8) 320 let p_g2e: *u8 = eq_uniq("_build/eqg2_\x00" as *u8, uq, ".elf\x00" as *u8) 321 let p_prs: *u8 = eq_uniq("/tmp/eqpr_\x00" as *u8, uq, ".s\x00" as *u8) 322 let p_pro: *u8 = eq_uniq("/tmp/eqpr_\x00" as *u8, uq, ".o\x00" as *u8) 323 let p_pre: *u8 = eq_uniq("_build/eqpr_\x00" as *u8, uq, ".elf\x00" as *u8) 324 let p_prout: *u8 = eq_uniq("/tmp/eqpr_\x00" as *u8, uq, ".out\x00" as *u8) 325 326 var passed: i64 = 0 327 var ri: i64 = 0 328 while ri < EQ_ROWS { 329 let mod: *u8 = names[ri] as *u8 330 let src: *u8 = sys_mmap(512) 331 var o: i64 = 0 332 o = eq_cat(src, o, "runtime/\x00" as *u8) 333 o = eq_cat(src, o, mod) 334 o = eq_cat(src, o, ".nx\x00" as *u8) 335 src[o] = 0 as u8 336 337 // per-side compiler stderr (see the eq_build header): a refused row must be able to say WHY. 338 let cca: i64 = sys_openat_wr(p_alog, 0x1a4) 339 let ba_rc: i64 = eq_build(baseline, src, 340 p_as, p_ao, 341 p_ae, envp, cca, p_asm, p_nxl) 342 sys_close(cca) 343 let ccb: i64 = sys_openat_wr(p_blog, 0x1a4) 344 let ch_rc: i64 = eq_build(challenger, src, 345 p_bs, p_bo, 346 p_be, envp, ccb, p_asm, p_nxl) 347 sys_close(ccb) 348 349 var st_a: i64 = 0 - 1 350 var st_b: i64 = 0 - 2 351 var out_eq: i64 = 0 352 if ba_rc == 0 { 353 if ch_rc == 0 { 354 st_a = eq_run_capture(p_ae, p_aout, envp, devnull) 355 st_b = eq_run_capture(p_be, p_bout, envp, devnull) 356 out_eq = eq_files_equal(p_aout, p_bout) 357 } 358 } 359 360 var pass: i64 = 0 361 // NON-VACUITY: exit 127 means execve never ran the product. Two sides that BOTH fail to 362 // run agree perfectly and compare two empty outputs, which this gate used to report as PASS 363 // -- 10/10 GREEN while measuring nothing. A gate that can pass on a non-measurement is worse 364 // than a red one, because it is trusted. Require a REAL run before equivalence can mean 365 // anything. ★TWO FAILURES THAT AGREE ARE NOT AN EQUIVALENCE. 366 var ran: i64 = 1 367 if ((st_a >> 8) & 255) == 127 { ran = 0 } 368 if ((st_b >> 8) & 255) == 127 { ran = 0 } 369 if ran == 0 { eq_puts2(logfd, " VACUOUS(exec-127)" as *u8) } 370 if ran == 1 { if ba_rc == 0 { if ch_rc == 0 { if st_a == st_b { if out_eq == 1 { pass = 1 } } } } } 371 372 eq_puts2(logfd, "CC-EQUIV row=\x00" as *u8) 373 eq_puts2(logfd, mod) 374 eq_puts2(logfd, " sa=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_as)) 375 eq_puts2(logfd, " ea=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_ae)) 376 eq_puts2(logfd, " eb=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_be)) 377 eq_puts2(logfd, " alog=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_nxl)) 378 eq_puts2(logfd, " ccloga=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_alog)) 379 eq_puts2(logfd, " cclogb=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_blog)) 380 eq_puts2(logfd, " build_a=\x00" as *u8) 381 eq_putn2(logfd, ba_rc) 382 eq_puts2(logfd, " build_b=\x00" as *u8) 383 eq_putn2(logfd, ch_rc) 384 eq_puts2(logfd, " st_a=\x00" as *u8) 385 eq_putn2(logfd, st_a) 386 eq_puts2(logfd, " st_b=\x00" as *u8) 387 eq_putn2(logfd, st_b) 388 eq_puts2(logfd, " out_eq=\x00" as *u8) 389 eq_putn2(logfd, out_eq) 390 if pass == 1 { eq_puts2(logfd, " verdict=PASS\n\x00" as *u8) } 391 if pass != 1 { eq_puts2(logfd, " verdict=FAIL\n\x00" as *u8) } 392 if pass == 1 { passed = passed + 1 } 393 ri = ri + 1 394 } 395 396 // ---- SELF-HOST stage: challenger -> gen2 -> probe runs 0 ---- 397 var sh_pass: i64 = 0 398 let g2_rc: i64 = eq_build(challenger, "runtime/nx_compile_x86.nx\x00" as *u8, 399 p_g2s, p_g2o, 400 p_g2e, envp, devnull, p_asm, p_nxl) 401 var pr_rc: i64 = 0 - 1 402 var pr_st: i64 = 0 - 1 403 if g2_rc == 0 { 404 pr_rc = eq_build(p_g2e, "runtime/_derefcast_minrepro.nx\x00" as *u8, 405 p_prs, p_pro, 406 p_pre, envp, devnull, p_asm, p_nxl) 407 if pr_rc == 0 { 408 pr_st = eq_run_capture(p_pre, p_prout, envp, devnull) 409 if pr_st == 0 { sh_pass = 1 } 410 } 411 } 412 eq_puts2(logfd, "CC-EQUIV selfhost sizes g2s=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_g2s)) 413 eq_puts2(logfd, " g2elf=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_g2e)) 414 eq_puts2(logfd, " asmtmp=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_asm)) 415 eq_puts2(logfd, " alog=\x00" as *u8); eq_putn2(logfd, eq_fsize(p_nxl)) 416 eq_puts2(logfd, "\n\x00" as *u8) 417 eq_puts2(logfd, "CC-EQUIV selfhost g2_build=\x00" as *u8) 418 eq_putn2(logfd, g2_rc) 419 eq_puts2(logfd, " probe_build=\x00" as *u8) 420 eq_putn2(logfd, pr_rc) 421 eq_puts2(logfd, " probe_st=\x00" as *u8) 422 eq_putn2(logfd, pr_st) 423 if sh_pass == 1 { eq_puts2(logfd, " verdict=PASS\n\x00" as *u8) } 424 if sh_pass != 1 { eq_puts2(logfd, " verdict=FAIL\n\x00" as *u8) } 425 426 eq_puts2(logfd, "CC-EQUIV rows=\x00" as *u8) 427 eq_putn2(logfd, EQ_ROWS) 428 eq_puts2(logfd, " passed=\x00" as *u8) 429 eq_putn2(logfd, passed) 430 eq_puts2(logfd, " selfhost=\x00" as *u8) 431 eq_putn2(logfd, sh_pass) 432 var verdict_green: i64 = 0 433 if passed == EQ_ROWS { if sh_pass == 1 { verdict_green = 1 } } 434 if verdict_green == 1 { eq_puts2(logfd, " verdict=GREEN\n\x00" as *u8) } 435 if verdict_green != 1 { eq_puts2(logfd, " verdict=RED\n\x00" as *u8) } 436 // TEMP CLEANUP (2026-08-05): nonce'd temps would otherwise accumulate one set per run 437 // (the old fixed names self-overwrote); NAS /tmp persists until reboot, so unlink is not 438 // optional. On RED the two per-side compiler logs are KEPT and named -- they are the 439 // diagnosis, and a gate that reports a failure without its diagnostic is unactionable. 440 if verdict_green != 1 { 441 eq_puts2(logfd, "CC-EQUIV cc-logs-kept=\x00" as *u8) 442 eq_puts2(logfd, p_alog) 443 eq_puts2(logfd, " \x00" as *u8) 444 eq_puts2(logfd, p_blog) 445 eq_puts2(logfd, "\n\x00" as *u8) 446 } 447 sys_unlinkat(p_as); sys_unlinkat(p_ao); sys_unlinkat(p_ae); sys_unlinkat(p_aout) 448 sys_unlinkat(p_bs); sys_unlinkat(p_bo); sys_unlinkat(p_be); sys_unlinkat(p_bout) 449 sys_unlinkat(p_asm); sys_unlinkat(p_nxl) 450 sys_unlinkat(p_g2s); sys_unlinkat(p_g2o); sys_unlinkat(p_g2e) 451 sys_unlinkat(p_prs); sys_unlinkat(p_pro); sys_unlinkat(p_pre); sys_unlinkat(p_prout) 452 if verdict_green == 1 { sys_unlinkat(p_alog); sys_unlinkat(p_blog) } 453 if logfd > 0 { sys_close(logfd) } 454 // MIGRATED onto nx_gate_verdict by nx_gate_dry_apply (D001, minimal form): every check 455 // row above is untouched, so the PASS/FAIL vector cannot change; only the hand-rolled 456 // verdict emission is replaced by the ONE shared base class. Proven by nx_gate_migrate verify. 457 let ctr__dry: *i64 = gv_ctr() 458 ctr__dry[0] = verdict_green 459 ctr__dry[1] = 1 460 let rc__dry: i64 = gv_verdict("CC-EQUIV-GATE" as *u8, ctr__dry, "teeth unchanged; verdict emission migrated onto the shared base class" as *u8) 461 sys_exit(rc__dry) 462 return rc__dry 463}