jz 0.1.1 → 0.2.1

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.
@@ -7,7 +7,7 @@
7
7
  * @module typed
8
8
  */
9
9
 
10
- import { typed, asF64, asI32, UNDEF_NAN, allocPtr, mkPtrIR, ptrOffsetIR, temp, tempI32 } from '../src/ir.js'
10
+ import { typed, asF64, asI32, asI64, UNDEF_NAN, allocPtr, mkPtrIR, ptrOffsetIR, temp, tempI32, undefExpr } from '../src/ir.js'
11
11
  import { emit } from '../src/emit.js'
12
12
  import { valTypeOf, lookupValType, VAL } from '../src/analyze.js'
13
13
  import { inc, PTR } from '../src/ctx.js'
@@ -21,6 +21,9 @@ const ELEM = {
21
21
  Float32Array: 6, Float64Array: 7,
22
22
  BigInt64Array: 7, BigUint64Array: 7,
23
23
  }
24
+ const BIGINT_ELEM_FLAG = 16
25
+ const typedAux = (name, isView = false) => ELEM[name] | (isView ? 8 : 0) |
26
+ (name === 'BigInt64Array' || name === 'BigUint64Array' ? BIGINT_ELEM_FLAG : 0)
24
27
  const STRIDE = [1, 1, 2, 2, 4, 4, 4, 8]
25
28
  const SHIFT = [0, 0, 1, 1, 2, 2, 2, 3]
26
29
  const LOAD = [
@@ -165,7 +168,7 @@ function genSimdMap(name, elemType, pattern) {
165
168
  : `(local.set $ei (${scalarLoad} ${byteOff}))`
166
169
  const scalarStoreExpr = `${scalarLoadSet}\n (${store} ${dstByteOff} ${scalarOp})`
167
170
 
168
- return `(func $${name} (param $src f64) (result f64)
171
+ return `(func $${name} (param $src i64) (result f64)
169
172
  (local $len i32) (local $srcOff i32) (local $dstOff i32) (local $dst i32)
170
173
  (local $i i32) (local $simdLen i32) (local $byteOff i32)
171
174
  (local $v v128)
@@ -203,6 +206,7 @@ export default (ctx) => {
203
206
  __byte_length: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
204
207
  __byte_offset: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
205
208
  __to_buffer: ['__ptr_type', '__ptr_offset', '__ptr_aux', '__mkptr'],
209
+ __typed_set_idx: ['__ptr_aux', '__ptr_offset'],
206
210
  })
207
211
 
208
212
  // .map/.filter invoke callbacks with arity 1 internally.
@@ -216,7 +220,7 @@ export default (ctx) => {
216
220
  // __byte_length(ptr) — byte size for BUFFER/TYPED; 0 otherwise.
217
221
  // BUFFER and owned TYPED store byteLen at [-8]. TYPED view (aux bit 3) stores byteLen
218
222
  // at descriptor[0].
219
- ctx.core.stdlib['__byte_length'] = `(func $__byte_length (param $ptr f64) (result i32)
223
+ ctx.core.stdlib['__byte_length'] = `(func $__byte_length (param $ptr i64) (result i32)
220
224
  (local $t i32) (local $off i32)
221
225
  (local.set $t (call $__ptr_type (local.get $ptr)))
222
226
  (if (result i32)
@@ -238,11 +242,11 @@ export default (ctx) => {
238
242
  // Owned TYPED: retag as BUFFER at same offset — the byteLen header is shared.
239
243
  // TYPED view: retag as BUFFER at the parent data offset (descriptor[8]) — reconstructs
240
244
  // the root ArrayBuffer so its own header supplies byteLength.
241
- ctx.core.stdlib['__to_buffer'] = `(func $__to_buffer (param $ptr f64) (result f64)
245
+ ctx.core.stdlib['__to_buffer'] = `(func $__to_buffer (param $ptr i64) (result f64)
242
246
  (local $t i32) (local $off i32)
243
247
  (local.set $t (call $__ptr_type (local.get $ptr)))
244
248
  (if (result f64) (i32.eq (local.get $t) (i32.const ${PTR.BUFFER}))
245
- (then (local.get $ptr))
249
+ (then (f64.reinterpret_i64 (local.get $ptr)))
246
250
  (else
247
251
  (local.set $off (call $__ptr_offset (local.get $ptr)))
248
252
  (if (result f64)
@@ -255,6 +259,7 @@ export default (ctx) => {
255
259
 
256
260
  // Constructor: new Float64Array(len) | new F64Array(arr) | new F64Array(buf) | new F64Array(buf, off, len)
257
261
  for (const [name, elemType] of Object.entries(ELEM)) {
262
+ const aux = typedAux(name)
258
263
  const stride = STRIDE[elemType]
259
264
  ctx.core.emit[`new.${name}`] = (lenExpr, offsetExpr, lenExpr2) => {
260
265
  ctx.features.typedarray = true
@@ -280,7 +285,7 @@ export default (ctx) => {
280
285
  ['i32.store',
281
286
  ['i32.add', ['local.get', `$${dst}`], ['i32.const', 8]],
282
287
  ['local.get', `$${parentOff}`]],
283
- mkPtrIR(PTR.TYPED, elemType | 8, ['local.get', `$${dst}`])], 'f64')
288
+ mkPtrIR(PTR.TYPED, typedAux(name, true), ['local.get', `$${dst}`])], 'f64')
284
289
  }
285
290
  // Single arg array-like source: copy elements instead of treating the pointer as a length.
286
291
  if (srcType === VAL.ARRAY && ctx.core.emit[`${name}.from`])
@@ -289,7 +294,7 @@ export default (ctx) => {
289
294
  // TYPED retagged at the same offset — the byteLen header is shared with the parent.
290
295
  // __len(view) = byteLen >> shift computes elemCount for this view's elemType.
291
296
  if (srcType === VAL.BUFFER || srcType === VAL.TYPED) {
292
- return mkPtrIR(PTR.TYPED, elemType, ['call', '$__ptr_offset', asF64(emit(lenExpr))])
297
+ return mkPtrIR(PTR.TYPED, aux, ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(emit(lenExpr))]])
293
298
  }
294
299
  if (srcType == null && ctx.core.emit[`${name}.from`]) {
295
300
  // Runtime dispatch: number → allocate; array → copy elements; buffer/typed → zero-copy view.
@@ -297,7 +302,7 @@ export default (ctx) => {
297
302
  const len = tempI32('tl')
298
303
  const shift = SHIFT[elemType]
299
304
  const numBytes = ['i32.shl', ['local.get', `$${len}`], ['i32.const', shift]]
300
- const numAlloc = allocPtr({ type: PTR.TYPED, aux: elemType, len: numBytes, stride: 1, tag: 'ta' })
305
+ const numAlloc = allocPtr({ type: PTR.TYPED, aux, len: numBytes, stride: 1, tag: 'ta' })
301
306
  return typed(['block', ['result', 'f64'],
302
307
  ['local.set', `$${src}`, asF64(emit(lenExpr))],
303
308
  ['if', ['result', 'f64'],
@@ -309,15 +314,15 @@ export default (ctx) => {
309
314
  numAlloc.ptr]],
310
315
  // Pointer: array → copy elements; buffer/typed → zero-copy view on same offset
311
316
  ['else', ['if', ['result', 'f64'],
312
- ['i32.eq', ['call', '$__ptr_type', ['local.get', `$${src}`]], ['i32.const', PTR.ARRAY]],
317
+ ['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${src}`]]], ['i32.const', PTR.ARRAY]],
313
318
  ['then', ctx.core.emit[`${name}.from`](src)],
314
- ['else', mkPtrIR(PTR.TYPED, elemType,
315
- ['call', '$__ptr_offset', ['local.get', `$${src}`]])]]]]], 'f64')
319
+ ['else', mkPtrIR(PTR.TYPED, aux,
320
+ ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${src}`]]])]]]]], 'f64')
316
321
  }
317
322
  // Normal: allocate fresh typed array (lenExpr is numeric size). Header stores byteLen.
318
323
  const shift = SHIFT[elemType]
319
324
  const lenL = tempI32('tan')
320
- const out = allocPtr({ type: PTR.TYPED, aux: elemType,
325
+ const out = allocPtr({ type: PTR.TYPED, aux,
321
326
  len: ['i32.shl', ['local.get', `$${lenL}`], ['i32.const', shift]], stride: 1, tag: 'ta' })
322
327
  return typed(['block', ['result', 'f64'],
323
328
  ['local.set', `$${lenL}`, asI32(emit(lenExpr))],
@@ -345,7 +350,7 @@ export default (ctx) => {
345
350
  ctx.core.emit['BigInt64Array'] = (bufExpr) => {
346
351
  ctx.features.typedarray = true
347
352
  const va = asF64(emit(bufExpr))
348
- return mkPtrIR(PTR.TYPED, 7, ['call', '$__ptr_offset', va])
353
+ return mkPtrIR(PTR.TYPED, typedAux('BigInt64Array'), ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]])
349
354
  }
350
355
 
351
356
  // .buffer — always aliased (zero-copy). BUFFER/DataView: passthrough.
@@ -367,7 +372,7 @@ export default (ctx) => {
367
372
  }
368
373
  }
369
374
  inc('__to_buffer')
370
- return typed(['call', '$__to_buffer', asF64(emit(obj))], 'f64')
375
+ return typed(['call', '$__to_buffer', asI64(emit(obj))], 'f64')
371
376
  }
372
377
 
373
378
  // .byteLength — BUFFER: raw __len. Owned TYPED: elemCount * stride. View TYPED: descriptor[0].
@@ -375,7 +380,7 @@ export default (ctx) => {
375
380
  if (typeof obj === 'string') {
376
381
  const ctor = ctx.types.typedElem?.get(obj)
377
382
  if (ctor === 'new.ArrayBuffer' || ctor === 'new.DataView') {
378
- return typed(['f64.convert_i32_s', ['call', '$__len', asF64(emit(obj))]], 'f64')
383
+ return typed(['f64.convert_i32_s', ['call', '$__len', ['i64.reinterpret_f64', asF64(emit(obj))]]], 'f64')
379
384
  }
380
385
  if (ctor && ctor.startsWith('new.')) {
381
386
  const isView = ctor.endsWith('.view')
@@ -387,12 +392,12 @@ export default (ctx) => {
387
392
  ['i32.load', ptrOffsetIR(emit(obj), VAL.TYPED)]], 'f64')
388
393
  }
389
394
  return typed(['f64.convert_i32_s',
390
- ['i32.shl', ['call', '$__len', asF64(emit(obj))], ['i32.const', SHIFT[et]]]], 'f64')
395
+ ['i32.shl', ['call', '$__len', ['i64.reinterpret_f64', asF64(emit(obj))]], ['i32.const', SHIFT[et]]]], 'f64')
391
396
  }
392
397
  }
393
398
  }
394
399
  inc('__byte_length')
395
- return typed(['f64.convert_i32_s', ['call', '$__byte_length', asF64(emit(obj))]], 'f64')
400
+ return typed(['f64.convert_i32_s', ['call', '$__byte_length', asI64(emit(obj))]], 'f64')
396
401
  }
397
402
 
398
403
  // .byteOffset — owned: 0. View: descriptor[4] - descriptor[8].
@@ -411,11 +416,11 @@ export default (ctx) => {
411
416
  if (ctor?.startsWith('new.') && ELEM[ctor.slice(4)] != null) return typed(['f64.const', 0], 'f64')
412
417
  }
413
418
  inc('__byte_offset')
414
- return typed(['f64.convert_i32_s', ['call', '$__byte_offset', asF64(emit(obj))]], 'f64')
419
+ return typed(['f64.convert_i32_s', ['call', '$__byte_offset', asI64(emit(obj))]], 'f64')
415
420
  }
416
421
 
417
422
  // Runtime fallback for .byteOffset when variable view-ness is unknown.
418
- ctx.core.stdlib['__byte_offset'] = `(func $__byte_offset (param $ptr f64) (result i32)
423
+ ctx.core.stdlib['__byte_offset'] = `(func $__byte_offset (param $ptr i64) (result i32)
419
424
  (local $off i32)
420
425
  (if (result i32)
421
426
  (i32.and
@@ -431,9 +436,10 @@ export default (ctx) => {
431
436
  // ArrayBuffer.isView(x) — true iff x is a TYPED pointer. (DataView passthrough cannot be
432
437
  // distinguished from ArrayBuffer since both are BUFFER pointers; both report false.)
433
438
  ctx.core.emit['ArrayBuffer.isView'] = (v) => {
439
+ if (v === undefined) return typed(['f64.const', 0], 'f64')
434
440
  const va = asF64(emit(v))
435
441
  return typed(['f64.convert_i32_s',
436
- ['i32.eq', ['call', '$__ptr_type', va], ['i32.const', PTR.TYPED]]], 'f64')
442
+ ['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', va]], ['i32.const', PTR.TYPED]]], 'f64')
437
443
  }
438
444
 
439
445
  // buf.slice(begin?, end?) on a BUFFER → fresh BUFFER with the byte range copied.
@@ -446,7 +452,7 @@ export default (ctx) => {
446
452
  const out = allocPtr({ type: PTR.BUFFER, len: ['local.get', `$${bytes}`], stride: 1, tag: 'bsd' })
447
453
  const beginWat = beginExpr == null ? ['i32.const', 0] : asI32(emit(beginExpr))
448
454
  const endWat = endExpr == null
449
- ? ['call', '$__len', ['local.get', `$${src}`]]
455
+ ? ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]
450
456
  : asI32(emit(endExpr))
451
457
  return typed(['block', ['result', 'f64'],
452
458
  ['local.set', `$${src}`, asF64(emit(obj))],
@@ -459,7 +465,7 @@ export default (ctx) => {
459
465
  out.init,
460
466
  ['memory.copy',
461
467
  ['local.get', `$${out.local}`],
462
- ['i32.add', ['call', '$__ptr_offset', ['local.get', `$${src}`]], ['local.get', `$${beg}`]],
468
+ ['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${src}`]]], ['local.get', `$${beg}`]],
463
469
  ['local.get', `$${bytes}`]],
464
470
  out.ptr], 'f64')
465
471
  }
@@ -507,12 +513,13 @@ export default (ctx) => {
507
513
 
508
514
  // TypedArray.from(arr) — convert regular array to typed array
509
515
  for (const [name, elemType] of Object.entries(ELEM)) {
516
+ const aux = typedAux(name)
510
517
  const stride = STRIDE[elemType], store = STORE[elemType]
511
518
  ctx.core.emit[`${name}.from`] = (src) => {
512
519
  ctx.features.typedarray = true
513
520
  const srcL = temp('tfs')
514
521
  const len = tempI32('tfl'), i = tempI32('tfi'), off = tempI32('tfo')
515
- const out = allocPtr({ type: PTR.TYPED, aux: elemType,
522
+ const out = allocPtr({ type: PTR.TYPED, aux,
516
523
  len: ['i32.mul', ['local.get', `$${len}`], ['i32.const', stride]], stride: 1, tag: 'tf' })
517
524
  const t = out.local
518
525
  const id = ctx.func.uniq++
@@ -528,8 +535,8 @@ export default (ctx) => {
528
535
  ['f64.load', ['i32.add', ['local.get', `$${off}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]]]
529
536
  return typed(['block', ['result', 'f64'],
530
537
  ['local.set', `$${srcL}`, asF64(emit(src))],
531
- ['local.set', `$${off}`, ['call', '$__ptr_offset', ['local.get', `$${srcL}`]]],
532
- ['local.set', `$${len}`, ['call', '$__len', ['local.get', `$${srcL}`]]],
538
+ ['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${srcL}`]]]],
539
+ ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${srcL}`]]]],
533
540
  out.init,
534
541
  ['local.set', `$${i}`, ['i32.const', 0]],
535
542
  ['block', `$brk${id}`, ['loop', `$loop${id}`,
@@ -544,14 +551,14 @@ export default (ctx) => {
544
551
  // .length handled by ptr.js's __len (reads from memory header [-8:len])
545
552
 
546
553
  /** Resolve element type + view-ness for a known TypedArray variable.
547
- * Returns { et, isView } or null. */
554
+ * Returns { et, isView, isBigInt } or null. */
548
555
  const resolveElem = (arr) => {
549
556
  const ctor = typeof arr === 'string' && ctx.types.typedElem?.get(arr)
550
557
  if (!ctor) return null
551
558
  const isView = ctor.endsWith('.view')
552
559
  const name = isView ? ctor.slice(4, -5) : ctor.slice(4)
553
560
  const et = ELEM[name]
554
- return et == null ? null : { et, isView }
561
+ return et == null ? null : { et, isView, isBigInt: name === 'BigInt64Array' || name === 'BigUint64Array' }
555
562
  }
556
563
 
557
564
  /** Emit the real data byte-address for a typed array IR node.
@@ -568,7 +575,7 @@ export default (ctx) => {
568
575
  // Identical factory in array.js; whichever module loads last wins the registration.
569
576
  ctx.core.stdlib['__typed_idx'] = () => {
570
577
  if (!ctx.features.typedarray && !ctx.features.external) {
571
- return `(func $__typed_idx (param $ptr f64) (param $i i32) (result f64)
578
+ return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
572
579
  (local $len i32)
573
580
  (local.set $len (call $__len (local.get $ptr)))
574
581
  (if (result f64)
@@ -578,7 +585,7 @@ export default (ctx) => {
578
585
  (then (f64.const nan:${UNDEF_NAN}))
579
586
  (else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
580
587
  }
581
- return `(func $__typed_idx (param $ptr f64) (param $i i32) (result f64)
588
+ return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
582
589
  (local $off i32) (local $et i32) (local $len i32) (local $aux i32)
583
590
  (local.set $aux (call $__ptr_aux (local.get $ptr)))
584
591
  (local.set $off (call $__ptr_offset (local.get $ptr)))
@@ -599,7 +606,9 @@ export default (ctx) => {
599
606
  (local.set $et (i32.and (local.get $aux) (i32.const 7)))
600
607
  (if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
601
608
  (then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
602
- (then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
609
+ (then (if (result f64) (i32.and (local.get $aux) (i32.const ${BIGINT_ELEM_FLAG}))
610
+ (then (f64.reinterpret_i64 (i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
611
+ (else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))
603
612
  (else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
604
613
  (else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
605
614
  (then (if (result f64) (i32.and (local.get $et) (i32.const 1))
@@ -615,13 +624,42 @@ export default (ctx) => {
615
624
  (else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))))`
616
625
  }
617
626
 
627
+ ctx.core.stdlib['__typed_set_idx'] = `(func $__typed_set_idx (param $ptr i64) (param $i i32) (param $v f64) (result f64)
628
+ (local $off i32) (local $aux i32) (local $et i32) (local $bits i32)
629
+ (local.set $aux (call $__ptr_aux (local.get $ptr)))
630
+ (local.set $off (call $__ptr_offset (local.get $ptr)))
631
+ (if (i32.ne (i32.and (local.get $aux) (i32.const 8)) (i32.const 0))
632
+ (then (local.set $off (i32.load (i32.add (local.get $off) (i32.const 4))))))
633
+ (local.set $et (i32.and (local.get $aux) (i32.const 7)))
634
+ (if (i32.and (local.get $aux) (i32.const ${BIGINT_ELEM_FLAG}))
635
+ (then (i64.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))) (i64.reinterpret_f64 (local.get $v))))
636
+ (else
637
+ (if (i32.eq (local.get $et) (i32.const 7))
638
+ (then (f64.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))) (local.get $v)))
639
+ (else
640
+ (if (i32.eq (local.get $et) (i32.const 6))
641
+ (then (f32.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))) (f32.demote_f64 (local.get $v))))
642
+ (else
643
+ (local.set $bits
644
+ (i32.wrap_i64
645
+ (if (result i64) (f64.lt (local.get $v) (f64.const 0))
646
+ (then (i64.trunc_sat_f64_s (local.get $v)))
647
+ (else (i64.trunc_sat_f64_u (local.get $v))))))
648
+ (if (i32.ge_u (local.get $et) (i32.const 4))
649
+ (then (i32.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))) (local.get $bits)))
650
+ (else (if (i32.ge_u (local.get $et) (i32.const 2))
651
+ (then (i32.store16 (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))) (local.get $bits)))
652
+ (else (i32.store8 (i32.add (local.get $off) (local.get $i)) (local.get $bits))))))))))))
653
+ (local.get $v))`
654
+
618
655
  // Type-aware TypedArray read: arr[i]
619
656
  ctx.core.emit['.typed:[]'] = (arr, idx) => {
620
657
  const r = resolveElem(arr)
621
658
  if (r == null) return null // unknown type, fallback to generic
622
- const { et, isView } = r
659
+ const { et, isView, isBigInt } = r
623
660
  const objIR = emit(arr), vi = asI32(emit(idx))
624
661
  const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
662
+ if (isBigInt) return typed(['f64.reinterpret_i64', ['i64.load', off]], 'f64')
625
663
  if (et === 7) return typed(['f64.load', off], 'f64') // Float64Array
626
664
  if (et === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64') // Float32Array
627
665
  // Integer types: load and convert to f64 (unsigned types use unsigned conversion)
@@ -629,19 +667,111 @@ export default (ctx) => {
629
667
  }
630
668
 
631
669
  // Type-aware TypedArray write: arr[i] = val
632
- ctx.core.emit['.typed:[]='] = (arr, idx, val) => {
670
+ ctx.core.emit['.typed:[]='] = (arr, idx, val, void_ = false) => {
633
671
  const r = resolveElem(arr)
634
672
  if (r == null) return null
635
- const { et, isView } = r
636
- const objIR = emit(arr), vi = asI32(emit(idx)), vv = asF64(emit(val))
673
+ const { et, isView, isBigInt } = r
674
+ const objIR = emit(arr), vi = asI32(emit(idx)), valIR = emit(val)
637
675
  const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
638
- if (et === 7) return ['f64.store', off, vv] // Float64Array
639
- if (et === 6) return ['f32.store', off, ['f32.demote_f64', vv]] // Float32Array
640
- // Integer types: truncate f64 to i32, then store. Peel f64.convert_i32_*(x) → x:
641
- // store of bitwise-result already-i32 needs no round-trip.
642
- const isConv = Array.isArray(vv) && (vv[0] === 'f64.convert_i32_s' || vv[0] === 'f64.convert_i32_u')
643
- const i32val = isConv ? vv[1] : [(et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', vv]
644
- return [STORE[et], off, i32val]
676
+ if (isBigInt) {
677
+ const vt = temp('tw')
678
+ return typed(void_ ? ['block',
679
+ ['local.set', `$${vt}`, asF64(valIR)],
680
+ ['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]]]
681
+ : ['block', ['result', 'f64'],
682
+ ['local.set', `$${vt}`, asF64(valIR)],
683
+ ['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]],
684
+ ['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
685
+ }
686
+ if (et === 7) {
687
+ const vt = temp('tw')
688
+ return typed(void_ ? ['block',
689
+ ['local.set', `$${vt}`, asF64(valIR)],
690
+ ['f64.store', off, ['local.get', `$${vt}`]]]
691
+ : ['block', ['result', 'f64'],
692
+ ['local.set', `$${vt}`, asF64(valIR)],
693
+ ['f64.store', off, ['local.get', `$${vt}`]],
694
+ ['local.get', `$${vt}`]], void_ ? 'void' : 'f64') // Float64Array
695
+ }
696
+ if (et === 6) {
697
+ const vt = temp('tw')
698
+ return typed(void_ ? ['block',
699
+ ['local.set', `$${vt}`, asF64(valIR)],
700
+ ['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]]]
701
+ : ['block', ['result', 'f64'],
702
+ ['local.set', `$${vt}`, asF64(valIR)],
703
+ ['f32.store', off, ['f32.demote_f64', ['local.get', `$${vt}`]]],
704
+ ['local.get', `$${vt}`]], void_ ? 'void' : 'f64') // Float32Array
705
+ }
706
+ // Integer store: when the source is already i32-typed (bitwise ops, |0,
707
+ // typed-array load, known-i32 var), store it directly — skip the f64
708
+ // detour that costs a sign branch + i64 trunc + i32 wrap on every write.
709
+ if (valIR.type === 'i32') {
710
+ const cheap = Array.isArray(valIR) &&
711
+ ((valIR[0] === 'local.get' && typeof valIR[1] === 'string') ||
712
+ (valIR[0] === 'i32.const' && (typeof valIR[1] === 'number' || typeof valIR[1] === 'string')))
713
+ if (void_ && cheap) return typed([STORE[et], off, valIR], 'void')
714
+ const v32 = tempI32('tw')
715
+ return typed(void_ ? ['block',
716
+ ['local.set', `$${v32}`, valIR],
717
+ [STORE[et], off, ['local.get', `$${v32}`]]]
718
+ : ['block', ['result', 'f64'],
719
+ ['local.set', `$${v32}`, valIR],
720
+ [STORE[et], off, ['local.get', `$${v32}`]],
721
+ [(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', ['local.get', `$${v32}`]]], void_ ? 'void' : 'f64')
722
+ }
723
+ const vt = temp('tw')
724
+ const i32val = ['i32.wrap_i64',
725
+ ['if', ['result', 'i64'], ['f64.lt', ['local.get', `$${vt}`], ['f64.const', 0]],
726
+ ['then', ['i64.trunc_sat_f64_s', ['local.get', `$${vt}`]]],
727
+ ['else', ['i64.trunc_sat_f64_u', ['local.get', `$${vt}`]]]]]
728
+ return typed(void_ ? ['block',
729
+ ['local.set', `$${vt}`, asF64(valIR)],
730
+ [STORE[et], off, i32val]]
731
+ : ['block', ['result', 'f64'],
732
+ ['local.set', `$${vt}`, asF64(valIR)],
733
+ [STORE[et], off, i32val],
734
+ ['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
735
+ }
736
+
737
+ // TypedArray.prototype.set(source, offset = 0). Copies array-like numeric
738
+ // values into the receiver. Falls back to runtime aux-byte dispatch via
739
+ // __typed_set_idx when sibling-scope decls poison the dst's typedElem (e.g.
740
+ // v128const's i-branch ternary `num===16 ? Uint8Array : Uint32Array`
741
+ // hoisted alongside the f-branch's plain `new Uint8Array(16)`).
742
+ ctx.core.emit['.typed:set'] = (arr, src, offset) => {
743
+ const r = resolveElem(arr)
744
+ inc('__len', '__typed_idx')
745
+ if (r == null) inc('__typed_set_idx')
746
+ const srcVal = src === undefined ? undefExpr() : asF64(emit(src))
747
+ const offVal = offset === undefined ? typed(['i32.const', 0], 'i32') : asI32(emit(offset))
748
+ const dst = r ? tempI32('tsd') : temp('tsd')
749
+ const srcTmp = temp('tss'), len = tempI32('tsl'), off = tempI32('tso'), i = tempI32('tsi')
750
+ const idx = ['i32.add', ['local.get', `$${off}`], ['local.get', `$${i}`]]
751
+ const val = typed(['call', '$__typed_idx', ['i64.reinterpret_f64', ['local.get', `$${srcTmp}`]], ['local.get', `$${i}`]], 'f64')
752
+ let store
753
+ if (r) {
754
+ const { et } = r
755
+ const addr = ['i32.add', ['local.get', `$${dst}`], ['i32.shl', idx, ['i32.const', SHIFT[et]]]]
756
+ store = et === 7 ? ['f64.store', addr, val]
757
+ : et === 6 ? ['f32.store', addr, ['f32.demote_f64', val]]
758
+ : [STORE[et], addr, [(et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', val]]
759
+ } else {
760
+ store = ['drop', ['call', '$__typed_set_idx', ['i64.reinterpret_f64', ['local.get', `$${dst}`]], idx, val]]
761
+ }
762
+ const id = ctx.func.uniq++
763
+ return typed(['block', ['result', 'f64'],
764
+ ['local.set', `$${dst}`, r ? typedDataAddr(emit(arr), r.isView) : asF64(emit(arr))],
765
+ ['local.set', `$${srcTmp}`, srcVal],
766
+ ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${srcTmp}`]]]],
767
+ ['local.set', `$${off}`, offVal],
768
+ ['local.set', `$${i}`, ['i32.const', 0]],
769
+ ['block', `$brk${id}`, ['loop', `$loop${id}`,
770
+ ['br_if', `$brk${id}`, ['i32.ge_u', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
771
+ store,
772
+ ['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
773
+ ['br', `$loop${id}`]]],
774
+ undefExpr()], 'f64')
645
775
  }
646
776
 
647
777
  // .map() on TypedArrays — SIMD auto-vectorization when pattern detected
@@ -665,7 +795,7 @@ export default (ctx) => {
665
795
  if (wat) {
666
796
  ctx.core.stdlib[funcName] = wat
667
797
  inc(funcName, '__typed_data', '__len')
668
- return typed(['call', `$${funcName}`, asF64(emit(arr))], 'f64')
798
+ return typed(['call', `$${funcName}`, asI64(emit(arr))], 'f64')
669
799
  }
670
800
  }
671
801
  }
@@ -675,7 +805,7 @@ export default (ctx) => {
675
805
  const va = emit(arr), vf = emit(fn)
676
806
  const len = tempI32('tml'), ptr = tempI32('tmp'), i = tempI32('tmi')
677
807
  const stride = STRIDE[elemType], shift = SHIFT[elemType]
678
- const dst = allocPtr({ type: PTR.TYPED, aux: elemType,
808
+ const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(elemName),
679
809
  len: ['i32.shl', ['local.get', `$${len}`], ['i32.const', shift]], stride: 1, tag: 'tmo' })
680
810
  const out = dst.local
681
811
 
@@ -695,7 +825,7 @@ export default (ctx) => {
695
825
  const id = ctx.func.uniq++
696
826
  return typed(['block', ['result', 'f64'],
697
827
  ['local.set', `$${ptr}`, typedDataAddr(va, isView)],
698
- ['local.set', `$${len}`, ['call', '$__len', asF64(va)]],
828
+ ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', asF64(va)]]],
699
829
  dst.init,
700
830
  ['local.set', `$${i}`, ['i32.const', 0]],
701
831
  ['block', `$brk${id}`, ['loop', `$loop${id}`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jz",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Modern functional JS compiling to WASM",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -21,17 +21,10 @@
21
21
  },
22
22
  "scripts": {
23
23
  "test": "node test/index.js",
24
- "test:safe": "nice -n 15 node scripts/timebox.mjs 3000 node test/index.js",
25
- "test:segments": "nice -n 15 node scripts/test-segments.mjs 3000 files",
26
- "test:prefixes": "nice -n 15 node scripts/test-segments.mjs 3000 prefix",
27
24
  "test262": "node test/test262.js",
28
25
  "test262:builtins": "node test/test262-builtins.js",
29
- "test262:runner": "node test/runner.js",
30
- "dev": "node cli.js",
31
- "build": "node cli.js compile examples/*.js",
32
26
  "bench": "node bench/bench.mjs",
33
27
  "bench:compile": "node scripts/bench-compile.mjs",
34
- "bench:biquad": "node bench/bench.mjs biquad",
35
28
  "test:bench-pin": "node test/bench-pin.js"
36
29
  },
37
30
  "repository": {
@@ -45,7 +38,7 @@
45
38
  "author": "Dmitry Iv",
46
39
  "license": "MIT",
47
40
  "dependencies": {
48
- "subscript": "^10.3.5",
41
+ "subscript": "^10.4.2",
49
42
  "watr": "^4.5.1"
50
43
  },
51
44
  "keywords": [
@@ -57,6 +50,6 @@
57
50
  "wasm"
58
51
  ],
59
52
  "devDependencies": {
60
- "tst": "^9.2.0"
53
+ "tst": "^9.4.0"
61
54
  }
62
55
  }