code wiki / _hdl_build / nx_rv64_jit_mem_gate.nx
nx_rv64_jit_mem_gate.nx source
↩ module page · 100 lines · 8800 B
1// nx_rv64_jit_mem_gate.nx -- GATE: the JIT now compiles GUEST MEMORY loads/stores (LW/LD/SW/SD) + the RAM-addressing
2// shifts (SRLI/SRAI), against a baked-in biased guest-mem base ([rsi+rax], rsi = host_ptr-0x80000000). Proves a real
3// memory round-trip (store -> load-back -> compute -> store) gives byte-identical guest memory + registers vs the
4// golden interpreter. This is the capability that lets the JIT run REAL code (arrays/structs/stack), not just
5// register math -- the biggest remaining gap. Honest limits: no bounds-check (interpreter has one; a JIT needs guard
6// pages), no MMIO/finisher inside JIT'd blocks (those stay the interpreter's job). expect_exit:0
7import "nx_syscalls.nx"
8import "nx_rv64_asm.nx"
9import "nx_rv64_fast.nx"
10import "nx_rv64_jit.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 = 65536
17const MEMBASE: i64 = 0x80000000
18
19func predec(src: *u8, code: *u8, opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64) -> i64 {
20 let nb: i64 = rvasm_assemble_str(src, code, 4096); if nb<0 { return 0-1 } return fk_predecode(code, nb, opk, rd, rs1, rs2, imm)
21}
22// JIT the program against guest memory `mem` (bakes the biased base); reg + mem updated in place. returns xlen.
23func run_jit_mem(opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64, nc: i64, reg: *i64, mem: *u8) -> i64 {
24 let x86: *u8=sys_mmap(65536); let x86off: *i64=sys_mmap(nc*8+64) as *i64
25 jit_set_membase((mem as i64) - MEMBASE, MEMSZ) // biased base + bound: host_ptr - 0x80000000, size MEMSZ
26 let xlen: i64 = jit_compile(opk, rd, rs1, rs2, imm, nc, x86, x86off)
27 if xlen<0 { return xlen }
28 var z: i64=0; while z<32 { reg[z]=0; z=z+1 }
29 jit_run(x86, xlen, reg); return xlen
30}
31func run_interp_mem(opk: *i64, rd: *i64, rs1: *i64, rs2: *i64, imm: *i64, nc: i64, reg: *i64, mem: *u8) -> i64 {
32 var z: i64=0; while z<32 { reg[z]=0; z=z+1 }
33 fk_run(opk, rd, rs1, rs2, imm, nc, reg, mem, MEMSZ, 200000000, (0 as i64) as *NxVirtioMmio); return 0
34}
35
36func main() -> i64 {
37 g_puts("nx_rv64_jit_mem_gate (JIT guest memory loads/stores: a real memory round-trip, byte-identical vs the interpreter)\n" as *u8)
38 var pass: i64=0; var total: i64=0
39 let code: *u8=sys_mmap(4096)
40 let opk: *i64=sys_mmap(64*8) as *i64; let rd: *i64=sys_mmap(64*8) as *i64; let rs1: *i64=sys_mmap(64*8) as *i64; let rs2: *i64=sys_mmap(64*8) as *i64; let imm: *i64=sys_mmap(64*8) as *i64
41 let rj: *i64=sys_mmap(32*8) as *i64; let ri: *i64=sys_mmap(32*8) as *i64
42 let mj: *u8=sys_mmap(MEMSZ); let mi: *u8=sys_mmap(MEMSZ)
43
44 // memory round-trip: build scratch ptr 0x80000100 (mask off lui's sign-extension via slli/srli), store 111, load
45 // it back, +1 -> 112, store at +8. (s0=8, t0=5, t1=6)
46 let nc1: i64 = predec(" li t0, 111\n lui s0, 0x80000\n slli s0, s0, 32\n srli s0, s0, 32\n addi s0, s0, 0x100\n sw t0, 0(s0)\n lw t1, 0(s0)\n addi t1, t1, 1\n sw t1, 8(s0)\n" as *u8, code, opk, rd, rs1, rs2, imm)
47 let xj: i64 = run_jit_mem(opk,rd,rs1,rs2,imm,nc1,rj,mj)
48 run_interp_mem(opk,rd,rs1,rs2,imm,nc1,ri,mi)
49 let jm0: i64=fk_ld(mj,0x100,4); let jm1: i64=fk_ld(mj,0x108,4); let im0: i64=fk_ld(mi,0x100,4); let im1: i64=fk_ld(mi,0x108,4)
50 g_puts(" JIT: mem[0x100]="); g_pn(jm0); g_puts(" mem[0x108]="); g_pn(jm1); g_puts(" t1(loaded+1)="); g_pn(rj[6]); g_puts(" xlen="); g_pn(xj); g_puts("\n" as *u8)
51 g_puts(" interp: mem[0x100]="); g_pn(im0); g_puts(" mem[0x108]="); g_pn(im1); g_puts(" t1="); g_pn(ri[6]); g_puts("\n" as *u8)
52 var t1: i64=0; if xj>0 { if jm0==111 { if jm1==112 { if rj[6]==112 { t1=1 } } } }
53 pass=pass+ck("T1: JIT stores 111, loads it back, +1, stores 112 -- the load/store path works" as *u8, t1); total=total+1
54 var t2: i64=0; if jm0==im0 { if jm1==im1 { if rj[6]==ri[6] { t2=1 } } }
55 pass=pass+ck("T2: JIT guest memory + registers are BYTE-IDENTICAL to the interpreter (equivalence with real memory)" as *u8, t2); total=total+1
56
57 // P2: a memory SUM loop -- store an array [10,20,30,40] then sum it via a loop of loads. exercises LW in a loop.
58 let nc2: i64 = predec(" lui s0, 0x80000\n slli s0, s0, 32\n srli s0, s0, 32\n addi s0, s0, 0x200\n li t0, 10\n sw t0, 0(s0)\n li t0, 20\n sw t0, 4(s0)\n li t0, 30\n sw t0, 8(s0)\n li t0, 40\n sw t0, 12(s0)\n li t1, 0\n li t2, 0\n li t3, 4\nsum:\n slli t4, t2, 2\n add t4, t4, s0\n lw t5, 0(t4)\n add t1, t1, t5\n addi t2, t2, 1\n blt t2, t3, sum\n" as *u8, code, opk, rd, rs1, rs2, imm)
59 let xj2: i64 = run_jit_mem(opk,rd,rs1,rs2,imm,nc2,rj,mj)
60 run_interp_mem(opk,rd,rs1,rs2,imm,nc2,ri,mi)
61 g_puts(" P2 array-sum [10,20,30,40]: JIT t1="); g_pn(rj[6]); g_puts(" interp t1="); g_pn(ri[6]); g_puts(" (expect 100) xlen="); g_pn(xj2); g_puts("\n" as *u8)
62 var t3: i64=0; if xj2>0 { if rj[6]==100 { if rj[6]==ri[6] { t3=1 } } }
63 pass=pass+ck("T3: JIT loads-in-a-loop (array sum) == interp == 100 (memory + control flow together)" as *u8, t3); total=total+1
64
65 // teeth: an unsupported load width (LBU) -> fail-loud (only LW/LD/SW/SD compiled; narrower widths stay interpreted).
66 let bopk: *i64=sys_mmap(16) as *i64; let bz: *i64=sys_mmap(16) as *i64; let bimm: *i64=sys_mmap(16) as *i64
67 bopk[0]=FK_LBU; bz[0]=0; bimm[0]=0
68 let bx86: *u8=sys_mmap(256); let bx86off: *i64=sys_mmap(16) as *i64
69 let brc: i64 = jit_compile(bopk, bz, bz, bz, bimm, 1, bx86, bx86off)
70 var t4: i64=0; if brc==(0-1) { t4=1 }
71 g_puts(" teeth: LBU (unsupported width) rc="); g_pn(brc); g_puts(" (-1=fail-loud)\n" as *u8)
72 pass=pass+ck("T4 (teeth): an unsupported load width (LBU) fails-loud (-1) -> stays interpreted, never miscompiled" as *u8, t4); total=total+1
73
74 // T5 (DEBT eaten): the JIT must BOUNDS-CHECK loads/stores exactly like the interpreter (fk_memok) -- never escape
75 // the guest sandbox. Large real buffer (readable) but a small guest bound (4096): an in-bounds store (offset 0x100)
76 // lands; an OOB store (offset 5000) is SKIPPED by BOTH the JIT and the interpreter.
77 let BOUND: i64 = 4096
78 let ncb: i64 = predec(" lui s0, 0x80000\n slli s0, s0, 32\n srli s0, s0, 32\n addi t2, s0, 0x100\n li t0, 111\n sw t0, 0(t2)\n lui t3, 0x80001\n slli t3, t3, 32\n srli t3, t3, 32\n addi t3, t3, 0x388\n li t1, 222\n sw t1, 0(t3)\n" as *u8, code, opk, rd, rs1, rs2, imm)
79 let mjb: *u8=sys_mmap(MEMSZ); let mib: *u8=sys_mmap(MEMSZ)
80 let x86b: *u8=sys_mmap(65536); let x86offb: *i64=sys_mmap(ncb*8+64) as *i64
81 jit_set_membase((mjb as i64) - MEMBASE, BOUND)
82 let xb: i64 = jit_compile(opk, rd, rs1, rs2, imm, ncb, x86b, x86offb)
83 var zb2: i64=0; while zb2<32 { rj[zb2]=0; zb2=zb2+1 }
84 jit_run(x86b, xb, rj)
85 var zb3: i64=0; while zb3<32 { ri[zb3]=0; zb3=zb3+1 }
86 fk_run(opk, rd, rs1, rs2, imm, ncb, ri, mib, BOUND, 200000000, (0 as i64) as *NxVirtioMmio)
87 let j_inb: i64=fk_ld(mjb,0x100,4); let j_oob: i64=fk_ld(mjb,5000,4); let i_inb: i64=fk_ld(mib,0x100,4); let i_oob: i64=fk_ld(mib,5000,4)
88 g_puts(" T5 bounds: JIT in[0x100]="); g_pn(j_inb); g_puts(" OOB[5000]="); g_pn(j_oob); g_puts(" | interp in="); g_pn(i_inb); g_puts(" OOB="); g_pn(i_oob); g_puts(" (OOB must be 0 = skipped, no host escape)\n" as *u8)
89 var t5: i64=0; if xb>0 { if j_inb==111 { if j_oob==0 { if j_inb==i_inb { if j_oob==i_oob { t5=1 } } } } }
90 pass=pass+ck("T5 (DEBT eaten): the JIT BOUNDS-CHECKS -- an OOB store is skipped (no host escape), matching the interpreter" as *u8, t5); total=total+1
91
92 var okall: i64=0; if pass==total { okall=1 }
93 g_puts("---- nx_rv64_jit_mem_gate: passed "); g_pn(pass); g_puts(" / "); g_pn(total); g_puts(" ----\n" as *u8)
94 if okall==1 {
95 let logf: i64=sys_openat_append("knowledge/status/rv64_jit_mem.log" as *u8, 420)
96 if logf>=0 { let z2: i64=sys_write(logf,"NXRV64JITMEM GREEN: JIT compiles guest memory LW/LD/SW/SD (biased base [rsi+rax]) + SRLI/SRAI -- real memory round-trip + array-sum-loop byte-identical to the interpreter; the capability to run real code (arrays/structs)\n" as *u8,214); sys_close(logf) }
97 g_puts("verdict=GREEN (the JIT now compiles guest memory loads/stores -- real round-trip + array-sum-in-a-loop byte-identical to the interpreter; the JIT can run real memory-touching code now)\n" as *u8); sys_exit(0); return 0
98 }
99 g_puts("verdict=RED\n" as *u8); sys_exit(1); return 1
100}