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}