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.
- package/README.md +256 -90
- package/cli.js +34 -8
- package/index.js +100 -14
- package/module/array.js +396 -133
- package/module/collection.js +455 -212
- package/module/console.js +169 -88
- package/module/core.js +246 -144
- package/module/date.js +783 -0
- package/module/function.js +81 -21
- package/module/index.js +2 -1
- package/module/json.js +483 -138
- package/module/math.js +79 -39
- package/module/number.js +188 -78
- package/module/object.js +227 -47
- package/module/regex.js +33 -30
- package/module/schema.js +14 -17
- package/module/string.js +439 -235
- package/module/symbol.js +4 -3
- package/module/timer.js +89 -31
- package/module/typedarray.js +174 -44
- package/package.json +3 -10
- package/src/analyze.js +627 -133
- package/src/autoload.js +15 -7
- package/src/compile.js +399 -70
- package/src/ctx.js +41 -12
- package/src/emit.js +658 -145
- package/src/host.js +244 -51
- package/src/ir.js +87 -31
- package/src/jzify.js +181 -24
- package/src/narrow.js +32 -4
- package/src/optimize.js +628 -42
- package/src/plan.js +1167 -4
- package/src/prepare.js +322 -75
- package/src/vectorize.js +1016 -0
- package/wasi.js +13 -0
- package/src/key.js +0 -73
- package/src/source.js +0 -76
package/module/typedarray.js
CHANGED
|
@@ -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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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)),
|
|
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 (
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
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}`,
|
|
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:
|
|
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.
|
|
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.
|
|
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.
|
|
53
|
+
"tst": "^9.4.0"
|
|
61
54
|
}
|
|
62
55
|
}
|