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}