jz 0.7.0 → 0.8.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 +37 -33
- package/bench/README.md +176 -73
- package/bench/bench.svg +58 -71
- package/cli.js +12 -5
- package/dist/interop.js +1 -1
- package/dist/jz.js +1366 -1101
- package/index.js +40 -9
- package/interop.js +193 -138
- package/layout.js +29 -18
- package/module/array.js +49 -73
- package/module/collection.js +83 -25
- package/module/console.js +1 -1
- package/module/core.js +161 -15
- package/module/json.js +3 -3
- package/module/math.js +167 -117
- package/module/number.js +247 -13
- package/module/object.js +11 -5
- package/module/regex.js +8 -7
- package/module/string.js +295 -171
- package/module/typedarray.js +169 -105
- package/package.json +7 -3
- package/src/abi/string.js +40 -35
- package/src/ast.js +19 -2
- package/src/compile/analyze.js +64 -2
- package/src/compile/cse-load.js +200 -0
- package/src/compile/emit-assign.js +73 -14
- package/src/compile/emit.js +324 -34
- package/src/compile/index.js +204 -61
- package/src/compile/infer.js +8 -1
- package/src/compile/loop-divmod.js +12 -58
- package/src/compile/loop-model.js +91 -0
- package/src/compile/loop-recurrence.js +167 -0
- package/src/compile/loop-square.js +102 -0
- package/src/compile/narrow.js +180 -34
- package/src/compile/peel-stencil.js +18 -64
- package/src/compile/plan/common.js +29 -0
- package/src/compile/plan/index.js +4 -1
- package/src/compile/plan/inline.js +176 -21
- package/src/compile/plan/literals.js +93 -19
- package/src/ctx.js +51 -12
- package/src/helper-counters.js +137 -0
- package/src/ir.js +102 -13
- package/src/kind-traits.js +7 -3
- package/src/kind.js +14 -1
- package/src/op-policy.js +5 -2
- package/src/ops.js +119 -0
- package/src/optimize/index.js +1125 -136
- package/src/optimize/recurse.js +182 -0
- package/src/optimize/vectorize.js +1302 -144
- package/src/prepare/index.js +29 -12
- package/src/reps.js +4 -1
- package/src/type.js +53 -45
- package/src/wat/assemble.js +92 -9
- package/src/widen.js +21 -0
- package/src/wat/optimize.js +0 -3938
package/layout.js
CHANGED
|
@@ -46,6 +46,24 @@ export const ATOM = { NULL: 1, UNDEF: 2, FALSE: 4, TRUE: 5 }
|
|
|
46
46
|
* tests membership in one shl+and, replacing a 4-way tag-equality OR. */
|
|
47
47
|
export const FORWARDING_MASK = (1 << PTR.ARRAY) | (1 << PTR.HASH) | (1 << PTR.SET) | (1 << PTR.MAP)
|
|
48
48
|
|
|
49
|
+
// BigInt views of the NaN-box fields — the carrier is 64-bit, JS bit-ops are 32-bit.
|
|
50
|
+
const TAG_SHIFT = BigInt(LAYOUT.TAG_SHIFT), TAG_MASK = BigInt(LAYOUT.TAG_MASK)
|
|
51
|
+
const AUX_SHIFT = BigInt(LAYOUT.AUX_SHIFT), AUX_MASK = BigInt(LAYOUT.AUX_MASK)
|
|
52
|
+
const OFFSET_MASK = BigInt(LAYOUT.OFFSET_MASK)
|
|
53
|
+
|
|
54
|
+
/** Format an i64 BigInt as a zero-padded `0x…` hex literal for WAT/IR templates. */
|
|
55
|
+
export const i64Hex = bits => '0x' + bits.toString(16).toUpperCase().padStart(16, '0')
|
|
56
|
+
|
|
57
|
+
/** Pack (type, aux, offset) into the i64 NaN-box carrier — the single source of
|
|
58
|
+
* truth for pointer bit layout. Compiler IR (`packPtrBits`/`mkPtrIR`), the
|
|
59
|
+
* `$__mkptr` inline specializer, and the interop high-word encoder all derive
|
|
60
|
+
* from this. `offset` defaults to 0 to yield the box prefix (offset OR'd later). */
|
|
61
|
+
export const ptrBits = (type, aux = 0, offset = 0) =>
|
|
62
|
+
LAYOUT.NAN_PREFIX_BITS
|
|
63
|
+
| ((BigInt(type) & TAG_MASK) << TAG_SHIFT)
|
|
64
|
+
| ((BigInt(aux) & AUX_MASK) << AUX_SHIFT)
|
|
65
|
+
| (BigInt(offset >>> 0) & OFFSET_MASK)
|
|
66
|
+
|
|
49
67
|
// =============================================================================
|
|
50
68
|
// PTR.TYPED element-type aux codec — which typed-array flavor lives in the aux
|
|
51
69
|
// field of a PTR.TYPED box. Pure (no compiler state) → lives with the NaN-box
|
|
@@ -94,20 +112,19 @@ export function ctorFromElemAux(aux) {
|
|
|
94
112
|
return isView ? `new.${name}.view` : `new.${name}`
|
|
95
113
|
}
|
|
96
114
|
|
|
97
|
-
/** Host-side high u32 word for NaN-boxed f64 pointer encoding (interop)
|
|
115
|
+
/** Host-side high u32 word for NaN-boxed f64 pointer encoding (interop) — the
|
|
116
|
+
* top 32 bits of `ptrBits(type, aux)` (offset lives in the low word). */
|
|
98
117
|
export const encodePtrHi = (type, aux) =>
|
|
99
|
-
(
|
|
118
|
+
((LAYOUT.NAN_PREFIX << 16) | ((type & LAYOUT.TAG_MASK) << (LAYOUT.TAG_SHIFT - 32)) | (aux & LAYOUT.AUX_MASK)) >>> 0
|
|
100
119
|
|
|
101
|
-
export const decodePtrType = hi => (hi >>>
|
|
102
|
-
export const decodePtrAux = hi => hi &
|
|
120
|
+
export const decodePtrType = hi => (hi >>> (LAYOUT.TAG_SHIFT - 32)) & LAYOUT.TAG_MASK
|
|
121
|
+
export const decodePtrAux = hi => hi & LAYOUT.AUX_MASK
|
|
103
122
|
|
|
104
123
|
/** i64 NaN-prefix OR-mask for WAT `(i64.const …)` templates. */
|
|
105
|
-
export const nanPrefixHex = () =>
|
|
106
|
-
'0x' + LAYOUT.NAN_PREFIX_BITS.toString(16).toUpperCase().padStart(16, '0')
|
|
124
|
+
export const nanPrefixHex = () => i64Hex(LAYOUT.NAN_PREFIX_BITS)
|
|
107
125
|
|
|
108
126
|
/** Atom sentinel as i64 hex (compiler WAT templates). */
|
|
109
|
-
export const atomNanHex = atomId =>
|
|
110
|
-
'0x' + (LAYOUT.NAN_PREFIX_BITS | (BigInt(atomId) << BigInt(LAYOUT.AUX_SHIFT))).toString(16).toUpperCase().padStart(16, '0')
|
|
127
|
+
export const atomNanHex = atomId => i64Hex(LAYOUT.NAN_PREFIX_BITS | (BigInt(atomId) << AUX_SHIFT))
|
|
111
128
|
|
|
112
129
|
/** STRING aux bit 0 on a PLAIN-HEAP string (SSO and SLICE clear): this is a
|
|
113
130
|
* CANONICAL interned string — the static-pool copy (or an intern-table hit
|
|
@@ -120,22 +137,16 @@ export const atomNanHex = atomId =>
|
|
|
120
137
|
export const STR_INTERN_BIT = 0x1
|
|
121
138
|
|
|
122
139
|
/** Pre-shifted STRING SSO aux bit as i64 hex. */
|
|
123
|
-
export const ssoBitI64Hex = () =>
|
|
124
|
-
'0x' + (BigInt(LAYOUT.SSO_BIT) << BigInt(LAYOUT.AUX_SHIFT)).toString(16).toUpperCase().padStart(16, '0')
|
|
140
|
+
export const ssoBitI64Hex = () => i64Hex(BigInt(LAYOUT.SSO_BIT) << AUX_SHIFT)
|
|
125
141
|
|
|
126
142
|
/** Pre-shifted STRING slice/view aux bit as i64 hex. */
|
|
127
|
-
export const sliceBitI64Hex = () =>
|
|
128
|
-
'0x' + (BigInt(LAYOUT.SLICE_BIT) << BigInt(LAYOUT.AUX_SHIFT)).toString(16).toUpperCase().padStart(16, '0')
|
|
143
|
+
export const sliceBitI64Hex = () => i64Hex(BigInt(LAYOUT.SLICE_BIT) << AUX_SHIFT)
|
|
129
144
|
|
|
130
145
|
/** Full i64 NaN-box hex for `(i64.const …)` — ptr type + aux, offset OR'd separately. */
|
|
131
|
-
export const ptrNanHex = (ptrType, aux = 0) =>
|
|
132
|
-
'0x' + ptrBoxPrefixBigInt(ptrType, aux).toString(16).toUpperCase().padStart(16, '0')
|
|
146
|
+
export const ptrNanHex = (ptrType, aux = 0) => i64Hex(ptrBits(ptrType, aux))
|
|
133
147
|
|
|
134
148
|
/** Compile-time i64 prefix for mkPtrIR (before offset OR). */
|
|
135
|
-
export const ptrBoxPrefixBigInt = (ptrType, aux = 0) =>
|
|
136
|
-
(0x7FF8n << 48n)
|
|
137
|
-
| ((BigInt(ptrType) & 0xFn) << 47n)
|
|
138
|
-
| ((BigInt(aux) & 0x7FFFn) << 32n)
|
|
149
|
+
export const ptrBoxPrefixBigInt = (ptrType, aux = 0) => ptrBits(ptrType, aux)
|
|
139
150
|
|
|
140
151
|
/** Host-side atom sentinel high-u32 values (interop f64 decode). */
|
|
141
152
|
export const ATOM_HI = {
|
package/module/array.js
CHANGED
|
@@ -13,7 +13,7 @@ import { inBoundsArrIdx } from '../src/type.js'
|
|
|
13
13
|
import { emit, spread, deps, idx as emitIndex } from '../src/bridge.js'
|
|
14
14
|
import { valTypeOf } from '../src/kind.js'
|
|
15
15
|
import { extractParams, classifyParam, ASSIGN_OPS, refsName, REFS_IN_EXPR } from '../src/ast.js'
|
|
16
|
-
import { staticPropertyKey, staticObjectProps, inlineArraySid, staticIndexKey } from '../src/static.js'
|
|
16
|
+
import { staticPropertyKey, staticObjectProps, inlineArraySid, staticIndexKey, intLiteralValue } from '../src/static.js'
|
|
17
17
|
import { VAL, lookupValType, lookupNotString, updateRep } from '../src/reps.js'
|
|
18
18
|
import { structInline } from '../src/abi/index.js'
|
|
19
19
|
import { ctx, inc, err, warnDeopt, PTR, LAYOUT, followForwardingWat } from '../src/ctx.js'
|
|
@@ -249,9 +249,9 @@ export default (ctx) => {
|
|
|
249
249
|
'__ptr_offset',
|
|
250
250
|
...(needsArrayDynMove() ? ['__dyn_move', '__hash_new', '__ihash_set_local'] : []),
|
|
251
251
|
],
|
|
252
|
-
__arr_fill: ['__ptr_offset'],
|
|
252
|
+
__arr_fill: ['__ptr_offset', '__clamp_idx'], // body-calls __clamp_idx; declare it (self-host auto-scan can't be relied on — see test/selfhost-includes.js)
|
|
253
253
|
__arr_set_idx_ptr: ['__arr_grow', '__ptr_offset'],
|
|
254
|
-
__arr_push1: ['__arr_grow_known', '
|
|
254
|
+
__arr_push1: ['__arr_grow_known', '__ptr_offset_fwd'],
|
|
255
255
|
__arr_set_length: ['__arr_grow_known', '__ptr_offset', '__ptr_type'],
|
|
256
256
|
__arr_unshift: ['__arr_grow', '__len', '__ptr_offset'],
|
|
257
257
|
__arr_splice: ['__arr_grow', '__len', '__ptr_offset', '__alloc_hdr', '__mkptr'],
|
|
@@ -333,67 +333,6 @@ export default (ctx) => {
|
|
|
333
333
|
// Full body handles TYPED element types and view indirection since external host can
|
|
334
334
|
// pass typed arrays even when typedarray module isn't loaded. When features.typedarray
|
|
335
335
|
// and features.external are both off, collapses to ARRAY-only f64 indexing.
|
|
336
|
-
ctx.core.stdlib['__typed_idx'] = () => {
|
|
337
|
-
if (!ctx.features.typedarray && !ctx.features.external) {
|
|
338
|
-
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
339
|
-
(local $len i32)
|
|
340
|
-
(local.set $len (call $__len (local.get $ptr)))
|
|
341
|
-
(if (result f64)
|
|
342
|
-
(i32.or
|
|
343
|
-
(i32.lt_s (local.get $i) (i32.const 0))
|
|
344
|
-
(i32.ge_u (local.get $i) (local.get $len)))
|
|
345
|
-
(then (f64.const nan:${UNDEF_NAN}))
|
|
346
|
-
(else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
|
|
347
|
-
}
|
|
348
|
-
// Hot (~37M calls in watr self-host). Type/aux/offset extracted once from $ptr.
|
|
349
|
-
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
350
|
-
(local $t i32) (local $off i32) (local $et i32) (local $len i32) (local $aux i32)
|
|
351
|
-
(local.set $t (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
352
|
-
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
353
|
-
;; ARRAY fast path: follow forwarding inline, bounds-check against header len, f64.load — no $__len call.
|
|
354
|
-
(if (i32.and (i32.eq (local.get $t) (i32.const ${PTR.ARRAY})) (i32.ge_u (local.get $off) (i32.const 8)))
|
|
355
|
-
(then
|
|
356
|
-
${followForwardingWat('$off', { lowGuard: false })}
|
|
357
|
-
(return (if (result f64)
|
|
358
|
-
(i32.and (i32.ge_s (local.get $i) (i32.const 0)) (i32.lt_u (local.get $i) (i32.load (i32.sub (local.get $off) (i32.const 8)))))
|
|
359
|
-
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
360
|
-
(else (f64.const nan:${UNDEF_NAN}))))))
|
|
361
|
-
(local.set $aux (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK}))))
|
|
362
|
-
(if
|
|
363
|
-
(i32.and
|
|
364
|
-
(i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
365
|
-
(i32.ne (i32.and (local.get $aux) (i32.const 8)) (i32.const 0)))
|
|
366
|
-
(then (local.set $off (i32.load (i32.add (local.get $off) (i32.const 4))))))
|
|
367
|
-
(local.set $len (call $__len (local.get $ptr)))
|
|
368
|
-
(if (result f64)
|
|
369
|
-
(i32.or
|
|
370
|
-
(i32.lt_s (local.get $i) (i32.const 0))
|
|
371
|
-
(i32.ge_u (local.get $i) (local.get $len)))
|
|
372
|
-
(then (f64.const nan:${UNDEF_NAN}))
|
|
373
|
-
(else
|
|
374
|
-
(if (result f64) (i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
375
|
-
(then
|
|
376
|
-
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
377
|
-
(if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
|
|
378
|
-
(then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
|
|
379
|
-
(then (if (result f64) (i32.and (local.get $aux) (i32.const 16))
|
|
380
|
-
(then (f64.reinterpret_i64 (i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
|
|
381
|
-
(else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))
|
|
382
|
-
(else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
383
|
-
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
|
|
384
|
-
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
385
|
-
(then (f64.convert_i32_u (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
|
|
386
|
-
(else (f64.convert_i32_s (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
387
|
-
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 2))
|
|
388
|
-
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
389
|
-
(then (f64.convert_i32_u (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
|
|
390
|
-
(else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))))
|
|
391
|
-
(else (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
392
|
-
(then (f64.convert_i32_u (i32.load8_u (i32.add (local.get $off) (local.get $i)))))
|
|
393
|
-
(else (f64.convert_i32_s (i32.load8_s (i32.add (local.get $off) (local.get $i)))))))))))))
|
|
394
|
-
(else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))))`
|
|
395
|
-
}
|
|
396
|
-
|
|
397
336
|
// Array.from(src) — shallow copy of array (memory.copy of f64 elements)
|
|
398
337
|
ctx.core.stdlib['__arr_from'] = `(func $__arr_from (param $src i64) (result f64)
|
|
399
338
|
(local $len i32) (local $dst i32)
|
|
@@ -576,12 +515,13 @@ export default (ctx) => {
|
|
|
576
515
|
ctx.core.stdlib['__arr_push1'] = `(func $__arr_push1 (param $ptr i64) (param $val f64) (result f64)
|
|
577
516
|
(local $p f64) (local $base i32) (local $len i32)
|
|
578
517
|
(local.set $p (f64.reinterpret_i64 (local.get $ptr)))
|
|
579
|
-
(local.set $base (
|
|
518
|
+
(local.set $base (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
519
|
+
${followForwardingWat('$base', { lowGuard: true })}
|
|
580
520
|
(local.set $len (i32.load (i32.sub (local.get $base) (i32.const 8))))
|
|
581
521
|
(if (i32.lt_s (i32.load (i32.sub (local.get $base) (i32.const 4))) (i32.add (local.get $len) (i32.const 1)))
|
|
582
522
|
(then
|
|
583
523
|
(local.set $p (call $__arr_grow_known (local.get $ptr) (i32.add (local.get $len) (i32.const 1))))
|
|
584
|
-
(local.set $base (
|
|
524
|
+
(local.set $base (i32.wrap_i64 (i64.and (i64.reinterpret_f64 (local.get $p)) (i64.const ${LAYOUT.OFFSET_MASK}))))))
|
|
585
525
|
(f64.store (i32.add (local.get $base) (i32.shl (local.get $len) (i32.const 3))) (local.get $val))
|
|
586
526
|
(i32.store (i32.sub (local.get $base) (i32.const 8)) (i32.add (local.get $len) (i32.const 1)))
|
|
587
527
|
(local.get $p))`
|
|
@@ -657,6 +597,14 @@ export default (ctx) => {
|
|
|
657
597
|
// === Index read ===
|
|
658
598
|
|
|
659
599
|
ctx.core.emit['[]'] = (arr, idx) => {
|
|
600
|
+
// A literal NEGATIVE index is always out of range → undefined (JS semantics), never a
|
|
601
|
+
// raw `payload + (-1)*8` load that reads heap before the allocation. A side-effecting
|
|
602
|
+
// receiver still evaluates. Mirrors VT['[]'] returning null for the same case.
|
|
603
|
+
{ const li = intLiteralValue(idx)
|
|
604
|
+
if (li != null && li < 0)
|
|
605
|
+
return typeof arr === 'string'
|
|
606
|
+
? undefExpr()
|
|
607
|
+
: typed(['block', ['result', 'f64'], ['drop', asF64(emit(arr))], undefExpr()], 'f64') }
|
|
660
608
|
// Hoist non-identifier arr so side-effecting sources (e.g. `foo.shift()[i]`) execute once.
|
|
661
609
|
// The rest of the handler inlines `emit(arr)` into multiple IR positions, which would
|
|
662
610
|
// otherwise re-execute the source expression per use at runtime.
|
|
@@ -731,6 +679,12 @@ export default (ctx) => {
|
|
|
731
679
|
return ['f64.reinterpret_i64', ['call', '$__dyn_get', ['i64.reinterpret_f64', objExpr], ['i64.reinterpret_f64', keyExpr]]]
|
|
732
680
|
}
|
|
733
681
|
const stringLoad = () => (inc('__str_idx'), ['call', '$__str_idx', ['i64.reinterpret_f64', ptrExpr], vi])
|
|
682
|
+
// A numeric index on an unknown receiver is array/typed access by design — kept
|
|
683
|
+
// lean (no OBJECT/HASH dyn-get fork): an object with numeric keys is a degenerate
|
|
684
|
+
// pattern not worth a per-access string-coercion + hash probe in every hot loop.
|
|
685
|
+
// The WRITE path still routes a numeric `o[i]=v` on an OBJECT to __dyn_set for
|
|
686
|
+
// SAFETY (no schema-slot corruption / OOB), so such a read returns undefined
|
|
687
|
+
// rather than corrupting — matching JS for an out-of-range typed/array index.
|
|
734
688
|
const arrayLoad = (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], vi])
|
|
735
689
|
const emitDynamicKeyDispatch = (objExpr, numericLoad) => {
|
|
736
690
|
const keyTmp = temp()
|
|
@@ -929,6 +883,10 @@ export default (ctx) => {
|
|
|
929
883
|
|
|
930
884
|
// .push(val) → append, increment len, return array (possibly reallocated pointer)
|
|
931
885
|
ctx.core.emit['.push'] = (arr, ...vals) => {
|
|
886
|
+
// `_expect` is overwritten by recursive emit() calls below. Capture the
|
|
887
|
+
// statement-position hint now so a dropped `xs.push(v)` can skip computing
|
|
888
|
+
// the JS return length while still performing the mutation/writeback.
|
|
889
|
+
const void_ = ctx.func._expect === 'void'
|
|
932
890
|
// structInline Array<S>: `.push({S})` writes the K schema fields as K
|
|
933
891
|
// consecutive f64 cells. Flatten the struct literal into K schema-ordered
|
|
934
892
|
// field-value nodes and fall through to the general multi-value store path
|
|
@@ -956,8 +914,10 @@ export default (ctx) => {
|
|
|
956
914
|
const readVar = box ? ['f64.load', ['local.get', `$${box}`]] : isGlobal ? ['global.get', `$${arr}`] : ['local.get', `$${arr}`]
|
|
957
915
|
const writeVar = v => box ? ['f64.store', ['local.get', `$${box}`], v] : isGlobal ? ['global.set', `$${arr}`, v] : ['local.set', `$${arr}`, v]
|
|
958
916
|
const vv = asF64(emit(vals[0]))
|
|
917
|
+
const pushed = ['call', '$__arr_push1', ['i64.reinterpret_f64', readVar], vv]
|
|
918
|
+
if (void_) return typed(['block', writeVar(pushed)], 'void')
|
|
959
919
|
return typed(['block', ['result', 'f64'],
|
|
960
|
-
writeVar(
|
|
920
|
+
writeVar(pushed),
|
|
961
921
|
['f64.convert_i32_s', ['i32.load', ['i32.sub',
|
|
962
922
|
['call', '$__ptr_offset', ['i64.reinterpret_f64', readVar]], ['i32.const', 8]]]]], 'f64')
|
|
963
923
|
}
|
|
@@ -1109,12 +1069,8 @@ export default (ctx) => {
|
|
|
1109
1069
|
(if (i32.ge_u (local.get $off) (i32.const 8))
|
|
1110
1070
|
(then
|
|
1111
1071
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
1112
|
-
(
|
|
1113
|
-
(
|
|
1114
|
-
(if (i32.gt_s (local.get $start) (local.get $len)) (then (local.set $start (local.get $len))))
|
|
1115
|
-
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
1116
|
-
(if (i32.lt_s (local.get $end) (i32.const 0)) (then (local.set $end (i32.const 0))))
|
|
1117
|
-
(if (i32.gt_s (local.get $end) (local.get $len)) (then (local.set $end (local.get $len))))
|
|
1072
|
+
(local.set $start (call $__clamp_idx (local.get $start) (local.get $len)))
|
|
1073
|
+
(local.set $end (call $__clamp_idx (local.get $end) (local.get $len)))
|
|
1118
1074
|
(local.set $i (local.get $start))
|
|
1119
1075
|
(block $done (loop $fill
|
|
1120
1076
|
(br_if $done (i32.ge_s (local.get $i) (local.get $end)))
|
|
@@ -1764,6 +1720,26 @@ export default (ctx) => {
|
|
|
1764
1720
|
['f64.convert_i32_s', ['local.get', `$${result}`]]], 'f64')
|
|
1765
1721
|
}
|
|
1766
1722
|
|
|
1723
|
+
// Mirror of .indexOf scanning to the highest matching index — no early break, the last hit wins.
|
|
1724
|
+
// Registering it (alongside .string:lastIndexOf) is what lets lastIndexOf leave STRING_ONLY_METHODS:
|
|
1725
|
+
// an untyped receiver now forks string-vs-array at runtime instead of force-narrowing to string
|
|
1726
|
+
// (which returned -1 for every array). fromIndex is unsupported, matching .indexOf's array path.
|
|
1727
|
+
ctx.core.emit['.lastIndexOf'] = (arr, val) => {
|
|
1728
|
+
const recv = hoistArrayValue(arr)
|
|
1729
|
+
const vv = asF64(emit(val))
|
|
1730
|
+
const eq = arrEqIR(val)
|
|
1731
|
+
const result = tempI32('lx')
|
|
1732
|
+
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
1733
|
+
['if', eq(item, vv),
|
|
1734
|
+
['then', ['local.set', `$${result}`, ['local.get', `$${i}`]]]]
|
|
1735
|
+
])
|
|
1736
|
+
return typed(['block', ['result', 'f64'],
|
|
1737
|
+
recv.setup,
|
|
1738
|
+
['local.set', `$${result}`, ['i32.const', -1]],
|
|
1739
|
+
...loop,
|
|
1740
|
+
['f64.convert_i32_s', ['local.get', `$${result}`]]], 'f64')
|
|
1741
|
+
}
|
|
1742
|
+
|
|
1767
1743
|
// .at(i) → array element with negative index support
|
|
1768
1744
|
ctx.core.emit['.array:at'] = (arr, idx) => {
|
|
1769
1745
|
const vt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
package/module/collection.js
CHANGED
|
@@ -12,8 +12,8 @@ import { typed, asF64, asI64, asI32, NULL_NAN, UNDEF_NAN, temp, tempI32, tempI64
|
|
|
12
12
|
import { emit, deps, call } from '../src/bridge.js'
|
|
13
13
|
import { valTypeOf } from '../src/kind.js'
|
|
14
14
|
import { VAL, lookupValType } from '../src/reps.js'
|
|
15
|
-
import { hasOwnContinue, isBlockBody } from '../src/ast.js'
|
|
16
|
-
import { ctx, inc, PTR, LAYOUT,
|
|
15
|
+
import { hasOwnContinue, isBlockBody, isLiteralStr } from '../src/ast.js'
|
|
16
|
+
import { ctx, inc, PTR, LAYOUT, registerGetter, declGlobal } from '../src/ctx.js'
|
|
17
17
|
import { STR_INTERN_BIT } from '../layout.js'
|
|
18
18
|
|
|
19
19
|
const SET_ENTRY = 16 // hash + key
|
|
@@ -43,6 +43,20 @@ function numConstLiteral(expr) {
|
|
|
43
43
|
return null
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
// Compile-time probe hash for a LITERAL collection/property key, else null. A numeric
|
|
47
|
+
// constant → numHashLiteral; an ASCII string literal → strHashLiteral. The string case is
|
|
48
|
+
// ASCII-only on purpose: strHashLiteral folds `charCodeAt(i) & 0xFF`, which equals __str_hash /
|
|
49
|
+
// __map_hash (FNV-1a over the UTF-8 bytes) ONLY for code points < 0x80 — a non-ASCII literal
|
|
50
|
+
// would fold a different hash than its stored key and silently miss, so it falls back to the
|
|
51
|
+
// runtime hash. Lets `m.get("if")` / `m.has("x")` skip the per-access __map_hash call.
|
|
52
|
+
const ASCII_KEY = /^[\x00-\x7f]*$/
|
|
53
|
+
const litKeyHash = (key) => {
|
|
54
|
+
const num = numConstLiteral(key)
|
|
55
|
+
if (num != null) return numHashLiteral(num)
|
|
56
|
+
if (isLiteralStr(key) && ASCII_KEY.test(key[1])) return strHashLiteral(key[1])
|
|
57
|
+
return null
|
|
58
|
+
}
|
|
59
|
+
|
|
46
60
|
// Equality expressions for probe templates
|
|
47
61
|
const sameValueZeroEq = '(call $__same_value_zero (i64.load (i32.add (local.get $slot) (i32.const 8))) (local.get $key))'
|
|
48
62
|
const strEq = '(call $__str_eq (i64.load (i32.add (local.get $slot) (i32.const 8))) (local.get $key))'
|
|
@@ -55,9 +69,22 @@ const strEq = '(call $__str_eq (i64.load (i32.add (local.get $slot) (i32.const 8
|
|
|
55
69
|
// walking shared-prefix bytes through __str_eq (31M unequal content-compares
|
|
56
70
|
// per self-host bench run before this). If-expression for short-circuit:
|
|
57
71
|
// i32.and would still evaluate the call.
|
|
72
|
+
//
|
|
73
|
+
// On a hash hit, an inline `storedKey == queryKey` (one i64.eq on the slot's key
|
|
74
|
+
// word) decides the overwhelmingly-common identity case — interned/SSO literals and
|
|
75
|
+
// the same heap pointer are bit-equal — WITHOUT the __str_eq / __same_value_zero call
|
|
76
|
+
// frame. Sound for both: bit-equality implies string-equality and SameValueZero (the
|
|
77
|
+
// only cross-bit-pattern equals — +0/-0, distinct NaN payloads — fall through to the
|
|
78
|
+
// full compare, never the reverse). Only a content-equal-but-bit-distinct key (a heap
|
|
79
|
+
// string vs a literal) still pays the call. This attacks the __str_eq call tax directly
|
|
80
|
+
// and helps the runtimes that DON'T inline tiny callees (wasmtime, wasm2c).
|
|
58
81
|
const hashGuard = (eqExpr) =>
|
|
59
82
|
`(if (result i32) (i32.eq (i32.load (local.get $slot)) (local.get $h))
|
|
60
|
-
(then
|
|
83
|
+
(then (if (result i32)
|
|
84
|
+
(i64.eq (i64.load (i32.add (local.get $slot) (i32.const 8))) (local.get $key))
|
|
85
|
+
(then (i32.const 1))
|
|
86
|
+
(else ${eqExpr})))
|
|
87
|
+
(else (i32.const 0)))`
|
|
61
88
|
const strEqG = hashGuard(strEq)
|
|
62
89
|
const sameValueZeroEqG = hashGuard(sameValueZeroEq)
|
|
63
90
|
|
|
@@ -367,34 +394,39 @@ function genLookupStrict(name, entrySize, hashFn, eqExpr, expectedType, missing
|
|
|
367
394
|
(i64.const ${missing}))`
|
|
368
395
|
}
|
|
369
396
|
|
|
370
|
-
|
|
397
|
+
// wantValue=true (default): return the slot value, missing → `missing` (i64). wantValue=false:
|
|
398
|
+
// return an i32 0/1 existence flag (for `.has`). Mirrors genLookup's two-mode shape, prehashed.
|
|
399
|
+
function genLookupStrictPrehashed(name, entrySize, eqExpr, expectedType, missing = UNDEF_NAN, hasExt = false, wantValue = true) {
|
|
400
|
+
const rt = wantValue ? 'i64' : 'i32'
|
|
401
|
+
const onEmpty = wantValue ? `(return (i64.const ${missing}))` : '(return (i32.const 0))'
|
|
402
|
+
const onFound = wantValue ? '(return (i64.load (i32.add (local.get $slot) (i32.const 16))))' : '(return (i32.const 1))'
|
|
403
|
+
const notFound = wantValue ? `(i64.const ${missing})` : '(i32.const 0)'
|
|
404
|
+
const extHit = wantValue ? '(call $__ext_prop (local.get $coll) (local.get $key))' : '(call $__ext_has (local.get $coll) (local.get $key))'
|
|
371
405
|
const tExpr = `(i32.wrap_i64 (i64.and (i64.shr_u (local.get $coll) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))`
|
|
372
406
|
const typeGuard = hasExt
|
|
373
407
|
? `(if (i32.ne ${tExpr} (i32.const ${expectedType}))
|
|
374
408
|
(then
|
|
375
409
|
(if (i32.eq ${tExpr} (i32.const ${PTR.EXTERNAL}))
|
|
376
|
-
(then (return
|
|
377
|
-
(else
|
|
410
|
+
(then (return ${extHit}))
|
|
411
|
+
(else ${onEmpty}))))`
|
|
378
412
|
: `(if (i32.ne ${tExpr} (i32.const ${expectedType}))
|
|
379
|
-
(then
|
|
413
|
+
(then ${onEmpty}))`
|
|
380
414
|
// SET/MAP/HASH grow by forward-marking; a boxed pointer may be stale → follow the chain.
|
|
381
415
|
const offExpr = '(call $__ptr_offset (local.get $coll))'
|
|
382
|
-
return `(func $${name} (param $coll i64) (param $key i64) (param $h i32) (result
|
|
416
|
+
return `(func $${name} (param $coll i64) (param $key i64) (param $h i32) (result ${rt})
|
|
383
417
|
(local $off i32) (local $cap i32) (local $end i32) (local $slot i32) (local $tries i32)
|
|
384
418
|
${typeGuard}
|
|
385
419
|
(local.set $off ${offExpr})
|
|
386
420
|
(local.set $cap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
387
421
|
${probeStart(entrySize)}
|
|
388
422
|
(block $done (loop $probe
|
|
389
|
-
(if (i64.eqz (i64.load (local.get $slot)))
|
|
390
|
-
|
|
391
|
-
(if ${eqExpr}
|
|
392
|
-
(then (return (i64.load (i32.add (local.get $slot) (i32.const 16))))))
|
|
423
|
+
(if (i64.eqz (i64.load (local.get $slot))) (then ${onEmpty}))
|
|
424
|
+
(if ${eqExpr} (then ${onFound}))
|
|
393
425
|
${probeNext(entrySize)}
|
|
394
426
|
(local.set $tries (i32.add (local.get $tries) (i32.const 1)))
|
|
395
427
|
(br_if $done (i32.ge_s (local.get $tries) (local.get $cap)))
|
|
396
428
|
(br $probe)))
|
|
397
|
-
|
|
429
|
+
${notFound})`
|
|
398
430
|
}
|
|
399
431
|
|
|
400
432
|
function genUpsertStrictPrehashed(name, entrySize, eqExpr, expectedType) {
|
|
@@ -440,6 +472,9 @@ export default (ctx) => {
|
|
|
440
472
|
__map_get: () => ctx.features.external ? ['__ext_prop', '__map_set', '__ptr_offset'] : ['__map_set', '__ptr_offset'],
|
|
441
473
|
__map_get_h: () => ctx.features.external ? ['__ext_prop', '__same_value_zero', '__ptr_offset'] : ['__same_value_zero', '__ptr_offset'],
|
|
442
474
|
__map_has: () => ctx.features.external ? ['__map_hash', '__same_value_zero', '__ptr_offset', '__ext_has'] : ['__map_hash', '__same_value_zero', '__ptr_offset'],
|
|
475
|
+
// Prehashed has-probes: caller folds the hash, so no __map_hash dependency.
|
|
476
|
+
__map_has_h: () => ctx.features.external ? ['__same_value_zero', '__ptr_offset', '__ext_has'] : ['__same_value_zero', '__ptr_offset'],
|
|
477
|
+
__set_has_h: () => ctx.features.external ? ['__same_value_zero', '__ptr_offset', '__ext_has'] : ['__same_value_zero', '__ptr_offset'],
|
|
443
478
|
__map_delete: ['__map_hash', '__same_value_zero'],
|
|
444
479
|
__map_from: ['__ptr_type', '__ptr_offset', '__len', '__typed_idx', '__map_set', '__mkptr', '__alloc_hdr_n', '__coll_order'],
|
|
445
480
|
__hash_set: () => ctx.features.external
|
|
@@ -628,20 +663,35 @@ export default (ctx) => {
|
|
|
628
663
|
// the Set probe carries a PTR.SET type guard that rejects a Map outright, so
|
|
629
664
|
// routing a Map through `__set_has`/`__set_delete` makes every lookup/delete
|
|
630
665
|
// silently report absent. Mirrors collViewDyn below; typed receivers skip it.
|
|
631
|
-
|
|
666
|
+
// `h` (optional precomputed key hash) appends to the call → routes to the `_h` prehashed
|
|
667
|
+
// probes, skipping __map_hash per access; omitted → the generic hashing probes.
|
|
668
|
+
const collProbeDyn = (mapFn, setFn) => (collExpr, key, h) => {
|
|
632
669
|
inc(mapFn, setFn, '__ptr_type')
|
|
633
670
|
const o = temp('cp'), k = tempI64('cpk')
|
|
671
|
+
const extra = h != null ? [['i32.const', h]] : []
|
|
634
672
|
return typed(['block', ['result', 'f64'],
|
|
635
673
|
['local.set', `$${o}`, asF64(emit(collExpr))],
|
|
636
674
|
['local.set', `$${k}`, asI64(emit(key))],
|
|
637
675
|
['f64.convert_i32_s', ['if', ['result', 'i32'],
|
|
638
676
|
ptrTypeEq(['local.get', `$${o}`], PTR.MAP),
|
|
639
|
-
['then', ['call', `$${mapFn}`, ['i64.reinterpret_f64', ['local.get', `$${o}`]], ['local.get', `$${k}`]]],
|
|
640
|
-
['else', ['call', `$${setFn}`, ['i64.reinterpret_f64', ['local.get', `$${o}`]], ['local.get', `$${k}`]]]]]], 'f64')
|
|
677
|
+
['then', ['call', `$${mapFn}`, ['i64.reinterpret_f64', ['local.get', `$${o}`]], ['local.get', `$${k}`], ...extra]],
|
|
678
|
+
['else', ['call', `$${setFn}`, ['i64.reinterpret_f64', ['local.get', `$${o}`]], ['local.get', `$${k}`], ...extra]]]]], 'f64')
|
|
679
|
+
}
|
|
680
|
+
// `.has` on an unproven receiver: a literal key folds its hash and uses the _h probes.
|
|
681
|
+
ctx.core.emit['.has'] = (collExpr, key) => {
|
|
682
|
+
const h = litKeyHash(key)
|
|
683
|
+
return h != null
|
|
684
|
+
? collProbeDyn('__map_has_h', '__set_has_h')(collExpr, key, h)
|
|
685
|
+
: collProbeDyn('__map_has', '__set_has')(collExpr, key)
|
|
641
686
|
}
|
|
642
|
-
ctx.core.emit['.has'] = collProbeDyn('__map_has', '__set_has')
|
|
643
687
|
ctx.core.emit['.delete'] = collProbeDyn('__map_delete', '__set_delete')
|
|
644
|
-
|
|
688
|
+
// Typed Set.has: literal key → prehashed __set_has_h, else the generic probe.
|
|
689
|
+
ctx.core.emit[`.${VAL.SET}:has`] = (collExpr, key) => {
|
|
690
|
+
const h = litKeyHash(key)
|
|
691
|
+
if (h == null) return call('__set_has', 'II', 'i32')(collExpr, key)
|
|
692
|
+
inc('__set_has_h')
|
|
693
|
+
return typed(['f64.convert_i32_s', ['call', '$__set_has_h', asI64(emit(collExpr)), asI64(emit(key)), ['i32.const', h]]], 'f64')
|
|
694
|
+
}
|
|
645
695
|
ctx.core.emit[`.${VAL.SET}:delete`] = call('__set_delete', 'II', 'i32')
|
|
646
696
|
|
|
647
697
|
// Map.prototype.clear / Set.prototype.clear — drop every entry. `.clear` only
|
|
@@ -666,7 +716,7 @@ export default (ctx) => {
|
|
|
666
716
|
(i32.store (i32.sub (local.get $off) (i32.const 8)) (i32.const 0))))
|
|
667
717
|
(f64.reinterpret_i64 (i64.const ${UNDEF_NAN})))`
|
|
668
718
|
|
|
669
|
-
|
|
719
|
+
registerGetter('.size', (expr) => {
|
|
670
720
|
return typed(['f64.convert_i32_s', ['call', '$__len', ['i64.reinterpret_f64', asF64(emit(expr))]]], 'f64')
|
|
671
721
|
})
|
|
672
722
|
|
|
@@ -691,6 +741,7 @@ export default (ctx) => {
|
|
|
691
741
|
// Generated Set probe functions
|
|
692
742
|
ctx.core.stdlib['__set_add'] = () => genUpsert('__set_add', SET_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.SET, false, ctx.features.external)
|
|
693
743
|
ctx.core.stdlib['__set_has'] = () => genLookup('__set_has', SET_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.SET, false, ctx.features.external)
|
|
744
|
+
ctx.core.stdlib['__set_has_h'] = () => genLookupStrictPrehashed('__set_has_h', SET_ENTRY, sameValueZeroEqG, PTR.SET, UNDEF_NAN, ctx.features.external, false)
|
|
694
745
|
ctx.core.stdlib['__set_delete'] = genDelete('__set_delete', SET_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.SET)
|
|
695
746
|
|
|
696
747
|
// === Map ===
|
|
@@ -717,10 +768,10 @@ export default (ctx) => {
|
|
|
717
768
|
ctx.core.emit[`.${VAL.MAP}:set`] = ctx.core.emit['.set']
|
|
718
769
|
|
|
719
770
|
const emitMapGet = (mapExpr, key) => {
|
|
720
|
-
const
|
|
721
|
-
if (
|
|
771
|
+
const h = litKeyHash(key)
|
|
772
|
+
if (h != null) {
|
|
722
773
|
inc('__map_get_h')
|
|
723
|
-
return typed(['f64.reinterpret_i64', ['call', '$__map_get_h', asI64(emit(mapExpr)), asI64(emit(key)), ['i32.const',
|
|
774
|
+
return typed(['f64.reinterpret_i64', ['call', '$__map_get_h', asI64(emit(mapExpr)), asI64(emit(key)), ['i32.const', h]]], 'f64')
|
|
724
775
|
}
|
|
725
776
|
inc('__map_get')
|
|
726
777
|
return typed(['f64.reinterpret_i64', ['call', '$__map_get', asI64(emit(mapExpr)), asI64(emit(key))]], 'f64')
|
|
@@ -729,7 +780,13 @@ export default (ctx) => {
|
|
|
729
780
|
ctx.core.emit['.get'] = emitMapGet
|
|
730
781
|
ctx.core.emit[`.${VAL.MAP}:get`] = emitMapGet
|
|
731
782
|
|
|
732
|
-
|
|
783
|
+
// Typed Map.has: literal key → prehashed __map_has_h, else the generic probe.
|
|
784
|
+
ctx.core.emit[`.${VAL.MAP}:has`] = (collExpr, key) => {
|
|
785
|
+
const h = litKeyHash(key)
|
|
786
|
+
if (h == null) return call('__map_has', 'II', 'i32')(collExpr, key)
|
|
787
|
+
inc('__map_has_h')
|
|
788
|
+
return typed(['f64.convert_i32_s', ['call', '$__map_has_h', asI64(emit(collExpr)), asI64(emit(key)), ['i32.const', h]]], 'f64')
|
|
789
|
+
}
|
|
733
790
|
ctx.core.emit[`.${VAL.MAP}:delete`] = call('__map_delete', 'II', 'i32')
|
|
734
791
|
|
|
735
792
|
// Map/Set iteration views: keys() / values() / entries() materialize a dense
|
|
@@ -815,6 +872,7 @@ export default (ctx) => {
|
|
|
815
872
|
ctx.core.stdlib['__map_set'] = () => genUpsert('__map_set', MAP_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.MAP, true, ctx.features.external)
|
|
816
873
|
ctx.core.stdlib['__map_get'] = () => genLookup('__map_get', MAP_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.MAP, true, ctx.features.external)
|
|
817
874
|
ctx.core.stdlib['__map_get_h'] = () => genLookupStrictPrehashed('__map_get_h', MAP_ENTRY, sameValueZeroEqG, PTR.MAP, UNDEF_NAN, ctx.features.external)
|
|
875
|
+
ctx.core.stdlib['__map_has_h'] = () => genLookupStrictPrehashed('__map_has_h', MAP_ENTRY, sameValueZeroEqG, PTR.MAP, UNDEF_NAN, ctx.features.external, false)
|
|
818
876
|
ctx.core.stdlib['__map_has'] = () => genLookup('__map_has', MAP_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.MAP, false, ctx.features.external)
|
|
819
877
|
ctx.core.stdlib['__map_delete'] = genDelete('__map_delete', MAP_ENTRY, '$__map_hash', sameValueZeroEqG, PTR.MAP)
|
|
820
878
|
|
|
@@ -877,12 +935,12 @@ export default (ctx) => {
|
|
|
877
935
|
(local.set $aux (i32.wrap_i64 (i64.and (i64.shr_u (local.get $s) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK}))))
|
|
878
936
|
(if (i32.and (i32.eq (local.get $t) (i32.const ${PTR.STRING})) (i32.shr_u (local.get $aux) (i32.const 14)))
|
|
879
937
|
(then
|
|
880
|
-
(local.set $len (i32.and (local.get $aux) (i32.const 7)))
|
|
938
|
+
(local.set $len (i32.and (i32.shr_u (local.get $aux) (i32.const 10)) (i32.const 7)))
|
|
881
939
|
(block $ds (loop $ls
|
|
882
940
|
(br_if $ds (i32.ge_s (local.get $i) (local.get $len)))
|
|
883
941
|
(local.set $h (i32.mul
|
|
884
942
|
(i32.xor (local.get $h)
|
|
885
|
-
(i32.and (
|
|
943
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $s) (i64.mul (i64.extend_i32_u (local.get $i)) (i64.const 7))) (i64.const 0x7f))))
|
|
886
944
|
(i32.const 0x01000193)))
|
|
887
945
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
888
946
|
(br $ls))))
|
package/module/console.js
CHANGED
|
@@ -69,7 +69,7 @@ const setupWasi = (ctx) => {
|
|
|
69
69
|
(local.set $aux (call $__ptr_aux (local.get $ptr)))
|
|
70
70
|
(if (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT}))
|
|
71
71
|
(then
|
|
72
|
-
(local.set $len (i32.and (local.get $aux) (i32.const 7)))
|
|
72
|
+
(local.set $len (i32.and (i32.shr_u (local.get $aux) (i32.const 10)) (i32.const 7)))
|
|
73
73
|
(local.set $buf (call $__alloc (local.get $len)))
|
|
74
74
|
(local.set $off (i32.const 0))
|
|
75
75
|
(block $done (loop $loop
|