code wiki / _hdl_build / nx_js_jitgc_gate.nx

nx_js_jitgc_gate.nx source

↩ module page · 49 lines · 3629 B

1// nx_js_jitgc_gate.nx -- prove JIT + GC COEXISTENCE: the P5 native JIT runs WITH GC enabled, a collection can 2// fire from JIT'd code at a loop back-edge safepoint, and every JS value stays traceable (on the vs-stack; 3// vs[VS_SP]/vs[VS_ENV] synced at calls + safepoints). Each test asserts JIT=1 (it really tiered to native) AND 4// the result is exact under an aggressive 64KB GC threshold. expect_exit: 0 license_tier: ORIGINAL 5// helpers <=2 data args + `g_want` global (nx_cc 3-arg-helper miscompile, see reference-nishilang-nx-cc-gotchas). 6import "nx_js_vm.nx" 7func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 8func nn(v: i64) -> i64 { if v==0 { sys_write(1,"0" as *u8,1); return 0 } var m: i64=v; if m<0 { sys_write(1,"-" as *u8,1); m=0-m } let t: *u8=sys_mmap(24); var k: i64=0; while m>0 { t[k]=(48+(m%10)) as u8; m=m/10; k=k+1 } let o: *u8=sys_mmap(24); var q: i64=k-1; var x: i64=0; while q>=0 { o[x]=t[q]; x=x+1; q=q-1 } sys_write(1,o,x); return 0 } 9static g_want: i64 10func tckj(label: *u8, src: *u8) -> i64 { // PASS iff rc==0 AND result==g_want AND it JIT'd (native) 11 let ov: *i64 = sys_mmap(16) as *i64 12 let c0: i64 = gc_collection_count() 13 let rc: i64 = compile_run(src, ov) 14 let jit: i64 = jit_ran_get() 15 w(" " as *u8); w(label); w(": rc=" as *u8); nn(rc); w(" got=" as *u8); nn(ov[1]); w(" want=" as *u8); nn(g_want); w(" JIT=" as *u8); nn(jit); w(" +GCs=" as *u8); nn(gc_collection_count() - c0) 16 if rc == 0 { if ov[1] == g_want { if jit == 1 { w(" ok\n" as *u8); return 1 } w(" FAIL(interpreted-not-JIT)\n" as *u8); return 0 } } 17 w(" FAIL\n" as *u8) 18 return 0 19} 20func main(argc: i64, argv: *i64) -> i64 { 21 w("=== nx_js_jitgc_gate: JIT + GC coexistence (loop back-edge safepoints) ===\n" as *u8) 22 gc_configure(65536) 23 gc_enable(1) 24 var pass: i64 = 0 25 var tot: i64 = 0 26 let wv: *i64 = sys_mmap(16) as *i64 27 compile_run("var z=1;z" as *u8, wv) // warm up 28 29 // (1) HOT ARITHMETIC loop, no allocation: 1,000,000 iterations. JITs; the back-edge safepoint runs every 30 // iteration (gc_poll returns 0, no GC) -- proves the safepoint doesn't corrupt a hot JIT loop. sum 0..999999. 31 g_want = 499999500000 32 tot=tot+1; let r1: i64 = tckj("jit-arith-1M " as *u8, "var s=0;var i=0;while(i<1000000){s=s+i;i=i+1;} s" as *u8); pass = pass + r1 33 34 // (2) OBJECT-ALLOC RECLAIM: `new C()` each iteration = a throwaway object (garbage). JITs (BC_NEW/CLOSURE/CALL 35 // templatable); the back-edge safepoint FIRES GC mid-loop and must reclaim the garbage while the JIT frame 36 // is live -> resident stays bounded, result exact. 50000 objects. 37 g_want = 50000 38 tot=tot+1; let r2: i64 = tckj("jit-gc-newobj " as *u8, "function C(){} var t=0;var i=0;while(i<50000){var o=new C();t=t+1;i=i+1;} t" as *u8); pass = pass + r2 39 40 // (3) STRING-CONS RETAIN: `s=s+"q"` builds a 20000-deep cons-rope tree, ALL live via s. JITs; GC fires mid-loop 41 // from JIT'd code and must NOT free any live cons node (a missed root => wrong length or crash). length=20000. 42 g_want = 20000 43 tot=tot+1; let r3: i64 = tckj("jit-gc-cons-retain " as *u8, "var s=\x22\x22;var i=0;while(i<20000){s=s+\x22q\x22;i=i+1;} s.length" as *u8); pass = pass + r3 44 45 w(" --- " as *u8); nn(pass); w("/" as *u8); nn(tot); w(" --- total collections: " as *u8); nn(gc_collection_count()); w("\n" as *u8) 46 if pass == tot { w("=== GREEN: JIT runs WITH GC; back-edge safepoints collect mid-loop; roots traceable; exact ===\n" as *u8); return 0 } 47 w("=== RED ===\n" as *u8) 48 return 1 49}