jz 0.8.0 → 0.9.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/README.md +56 -9
  2. package/bench/README.md +121 -50
  3. package/bench/bench.svg +27 -27
  4. package/cli.js +3 -1
  5. package/dist/interop.js +1 -1
  6. package/dist/jz.js +7541 -6178
  7. package/index.js +165 -74
  8. package/interop.js +189 -17
  9. package/jzify/arguments.js +32 -1
  10. package/jzify/async.js +409 -0
  11. package/jzify/classes.js +136 -18
  12. package/jzify/generators.js +639 -0
  13. package/jzify/hoist-vars.js +73 -1
  14. package/jzify/index.js +123 -4
  15. package/jzify/names.js +1 -0
  16. package/jzify/transform.js +239 -1
  17. package/layout.js +48 -3
  18. package/module/array.js +318 -43
  19. package/module/atomics.js +144 -0
  20. package/module/collection.js +1429 -155
  21. package/module/core.js +431 -40
  22. package/module/date.js +142 -120
  23. package/module/fs.js +144 -0
  24. package/module/function.js +6 -3
  25. package/module/index.js +4 -1
  26. package/module/json.js +270 -49
  27. package/module/math.js +1032 -145
  28. package/module/number.js +532 -163
  29. package/module/object.js +353 -95
  30. package/module/regex.js +157 -7
  31. package/module/schema.js +100 -2
  32. package/module/string.js +428 -93
  33. package/module/typedarray.js +264 -72
  34. package/module/web.js +36 -0
  35. package/package.json +5 -5
  36. package/src/abi/string.js +82 -11
  37. package/src/ast.js +11 -5
  38. package/src/autoload.js +28 -1
  39. package/src/bridge.js +7 -2
  40. package/src/compile/analyze-scans.js +22 -7
  41. package/src/compile/analyze.js +136 -30
  42. package/src/compile/dyn-closure-tables.js +277 -0
  43. package/src/compile/emit-assign.js +281 -15
  44. package/src/compile/emit.js +1055 -70
  45. package/src/compile/index.js +277 -29
  46. package/src/compile/infer.js +42 -4
  47. package/src/compile/inplace-store.js +329 -0
  48. package/src/compile/loop-recurrence.js +167 -0
  49. package/src/compile/narrow.js +555 -18
  50. package/src/compile/peel-stencil.js +5 -0
  51. package/src/compile/plan/index.js +45 -3
  52. package/src/compile/plan/inline.js +8 -1
  53. package/src/compile/plan/literals.js +39 -0
  54. package/src/compile/plan/scope.js +430 -23
  55. package/src/compile/program-facts.js +732 -38
  56. package/src/ctx.js +64 -9
  57. package/src/helper-counters.js +7 -1
  58. package/src/ir.js +113 -5
  59. package/src/kind-traits.js +66 -3
  60. package/src/kind.js +84 -12
  61. package/src/op-policy.js +7 -4
  62. package/src/optimize/index.js +1179 -704
  63. package/src/optimize/recurse.js +2 -2
  64. package/src/optimize/vectorize.js +982 -67
  65. package/src/prepare/index.js +809 -67
  66. package/src/prepare/math-kernel.js +331 -0
  67. package/src/prepare/pre-eval.js +714 -0
  68. package/src/snapshot.js +194 -0
  69. package/src/type.js +1170 -56
  70. package/src/wat/assemble.js +403 -65
  71. package/src/wat/codegen.js +74 -14
  72. package/transform.js +113 -4
  73. package/wasi.js +3 -0
@@ -0,0 +1,331 @@
1
+ /**
2
+ * Host-side JS mirrors of module/math.js's WAT transcendentals, op-for-op, so
3
+ * `preEval` (pre-eval.js) can fold `Math.sin(1.5)` etc. at COMPILE time to the
4
+ * exact bits the RUNTIME wasm would compute — bit-exact vs jz's own kernel,
5
+ * deliberately NOT vs host `Math.sin`/`Math.cos`/… (whose libm differs in the
6
+ * last ulp from jz's minimax/Newton approximations by design; see module/math.js
7
+ * header comments on each algorithm).
8
+ *
9
+ * Every function below transliterates its `wat('math.X', ...)` twin literally:
10
+ * same operand order, same parenthesization (float ops are NOT associative —
11
+ * reordering would silently change the fold). f64 arithmetic in JS (+,-,*,/,
12
+ * Math.sqrt/abs) is IEEE754 binary64 exactly like wasm's f64 ops, so the port
13
+ * is bit-identical wherever the JS expression shape matches the WAT shape.
14
+ *
15
+ * A handful of ops are genuinely host-exact already (no algorithmic mirror
16
+ * needed) because either jz's WAT wraps the SAME host-computed constant
17
+ * (Math.PI et al — emitted via `f64.const ${Math.PI}` using the compiler's own
18
+ * host Math), or the op is IEEE754-mandated correctly-rounded in both JS and
19
+ * wasm (sqrt/abs/floor/ceil/trunc), or jz's WAT was deliberately engineered to
20
+ * reproduce host JS Math semantics exactly (round, sign, imul, clz32, fround,
21
+ * min/max — see module/math.js comments on each). Those are folded directly
22
+ * via host Math in pre-eval.js's MATH_HOST_EXACT table; this module only ports
23
+ * the ones with a genuinely bespoke algorithm.
24
+ *
25
+ * @module prepare/math-kernel
26
+ */
27
+
28
+ // ---- bit-level helpers (i64.reinterpret_f64 / f64.reinterpret_i64) ----
29
+ const _buf = new ArrayBuffer(8)
30
+ const _dv = new DataView(_buf)
31
+ /** f64 → its IEEE754 bit pattern, as an unsigned 64-bit BigInt (big-endian: bit 63 = sign). */
32
+ function f64Bits(x) { _dv.setFloat64(0, x, false); return _dv.getBigUint64(0, false) }
33
+ /** Unsigned 64-bit BigInt bit pattern → f64. */
34
+ function bitsF64(bits) { _dv.setBigUint64(0, BigInt.asUintN(64, bits), false); return _dv.getFloat64(0, false) }
35
+
36
+ /** `f64.copysign`: magnitude of `mag`, sign of `sign` (handles ±0 correctly). */
37
+ function copysign(mag, sign) {
38
+ const sNeg = sign < 0 || Object.is(sign, -0)
39
+ const mNeg = mag < 0 || Object.is(mag, -0)
40
+ return sNeg === mNeg ? mag : -mag
41
+ }
42
+
43
+ /** `f64.nearest`: round-to-nearest, ties-to-even (NOT JS `Math.round`, which
44
+ * ties away from zero toward +Infinity). Preserves sign of a zero result. */
45
+ function nearest(x) {
46
+ if (!Number.isFinite(x) || x === 0) return x
47
+ const floor = Math.floor(x)
48
+ const diff = x - floor
49
+ let r
50
+ if (diff < 0.5) r = floor
51
+ else if (diff > 0.5) r = floor + 1
52
+ else r = (floor % 2 === 0) ? floor : floor + 1
53
+ return r === 0 ? copysign(0, x) : r
54
+ }
55
+
56
+ /** Horner evaluation matching module/math.js's `horner()` builder: for
57
+ * cs = [c0, c1, ..., cN], returns c0 + v*(c1 + v*(c2 + ... + v*cN)). */
58
+ function horner(cs, v) {
59
+ let acc = cs[cs.length - 1]
60
+ for (let i = cs.length - 2; i >= 0; i--) acc = cs[i] + v * acc
61
+ return acc
62
+ }
63
+
64
+ // Range-reduction constants — the EXACT decimal strings module/math.js embeds
65
+ // (native toString's shortest round-trip repr of Math.PI etc.); JS numeric
66
+ // literal parsing is correctly-rounded like WAT float parsing, so the same
67
+ // string reproduces the identical f64 bits in both legs.
68
+ const PI = 3.141592653589793, INV_PI = 0.3183098861837907, HALF_PI = 1.5707963267948966
69
+ const SIN_C = [1, -0.16666660296130772, 0.008333091744946387, -0.00019811771757028443, 0.000002611054662215034]
70
+ const COS_C = [1, -0.4999993043717576, 0.04166402742354027, -0.0013856638518363177, 0.00002321737177898552]
71
+ const EXP2_C = [1, 0.6931472000619209, 0.24022650999918949, 0.05550340682450019, 0.009618048870444599, 0.0013395279077191057, 0.00015463102004723134]
72
+ const EXPM1_COEF = [1, 1 / 2, 1 / 6, 1 / 24, 1 / 120, 1 / 720, 1 / 5040, 1 / 40320]
73
+
74
+ function sinCore(x) {
75
+ if (Number.isNaN(x)) return x
76
+ if (Math.abs(x) === Infinity) return NaN
77
+ if (Math.abs(x) < 2 ** -27) return x
78
+ let q = nearest(x * INV_PI)
79
+ let r = x - q * PI
80
+ if (Math.abs(r) > HALF_PI) {
81
+ const q2 = nearest(r * INV_PI)
82
+ r = r - q2 * PI
83
+ q = q + q2
84
+ }
85
+ q = q - 2 * nearest(q * 0.5)
86
+ const r2 = r * r
87
+ r = r * horner(SIN_C, r2)
88
+ if (Math.abs(q) > 0.5) r = -r
89
+ return Math.min(Math.max(r, -1), 1)
90
+ }
91
+
92
+ function cosCore(x) {
93
+ if (Number.isNaN(x)) return x
94
+ if (Math.abs(x) === Infinity) return NaN
95
+ let q = nearest(x * INV_PI)
96
+ let r = x - q * PI
97
+ if (Math.abs(r) > HALF_PI) {
98
+ const q2 = nearest(r * INV_PI)
99
+ r = r - q2 * PI
100
+ q = q + q2
101
+ }
102
+ q = q - 2 * nearest(q * 0.5)
103
+ const r2 = r * r
104
+ r = horner(COS_C, r2)
105
+ if (Math.abs(q) > 0.5) r = -r
106
+ return Math.min(Math.max(r, -1), 1)
107
+ }
108
+
109
+ function tan(x) { return sinCore(x) / cosCore(x) }
110
+
111
+ function exp2(y) {
112
+ if (Number.isNaN(y)) return y
113
+ if (y > 1024) return Infinity
114
+ if (y < -1075) return 0
115
+ const k = nearest(y) // i32.trunc_f64_s(f64.nearest y) — nearest is already integral here
116
+ const f = y - k
117
+ const p = horner(EXP2_C, f)
118
+ if (k > -1023 && k < 1024) {
119
+ return p * bitsF64(BigInt(k + 1023) << 52n)
120
+ }
121
+ const k2 = k >> 1
122
+ return p * bitsF64(BigInt(k2 + 1023) << 52n) * bitsF64(BigInt(k - k2 + 1023) << 52n)
123
+ }
124
+
125
+ function exp(x) { return exp2(x * Math.LOG2E) }
126
+
127
+ function expm1(x) {
128
+ if (Math.abs(x) < 0.5) return x * horner(EXPM1_COEF, x)
129
+ return exp(x) - 1
130
+ }
131
+
132
+ function log(x) {
133
+ if (Number.isNaN(x)) return x
134
+ if (x <= 0) return x === 0 ? -Infinity : NaN
135
+ if (x === Infinity) return x
136
+ let k = 0
137
+ if (x < 2.2250738585072014e-308) { x = x * 18014398509481984; k = -54 }
138
+ const bits = f64Bits(x)
139
+ k += Number((bits >> 52n) & 0x7ffn) - 1023
140
+ let m = bitsF64((bits & 0x000fffffffffffffn) | 0x3ff0000000000000n)
141
+ if (m >= 1.4142135623730951) { m = m * 0.5; k += 1 }
142
+ const s = (m - 1) / (m + 1)
143
+ const z = s * s
144
+ return k * Math.LN2 + 2 * s * (1 + z * (0.33333333283005556 + z * (0.20000059590510924 + z * (0.14275490984342690 + z * 0.11663796426848184))))
145
+ }
146
+
147
+ function log2_(x) { return log(x) / Math.LN2 }
148
+
149
+ function log10_(x) {
150
+ if (Number.isNaN(x)) return x
151
+ if (x <= 0) return x === 0 ? -Infinity : NaN
152
+ if (x === Infinity) return x
153
+ let k = 0
154
+ if (x < 2.2250738585072014e-308) { x = x * 18014398509481984; k = -54 }
155
+ const bits = f64Bits(x)
156
+ k += Number((bits >> 52n) & 0x7ffn) - 1023
157
+ let m = bitsF64((bits & 0x000fffffffffffffn) | 0x3ff0000000000000n)
158
+ if (m >= 1.4142135623730951) { m = m * 0.5; k += 1 }
159
+ const f = m - 1
160
+ const hfsq = 0.5 * (f * f)
161
+ const s = f / (2 + f)
162
+ const z = s * s
163
+ const w = z * z
164
+ const t1 = w * (0.3999999999940942 + w * (0.22222198432149792 + w * 0.15313837699209373))
165
+ const t2 = z * (0.6666666666666735 + w * (0.2857142874366239 + w * (0.1818357216161805 + w * 0.14798198605116586)))
166
+ const R = t2 + t1
167
+ let hi = f - hfsq
168
+ hi = bitsF64(f64Bits(hi) & 0xffffffff00000000n)
169
+ const lo = ((f - hi) - hfsq) + s * (hfsq + R)
170
+ const valhi = hi * 0.4342944818781689
171
+ const dk = k
172
+ const y = dk * 0.30102999566361177
173
+ const vallo = ((dk * 3.694239077158931e-13) + ((lo + hi) * 2.5082946711645275e-11)) + (lo * 0.4342944818781689)
174
+ const w2 = y + valhi
175
+ const vallo2 = vallo + ((y - w2) + valhi)
176
+ return vallo2 + w2
177
+ }
178
+
179
+ function log1p(x) {
180
+ if (x === Infinity) return Infinity
181
+ const u = 1 + x
182
+ if (u === 1) return x
183
+ return (log(u) * x) / (u - 1)
184
+ }
185
+
186
+ /** Fully-constant `Math.pow`/`**` fold, mirroring emitPow's own constant-arg
187
+ * branches exactly (module/math.js `emitPow`) — NOT the general runtime
188
+ * `$math.pow`, because emit.js already special-cases fully-literal operands
189
+ * before ever reaching that call: an integer |n|<=16 exponent square-and-
190
+ * multiplies (foldPow), exponent 0.5 is f64.sqrt, and everything else is
191
+ * host `Math.pow` (emit.js's own constant fold, line ~358). Folding earlier
192
+ * at the source level with this SAME 3-way split reproduces exactly what
193
+ * compiling the unfolded expression already does today — zero new divergence. */
194
+ function pow(a, b) {
195
+ if (Number.isInteger(b) && Math.abs(b) <= 16) return powInt(a, b)
196
+ if (b === 0.5) return Math.sqrt(a)
197
+ return Math.pow(a, b)
198
+ }
199
+ function powInt(a, n) {
200
+ if (n === 0) return 1
201
+ let sq = a, res = null
202
+ for (let m = Math.abs(n); m > 0; m >>= 1) {
203
+ if (m & 1) res = (res === null) ? sq : res * sq
204
+ if (m >> 1) sq = sq * sq
205
+ }
206
+ return n < 0 ? 1 / res : res
207
+ }
208
+
209
+ function atan(x) {
210
+ if (Number.isNaN(x)) return x
211
+ if (x === 0) return x
212
+ let t = Math.abs(x)
213
+ let off = 0
214
+ let flip = false
215
+ if (t > 1) { t = 1 / t; flip = true }
216
+ if (t > 0.41421356237309503) {
217
+ t = (t - 0.41421356237309503) / (1 + 0.41421356237309503 * t)
218
+ off = 0.39269908169872414
219
+ }
220
+ const u = t * t
221
+ let r = off + t * (0.99999999939667072 + u * (-0.33333307625846248 + u * (0.19998216947828790 + u * (-0.14240083011830104 + u * (0.10573479828448784 + u * (-0.060347904072425573))))))
222
+ if (flip) r = HALF_PI - r
223
+ return copysign(r, x)
224
+ }
225
+
226
+ function asin(x) {
227
+ if (Math.abs(x) > 1) return NaN
228
+ const ax = Math.abs(x)
229
+ const a = ax <= 0.5 ? ax : Math.sqrt(0.5 * (1 - ax))
230
+ const u = a * a
231
+ let r = a + (a * u) * (0.16666666715486264 + u * (0.074999892151409259 + u * (0.044648555271317079 + u * (0.030259196387355945 + u * (0.023661273034955098 + u * (0.010472588920432560 + u * 0.031028862087420162))))))
232
+ if (ax > 0.5) r = HALF_PI - 2 * r
233
+ return copysign(r, x)
234
+ }
235
+
236
+ function acos(x) { return HALF_PI - asin(x) }
237
+
238
+ function atan2(y, x) {
239
+ if (Number.isNaN(x)) return x
240
+ if (Number.isNaN(y)) return y
241
+ if (x === 0) {
242
+ if (y === 0) return copysign((x < 0 || Object.is(x, -0)) ? PI : 0, y)
243
+ return y > 0 ? HALF_PI : -HALF_PI
244
+ }
245
+ if (x >= 0) return atan(y / x)
246
+ return y >= 0 ? atan(y / x) + PI : atan(y / x) - PI
247
+ }
248
+
249
+ function sinh(x) {
250
+ if (x === 0) return x
251
+ let ex = exp(Math.abs(x))
252
+ ex = 0.5 * (ex - 1 / ex)
253
+ return x < 0 ? -ex : ex
254
+ }
255
+
256
+ function cosh(x) {
257
+ const ex = exp(Math.abs(x))
258
+ return 0.5 * (ex + 1 / ex)
259
+ }
260
+
261
+ function tanh(x) {
262
+ if (x === 0) return x
263
+ if (Math.abs(x) > 22) return x < 0 ? -1 : 1
264
+ let e2x = exp(2 * Math.abs(x))
265
+ e2x = (e2x - 1) / (e2x + 1)
266
+ return x < 0 ? -e2x : e2x
267
+ }
268
+
269
+ function asinh(x) {
270
+ if (!Number.isFinite(x)) return x
271
+ if (x === 0) return x
272
+ return log(x + Math.sqrt(x * x + 1))
273
+ }
274
+
275
+ function acosh(x) {
276
+ if (x === Infinity) return Infinity
277
+ if (x < 1) return NaN
278
+ return log(x + Math.sqrt(x * x - 1))
279
+ }
280
+
281
+ function atanh(x) {
282
+ if (x === 0) return x
283
+ if (Math.abs(x) === Infinity) return NaN
284
+ return 0.5 * log((1 + x) / (1 - x))
285
+ }
286
+
287
+ function cbrt(x) {
288
+ if (!Number.isFinite(x)) return x
289
+ if (x === 0) return x
290
+ let a = Math.abs(x)
291
+ let s = 1
292
+ if (a < 2.2250738585072014e-308) { a = a * 1152921504606846976; s = 9.5367431640625e-7 }
293
+ let t = bitsF64((f64Bits(a) / 3n) + 0x2A9F7893BF800000n)
294
+ t = ((t + t) + a / (t * t)) * 0.3333333333333333
295
+ t = ((t + t) + a / (t * t)) * 0.3333333333333333
296
+ t = ((t + t) + a / (t * t)) * 0.3333333333333333
297
+ t = t * s
298
+ return x < 0 ? -t : t
299
+ }
300
+
301
+ // N-ary like Math.hypot, folded as the SAME left-chained 2-ary calls the runtime
302
+ // emitter builds (module/math.js `math.hypot`) so constant folds stay bit-equal to
303
+ // the compiled chain: () → +0, (x) → abs(x), (a,b,…) → hypot2(hypot2(a,b),…).
304
+ function hypot2(x, y) {
305
+ if (Math.abs(x) === Infinity) return Infinity
306
+ if (Math.abs(y) === Infinity) return Infinity
307
+ return Math.sqrt(x * x + y * y)
308
+ }
309
+ function hypot(...vs) {
310
+ if (vs.length === 0) return 0
311
+ if (vs.length === 1) return Math.abs(vs[0])
312
+ let r = hypot2(vs[0], vs[1])
313
+ for (let i = 2; i < vs.length; i++) r = hypot2(r, vs[i])
314
+ return r
315
+ }
316
+
317
+ /** Pure bit-exact-vs-kernel transcendentals — dispatched by `math.<name>` key
318
+ * (matches the resolved callee jz's prepare already produces for `Math.foo`). */
319
+ export const MATH_KERNEL = {
320
+ 'math.sin': sinCore, 'math.sin_core': sinCore,
321
+ 'math.cos': cosCore, 'math.cos_core': cosCore,
322
+ 'math.tan': tan,
323
+ 'math.exp2': exp2, 'math.exp': exp, 'math.expm1': expm1,
324
+ 'math.log': log, 'math.log2': log2_, 'math.log10': log10_, 'math.log1p': log1p,
325
+ 'math.atan': atan, 'math.asin': asin, 'math.acos': acos, 'math.atan2': atan2,
326
+ 'math.sinh': sinh, 'math.cosh': cosh, 'math.tanh': tanh,
327
+ 'math.asinh': asinh, 'math.acosh': acosh, 'math.atanh': atanh,
328
+ 'math.cbrt': cbrt, 'math.hypot': hypot,
329
+ }
330
+ /** `Math.pow`/`**` — special-cased 3-way split (see `pow` doc above), not a plain unary kernel entry. */
331
+ export const powFold = pow