code wiki / _hdl_build / nx_js_gc_gate.nx
nx_js_gc_gate.nx source
↩ module page · 78 lines · 6241 B
1// nx_js_gc_gate.nx -- prove the non-moving mark-sweep collector (conservative word-scan made EXACT by the
2// block-start bitmap). Under a TINY 64KB threshold (a collection fires ~every 500 objects):
3// (A) RECLAIM -- 100k garbage objects leave resident memory BOUNDED (nx_pool_hp plateaus; collections fire).
4// (B) RETAIN -- large structures kept live across many collections SURVIVE intact (missing root=crash/wrong).
5// (C) EXACT -- every result stays V8-exact under aggressive GC, incl the 6 self-validating Octane benches.
6// Any missing root manifests as a use-after-free the self-checking programs catch. expect_exit: 0 license_tier: ORIGINAL
7// NOTE: helpers take <=2 data args -- nx_cc miscompiled a 3-arg helper call here (crash before/at the callee),
8// so `want` is threaded through a module global instead of a 3rd parameter.
9import "nx_js_vm.nx"
10func w(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
11func 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 }
12static g_want: i64
13func tck(label: *u8, src: *u8) -> i64 { // check a snippet against the pre-set g_want
14 let ov: *i64 = sys_mmap(16) as *i64
15 let rc: i64 = compile_run(src, ov)
16 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)
17 if rc == 0 { if ov[1] == g_want { w(" ok\n" as *u8); return 1 } }
18 w(" FAIL\n" as *u8)
19 return 0
20}
21func run_octane(path: *u8, label: *u8) -> i64 {
22 let fd: i64 = sys_openat_rd(path)
23 if fd < 0 { w(" " as *u8); w(label); w(": (skip - no file)\n" as *u8); return 1 }
24 let cap: i64 = 262144
25 let buf: *u8 = sys_mmap(cap + 8)
26 var total: i64 = 0
27 var done: i64 = 0
28 while done == 0 { let n: i64 = sys_read(fd, ((buf as i64) + total) as *u8, cap - total); if n <= 0 { done = 1 } else { total = total + n } }
29 sys_close(fd)
30 buf[total] = 0 as u8
31 let c0: i64 = gc_collection_count()
32 let out: *i64 = sys_mmap(16) as *i64
33 let rc: i64 = compile_run(buf, out)
34 w(" " as *u8); w(label); w(": rc=" as *u8); nn(rc); w(" (+" as *u8); nn(gc_collection_count() - c0); w(" GCs)" as *u8)
35 if rc == 0 { w(" ok\n" as *u8); return 1 } else { w(" FAIL\n" as *u8); return 0 }
36}
37func main(argc: i64, argv: *i64) -> i64 {
38 w("=== nx_js_gc_gate: non-moving mark-sweep, aggressive 64KB threshold ===\n" as *u8)
39 gc_configure(65536) // collect every ~64KB bump-allocated (== ~500 small objects)
40 gc_enable(1)
41 var pass: i64 = 0
42 var tot: i64 = 0
43 let wv: *i64 = sys_mmap(16) as *i64
44 compile_run("var z={a:1}; z.a" as *u8, wv) // warm up persistent prototypes once
45
46 // (A) RECLAIM: 100k throwaway 2-prop objects, retain only the running sum = 100000^2 = 10000000000.
47 w("--- (A) reclaim: 100k garbage objects, resident must stay bounded ---\n" as *u8)
48 let hp0: i64 = nx_pool_hp_bytes()
49 let gc0: i64 = gc_collection_count()
50 g_want = 100000 * 100000
51 tot=tot+1; let ra: i64 = tck("obj-churn-sum" as *u8, "var s=0;var i=0;while(i<100000){var o={a:i,b:i+1};s=s+o.a+o.b;i=i+1;} s" as *u8); pass = pass + ra
52 let dhp: i64 = nx_pool_hp_bytes() - hp0
53 let dgc: i64 = gc_collection_count() - gc0
54 w(" churn: +" as *u8); nn(dgc); w(" collections, resident delta +" as *u8); nn(dhp / 1024); w("KB\n" as *u8)
55 tot=tot+1; if dgc > 0 { w(" collections-fired: ok\n" as *u8); pass=pass+1 } else { w(" collections-fired: FAIL\n" as *u8) }
56 tot=tot+1; if dhp < 4194304 { w(" resident-bounded(<4MB): ok\n" as *u8); pass=pass+1 } else { w(" resident-bounded: FAIL\n" as *u8) }
57
58 // (B) RETAIN: structures kept fully reachable across many collections must survive intact.
59 w("--- (B) retain: live structures must survive collections ---\n" as *u8)
60 g_want = 12497500; tot=tot+1; let rb1: i64 = tck("list-retain-walk" as *u8, "var head=null;var i=0;while(i<5000){head={v:i,next:head};i=i+1;} var s=0;var p=head;while(p!=null){s=s+p.v;p=p.next;} s" as *u8); pass = pass + rb1
61 g_want = 3998000; tot=tot+1; let rb2: i64 = tck("array-retain-sum" as *u8, "var a=[];var i=0;while(i<2000){a[i]=i*2;i=i+1;} var s=0;var j=0;while(j<2000){s=s+a[j];j=j+1;} s" as *u8); pass = pass + rb2
62 g_want = 500; tot=tot+1; let rb3: i64 = tck("string-cons-churn" as *u8, "var r=\x22\x22;var i=0;while(i<500){r=r+\x22x\x22;i=i+1;} r.length" as *u8); pass = pass + rb3
63 g_want = 4501500; tot=tot+1; let rb4: i64 = tck("nested-obj-retain" as *u8, "var root={n:0,c:null};var cur=root;var i=0;while(i<3000){cur.c={n:i+1,c:null};cur=cur.c;i=i+1;} var s=0;var p=root;while(p!=null){s=s+p.n;p=p.c;} s" as *u8); pass = pass + rb4
64
65 // (C) EXACT: the 6 real Octane benchmarks under aggressive GC (each validates its own output internally).
66 w("--- (C) exact: 6 Octane benchmarks under aggressive GC (self-validating) ---\n" as *u8)
67 tot=tot+1; let rc1: i64 = run_octane("knowledge/octane/richards_eng.js\x00" as *u8, "Richards " as *u8); pass = pass + rc1
68 tot=tot+1; let rc2: i64 = run_octane("knowledge/octane/splay_eng.js\x00" as *u8, "Splay " as *u8); pass = pass + rc2
69 tot=tot+1; let rc3: i64 = run_octane("knowledge/octane/navier_eng.js\x00" as *u8, "NavierStokes" as *u8); pass = pass + rc3
70 tot=tot+1; let rc4: i64 = run_octane("knowledge/octane/crypto_eng.js\x00" as *u8, "Crypto " as *u8); pass = pass + rc4
71 tot=tot+1; let rc5: i64 = run_octane("knowledge/octane/deltablue_eng.js\x00" as *u8, "DeltaBlue " as *u8); pass = pass + rc5
72 tot=tot+1; let rc6: i64 = run_octane("knowledge/octane/raytrace_eng.js\x00" as *u8, "RayTrace " as *u8); pass = pass + rc6
73
74 w(" --- " as *u8); nn(pass); w("/" as *u8); nn(tot); w(" --- total collections this run: " as *u8); nn(gc_collection_count()); w("\n" as *u8)
75 if pass == tot { w("=== GREEN: mark-sweep reclaims + retains + stays V8-exact under aggressive GC ===\n" as *u8); return 0 }
76 w("=== RED ===\n" as *u8)
77 return 1
78}