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.
- package/README.md +221 -62
- package/cli.js +34 -8
- package/index.js +133 -14
- package/module/array.js +399 -131
- package/module/collection.js +457 -212
- package/module/console.js +170 -87
- package/module/core.js +247 -143
- package/module/date.js +691 -0
- package/module/function.js +84 -23
- package/module/index.js +2 -1
- package/module/json.js +485 -138
- package/module/math.js +81 -40
- package/module/number.js +189 -83
- package/module/object.js +228 -46
- package/module/regex.js +34 -30
- package/module/schema.js +17 -18
- package/module/string.js +483 -227
- package/module/symbol.js +6 -4
- package/module/timer.js +90 -32
- package/module/typedarray.js +176 -44
- package/package.json +5 -10
- package/src/analyze.js +639 -124
- package/src/autoload.js +183 -0
- package/src/compile.js +448 -1083
- package/src/ctx.js +42 -12
- package/src/emit.js +646 -147
- package/src/host.js +273 -68
- package/src/ir.js +81 -31
- package/src/jzify.js +220 -36
- package/src/narrow.js +956 -0
- package/src/optimize.js +628 -42
- package/src/plan.js +1233 -0
- package/src/prepare.js +438 -256
- package/src/vectorize.js +1016 -0
- package/wasi.js +23 -4
package/module/typedarray.js
CHANGED
|
@@ -7,7 +7,9 @@
|
|
|
7
7
|
* @module typed
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
import {
|
|
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
|
|
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
|
|
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
|
|
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,
|
|
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,
|
|
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
|
|
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,
|
|
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
|
|
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,
|
|
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',
|
|
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',
|
|
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',
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
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)),
|
|
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 (
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
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}`,
|
|
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:
|
|
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.
|
|
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:
|
|
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:
|
|
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.
|
|
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.
|
|
53
|
+
"tst": "^9.4.0"
|
|
59
54
|
}
|
|
60
55
|
}
|