code wiki / _hdl_build / nx_rv64_difftest.nx

nx_rv64_difftest.nx source

↩ module page · 113 lines · 8740 B

1// nx_rv64_difftest.nx -- DIFFERENTIAL FUZZER for the RV64 emulator stack (the SOTA way to validate a JIT: V8/QEMU do 2// exactly this). Generates hundreds of RANDOM, terminating-by-construction RV64 programs (random arithmetic/shift/ 3// memory ops + a bounded counting loop) with a FIXED SEED (reproducible), and asserts the GOLDEN INTERPRETER, the JIT 4// (whole-program compile+run), and the TIERED ENGINE all produce BIT-IDENTICAL final state (registers XOR memory). 5// This turns "equivalence-validated" from a handful of hand-written KATs into broad evidence -- and any divergence is 6// a real bug (reproducible from the seed). expect_exit: 0 7import "nx_syscalls.nx" 8import "nx_rv64_fast.nx" 9import "nx_rv64_jit.nx" 10import "nx_rv64_tier.nx" 11 12func g_puts(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 13func g_pn(v: i64) -> i64 { let b: *u8=sys_mmap(28); var x: i64=v; if x<0{b[0]=45;sys_write(1,b,1);x=0-x} if x==0{b[0]=48;sys_write(1,b,1);return 0} var d: i64=0; var y: i64=x; while y>0{d=d+1;y=y/10} var i: i64=d-1; y=x; while i>=0{b[i]=(48+(y%10)) as u8;y=y/10;i=i-1} sys_write(1,b,d); return 0 } 14func ck(name: *u8, c: i64) -> i64 { if c==1 { g_puts(" PASS " as *u8) } else { g_puts(" FAIL " as *u8) } g_puts(name); g_puts("\n" as *u8); return c } 15 16const MEMSZ: i64 = 4096 17const NPROG: i64 = 500 18const BIGSTEPS: i64 = 4000000 19 20// MMIX LCG -> a 24-bit positive slice. deterministic (fixed seed => reproducible fuzzing). 21func rnd(st: *i64) -> i64 { st[0]=st[0]*6364136223846793005 + 1442695040888963407; return (st[0]>>16)&0xFFFFFF } 22func rndn(st: *i64, n: i64) -> i64 { return rnd(st)%n } 23 24// emit one random JIT-supported op at idx: dest x3..x8, operands x1..x8 (never writes x1/x2 -> the loop counter/limit 25// stay sacred => the generated loop always terminates). immediates kept in sext-imm32 range so JIT==interpreter. 26func gen_op(st: *i64, opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64, idx: i64) -> i64 { 27 let sel: i64 = rndn(st,15); let d: i64=3+rndn(st,6); let a: i64=1+rndn(st,8); let b: i64=1+rndn(st,8) 28 rd[idx]=d; rs1[idx]=a; rs2[idx]=b; imm[idx]=0 29 if sel==12 { opk[idx]=FK_MUL } // M ext MUL/MULH/MULHU (all JIT-supported) -- fuzz interp==JIT==tier equivalence 30 if sel==13 { opk[idx]=FK_MULH } 31 if sel==14 { opk[idx]=FK_MULHU } 32 if sel==0 { opk[idx]=FK_ADDI; imm[idx]=rndn(st,64)-32 } 33 if sel==1 { opk[idx]=FK_ADD } 34 if sel==2 { opk[idx]=FK_SUB } 35 if sel==3 { opk[idx]=FK_AND } 36 if sel==4 { opk[idx]=FK_OR } 37 if sel==5 { opk[idx]=FK_ANDI; imm[idx]=rndn(st,256) } 38 if sel==6 { opk[idx]=FK_ORI; imm[idx]=rndn(st,256) } 39 if sel==7 { opk[idx]=FK_SLLI; imm[idx]=rndn(st,32) } 40 if sel==8 { opk[idx]=FK_SRLI; imm[idx]=rndn(st,32) } 41 if sel==9 { opk[idx]=FK_LUI; var v: i64=(rndn(st,1048576))<<12; if v>=2147483648 { v=v-4294967296 } imm[idx]=v } // sext32 42 if sel==10 { opk[idx]=FK_LW; imm[idx]=rndn(st,64) } // reg[a]+imm -> mostly OOB (tests bounds-check equivalence) 43 if sel==11 { opk[idx]=FK_SW; imm[idx]=rndn(st,64) } 44 return 0 45} 46// build a random terminating program into the arrays; return instruction count. 47func gen_program(st: *i64, opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64) -> i64 { 48 var idx: i64=0 49 var pk: i64=3+rndn(st,4); var c: i64=0; while c<pk { gen_op(st,opk,rd,rs1,rs2,imm,idx); idx=idx+1; c=c+1 } 50 opk[idx]=FK_ADDI; rd[idx]=1; rs1[idx]=0; rs2[idx]=0; imm[idx]=0; idx=idx+1 // li x1,0 (counter) 51 opk[idx]=FK_ADDI; rd[idx]=2; rs1[idx]=0; rs2[idx]=0; imm[idx]=3+rndn(st,10); idx=idx+1 // li x2, 3..12 (limit) 52 let loop_start: i64=idx 53 var bk: i64=2+rndn(st,4); c=0; while c<bk { gen_op(st,opk,rd,rs1,rs2,imm,idx); idx=idx+1; c=c+1 } 54 opk[idx]=FK_ADDI; rd[idx]=1; rs1[idx]=1; rs2[idx]=0; imm[idx]=1; idx=idx+1 // addi x1,x1,1 55 opk[idx]=FK_BLT; rd[idx]=0; rs1[idx]=1; rs2[idx]=2; imm[idx]=(loop_start-idx)*4; idx=idx+1 // blt x1,x2,loop 56 var qk: i64=2+rndn(st,3); c=0; while c<qk { gen_op(st,opk,rd,rs1,rs2,imm,idx); idx=idx+1; c=c+1 } 57 return idx 58} 59func ck_reg(reg: *i64) -> i64 { var h: i64=1469598103934665603; var i: i64=1; while i<32 { h=(h^reg[i])*1099511628211; i=i+1 } return h } 60func ck_mem(mem: *u8, n: i64) -> i64 { var h: i64=1469598103934665603; var i: i64=0; while i<n { h=(h^(mem[i] as i64))*1099511628211; i=i+1 } return h } 61func zero32(r: *i64) -> i64 { var i: i64=0; while i<32 { r[i]=0; i=i+1 } return 0 } 62 63func main() -> i64 { 64 g_puts("nx_rv64_difftest (differential fuzzer: 500 random programs, interpreter == JIT == tiered engine, bit-identical)\n" as *u8) 65 var pass: i64=0; var total: i64=0 66 let opk: *i64=sys_mmap(256*8) as *i64; let rd: *i64=sys_mmap(256*8) as *i64; let rs1: *i64=sys_mmap(256*8) as *i64; let rs2: *i64=sys_mmap(256*8) as *i64; let imm: *i64=sys_mmap(256*8) as *i64 67 let ri: *i64=sys_mmap(32*8) as *i64; let rj: *i64=sys_mmap(32*8) as *i64; let rt: *i64=sys_mmap(32*8) as *i64 68 let mi: *u8=sys_mmap(MEMSZ); let mj: *u8=sys_mmap(MEMSZ); let mt: *u8=sys_mmap(MEMSZ) 69 let x86: *u8=sys_mmap(65536); let x86off: *i64=sys_mmap(256*8) as *i64; let stats: *i64=sys_mmap(64) as *i64 70 let st: *i64=sys_mmap(8) as *i64; st[0]=305419896 // fixed seed => reproducible 71 72 var jit_div: i64=0; var tier_div: i64=0; var jit_bad: i64=0; var first_div: i64=0-1; var loops_jitted: i64=0 73 var p: i64=0 74 while p<NPROG { 75 let nc: i64 = gen_program(st, opk, rd, rs1, rs2, imm) 76 var z: i64=0; while z<MEMSZ { mi[z]=0 as u8; mj[z]=0 as u8; mt[z]=0 as u8; z=z+1 } 77 // 1) golden interpreter 78 zero32(ri); fk_run(opk, rd, rs1, rs2, imm, nc, ri, mi, MEMSZ, BIGSTEPS, (0 as i64) as *NxVirtioMmio) 79 let hi: i64 = ck_reg(ri) ^ ck_mem(mi, MEMSZ) 80 // 2) JIT: whole-program compile + run 81 zero32(rj); jit_set_membase((mj as i64) - FK_MEMBASE, MEMSZ) 82 let xlen: i64 = jit_compile(opk, rd, rs1, rs2, imm, nc, x86, x86off) 83 if xlen<0 { jit_bad=jit_bad+1 } else { jit_run(x86, xlen, rj) } 84 let hj: i64 = ck_reg(rj) ^ ck_mem(mj, MEMSZ) 85 // 3) tiered engine (threshold 2 -> even the shortest generated loop (>=2 back-edges) is JIT-compiled + run natively) 86 zero32(rt); tier_run(opk, rd, rs1, rs2, imm, nc, rt, mt, MEMSZ, BIGSTEPS, (0 as i64) as *NxVirtioMmio, 2, stats) 87 let ht: i64 = ck_reg(rt) ^ ck_mem(mt, MEMSZ) 88 loops_jitted=loops_jitted+stats[1] 89 if xlen>=0 { if hj!=hi { jit_div=jit_div+1; if first_div<0 { first_div=p } } } 90 if ht!=hi { tier_div=tier_div+1; if first_div<0 { first_div=p } } 91 p=p+1 92 } 93 g_puts(" ran "); g_pn(NPROG); g_puts(" random programs. JIT divergences="); g_pn(jit_div); g_puts(" tier divergences="); g_pn(tier_div); g_puts(" (jit-uncompilable="); g_pn(jit_bad); g_puts(", loops JIT'd="); g_pn(loops_jitted); g_puts(")\n" as *u8) 94 if first_div>=0 { g_puts(" FIRST DIVERGENCE at program #"); g_pn(first_div); g_puts(" (reproducible from the fixed seed)\n" as *u8) } 95 96 var t1: i64=0; if jit_div==0 { t1=1 } 97 pass=pass+ck("T1: the JIT matches the golden interpreter on ALL 500 random programs (bit-identical reg+mem)" as *u8, t1); total=total+1 98 var t2: i64=0; if tier_div==0 { t2=1 } 99 pass=pass+ck("T2: the TIERED ENGINE matches the golden interpreter on ALL 500 (interpret-cold + JIT-hot compose correctly)" as *u8, t2); total=total+1 100 var t3: i64=0; if jit_bad==0 { t3=1 } 101 pass=pass+ck("T3: every generated program was JIT-compilable (the generator stays inside the JIT's supported ISA)" as *u8, t3); total=total+1 102 var t4: i64=0; if loops_jitted>=NPROG { t4=1 } 103 pass=pass+ck("T4: the tiered engine actually JIT-compiled the hot loop in every program (the JIT path was exercised)" as *u8, t4); total=total+1 104 105 var okall: i64=0; if pass==total { okall=1 } 106 g_puts("---- nx_rv64_difftest: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8) 107 if okall==1 { 108 let logf: i64=sys_openat_append("knowledge/status/rv64_difftest.log" as *u8, 420) 109 if logf>=0 { let z: i64=sys_write(logf,"NXRV64DIFFTEST GREEN: differential fuzzer -- 500 random RV64 programs, interpreter==JIT==tiered engine bit-identical (reg+mem); 0 divergences. equivalence validated broadly, not just on hand-written KATs. the SOTA way to validate a JIT (V8/QEMU method)\n" as *u8,260); sys_close(logf) } 110 g_puts("verdict=GREEN (differential fuzzer: 500 random programs, interpreter == JIT == tiered engine bit-identical, 0 divergences; equivalence validated broadly -- the SOTA JIT-validation method)\n" as *u8); sys_exit(0); return 0 111 } 112 g_puts("verdict=RED (a divergence was found -- a real bug, reproducible from the seed)\n" as *u8); sys_exit(1); return 1 113}