jz 0.1.1 → 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 +257 -90
- package/cli.js +34 -8
- package/index.js +102 -13
- 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 +691 -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 +434 -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 +626 -132
- package/src/autoload.js +14 -6
- package/src/compile.js +399 -70
- package/src/ctx.js +41 -12
- package/src/emit.js +634 -145
- package/src/host.js +244 -51
- package/src/ir.js +81 -31
- package/src/jzify.js +181 -24
- package/src/narrow.js +32 -4
- package/src/optimize.js +628 -42
- package/src/plan.js +1132 -4
- package/src/prepare.js +321 -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/array.js
CHANGED
|
@@ -8,11 +8,10 @@
|
|
|
8
8
|
* @module array
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import { typed, asF64, asI32, NULL_NAN, UNDEF_NAN, temp, tempI32, allocPtr, multiCount, arrayLoop, elemLoad, elemStore, truthyIR, extractF64Bits, appendStaticSlots, mkPtrIR, slotAddr } from '../src/ir.js'
|
|
11
|
+
import { typed, asF64, asI64, asI32, NULL_NAN, UNDEF_NAN, temp, tempI32, allocPtr, multiCount, arrayLoop, elemLoad, elemStore, truthyIR, extractF64Bits, appendStaticSlots, mkPtrIR, slotAddr, isLiteralStr, resolveValType, undefExpr } from '../src/ir.js'
|
|
12
12
|
import { emit, materializeMulti } from '../src/emit.js'
|
|
13
|
-
import { valTypeOf, lookupValType, VAL, extractParams, updateRep } from '../src/analyze.js'
|
|
14
|
-
import { ctx, inc, err, PTR } from '../src/ctx.js'
|
|
15
|
-
import { staticPropertyKey } from '../src/key.js'
|
|
13
|
+
import { valTypeOf, lookupValType, VAL, extractParams, updateRep, staticPropertyKey } from '../src/analyze.js'
|
|
14
|
+
import { ctx, inc, err, PTR, LAYOUT } from '../src/ctx.js'
|
|
16
15
|
import { strHashLiteral } from './collection.js'
|
|
17
16
|
|
|
18
17
|
|
|
@@ -43,6 +42,8 @@ function hoistArrayValue(arr) {
|
|
|
43
42
|
}
|
|
44
43
|
}
|
|
45
44
|
|
|
45
|
+
const arrayLenFromPtr = ptr => ['i32.load', ['i32.sub', ['local.get', `$${ptr}`], ['i32.const', 8]]]
|
|
46
|
+
|
|
46
47
|
// Pure-expression check: no statements, binders, control flow, or assignments.
|
|
47
48
|
// Inlining is only safe for these — anything else needs the full closure machinery.
|
|
48
49
|
const NOT_PURE_OPS = new Set([
|
|
@@ -179,10 +180,42 @@ const arrayGrowDeps = (knownArray = false) => () => [
|
|
|
179
180
|
...(needsArrayDynMove() ? ['__dyn_move'] : []),
|
|
180
181
|
]
|
|
181
182
|
|
|
183
|
+
// Arrays keep dynamic props in the global table because old/new array storage can
|
|
184
|
+
// be forwarded. Relocate that entry when growth moves the backing store.
|
|
182
185
|
const maybeDynMoveIR = () => needsArrayDynMove()
|
|
183
186
|
? '(call $__dyn_move (local.get $off) (local.get $newOff))'
|
|
184
187
|
: ''
|
|
185
188
|
|
|
189
|
+
// Per-object propsPtr lives in the 16-byte header at $off-16. On grow we copy it
|
|
190
|
+
// from old to new header (still HASH-tagged → unshifted ARRAY case). On shift we
|
|
191
|
+
// migrate it to the global __dyn_props because the forwarding writes overwrite
|
|
192
|
+
// the destination's $newOff-16 slot. The HASH-tag check rejects 0 (no props) and
|
|
193
|
+
// forwarding garbage from a prior shift, so chained shift→grow is safe — the
|
|
194
|
+
// global hash takes over and __dyn_move keeps it in sync.
|
|
195
|
+
const headerPropsCopyIR = () => needsArrayDynMove() ? `
|
|
196
|
+
(local.set $oldProps (f64.load (i32.sub (local.get $off) (i32.const 16))))
|
|
197
|
+
(if (i32.eq
|
|
198
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (i64.reinterpret_f64 (local.get $oldProps)) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))
|
|
199
|
+
(i32.const ${PTR.HASH}))
|
|
200
|
+
(then (f64.store (i32.sub (local.get $newOff) (i32.const 16)) (local.get $oldProps))))` : ''
|
|
201
|
+
|
|
202
|
+
const headerPropsToGlobalIR = () => needsArrayDynMove() ? `
|
|
203
|
+
(if (i32.ge_u (local.get $off) (i32.const 16))
|
|
204
|
+
(then
|
|
205
|
+
(local.set $oldProps (f64.load (i32.sub (local.get $off) (i32.const 16))))
|
|
206
|
+
(if (i32.eq
|
|
207
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (i64.reinterpret_f64 (local.get $oldProps)) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))
|
|
208
|
+
(i32.const ${PTR.HASH}))
|
|
209
|
+
(then
|
|
210
|
+
(local.set $root (global.get $__dyn_props))
|
|
211
|
+
(if (f64.eq (local.get $root) (f64.const 0))
|
|
212
|
+
(then (local.set $root (call $__hash_new))))
|
|
213
|
+
(local.set $root (f64.reinterpret_i64 (call $__ihash_set_local
|
|
214
|
+
(i64.reinterpret_f64 (local.get $root))
|
|
215
|
+
(i64.reinterpret_f64 (f64.convert_i32_s (local.get $newOff)))
|
|
216
|
+
(i64.reinterpret_f64 (local.get $oldProps)))))
|
|
217
|
+
(global.set $__dyn_props (local.get $root)))))) ` : ''
|
|
218
|
+
|
|
186
219
|
export default (ctx) => {
|
|
187
220
|
Object.assign(ctx.core.stdlibDeps, {
|
|
188
221
|
__arr_idx: [],
|
|
@@ -190,7 +223,7 @@ export default (ctx) => {
|
|
|
190
223
|
__arr_grow_known: arrayGrowDeps(true),
|
|
191
224
|
__arr_shift: () => [
|
|
192
225
|
'__ptr_offset',
|
|
193
|
-
...(needsArrayDynMove() ? ['__dyn_move'] : []),
|
|
226
|
+
...(needsArrayDynMove() ? ['__dyn_move', '__hash_new', '__ihash_set_local'] : []),
|
|
194
227
|
],
|
|
195
228
|
__arr_set_idx_ptr: ['__arr_grow', '__ptr_offset', '__set_len'],
|
|
196
229
|
__typed_idx: () => ctx.features.typedarray || ctx.features.external
|
|
@@ -211,21 +244,20 @@ export default (ctx) => {
|
|
|
211
244
|
const t = temp('t')
|
|
212
245
|
return typed(['i32.and',
|
|
213
246
|
['f64.ne', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
214
|
-
['i32.eq', ['call', '$__ptr_type', ['local.get', `$${t}`]], ['i32.const', PTR.ARRAY]]], 'i32')
|
|
247
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${t}`]]], ['i32.const', PTR.ARRAY]]], 'i32')
|
|
215
248
|
}
|
|
216
249
|
|
|
217
250
|
// ARRAY-only indexed read. Inline forwarding-follow + bounds check + load — avoids
|
|
218
251
|
// the redundant double pass through __len then __ptr_offset that both follow forwarding.
|
|
219
|
-
ctx.core.stdlib['__arr_idx'] = `(func $__arr_idx (param $ptr
|
|
220
|
-
(local $
|
|
221
|
-
(local.set $bits (i64.reinterpret_f64 (local.get $ptr)))
|
|
252
|
+
ctx.core.stdlib['__arr_idx'] = `(func $__arr_idx (param $ptr i64) (param $i i32) (result f64)
|
|
253
|
+
(local $off i32)
|
|
222
254
|
(if (result f64)
|
|
223
255
|
(i32.ne
|
|
224
|
-
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $
|
|
256
|
+
(i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK})))
|
|
225
257
|
(i32.const ${PTR.ARRAY}))
|
|
226
258
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
227
259
|
(else
|
|
228
|
-
(local.set $off (i32.wrap_i64 (i64.and (local.get $
|
|
260
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
229
261
|
(block $done
|
|
230
262
|
(loop $follow
|
|
231
263
|
(br_if $done (i32.lt_u (local.get $off) (i32.const 8)))
|
|
@@ -242,13 +274,32 @@ export default (ctx) => {
|
|
|
242
274
|
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
243
275
|
(else (f64.const nan:${UNDEF_NAN})))))) `
|
|
244
276
|
|
|
277
|
+
ctx.core.stdlib['__arr_idx_known'] = `(func $__arr_idx_known (param $ptr i64) (param $i i32) (result f64)
|
|
278
|
+
(local $off i32)
|
|
279
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
280
|
+
(block $done
|
|
281
|
+
(loop $follow
|
|
282
|
+
(br_if $done (i32.lt_u (local.get $off) (i32.const 8)))
|
|
283
|
+
(br_if $done (i32.gt_u (local.get $off) (i32.shl (memory.size) (i32.const 16))))
|
|
284
|
+
(br_if $done (i32.ne (i32.load (i32.sub (local.get $off) (i32.const 4))) (i32.const -1)))
|
|
285
|
+
(local.set $off (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
286
|
+
(br $follow)))
|
|
287
|
+
(if (result f64)
|
|
288
|
+
(i32.and
|
|
289
|
+
(i32.ge_u (local.get $off) (i32.const 8))
|
|
290
|
+
(i32.and
|
|
291
|
+
(i32.ge_s (local.get $i) (i32.const 0))
|
|
292
|
+
(i32.lt_u (local.get $i) (i32.load (i32.sub (local.get $off) (i32.const 8))))))
|
|
293
|
+
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
294
|
+
(else (f64.const nan:${UNDEF_NAN}))))`
|
|
295
|
+
|
|
245
296
|
// Runtime-dispatch index: element-type aware load with bounds check + view indirection.
|
|
246
297
|
// Full body handles TYPED element types and view indirection since external host can
|
|
247
298
|
// pass typed arrays even when typedarray module isn't loaded. When features.typedarray
|
|
248
299
|
// and features.external are both off, collapses to ARRAY-only f64 indexing.
|
|
249
300
|
ctx.core.stdlib['__typed_idx'] = () => {
|
|
250
301
|
if (!ctx.features.typedarray && !ctx.features.external) {
|
|
251
|
-
return `(func $__typed_idx (param $ptr
|
|
302
|
+
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
252
303
|
(local $len i32)
|
|
253
304
|
(local.set $len (call $__len (local.get $ptr)))
|
|
254
305
|
(if (result f64)
|
|
@@ -258,13 +309,12 @@ export default (ctx) => {
|
|
|
258
309
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
259
310
|
(else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
|
|
260
311
|
}
|
|
261
|
-
// Hot (~37M calls in watr self-host). Type/aux/offset extracted once from $
|
|
262
|
-
return `(func $__typed_idx (param $ptr
|
|
263
|
-
(local $
|
|
264
|
-
(local.set $
|
|
265
|
-
(local.set $
|
|
266
|
-
(local.set $
|
|
267
|
-
(local.set $off (i32.wrap_i64 (i64.and (local.get $bits) (i64.const 0xFFFFFFFF))))
|
|
312
|
+
// Hot (~37M calls in watr self-host). Type/aux/offset extracted once from $ptr.
|
|
313
|
+
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
314
|
+
(local $t i32) (local $off i32) (local $et i32) (local $len i32) (local $aux i32)
|
|
315
|
+
(local.set $t (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
316
|
+
(local.set $aux (i32.wrap_i64 (i64.and (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})) (i64.const ${LAYOUT.AUX_MASK}))))
|
|
317
|
+
(local.set $off (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
268
318
|
(if
|
|
269
319
|
(i32.and
|
|
270
320
|
(i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
@@ -282,7 +332,9 @@ export default (ctx) => {
|
|
|
282
332
|
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
283
333
|
(if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
|
|
284
334
|
(then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
|
|
285
|
-
(then (
|
|
335
|
+
(then (if (result f64) (i32.and (local.get $aux) (i32.const 16))
|
|
336
|
+
(then (f64.reinterpret_i64 (i64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))))))
|
|
337
|
+
(else (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))))
|
|
286
338
|
(else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
287
339
|
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
|
|
288
340
|
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
@@ -299,23 +351,56 @@ export default (ctx) => {
|
|
|
299
351
|
}
|
|
300
352
|
|
|
301
353
|
// Array.from(src) — shallow copy of array (memory.copy of f64 elements)
|
|
302
|
-
ctx.core.stdlib['__arr_from'] = `(func $__arr_from (param $src
|
|
354
|
+
ctx.core.stdlib['__arr_from'] = `(func $__arr_from (param $src i64) (result f64)
|
|
303
355
|
(local $len i32) (local $dst i32)
|
|
304
356
|
(local.set $len (call $__len (local.get $src)))
|
|
305
357
|
(local.set $dst (call $__alloc_hdr (local.get $len) (local.get $len) (i32.const 8)))
|
|
306
358
|
(memory.copy (local.get $dst) (call $__ptr_offset (local.get $src)) (i32.shl (local.get $len) (i32.const 3)))
|
|
307
359
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $dst)))`
|
|
308
360
|
|
|
309
|
-
|
|
361
|
+
function arrayLikeLength(src) {
|
|
362
|
+
if (!Array.isArray(src) || src[0] !== '{}') return null
|
|
363
|
+
for (let i = 1; i < src.length; i++) {
|
|
364
|
+
const prop = src[i]
|
|
365
|
+
if (!Array.isArray(prop) || prop[0] !== ':') continue
|
|
366
|
+
const key = typeof prop[1] === 'string' ? prop[1] : staticPropertyKey(prop[1])
|
|
367
|
+
if (key === 'length') return prop[2]
|
|
368
|
+
}
|
|
369
|
+
return null
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
ctx.core.emit['Array.from'] = (src, mapFn) => {
|
|
373
|
+
const lengthExpr = arrayLikeLength(src)
|
|
374
|
+
if (lengthExpr) {
|
|
375
|
+
const len = tempI32('fl'), i = tempI32('fi')
|
|
376
|
+
const lenIR = ['local.get', `$${len}`]
|
|
377
|
+
const out = allocPtr({ type: PTR.ARRAY, len: lenIR, tag: 'fr' })
|
|
378
|
+
const cb = mapFn && makeCallback(mapFn, [null, { val: VAL.NUMBER }])
|
|
379
|
+
const undef = typed(['f64.reinterpret_i64', ['i64.const', UNDEF_NAN]], 'f64')
|
|
380
|
+
const item = cb ? cb.call([undef, idxArg(cb, i)]) : undef
|
|
381
|
+
const id = ctx.func.uniq++
|
|
382
|
+
return typed(['block', ['result', 'f64'],
|
|
383
|
+
['local.set', `$${len}`, asI32(emit(lengthExpr))],
|
|
384
|
+
out.init,
|
|
385
|
+
...(cb ? [cb.setup] : []),
|
|
386
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
387
|
+
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
388
|
+
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${i}`], lenIR]],
|
|
389
|
+
elemStore(out.local, i, asF64(item)),
|
|
390
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
391
|
+
['br', `$loop${id}`]]],
|
|
392
|
+
out.ptr], 'f64')
|
|
393
|
+
}
|
|
310
394
|
inc('__arr_from')
|
|
311
|
-
return typed(['call', '$__arr_from',
|
|
395
|
+
return typed(['call', '$__arr_from', asI64(emit(src))], 'f64')
|
|
312
396
|
}
|
|
313
397
|
|
|
314
398
|
// Grow array if capacity insufficient. Returns (possibly new) NaN-boxed pointer.
|
|
315
399
|
// Old storage is left behind as a forwarding header so existing aliases keep
|
|
316
400
|
// seeing the current backing store after growth.
|
|
317
|
-
ctx.core.stdlib['__arr_grow'] = () => `(func $__arr_grow (param $ptr
|
|
401
|
+
ctx.core.stdlib['__arr_grow'] = () => `(func $__arr_grow (param $ptr i64) (param $minCap i32) (result f64)
|
|
318
402
|
(local $t i32) (local $off i32) (local $oldCap i32) (local $newCap i32) (local $newOff i32) (local $len i32)
|
|
403
|
+
${needsArrayDynMove() ? '(local $oldProps f64)' : ''}
|
|
319
404
|
(local.set $t (call $__ptr_type (local.get $ptr)))
|
|
320
405
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
321
406
|
;; Defensive path: invalid/non-array pointer -> create fresh array buffer.
|
|
@@ -329,7 +414,7 @@ export default (ctx) => {
|
|
|
329
414
|
(return (call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $newOff)))))
|
|
330
415
|
(local.set $oldCap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
331
416
|
(if (i32.ge_s (local.get $oldCap) (local.get $minCap))
|
|
332
|
-
(then (return (local.get $ptr))))
|
|
417
|
+
(then (return (f64.reinterpret_i64 (local.get $ptr)))))
|
|
333
418
|
(local.set $newCap (select
|
|
334
419
|
(local.get $minCap)
|
|
335
420
|
(i32.shl (local.get $oldCap) (i32.const 1))
|
|
@@ -337,17 +422,19 @@ export default (ctx) => {
|
|
|
337
422
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
338
423
|
(local.set $newOff (call $__alloc_hdr (local.get $len) (local.get $newCap) (i32.const 8)))
|
|
339
424
|
(memory.copy (local.get $newOff) (local.get $off) (i32.shl (local.get $len) (i32.const 3)))
|
|
425
|
+
${headerPropsCopyIR()}
|
|
340
426
|
${maybeDynMoveIR()}
|
|
341
427
|
(i32.store (i32.sub (local.get $off) (i32.const 8)) (local.get $newOff))
|
|
342
428
|
(i32.store (i32.sub (local.get $off) (i32.const 4)) (i32.const -1))
|
|
343
429
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $newOff)))`
|
|
344
430
|
|
|
345
|
-
ctx.core.stdlib['__arr_grow_known'] = () => `(func $__arr_grow_known (param $ptr
|
|
431
|
+
ctx.core.stdlib['__arr_grow_known'] = () => `(func $__arr_grow_known (param $ptr i64) (param $minCap i32) (result f64)
|
|
346
432
|
(local $off i32) (local $oldCap i32) (local $newCap i32) (local $newOff i32) (local $len i32)
|
|
433
|
+
${needsArrayDynMove() ? '(local $oldProps f64)' : ''}
|
|
347
434
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
348
435
|
(local.set $oldCap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
349
436
|
(if (i32.ge_s (local.get $oldCap) (local.get $minCap))
|
|
350
|
-
(then (return (local.get $ptr))))
|
|
437
|
+
(then (return (f64.reinterpret_i64 (local.get $ptr)))))
|
|
351
438
|
(local.set $newCap (select
|
|
352
439
|
(local.get $minCap)
|
|
353
440
|
(i32.shl (local.get $oldCap) (i32.const 1))
|
|
@@ -355,6 +442,7 @@ export default (ctx) => {
|
|
|
355
442
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
356
443
|
(local.set $newOff (call $__alloc_hdr (local.get $len) (local.get $newCap) (i32.const 8)))
|
|
357
444
|
(memory.copy (local.get $newOff) (local.get $off) (i32.shl (local.get $len) (i32.const 3)))
|
|
445
|
+
${headerPropsCopyIR()}
|
|
358
446
|
${maybeDynMoveIR()}
|
|
359
447
|
(i32.store (i32.sub (local.get $off) (i32.const 8)) (local.get $newOff))
|
|
360
448
|
(i32.store (i32.sub (local.get $off) (i32.const 4)) (i32.const -1))
|
|
@@ -363,21 +451,22 @@ export default (ctx) => {
|
|
|
363
451
|
// Hot for arr[i] = val (~18M calls in watr self-host). Compute base via __ptr_offset
|
|
364
452
|
// once and read len from the inline header (i32.load base-8) — avoids __len's separate
|
|
365
453
|
// forwarding follow. On the rare grow path the base is recomputed after relocation.
|
|
366
|
-
ctx.core.stdlib['__arr_set_idx_ptr'] = `(func $__arr_set_idx_ptr (param $ptr
|
|
367
|
-
(local $base i32)
|
|
454
|
+
ctx.core.stdlib['__arr_set_idx_ptr'] = `(func $__arr_set_idx_ptr (param $ptr i64) (param $i i32) (param $val f64) (result f64)
|
|
455
|
+
(local $base i32) (local $p f64)
|
|
456
|
+
(local.set $p (f64.reinterpret_i64 (local.get $ptr)))
|
|
368
457
|
(if (i32.lt_s (local.get $i) (i32.const 0))
|
|
369
|
-
(then (return (local.get $
|
|
458
|
+
(then (return (local.get $p))))
|
|
370
459
|
(local.set $base (call $__ptr_offset (local.get $ptr)))
|
|
371
460
|
(if (i32.ge_u (local.get $i)
|
|
372
461
|
(i32.load (i32.sub (local.get $base) (i32.const 8))))
|
|
373
462
|
(then
|
|
374
|
-
(local.set $
|
|
375
|
-
(call $__set_len (local.get $
|
|
376
|
-
(local.set $base (call $__ptr_offset (local.get $
|
|
463
|
+
(local.set $p (call $__arr_grow (local.get $ptr) (i32.add (local.get $i) (i32.const 1))))
|
|
464
|
+
(call $__set_len (i64.reinterpret_f64 (local.get $p)) (i32.add (local.get $i) (i32.const 1)))
|
|
465
|
+
(local.set $base (call $__ptr_offset (i64.reinterpret_f64 (local.get $p))))))
|
|
377
466
|
(f64.store
|
|
378
467
|
(i32.add (local.get $base) (i32.shl (local.get $i) (i32.const 3)))
|
|
379
468
|
(local.get $val))
|
|
380
|
-
(local.get $
|
|
469
|
+
(local.get $p))`
|
|
381
470
|
|
|
382
471
|
// === Array literal ===
|
|
383
472
|
|
|
@@ -416,28 +505,31 @@ export default (ctx) => {
|
|
|
416
505
|
const src = temp('ss'), slen = tempI32('sl'), si = tempI32('si')
|
|
417
506
|
const id = ctx.func.uniq++
|
|
418
507
|
const spreadVal = multiCount(e[1]) ? materializeMulti(e[1]) : asF64(emit(e[1]))
|
|
419
|
-
const
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
508
|
+
const srcVT = valTypeOf(e[1])
|
|
509
|
+
const spreadItem = srcVT === VAL.ARRAY
|
|
510
|
+
? (inc('__arr_idx_known'), ['call', '$__arr_idx_known', ['i64.reinterpret_f64', ['local.get', `$${src}`]], ['local.get', `$${si}`]])
|
|
511
|
+
: srcVT === VAL.STRING
|
|
512
|
+
? (inc('__str_idx'), ['call', '$__str_idx', ['i64.reinterpret_f64', ['local.get', `$${src}`]], ['local.get', `$${si}`]])
|
|
513
|
+
: ctx.module.modules['string']
|
|
514
|
+
? ['if', ['result', 'f64'],
|
|
515
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ['local.get', `$${src}`]]], ['i32.const', PTR.STRING]],
|
|
516
|
+
['then', (inc('__str_idx'), ['call', '$__str_idx', ['i64.reinterpret_f64', ['local.get', `$${src}`]], ['local.get', `$${si}`]])],
|
|
517
|
+
['else', (['call', '$__typed_idx', ['i64.reinterpret_f64', ['local.get', `$${src}`]], ['local.get', `$${si}`]])]]
|
|
518
|
+
: (['call', '$__typed_idx', ['i64.reinterpret_f64', ['local.get', `$${src}`]], ['local.get', `$${si}`]])
|
|
427
519
|
|
|
428
520
|
body.push(
|
|
429
521
|
['local.set', `$${src}`, spreadVal],
|
|
430
|
-
['local.set', `$${slen}`, ['call', '$__len', ['local.get', `$${src}`]]],
|
|
522
|
+
['local.set', `$${slen}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]],
|
|
431
523
|
['local.set', `$${si}`, ['i32.const', 0]],
|
|
432
524
|
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
433
525
|
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${si}`], ['local.get', `$${slen}`]]],
|
|
434
|
-
['local.set', `$${out}`, ['call', '$__arr_set_idx_ptr', ['local.get', `$${out}`], ['local.get', `$${pos}`], spreadItem]],
|
|
526
|
+
['local.set', `$${out}`, ['call', '$__arr_set_idx_ptr', ['i64.reinterpret_f64', ['local.get', `$${out}`]], ['local.get', `$${pos}`], spreadItem]],
|
|
435
527
|
['local.set', `$${pos}`, ['i32.add', ['local.get', `$${pos}`], ['i32.const', 1]]],
|
|
436
528
|
['local.set', `$${si}`, ['i32.add', ['local.get', `$${si}`], ['i32.const', 1]]],
|
|
437
529
|
['br', `$loop${id}`]]])
|
|
438
530
|
} else {
|
|
439
531
|
body.push(
|
|
440
|
-
['local.set', `$${out}`, ['call', '$__arr_set_idx_ptr', ['local.get', `$${out}`], ['local.get', `$${pos}`], asF64(emit(e))]],
|
|
532
|
+
['local.set', `$${out}`, ['call', '$__arr_set_idx_ptr', ['i64.reinterpret_f64', ['local.get', `$${out}`]], ['local.get', `$${pos}`], asF64(emit(e))]],
|
|
441
533
|
['local.set', `$${pos}`, ['i32.add', ['local.get', `$${pos}`], ['i32.const', 1]]])
|
|
442
534
|
}
|
|
443
535
|
}
|
|
@@ -469,7 +561,7 @@ export default (ctx) => {
|
|
|
469
561
|
if (r) return r
|
|
470
562
|
}
|
|
471
563
|
// Literal string key on schema-known object → direct payload slot read (skip __dyn_get)
|
|
472
|
-
const litKey =
|
|
564
|
+
const litKey = isLiteralStr(idx) ? idx[1]
|
|
473
565
|
: typeof arr === 'string' && lookupValType(arr) === VAL.OBJECT ? staticPropertyKey(idx)
|
|
474
566
|
: null
|
|
475
567
|
if (litKey != null && typeof arr === 'string' && ctx.schema.find) {
|
|
@@ -477,16 +569,16 @@ export default (ctx) => {
|
|
|
477
569
|
if (slot >= 0) {
|
|
478
570
|
inc('__ptr_offset')
|
|
479
571
|
return typed(['f64.load',
|
|
480
|
-
['i32.add', ['call', '$__ptr_offset', asF64(emit(arr))], ['i32.const', slot * 8]]], 'f64')
|
|
572
|
+
['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(emit(arr))]], ['i32.const', slot * 8]]], 'f64')
|
|
481
573
|
}
|
|
482
574
|
}
|
|
483
575
|
if (litKey != null && typeof arr === 'string' && lookupValType(arr) === VAL.HASH) {
|
|
484
576
|
inc('__hash_get_local_h')
|
|
485
|
-
return typed(['call', '$__hash_get_local_h',
|
|
577
|
+
return typed(['f64.reinterpret_i64', ['call', '$__hash_get_local_h', asI64(emit(arr)), asI64(emit(['str', litKey])), ['i32.const', strHashLiteral(litKey)]]], 'f64')
|
|
486
578
|
}
|
|
487
579
|
if (litKey != null && typeof arr !== 'string' && valTypeOf(arr) === VAL.HASH) {
|
|
488
580
|
inc('__hash_get_local_h')
|
|
489
|
-
return typed(['call', '$__hash_get_local_h',
|
|
581
|
+
return typed(['f64.reinterpret_i64', ['call', '$__hash_get_local_h', asI64(emit(arr)), asI64(emit(['str', litKey])), ['i32.const', strHashLiteral(litKey)]]], 'f64')
|
|
490
582
|
}
|
|
491
583
|
// Multi-value calls are materialized at call site (see '()' handler), so
|
|
492
584
|
// func()[i] works naturally — func() returns a heap array pointer, [i] indexes it.
|
|
@@ -496,16 +588,16 @@ export default (ctx) => {
|
|
|
496
588
|
const dynLoad = (objExpr, keyExpr) => {
|
|
497
589
|
if (ctx.transform.strict) err(`strict mode: dynamic property access \`${typeof arr === 'string' ? arr : '<expr>'}[<expr>]\` falls back to __dyn_get. Use a literal key or known typed-array receiver, or pass { strict: false }.`)
|
|
498
590
|
inc('__dyn_get')
|
|
499
|
-
return ['call', '$__dyn_get', objExpr, keyExpr]
|
|
591
|
+
return ['f64.reinterpret_i64', ['call', '$__dyn_get', ['i64.reinterpret_f64', objExpr], ['i64.reinterpret_f64', keyExpr]]]
|
|
500
592
|
}
|
|
501
|
-
const stringLoad = () => (inc('__str_idx'), ['call', '$__str_idx', ptrExpr, vi])
|
|
502
|
-
const arrayLoad = (['call', '$__typed_idx', ptrExpr, vi])
|
|
593
|
+
const stringLoad = () => (inc('__str_idx'), ['call', '$__str_idx', ['i64.reinterpret_f64', ptrExpr], vi])
|
|
594
|
+
const arrayLoad = (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], vi])
|
|
503
595
|
const emitDynamicKeyDispatch = (objExpr, numericLoad) => {
|
|
504
596
|
const keyTmp = temp()
|
|
505
597
|
inc('__is_str_key')
|
|
506
598
|
return typed(['block', ['result', 'f64'],
|
|
507
599
|
['local.set', `$${keyTmp}`, asF64(emit(idx))],
|
|
508
|
-
['if', ['result', 'f64'], ['call', '$__is_str_key', ['local.get', `$${keyTmp}`]],
|
|
600
|
+
['if', ['result', 'f64'], ['call', '$__is_str_key', ['i64.reinterpret_f64', ['local.get', `$${keyTmp}`]]],
|
|
509
601
|
['then', dynLoad(objExpr, ['local.get', `$${keyTmp}`])],
|
|
510
602
|
['else', numericLoad(['local.get', `$${keyTmp}`])]]], 'f64')
|
|
511
603
|
}
|
|
@@ -515,11 +607,31 @@ export default (ctx) => {
|
|
|
515
607
|
if (keyType === VAL.STRING) return typed(dynLoad(asF64(emit(arr)), asF64(emit(idx))), 'f64')
|
|
516
608
|
if (useRuntimeKeyDispatch)
|
|
517
609
|
return emitDynamicKeyDispatch(asF64(emit(arr)), keyExpr =>
|
|
518
|
-
['f64.load', ['i32.add', ['call', '$__ptr_offset', inner], ['i32.shl', asI32(typed(keyExpr, 'f64')), ['i32.const', 3]]]])
|
|
610
|
+
['f64.load', ['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', inner]], ['i32.shl', asI32(typed(keyExpr, 'f64')), ['i32.const', 3]]]])
|
|
519
611
|
return typed(
|
|
520
|
-
['f64.load', ['i32.add', ['call', '$__ptr_offset', inner], ['i32.shl', vi, ['i32.const', 3]]]],
|
|
612
|
+
['f64.load', ['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', inner]], ['i32.shl', vi, ['i32.const', 3]]]],
|
|
521
613
|
'f64')
|
|
522
614
|
}
|
|
615
|
+
// HASH receiver with runtime string key: probe the HASH directly via
|
|
616
|
+
// __hash_get_local. Mirrors the literal-key path above but defers the
|
|
617
|
+
// hash computation to runtime. When the key's val-type is unknown at
|
|
618
|
+
// compile time, gate on __is_str_key — HASH has no numeric-key
|
|
619
|
+
// semantics, so non-string keys return undef.
|
|
620
|
+
if (vt === VAL.HASH) {
|
|
621
|
+
if (keyType === VAL.STRING) {
|
|
622
|
+
inc('__hash_get_local')
|
|
623
|
+
return typed(['f64.reinterpret_i64', ['call', '$__hash_get_local', ['i64.reinterpret_f64', ptrExpr], asI64(emit(idx))]], 'f64')
|
|
624
|
+
}
|
|
625
|
+
if (useRuntimeKeyDispatch) {
|
|
626
|
+
inc('__hash_get_local', '__is_str_key')
|
|
627
|
+
const keyTmp = temp()
|
|
628
|
+
return typed(['block', ['result', 'f64'],
|
|
629
|
+
['local.set', `$${keyTmp}`, asF64(emit(idx))],
|
|
630
|
+
['if', ['result', 'f64'], ['call', '$__is_str_key', ['i64.reinterpret_f64', ['local.get', `$${keyTmp}`]]],
|
|
631
|
+
['then', ['f64.reinterpret_i64', ['call', '$__hash_get_local', ['i64.reinterpret_f64', ptrExpr], ['i64.reinterpret_f64', ['local.get', `$${keyTmp}`]]]]],
|
|
632
|
+
['else', undefExpr()]]], 'f64')
|
|
633
|
+
}
|
|
634
|
+
}
|
|
523
635
|
// Known array → direct f64 element load, skip string check
|
|
524
636
|
if (keyType === VAL.STRING)
|
|
525
637
|
return typed(dynLoad(ptrExpr, asF64(emit(idx))), 'f64')
|
|
@@ -539,7 +651,7 @@ export default (ctx) => {
|
|
|
539
651
|
// Fast path fires on schemaId (Array<{x,y,z}> shapes) OR plain elem-val
|
|
540
652
|
// (Array<NUMBER> from `.map(x => x*k)` etc.).
|
|
541
653
|
const rep = typeof arr === 'string' ? ctx.func.repByLocal?.get(arr) : null
|
|
542
|
-
const hasElemFact = rep?.arrayElemSchema != null
|
|
654
|
+
const hasElemFact = rep?.arrayElemSchema != null
|
|
543
655
|
if (hasElemFact && keyIsNum) {
|
|
544
656
|
inc('__ptr_offset')
|
|
545
657
|
// __ptr_offset returns i32 — base local must be i32 (not the default
|
|
@@ -550,10 +662,9 @@ export default (ctx) => {
|
|
|
550
662
|
return typed(['f64.load',
|
|
551
663
|
['i32.add',
|
|
552
664
|
['local.tee', `$${baseI32}`,
|
|
553
|
-
['call', '$__ptr_offset', ptrExpr]],
|
|
665
|
+
['call', '$__ptr_offset', ['i64.reinterpret_f64', ptrExpr]]],
|
|
554
666
|
['i32.shl', vi, ['i32.const', 3]]]], 'f64')
|
|
555
667
|
}
|
|
556
|
-
inc('__arr_idx')
|
|
557
668
|
const baseTmp = temp()
|
|
558
669
|
// Numeric key (literal or known-NUMBER name) → skip __is_str_key dispatch;
|
|
559
670
|
// arrays don't honor string-key access for numeric keys (keys aren't coerced
|
|
@@ -563,11 +674,13 @@ export default (ctx) => {
|
|
|
563
674
|
['local.set', `$${baseTmp}`, ptrExpr],
|
|
564
675
|
emitDynamicKeyDispatch(typed(['local.get', `$${baseTmp}`], 'f64'), keyExpr => {
|
|
565
676
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
566
|
-
|
|
677
|
+
inc('__arr_idx')
|
|
678
|
+
return (['call', '$__arr_idx', ['i64.reinterpret_f64', ['local.get', `$${baseTmp}`]], keyI32])
|
|
567
679
|
})], 'f64')
|
|
680
|
+
inc('__arr_idx_known')
|
|
568
681
|
return typed(['block', ['result', 'f64'],
|
|
569
682
|
['local.set', `$${baseTmp}`, ptrExpr],
|
|
570
|
-
(['call', '$
|
|
683
|
+
(['call', '$__arr_idx_known', ['i64.reinterpret_f64', ['local.get', `$${baseTmp}`]], vi])], 'f64')
|
|
571
684
|
}
|
|
572
685
|
// Known string → single-char SSO string
|
|
573
686
|
if (vt === 'string')
|
|
@@ -582,30 +695,26 @@ export default (ctx) => {
|
|
|
582
695
|
if (useRuntimeKeyDispatch && !keyIsNum)
|
|
583
696
|
return emitDynamicKeyDispatch(ptrExpr, keyExpr => {
|
|
584
697
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
585
|
-
return (['call', '$__typed_idx', ptrExpr, keyI32])
|
|
698
|
+
return (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])
|
|
586
699
|
})
|
|
587
|
-
return typed((['call', '$__typed_idx', ptrExpr, vi]), 'f64')
|
|
700
|
+
return typed((['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], vi]), 'f64')
|
|
588
701
|
}
|
|
589
702
|
if (useRuntimeKeyDispatch)
|
|
590
703
|
return emitDynamicKeyDispatch(ptrExpr, keyExpr => {
|
|
591
704
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
592
705
|
if (ctx.module.modules['string']) {
|
|
593
706
|
return ['if', ['result', 'f64'],
|
|
594
|
-
['i32.
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
['then', (inc('__str_idx'), ['call', '$__str_idx', ptrExpr, keyI32])],
|
|
598
|
-
['else', (['call', '$__typed_idx', ptrExpr, keyI32])]]
|
|
707
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ptrExpr]], ['i32.const', PTR.STRING]],
|
|
708
|
+
['then', (inc('__str_idx'), ['call', '$__str_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])],
|
|
709
|
+
['else', (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])]]
|
|
599
710
|
}
|
|
600
|
-
return (['call', '$__typed_idx', ptrExpr, keyI32])
|
|
711
|
+
return (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])
|
|
601
712
|
})
|
|
602
713
|
// Unknown → runtime dispatch (string module loaded → check ptr_type)
|
|
603
714
|
if (ctx.module.modules['string'])
|
|
604
715
|
return typed(
|
|
605
716
|
['if', ['result', 'f64'],
|
|
606
|
-
['i32.
|
|
607
|
-
['i32.eq', ['call', '$__ptr_type', ptrExpr], ['i32.const', PTR.STRING]],
|
|
608
|
-
['i32.eq', ['call', '$__ptr_type', ptrExpr], ['i32.const', PTR.SSO]]],
|
|
717
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ptrExpr]], ['i32.const', PTR.STRING]],
|
|
609
718
|
['then', stringLoad()],
|
|
610
719
|
['else', arrayLoad]],
|
|
611
720
|
'f64')
|
|
@@ -637,7 +746,7 @@ export default (ctx) => {
|
|
|
637
746
|
// dispatch and prologue entirely; on grow we re-extract offset because the
|
|
638
747
|
// alloc may have relocated the buffer.
|
|
639
748
|
body.push(
|
|
640
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]],
|
|
749
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
641
750
|
['local.set', `$${len}`,
|
|
642
751
|
['i32.load', ['i32.sub', ['local.get', `$${pushBase}`], ['i32.const', 8]]]],
|
|
643
752
|
['if',
|
|
@@ -645,17 +754,17 @@ export default (ctx) => {
|
|
|
645
754
|
['i32.load', ['i32.sub', ['local.get', `$${pushBase}`], ['i32.const', 4]]],
|
|
646
755
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]],
|
|
647
756
|
['then',
|
|
648
|
-
['local.set', `$${t}`, ['call', `$${grow}`, ['local.get', `$${t}`],
|
|
757
|
+
['local.set', `$${t}`, ['call', `$${grow}`, ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
649
758
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]]],
|
|
650
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]],
|
|
759
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]],
|
|
651
760
|
)
|
|
652
761
|
} else {
|
|
653
762
|
body.push(
|
|
654
|
-
['local.set', `$${len}`, ['call', '$__len', ['local.get', `$${t}`]]],
|
|
763
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
655
764
|
// Grow if needed: ensure cap >= len + vals.length
|
|
656
|
-
['local.set', `$${t}`, ['call', `$${grow}`, ['local.get', `$${t}`],
|
|
765
|
+
['local.set', `$${t}`, ['call', `$${grow}`, ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
657
766
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]]],
|
|
658
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]],
|
|
767
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
659
768
|
)
|
|
660
769
|
}
|
|
661
770
|
|
|
@@ -671,7 +780,7 @@ export default (ctx) => {
|
|
|
671
780
|
}
|
|
672
781
|
|
|
673
782
|
// Update length header, update source variable (pointer may have changed from grow), return new length
|
|
674
|
-
body.push(['call', '$__set_len', ['local.get', `$${t}`], ['local.get', `$${len}`]])
|
|
783
|
+
body.push(['call', '$__set_len', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['local.get', `$${len}`]])
|
|
675
784
|
// Update the source variable if it's a named variable (so arr still points to valid memory)
|
|
676
785
|
if (typeof arr === 'string') {
|
|
677
786
|
if (ctx.func.boxed?.has(arr)) {
|
|
@@ -694,34 +803,36 @@ export default (ctx) => {
|
|
|
694
803
|
// Known ARRAY → inline len (skips __len dispatch tree).
|
|
695
804
|
const vt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
|
696
805
|
const rawLen = vt === VAL.ARRAY
|
|
697
|
-
? ['i32.load', ['i32.sub', ['call', '$__ptr_offset', ['local.get', `$${t}`]], ['i32.const', 8]]]
|
|
698
|
-
: ['call', '$__len', ['local.get', `$${t}`]]
|
|
806
|
+
? ['i32.load', ['i32.sub', ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]], ['i32.const', 8]]]
|
|
807
|
+
: ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]
|
|
699
808
|
return typed(['block', ['result', 'f64'],
|
|
700
809
|
['local.set', `$${t}`, va],
|
|
701
810
|
['local.set', `$${len}`, ['i32.sub', rawLen, ['i32.const', 1]]],
|
|
702
|
-
['call', '$__set_len', ['local.get', `$${t}`], ['local.get', `$${len}`]],
|
|
811
|
+
['call', '$__set_len', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['local.get', `$${len}`]],
|
|
703
812
|
['f64.load',
|
|
704
|
-
['i32.add', ['call', '$__ptr_offset', ['local.get', `$${t}`]], ['i32.shl', ['local.get', `$${len}`], ['i32.const', 3]]]]], 'f64')
|
|
813
|
+
['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]], ['i32.shl', ['local.get', `$${len}`], ['i32.const', 3]]]]], 'f64')
|
|
705
814
|
}
|
|
706
815
|
|
|
707
816
|
// .shift() → remove first element, shift remaining left, return removed
|
|
708
|
-
ctx.core.emit['.shift'] =
|
|
817
|
+
ctx.core.emit['.shift'] = (arr) => (inc('__arr_shift'),
|
|
818
|
+
typed(['call', '$__arr_shift', asI64(emit(arr))], 'f64'))
|
|
709
819
|
|
|
710
|
-
ctx.core.stdlib['__arr_shift'] = () => `(func $__arr_shift (param $arr
|
|
711
|
-
(local $
|
|
712
|
-
(local
|
|
713
|
-
(local.set $rawOff (i32.wrap_i64 (i64.and (local.get $
|
|
820
|
+
ctx.core.stdlib['__arr_shift'] = () => `(func $__arr_shift (param $arr i64) (result f64)
|
|
821
|
+
(local $rawOff i32) (local $off i32) (local $newOff i32) (local $len i32) (local $cap i32) (local $val f64)
|
|
822
|
+
${needsArrayDynMove() ? '(local $oldProps f64) (local $root f64)' : ''}
|
|
823
|
+
(local.set $rawOff (i32.wrap_i64 (i64.and (local.get $arr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
714
824
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
715
825
|
(if (result f64) (i32.lt_u (local.get $off) (i32.const 8))
|
|
716
|
-
(then (f64.const
|
|
826
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
717
827
|
(else
|
|
718
828
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
719
829
|
(if (result f64) (i32.le_s (local.get $len) (i32.const 0))
|
|
720
|
-
(then (f64.const
|
|
830
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
721
831
|
(else
|
|
722
832
|
(local.set $val (f64.load (local.get $off)))
|
|
723
833
|
(local.set $cap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
724
834
|
(local.set $newOff (i32.add (local.get $off) (i32.const 8)))
|
|
835
|
+
${headerPropsToGlobalIR()}
|
|
725
836
|
(i32.store (local.get $off) (i32.sub (local.get $len) (i32.const 1)))
|
|
726
837
|
(i32.store (i32.add (local.get $off) (i32.const 4))
|
|
727
838
|
(select (i32.sub (local.get $cap) (i32.const 1)) (i32.const 0) (i32.gt_s (local.get $cap) (i32.const 0))))
|
|
@@ -746,10 +857,10 @@ export default (ctx) => {
|
|
|
746
857
|
const svt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
|
747
858
|
const lenInit = svt === VAL.ARRAY
|
|
748
859
|
? ['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${off}`], ['i32.const', 8]]]]
|
|
749
|
-
: ['local.set', `$${len}`, ['call', '$__len', va]]
|
|
860
|
+
: ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]]
|
|
750
861
|
const body = [
|
|
751
862
|
recv.setup,
|
|
752
|
-
['local.set', `$${off}`, ['call', '$__ptr_offset', va]],
|
|
863
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]]],
|
|
753
864
|
lenInit,
|
|
754
865
|
// clamp start to [0, len]
|
|
755
866
|
['local.set', `$${s}`, vs],
|
|
@@ -788,26 +899,27 @@ export default (ctx) => {
|
|
|
788
899
|
['i32.sub', ['i32.sub', ['local.get', `$${len}`], ['local.get', `$${s}`]], ['local.get', `$${cnt}`]],
|
|
789
900
|
['i32.const', 3]]],
|
|
790
901
|
// update length
|
|
791
|
-
['call', '$__set_len', va, ['i32.sub', ['local.get', `$${len}`], ['local.get', `$${cnt}`]]],
|
|
902
|
+
['call', '$__set_len', ['i64.reinterpret_f64', va], ['i32.sub', ['local.get', `$${len}`], ['local.get', `$${cnt}`]]],
|
|
792
903
|
out.ptr,
|
|
793
904
|
]
|
|
794
905
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
795
906
|
}
|
|
796
907
|
|
|
797
908
|
// .unshift(val) → prepend element, shift existing right
|
|
798
|
-
ctx.core.emit['.unshift'] =
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
(local
|
|
803
|
-
(local.set $
|
|
804
|
-
(local.set $
|
|
909
|
+
ctx.core.emit['.unshift'] = (arr, val) => (inc('__arr_unshift'),
|
|
910
|
+
typed(['call', '$__arr_unshift', asI64(emit(arr)), asF64(emit(val))], 'f64'))
|
|
911
|
+
|
|
912
|
+
ctx.core.stdlib['__arr_unshift'] = `(func $__arr_unshift (param $arr i64) (param $val f64) (result f64)
|
|
913
|
+
(local $off i32) (local $len i32) (local $a f64)
|
|
914
|
+
(local.set $a (call $__arr_grow (local.get $arr) (i32.add (call $__len (local.get $arr)) (i32.const 1))))
|
|
915
|
+
(local.set $off (call $__ptr_offset (i64.reinterpret_f64 (local.get $a))))
|
|
916
|
+
(local.set $len (call $__len (i64.reinterpret_f64 (local.get $a))))
|
|
805
917
|
(memory.copy
|
|
806
918
|
(i32.add (local.get $off) (i32.const 8))
|
|
807
919
|
(local.get $off)
|
|
808
920
|
(i32.shl (local.get $len) (i32.const 3)))
|
|
809
921
|
(f64.store (local.get $off) (local.get $val))
|
|
810
|
-
(call $__set_len (local.get $
|
|
922
|
+
(call $__set_len (i64.reinterpret_f64 (local.get $a)) (i32.add (local.get $len) (i32.const 1)))
|
|
811
923
|
(f64.convert_i32_s (i32.add (local.get $len) (i32.const 1))))`
|
|
812
924
|
|
|
813
925
|
// .some(fn) → return 1 if any element passes, else 0 (early exit)
|
|
@@ -929,7 +1041,7 @@ export default (ctx) => {
|
|
|
929
1041
|
const up = detectUpstream(arr)
|
|
930
1042
|
if (up && up.method === 'filter' && isPureCallback(fn)) {
|
|
931
1043
|
const recv = hoistArrayValue(up.source)
|
|
932
|
-
const count = tempI32('fc'), maxLen = tempI32('fm')
|
|
1044
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb')
|
|
933
1045
|
const upReps = callbackArgReps(up.source)
|
|
934
1046
|
const filterCb = makeCallback(up.fn, upReps), mapCb = makeCallback(fn, upReps)
|
|
935
1047
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'fm' })
|
|
@@ -938,28 +1050,32 @@ export default (ctx) => {
|
|
|
938
1050
|
['then',
|
|
939
1051
|
elemStore(out.local, count, asF64(mapCb.call([item, idxArg(mapCb, count)]))),
|
|
940
1052
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
941
|
-
], maxLen)
|
|
1053
|
+
], maxLen, base)
|
|
1054
|
+
inc('__ptr_offset')
|
|
942
1055
|
return typed(['block', ['result', 'f64'],
|
|
943
1056
|
recv.setup, filterCb.setup, mapCb.setup,
|
|
944
|
-
['local.set', `$${
|
|
1057
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1058
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
945
1059
|
out.init, ['local.set', `$${count}`, ['i32.const', 0]],
|
|
946
1060
|
...loop,
|
|
947
1061
|
['i32.store', ['i32.sub', ['local.get', `$${out.local}`], ['i32.const', 8]], ['local.get', `$${count}`]],
|
|
948
1062
|
out.ptr], 'f64')
|
|
949
1063
|
}
|
|
950
1064
|
const recv = hoistArrayValue(arr)
|
|
951
|
-
const len = tempI32('ml')
|
|
1065
|
+
const len = tempI32('ml'), base = tempI32('mb')
|
|
952
1066
|
const cb = makeCallback(fn, callbackArgReps(arr))
|
|
953
1067
|
const lenIR = ['local.get', `$${len}`]
|
|
954
1068
|
const out = allocPtr({ type: PTR.ARRAY, len: lenIR, tag: 'mo' })
|
|
955
1069
|
// Reuse the precomputed len local in arrayLoop (skip its internal load).
|
|
956
1070
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
957
1071
|
elemStore(out.local, i, asF64(cb.call([item, idxArg(cb, i)])))
|
|
958
|
-
], len)
|
|
1072
|
+
], len, base)
|
|
1073
|
+
inc('__ptr_offset')
|
|
959
1074
|
return typed(['block', ['result', 'f64'],
|
|
960
1075
|
recv.setup,
|
|
961
1076
|
cb.setup,
|
|
962
|
-
['local.set', `$${
|
|
1077
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1078
|
+
['local.set', `$${len}`, arrayLenFromPtr(base)],
|
|
963
1079
|
out.init,
|
|
964
1080
|
...loop,
|
|
965
1081
|
out.ptr], 'f64')
|
|
@@ -970,7 +1086,7 @@ export default (ctx) => {
|
|
|
970
1086
|
const up = detectUpstream(arr)
|
|
971
1087
|
if (up && up.method === 'map' && isPureCallback(fn)) {
|
|
972
1088
|
const recv = hoistArrayValue(up.source)
|
|
973
|
-
const count = tempI32('fc'), maxLen = tempI32('fm'), mapped = temp('mv')
|
|
1089
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb'), mapped = temp('mv')
|
|
974
1090
|
const upReps = callbackArgReps(up.source)
|
|
975
1091
|
const mapCb = makeCallback(up.fn, upReps), filterCb = makeCallback(fn)
|
|
976
1092
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'mf' })
|
|
@@ -980,17 +1096,19 @@ export default (ctx) => {
|
|
|
980
1096
|
['then',
|
|
981
1097
|
['f64.store', ['i32.add', ['local.get', `$${out.local}`], ['i32.shl', ['local.get', `$${count}`], ['i32.const', 3]]], ['local.get', `$${mapped}`]],
|
|
982
1098
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
983
|
-
], maxLen)
|
|
1099
|
+
], maxLen, base)
|
|
1100
|
+
inc('__ptr_offset')
|
|
984
1101
|
return typed(['block', ['result', 'f64'],
|
|
985
1102
|
recv.setup, mapCb.setup, filterCb.setup,
|
|
986
|
-
['local.set', `$${
|
|
1103
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1104
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
987
1105
|
out.init, ['local.set', `$${count}`, ['i32.const', 0]],
|
|
988
1106
|
...loop,
|
|
989
1107
|
['i32.store', ['i32.sub', ['local.get', `$${out.local}`], ['i32.const', 8]], ['local.get', `$${count}`]],
|
|
990
1108
|
out.ptr], 'f64')
|
|
991
1109
|
}
|
|
992
1110
|
const recv = hoistArrayValue(arr)
|
|
993
|
-
const count = tempI32('fc'), maxLen = tempI32('fm')
|
|
1111
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb')
|
|
994
1112
|
const cb = makeCallback(fn, callbackArgReps(arr))
|
|
995
1113
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'fo' })
|
|
996
1114
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
@@ -998,11 +1116,13 @@ export default (ctx) => {
|
|
|
998
1116
|
['then',
|
|
999
1117
|
['f64.store', ['i32.add', ['local.get', `$${out.local}`], ['i32.shl', ['local.get', `$${count}`], ['i32.const', 3]]], item],
|
|
1000
1118
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
1001
|
-
], maxLen)
|
|
1119
|
+
], maxLen, base)
|
|
1120
|
+
inc('__ptr_offset')
|
|
1002
1121
|
return typed(['block', ['result', 'f64'],
|
|
1003
1122
|
recv.setup,
|
|
1004
1123
|
cb.setup,
|
|
1005
|
-
['local.set', `$${
|
|
1124
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1125
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
1006
1126
|
out.init,
|
|
1007
1127
|
['local.set', `$${count}`, ['i32.const', 0]],
|
|
1008
1128
|
...loop,
|
|
@@ -1112,13 +1232,139 @@ export default (ctx) => {
|
|
|
1112
1232
|
['local.get', `$${result}`]], 'f64')
|
|
1113
1233
|
}
|
|
1114
1234
|
|
|
1235
|
+
// .reverse() → in-place swap arr[i] ↔ arr[len-1-i], returns the array.
|
|
1236
|
+
ctx.core.emit['.reverse'] = (arr) => {
|
|
1237
|
+
const recv = hoistArrayValue(arr)
|
|
1238
|
+
const arrTmp = temp('rv')
|
|
1239
|
+
const base = tempI32('rb')
|
|
1240
|
+
const len = tempI32('rl')
|
|
1241
|
+
const i = tempI32('ri')
|
|
1242
|
+
const j = tempI32('rj')
|
|
1243
|
+
const tmp = temp('rt')
|
|
1244
|
+
const id = ctx.func.uniq++
|
|
1245
|
+
const exit = `$revexit${id}`, loop = `$revloop${id}`
|
|
1246
|
+
|
|
1247
|
+
inc('__ptr_offset')
|
|
1248
|
+
|
|
1249
|
+
const addr = (idxIR) => ['i32.add', ['local.get', `$${base}`], ['i32.shl', idxIR, ['i32.const', 3]]]
|
|
1250
|
+
|
|
1251
|
+
return typed(['block', ['result', 'f64'],
|
|
1252
|
+
recv.setup,
|
|
1253
|
+
['local.set', `$${arrTmp}`, recv.value],
|
|
1254
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${arrTmp}`]]]],
|
|
1255
|
+
['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${base}`], ['i32.const', 8]]]],
|
|
1256
|
+
['local.set', `$${i}`, ['i32.const', 0]],
|
|
1257
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${len}`], ['i32.const', 1]]],
|
|
1258
|
+
['block', exit,
|
|
1259
|
+
['loop', loop,
|
|
1260
|
+
['br_if', exit, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${j}`]]],
|
|
1261
|
+
['local.set', `$${tmp}`, ['f64.load', addr(['local.get', `$${i}`])]],
|
|
1262
|
+
['f64.store', addr(['local.get', `$${i}`]), ['f64.load', addr(['local.get', `$${j}`])]],
|
|
1263
|
+
['f64.store', addr(['local.get', `$${j}`]), ['local.get', `$${tmp}`]],
|
|
1264
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1265
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${j}`], ['i32.const', 1]]],
|
|
1266
|
+
['br', loop]]],
|
|
1267
|
+
['local.get', `$${arrTmp}`]
|
|
1268
|
+
], 'f64')
|
|
1269
|
+
}
|
|
1270
|
+
|
|
1271
|
+
// Insertion sort — stable, in-place, O(n²). The comparator is called per
|
|
1272
|
+
// shift; positive return → swap. NaN returns become "no swap" via f64.gt's
|
|
1273
|
+
// IEEE 754 semantics (NaN compares false), matching the spec's NaN-as-0
|
|
1274
|
+
// behavior. When fn is omitted, elements are compared as strings via
|
|
1275
|
+
// __to_str → __str_cmp (byte-wise; NOT locale-aware).
|
|
1276
|
+
ctx.core.emit['.sort'] = (arr, fn) => {
|
|
1277
|
+
const recv = hoistArrayValue(arr)
|
|
1278
|
+
const arrTmp = temp('sr')
|
|
1279
|
+
const base = tempI32('sb')
|
|
1280
|
+
const len = tempI32('sl')
|
|
1281
|
+
const i = tempI32('si')
|
|
1282
|
+
const j = tempI32('sj')
|
|
1283
|
+
const cur = temp('sc')
|
|
1284
|
+
const neighbor = temp('sn')
|
|
1285
|
+
const id = ctx.func.uniq++
|
|
1286
|
+
const outerExit = `$sortexit${id}`, innerExit = `$sortinnerexit${id}`
|
|
1287
|
+
const outerLoop = `$sortouter${id}`, innerLoop = `$sortinner${id}`
|
|
1288
|
+
|
|
1289
|
+
let cmpExpr, cmpSetup
|
|
1290
|
+
if (fn == null) {
|
|
1291
|
+
inc('__to_str', '__str_cmp')
|
|
1292
|
+
cmpExpr = (aIR, bIR) => typed(['f64.convert_i32_s',
|
|
1293
|
+
['call', '$__str_cmp',
|
|
1294
|
+
['call', '$__to_str', ['i64.reinterpret_f64', aIR]],
|
|
1295
|
+
['call', '$__to_str', ['i64.reinterpret_f64', bIR]]
|
|
1296
|
+
]
|
|
1297
|
+
], 'f64')
|
|
1298
|
+
cmpSetup = ['nop']
|
|
1299
|
+
} else {
|
|
1300
|
+
const cb = makeCallback(fn, [])
|
|
1301
|
+
cmpSetup = cb.setup
|
|
1302
|
+
cmpExpr = (aIR, bIR) => asF64(cb.call([
|
|
1303
|
+
typed(aIR, 'f64'),
|
|
1304
|
+
typed(bIR, 'f64')
|
|
1305
|
+
]))
|
|
1306
|
+
}
|
|
1307
|
+
|
|
1308
|
+
inc('__ptr_offset')
|
|
1309
|
+
|
|
1310
|
+
const addr = (idxIR) => ['i32.add', ['local.get', `$${base}`], ['i32.shl', idxIR, ['i32.const', 3]]]
|
|
1311
|
+
const jPlus1 = ['i32.add', ['local.get', `$${j}`], ['i32.const', 1]]
|
|
1312
|
+
|
|
1313
|
+
return typed(['block', ['result', 'f64'],
|
|
1314
|
+
recv.setup,
|
|
1315
|
+
cmpSetup,
|
|
1316
|
+
['local.set', `$${arrTmp}`, recv.value],
|
|
1317
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${arrTmp}`]]]],
|
|
1318
|
+
['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${base}`], ['i32.const', 8]]]],
|
|
1319
|
+
|
|
1320
|
+
['local.set', `$${i}`, ['i32.const', 1]],
|
|
1321
|
+
['block', outerExit,
|
|
1322
|
+
['loop', outerLoop,
|
|
1323
|
+
['br_if', outerExit, ['i32.ge_s', ['local.get', `$${i}`], ['local.get', `$${len}`]]],
|
|
1324
|
+
['local.set', `$${cur}`, ['f64.load', addr(['local.get', `$${i}`])]],
|
|
1325
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1326
|
+
|
|
1327
|
+
['block', innerExit,
|
|
1328
|
+
['loop', innerLoop,
|
|
1329
|
+
['br_if', innerExit, ['i32.lt_s', ['local.get', `$${j}`], ['i32.const', 0]]],
|
|
1330
|
+
['local.set', `$${neighbor}`, ['f64.load', addr(['local.get', `$${j}`])]],
|
|
1331
|
+
// Break unless cmp(neighbor, cur) > 0. f64.gt is false for NaN.
|
|
1332
|
+
['br_if', innerExit, ['i32.eqz',
|
|
1333
|
+
['f64.gt',
|
|
1334
|
+
cmpExpr(['local.get', `$${neighbor}`], ['local.get', `$${cur}`]),
|
|
1335
|
+
['f64.const', 0]]]],
|
|
1336
|
+
['f64.store', addr(jPlus1), ['local.get', `$${neighbor}`]],
|
|
1337
|
+
['local.set', `$${j}`, ['i32.sub', ['local.get', `$${j}`], ['i32.const', 1]]],
|
|
1338
|
+
['br', innerLoop]]],
|
|
1339
|
+
|
|
1340
|
+
['f64.store', addr(jPlus1), ['local.get', `$${cur}`]],
|
|
1341
|
+
['local.set', `$${i}`, ['i32.add', ['local.get', `$${i}`], ['i32.const', 1]]],
|
|
1342
|
+
['br', outerLoop]]],
|
|
1343
|
+
|
|
1344
|
+
['local.get', `$${arrTmp}`]
|
|
1345
|
+
], 'f64')
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
// Boxed pointer values (strings/objects/etc.) carry NaN payloads, and
|
|
1349
|
+
// f64.eq treats NaN as not-equal to anything — even bit-identical NaN —
|
|
1350
|
+
// so a raw f64 compare misses string and reference matches. Route those
|
|
1351
|
+
// through __eq, the same helper `==` uses for STRING/BIGINT/cross-type.
|
|
1352
|
+
// f64.eq stays the fast path when the search value is statically NUMBER.
|
|
1353
|
+
const arrEqIR = (val) => {
|
|
1354
|
+
const vt = resolveValType(val, valTypeOf, lookupValType)
|
|
1355
|
+
if (vt === VAL.NUMBER) return (item, vv) => ['f64.eq', item, vv]
|
|
1356
|
+
inc('__eq')
|
|
1357
|
+
return (item, vv) => ['call', '$__eq', ['i64.reinterpret_f64', item], ['i64.reinterpret_f64', vv]]
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1115
1360
|
ctx.core.emit['.indexOf'] = (arr, val) => {
|
|
1116
1361
|
const recv = hoistArrayValue(arr)
|
|
1117
1362
|
const vv = asF64(emit(val))
|
|
1363
|
+
const eq = arrEqIR(val)
|
|
1118
1364
|
const result = tempI32('ix')
|
|
1119
1365
|
const exit = `$exit${ctx.func.uniq++}`
|
|
1120
1366
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
1121
|
-
['if',
|
|
1367
|
+
['if', eq(item, vv),
|
|
1122
1368
|
['then', ['local.set', `$${result}`, ['local.get', `$${i}`]], ['br', exit]]]
|
|
1123
1369
|
])
|
|
1124
1370
|
return typed(['block', ['result', 'f64'],
|
|
@@ -1131,10 +1377,11 @@ export default (ctx) => {
|
|
|
1131
1377
|
ctx.core.emit['.includes'] = (arr, val) => {
|
|
1132
1378
|
const recv = hoistArrayValue(arr)
|
|
1133
1379
|
const vv = asF64(emit(val))
|
|
1380
|
+
const eq = arrEqIR(val)
|
|
1134
1381
|
const result = tempI32('ic')
|
|
1135
1382
|
const exit = `$exit${ctx.func.uniq++}`
|
|
1136
1383
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
1137
|
-
['if',
|
|
1384
|
+
['if', eq(item, vv),
|
|
1138
1385
|
['then', ['local.set', `$${result}`, ['i32.const', 1]], ['br', exit]]]
|
|
1139
1386
|
])
|
|
1140
1387
|
return typed(['block', ['result', 'f64'],
|
|
@@ -1146,6 +1393,19 @@ export default (ctx) => {
|
|
|
1146
1393
|
|
|
1147
1394
|
// .at(i) → array element with negative index support
|
|
1148
1395
|
ctx.core.emit['.array:at'] = (arr, idx) => {
|
|
1396
|
+
const vt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
|
1397
|
+
if (vt === VAL.ARRAY) {
|
|
1398
|
+
inc('__ptr_offset')
|
|
1399
|
+
const t = tempI32('ai'), off = tempI32('ao')
|
|
1400
|
+
return typed(['block', ['result', 'f64'],
|
|
1401
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(emit(arr))]]],
|
|
1402
|
+
['local.set', `$${t}`, asI32(emit(idx))],
|
|
1403
|
+
['if', ['i32.lt_s', ['local.get', `$${t}`], ['i32.const', 0]],
|
|
1404
|
+
['then', ['local.set', `$${t}`, ['i32.add', ['local.get', `$${t}`],
|
|
1405
|
+
['i32.load', ['i32.sub', ['local.get', `$${off}`], ['i32.const', 8]]]]]]],
|
|
1406
|
+
['f64.load', ['i32.add', ['local.get', `$${off}`],
|
|
1407
|
+
['i32.shl', ['local.get', `$${t}`], ['i32.const', 3]]]]], 'f64')
|
|
1408
|
+
}
|
|
1149
1409
|
const t = tempI32('ai'), a = temp('aa')
|
|
1150
1410
|
return typed(['block', ['result', 'f64'],
|
|
1151
1411
|
['local.set', `$${a}`, asF64(emit(arr))],
|
|
@@ -1153,10 +1413,11 @@ export default (ctx) => {
|
|
|
1153
1413
|
// Negative index: t += length
|
|
1154
1414
|
['if', ['i32.lt_s', ['local.get', `$${t}`], ['i32.const', 0]],
|
|
1155
1415
|
['then', ['local.set', `$${t}`, ['i32.add', ['local.get', `$${t}`],
|
|
1156
|
-
['call', '$__len', ['local.get', `$${a}`]]]]]],
|
|
1157
|
-
['f64.load', ['i32.add', ['call', '$__ptr_offset', ['local.get', `$${a}`]],
|
|
1416
|
+
['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${a}`]]]]]]],
|
|
1417
|
+
['f64.load', ['i32.add', ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${a}`]]],
|
|
1158
1418
|
['i32.shl', ['local.get', `$${t}`], ['i32.const', 3]]]]], 'f64')
|
|
1159
1419
|
}
|
|
1420
|
+
ctx.core.emit['.at'] = ctx.core.emit['.array:at']
|
|
1160
1421
|
|
|
1161
1422
|
ctx.core.emit['.slice'] = (arr, start, end) => {
|
|
1162
1423
|
// BUFFER slice → byte-level copy handled in typedarray module.
|
|
@@ -1171,7 +1432,7 @@ export default (ctx) => {
|
|
|
1171
1432
|
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${outLen}`], tag: 'so' })
|
|
1172
1433
|
return typed(['block', ['result', 'f64'],
|
|
1173
1434
|
recv.setup,
|
|
1174
|
-
['local.set', `$${ptr}`, ['call', '$__ptr_offset', recv.value]],
|
|
1435
|
+
['local.set', `$${ptr}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1175
1436
|
['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${ptr}`], ['i32.const', 8]]]],
|
|
1176
1437
|
['local.set', `$${s}`, rawStart],
|
|
1177
1438
|
['if', ['i32.lt_s', ['local.get', `$${s}`], ['i32.const', 0]],
|
|
@@ -1204,14 +1465,14 @@ export default (ctx) => {
|
|
|
1204
1465
|
// Calculate total length
|
|
1205
1466
|
const body = [
|
|
1206
1467
|
recv.setup,
|
|
1207
|
-
['local.set', `$${len}`, ['call', '$__len', va]],
|
|
1468
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]],
|
|
1208
1469
|
]
|
|
1209
1470
|
|
|
1210
1471
|
const otherVals = []
|
|
1211
1472
|
for (const other of others) {
|
|
1212
1473
|
const vo = asF64(emit(other))
|
|
1213
1474
|
otherVals.push(vo)
|
|
1214
|
-
body.push(['local.set', `$${len}`, ['i32.add', ['local.get', `$${len}`], ['call', '$__len', vo]]])
|
|
1475
|
+
body.push(['local.set', `$${len}`, ['i32.add', ['local.get', `$${len}`], ['call', '$__len', ['i64.reinterpret_f64', vo]]]])
|
|
1215
1476
|
}
|
|
1216
1477
|
|
|
1217
1478
|
body.push(out.init)
|
|
@@ -1220,8 +1481,8 @@ export default (ctx) => {
|
|
|
1220
1481
|
const srcOff = tempI32('co')
|
|
1221
1482
|
body.push(
|
|
1222
1483
|
['local.set', `$${pos}`, ['i32.const', 0]],
|
|
1223
|
-
['local.set', `$${len}`, ['call', '$__len', va]],
|
|
1224
|
-
['local.set', `$${srcOff}`, ['call', '$__ptr_offset', va]]
|
|
1484
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]],
|
|
1485
|
+
['local.set', `$${srcOff}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]]]
|
|
1225
1486
|
)
|
|
1226
1487
|
const id = ctx.func.uniq++
|
|
1227
1488
|
body.push(
|
|
@@ -1236,7 +1497,7 @@ export default (ctx) => {
|
|
|
1236
1497
|
|
|
1237
1498
|
// Copy each other array
|
|
1238
1499
|
const offset = tempI32('off')
|
|
1239
|
-
body.push(['local.set', `$${offset}`, ['call', '$__len', va]])
|
|
1500
|
+
body.push(['local.set', `$${offset}`, ['call', '$__len', ['i64.reinterpret_f64', va]]])
|
|
1240
1501
|
|
|
1241
1502
|
const otherOff = tempI32('co2')
|
|
1242
1503
|
for (let i = 0; i < otherVals.length; i++) {
|
|
@@ -1244,8 +1505,8 @@ export default (ctx) => {
|
|
|
1244
1505
|
const id2 = ctx.func.uniq++
|
|
1245
1506
|
body.push(
|
|
1246
1507
|
['local.set', `$${pos}`, ['i32.const', 0]],
|
|
1247
|
-
['local.set', `$${len}`, ['call', '$__len', vo]],
|
|
1248
|
-
['local.set', `$${otherOff}`, ['call', '$__ptr_offset', vo]],
|
|
1508
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', vo]]],
|
|
1509
|
+
['local.set', `$${otherOff}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', vo]]],
|
|
1249
1510
|
['block', `$done${id2}`, ['loop', `$loop${id2}`,
|
|
1250
1511
|
['br_if', `$done${id2}`, ['i32.ge_s', ['local.get', `$${pos}`], ['local.get', `$${len}`]]],
|
|
1251
1512
|
['f64.store',
|
|
@@ -1262,7 +1523,7 @@ export default (ctx) => {
|
|
|
1262
1523
|
}
|
|
1263
1524
|
|
|
1264
1525
|
// .flat() → flatten one level of nested arrays
|
|
1265
|
-
ctx.core.stdlib['__arr_flat'] = `(func $__arr_flat (param $src
|
|
1526
|
+
ctx.core.stdlib['__arr_flat'] = `(func $__arr_flat (param $src i64) (result f64)
|
|
1266
1527
|
(local $len i32) (local $off i32) (local $i i32) (local $total i32) (local $dst i32) (local $pos i32)
|
|
1267
1528
|
(local $elem f64) (local $subLen i32) (local $subOff i32) (local $j i32)
|
|
1268
1529
|
(local.set $off (call $__ptr_offset (local.get $src)))
|
|
@@ -1273,8 +1534,8 @@ export default (ctx) => {
|
|
|
1273
1534
|
(br_if $c1 (i32.ge_s (local.get $i) (local.get $len)))
|
|
1274
1535
|
(local.set $elem (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
1275
1536
|
(if (i32.and (f64.ne (local.get $elem) (local.get $elem))
|
|
1276
|
-
(i32.eq (call $__ptr_type (local.get $elem)) (i32.const ${PTR.ARRAY})))
|
|
1277
|
-
(then (local.set $total (i32.add (local.get $total) (call $__len (local.get $elem)))))
|
|
1537
|
+
(i32.eq (call $__ptr_type (i64.reinterpret_f64 (local.get $elem))) (i32.const ${PTR.ARRAY})))
|
|
1538
|
+
(then (local.set $total (i32.add (local.get $total) (call $__len (i64.reinterpret_f64 (local.get $elem))))))
|
|
1278
1539
|
(else (local.set $total (i32.add (local.get $total) (i32.const 1)))))
|
|
1279
1540
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
1280
1541
|
(br $cl1)))
|
|
@@ -1289,10 +1550,10 @@ export default (ctx) => {
|
|
|
1289
1550
|
(br_if $c2 (i32.ge_s (local.get $i) (local.get $len)))
|
|
1290
1551
|
(local.set $elem (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
1291
1552
|
(if (i32.and (f64.ne (local.get $elem) (local.get $elem))
|
|
1292
|
-
(i32.eq (call $__ptr_type (local.get $elem)) (i32.const ${PTR.ARRAY})))
|
|
1553
|
+
(i32.eq (call $__ptr_type (i64.reinterpret_f64 (local.get $elem))) (i32.const ${PTR.ARRAY})))
|
|
1293
1554
|
(then
|
|
1294
|
-
(local.set $subOff (call $__ptr_offset (local.get $elem)))
|
|
1295
|
-
(local.set $subLen (call $__len (local.get $elem)))
|
|
1555
|
+
(local.set $subOff (call $__ptr_offset (i64.reinterpret_f64 (local.get $elem))))
|
|
1556
|
+
(local.set $subLen (call $__len (i64.reinterpret_f64 (local.get $elem))))
|
|
1296
1557
|
(local.set $j (i32.const 0))
|
|
1297
1558
|
(block $s (loop $sl
|
|
1298
1559
|
(br_if $s (i32.ge_s (local.get $j) (local.get $subLen)))
|
|
@@ -1308,15 +1569,17 @@ export default (ctx) => {
|
|
|
1308
1569
|
(br $cl2)))
|
|
1309
1570
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $dst)))`
|
|
1310
1571
|
|
|
1311
|
-
ctx.core.emit['.flat'] =
|
|
1572
|
+
ctx.core.emit['.flat'] = (arr) => (inc('__arr_flat'),
|
|
1573
|
+
typed(['call', '$__arr_flat', asI64(emit(arr))], 'f64'))
|
|
1312
1574
|
|
|
1313
1575
|
// .flatMap(fn) → map then flatten
|
|
1314
1576
|
ctx.core.emit['.flatMap'] = (arr, fn) => {
|
|
1315
1577
|
const mapped = ctx.core.emit['.map'](arr, fn)
|
|
1316
1578
|
inc('__arr_flat')
|
|
1317
|
-
return typed(['call', '$__arr_flat',
|
|
1579
|
+
return typed(['call', '$__arr_flat', asI64(mapped)], 'f64')
|
|
1318
1580
|
}
|
|
1319
1581
|
|
|
1320
1582
|
// .join(sep) → concatenate array elements with separator string
|
|
1321
|
-
ctx.core.emit['.join'] =
|
|
1583
|
+
ctx.core.emit['.join'] = (arr, sep) => (inc('__str_join'),
|
|
1584
|
+
typed(['call', '$__str_join', asI64(emit(arr)), asI64(emit(sep))], 'f64'))
|
|
1322
1585
|
}
|