code wiki / (root) / nx_inc_compile.nx

nx_inc_compile.nx

buildroot/runtime/nx_inc_compile.nx

56944 B1250 linesdepth 5pulls 10 transitivereach 0 importersview sourcekind tool
docsdependenciesstructsconstsfunctions

about

nx_inc_compile.nx -- LN10: THE PER-MODULE INCREMENTAL BUILD CACHE. CONTRACT (buildroot/knowledge/compare/lang.plan, rung LN10, verbatim): "Per-module cache keyed by the closure sha the build door already computes (the B2 cache keys whole targets); gate proves a one-file edit rebuilds only its dependents and the output equals a full build" WHAT ALREADY EXISTED, AND WHAT WAS MISSING ------------------------------------------ B2 (nx_sov_build_run's bld_cache_cas) is a WHOLE-TARGET content-addressed cache: key = sha256 over the EXPANDED closure text plus the compiler and assembler shas, value = the finished elf. It is excellent and it is not this rung. Its unit is ONE target and its key costs a full closure EXPANSION (a 32 MiB buffer, every byte of every dependency) paid once PER TARGET -- so it can answer "rebuild this target?" only after doing most of the work of deciding. The question a multi-target session actually asks is the DEPENDENTS question: "I edited one file -- which targets must rebuild, and which must not?" Nothing in the estate could answer that without re-walking every closure. THE KEY WAS ALREADY SOLVED AND IS NOT RE-INVENTED HERE. nx_closurehash is the estate's ONE closure ruler; /api/build forks it on every build and records `closure_sha256` into <target>.provenance. This organ NEVER computes a closure sha. It parses the ruler's own per-file listing -- which is already a per-MODULE manifest, one `<sha256> <bytes> <name>` row per source -- stores those rows as the per-module cache, and REPLAYS the ruler's own key on a hit. There is exactly one closure-sha implementation in the estate and this is not a second one. WHY PER-MODULE SHAS ARE SUFFICIENT -- stated because a cache that returns a stale object is a silent miscompile, the worst class this estate tracks: closure_sha = sha256 over the rows "<name> <sha>" in discovery order. It therefore changes iff the member SET changes or some member's SHA changes. The member set can change only because some module's import list changed, or because a name started resolving to a different file -- and BOTH of those change that module's bytes, hence its sha. So: every recorded module still hashes to its recorded sha <=> closure_sha unchanged. The gate proves the forward direction against the LIVE ruler on every subject it touches, and the neg-control proves the converse (a one-byte edit MISSES). The one assumption is that resolution is the SAME resolution the ruler performed -- which is why the resolver lives in nx_incclosure_lib and both organs call it. A private copy here would be the exact defect that produces a false HIT. THE INVARIANT THAT MAKES A SHARED MODULE TABLE SOUND (and the dependents mechanism itself): for every target record T and every module M in T's member list, icm_rsha[M] is the sha that was used when T's key was recorded. It is maintained by ic_record(): writing a NEW sha for M INVALIDATES every other target whose member list contains M (announced, one INC-INVALIDATE line each). Without that,

dependencies 6 imports · 0 importers

nx_syscalls.nx nx_sha256.nx nx_itoa_lib.nx nx_lineconf_lib.nx nx_tool_run.nx nx_incclosure_lib.nx nx_inc_compile.nx

imports: nx_syscalls.nxnx_sha256.nxnx_itoa_lib.nxnx_lineconf_lib.nxnx_tool_run.nxnx_incclosure_lib.nx

imported by: nobody (leaf or entry point)

call flow from main pre-order; caps 40 nodes / depth 6 declared; ↻ = already shown

main ic_usage ic_e sys_write icl_len sys_exit sys_mmap nxa_die sys_write ↻ sys_exit ↻ nxa_lock_take nxa_lock_addr sys_write ↻ nxa_lock_give nxa_lock_addr ↻ nxa_report_overrun sys_write ↻ nxa_dump_printable sys_write ↻ nxa_dump_sizes sys_write ↻ ic_starts icl_len ↻ icl_seq ic_i64at icl_cat icl_exists sys_openat_rd sys_close lcf_int_of sys_mmap ↻ sys_read_file sys_openat_rd ↻ sys_lseek sys_mmap ↻ sys_read sys_munmap sys_close ↻ lcf_int lcf_find

structs

none

consts

89const IC_TAB_INIT: i64 = 256
90const IC_MEM_INIT: i64 = 4096
94const IC_ROWW: i64 = 320
95const IC_SLACK: i64 = 4096
96const IC_SHOWSHA: i64 = 12 // digest prefix shown in a dirty-module line (full shas stay in the index)
97const IC_BUDGET_BOOTSTRAP: i64 = 1048576
98const IC_RULER_TMO_BOOT: i64 = 120000
99const IC_BUILD_TMO_BOOT: i64 = 900000
100const IC_PIPE: i64 = 124
101const IC_COMMA: i64 = 44
102const IC_NL: i64 = 10
103const IC_SP: i64 = 32
104const IC_HASH: i64 = 35
105const IC_DIRMODE: i64 = 0x1ed // 0755
106const IC_FILEMODE: i64 = 0x1a4 // 0644
107const IC_OK: i64 = 0
108const IC_BUILD_FAIL: i64 = 1
109const IC_USAGE: i64 = 2
110const IC_NO_TOOL: i64 = 4
111const IC_HIT: i64 = 1
112const IC_MISS: i64 = 0
113const IC_NEW: i64 = 0 - 1

functions

115func ic_ruler_cap() -> i64 { return ICL_MAXF * IC_ROWW + IC_SLACK }
called by 2: ic_recordmain
116func ic_shaw() -> i64 { return ICL_SHA_HEX + 1 }
118func ic_p(s: *u8) -> i64 { sys_write(1, s, icl_len(s)); return 0 }
119func ic_e(s: *u8) -> i64 { sys_write(2, s, icl_len(s)); return 0 }
called by 2: ic_usagemain calls 2: sys_writeicl_len
120func ic_n(v: i64) -> i64 { nxi_out(v); return 0 }
called by 3: ic_index_saveic_recordmain calls 1: nxi_out
121func ic_nl() -> i64 { let b: *u8 = sys_mmap(2); b[0] = IC_NL as u8; sys_write(1, b, 1); sys_munmap(b, 2); return 0 }
122func ic_hexeq(a: *u8, b: *u8) -> i64
127func ic_starts(s: *u8, pfx: *u8) -> i64
called by 2: ic_observemain
132func ic_ishex64(b: *u8, off: i64, n: i64) -> i64
called by 1: ic_record
176func ic_guard_name() -> *u8 { return ic_guard_p as *u8 }
called by 1: ic_record
177func ic_cc_name() -> *u8 { return ic_cc_p as *u8 }
called by 1: ic_record
178func ic_asm_name() -> *u8 { return ic_asm_p as *u8 }
called by 1: ic_record
180func ic_u8slot(base: i64, i: i64, stride: i64) -> *u8 { return (base + i*stride) as *u8 }
181func ic_i64at(base: i64, i: i64) -> *i64 { return (base + i*8) as *i64 }
182func ic_copy_u8(dstp: i64, srcp: i64, n: i64) -> i64
192func ic_mod_init() -> i64
called by 1: main calls 2: sys_mmapic_shaw
202func ic_mod_grow() -> i64
225func ic_intern(name: *u8) -> i64
246func ic_tgt_init() -> i64
called by 1: main calls 2: sys_mmapic_shaw
262func ic_tgt_grow() -> i64
285func ic_mem_push(v: i64) -> i64
299func ic_tgt_find(name: *u8) -> i64
311func ic_tgt_slot(name: *u8) -> i64
333func ic_tgt_has_module(ti: i64, mi: i64) -> i64
called by 1: ic_invalidate_others calls 1: ic_i64at
352func ic_observe(root: *u8, mi: i64) -> i64
388func inc_cache_lookup(root: *u8, target: *u8, dirty: *i64, dirtycap: i64, dirtyn: *i64, reason: *i64) -> i64
418func ic_artifact_path(root: *u8, target: *u8, out: *u8) -> i64
called by 2: ic_recordmain calls 1: icl_cat
426func ic_sha_file(path: *u8, out: *u8) -> i64
439func ic_dir(root: *u8, out: *u8) -> i64
called by 2: ic_index_pathic_index_save calls 1: icl_cat
445func ic_index_path(root: *u8, out: *u8) -> i64
451func ic_upto(b: *u8, i: i64, end: i64, sep: i64, out: *u8, cap: i64) -> i64
called by 1: ic_index_load
467func ic_dec(b: *u8, i: i64, end: i64, outv: *i64) -> i64
484func ic_eol(b: *u8, from: i64, n: i64) -> i64
489func ic_setsha(dst: *u8, src: *u8) -> i64
497func ic_index_load(root: *u8) -> i64
586func ic_catn(d: *u8, o: i64, v: i64) -> i64
called by 1: ic_serialize calls 2: sys_mmapsys_munmap
602func ic_skipsp(b: *u8, from: i64, end: i64) -> i64
called by 1: ic_record
614func ic_serialize(buf: *u8, cap: i64, remap: *i64) -> i64
694func ic_index_save(root: *u8, budget: i64) -> i64
762func ic_find(b: *u8, from: i64, end: i64, needle: *u8) -> i64
called by 1: ic_record calls 1: icl_len
780func ic_add_extra(root: *u8, ti: i64, name: *u8, ms_start: i64, count: i64) -> i64
799func ic_invalidate_others(ti: i64, mi: i64) -> i64
821func ic_record(root: *u8, target: *u8, ruler: *u8, tmo: i64, now: i64) -> i64
924func ic_try(root: *u8, tail: *u8, out: *u8) -> i64
called by 1: ic_pick calls 2: icl_caticl_exists
937func ic_abs(p: *u8) -> i64
953func ic_pick(root: *u8, conf: *u8, key: *u8, t1: *u8, t2: *u8, t3: *u8, out: *u8) -> i64
963func ic_build_one(root: *u8, builder: *u8, target: *u8, tmo: i64, out: *u8, cap: i64, ol: *i64) -> i64
called by 1: main calls 2: sys_mmaptr_run_capture_cwd
971func ic_reason(code: i64) -> i64
called by 1: main calls 1: ic_p
981func ic_usage() -> i64
called by 1: main calls 1: ic_e
990func main(argc: i64, argv: *i64) -> i64