nx_langcensus.nx
buildroot/runtime/nx_langcensus.nx
about
nx_langcensus.nx -- THE WHOLE-POPULATION COMPILER CENSUS: compile EVERY top-level program in the tree with
a GIVEN compiler, classify each outcome by REASON, and (against a baseline census) name every program whose
class changed. This is the instrument a language or codegen change is measured by.
WHY THIS EXISTS, MEASURED 2026-09-03. The estate's incumbent compiler ruler, nx_cc_equiv_gate, runs
EQ_ROWS = 10 corpus rows. Ten rows is a SAMPLE, and a sample can see that a mechanism works; it can never
see that a CLASS of program stopped compiling. On the day LN36/LN39/LN40 landed, a first cut of the integer
literal bound refused every decimal above i64 max. Every hand-written fixture was GREEN and the 10-row
equivalence gate was GREEN, while 286 real estate programs had stopped compiling -- the estate writes u64
BIT PATTERNS in decimal (FNV offset bases, all-ones masks, hash multipliers), so the correct bound is
2^64-1. Only a census over all 13,790 programs could see that class, and it existed only as a shell loop.
★★★★★★A RULER THAT SAMPLES CAN PROVE A MECHANISM AND CAN NEVER PROVE A POPULATION -- AND A COMPILER CHANGE
IS A CLAIM ABOUT THE POPULATION.
THREE OUTCOMES, NEVER TWO. A compiler that LOOPS on an input is not a compiler that refused it: before the
LN40 packer fix a 20-digit fraction literal spun forever, and a census without a per-program deadline hangs
at that file and reports nothing about the 9,000 programs after it. HANG is its own class here, and it is
counted, named, and treated as a regression. A harness failure (fork/pipe/exec) is a FOURTH class that
asserts nothing about the subject -- ★AN AXIS THAT CANNOT SEE MUST ABSTAIN, NEVER ACQUIT.
THE REASON TRAVELS WITH THE COUNT. Every refusal row carries the compiler's own first diagnostic line with
the file:line prefix stripped, so rows group into causes and a 773-refusal census reads as a dozen classes
rather than a number. The delta against a baseline names each program, because ★A COUNT WITHOUT A WORKLIST
IS NOT ACTIONABLE.
NO SILENT CAP. If the file table or any buffer fills, coverage_complete goes to 0 and the organ REFUSES
(exit LC_EXIT_UNPROVEN) rather than publishing a prefix as a population.
Usage: nx_langcensus <cc.elf> <out-path> [timeout_ms] [baseline-census-path]
Run with CWD = nxc2/ (or any root holding runtime/). Writes one row per program to <out-path>:
OK <path> | ERR <path> :: <reason> | HANG <path> | HARN <path> rc=<n>
Exit: 0 NO-REGRESSION - 1 REGRESSION (new refusals or hangs, each named) - 2 usage - 3 UNPROVEN.
With no baseline the exit is 0 whenever the census completed: a first census has nothing to regress from,
and saying otherwise would make the instrument's first run a permanent RED.
RESOURCE ENVELOPE. One compiler fork per program, strictly sequential, each bounded by timeout_ms. Buffers
(capture, dirent, path table, reason table) are allocated ONCE before the walk and reused -- ★NEVER
ALLOCATE IN A HOT LOOP: a per-row capture buffer over 13,790 rows is gigabytes of address space for
nothing. The compiler's stdout is discarded to the capture buffer and never to disk.
dependencies 2 imports · 0 importers
imports: nx_syscalls.nxnx_tool_run.nx
imported by: nobody (leaf or entry point)
call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown
structs
| none |
consts
| 50 | const LC_MAX_PROGRAMS: i64 = 32768 |
| 52 | const LC_PATH_MAX: i64 = 4096 |
| 55 | const LC_DIRBUF: i64 = 65536 |
| 64 | const LC_CAPBUF: i64 = 16777216 |
| 67 | const LC_CAP_NEARFULL: i64 = 16773120 |
| 69 | const LC_REASON_MAX: i64 = 120 |
| 71 | const LC_MAX_REASONS: i64 = 512 |
| 74 | const LC_DEFAULT_TIMEOUT_MS: i64 = 60000 |
| 78 | const LC_ROWBUF: i64 = 8192 |
| 79 | const LC_MODE_0644: i64 = 0x1a4 |
| 82 | const LC_HEARTBEAT_EVERY: i64 = 250 |
| 84 | const LC_EXIT_OK: i64 = 0 |
| 85 | const LC_EXIT_REGRESSION: i64 = 1 |
| 86 | const LC_EXIT_USAGE: i64 = 2 |
| 87 | const LC_EXIT_UNPROVEN: i64 = 3 |
| 90 | const LC_OK: i64 = 0 |
| 91 | const LC_ERR: i64 = 1 |
| 92 | const LC_HANG: i64 = 2 |
| 93 | const LC_HARN: i64 = 3 |
functions
| 96 | func lc_len(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } |
| 97 | func lc_eq_n(a: *u8, b: *u8, n: i64) -> i64 { |
| 102 | func lc_ends_with(s: *u8, sfx: *u8) -> i64 { |
| 108 | func lc_cat(dst: *u8, at: i64, s: *u8, cap: i64) -> i64 { |
| 120 | func lc_catn(dst: *u8, at: i64, v: i64, cap: i64) -> i64 { |
| 137 | func lc_puts(s: *u8) -> i64 { sys_write(1, s, lc_len(s)) return 0 } |
| 138 | func lc_putn(v: i64) -> i64 { |
| 148 | func lc_has_main(path: *u8) -> i64 { |
| 172 | func lc_marker_at(buf: *u8, i: i64, n: i64) -> i64 { |
| 195 | func lc_reason(buf: *u8, n: i64, out: *u8) -> i64 { |
| 238 | func lc_walk(dir: *u8, tab: *i64, cap: i64, n_in: i64, full: *i64) -> i64 { |
| 274 | func lc_base_class(base: *u8, blen: i64, path: *u8) -> i64 { |
| 306 | func lc_class_name(c: i64) -> *u8 {
called by 1: main |
| 314 | func main(argc: i64, argv: *i64) -> i64 { |