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}