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}