jz 0.6.0 → 0.8.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 (63) hide show
  1. package/README.md +99 -72
  2. package/bench/README.md +253 -100
  3. package/bench/bench.svg +58 -81
  4. package/cli.js +85 -12
  5. package/dist/interop.js +1 -0
  6. package/dist/jz.js +6196 -0
  7. package/index.d.ts +126 -0
  8. package/index.js +222 -35
  9. package/interop.js +240 -141
  10. package/layout.js +34 -18
  11. package/module/array.js +99 -111
  12. package/module/collection.js +124 -30
  13. package/module/console.js +1 -1
  14. package/module/core.js +163 -19
  15. package/module/json.js +3 -3
  16. package/module/math.js +162 -3
  17. package/module/number.js +268 -13
  18. package/module/object.js +37 -5
  19. package/module/regex.js +8 -7
  20. package/module/simd.js +37 -5
  21. package/module/string.js +203 -168
  22. package/module/typedarray.js +565 -112
  23. package/package.json +26 -7
  24. package/src/abi/string.js +29 -29
  25. package/src/ast.js +19 -2
  26. package/src/autoload.js +3 -0
  27. package/src/compile/analyze-scans.js +174 -11
  28. package/src/compile/analyze.js +101 -4
  29. package/src/compile/cse-load.js +200 -0
  30. package/src/compile/emit-assign.js +82 -20
  31. package/src/compile/emit.js +592 -51
  32. package/src/compile/index.js +504 -89
  33. package/src/compile/infer.js +36 -1
  34. package/src/compile/loop-divmod.js +109 -0
  35. package/src/compile/loop-model.js +91 -0
  36. package/src/compile/loop-square.js +102 -0
  37. package/src/compile/narrow.js +275 -41
  38. package/src/compile/peel-stencil.js +215 -0
  39. package/src/compile/plan/advise.js +55 -1
  40. package/src/compile/plan/common.js +29 -0
  41. package/src/compile/plan/index.js +8 -1
  42. package/src/compile/plan/inline.js +180 -22
  43. package/src/compile/plan/literals.js +313 -24
  44. package/src/compile/plan/scope.js +115 -39
  45. package/src/compile/program-facts.js +21 -2
  46. package/src/ctx.js +96 -19
  47. package/src/helper-counters.js +137 -0
  48. package/src/ir.js +157 -20
  49. package/src/kind-traits.js +34 -3
  50. package/src/kind.js +79 -4
  51. package/src/op-policy.js +5 -2
  52. package/src/ops.js +119 -0
  53. package/src/optimize/index.js +1274 -151
  54. package/src/optimize/recurse.js +182 -0
  55. package/src/optimize/vectorize.js +4187 -253
  56. package/src/prepare/index.js +75 -14
  57. package/src/prepare/lift-iife.js +149 -0
  58. package/src/reps.js +6 -2
  59. package/src/static.js +9 -0
  60. package/src/type.js +63 -51
  61. package/src/wat/assemble.js +286 -21
  62. package/src/widen.js +21 -0
  63. package/src/wat/optimize.js +0 -3760
package/module/regex.js CHANGED
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { typed, asF64, asI64, UNDEF_NAN, NULL_NAN, mkPtrIR, temp, tempI32 } from '../src/ir.js'
11
11
  import { emit, deps } from '../src/bridge.js'
12
- import { ctx, err, inc, PTR, LAYOUT, getter, declGlobal } from '../src/ctx.js'
12
+ import { ctx, err, inc, PTR, LAYOUT, registerGetter, declGlobal } from '../src/ctx.js'
13
13
  import { valTypeOf } from '../src/kind.js'
14
14
  import { VAL } from '../src/reps.js'
15
15
 
@@ -746,13 +746,14 @@ export default (ctx) => {
746
746
  (if (i32.eqz (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT})))
747
747
  (then (return (call $__ptr_offset (local.get $ptr)))))
748
748
  (local.set $off (call $__ptr_offset (local.get $ptr)))
749
- (local.set $len (i32.and (local.get $aux) (i32.const 7)))
749
+ (local.set $len (i32.and (i32.shr_u (local.get $aux) (i32.const 10)) (i32.const 7)))
750
750
  (local.set $buf (call $__alloc (local.get $len)))
751
751
  (local.set $i (i32.const 0))
752
752
  (block $done (loop $next
753
753
  (br_if $done (i32.ge_u (local.get $i) (local.get $len)))
754
+ ;; 7-bit ASCII SSO: char i at payload bit i*7 (read from the full i64 ptr; chars 4-5 span aux).
754
755
  (i32.store8 (i32.add (local.get $buf) (local.get $i))
755
- (i32.and (i32.shr_u (local.get $off) (i32.mul (local.get $i) (i32.const 8))) (i32.const 0xFF)))
756
+ (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.mul (i64.extend_i32_u (local.get $i)) (i64.const 7))) (i64.const 0x7f))))
756
757
  (local.set $i (i32.add (local.get $i) (i32.const 1)))
757
758
  (br $next)))
758
759
  (local.get $buf))`
@@ -911,14 +912,14 @@ export default (ctx) => {
911
912
  // RegExp.prototype.source — the pattern text. A literal stores it verbatim
912
913
  // (already grammar-escaped), so `/A/.source` is the 6-char "A".
913
914
  // An empty pattern serializes to "(?:)" so the result re-parses to a regex.
914
- ctx.core.emit['.regex:source'] = getter((obj) => {
915
+ registerGetter('.regex:source', (obj) => {
915
916
  const a = regexAstOf(obj)
916
917
  return emit(['str', (a && a[1]) || '(?:)'])
917
918
  })
918
919
 
919
920
  // RegExp.prototype.flags — flag characters in canonical order (sec-get-regexp.prototype.flags).
920
921
  const FLAG_ORDER = 'dgimsvy'
921
- ctx.core.emit['.regex:flags'] = getter((obj) => {
922
+ registerGetter('.regex:flags', (obj) => {
922
923
  const f = flagsOf(obj)
923
924
  return emit(['str', [...FLAG_ORDER].filter(c => f.includes(c)).join('')])
924
925
  })
@@ -927,10 +928,10 @@ export default (ctx) => {
927
928
  for (const [prop, ch] of [
928
929
  ['global', 'g'], ['ignoreCase', 'i'], ['multiline', 'm'], ['dotAll', 's'],
929
930
  ['unicode', 'u'], ['sticky', 'y'], ['hasIndices', 'd'], ['unicodeSets', 'v'],
930
- ]) ctx.core.emit[`.regex:${prop}`] = getter((obj) => typed(['f64.const', flagsOf(obj).includes(ch) ? 1 : 0], 'f64'))
931
+ ]) registerGetter(`.regex:${prop}`, (obj) => typed(['f64.const', flagsOf(obj).includes(ch) ? 1 : 0], 'f64'))
931
932
 
932
933
  // lastIndex — for /g and /y regexes, reads the mutable global; others always 0.
933
- ctx.core.emit['.regex:lastIndex'] = getter((obj) => {
934
+ registerGetter('.regex:lastIndex', (obj) => {
934
935
  const id = resolveRegex(obj)
935
936
  if (id != null) {
936
937
  const flags = flagsOf(obj)
package/module/simd.js CHANGED
@@ -1,8 +1,9 @@
1
1
  /**
2
- * SIMD module — source-level f32x4 / i32x4 intrinsics that lower 1:1 to wasm SIMD
3
- * (v128). Lets a jz kernel process 4 lanes per instruction: the per-pixel-parallel
4
- * kernels (mandelbrot, raymarcher) run 4 pixels/rays in masked lockstep and beat
5
- * scalar V8 several-fold (validated: SIMD-4 mandelbrot 4.6× a warm-V8 scalar loop).
2
+ * SIMD module — source-level f32x4 / i32x4 / f64x2 intrinsics that lower 1:1 to wasm
3
+ * SIMD (v128). Lets a jz kernel process 4 (or, for f64x2, 2) lanes per instruction:
4
+ * the per-pixel-parallel kernels (mandelbrot, raymarcher) run 4 pixels/rays in masked
5
+ * lockstep, and the attractors kernel packs its two sines + two cosines two-per-f64x2.
6
+ * Several-fold over scalar V8 (validated: SIMD-4 mandelbrot ≈ 4.6× a warm-V8 scalar loop).
6
7
  *
7
8
  * A v128 value is just a local whose wasm type is `v128` (exprType returns 'v128'
8
9
  * for these calls; emit passes it through without an f64/i32 coercion). Building
@@ -18,11 +19,14 @@
18
19
  * i32x4.splat(n) / add/sub(a,b) / lane(v,k)
19
20
  * v128.and/or/xor(a,b) / not(a) / bitselect(t,f,mask)
20
21
  * v128.anyTrue(v) / v128.allTrue(v) → i32 (loop-exit tests)
22
+ * f64x2.splat(x) / lanes(a,b) / add/sub/mul/div/min/max / sqrt/abs/neg/… / cmp
23
+ * f64x2.sin(v) / f64x2.cos(v) → both lanes through one poly ($math.sin2/$math.cos2)
24
+ * f64x2.lane(v, k) extract lane k (0..1) as a number
21
25
  *
22
26
  * @module simd
23
27
  */
24
28
  import { typed, asF64, asI32 } from '../src/ir.js'
25
- import { emit } from '../src/bridge.js'
29
+ import { emit, emitter } from '../src/bridge.js'
26
30
  import { err } from '../src/ctx.js'
27
31
 
28
32
  export default (ctx) => {
@@ -82,4 +86,32 @@ export default (ctx) => {
82
86
  // ── lane extract (read a single lane back to a scalar) ───────────────────────
83
87
  e['f32x4.lane'] = (v, k) => F(['f64.promote_f32', ['f32x4.extract_lane', laneIdx(k), op(v)]])
84
88
  e['i32x4.lane'] = (v, k) => I(['i32x4.extract_lane', laneIdx(k), op(v)])
89
+
90
+ // ── f64x2 — two full-precision f64 lanes (no f32 demotion) ───────────────────
91
+ // For kernels whose hot value is f64: two independent angles/coords per instruction.
92
+ // `f64x2.sin`/`f64x2.cos` lower to the shared $math.sin2/$math.cos2 poly (module/math.js)
93
+ // — sin and cos of distinct args pack two-per-vector and ≈halve transcendental cost.
94
+ const lane2 = (k) => {
95
+ const v = typeof k === 'number' ? k : (Array.isArray(k) && k.length === 2 && k[0] == null && typeof k[1] === 'number') ? k[1] : null
96
+ if (v == null || (v | 0) !== v || v < 0 || v > 1)
97
+ err(`f64x2 lane index must be a 0..1 literal (got ${JSON.stringify(k)}).`)
98
+ return v
99
+ }
100
+ e['f64x2.splat'] = (a) => V(['f64x2.splat', asF64(emit(a))])
101
+ e['f64x2.lanes'] = (a, b) => V(['f64x2.replace_lane', 1, ['f64x2.splat', asF64(emit(a))], asF64(emit(b))])
102
+ for (const o of ['add', 'sub', 'mul', 'div', 'min', 'max'])
103
+ e[`f64x2.${o}`] = (a, b) => V([`f64x2.${o}`, op(a), op(b)])
104
+ for (const o of ['sqrt', 'abs', 'neg', 'floor', 'ceil', 'trunc', 'nearest'])
105
+ e[`f64x2.${o}`] = (a) => V([`f64x2.${o}`, op(a)])
106
+ for (const o of ['eq', 'ne', 'lt', 'le', 'gt', 'ge'])
107
+ e[`f64x2.${o}`] = (a, b) => V([`f64x2.${o}`, op(a), op(b)])
108
+ e['f64x2.sin'] = emitter(['math.sin2'], (a) => V(['call', '$math.sin2', op(a)]))
109
+ e['f64x2.cos'] = emitter(['math.cos2'], (a) => V(['call', '$math.cos2', op(a)]))
110
+ // f64x2.log/exp/exp2 lower to the true-vectorized $math.log_v/$math.exp_v/$math.exp2_v polys —
111
+ // both lanes through one fdlibm/2^f evaluation (≈2× over two scalar calls), bit-exact via a
112
+ // hot-path-vectorized + scalar-edge-fallback split (module/math.js).
113
+ e['f64x2.log'] = emitter(['math.log_v'], (a) => V(['call', '$math.log_v', op(a)]))
114
+ e['f64x2.exp'] = emitter(['math.exp_v'], (a) => V(['call', '$math.exp_v', op(a)]))
115
+ e['f64x2.exp2'] = emitter(['math.exp2_v'], (a) => V(['call', '$math.exp2_v', op(a)]))
116
+ e['f64x2.lane'] = (v, k) => F(['f64x2.extract_lane', lane2(k), op(v)])
85
117
  }