jz 0.1.0 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +221 -62
- package/cli.js +34 -8
- package/index.js +133 -14
- package/module/array.js +399 -131
- package/module/collection.js +457 -212
- package/module/console.js +170 -87
- package/module/core.js +247 -143
- package/module/date.js +691 -0
- package/module/function.js +84 -23
- package/module/index.js +2 -1
- package/module/json.js +485 -138
- package/module/math.js +81 -40
- package/module/number.js +189 -83
- package/module/object.js +228 -46
- package/module/regex.js +34 -30
- package/module/schema.js +17 -18
- package/module/string.js +483 -227
- package/module/symbol.js +6 -4
- package/module/timer.js +90 -32
- package/module/typedarray.js +176 -44
- package/package.json +5 -10
- package/src/analyze.js +639 -124
- package/src/autoload.js +183 -0
- package/src/compile.js +448 -1083
- package/src/ctx.js +42 -12
- package/src/emit.js +646 -147
- package/src/host.js +273 -68
- package/src/ir.js +81 -31
- package/src/jzify.js +220 -36
- package/src/narrow.js +956 -0
- package/src/optimize.js +628 -42
- package/src/plan.js +1233 -0
- package/src/prepare.js +438 -256
- package/src/vectorize.js +1016 -0
- package/wasi.js +23 -4
package/module/array.js
CHANGED
|
@@ -8,8 +8,10 @@
|
|
|
8
8
|
* @module array
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
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
|
+
import { emit, materializeMulti } from '../src/emit.js'
|
|
13
|
+
import { valTypeOf, lookupValType, VAL, extractParams, updateRep, staticPropertyKey } from '../src/analyze.js'
|
|
14
|
+
import { ctx, inc, err, PTR, LAYOUT } from '../src/ctx.js'
|
|
13
15
|
import { strHashLiteral } from './collection.js'
|
|
14
16
|
|
|
15
17
|
|
|
@@ -40,6 +42,8 @@ function hoistArrayValue(arr) {
|
|
|
40
42
|
}
|
|
41
43
|
}
|
|
42
44
|
|
|
45
|
+
const arrayLenFromPtr = ptr => ['i32.load', ['i32.sub', ['local.get', `$${ptr}`], ['i32.const', 8]]]
|
|
46
|
+
|
|
43
47
|
// Pure-expression check: no statements, binders, control flow, or assignments.
|
|
44
48
|
// Inlining is only safe for these — anything else needs the full closure machinery.
|
|
45
49
|
const NOT_PURE_OPS = new Set([
|
|
@@ -176,10 +180,42 @@ const arrayGrowDeps = (knownArray = false) => () => [
|
|
|
176
180
|
...(needsArrayDynMove() ? ['__dyn_move'] : []),
|
|
177
181
|
]
|
|
178
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.
|
|
179
185
|
const maybeDynMoveIR = () => needsArrayDynMove()
|
|
180
186
|
? '(call $__dyn_move (local.get $off) (local.get $newOff))'
|
|
181
187
|
: ''
|
|
182
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
|
+
|
|
183
219
|
export default (ctx) => {
|
|
184
220
|
Object.assign(ctx.core.stdlibDeps, {
|
|
185
221
|
__arr_idx: [],
|
|
@@ -187,7 +223,7 @@ export default (ctx) => {
|
|
|
187
223
|
__arr_grow_known: arrayGrowDeps(true),
|
|
188
224
|
__arr_shift: () => [
|
|
189
225
|
'__ptr_offset',
|
|
190
|
-
...(needsArrayDynMove() ? ['__dyn_move'] : []),
|
|
226
|
+
...(needsArrayDynMove() ? ['__dyn_move', '__hash_new', '__ihash_set_local'] : []),
|
|
191
227
|
],
|
|
192
228
|
__arr_set_idx_ptr: ['__arr_grow', '__ptr_offset', '__set_len'],
|
|
193
229
|
__typed_idx: () => ctx.features.typedarray || ctx.features.external
|
|
@@ -208,21 +244,20 @@ export default (ctx) => {
|
|
|
208
244
|
const t = temp('t')
|
|
209
245
|
return typed(['i32.and',
|
|
210
246
|
['f64.ne', ['local.tee', `$${t}`, v], ['local.get', `$${t}`]],
|
|
211
|
-
['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')
|
|
212
248
|
}
|
|
213
249
|
|
|
214
250
|
// ARRAY-only indexed read. Inline forwarding-follow + bounds check + load — avoids
|
|
215
251
|
// the redundant double pass through __len then __ptr_offset that both follow forwarding.
|
|
216
|
-
ctx.core.stdlib['__arr_idx'] = `(func $__arr_idx (param $ptr
|
|
217
|
-
(local $
|
|
218
|
-
(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)
|
|
219
254
|
(if (result f64)
|
|
220
255
|
(i32.ne
|
|
221
|
-
(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})))
|
|
222
257
|
(i32.const ${PTR.ARRAY}))
|
|
223
258
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
224
259
|
(else
|
|
225
|
-
(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}))))
|
|
226
261
|
(block $done
|
|
227
262
|
(loop $follow
|
|
228
263
|
(br_if $done (i32.lt_u (local.get $off) (i32.const 8)))
|
|
@@ -239,13 +274,32 @@ export default (ctx) => {
|
|
|
239
274
|
(then (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
240
275
|
(else (f64.const nan:${UNDEF_NAN})))))) `
|
|
241
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
|
+
|
|
242
296
|
// Runtime-dispatch index: element-type aware load with bounds check + view indirection.
|
|
243
297
|
// Full body handles TYPED element types and view indirection since external host can
|
|
244
298
|
// pass typed arrays even when typedarray module isn't loaded. When features.typedarray
|
|
245
299
|
// and features.external are both off, collapses to ARRAY-only f64 indexing.
|
|
246
300
|
ctx.core.stdlib['__typed_idx'] = () => {
|
|
247
301
|
if (!ctx.features.typedarray && !ctx.features.external) {
|
|
248
|
-
return `(func $__typed_idx (param $ptr
|
|
302
|
+
return `(func $__typed_idx (param $ptr i64) (param $i i32) (result f64)
|
|
249
303
|
(local $len i32)
|
|
250
304
|
(local.set $len (call $__len (local.get $ptr)))
|
|
251
305
|
(if (result f64)
|
|
@@ -255,13 +309,12 @@ export default (ctx) => {
|
|
|
255
309
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
256
310
|
(else (f64.load (i32.add (call $__ptr_offset (local.get $ptr)) (i32.shl (local.get $i) (i32.const 3)))))))`
|
|
257
311
|
}
|
|
258
|
-
// Hot (~37M calls in watr self-host). Type/aux/offset extracted once from $
|
|
259
|
-
return `(func $__typed_idx (param $ptr
|
|
260
|
-
(local $
|
|
261
|
-
(local.set $
|
|
262
|
-
(local.set $
|
|
263
|
-
(local.set $
|
|
264
|
-
(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}))))
|
|
265
318
|
(if
|
|
266
319
|
(i32.and
|
|
267
320
|
(i32.eq (local.get $t) (i32.const ${PTR.TYPED}))
|
|
@@ -279,7 +332,9 @@ export default (ctx) => {
|
|
|
279
332
|
(local.set $et (i32.and (local.get $aux) (i32.const 7)))
|
|
280
333
|
(if (result f64) (i32.ge_u (local.get $et) (i32.const 6))
|
|
281
334
|
(then (if (result f64) (i32.eq (local.get $et) (i32.const 7))
|
|
282
|
-
(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)))))))
|
|
283
338
|
(else (f64.promote_f32 (f32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
|
|
284
339
|
(else (if (result f64) (i32.ge_u (local.get $et) (i32.const 4))
|
|
285
340
|
(then (if (result f64) (i32.and (local.get $et) (i32.const 1))
|
|
@@ -296,23 +351,56 @@ export default (ctx) => {
|
|
|
296
351
|
}
|
|
297
352
|
|
|
298
353
|
// Array.from(src) — shallow copy of array (memory.copy of f64 elements)
|
|
299
|
-
ctx.core.stdlib['__arr_from'] = `(func $__arr_from (param $src
|
|
354
|
+
ctx.core.stdlib['__arr_from'] = `(func $__arr_from (param $src i64) (result f64)
|
|
300
355
|
(local $len i32) (local $dst i32)
|
|
301
356
|
(local.set $len (call $__len (local.get $src)))
|
|
302
357
|
(local.set $dst (call $__alloc_hdr (local.get $len) (local.get $len) (i32.const 8)))
|
|
303
358
|
(memory.copy (local.get $dst) (call $__ptr_offset (local.get $src)) (i32.shl (local.get $len) (i32.const 3)))
|
|
304
359
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $dst)))`
|
|
305
360
|
|
|
306
|
-
|
|
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
|
+
}
|
|
307
394
|
inc('__arr_from')
|
|
308
|
-
return typed(['call', '$__arr_from',
|
|
395
|
+
return typed(['call', '$__arr_from', asI64(emit(src))], 'f64')
|
|
309
396
|
}
|
|
310
397
|
|
|
311
398
|
// Grow array if capacity insufficient. Returns (possibly new) NaN-boxed pointer.
|
|
312
399
|
// Old storage is left behind as a forwarding header so existing aliases keep
|
|
313
400
|
// seeing the current backing store after growth.
|
|
314
|
-
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)
|
|
315
402
|
(local $t i32) (local $off i32) (local $oldCap i32) (local $newCap i32) (local $newOff i32) (local $len i32)
|
|
403
|
+
${needsArrayDynMove() ? '(local $oldProps f64)' : ''}
|
|
316
404
|
(local.set $t (call $__ptr_type (local.get $ptr)))
|
|
317
405
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
318
406
|
;; Defensive path: invalid/non-array pointer -> create fresh array buffer.
|
|
@@ -326,7 +414,7 @@ export default (ctx) => {
|
|
|
326
414
|
(return (call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $newOff)))))
|
|
327
415
|
(local.set $oldCap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
328
416
|
(if (i32.ge_s (local.get $oldCap) (local.get $minCap))
|
|
329
|
-
(then (return (local.get $ptr))))
|
|
417
|
+
(then (return (f64.reinterpret_i64 (local.get $ptr)))))
|
|
330
418
|
(local.set $newCap (select
|
|
331
419
|
(local.get $minCap)
|
|
332
420
|
(i32.shl (local.get $oldCap) (i32.const 1))
|
|
@@ -334,17 +422,19 @@ export default (ctx) => {
|
|
|
334
422
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
335
423
|
(local.set $newOff (call $__alloc_hdr (local.get $len) (local.get $newCap) (i32.const 8)))
|
|
336
424
|
(memory.copy (local.get $newOff) (local.get $off) (i32.shl (local.get $len) (i32.const 3)))
|
|
425
|
+
${headerPropsCopyIR()}
|
|
337
426
|
${maybeDynMoveIR()}
|
|
338
427
|
(i32.store (i32.sub (local.get $off) (i32.const 8)) (local.get $newOff))
|
|
339
428
|
(i32.store (i32.sub (local.get $off) (i32.const 4)) (i32.const -1))
|
|
340
429
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $newOff)))`
|
|
341
430
|
|
|
342
|
-
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)
|
|
343
432
|
(local $off i32) (local $oldCap i32) (local $newCap i32) (local $newOff i32) (local $len i32)
|
|
433
|
+
${needsArrayDynMove() ? '(local $oldProps f64)' : ''}
|
|
344
434
|
(local.set $off (call $__ptr_offset (local.get $ptr)))
|
|
345
435
|
(local.set $oldCap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
346
436
|
(if (i32.ge_s (local.get $oldCap) (local.get $minCap))
|
|
347
|
-
(then (return (local.get $ptr))))
|
|
437
|
+
(then (return (f64.reinterpret_i64 (local.get $ptr)))))
|
|
348
438
|
(local.set $newCap (select
|
|
349
439
|
(local.get $minCap)
|
|
350
440
|
(i32.shl (local.get $oldCap) (i32.const 1))
|
|
@@ -352,6 +442,7 @@ export default (ctx) => {
|
|
|
352
442
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
353
443
|
(local.set $newOff (call $__alloc_hdr (local.get $len) (local.get $newCap) (i32.const 8)))
|
|
354
444
|
(memory.copy (local.get $newOff) (local.get $off) (i32.shl (local.get $len) (i32.const 3)))
|
|
445
|
+
${headerPropsCopyIR()}
|
|
355
446
|
${maybeDynMoveIR()}
|
|
356
447
|
(i32.store (i32.sub (local.get $off) (i32.const 8)) (local.get $newOff))
|
|
357
448
|
(i32.store (i32.sub (local.get $off) (i32.const 4)) (i32.const -1))
|
|
@@ -360,21 +451,22 @@ export default (ctx) => {
|
|
|
360
451
|
// Hot for arr[i] = val (~18M calls in watr self-host). Compute base via __ptr_offset
|
|
361
452
|
// once and read len from the inline header (i32.load base-8) — avoids __len's separate
|
|
362
453
|
// forwarding follow. On the rare grow path the base is recomputed after relocation.
|
|
363
|
-
ctx.core.stdlib['__arr_set_idx_ptr'] = `(func $__arr_set_idx_ptr (param $ptr
|
|
364
|
-
(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)))
|
|
365
457
|
(if (i32.lt_s (local.get $i) (i32.const 0))
|
|
366
|
-
(then (return (local.get $
|
|
458
|
+
(then (return (local.get $p))))
|
|
367
459
|
(local.set $base (call $__ptr_offset (local.get $ptr)))
|
|
368
460
|
(if (i32.ge_u (local.get $i)
|
|
369
461
|
(i32.load (i32.sub (local.get $base) (i32.const 8))))
|
|
370
462
|
(then
|
|
371
|
-
(local.set $
|
|
372
|
-
(call $__set_len (local.get $
|
|
373
|
-
(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))))))
|
|
374
466
|
(f64.store
|
|
375
467
|
(i32.add (local.get $base) (i32.shl (local.get $i) (i32.const 3)))
|
|
376
468
|
(local.get $val))
|
|
377
|
-
(local.get $
|
|
469
|
+
(local.get $p))`
|
|
378
470
|
|
|
379
471
|
// === Array literal ===
|
|
380
472
|
|
|
@@ -413,28 +505,31 @@ export default (ctx) => {
|
|
|
413
505
|
const src = temp('ss'), slen = tempI32('sl'), si = tempI32('si')
|
|
414
506
|
const id = ctx.func.uniq++
|
|
415
507
|
const spreadVal = multiCount(e[1]) ? materializeMulti(e[1]) : asF64(emit(e[1]))
|
|
416
|
-
const
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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}`]])
|
|
424
519
|
|
|
425
520
|
body.push(
|
|
426
521
|
['local.set', `$${src}`, spreadVal],
|
|
427
|
-
['local.set', `$${slen}`, ['call', '$__len', ['local.get', `$${src}`]]],
|
|
522
|
+
['local.set', `$${slen}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${src}`]]]],
|
|
428
523
|
['local.set', `$${si}`, ['i32.const', 0]],
|
|
429
524
|
['block', `$brk${id}`, ['loop', `$loop${id}`,
|
|
430
525
|
['br_if', `$brk${id}`, ['i32.ge_s', ['local.get', `$${si}`], ['local.get', `$${slen}`]]],
|
|
431
|
-
['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]],
|
|
432
527
|
['local.set', `$${pos}`, ['i32.add', ['local.get', `$${pos}`], ['i32.const', 1]]],
|
|
433
528
|
['local.set', `$${si}`, ['i32.add', ['local.get', `$${si}`], ['i32.const', 1]]],
|
|
434
529
|
['br', `$loop${id}`]]])
|
|
435
530
|
} else {
|
|
436
531
|
body.push(
|
|
437
|
-
['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))]],
|
|
438
533
|
['local.set', `$${pos}`, ['i32.add', ['local.get', `$${pos}`], ['i32.const', 1]]])
|
|
439
534
|
}
|
|
440
535
|
}
|
|
@@ -466,22 +561,24 @@ export default (ctx) => {
|
|
|
466
561
|
if (r) return r
|
|
467
562
|
}
|
|
468
563
|
// Literal string key on schema-known object → direct payload slot read (skip __dyn_get)
|
|
469
|
-
const litKey =
|
|
564
|
+
const litKey = isLiteralStr(idx) ? idx[1]
|
|
565
|
+
: typeof arr === 'string' && lookupValType(arr) === VAL.OBJECT ? staticPropertyKey(idx)
|
|
566
|
+
: null
|
|
470
567
|
if (litKey != null && typeof arr === 'string' && ctx.schema.find) {
|
|
471
568
|
const slot = ctx.schema.find(arr, litKey)
|
|
472
569
|
if (slot >= 0) {
|
|
473
570
|
inc('__ptr_offset')
|
|
474
571
|
return typed(['f64.load',
|
|
475
|
-
['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')
|
|
476
573
|
}
|
|
477
574
|
}
|
|
478
575
|
if (litKey != null && typeof arr === 'string' && lookupValType(arr) === VAL.HASH) {
|
|
479
576
|
inc('__hash_get_local_h')
|
|
480
|
-
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')
|
|
481
578
|
}
|
|
482
579
|
if (litKey != null && typeof arr !== 'string' && valTypeOf(arr) === VAL.HASH) {
|
|
483
580
|
inc('__hash_get_local_h')
|
|
484
|
-
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')
|
|
485
582
|
}
|
|
486
583
|
// Multi-value calls are materialized at call site (see '()' handler), so
|
|
487
584
|
// func()[i] works naturally — func() returns a heap array pointer, [i] indexes it.
|
|
@@ -491,16 +588,16 @@ export default (ctx) => {
|
|
|
491
588
|
const dynLoad = (objExpr, keyExpr) => {
|
|
492
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 }.`)
|
|
493
590
|
inc('__dyn_get')
|
|
494
|
-
return ['call', '$__dyn_get', objExpr, keyExpr]
|
|
591
|
+
return ['f64.reinterpret_i64', ['call', '$__dyn_get', ['i64.reinterpret_f64', objExpr], ['i64.reinterpret_f64', keyExpr]]]
|
|
495
592
|
}
|
|
496
|
-
const stringLoad = () => (inc('__str_idx'), ['call', '$__str_idx', ptrExpr, vi])
|
|
497
|
-
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])
|
|
498
595
|
const emitDynamicKeyDispatch = (objExpr, numericLoad) => {
|
|
499
596
|
const keyTmp = temp()
|
|
500
597
|
inc('__is_str_key')
|
|
501
598
|
return typed(['block', ['result', 'f64'],
|
|
502
599
|
['local.set', `$${keyTmp}`, asF64(emit(idx))],
|
|
503
|
-
['if', ['result', 'f64'], ['call', '$__is_str_key', ['local.get', `$${keyTmp}`]],
|
|
600
|
+
['if', ['result', 'f64'], ['call', '$__is_str_key', ['i64.reinterpret_f64', ['local.get', `$${keyTmp}`]]],
|
|
504
601
|
['then', dynLoad(objExpr, ['local.get', `$${keyTmp}`])],
|
|
505
602
|
['else', numericLoad(['local.get', `$${keyTmp}`])]]], 'f64')
|
|
506
603
|
}
|
|
@@ -510,11 +607,31 @@ export default (ctx) => {
|
|
|
510
607
|
if (keyType === VAL.STRING) return typed(dynLoad(asF64(emit(arr)), asF64(emit(idx))), 'f64')
|
|
511
608
|
if (useRuntimeKeyDispatch)
|
|
512
609
|
return emitDynamicKeyDispatch(asF64(emit(arr)), keyExpr =>
|
|
513
|
-
['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]]]])
|
|
514
611
|
return typed(
|
|
515
|
-
['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]]]],
|
|
516
613
|
'f64')
|
|
517
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
|
+
}
|
|
518
635
|
// Known array → direct f64 element load, skip string check
|
|
519
636
|
if (keyType === VAL.STRING)
|
|
520
637
|
return typed(dynLoad(ptrExpr, asF64(emit(idx))), 'f64')
|
|
@@ -534,7 +651,7 @@ export default (ctx) => {
|
|
|
534
651
|
// Fast path fires on schemaId (Array<{x,y,z}> shapes) OR plain elem-val
|
|
535
652
|
// (Array<NUMBER> from `.map(x => x*k)` etc.).
|
|
536
653
|
const rep = typeof arr === 'string' ? ctx.func.repByLocal?.get(arr) : null
|
|
537
|
-
const hasElemFact = rep?.arrayElemSchema != null
|
|
654
|
+
const hasElemFact = rep?.arrayElemSchema != null
|
|
538
655
|
if (hasElemFact && keyIsNum) {
|
|
539
656
|
inc('__ptr_offset')
|
|
540
657
|
// __ptr_offset returns i32 — base local must be i32 (not the default
|
|
@@ -545,10 +662,9 @@ export default (ctx) => {
|
|
|
545
662
|
return typed(['f64.load',
|
|
546
663
|
['i32.add',
|
|
547
664
|
['local.tee', `$${baseI32}`,
|
|
548
|
-
['call', '$__ptr_offset', ptrExpr]],
|
|
665
|
+
['call', '$__ptr_offset', ['i64.reinterpret_f64', ptrExpr]]],
|
|
549
666
|
['i32.shl', vi, ['i32.const', 3]]]], 'f64')
|
|
550
667
|
}
|
|
551
|
-
inc('__arr_idx')
|
|
552
668
|
const baseTmp = temp()
|
|
553
669
|
// Numeric key (literal or known-NUMBER name) → skip __is_str_key dispatch;
|
|
554
670
|
// arrays don't honor string-key access for numeric keys (keys aren't coerced
|
|
@@ -558,11 +674,13 @@ export default (ctx) => {
|
|
|
558
674
|
['local.set', `$${baseTmp}`, ptrExpr],
|
|
559
675
|
emitDynamicKeyDispatch(typed(['local.get', `$${baseTmp}`], 'f64'), keyExpr => {
|
|
560
676
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
561
|
-
|
|
677
|
+
inc('__arr_idx')
|
|
678
|
+
return (['call', '$__arr_idx', ['i64.reinterpret_f64', ['local.get', `$${baseTmp}`]], keyI32])
|
|
562
679
|
})], 'f64')
|
|
680
|
+
inc('__arr_idx_known')
|
|
563
681
|
return typed(['block', ['result', 'f64'],
|
|
564
682
|
['local.set', `$${baseTmp}`, ptrExpr],
|
|
565
|
-
(['call', '$
|
|
683
|
+
(['call', '$__arr_idx_known', ['i64.reinterpret_f64', ['local.get', `$${baseTmp}`]], vi])], 'f64')
|
|
566
684
|
}
|
|
567
685
|
// Known string → single-char SSO string
|
|
568
686
|
if (vt === 'string')
|
|
@@ -577,30 +695,26 @@ export default (ctx) => {
|
|
|
577
695
|
if (useRuntimeKeyDispatch && !keyIsNum)
|
|
578
696
|
return emitDynamicKeyDispatch(ptrExpr, keyExpr => {
|
|
579
697
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
580
|
-
return (['call', '$__typed_idx', ptrExpr, keyI32])
|
|
698
|
+
return (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])
|
|
581
699
|
})
|
|
582
|
-
return typed((['call', '$__typed_idx', ptrExpr, vi]), 'f64')
|
|
700
|
+
return typed((['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], vi]), 'f64')
|
|
583
701
|
}
|
|
584
702
|
if (useRuntimeKeyDispatch)
|
|
585
703
|
return emitDynamicKeyDispatch(ptrExpr, keyExpr => {
|
|
586
704
|
const keyI32 = asI32(typed(keyExpr, 'f64'))
|
|
587
705
|
if (ctx.module.modules['string']) {
|
|
588
706
|
return ['if', ['result', 'f64'],
|
|
589
|
-
['i32.
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
['then', (inc('__str_idx'), ['call', '$__str_idx', ptrExpr, keyI32])],
|
|
593
|
-
['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])]]
|
|
594
710
|
}
|
|
595
|
-
return (['call', '$__typed_idx', ptrExpr, keyI32])
|
|
711
|
+
return (['call', '$__typed_idx', ['i64.reinterpret_f64', ptrExpr], keyI32])
|
|
596
712
|
})
|
|
597
713
|
// Unknown → runtime dispatch (string module loaded → check ptr_type)
|
|
598
714
|
if (ctx.module.modules['string'])
|
|
599
715
|
return typed(
|
|
600
716
|
['if', ['result', 'f64'],
|
|
601
|
-
['i32.
|
|
602
|
-
['i32.eq', ['call', '$__ptr_type', ptrExpr], ['i32.const', PTR.STRING]],
|
|
603
|
-
['i32.eq', ['call', '$__ptr_type', ptrExpr], ['i32.const', PTR.SSO]]],
|
|
717
|
+
['i32.eq', ['call', '$__ptr_type', ['i64.reinterpret_f64', ptrExpr]], ['i32.const', PTR.STRING]],
|
|
604
718
|
['then', stringLoad()],
|
|
605
719
|
['else', arrayLoad]],
|
|
606
720
|
'f64')
|
|
@@ -632,7 +746,7 @@ export default (ctx) => {
|
|
|
632
746
|
// dispatch and prologue entirely; on grow we re-extract offset because the
|
|
633
747
|
// alloc may have relocated the buffer.
|
|
634
748
|
body.push(
|
|
635
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]],
|
|
749
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
636
750
|
['local.set', `$${len}`,
|
|
637
751
|
['i32.load', ['i32.sub', ['local.get', `$${pushBase}`], ['i32.const', 8]]]],
|
|
638
752
|
['if',
|
|
@@ -640,17 +754,17 @@ export default (ctx) => {
|
|
|
640
754
|
['i32.load', ['i32.sub', ['local.get', `$${pushBase}`], ['i32.const', 4]]],
|
|
641
755
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]],
|
|
642
756
|
['then',
|
|
643
|
-
['local.set', `$${t}`, ['call', `$${grow}`, ['local.get', `$${t}`],
|
|
757
|
+
['local.set', `$${t}`, ['call', `$${grow}`, ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
644
758
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]]],
|
|
645
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]]]],
|
|
759
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]]],
|
|
646
760
|
)
|
|
647
761
|
} else {
|
|
648
762
|
body.push(
|
|
649
|
-
['local.set', `$${len}`, ['call', '$__len', ['local.get', `$${t}`]]],
|
|
763
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
650
764
|
// Grow if needed: ensure cap >= len + vals.length
|
|
651
|
-
['local.set', `$${t}`, ['call', `$${grow}`, ['local.get', `$${t}`],
|
|
765
|
+
['local.set', `$${t}`, ['call', `$${grow}`, ['i64.reinterpret_f64', ['local.get', `$${t}`]],
|
|
652
766
|
['i32.add', ['local.get', `$${len}`], ['i32.const', vals.length]]]],
|
|
653
|
-
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['local.get', `$${t}`]]],
|
|
767
|
+
['local.set', `$${pushBase}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]],
|
|
654
768
|
)
|
|
655
769
|
}
|
|
656
770
|
|
|
@@ -666,7 +780,7 @@ export default (ctx) => {
|
|
|
666
780
|
}
|
|
667
781
|
|
|
668
782
|
// Update length header, update source variable (pointer may have changed from grow), return new length
|
|
669
|
-
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}`]])
|
|
670
784
|
// Update the source variable if it's a named variable (so arr still points to valid memory)
|
|
671
785
|
if (typeof arr === 'string') {
|
|
672
786
|
if (ctx.func.boxed?.has(arr)) {
|
|
@@ -689,34 +803,36 @@ export default (ctx) => {
|
|
|
689
803
|
// Known ARRAY → inline len (skips __len dispatch tree).
|
|
690
804
|
const vt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
|
691
805
|
const rawLen = vt === VAL.ARRAY
|
|
692
|
-
? ['i32.load', ['i32.sub', ['call', '$__ptr_offset', ['local.get', `$${t}`]], ['i32.const', 8]]]
|
|
693
|
-
: ['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}`]]]
|
|
694
808
|
return typed(['block', ['result', 'f64'],
|
|
695
809
|
['local.set', `$${t}`, va],
|
|
696
810
|
['local.set', `$${len}`, ['i32.sub', rawLen, ['i32.const', 1]]],
|
|
697
|
-
['call', '$__set_len', ['local.get', `$${t}`], ['local.get', `$${len}`]],
|
|
811
|
+
['call', '$__set_len', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['local.get', `$${len}`]],
|
|
698
812
|
['f64.load',
|
|
699
|
-
['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')
|
|
700
814
|
}
|
|
701
815
|
|
|
702
816
|
// .shift() → remove first element, shift remaining left, return removed
|
|
703
|
-
ctx.core.emit['.shift'] =
|
|
817
|
+
ctx.core.emit['.shift'] = (arr) => (inc('__arr_shift'),
|
|
818
|
+
typed(['call', '$__arr_shift', asI64(emit(arr))], 'f64'))
|
|
704
819
|
|
|
705
|
-
ctx.core.stdlib['__arr_shift'] = () => `(func $__arr_shift (param $arr
|
|
706
|
-
(local $
|
|
707
|
-
(local
|
|
708
|
-
(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}))))
|
|
709
824
|
(local.set $off (call $__ptr_offset (local.get $arr)))
|
|
710
825
|
(if (result f64) (i32.lt_u (local.get $off) (i32.const 8))
|
|
711
|
-
(then (f64.const
|
|
826
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
712
827
|
(else
|
|
713
828
|
(local.set $len (i32.load (i32.sub (local.get $off) (i32.const 8))))
|
|
714
829
|
(if (result f64) (i32.le_s (local.get $len) (i32.const 0))
|
|
715
|
-
(then (f64.const
|
|
830
|
+
(then (f64.const nan:${UNDEF_NAN}))
|
|
716
831
|
(else
|
|
717
832
|
(local.set $val (f64.load (local.get $off)))
|
|
718
833
|
(local.set $cap (i32.load (i32.sub (local.get $off) (i32.const 4))))
|
|
719
834
|
(local.set $newOff (i32.add (local.get $off) (i32.const 8)))
|
|
835
|
+
${headerPropsToGlobalIR()}
|
|
720
836
|
(i32.store (local.get $off) (i32.sub (local.get $len) (i32.const 1)))
|
|
721
837
|
(i32.store (i32.add (local.get $off) (i32.const 4))
|
|
722
838
|
(select (i32.sub (local.get $cap) (i32.const 1)) (i32.const 0) (i32.gt_s (local.get $cap) (i32.const 0))))
|
|
@@ -741,10 +857,10 @@ export default (ctx) => {
|
|
|
741
857
|
const svt = typeof arr === 'string' ? lookupValType(arr) : valTypeOf(arr)
|
|
742
858
|
const lenInit = svt === VAL.ARRAY
|
|
743
859
|
? ['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${off}`], ['i32.const', 8]]]]
|
|
744
|
-
: ['local.set', `$${len}`, ['call', '$__len', va]]
|
|
860
|
+
: ['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]]
|
|
745
861
|
const body = [
|
|
746
862
|
recv.setup,
|
|
747
|
-
['local.set', `$${off}`, ['call', '$__ptr_offset', va]],
|
|
863
|
+
['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', va]]],
|
|
748
864
|
lenInit,
|
|
749
865
|
// clamp start to [0, len]
|
|
750
866
|
['local.set', `$${s}`, vs],
|
|
@@ -783,26 +899,27 @@ export default (ctx) => {
|
|
|
783
899
|
['i32.sub', ['i32.sub', ['local.get', `$${len}`], ['local.get', `$${s}`]], ['local.get', `$${cnt}`]],
|
|
784
900
|
['i32.const', 3]]],
|
|
785
901
|
// update length
|
|
786
|
-
['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}`]]],
|
|
787
903
|
out.ptr,
|
|
788
904
|
]
|
|
789
905
|
return typed(['block', ['result', 'f64'], ...body], 'f64')
|
|
790
906
|
}
|
|
791
907
|
|
|
792
908
|
// .unshift(val) → prepend element, shift existing right
|
|
793
|
-
ctx.core.emit['.unshift'] =
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
(local
|
|
798
|
-
(local.set $
|
|
799
|
-
(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))))
|
|
800
917
|
(memory.copy
|
|
801
918
|
(i32.add (local.get $off) (i32.const 8))
|
|
802
919
|
(local.get $off)
|
|
803
920
|
(i32.shl (local.get $len) (i32.const 3)))
|
|
804
921
|
(f64.store (local.get $off) (local.get $val))
|
|
805
|
-
(call $__set_len (local.get $
|
|
922
|
+
(call $__set_len (i64.reinterpret_f64 (local.get $a)) (i32.add (local.get $len) (i32.const 1)))
|
|
806
923
|
(f64.convert_i32_s (i32.add (local.get $len) (i32.const 1))))`
|
|
807
924
|
|
|
808
925
|
// .some(fn) → return 1 if any element passes, else 0 (early exit)
|
|
@@ -924,7 +1041,7 @@ export default (ctx) => {
|
|
|
924
1041
|
const up = detectUpstream(arr)
|
|
925
1042
|
if (up && up.method === 'filter' && isPureCallback(fn)) {
|
|
926
1043
|
const recv = hoistArrayValue(up.source)
|
|
927
|
-
const count = tempI32('fc'), maxLen = tempI32('fm')
|
|
1044
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb')
|
|
928
1045
|
const upReps = callbackArgReps(up.source)
|
|
929
1046
|
const filterCb = makeCallback(up.fn, upReps), mapCb = makeCallback(fn, upReps)
|
|
930
1047
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'fm' })
|
|
@@ -933,28 +1050,32 @@ export default (ctx) => {
|
|
|
933
1050
|
['then',
|
|
934
1051
|
elemStore(out.local, count, asF64(mapCb.call([item, idxArg(mapCb, count)]))),
|
|
935
1052
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
936
|
-
], maxLen)
|
|
1053
|
+
], maxLen, base)
|
|
1054
|
+
inc('__ptr_offset')
|
|
937
1055
|
return typed(['block', ['result', 'f64'],
|
|
938
1056
|
recv.setup, filterCb.setup, mapCb.setup,
|
|
939
|
-
['local.set', `$${
|
|
1057
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1058
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
940
1059
|
out.init, ['local.set', `$${count}`, ['i32.const', 0]],
|
|
941
1060
|
...loop,
|
|
942
1061
|
['i32.store', ['i32.sub', ['local.get', `$${out.local}`], ['i32.const', 8]], ['local.get', `$${count}`]],
|
|
943
1062
|
out.ptr], 'f64')
|
|
944
1063
|
}
|
|
945
1064
|
const recv = hoistArrayValue(arr)
|
|
946
|
-
const len = tempI32('ml')
|
|
1065
|
+
const len = tempI32('ml'), base = tempI32('mb')
|
|
947
1066
|
const cb = makeCallback(fn, callbackArgReps(arr))
|
|
948
1067
|
const lenIR = ['local.get', `$${len}`]
|
|
949
1068
|
const out = allocPtr({ type: PTR.ARRAY, len: lenIR, tag: 'mo' })
|
|
950
1069
|
// Reuse the precomputed len local in arrayLoop (skip its internal load).
|
|
951
1070
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
952
1071
|
elemStore(out.local, i, asF64(cb.call([item, idxArg(cb, i)])))
|
|
953
|
-
], len)
|
|
1072
|
+
], len, base)
|
|
1073
|
+
inc('__ptr_offset')
|
|
954
1074
|
return typed(['block', ['result', 'f64'],
|
|
955
1075
|
recv.setup,
|
|
956
1076
|
cb.setup,
|
|
957
|
-
['local.set', `$${
|
|
1077
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1078
|
+
['local.set', `$${len}`, arrayLenFromPtr(base)],
|
|
958
1079
|
out.init,
|
|
959
1080
|
...loop,
|
|
960
1081
|
out.ptr], 'f64')
|
|
@@ -965,7 +1086,7 @@ export default (ctx) => {
|
|
|
965
1086
|
const up = detectUpstream(arr)
|
|
966
1087
|
if (up && up.method === 'map' && isPureCallback(fn)) {
|
|
967
1088
|
const recv = hoistArrayValue(up.source)
|
|
968
|
-
const count = tempI32('fc'), maxLen = tempI32('fm'), mapped = temp('mv')
|
|
1089
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb'), mapped = temp('mv')
|
|
969
1090
|
const upReps = callbackArgReps(up.source)
|
|
970
1091
|
const mapCb = makeCallback(up.fn, upReps), filterCb = makeCallback(fn)
|
|
971
1092
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'mf' })
|
|
@@ -975,17 +1096,19 @@ export default (ctx) => {
|
|
|
975
1096
|
['then',
|
|
976
1097
|
['f64.store', ['i32.add', ['local.get', `$${out.local}`], ['i32.shl', ['local.get', `$${count}`], ['i32.const', 3]]], ['local.get', `$${mapped}`]],
|
|
977
1098
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
978
|
-
], maxLen)
|
|
1099
|
+
], maxLen, base)
|
|
1100
|
+
inc('__ptr_offset')
|
|
979
1101
|
return typed(['block', ['result', 'f64'],
|
|
980
1102
|
recv.setup, mapCb.setup, filterCb.setup,
|
|
981
|
-
['local.set', `$${
|
|
1103
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1104
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
982
1105
|
out.init, ['local.set', `$${count}`, ['i32.const', 0]],
|
|
983
1106
|
...loop,
|
|
984
1107
|
['i32.store', ['i32.sub', ['local.get', `$${out.local}`], ['i32.const', 8]], ['local.get', `$${count}`]],
|
|
985
1108
|
out.ptr], 'f64')
|
|
986
1109
|
}
|
|
987
1110
|
const recv = hoistArrayValue(arr)
|
|
988
|
-
const count = tempI32('fc'), maxLen = tempI32('fm')
|
|
1111
|
+
const count = tempI32('fc'), maxLen = tempI32('fm'), base = tempI32('fb')
|
|
989
1112
|
const cb = makeCallback(fn, callbackArgReps(arr))
|
|
990
1113
|
const out = allocPtr({ type: PTR.ARRAY, len: 0, cap: ['local.get', `$${maxLen}`], tag: 'fo' })
|
|
991
1114
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
@@ -993,11 +1116,13 @@ export default (ctx) => {
|
|
|
993
1116
|
['then',
|
|
994
1117
|
['f64.store', ['i32.add', ['local.get', `$${out.local}`], ['i32.shl', ['local.get', `$${count}`], ['i32.const', 3]]], item],
|
|
995
1118
|
['local.set', `$${count}`, ['i32.add', ['local.get', `$${count}`], ['i32.const', 1]]]]]
|
|
996
|
-
], maxLen)
|
|
1119
|
+
], maxLen, base)
|
|
1120
|
+
inc('__ptr_offset')
|
|
997
1121
|
return typed(['block', ['result', 'f64'],
|
|
998
1122
|
recv.setup,
|
|
999
1123
|
cb.setup,
|
|
1000
|
-
['local.set', `$${
|
|
1124
|
+
['local.set', `$${base}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1125
|
+
['local.set', `$${maxLen}`, arrayLenFromPtr(base)],
|
|
1001
1126
|
out.init,
|
|
1002
1127
|
['local.set', `$${count}`, ['i32.const', 0]],
|
|
1003
1128
|
...loop,
|
|
@@ -1107,13 +1232,139 @@ export default (ctx) => {
|
|
|
1107
1232
|
['local.get', `$${result}`]], 'f64')
|
|
1108
1233
|
}
|
|
1109
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
|
+
|
|
1110
1360
|
ctx.core.emit['.indexOf'] = (arr, val) => {
|
|
1111
1361
|
const recv = hoistArrayValue(arr)
|
|
1112
1362
|
const vv = asF64(emit(val))
|
|
1363
|
+
const eq = arrEqIR(val)
|
|
1113
1364
|
const result = tempI32('ix')
|
|
1114
1365
|
const exit = `$exit${ctx.func.uniq++}`
|
|
1115
1366
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
1116
|
-
['if',
|
|
1367
|
+
['if', eq(item, vv),
|
|
1117
1368
|
['then', ['local.set', `$${result}`, ['local.get', `$${i}`]], ['br', exit]]]
|
|
1118
1369
|
])
|
|
1119
1370
|
return typed(['block', ['result', 'f64'],
|
|
@@ -1126,10 +1377,11 @@ export default (ctx) => {
|
|
|
1126
1377
|
ctx.core.emit['.includes'] = (arr, val) => {
|
|
1127
1378
|
const recv = hoistArrayValue(arr)
|
|
1128
1379
|
const vv = asF64(emit(val))
|
|
1380
|
+
const eq = arrEqIR(val)
|
|
1129
1381
|
const result = tempI32('ic')
|
|
1130
1382
|
const exit = `$exit${ctx.func.uniq++}`
|
|
1131
1383
|
const loop = arrayLoop(recv.value, (_ptr, _len, i, item) => [
|
|
1132
|
-
['if',
|
|
1384
|
+
['if', eq(item, vv),
|
|
1133
1385
|
['then', ['local.set', `$${result}`, ['i32.const', 1]], ['br', exit]]]
|
|
1134
1386
|
])
|
|
1135
1387
|
return typed(['block', ['result', 'f64'],
|
|
@@ -1141,6 +1393,19 @@ export default (ctx) => {
|
|
|
1141
1393
|
|
|
1142
1394
|
// .at(i) → array element with negative index support
|
|
1143
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
|
+
}
|
|
1144
1409
|
const t = tempI32('ai'), a = temp('aa')
|
|
1145
1410
|
return typed(['block', ['result', 'f64'],
|
|
1146
1411
|
['local.set', `$${a}`, asF64(emit(arr))],
|
|
@@ -1148,10 +1413,11 @@ export default (ctx) => {
|
|
|
1148
1413
|
// Negative index: t += length
|
|
1149
1414
|
['if', ['i32.lt_s', ['local.get', `$${t}`], ['i32.const', 0]],
|
|
1150
1415
|
['then', ['local.set', `$${t}`, ['i32.add', ['local.get', `$${t}`],
|
|
1151
|
-
['call', '$__len', ['local.get', `$${a}`]]]]]],
|
|
1152
|
-
['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}`]]],
|
|
1153
1418
|
['i32.shl', ['local.get', `$${t}`], ['i32.const', 3]]]]], 'f64')
|
|
1154
1419
|
}
|
|
1420
|
+
ctx.core.emit['.at'] = ctx.core.emit['.array:at']
|
|
1155
1421
|
|
|
1156
1422
|
ctx.core.emit['.slice'] = (arr, start, end) => {
|
|
1157
1423
|
// BUFFER slice → byte-level copy handled in typedarray module.
|
|
@@ -1166,7 +1432,7 @@ export default (ctx) => {
|
|
|
1166
1432
|
const out = allocPtr({ type: PTR.ARRAY, len: ['local.get', `$${outLen}`], tag: 'so' })
|
|
1167
1433
|
return typed(['block', ['result', 'f64'],
|
|
1168
1434
|
recv.setup,
|
|
1169
|
-
['local.set', `$${ptr}`, ['call', '$__ptr_offset', recv.value]],
|
|
1435
|
+
['local.set', `$${ptr}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', recv.value]]],
|
|
1170
1436
|
['local.set', `$${len}`, ['i32.load', ['i32.sub', ['local.get', `$${ptr}`], ['i32.const', 8]]]],
|
|
1171
1437
|
['local.set', `$${s}`, rawStart],
|
|
1172
1438
|
['if', ['i32.lt_s', ['local.get', `$${s}`], ['i32.const', 0]],
|
|
@@ -1199,14 +1465,14 @@ export default (ctx) => {
|
|
|
1199
1465
|
// Calculate total length
|
|
1200
1466
|
const body = [
|
|
1201
1467
|
recv.setup,
|
|
1202
|
-
['local.set', `$${len}`, ['call', '$__len', va]],
|
|
1468
|
+
['local.set', `$${len}`, ['call', '$__len', ['i64.reinterpret_f64', va]]],
|
|
1203
1469
|
]
|
|
1204
1470
|
|
|
1205
1471
|
const otherVals = []
|
|
1206
1472
|
for (const other of others) {
|
|
1207
1473
|
const vo = asF64(emit(other))
|
|
1208
1474
|
otherVals.push(vo)
|
|
1209
|
-
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]]]])
|
|
1210
1476
|
}
|
|
1211
1477
|
|
|
1212
1478
|
body.push(out.init)
|
|
@@ -1215,8 +1481,8 @@ export default (ctx) => {
|
|
|
1215
1481
|
const srcOff = tempI32('co')
|
|
1216
1482
|
body.push(
|
|
1217
1483
|
['local.set', `$${pos}`, ['i32.const', 0]],
|
|
1218
|
-
['local.set', `$${len}`, ['call', '$__len', va]],
|
|
1219
|
-
['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]]]
|
|
1220
1486
|
)
|
|
1221
1487
|
const id = ctx.func.uniq++
|
|
1222
1488
|
body.push(
|
|
@@ -1231,7 +1497,7 @@ export default (ctx) => {
|
|
|
1231
1497
|
|
|
1232
1498
|
// Copy each other array
|
|
1233
1499
|
const offset = tempI32('off')
|
|
1234
|
-
body.push(['local.set', `$${offset}`, ['call', '$__len', va]])
|
|
1500
|
+
body.push(['local.set', `$${offset}`, ['call', '$__len', ['i64.reinterpret_f64', va]]])
|
|
1235
1501
|
|
|
1236
1502
|
const otherOff = tempI32('co2')
|
|
1237
1503
|
for (let i = 0; i < otherVals.length; i++) {
|
|
@@ -1239,8 +1505,8 @@ export default (ctx) => {
|
|
|
1239
1505
|
const id2 = ctx.func.uniq++
|
|
1240
1506
|
body.push(
|
|
1241
1507
|
['local.set', `$${pos}`, ['i32.const', 0]],
|
|
1242
|
-
['local.set', `$${len}`, ['call', '$__len', vo]],
|
|
1243
|
-
['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]]],
|
|
1244
1510
|
['block', `$done${id2}`, ['loop', `$loop${id2}`,
|
|
1245
1511
|
['br_if', `$done${id2}`, ['i32.ge_s', ['local.get', `$${pos}`], ['local.get', `$${len}`]]],
|
|
1246
1512
|
['f64.store',
|
|
@@ -1257,7 +1523,7 @@ export default (ctx) => {
|
|
|
1257
1523
|
}
|
|
1258
1524
|
|
|
1259
1525
|
// .flat() → flatten one level of nested arrays
|
|
1260
|
-
ctx.core.stdlib['__arr_flat'] = `(func $__arr_flat (param $src
|
|
1526
|
+
ctx.core.stdlib['__arr_flat'] = `(func $__arr_flat (param $src i64) (result f64)
|
|
1261
1527
|
(local $len i32) (local $off i32) (local $i i32) (local $total i32) (local $dst i32) (local $pos i32)
|
|
1262
1528
|
(local $elem f64) (local $subLen i32) (local $subOff i32) (local $j i32)
|
|
1263
1529
|
(local.set $off (call $__ptr_offset (local.get $src)))
|
|
@@ -1268,8 +1534,8 @@ export default (ctx) => {
|
|
|
1268
1534
|
(br_if $c1 (i32.ge_s (local.get $i) (local.get $len)))
|
|
1269
1535
|
(local.set $elem (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
1270
1536
|
(if (i32.and (f64.ne (local.get $elem) (local.get $elem))
|
|
1271
|
-
(i32.eq (call $__ptr_type (local.get $elem)) (i32.const ${PTR.ARRAY})))
|
|
1272
|
-
(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))))))
|
|
1273
1539
|
(else (local.set $total (i32.add (local.get $total) (i32.const 1)))))
|
|
1274
1540
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
1275
1541
|
(br $cl1)))
|
|
@@ -1284,10 +1550,10 @@ export default (ctx) => {
|
|
|
1284
1550
|
(br_if $c2 (i32.ge_s (local.get $i) (local.get $len)))
|
|
1285
1551
|
(local.set $elem (f64.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3)))))
|
|
1286
1552
|
(if (i32.and (f64.ne (local.get $elem) (local.get $elem))
|
|
1287
|
-
(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})))
|
|
1288
1554
|
(then
|
|
1289
|
-
(local.set $subOff (call $__ptr_offset (local.get $elem)))
|
|
1290
|
-
(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))))
|
|
1291
1557
|
(local.set $j (i32.const 0))
|
|
1292
1558
|
(block $s (loop $sl
|
|
1293
1559
|
(br_if $s (i32.ge_s (local.get $j) (local.get $subLen)))
|
|
@@ -1303,15 +1569,17 @@ export default (ctx) => {
|
|
|
1303
1569
|
(br $cl2)))
|
|
1304
1570
|
(call $__mkptr (i32.const ${PTR.ARRAY}) (i32.const 0) (local.get $dst)))`
|
|
1305
1571
|
|
|
1306
|
-
ctx.core.emit['.flat'] =
|
|
1572
|
+
ctx.core.emit['.flat'] = (arr) => (inc('__arr_flat'),
|
|
1573
|
+
typed(['call', '$__arr_flat', asI64(emit(arr))], 'f64'))
|
|
1307
1574
|
|
|
1308
1575
|
// .flatMap(fn) → map then flatten
|
|
1309
1576
|
ctx.core.emit['.flatMap'] = (arr, fn) => {
|
|
1310
1577
|
const mapped = ctx.core.emit['.map'](arr, fn)
|
|
1311
1578
|
inc('__arr_flat')
|
|
1312
|
-
return typed(['call', '$__arr_flat',
|
|
1579
|
+
return typed(['call', '$__arr_flat', asI64(mapped)], 'f64')
|
|
1313
1580
|
}
|
|
1314
1581
|
|
|
1315
1582
|
// .join(sep) → concatenate array elements with separator string
|
|
1316
|
-
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'))
|
|
1317
1585
|
}
|