jz 0.1.0 → 0.2.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.
@@ -7,7 +7,9 @@
7
7
  * @module typed
8
8
  */
9
9
 
10
- import { emit, typed, asF64, asI32, valTypeOf, lookupValType, VAL, UNDEF_NAN, allocPtr, mkPtrIR, ptrOffsetIR, temp, tempI32 } from '../src/compile.js'
10
+ import { typed, asF64, asI32, asI64, UNDEF_NAN, allocPtr, mkPtrIR, ptrOffsetIR, temp, tempI32, undefExpr } from '../src/ir.js'
11
+ import { emit } from '../src/emit.js'
12
+ import { valTypeOf, lookupValType, VAL } from '../src/analyze.js'
11
13
  import { inc, PTR } from '../src/ctx.js'
12
14
 
13
15
 
@@ -19,6 +21,9 @@ const ELEM = {
19
21
  Float32Array: 6, Float64Array: 7,
20
22
  BigInt64Array: 7, BigUint64Array: 7,
21
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)
22
27
  const STRIDE = [1, 1, 2, 2, 4, 4, 4, 8]
23
28
  const SHIFT = [0, 0, 1, 1, 2, 2, 2, 3]
24
29
  const LOAD = [
@@ -163,7 +168,7 @@ function genSimdMap(name, elemType, pattern) {
163
168
  : `(local.set $ei (${scalarLoad} ${byteOff}))`
164
169
  const scalarStoreExpr = `${scalarLoadSet}\n (${store} ${dstByteOff} ${scalarOp})`
165
170
 
166
- return `(func $${name} (param $src f64) (result f64)
171
+ return `(func $${name} (param $src i64) (result f64)
167
172
  (local $len i32) (local $srcOff i32) (local $dstOff i32) (local $dst i32)
168
173
  (local $i i32) (local $simdLen i32) (local $byteOff i32)
169
174
  (local $v v128)
@@ -201,6 +206,7 @@ export default (ctx) => {
201
206
  __byte_length: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
202
207
  __byte_offset: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
203
208
  __to_buffer: ['__ptr_type', '__ptr_offset', '__ptr_aux', '__mkptr'],
209
+ __typed_set_idx: ['__ptr_aux', '__ptr_offset'],
204
210
  })
205
211
 
206
212
  // .map/.filter invoke callbacks with arity 1 internally.
@@ -214,7 +220,7 @@ export default (ctx) => {
214
220
  // __byte_length(ptr) — byte size for BUFFER/TYPED; 0 otherwise.
215
221
  // BUFFER and owned TYPED store byteLen at [-8]. TYPED view (aux bit 3) stores byteLen
216
222
  // at descriptor[0].
217
- 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)
218
224
  (local $t i32) (local $off i32)
219
225
  (local.set $t (call $__ptr_type (local.get $ptr)))
220
226
  (if (result i32)
@@ -236,11 +242,11 @@ export default (ctx) => {
236
242
  // Owned TYPED: retag as BUFFER at same offset — the byteLen header is shared.
237
243
  // TYPED view: retag as BUFFER at the parent data offset (descriptor[8]) — reconstructs
238
244
  // the root ArrayBuffer so its own header supplies byteLength.
239
- 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)
240
246
  (local $t i32) (local $off i32)
241
247
  (local.set $t (call $__ptr_type (local.get $ptr)))
242
248
  (if (result f64) (i32.eq (local.get $t) (i32.const ${PTR.BUFFER}))
243
- (then (local.get $ptr))
249
+ (then (f64.reinterpret_i64 (local.get $ptr)))
244
250
  (else
245
251
  (local.set $off (call $__ptr_offset (local.get $ptr)))
246
252
  (if (result f64)
@@ -253,6 +259,7 @@ export default (ctx) => {
253
259
 
254
260
  // Constructor: new Float64Array(len) | new F64Array(arr) | new F64Array(buf) | new F64Array(buf, off, len)
255
261
  for (const [name, elemType] of Object.entries(ELEM)) {
262
+ const aux = typedAux(name)
256
263
  const stride = STRIDE[elemType]
257
264
  ctx.core.emit[`new.${name}`] = (lenExpr, offsetExpr, lenExpr2) => {
258
265
  ctx.features.typedarray = true
@@ -278,7 +285,7 @@ export default (ctx) => {
278
285
  ['i32.store',
279
286
  ['i32.add', ['local.get', `$${dst}`], ['i32.const', 8]],
280
287
  ['local.get', `$${parentOff}`]],
281
- mkPtrIR(PTR.TYPED, elemType | 8, ['local.get', `$${dst}`])], 'f64')
288
+ mkPtrIR(PTR.TYPED, typedAux(name, true), ['local.get', `$${dst}`])], 'f64')
282
289
  }
283
290
  // Single arg array-like source: copy elements instead of treating the pointer as a length.
284
291
  if (srcType === VAL.ARRAY && ctx.core.emit[`${name}.from`])
@@ -287,7 +294,7 @@ export default (ctx) => {
287
294
  // TYPED retagged at the same offset — the byteLen header is shared with the parent.
288
295
  // __len(view) = byteLen >> shift computes elemCount for this view's elemType.
289
296
  if (srcType === VAL.BUFFER || srcType === VAL.TYPED) {
290
- 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))]])
291
298
  }
292
299
  if (srcType == null && ctx.core.emit[`${name}.from`]) {
293
300
  // Runtime dispatch: number → allocate; array → copy elements; buffer/typed → zero-copy view.
@@ -295,7 +302,7 @@ export default (ctx) => {
295
302
  const len = tempI32('tl')
296
303
  const shift = SHIFT[elemType]
297
304
  const numBytes = ['i32.shl', ['local.get', `$${len}`], ['i32.const', shift]]
298
- 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' })
299
306
  return typed(['block', ['result', 'f64'],
300
307
  ['local.set', `$${src}`, asF64(emit(lenExpr))],
301
308
  ['if', ['result', 'f64'],
@@ -307,15 +314,15 @@ export default (ctx) => {
307
314
  numAlloc.ptr]],
308
315
  // Pointer: array → copy elements; buffer/typed → zero-copy view on same offset
309
316
  ['else', ['if', ['result', 'f64'],
310
- ['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]],
311
318
  ['then', ctx.core.emit[`${name}.from`](src)],
312
- ['else', mkPtrIR(PTR.TYPED, elemType,
313
- ['call', '$__ptr_offset', ['local.get', `$${src}`]])]]]]], 'f64')
319
+ ['else', mkPtrIR(PTR.TYPED, aux,
320
+ ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${src}`]]])]]]]], 'f64')
314
321
  }
315
322
  // Normal: allocate fresh typed array (lenExpr is numeric size). Header stores byteLen.
316
323
  const shift = SHIFT[elemType]
317
324
  const lenL = tempI32('tan')
318
- const out = allocPtr({ type: PTR.TYPED, aux: elemType,
325
+ const out = allocPtr({ type: PTR.TYPED, aux,
319
326
  len: ['i32.shl', ['local.get', `$${lenL}`], ['i32.const', shift]], stride: 1, tag: 'ta' })
320
327
  return typed(['block', ['result', 'f64'],
321
328
  ['local.set', `$${lenL}`, asI32(emit(lenExpr))],
@@ -343,7 +350,7 @@ export default (ctx) => {
343
350
  ctx.core.emit['BigInt64Array'] = (bufExpr) => {
344
351
  ctx.features.typedarray = true
345
352
  const va = asF64(emit(bufExpr))
346
- return mkPtrIR(PTR.TYPED, 7, ['call', '$__ptr_offset', va])
353
+ return mkPtrIR(PTR.TYPED, typedAux('BigInt64Array'), ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]])
347
354
  }
348
355
 
349
356
  // .buffer — always aliased (zero-copy). BUFFER/DataView: passthrough.
@@ -365,7 +372,7 @@ export default (ctx) => {
365
372
  }
366
373
  }
367
374
  inc('__to_buffer')
368
- return typed(['call', '$__to_buffer', asF64(emit(obj))], 'f64')
375
+ return typed(['call', '$__to_buffer', asI64(emit(obj))], 'f64')
369
376
  }
370
377
 
371
378
  // .byteLength — BUFFER: raw __len. Owned TYPED: elemCount * stride. View TYPED: descriptor[0].
@@ -373,7 +380,7 @@ export default (ctx) => {
373
380
  if (typeof obj === 'string') {
374
381
  const ctor = ctx.types.typedElem?.get(obj)
375
382
  if (ctor === 'new.ArrayBuffer' || ctor === 'new.DataView') {
376
- 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')
377
384
  }
378
385
  if (ctor && ctor.startsWith('new.')) {
379
386
  const isView = ctor.endsWith('.view')
@@ -385,12 +392,12 @@ export default (ctx) => {
385
392
  ['i32.load', ptrOffsetIR(emit(obj), VAL.TYPED)]], 'f64')
386
393
  }
387
394
  return typed(['f64.convert_i32_s',
388
- ['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')
389
396
  }
390
397
  }
391
398
  }
392
399
  inc('__byte_length')
393
- 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')
394
401
  }
395
402
 
396
403
  // .byteOffset — owned: 0. View: descriptor[4] - descriptor[8].
@@ -409,11 +416,11 @@ export default (ctx) => {
409
416
  if (ctor?.startsWith('new.') && ELEM[ctor.slice(4)] != null) return typed(['f64.const', 0], 'f64')
410
417
  }
411
418
  inc('__byte_offset')
412
- 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')
413
420
  }
414
421
 
415
422
  // Runtime fallback for .byteOffset when variable view-ness is unknown.
416
- 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)
417
424
  (local $off i32)
418
425
  (if (result i32)
419
426
  (i32.and
@@ -429,9 +436,10 @@ export default (ctx) => {
429
436
  // ArrayBuffer.isView(x) — true iff x is a TYPED pointer. (DataView passthrough cannot be
430
437
  // distinguished from ArrayBuffer since both are BUFFER pointers; both report false.)
431
438
  ctx.core.emit['ArrayBuffer.isView'] = (v) => {
439
+ if (v === undefined) return typed(['f64.const', 0], 'f64')
432
440
  const va = asF64(emit(v))
433
441
  return typed(['f64.convert_i32_s',
434
- ['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')
435
443
  }
436
444
 
437
445
  // buf.slice(begin?, end?) on a BUFFER → fresh BUFFER with the byte range copied.
@@ -444,7 +452,7 @@ export default (ctx) => {
444
452
  const out = allocPtr({ type: PTR.BUFFER, len: ['local.get', `$${bytes}`], stride: 1, tag: 'bsd' })
445
453
  const beginWat = beginExpr == null ? ['i32.const', 0] : asI32(emit(beginExpr))
446
454
  const endWat = endExpr == null
447
- ? ['call', '$__len', ['local.get', `$${src}`]]
455
+ ? ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]
448
456
  : asI32(emit(endExpr))
449
457
  return typed(['block', ['result', 'f64'],
450
458
  ['local.set', `$${src}`, asF64(emit(obj))],
@@ -457,7 +465,7 @@ export default (ctx) => {
457
465
  out.init,
458
466
  ['memory.copy',
459
467
  ['local.get', `$${out.local}`],
460
- ['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}`]],
461
469
  ['local.get', `$${bytes}`]],
462
470
  out.ptr], 'f64')
463
471
  }
@@ -505,12 +513,13 @@ export default (ctx) => {
505
513
 
506
514
  // TypedArray.from(arr) — convert regular array to typed array
507
515
  for (const [name, elemType] of Object.entries(ELEM)) {
516
+ const aux = typedAux(name)
508
517
  const stride = STRIDE[elemType], store = STORE[elemType]
509
518
  ctx.core.emit[`${name}.from`] = (src) => {
510
519
  ctx.features.typedarray = true
511
520
  const srcL = temp('tfs')
512
521
  const len = tempI32('tfl'), i = tempI32('tfi'), off = tempI32('tfo')
513
- const out = allocPtr({ type: PTR.TYPED, aux: elemType,
522
+ const out = allocPtr({ type: PTR.TYPED, aux,
514
523
  len: ['i32.mul', ['local.get', `$${len}`], ['i32.const', stride]], stride: 1, tag: 'tf' })
515
524
  const t = out.local
516
525
  const id = ctx.func.uniq++
@@ -526,8 +535,8 @@ export default (ctx) => {
526
535
  ['f64.load', ['i32.add', ['local.get', `$${off}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]]]
527
536
  return typed(['block', ['result', 'f64'],
528
537
  ['local.set', `$${srcL}`, asF64(emit(src))],
529
- ['local.set', `$${off}`, ['call', '$__ptr_offset', ['local.get', `$${srcL}`]]],
530
- ['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}`]]]],
531
540
  out.init,
532
541
  ['local.set', `$${i}`, ['i32.const', 0]],
533
542
  ['block', `$brk${id}`, ['loop', `$loop${id}`,
@@ -542,14 +551,14 @@ export default (ctx) => {
542
551
  // .length handled by ptr.js's __len (reads from memory header [-8:len])
543
552
 
544
553
  /** Resolve element type + view-ness for a known TypedArray variable.
545
- * Returns { et, isView } or null. */
554
+ * Returns { et, isView, isBigInt } or null. */
546
555
  const resolveElem = (arr) => {
547
556
  const ctor = typeof arr === 'string' && ctx.types.typedElem?.get(arr)
548
557
  if (!ctor) return null
549
558
  const isView = ctor.endsWith('.view')
550
559
  const name = isView ? ctor.slice(4, -5) : ctor.slice(4)
551
560
  const et = ELEM[name]
552
- return et == null ? null : { et, isView }
561
+ return et == null ? null : { et, isView, isBigInt: name === 'BigInt64Array' || name === 'BigUint64Array' }
553
562
  }
554
563
 
555
564
  /** Emit the real data byte-address for a typed array IR node.
@@ -566,7 +575,7 @@ export default (ctx) => {
566
575
  // Identical factory in array.js; whichever module loads last wins the registration.
567
576
  ctx.core.stdlib['__typed_idx'] = () => {
568
577
  if (!ctx.features.typedarray && !ctx.features.external) {
569
- 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)
570
579
  (local $len i32)
571
580
  (local.set $len (call $__len (local.get $ptr)))
572
581
  (if (result f64)
@@ -576,7 +585,7 @@ export default (ctx) => {
576
585
  (then (f64.const nan:${UNDEF_NAN}))
577
586
  (else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
578
587
  }
579
- 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)
580
589
  (local $off i32) (local $et i32) (local $len i32) (local $aux i32)
581
590
  (local.set $aux (call $__ptr_aux (local.get $ptr)))
582
591
  (local.set $off (call $__ptr_offset (local.get $ptr)))
@@ -597,7 +606,9 @@ export default (ctx) => {
597
606
  (local.set $et (i32.and (local.get $aux) (i32.const 7)))
598
607
  (if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
599
608
  (then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
600
- (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)))))))
601
612
  (else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
602
613
  (else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
603
614
  (then (if (result f64) (i32.and (local.get $et) (i32.const 1))
@@ -613,13 +624,42 @@ export default (ctx) => {
613
624
  (else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))))`
614
625
  }
615
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
+
616
655
  // Type-aware TypedArray read: arr[i]
617
656
  ctx.core.emit['.typed:[]'] = (arr, idx) => {
618
657
  const r = resolveElem(arr)
619
658
  if (r == null) return null // unknown type, fallback to generic
620
- const { et, isView } = r
659
+ const { et, isView, isBigInt } = r
621
660
  const objIR = emit(arr), vi = asI32(emit(idx))
622
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')
623
663
  if (et === 7) return typed(['f64.load', off], 'f64') // Float64Array
624
664
  if (et === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64') // Float32Array
625
665
  // Integer types: load and convert to f64 (unsigned types use unsigned conversion)
@@ -627,19 +667,111 @@ export default (ctx) => {
627
667
  }
628
668
 
629
669
  // Type-aware TypedArray write: arr[i] = val
630
- ctx.core.emit['.typed:[]='] = (arr, idx, val) => {
670
+ ctx.core.emit['.typed:[]='] = (arr, idx, val, void_ = false) => {
631
671
  const r = resolveElem(arr)
632
672
  if (r == null) return null
633
- const { et, isView } = r
634
- 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)
635
675
  const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
636
- if (et === 7) return ['f64.store', off, vv] // Float64Array
637
- if (et === 6) return ['f32.store', off, ['f32.demote_f64', vv]] // Float32Array
638
- // Integer types: truncate f64 to i32, then store. Peel f64.convert_i32_*(x) → x:
639
- // store of bitwise-result already-i32 needs no round-trip.
640
- const isConv = Array.isArray(vv) && (vv[0] === 'f64.convert_i32_s' || vv[0] === 'f64.convert_i32_u')
641
- const i32val = isConv ? vv[1] : [(et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', vv]
642
- 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')
643
775
  }
644
776
 
645
777
  // .map() on TypedArrays — SIMD auto-vectorization when pattern detected
@@ -663,7 +795,7 @@ export default (ctx) => {
663
795
  if (wat) {
664
796
  ctx.core.stdlib[funcName] = wat
665
797
  inc(funcName, '__typed_data', '__len')
666
- return typed(['call', `$${funcName}`, asF64(emit(arr))], 'f64')
798
+ return typed(['call', `$${funcName}`, asI64(emit(arr))], 'f64')
667
799
  }
668
800
  }
669
801
  }
@@ -673,7 +805,7 @@ export default (ctx) => {
673
805
  const va = emit(arr), vf = emit(fn)
674
806
  const len = tempI32('tml'), ptr = tempI32('tmp'), i = tempI32('tmi')
675
807
  const stride = STRIDE[elemType], shift = SHIFT[elemType]
676
- const dst = allocPtr({ type: PTR.TYPED, aux: elemType,
808
+ const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(elemName),
677
809
  len: ['i32.shl', ['local.get', `$${len}`], ['i32.const', shift]], stride: 1, tag: 'tmo' })
678
810
  const out = dst.local
679
811
 
@@ -693,7 +825,7 @@ export default (ctx) => {
693
825
  const id = ctx.func.uniq++
694
826
  return typed(['block', ['result', 'f64'],
695
827
  ['local.set', `$${ptr}`, typedDataAddr(va, isView)],
696
- ['local.set', `$${len}`, ['call', '$__len', asF64(va)]],
828
+ ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', asF64(va)]]],
697
829
  dst.init,
698
830
  ['local.set', `$${i}`, ['i32.const', 0]],
699
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.0",
3
+ "version": "0.2.0",
4
4
  "description": "Modern functional JS compiling to WASM",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -21,15 +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
- "test262:runner": "node test/runner.js",
29
- "dev": "node cli.js",
30
- "build": "node cli.js compile examples/*.js",
25
+ "test262:builtins": "node test/test262-builtins.js",
31
26
  "bench": "node bench/bench.mjs",
32
- "bench:biquad": "node bench/bench.mjs biquad",
27
+ "bench:compile": "node scripts/bench-compile.mjs",
33
28
  "test:bench-pin": "node test/bench-pin.js"
34
29
  },
35
30
  "repository": {
@@ -43,7 +38,7 @@
43
38
  "author": "Dmitry Iv",
44
39
  "license": "MIT",
45
40
  "dependencies": {
46
- "subscript": "^10.3.3",
41
+ "subscript": "^10.4.2",
47
42
  "watr": "^4.5.1"
48
43
  },
49
44
  "keywords": [
@@ -55,6 +50,6 @@
55
50
  "wasm"
56
51
  ],
57
52
  "devDependencies": {
58
- "tst": "^9.2.0"
53
+ "tst": "^9.4.0"
59
54
  }
60
55
  }