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}