code wiki / _hdl_build / nx_gcc_race.nx
nx_gcc_race.nx source
↩ module page · 301 lines · 12940 B
1// nx_gcc_race.nx -- the team's OWN capability to race a production compiler. Until now I
2// measured gcc by hand in a shell; that is not the team racing. This lets the TEAM do it:
3// it forks gcc -O2 to compile a C source, forks objdump to disassemble, and PARSES the
4// instruction count of the function body itself. With this, the team can race gcc on any
5// kernel, autonomously, and judge its own standing (win / tie / loss) -- the prerequisite
6// for hunting an EXCEED instead of me hand-picking targets. license_tier: ORIGINAL
7
8import "nx_syscalls.nx"
9
10// fork gcc -O2 -fcf-protection=none -c <cfile> -o <ofile>. cf-protection off so the count
11// is pure compute (no endbr64 landing pad). returns 0 ok, <0 on failure.
12func gr_gcc(cfile: *u8, ofile: *u8) -> i64 {
13 let argv: *i64 = sys_mmap(16 * 8) as *i64
14 argv[0] = ("/usr/bin/gcc" as *u8) as i64
15 argv[1] = ("-O2" as *u8) as i64
16 argv[2] = ("-fcf-protection=none" as *u8) as i64
17 argv[3] = ("-c" as *u8) as i64
18 argv[4] = cfile as i64
19 argv[5] = ("-o" as *u8) as i64
20 argv[6] = ofile as i64
21 argv[7] = 0
22 let envp: *i64 = sys_mmap(16) as *i64
23 envp[0] = ("PATH=/usr/bin:/bin:/usr/local/bin" as *u8) as i64; envp[1] = 0
24 let pid: i64 = sys_fork()
25 if pid < 0 { return 0 - 1 }
26 if pid == 0 {
27 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
28 if dn >= 0 { sys_dup3(dn, 2, 0); sys_close(dn) }
29 sys_execve("/usr/bin/gcc" as *u8, argv, envp)
30 sys_exit(127)
31 }
32 let st: *i64 = sys_mmap(16) as *i64
33 st[0] = 0; sys_wait4(pid, st, 0)
34 if (st[0] & 0x7f) != 0 { return 0 - 1 }
35 if ((st[0] >> 8) & 0xff) != 0 { return 0 - 1 }
36 return 0
37}
38
39// does a binary exist (readable)?
40func gr_exists(path: *u8) -> i64 { let fd: i64 = sys_openat_rd(path); if fd < 0 { return 0 } sys_close(fd); return 1 }
41
42// pick the NEWEST gcc the system has -- the team always races the latest available.
43func gr_newest_gcc() -> *u8 {
44 if gr_exists("/usr/bin/gcc-16" as *u8) == 1 { return "/usr/bin/gcc-16" as *u8 }
45 if gr_exists("/usr/bin/gcc-15" as *u8) == 1 { return "/usr/bin/gcc-15" as *u8 }
46 if gr_exists("/usr/bin/gcc-14" as *u8) == 1 { return "/usr/bin/gcc-14" as *u8 }
47 if gr_exists("/usr/bin/gcc-13" as *u8) == 1 { return "/usr/bin/gcc-13" as *u8 }
48 return "/usr/bin/gcc" as *u8
49}
50
51// version-parameterized compile: fork <gccpath> -O2 -fcf-protection=none -c. 0 ok, <0 fail.
52func gr_gcc_v(gccpath: *u8, cfile: *u8, ofile: *u8) -> i64 {
53 let argv: *i64 = sys_mmap(16 * 8) as *i64
54 argv[0] = gccpath as i64
55 argv[1] = ("-O2" as *u8) as i64
56 argv[2] = ("-fcf-protection=none" as *u8) as i64
57 argv[3] = ("-c" as *u8) as i64
58 argv[4] = cfile as i64
59 argv[5] = ("-o" as *u8) as i64
60 argv[6] = ofile as i64
61 argv[7] = 0
62 let envp: *i64 = sys_mmap(16) as *i64
63 envp[0] = ("PATH=/usr/bin:/bin:/usr/local/bin" as *u8) as i64; envp[1] = 0
64 let pid: i64 = sys_fork()
65 if pid < 0 { return 0 - 1 }
66 if pid == 0 {
67 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
68 if dn >= 0 { sys_dup3(dn, 2, 0); sys_close(dn) }
69 sys_execve(gccpath, argv, envp)
70 sys_exit(127)
71 }
72 let st: *i64 = sys_mmap(16) as *i64
73 st[0] = 0; sys_wait4(pid, st, 0)
74 if (st[0] & 0x7f) != 0 { return 0 - 1 }
75 if ((st[0] >> 8) & 0xff) != 0 { return 0 - 1 }
76 return 0
77}
78
79// fork objdump -d <ofile> with stdout -> <txtfile>. returns 0 ok.
80func gr_objdump(ofile: *u8, txtfile: *u8) -> i64 {
81 let argv: *i64 = sys_mmap(16) as *i64
82 argv[0] = ("/usr/bin/objdump" as *u8) as i64
83 argv[1] = ("-d" as *u8) as i64
84 argv[2] = ofile as i64
85 argv[3] = 0
86 let envp: *i64 = sys_mmap(16) as *i64
87 envp[0] = ("PATH=/usr/bin:/bin" as *u8) as i64; envp[1] = 0
88 let pid: i64 = sys_fork()
89 if pid < 0 { return 0 - 1 }
90 if pid == 0 {
91 let fd: i64 = sys_openat_wr(txtfile, 0x1a4)
92 if fd >= 0 { sys_dup3(fd, 1, 0); sys_close(fd) }
93 let dn: i64 = sys_openat_wr("/dev/null" as *u8, 0x1a4)
94 if dn >= 0 { sys_dup3(dn, 2, 0); sys_close(dn) }
95 sys_execve("/usr/bin/objdump" as *u8, argv, envp)
96 sys_exit(127)
97 }
98 let st: *i64 = sys_mmap(16) as *i64
99 st[0] = 0; sys_wait4(pid, st, 0)
100 if (st[0] & 0x7f) != 0 { return 0 - 1 }
101 return 0
102}
103
104func gr_match(buf: *u8, p: i64, lim: i64, lit: *u8) -> i64 {
105 var j: i64 = 0
106 while lit[j] != (0 as u8) { if p + j >= lim { return 0 } if buf[p + j] != lit[j] { return 0 } j = j + 1 }
107 return 1
108}
109func gr_find(buf: *u8, blen: i64, needle: *u8) -> i64 {
110 var i: i64 = 0
111 while i < blen { if gr_match(buf, i, blen, needle) == 1 { return i } i = i + 1 }
112 return 0 - 1
113}
114
115// count the real instructions in function <fname>'s body in the objdump text. An instr
116// line is `addr:\tbytes\tmnemonic ...` (2 tabs); byte-continuation lines have 1 tab; a
117// blank line ends the function. ret/endbr64/nop are excluded (not compute).
118func gr_count(txtpath: *u8, fname: *u8) -> i64 {
119 let lenp: *i64 = sys_mmap(8) as *i64
120 let buf: *u8 = sys_read_file(txtpath, lenp)
121 let blen: i64 = lenp[0]
122 // needle "<fname>:"
123 let nd: *u8 = sys_mmap(64)
124 nd[0] = 60 as u8 // '<'
125 var j: i64 = 0
126 while fname[j] != (0 as u8) { nd[1 + j] = fname[j]; j = j + 1 }
127 nd[1 + j] = 62 as u8; nd[2 + j] = 58 as u8; nd[3 + j] = 0 as u8 // '>' ':'
128 let at: i64 = gr_find(buf, blen, nd)
129 if at < 0 { return 0 - 1 }
130 // advance to the start of the line AFTER the label line
131 var i: i64 = at
132 var d0: i64 = 0
133 while d0 == 0 { if i >= blen { d0 = 1 } else { if buf[i] == (10 as u8) { d0 = 1 } else { i = i + 1 } } }
134 i = i + 1
135 var count: i64 = 0
136 var stop: i64 = 0
137 while stop == 0 {
138 if i >= blen { stop = 1 } else {
139 // line [i, le)
140 var le: i64 = i
141 var d1: i64 = 0
142 while d1 == 0 { if le >= blen { d1 = 1 } else { if buf[le] == (10 as u8) { d1 = 1 } else { le = le + 1 } } }
143 if le == i { stop = 1 } else { // blank line ends the function
144 // count tabs; find the 2nd tab position
145 var tabs: i64 = 0
146 var t2: i64 = 0 - 1
147 var p: i64 = i
148 while p < le { if buf[p] == (9 as u8) { tabs = tabs + 1; if tabs == 2 { t2 = p } } p = p + 1 }
149 if tabs >= 2 { // an instruction line (has a mnemonic)
150 let ms: i64 = t2 + 1
151 if gr_match(buf, ms, le, "ret" as *u8) == 1 { count = count + 0 }
152 else { if gr_match(buf, ms, le, "endbr64" as *u8) == 1 { count = count + 0 }
153 else { if gr_match(buf, ms, le, "nop" as *u8) == 1 { count = count + 0 }
154 else { count = count + 1 } } }
155 }
156 i = le + 1
157 }
158 }
159 }
160 return count
161}
162
163// approximate latency of an x86 mnemonic (Skylake-class, uops.info-grounded): cheap integer
164// ops 1 cycle; imul/mul ~3; div ~20. The COST metric -- what actually matters, not raw
165// instruction count (clang's imul is 1 insn but ~3x a lea's latency) -- so the team judges
166// fairly itself instead of me telling it count != cost.
167func gr_mnem_lat(buf: *u8, ms: i64, le: i64) -> i64 {
168 if gr_match(buf, ms, le, "imul" as *u8) == 1 { return 3 }
169 if gr_match(buf, ms, le, "mul" as *u8) == 1 { return 3 }
170 if gr_match(buf, ms, le, "idiv" as *u8) == 1 { return 20 }
171 if gr_match(buf, ms, le, "div" as *u8) == 1 { return 20 }
172 return 1
173}
174
175// COST a function body: sum the latency of its instructions, read straight from objdump.
176// The team uses this on BOTH its own emitted code and the competitor's -- apples to apples.
177func gr_func_cost(txtpath: *u8, fname: *u8) -> i64 {
178 let lenp: *i64 = sys_mmap(8) as *i64
179 let buf: *u8 = sys_read_file(txtpath, lenp)
180 let blen: i64 = lenp[0]
181 let nd: *u8 = sys_mmap(64)
182 nd[0] = 60 as u8
183 var j: i64 = 0
184 while fname[j] != (0 as u8) { nd[1 + j] = fname[j]; j = j + 1 }
185 nd[1 + j] = 62 as u8; nd[2 + j] = 58 as u8; nd[3 + j] = 0 as u8
186 let at: i64 = gr_find(buf, blen, nd)
187 if at < 0 { return 0 - 1 }
188 var i: i64 = at
189 var d0: i64 = 0
190 while d0 == 0 { if i >= blen { d0 = 1 } else { if buf[i] == (10 as u8) { d0 = 1 } else { i = i + 1 } } }
191 i = i + 1
192 var cost: i64 = 0
193 var stop: i64 = 0
194 while stop == 0 {
195 if i >= blen { stop = 1 } else {
196 var le: i64 = i
197 var d1: i64 = 0
198 while d1 == 0 { if le >= blen { d1 = 1 } else { if buf[le] == (10 as u8) { d1 = 1 } else { le = le + 1 } } }
199 if le == i { stop = 1 } else {
200 var tabs: i64 = 0
201 var t2: i64 = 0 - 1
202 var p: i64 = i
203 while p < le { if buf[p] == (9 as u8) { tabs = tabs + 1; if tabs == 2 { t2 = p } } p = p + 1 }
204 if tabs >= 2 {
205 let ms: i64 = t2 + 1
206 if gr_match(buf, ms, le, "ret" as *u8) == 0 { if gr_match(buf, ms, le, "endbr64" as *u8) == 0 { if gr_match(buf, ms, le, "nop" as *u8) == 0 {
207 cost = cost + gr_mnem_lat(buf, ms, le)
208 } } }
209 }
210 i = le + 1
211 }
212 }
213 }
214 return cost
215}
216
217// objdump <ofile> -> <txtfile> then cost function <fname>. <0 on error.
218func gr_cost_obj(ofile: *u8, txtfile: *u8, fname: *u8) -> i64 {
219 if gr_objdump(ofile, txtfile) != 0 { return 0 - 1 }
220 return gr_func_cost(txtfile, fname)
221}
222
223// end-to-end: compile <cfile> at -O2 and count function <fname>'s instructions. <0 = error.
224func gr_race_c(cfile: *u8, ofile: *u8, txtfile: *u8, fname: *u8) -> i64 {
225 if gr_gcc(cfile, ofile) != 0 { return 0 - 1 }
226 if gr_objdump(ofile, txtfile) != 0 { return 0 - 2 }
227 return gr_count(txtfile, fname)
228}
229
230// append mnemonic word at buf[ms..] to out (NUL-terminated, space-separated) if DISTINCT.
231func gr_append_mnem(out: *u8, ol: i64, buf: *u8, ms: i64, me: i64) -> i64 {
232 // already present? scan out for the word followed by a space
233 var p: i64 = 0
234 var found: i64 = 0
235 while p < ol {
236 var q: i64 = ms
237 var m: i64 = 1
238 while q < me { if out[p + q - ms] != buf[q] { m = 0 } q = q + 1 }
239 if m == 1 { if out[p + (me - ms)] == (32 as u8) { found = 1 } }
240 p = p + 1
241 }
242 if found == 1 { return ol }
243 var i: i64 = ms
244 while i < me { out[ol] = buf[i]; ol = ol + 1; i = i + 1 }
245 out[ol] = 32 as u8; ol = ol + 1; out[ol] = 0 as u8
246 return ol
247}
248
249// DIAGNOSIS: write the DISTINCT mnemonics of function <fname>'s body into out (the team
250// reading the competitor's machine code to see HOW it won). returns the instruction count.
251func gr_func_mnemonics(txtpath: *u8, fname: *u8, out: *u8) -> i64 {
252 let lenp: *i64 = sys_mmap(8) as *i64
253 let buf: *u8 = sys_read_file(txtpath, lenp)
254 let blen: i64 = lenp[0]
255 let nd: *u8 = sys_mmap(64)
256 nd[0] = 60 as u8
257 var j: i64 = 0
258 while fname[j] != (0 as u8) { nd[1 + j] = fname[j]; j = j + 1 }
259 nd[1 + j] = 62 as u8; nd[2 + j] = 58 as u8; nd[3 + j] = 0 as u8
260 let at: i64 = gr_find(buf, blen, nd)
261 out[0] = 0
262 if at < 0 { return 0 - 1 }
263 var i: i64 = at
264 var d0: i64 = 0
265 while d0 == 0 { if i >= blen { d0 = 1 } else { if buf[i] == (10 as u8) { d0 = 1 } else { i = i + 1 } } }
266 i = i + 1
267 var count: i64 = 0
268 var ol: i64 = 0
269 var stop: i64 = 0
270 while stop == 0 {
271 if i >= blen { stop = 1 } else {
272 var le: i64 = i
273 var d1: i64 = 0
274 while d1 == 0 { if le >= blen { d1 = 1 } else { if buf[le] == (10 as u8) { d1 = 1 } else { le = le + 1 } } }
275 if le == i { stop = 1 } else {
276 var tabs: i64 = 0
277 var t2: i64 = 0 - 1
278 var p: i64 = i
279 while p < le { if buf[p] == (9 as u8) { tabs = tabs + 1; if tabs == 2 { t2 = p } } p = p + 1 }
280 if tabs >= 2 {
281 let ms: i64 = t2 + 1
282 var me: i64 = ms
283 var dm: i64 = 0
284 while dm == 0 { if me >= le { dm = 1 } else { if buf[me] == (32 as u8) { dm = 1 } else { if buf[me] == (9 as u8) { dm = 1 } else { me = me + 1 } } } }
285 if gr_match(buf, ms, le, "ret" as *u8) == 0 { if gr_match(buf, ms, le, "endbr64" as *u8) == 0 { if gr_match(buf, ms, le, "nop" as *u8) == 0 {
286 ol = gr_append_mnem(out, ol, buf, ms, me); count = count + 1
287 } } }
288 }
289 i = le + 1
290 }
291 }
292 }
293 return count
294}
295
296// version-parameterized end-to-end race against a specific gcc binary.
297func gr_race_cv(gccpath: *u8, cfile: *u8, ofile: *u8, txtfile: *u8, fname: *u8) -> i64 {
298 if gr_gcc_v(gccpath, cfile, ofile) != 0 { return 0 - 1 }
299 if gr_objdump(ofile, txtfile) != 0 { return 0 - 2 }
300 return gr_count(txtfile, fname)
301}