code wiki / _hdl_build / nx_fwdconst_scan.nx
nx_fwdconst_scan.nx source
↩ module page · 255 lines · 9467 B
1// nx_fwdconst_scan.nx -- FORWARD-CONST DEAD-READ DETECTOR (eats seq360's discovery half, 2026-07-20).
2// The miscompile class: a module const READ by code textually ABOVE its declaration resolves to
3// CONSTANT 0 in binaries built before the 07-20 nx_cc diagnostic (proven live: AT_FDCWD dirfd=0,
4// VC_EO_PART/VC_EO_T8 codec early-outs dead since 07-12). The new compiler REJECTS the pattern at
5// build time; this organ finds it in SOURCE so already-shipped binaries' sources get fixed+rebuilt
6// deliberately, and the class is sweepable on cadence forever.
7// argv: <dir> [dir2] [dir3] (flat dirs, .nx files; the build-resolution pair is the usual input)
8// Report rows: FWDCONST <file> use-line=<L> const=<NAME> decl-line=<D>
9// Summary: NX-FWDCONST-SCAN files=<f> consts=<c> flagged=<n> skipped-big=<s> (exit 0 = scan completed)
10// HONEST SCOPE v1: full-comment lines (// or \) and double-quoted spans are SKIPPED; an INLINE
11// trailing comment mentioning the name still flags (rare; review rows by eye). Reporter only --
12// a ratchet gate asserting flagged<=N composes on top once the map is eaten.
13// license_tier: ORIGINAL No hw writes (Rule 26).
14import "nx_sovjson_lib.nx"
15import "nx_syscalls.nx"
16const FW_MAGIC_4096: i64 = 4096
17const FW_MAGIC_262144: i64 = 262144
18
19const FW_FILE_CAP: i64 = 2097152
20const FW_NAME_CAP: i64 = 64
21const FW_MAX_CONSTS: i64 = 512
22
23static g_names: *u8
24static g_doffs: *i64
25
26func fw_w(s: *u8) -> i64 { return sj_puts(s) }
27
28func fw_wn(v: i64) -> i64 {
29 var t: i64 = v
30 if t < 0 { sys_write(1, "-" as *u8, 1); t = 0 - t }
31 let tm: *u8 = sys_mmap(24)
32 var k: i64 = 0
33 if t == 0 { tm[0] = 48 as u8; k = 1 }
34 while t > 0 { tm[k] = (48 + (t % 10)) as u8; t = t / 10; k = k + 1 }
35 let b: *u8 = sys_mmap(24)
36 var j: i64 = 0
37 while j < k { b[j] = tm[k - 1 - j]; j = j + 1 }
38 sys_write(1, b, k)
39 return 0
40}
41
42func fw_isid(c: i64) -> i64 {
43 if c >= 97 { if c <= 122 { return 1 } }
44 if c >= 65 { if c <= 90 { return 1 } }
45 if c >= 48 { if c <= 57 { return 1 } }
46 if c == 95 { return 1 }
47 return 0
48}
49
50func fw_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 n: i64 = 0
54 var r: i64 = sys_read(fd, buf, cap - 1)
55 while r > 0 { n = n + r; if n >= cap - 1 { r = 0 } else { r = sys_read(fd, buf + n, cap - 1 - n) } }
56 sys_close(fd)
57 return n
58}
59
60func fw_line_of(buf: *u8, off: i64) -> i64 {
61 var ln: i64 = 1
62 var i: i64 = 0
63 while i < off { if buf[i] == (10 as u8) { ln = ln + 1 } i = i + 1 }
64 return ln
65}
66
67func ends_nx2(nm: *u8) -> i64 {
68 var n: i64 = 0
69 while nm[n] != (0 as u8) { n = n + 1 }
70 if n < 3 { return 0 }
71 if nm[n-3] == (46 as u8) { if nm[n-2] == (110 as u8) { if nm[n-1] == (120 as u8) { return 1 } } }
72 return 0
73}
74
75func join2(buf: *u8, dir: *u8, name: *u8) -> i64 {
76 var o: i64 = 0
77 var i: i64 = 0
78 while dir[i] != (0 as u8) { buf[o] = dir[i]; o = o + 1; i = i + 1 }
79 buf[o] = 47 as u8
80 o = o + 1
81 i = 0
82 while name[i] != (0 as u8) { buf[o] = name[i]; o = o + 1; i = i + 1 }
83 buf[o] = 0 as u8
84 return 0
85}
86
87// pass A: collect module const decls "const NAME" at line starts into g_names/g_doffs; returns count
88func fw_collect(buf: *u8, n: i64) -> i64 {
89 var cnt: i64 = 0
90 var ls: i64 = 0
91 while ls < n {
92 var p: i64 = ls
93 // "const " at line start (col 0 -- module consts in this codebase)
94 var m: i64 = 1
95 if p + 6 >= n { m = 0 }
96 if m == 1 { if buf[p] != (99 as u8) { m = 0 } }
97 if m == 1 { if buf[p+1] != (111 as u8) { m = 0 } }
98 if m == 1 { if buf[p+2] != (110 as u8) { m = 0 } }
99 if m == 1 { if buf[p+3] != (115 as u8) { m = 0 } }
100 if m == 1 { if buf[p+4] != (116 as u8) { m = 0 } }
101 if m == 1 { if buf[p+5] != (32 as u8) { m = 0 } }
102 if m == 1 {
103 var q: i64 = p + 6
104 while buf[q] == (32 as u8) { q = q + 1 }
105 var e: i64 = q
106 var run: i64 = 1
107 while run == 1 {
108 if e >= n { run = 0 } else { let c: i64 = buf[e] as i64; if fw_isid(c) == 1 { e = e + 1 } else { run = 0 } }
109 }
110 let nl: i64 = e - q
111 if nl > 0 { if nl < FW_NAME_CAP - 1 { if cnt < FW_MAX_CONSTS {
112 let slot: *u8 = ((g_names as i64) + cnt * FW_NAME_CAP) as *u8
113 var w: i64 = 0
114 while w < nl { slot[w] = buf[q + w]; w = w + 1 }
115 slot[nl] = 0 as u8
116 let dof: *i64 = g_doffs
117 dof[cnt] = ls
118 cnt = cnt + 1
119 } } }
120 }
121 var adv: i64 = 1
122 while adv == 1 {
123 if ls >= n { adv = 0 } else { let c2: i64 = buf[ls] as i64; ls = ls + 1; if c2 == 10 { adv = 0 } }
124 }
125 }
126 return cnt
127}
128
129// pass B: first word-boundary use of name in buf[0..limit), skipping comment lines + quoted spans.
130// returns byte offset of the hit or -1.
131func fw_first_use(buf: *u8, limit: i64, name: *u8) -> i64 {
132 var nl: i64 = 0
133 while name[nl] != (0 as u8) { nl = nl + 1 }
134 var p: i64 = 0
135 var linestart: i64 = 1
136 var incmt: i64 = 0
137 var inq: i64 = 0
138 while p < limit {
139 let c: i64 = buf[p] as i64
140 if c == 10 { linestart = 1; incmt = 0; inq = 0; p = p + 1 } else {
141 if linestart == 1 {
142 var sp: i64 = p
143 while buf[sp] == (32 as u8) { sp = sp + 1 }
144 if buf[sp] == (92 as u8) { incmt = 1 }
145 if buf[sp] == (47 as u8) { if buf[sp+1] == (47 as u8) { incmt = 1 } }
146 linestart = 0
147 }
148 if incmt == 1 { p = p + 1 } else {
149 if c == 34 { inq = 1 - inq; p = p + 1 } else {
150 if inq == 1 { p = p + 1 } else {
151 var hit: i64 = 1
152 if p + nl > limit { hit = 0 }
153 var k: i64 = 0
154 var chk: i64 = 1
155 while chk == 1 {
156 if hit == 0 { chk = 0 } else {
157 if k >= nl { chk = 0 } else {
158 if buf[p + k] != name[k] { hit = 0; chk = 0 } else { k = k + 1 }
159 }
160 }
161 }
162 if hit == 1 {
163 var pb: i64 = 0
164 if p > 0 { pb = fw_isid(buf[p-1] as i64) }
165 var nb: i64 = 0
166 if p + nl < limit { nb = fw_isid(buf[p+nl] as i64) }
167 if pb == 0 { if nb == 0 { return p } }
168 }
169 p = p + 1
170 }
171 }
172 }
173 }
174 }
175 return 0 - 1
176}
177
178// scan one file; prints FWDCONST rows; returns flags found
179func fw_process(path: *u8, buf: *u8) -> i64 {
180 let n: i64 = fw_read(path, buf, FW_FILE_CAP)
181 if n <= 0 { return 0 }
182 let nc: i64 = fw_collect(buf, n)
183 var flags: i64 = 0
184 var i: i64 = 0
185 while i < nc {
186 let nm: *u8 = ((g_names as i64) + i * FW_NAME_CAP) as *u8
187 let dof: *i64 = g_doffs
188 let dl: i64 = dof[i]
189 let hit: i64 = fw_first_use(buf, dl, nm)
190 if hit >= 0 {
191 fw_w("FWDCONST " as *u8)
192 fw_w(path)
193 fw_w(" use-line=" as *u8)
194 let ul: i64 = fw_line_of(buf, hit)
195 fw_wn(ul)
196 fw_w(" const=" as *u8)
197 fw_w(nm)
198 fw_w(" decl-line=" as *u8)
199 let dln: i64 = fw_line_of(buf, dl)
200 fw_wn(dln)
201 fw_w("\n" as *u8)
202 flags = flags + 1
203 }
204 i = i + 1
205 }
206 return flags
207}
208
209func main(argc: i64, argv: *i64) -> i64 {
210 if argc < 2 { fw_w("usage: nx_fwdconst_scan <dir> [dir2] [dir3]\n" as *u8); sys_exit(2); return 2 }
211 if (g_names as i64) == 0 { g_names = sys_mmap(FW_MAX_CONSTS * FW_NAME_CAP) }
212 if (g_doffs as i64) == 0 { g_doffs = sys_mmap(FW_MAX_CONSTS * 8) as *i64 }
213 let buf: *u8 = sys_mmap(FW_FILE_CAP)
214 let pbuf: *u8 = sys_mmap(FW_MAGIC_4096)
215 let dbuf: *u8 = sys_mmap(FW_MAGIC_262144)
216 var files: i64 = 0
217 var flagged: i64 = 0
218 var d: i64 = 1
219 while d < argc {
220 let dir: *u8 = argv[d] as *u8
221 let fd: i64 = sys_openat_rd(dir)
222 if fd >= 0 {
223 var go: i64 = 1
224 while go == 1 {
225 let nr: i64 = sys_getdents64(fd, dbuf, FW_MAGIC_262144)
226 if nr <= 0 { go = 0 } else {
227 var off: i64 = 0
228 while off < nr {
229 let rec: *u8 = ((dbuf as i64) + off) as *u8
230 let ty: i64 = dirent_type(rec)
231 let nm: *u8 = dirent_name(rec)
232 if ty != 4 {
233 if ends_nx2(nm) == 1 {
234 join2(pbuf, dir, nm)
235 files = files + 1
236 let fl: i64 = fw_process(pbuf, buf)
237 flagged = flagged + fl
238 }
239 }
240 off = off + dirent_reclen(rec)
241 }
242 }
243 }
244 sys_close(fd)
245 }
246 d = d + 1
247 }
248 fw_w("NX-FWDCONST-SCAN files=" as *u8)
249 fw_wn(files)
250 fw_w(" flagged=" as *u8)
251 fw_wn(flagged)
252 fw_w("\n" as *u8)
253 sys_exit(0)
254 return 0
255}