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
package/layout.js CHANGED
@@ -52,7 +52,16 @@ const AUX_SHIFT = BigInt(LAYOUT.AUX_SHIFT), AUX_MASK = BigInt(LAYOUT.AUX_MASK)
52
52
  const OFFSET_MASK = BigInt(LAYOUT.OFFSET_MASK)
53
53
 
54
54
  /** Format an i64 BigInt as a zero-padded `0x…` hex literal for WAT/IR templates. */
55
- export const i64Hex = bits => '0x' + bits.toString(16).toUpperCase().padStart(16, '0')
55
+ // Formatted via 32-bit halves with an explicit logical shift, NOT
56
+ // bits.toString(16): under self-host, BigInts are raw SIGNED i64 bits
57
+ // (kind-erased — see ir.js's SELF-HOST CONTRACT), so toString(16) of a
58
+ // bit-63-set value renders a signed "-8000…" fragment and the emitted
59
+ // `(i64.const 0x00-8000…)` kills the kernel's watr parse ("Bad int") — the
60
+ // nanPrefixMaskHex regression that silently broke every durable-log helper
61
+ // the kernel compiled. `>> 32n` sign-extends on raw bits; the & masks the
62
+ // extension off, so both host and kernel produce the same unsigned halves.
63
+ const _hx8 = (n) => n.toString(16).toUpperCase().padStart(8, '0')
64
+ export const i64Hex = bits => '0x' + _hx8(Number((bits >> 32n) & 0xFFFFFFFFn)) + _hx8(Number(bits & 0xFFFFFFFFn))
56
65
 
57
66
  /** Pack (type, aux, offset) into the i64 NaN-box carrier — the single source of
58
67
  * truth for pointer bit layout. Compiler IR (`packPtrBits`/`mkPtrIR`), the
@@ -123,6 +132,12 @@ export const decodePtrAux = hi => hi & LAYOUT.AUX_MASK
123
132
  /** i64 NaN-prefix OR-mask for WAT `(i64.const …)` templates. */
124
133
  export const nanPrefixHex = () => i64Hex(LAYOUT.NAN_PREFIX_BITS)
125
134
 
135
+ /** AND-mask isolating the boxed-carrier prefix (sign + exponent + quiet bit):
136
+ * `(v & nanPrefixMask) == nanPrefix` ⇔ v is a NaN-boxed carrier (any tag/aux/
137
+ * payload). The mask is the prefix with the SIGN bit forced on — boxes are
138
+ * emitted sign-clear, so a set sign bit must fail the carrier test. */
139
+ export const nanPrefixMaskHex = () => i64Hex(LAYOUT.NAN_PREFIX_BITS | (1n << 63n))
140
+
126
141
  /** Atom sentinel as i64 hex (compiler WAT templates). */
127
142
  export const atomNanHex = atomId => i64Hex(LAYOUT.NAN_PREFIX_BITS | (BigInt(atomId) << AUX_SHIFT))
128
143
 
@@ -136,12 +151,27 @@ export const atomNanHex = atomId => i64Hex(LAYOUT.NAN_PREFIX_BITS | (BigInt(atom
136
151
  * heap consumers read the len header at -4 regardless of aux. */
137
152
  export const STR_INTERN_BIT = 0x1
138
153
 
154
+ /** STRING aux bit 1 on a PLAIN-HEAP string (SSO and SLICE clear): the string was
155
+ * allocated with an [hash u32][len u32][bytes] header where the hash cell is a
156
+ * LAZY cache — seeded 0 (byte-FNV clamps to ≥2, so 0 is unambiguous "uncomputed"),
157
+ * filled by __str_hash on first hash. Sound because heap strings never relocate
158
+ * and die with their arena; the two in-place mutators (concat/append bump-extend
159
+ * of a heap-top accumulator) zero the cell when they change the bytes. Unlike
160
+ * STR_INTERN_BIT it carries NO canonicality claim — two hcache strings with equal
161
+ * content are ordinary bit-unequal pointers. Inert for slices (their aux[12:0]
162
+ * is a length, only read under SLICE_BIT) and every len/byte reader (-4 header
163
+ * unchanged). Producers opt in one by one; an unmarked string just re-hashes. */
164
+ export const STR_HCACHE_BIT = 0x2
165
+
139
166
  /** Pre-shifted STRING SSO aux bit as i64 hex. */
140
167
  export const ssoBitI64Hex = () => i64Hex(BigInt(LAYOUT.SSO_BIT) << AUX_SHIFT)
141
168
 
142
169
  /** Pre-shifted STRING slice/view aux bit as i64 hex. */
143
170
  export const sliceBitI64Hex = () => i64Hex(BigInt(LAYOUT.SLICE_BIT) << AUX_SHIFT)
144
171
 
172
+ /** Pre-shifted STRING hash-cache aux bit as i64 hex. */
173
+ export const hcacheBitI64Hex = () => i64Hex(BigInt(STR_HCACHE_BIT) << AUX_SHIFT)
174
+
145
175
  /** Full i64 NaN-box hex for `(i64.const …)` — ptr type + aux, offset OR'd separately. */
146
176
  export const ptrNanHex = (ptrType, aux = 0) => i64Hex(ptrBits(ptrType, aux))
147
177
 
@@ -174,10 +204,25 @@ export const oobNanIR = () => ['f64.const', oobNanLiteral()]
174
204
  * __arr_idx_known, __typed_idx…) — a body containing a loop is excluded from
175
205
  * V8's wasm inliner, and these helpers sit on ~25% of self-host compile time.
176
206
  * Callers must list '__ptr_offset_fwd' in their deps()/wat() dependency set. */
207
+ // Upper bound is `off <= memory.size() * 65536` (total bytes currently backed) — read from
208
+ // $__heap_end64 (module/core.js, kept in sync by __memgrow on every grow), NOT recomputed
209
+ // as `i64.shl(i64.extend_i32_u(memory.size), 16)` inline: this check is on the hottest
210
+ // pointer-dereference path (~25% of self-host compile time, per the doc comment above), so
211
+ // a per-call recompute would cost 3 extra instructions at every inlined site instead of one
212
+ // global read. The i32 form `memory.size() << 16` (what a naive version — and $__heap_end
213
+ // itself — uses) overflows to exactly 0 at the wasm32 ceiling (memory.size()==65536 pages,
214
+ // i.e. the full 4 GiB: 65536*65536 == 2^32, unrepresentable in i32). Reading $__heap_end
215
+ // here instead of $__heap_end64 would make the bound "off <= 0", failing for every real off
216
+ // and silently disabling the forward-chase for the rest of execution: any pointer to an
217
+ // already-relocated ARRAY/SET/MAP/HASH stops following its forwarding header and reads the
218
+ // abandoned old block (cap=-1 sentinel misread as a real capacity) instead. That wraparound
219
+ // is benign for $__heap_end's own consumer (__alloc's fast-path check just slow-paths one
220
+ // extra time and __memgrow re-derives everything fresh in i64) but NOT here, where it gates
221
+ // whether forwarding runs at all — hence the separate i64 global instead of reusing $__heap_end.
177
222
  export const followForwardingWat = (off = '$off', { lowGuard = true } = {}) =>
178
223
  `(if (i32.and
179
224
  ${lowGuard ? `(i32.ge_u (local.get ${off}) (i32.const 8))` : '(i32.const 1)'}
180
- (i32.le_u (local.get ${off}) (i32.shl (memory.size) (i32.const 16))))
225
+ (i64.le_u (i64.extend_i32_u (local.get ${off})) (global.get $__heap_end64)))
181
226
  (then (if (i32.eq (i32.load (i32.sub (local.get ${off}) (i32.const 4))) (i32.const -1))
182
227
  (then (local.set ${off} (call $__ptr_offset_fwd (local.get ${off})))))))`
183
228
 
@@ -188,7 +233,7 @@ export const ptrOffsetFwdWat = () =>
188
233
  `(func $__ptr_offset_fwd (param $off i32) (result i32)
189
234
  (block $done (loop $follow
190
235
  (br_if $done (i32.lt_u (local.get $off) (i32.const 8)))
191
- (br_if $done (i32.gt_u (local.get $off) (i32.shl (memory.size) (i32.const 16))))
236
+ (br_if $done (i64.gt_u (i64.extend_i32_u (local.get $off)) (global.get $__heap_end64)))
192
237
  (br_if $done (i32.ne (i32.load (i32.sub (local.get $off) (i32.const 4))) (i32.const -1)))
193
238
  (local.set $off (i32.load (i32.sub (local.get $off) (i32.const 8))))
194
239
  (br $follow)))