code wiki / (root) / nx_buildmode_lib.nx

nx_buildmode_lib.nx source

↩ module page · 173 lines · 7900 B

1// nx_buildmode_lib.nx -- THE COMPILE MODE THE BUILD LANE USES, AS DATA (LN44, 2026-09-03). 2// 3// THE GAP THIS CLOSES, measured. No build lane in this estate passes `--mode` at all: a grep for it across 4// the build-lane sources returns one hit and it is a GATE, not a builder. So every organ ships compiled in 5// DEFAULT, which by deliberate contract has every declared mode OFF -- including `--bckelide`, LN7 sound 6// bounds-check elision, which is already written, already promoted, and already proven by 7// nx_bck_elide_gate 13/13 against the live compiler with both out-of-bounds legs still trapping 71. 8// Cost of that omission, measured two independent ways: the published h2h receipt reads default 7105 us 9// against f1 4189 us on the same workload (1.70x), and a 15-round interleaved re-measure on a quiet host 10// reads 8275 us against 5210 us (1.59x). On a divide-dominated loop the same pair is 45807 against 44851, 11// i.e. NEUTRAL -- the mode wins where it can and costs nothing where it cannot. 12// 13// WHY THIS IS A BUILD-LANE POLICY AND EMPHATICALLY NOT A COMPILER-DEFAULT CHANGE. nx_compile_x86 states 14// the contract in its own source: default builds stay BYTE-IDENTICAL BY CONSTRUCTION, which is exactly 15// what every drift, contentdiff and artifact-comparison instrument in the estate rests on. Flipping the 16// language default would re-baseline ALL of those instruments in the same instant -- so the undo path 17// would run straight through the comparison surface needed to judge whether the change was safe. That is 18// the static-stability violation this estate already has doctrine about. The mode therefore moves 19// PER TARGET, declared here, provable one organ at a time. 20// 21// THE SAFETY PROPERTY, AND IT IS THE WHOLE DESIGN: an ABSENT conf, an absent row, or an empty flag list 22// yields ZERO flags AND contributes ZERO bytes to the build-cache key. Adopting this lib therefore cannot 23// change one byte of one artifact until somebody writes a row. There is no flag day. 24// 25// ⚠ THE CACHE KEY IS PART OF THE CONTRACT, NOT AN AFTERTHOUGHT. nx_sov_build_run caches artifacts under 26// sha256(expanded closure ++ compilerSha ++ asmSha ++ 'g'|'n'). A mode that changed the emitted code 27// WITHOUT entering that key would serve a stale artifact compiled under the other mode -- silently, and 28// looking like a cache hit. bm_key_token exists so the mode enters the key by construction, and the gate 29// asserts that two different modes produce two different tokens. 30// 31// Row grammar, one per line, first match wins: 32// target|<name>|<flags...> the mode for exactly this build target 33// all|<flags...> the mode for every target that has no row of its own 34// `#` and `;` start a comment. Flags are passed to the compiler verbatim and are exactly the tokens its 35// own driver already parses -- this lib can never invent a switch the compiler does not have. 36import "nx_syscalls.nx" 37 38const BM_CONF: *u8 = "knowledge/build_modes.conf" 39const BM_CONF_ALT: *u8 = "../knowledge/build_modes.conf" 40const BM_BUF: i64 = 65536 41const BM_FLAGS_CAP: i64 = 512 42const BM_NL: i64 = 10 43const BM_BAR: i64 = 124 44const BM_HASH: i64 = 35 45const BM_SEMI: i64 = 59 46const BM_SP: i64 = 32 47const BM_TAB: i64 = 9 48 49func bm_slen(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } return n } 50func bm_read(path: *u8, buf: *u8, cap: i64) -> i64 { 51 let fd: i64 = sys_openat_rd(path) 52 if fd < 0 { return 0 - 1 } 53 var got: i64 = 0 54 var r: i64 = 1 55 while r > 0 { 56 r = sys_read(fd, ((buf as i64) + got) as *u8, cap - got - 1) 57 if r > 0 { got = got + r } 58 if got >= cap - 1 { r = 0 } 59 } 60 sys_close(fd) 61 buf[got] = 0 as u8 62 return got 63} 64// field <idx> of the bar-separated line [ls,le) into out; -1 when the line has no such field 65func bm_field(buf: *u8, ls: i64, le: i64, idx: i64, out: *u8, cap: i64) -> i64 { 66 var f: i64 = 0 67 var i: i64 = ls 68 var st: i64 = ls 69 while i <= le { 70 var atend: i64 = 0 71 if i == le { atend = 1 } 72 if atend == 0 { if (buf[i] as i64) == BM_BAR { atend = 1 } } 73 if atend == 1 { 74 if f == idx { 75 var n: i64 = i - st 76 if n >= cap { n = cap - 1 } 77 var k: i64 = 0 78 while k < n { out[k] = buf[st + k]; k = k + 1 } 79 out[n] = 0 as u8 80 return n 81 } 82 f = f + 1 83 st = i + 1 84 } 85 i = i + 1 86 } 87 out[0] = 0 as u8 88 return 0 - 1 89} 90func bm_streq(a: *u8, b: *u8) -> i64 { 91 var i: i64 = 0 92 while a[i] != (0 as u8) { if a[i] != b[i] { return 0 } i = i + 1 } 93 if b[i] != (0 as u8) { return 0 } 94 return 1 95} 96// THE ONE READER. Writes the flag list for `target` into out (space separated, NUL terminated) and 97// returns its length. 0 means NO MODE DECLARED, which is the default and is byte-identical to today. 98// `all` is consulted only when the target has no row of its own, so a per-target row always wins. 99func bm_flags_for(target: *u8, out: *u8) -> i64 { 100 out[0] = 0 as u8 101 let buf: *u8 = sys_mmap(BM_BUF) 102 var n: i64 = bm_read(BM_CONF, buf, BM_BUF) 103 if n <= 0 { n = bm_read(BM_CONF_ALT, buf, BM_BUF) } 104 if n <= 0 { return 0 } // ABSENT CONF = no mode = nothing changes 105 let key: *u8 = sys_mmap(BM_FLAGS_CAP) 106 let val: *u8 = sys_mmap(BM_FLAGS_CAP) 107 let fallback: *u8 = sys_mmap(BM_FLAGS_CAP) 108 fallback[0] = 0 as u8 109 var ls: i64 = 0 110 while ls < n { 111 var le: i64 = ls 112 while le < n { if (buf[le] as i64) == BM_NL { break } le = le + 1 } 113 var skip: i64 = 0 114 if le <= ls { skip = 1 } 115 if skip == 0 { if (buf[ls] as i64) == BM_HASH { skip = 1 } } 116 if skip == 0 { if (buf[ls] as i64) == BM_SEMI { skip = 1 } } 117 if skip == 0 { 118 if bm_field(buf, ls, le, 0, key, BM_FLAGS_CAP) > 0 { 119 if bm_streq(key, "target" as *u8) == 1 { 120 if bm_field(buf, ls, le, 1, val, BM_FLAGS_CAP) > 0 { 121 if bm_streq(val, target) == 1 { 122 if bm_field(buf, ls, le, 2, out, BM_FLAGS_CAP) > 0 { return bm_slen(out) } 123 } 124 } 125 } 126 if bm_streq(key, "all" as *u8) == 1 { 127 if bm_field(buf, ls, le, 1, fallback, BM_FLAGS_CAP) > 0 { } 128 } 129 } 130 } 131 ls = le + 1 132 } 133 if bm_slen(fallback) > 0 { 134 var k: i64 = 0 135 while fallback[k] != (0 as u8) { out[k] = fallback[k]; k = k + 1 } 136 out[k] = 0 as u8 137 return k 138 } 139 return 0 140} 141// THE CACHE-KEY TOKEN. Identical bytes to the flag list, and EMPTY when no mode is declared -- so a tree 142// with no conf keeps every cache entry it already has, and two different modes can never share a key. 143func bm_key_token(target: *u8, out: *u8) -> i64 { return bm_flags_for(target, out) } 144// Split the flag list into argv entries. `slots` is the caller's array, `cap` its length; returns the 145// number written. Whitespace-separated, so "--bckelide --chkarith" becomes two argv tokens, never one. 146func bm_split(flags: *u8, store: *u8, slots: *i64, cap: i64) -> i64 { 147 var n: i64 = 0 148 var i: i64 = 0 149 var w: i64 = 0 150 var inword: i64 = 0 151 while flags[i] != (0 as u8) { 152 let ch: i64 = flags[i] as i64 153 var issp: i64 = 0 154 if ch == BM_SP { issp = 1 } 155 if ch == BM_TAB { issp = 1 } 156 if issp == 1 { 157 if inword == 1 { store[w] = 0 as u8; w = w + 1; inword = 0 } 158 } 159 if issp == 0 { 160 if inword == 0 { 161 if n >= cap { return n } 162 slots[n] = ((store as i64) + w) as i64 163 n = n + 1 164 inword = 1 165 } 166 store[w] = flags[i] 167 w = w + 1 168 } 169 i = i + 1 170 } 171 if inword == 1 { store[w] = 0 as u8 } 172 return n 173}