code wiki / _hdl_build / nx_regalloc_exec_test.nx

nx_regalloc_exec_test.nx source

↩ module page · 112 lines · 5260 B

1// nx_regalloc_exec_test.nx -- PROVE register-allocated execution is 1:1 with the 2// reference, on the race kernel's real arithmetic, over many inputs -- AND prove 3// the check CATCHES a corrupted allocation. This is the gate that makes absorbing 4// the allocator into codegen safe: any g1-style miscompile diverges here and fails. 5// 6// v0=INPUT c, v1=INPUT acc, v2=INPUT i 7// v3=c*K, v4=v3+A, v5=v4>>31, v6=v4^v5(c2,live-out), 8// v7=v6&M, v8=acc+v7(acc1,live-out), v9=i+1(i1,live-out), v10=v9<KK 9// 10// Known answer: allocation sound + 0 spills; ref==alloc for ALL random inputs; a 11// corrupted allocation (two overlapping vregs aliased to one register) DIVERGES 12// (the verifier catches the miscompile). exit 0. 13 14import "nx_regalloc_interp.nx" 15 16func re_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 17func re_emit(name: *u8, v: i64) -> i64 { 18 re_puts(name) 19 let b: *u8 = sys_mmap(28); var m: i64 = v; if m < 0 { m = 0 - m } 20 let t: *u8 = sys_mmap(28); var k: i64 = 0 21 if m == 0 { t[0] = 48; k = 1 } 22 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 } 23 var i: i64 = 0; while i < k { b[i] = t[k - 1 - i]; i = i + 1 } 24 b[k] = 10; sys_write(1, b, k + 1); return 0 25} 26 27const RE_N: i64 = 11 28const RE_NREG: i64 = 8 29const RE_TRIALS: i64 = 256 30const RE_K: i64 = 6364136223846793005 31const RE_A: i64 = 1442695040888963407 32const RE_M: i64 = 65535 33const RE_KK: i64 = 1000000 34 35func re_rng(s: i64) -> i64 { return s * 6364136223846793005 + 1442695040888963407 } 36 37func main() -> i64 { 38 re_puts("=== register-allocated execution == reference (1:1), race kernel ===\n" as *u8) 39 40 let op: *i64 = sys_mmap(8 * RE_N) 41 let u0: *i64 = sys_mmap(8 * RE_N) 42 let u1: *i64 = sys_mmap(8 * RE_N) 43 let imm: *i64 = sys_mmap(8 * RE_N) 44 // v0..v2 inputs 45 op[0] = ROP_INPUT; u0[0] = 0 - 1; u1[0] = 0 - 1; imm[0] = 0 46 op[1] = ROP_INPUT; u0[1] = 0 - 1; u1[1] = 0 - 1; imm[1] = 1 47 op[2] = ROP_INPUT; u0[2] = 0 - 1; u1[2] = 0 - 1; imm[2] = 2 48 op[3] = ROP_MUL_IMM; u0[3] = 0; u1[3] = 0 - 1; imm[3] = RE_K 49 op[4] = ROP_ADD_IMM; u0[4] = 3; u1[4] = 0 - 1; imm[4] = RE_A 50 op[5] = ROP_SHR_IMM; u0[5] = 4; u1[5] = 0 - 1; imm[5] = 31 51 op[6] = ROP_XOR; u0[6] = 4; u1[6] = 5; imm[6] = 0 52 op[7] = ROP_AND_IMM; u0[7] = 6; u1[7] = 0 - 1; imm[7] = RE_M 53 op[8] = ROP_ADD; u0[8] = 1; u1[8] = 7; imm[8] = 0 54 op[9] = ROP_ADD_IMM; u0[9] = 2; u1[9] = 0 - 1; imm[9] = 1 55 op[10] = ROP_LT_IMM; u0[10] = 9; u1[10] = 0 - 1; imm[10] = RE_KK 56 57 let loa: i64 = 6 // c2 (live-out) 58 let lob: i64 = 8 // acc1 (live-out) 59 let loc: i64 = 9 // i1 (live-out) 60 61 // intervals; live-outs kept live to the block end (loop-carried). 62 let last_use: *i64 = sys_mmap(8 * RE_N) 63 let alloc: *i64 = sys_mmap(8 * RE_N) 64 ra_compute_last_use(RE_N, u0, u1, last_use) 65 last_use[loa] = RE_N; last_use[lob] = RE_N; last_use[loc] = RE_N 66 let spills: i64 = ra_linscan(RE_N, RE_NREG, last_use, alloc) 67 let valid: i64 = ra_validate(RE_N, last_use, alloc, RE_NREG) 68 re_emit(" spills (0 = fits) : " as *u8, spills) 69 re_emit(" sound (0 = valid) : " as *u8, valid) 70 71 // 1:1 over random inputs: reference vs allocated execution. 72 let inputs: *i64 = sys_mmap(8 * 4) 73 var mism: i64 = 0 74 var s: i64 = 11400714819323198485 75 var tr: i64 = 0 76 while tr < RE_TRIALS { 77 s = re_rng(s); inputs[0] = s 78 s = re_rng(s); inputs[1] = s 79 s = re_rng(s); inputs[2] = s 80 let rref: i64 = ri_interp_ref(RE_N, op, u0, u1, imm, inputs, loa, lob, loc) 81 let ralc: i64 = ri_interp_alloc(RE_N, RE_NREG, op, u0, u1, imm, alloc, inputs, loa, lob, loc) 82 if rref != ralc { mism = mism + 1 } 83 tr = tr + 1 84 } 85 re_emit(" trials : " as *u8, RE_TRIALS) 86 re_emit(" ref != alloc mismatches : " as *u8, mism) 87 88 // NEGATIVE CONTROL: corrupt the allocation -- alias acc_in(v1) onto t0(v3)'s 89 // register (they overlap), then the verifier MUST catch the divergence. 90 let bad: *i64 = sys_mmap(8 * RE_N) 91 var c: i64 = 0 92 while c < RE_N { bad[c] = alloc[c]; c = c + 1 } 93 bad[1] = alloc[3] // force a real collision 94 inputs[0] = 123456789; inputs[1] = 987654321; inputs[2] = 42 95 let g_ref: i64 = ri_interp_ref(RE_N, op, u0, u1, imm, inputs, loa, lob, loc) 96 let g_bad: i64 = ri_interp_alloc(RE_N, RE_NREG, op, u0, u1, imm, bad, inputs, loa, lob, loc) 97 var caught: i64 = 0 98 if g_ref != g_bad { caught = 1 } 99 re_emit(" corrupted alloc caught : " as *u8, caught) 100 101 re_puts("----------------------------------------------------------------\n" as *u8) 102 re_puts(" allocated code computes IDENTICALLY to the reference, and a corrupted\n" as *u8) 103 re_puts(" allocation is CAUGHT -> the gate that makes codegen absorption safe.\n" as *u8) 104 105 // GATE 106 if valid != 0 { sys_exit(1); return 1 } 107 if spills != 0 { sys_exit(2); return 2 } // race body fits in registers 108 if mism != 0 { sys_exit(3); return 3 } // execution equivalence must be exact 109 if caught != 1 { sys_exit(4); return 4 } // verifier must catch a miscompile 110 sys_exit(0) 111 return 0 112}