code wiki / _hdl_build / nx_js_jit_gate.nx
nx_js_jit_gate.nx source
↩ module page · 241 lines · 12178 B
1// nx_js_jit_gate.nx -- P5 BASELINE JIT: prove it (A) is CORRECT vs the VM and the tree-walker on the
2// templatable subset, (B) DECLINES cleanly (jit_compile==0 -> VM) on everything outside the subset,
3// and (C) MEASURES the native speedup + class band on the SAME entry point (js_jit_disable A/B).
4// The 108/108 parity gate already proves JIT==tree end-to-end; this adds the three-way A/B/tree
5// agreement, the decline contract, and the ladder placement. expect_exit: 0 license_tier: ORIGINAL
6import "nx_js_vm.nx"
7
8// three-way: tree-walker vs JIT-on vs VM-forced must ALL agree (value + rc) on `src`.
9func jchk(src: *u8, label: *u8, pp: *i64, tp: *i64) -> i64 {
10 tp[0] = tp[0] + 1
11 let tw: *i64 = sys_mmap(16) as *i64
12 let trc: i64 = js_run_source(src, bsl(src), tw)
13 js_jit_disable(0)
14 let jv: *i64 = sys_mmap(16) as *i64
15 let jrc: i64 = compile_run(src, jv)
16 js_jit_disable(1)
17 let vv: *i64 = sys_mmap(16) as *i64
18 let vrc: i64 = compile_run(src, vv)
19 js_jit_disable(0)
20 bw(" ")
21 bw(label)
22 bw(": ")
23 var ok: i64 = 1
24 if trc != jrc { ok = 0 }
25 if trc != vrc { ok = 0 }
26 if trc == 0 { if cells_eq(tw, jv) == 0 { ok = 0 } }
27 if trc == 0 { if cells_eq(tw, vv) == 0 { ok = 0 } }
28 if ok == 1 { pp[0] = pp[0] + 1; bw("ok (tree=JIT=VM)\n" as *u8); return 0 }
29 bw("FAIL tree(rc=")
30 bn(trc)
31 bw(",tag=")
32 bn(tw[0])
33 bw(",pay=")
34 bn(tw[1])
35 bw(") JIT(rc=")
36 bn(jrc)
37 bw(",tag=")
38 bn(jv[0])
39 bw(",pay=")
40 bn(jv[1])
41 bw(") VM(rc=")
42 bn(vrc)
43 bw(",tag=")
44 bn(vv[0])
45 bw(",pay=")
46 bn(vv[1])
47 bw(")\n" as *u8)
48 return 0
49}
50// decline contract: `src` MUST or MUST NOT be JIT-compiled (want=1 templatable, 0 declines to VM).
51func jdec(src: *u8, want: i64, label: *u8, pp: *i64, tp: *i64) -> i64 {
52 tp[0] = tp[0] + 1
53 let got: i64 = js_jit_probe(src)
54 bw(" ")
55 bw(label)
56 bw(": jit=")
57 bn(got)
58 bw(" want=")
59 bn(want)
60 if got == want { pp[0] = pp[0] + 1; bw(" ok\n" as *u8); return 0 }
61 bw(" FAIL\n" as *u8)
62 return 0
63}
64// best-of-3 microseconds for `src` at a JIT setting (0=JIT on, 1=forced VM); records result in `outcell`.
65// min-of-N cancels WSL load spikes -- single-sample A/B margins flake and the pass asserts should not.
66func jt3(src: *u8, disabled: i64, outcell: *i64) -> i64 {
67 js_jit_disable(disabled)
68 var best: i64 = 0
69 var r: i64 = 0
70 while r < 3 {
71 let t0: i64 = sys_now_us()
72 compile_run(src, outcell)
73 let t1: i64 = sys_now_us()
74 var us: i64 = t1 - t0
75 if us < 1 { us = 1 }
76 if r == 0 { best = us }
77 if us < best { best = us }
78 r = r + 1
79 }
80 js_jit_disable(0)
81 return best
82}
83
84func main(argc: i64, argv: *i64) -> i64 {
85 bw("=== nx_js_jit_gate: P5 baseline JIT -- correct + declines + measured ===\n" as *u8)
86 let pp: *i64 = sys_mmap(8) as *i64
87 let tp: *i64 = sys_mmap(8) as *i64
88 bw(" [A: tree == JIT == VM on the templatable subset]\n" as *u8)
89 jchk("1+2*3" as *u8, "J1 int precedence" as *u8, pp, tp)
90 jchk("var s=0;var i=0;while(i<1000){s=s+i;i++;}s" as *u8, "J2 counted loop 1000" as *u8, pp, tp)
91 jchk("var n=6;var f=1;var i=1;while(i<=n){f=f*i;i=i+1;}f" as *u8, "J3 factorial(6)" as *u8, pp, tp)
92 jchk("var x=7;if(x>5){x=1;}else{x=2;}x" as *u8, "J4 if/else" as *u8, pp, tp)
93 jchk("0-5+3" as *u8, "J5 neg arith" as *u8, pp, tp)
94 jchk("var n=4;-n" as *u8, "J6 unary neg" as *u8, pp, tp)
95 jchk("var i=5;i++;i" as *u8, "J7 i++" as *u8, pp, tp)
96 jchk("var i=5;var j=i++;j*10+i" as *u8, "J8 postfix OLD" as *u8, pp, tp)
97 jchk("var x=5;x+=3;x" as *u8, "J9 compound += (SWAP)" as *u8, pp, tp)
98 jchk("var a=1;var b=5;a&&b" as *u8, "J10 && (DUP/JMPF)" as *u8, pp, tp)
99 jchk("var a=0;var b=5;a||b" as *u8, "J11 || (JMPT)" as *u8, pp, tp)
100 jchk("var i=0;var c=0;while(i<10){if(i>2){if(i<7){c=c+1;}}i=i+1;}c" as *u8, "J12 nested-if loop" as *u8, pp, tp)
101 jchk("var x=5;x>3?100:200" as *u8, "J13 ternary" as *u8, pp, tp)
102 jchk("!0" as *u8, "J14 unary not" as *u8, pp, tp)
103 jchk("var s=0;var i=1;while(i<20){s=s+7/2;i++;}s" as *u8, "J15 float in loop (7/2=3.5, tree=JIT=VM)" as *u8, pp, tp)
104 jchk("5+zzz" as *u8, "J16 ReferenceError parity" as *u8, pp, tp)
105 jchk("var r='';var i=0;while(i<5){r=r+'x';i++;}r" as *u8, "J17 string concat (slow BINOP path)" as *u8, pp, tp)
106 jchk("var s=0;var i=0;while(i<100){s=s+i*2-1;i++;}s" as *u8, "J18 mixed arith loop" as *u8, pp, tp)
107 // objects/arrays now JIT (inline shape-guarded GETPROP + call-threaded set/idx/literal); must match
108 jchk("var o={a:1,b:2,c:3};o.a+o.b+o.c" as *u8, "J19 object member reads" as *u8, pp, tp)
109 jchk("var o={a:0,b:7};var i=0;while(i<1000){o.a=o.a+o.b;i++;}o.a" as *u8, "J20 property churn loop (inline IC)" as *u8, pp, tp)
110 jchk("var a=[];var i=0;while(i<50){a[i]={n:i,m:i*2};i++;}var s=0;var j=0;while(j<50){s=s+a[j].m;j++;}s" as *u8, "J21 array of objects (many shapes-hit)" as *u8, pp, tp)
111 jchk("var o={a:1};o.b=2;o.a+o.b" as *u8, "J22 incremental prop (shape transition)" as *u8, pp, tp)
112 jchk("var a=[3,1,4];a[1]=9;a[0]+a[1]+a[2]" as *u8, "J23 array index get+set" as *u8, pp, tp)
113 jchk("var o={x:5};o.nope" as *u8, "J24 absent prop -> undefined (shape miss slow path)" as *u8, pp, tp)
114 jchk("var o={n:2};o.n+=5;o.n" as *u8, "J25 compound member (get+set)" as *u8, pp, tp)
115 // functions now run NATIVE (frame setup + hardware call/ret + closures + recursion); must match
116 jchk("function add(a,b){return a+b;}add(3,4)" as *u8, "J26 fn decl+call (native)" as *u8, pp, tp)
117 jchk("function fib(n){if(n<2){return n;}return fib(n-1)+fib(n-2);}fib(12)" as *u8, "J27 recursion fib(12)" as *u8, pp, tp)
118 jchk("function mk(){var c=0;return function(){c=c+1;return c;};}var f=mk();f();f();f()" as *u8, "J28 closure counter (captured env)" as *u8, pp, tp)
119 jchk("function mkAdd(x){return function(y){return x+y;};}var a5=mkAdd(5);a5(7)" as *u8, "J29 closure adder" as *u8, pp, tp)
120 jchk("function f(a,b=10){return a+b;}f(5)" as *u8, "J30 param default (native)" as *u8, pp, tp)
121 jchk("function f(a,...r){return r.length+a;}f(1,2,3,4)" as *u8, "J31 rest param (native)" as *u8, pp, tp)
122 jchk("(function(){return 5;})()" as *u8, "J32 IIFE" as *u8, pp, tp)
123 jchk("function g(){var o={a:1,b:2};return o.a+o.b;}g()+g()" as *u8, "J33 object inside fn body" as *u8, pp, tp)
124 jchk("function bad(){return zzzundeclared;}var r=0;bad()" as *u8, "J34 error inside call -> rc1 (longjmp bail)" as *u8, pp, tp)
125 // method calls, new, and callbacks now JIT too (native method-resolution + this-binding; callbacks re-enter)
126 jchk("var o={v:6,f:function(){return this.v;}};o.f()" as *u8, "J35 method call (this-binding)" as *u8, pp, tp)
127 jchk("[1,2,3].map(function(x){return x*2;}).join('-')" as *u8, "J36 forEach/map callback (CALLM)" as *u8, pp, tp)
128 jchk("[1,2,3,4].reduce(function(a,b){return a+b;},0)" as *u8, "J37 reduce callback" as *u8, pp, tp)
129 jchk("function P(n){this.n=n;this.m=n*2;}var p=new P(4);p.n+p.m" as *u8, "J38 new user ctor (this)" as *u8, pp, tp)
130 jchk("Math.max(3,7,5)+Math.min(2,9)" as *u8, "J39 globalns method (Math)" as *u8, pp, tp)
131 jchk("var a=[3,1];a.push(9);a.length" as *u8, "J40 array method (push)" as *u8, pp, tp)
132 bw(" [B: decline contract -- only TRY/THROW decline now; functions/methods/new/callbacks all JIT]\n" as *u8)
133 jdec("var s=0;var i=0;while(i<10){s=s+i;i++;}s" as *u8, 1, "D1 pure loop -> JIT" as *u8, pp, tp)
134 jdec("var o={a:1};o.a" as *u8, 1, "D2 object member -> JIT" as *u8, pp, tp)
135 jdec("function fib(n){if(n<2){return n;}return fib(n-1)+fib(n-2);}fib(5)" as *u8, 1, "D3 recursion -> JIT" as *u8, pp, tp)
136 jdec("var o={f:function(){return 1;}};o.f()" as *u8, 1, "D4 method call -> JIT (native)" as *u8, pp, tp)
137 jdec("[1,2,3].forEach(function(x){});1" as *u8, 1, "D5 callback (CALLM) -> JIT" as *u8, pp, tp)
138 jdec("function P(n){this.n=n;}var p=new P(4);p.n" as *u8, 1, "D6 new (BC_NEW) -> JIT" as *u8, pp, tp)
139 jdec("Math.max(1,2,3)" as *u8, 1, "D7 Math.max (CALLM globalns) -> JIT" as *u8, pp, tp)
140 jdec("try{throw 1;}catch(e){}1" as *u8, 0, "D8 try/catch -> decline" as *u8, pp, tp)
141 jdec("try{var x=1;}finally{var y=2;}x" as *u8, 0, "D9 try/finally -> decline" as *u8, pp, tp)
142 bw(" --- correctness+decline: " as *u8)
143 bn(pp[0])
144 bw("/" as *u8)
145 bn(tp[0])
146 bw(" ---\n" as *u8)
147
148 // [C] measurement: SAME entry (compile_run), JIT vs forced-VM, on the K1 dispatch kernel.
149 bw(" [C: native speedup + class band on the K1 dispatch kernel (200k int loop)]\n" as *u8)
150 let ksrc: *u8 = "var s=0;var i=0;while(i<200000){s=s+i;i++;}s" as *u8
151 let jo: *i64 = sys_mmap(16) as *i64
152 let vo: *i64 = sys_mmap(16) as *i64
153 let jus: i64 = jt3(ksrc, 0, jo)
154 let vus: i64 = jt3(ksrc, 1, vo)
155 let jrate: i64 = 200000 * 1000000 / jus
156 let vrate: i64 = 200000 * 1000000 / vus
157 var okc: i64 = 0
158 if cells_eq(jo, vo) == 1 { okc = 1 }
159 bw(" VM : ")
160 bn(vrate)
161 bw(" iters/s (")
162 bn(vus)
163 bw("us)\n" as *u8)
164 bw(" JIT : ")
165 bn(jrate)
166 bw(" iters/s (")
167 bn(jus)
168 bw("us) speedup=")
169 bn(jrate / vrate)
170 bw("x\n" as *u8)
171 bw(" band : JIT lands in ")
172 if jrate >= 500000000 { bw("optimizing-JIT-class (>=500M iters/s)" as *u8) }
173 if jrate >= 100000000 { if jrate < 500000000 { bw("baseline-JIT-class (>=100M iters/s)" as *u8) } }
174 if jrate < 100000000 { bw("interpreter-class (<100M -- box under load; retry on a fresh VM)" as *u8) }
175 bw("\n" as *u8)
176
177 // [D] object-heavy kernel (property churn): inline shape-guarded GETPROP runs native; call-threaded
178 // SETPROP. JIT vs forced-VM A/B on the SAME entry.
179 bw(" [D: property-churn kernel (100k iters, o.a=o.a+o.b -- inline shape-IC GETPROP)]\n" as *u8)
180 let psrc: *u8 = "var o={a:0,b:7};var i=0;while(i<100000){o.a=o.a+o.b;i++;}o.a" as *u8
181 let pjo: *i64 = sys_mmap(16) as *i64
182 let pvo: *i64 = sys_mmap(16) as *i64
183 let pjus: i64 = jt3(psrc, 0, pjo)
184 let pvus: i64 = jt3(psrc, 1, pvo)
185 var okp: i64 = 0
186 if cells_eq(pjo, pvo) == 1 { okp = 1 }
187 bw(" VM : ")
188 bn(pvus)
189 bw("us JIT: ")
190 bn(pjus)
191 bw("us speedup=")
192 bn(pvus * 100 / pjus)
193 bw("/100x (result match=")
194 bn(okp)
195 bw(")\n" as *u8)
196
197 // [E] call-heavy kernel (recursion): native frames + hardware call/ret. JIT vs forced-VM A/B.
198 bw(" [E: recursion kernel fib(28) -- native call frames + hardware call/ret]\n" as *u8)
199 let fsrc: *u8 = "function fib(n){if(n<2){return n;}return fib(n-1)+fib(n-2);}fib(28)" as *u8
200 let fjo: *i64 = sys_mmap(16) as *i64
201 let fvo: *i64 = sys_mmap(16) as *i64
202 let fjus: i64 = jt3(fsrc, 0, fjo)
203 let fvus: i64 = jt3(fsrc, 1, fvo)
204 var okf: i64 = 0
205 if cells_eq(fjo, fvo) == 1 { okf = 1 }
206 bw(" VM : ")
207 bn(fvus)
208 bw("us JIT: ")
209 bn(fjus)
210 bw("us speedup=")
211 bn(fvus * 100 / fjus)
212 bw("/100x (result match=")
213 bn(okf)
214 bw(")\n" as *u8)
215
216 // [F] method-call kernel: hot loop calling a user method with this-binding (native CALLM + frame).
217 bw(" [F: method-call kernel (100k o.inc() -- native CALLM + this-binding + native frame)]\n" as *u8)
218 let msrc: *u8 = "var o={v:0,inc:function(){this.v=this.v+1;return this.v;}};var s=0;var i=0;while(i<100000){s=s+o.inc();i++;}s" as *u8
219 let mjo: *i64 = sys_mmap(16) as *i64
220 let mvo: *i64 = sys_mmap(16) as *i64
221 let mjus: i64 = jt3(msrc, 0, mjo)
222 let mvus: i64 = jt3(msrc, 1, mvo)
223 var okm: i64 = 0
224 if cells_eq(mjo, mvo) == 1 { okm = 1 }
225 bw(" VM : ")
226 bn(mvus)
227 bw("us JIT: ")
228 bn(mjus)
229 bw("us speedup=")
230 bn(mvus * 100 / mjus)
231 bw("/100x (result match=")
232 bn(okm)
233 bw(")\n" as *u8)
234
235 if pp[0] == tp[0] { if okc == 1 { if okp == 1 { if okf == 1 { if okm == 1 { if jrate > vrate { if pjus < pvus { if fjus < fvus { if mjus < mvus {
236 bw("=== GREEN: JIT CORRECT (tree=JIT=VM) incl OBJECTS + FUNCTIONS/METHODS/NEW/RECURSION/CLOSURES/CALLBACKS; declines only TRY; native-fast on ints, property churn, calls, AND methods ===\n" as *u8)
237 return 0
238 } } } } } } } } }
239 bw("=== RED ===\n" as *u8)
240 return 1
241}