jz 0.9.1 → 0.9.2

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.
@@ -7,11 +7,12 @@
7
7
  * @module typed
8
8
  */
9
9
 
10
- import { typed, asF64, asI32, asI64, toNumF64, UNDEF_NAN, NULL_NAN, TRUE_NAN, FALSE_NAN, allocPtr, mkPtrIR, ptrOffsetIR, ptrTypeEq, temp, tempI32, tempI64, undefExpr, truthyIR } from '../src/ir.js'
10
+ import { typed, asF64, asI32, asI64, toNumF64, coerceNullishToNum, UNDEF_NAN, NULL_NAN, TRUE_NAN, FALSE_NAN, allocPtr, mkPtrIR, ptrOffsetIR, ptrTypeEq, temp, tempI32, tempI64, undefExpr, truthyIR } from '../src/ir.js'
11
+ import { isReassigned, T } from '../src/ast.js'
11
12
  import { emit, idx, deps, call } from '../src/bridge.js'
12
13
  import { strHashLiteral } from './collection.js'
13
14
  import { valTypeOf } from '../src/kind.js'
14
- import { typedIdxProven } from '../src/type.js'
15
+ import { typedIdxProven, typedElemCtor } from '../src/type.js'
15
16
  import { VAL, lookupValType } from '../src/reps.js'
16
17
  import { nanPrefixHex, TYPED_ELEM_NAMES, TYPED_ELEM_CODE, TYPED_ELEM_BIGINT_FLAG, encodeTypedElemAux } from '../layout.js'
17
18
  import { inc, PTR, LAYOUT, registerGetter } from '../src/ctx.js'
@@ -208,8 +209,9 @@ export default (ctx) => {
208
209
  __byte_length: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
209
210
  __byte_offset: ['__ptr_type', '__ptr_offset', '__ptr_aux'],
210
211
  __to_buffer: ['__ptr_type', '__ptr_offset', '__ptr_aux', '__mkptr'],
211
- __typed_set_idx: ['__ptr_aux', '__ptr_offset'],
212
- __typed_get_idx: ['__ptr_aux', '__ptr_offset'],
212
+ __typed_set_idx: () => ['__ptr_aux', '__ptr_offset',
213
+ ...(ctx.features.f16 ? ['__f64_to_f16'] : []), ...(ctx.features.clamped ? ['__u8_clamp'] : [])],
214
+ __typed_get_idx: () => ['__ptr_aux', '__ptr_offset', ...(ctx.features.f16 ? ['__f16_to_f64'] : [])],
213
215
  // __clamp_idx is body-called by every range op (fill/copyWithin/subarray/slice). It has NO
214
216
  // other manual-dep edge in the whole stdlib, so it's reachable ONLY via resolveIncludes'
215
217
  // auto-scan — which diverges under self-host (jz.wasm), dropping it ("Unknown func
@@ -367,7 +369,7 @@ export default (ctx) => {
367
369
  (local.set $byteLen (i32.shl (local.get $n) (local.get $shift)))
368
370
  (local.set $dst (call $__alloc_hdr_n (local.get $byteLen) (local.get $byteLen) (i32.const 1)))
369
371
  (memory.copy (local.get $dst) (i32.add (local.get $src) (i32.shl (local.get $lo) (local.get $shift))) (local.get $byteLen))
370
- (call $__mkptr (i32.const ${PTR.TYPED}) (i32.and (local.get $aux) (i32.const 23)) (local.get $dst)))`
372
+ (call $__mkptr (i32.const ${PTR.TYPED}) (i32.and (local.get $aux) (i32.const 119)) (local.get $dst)))`
371
373
 
372
374
  // Constructor: new Float64Array(len) | new F64Array(arr) | new F64Array(buf) | new F64Array(buf, off, len)
373
375
  for (const [name, elemType] of Object.entries(TYPED_ELEM_CODE)) {
@@ -375,6 +377,8 @@ export default (ctx) => {
375
377
  const stride = STRIDE[elemType]
376
378
  ctx.core.emit[`new.${name}`] = (lenExpr, offsetExpr, lenExpr2) => {
377
379
  ctx.features.typedarray = true
380
+ if (name === 'Float16Array') ctx.features.f16 = true
381
+ if (name === 'Uint8ClampedArray') ctx.features.clamped = true
378
382
  const srcType = typeof lenExpr === 'string' ? lookupValType(lenExpr) : valTypeOf(lenExpr)
379
383
  // Subview: new TypedArray(buffer, byteOffset, length) — true JS-parity view.
380
384
  // Allocates a 16-byte descriptor [byteLen:i32][dataOff:i32][parentOff:i32][pad]
@@ -412,15 +416,17 @@ export default (ctx) => {
412
416
  const conv = FROM_F64[elemType]
413
417
  const srcElem = ['call', '$__typed_idx', ['i64.reinterpret_f64', ['local.get', `$${srcTemp}`]], ['local.get', `$${ci}`]]
414
418
  const cid = ctx.func.uniq++
419
+ const dstOff = ['i32.add', ['local.get', `$${out.local}`], ['i32.mul', ['local.get', `$${ci}`], ['i32.const', stride]]]
420
+ const storeIR = (name === 'Float16Array' || name === 'Uint8ClampedArray')
421
+ ? elemStoreIR({ et: elemType, isF16: name === 'Float16Array', isClamped: name === 'Uint8ClampedArray' }, dstOff, srcElem)
422
+ : [STORE[elemType], dstOff, conv ? [conv, srcElem] : srcElem]
415
423
  return ['block', ['result', 'f64'],
416
424
  ['local.set', `$${cl}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${srcTemp}`]]]],
417
425
  out.init,
418
426
  ['local.set', `$${ci}`, ['i32.const', 0]],
419
427
  ['block', `$tcb${cid}`, ['loop', `$tclp${cid}`,
420
428
  ['br_if', `$tcb${cid}`, ['i32.ge_s', ['local.get', `$${ci}`], ['local.get', `$${cl}`]]],
421
- [STORE[elemType],
422
- ['i32.add', ['local.get', `$${out.local}`], ['i32.mul', ['local.get', `$${ci}`], ['i32.const', stride]]],
423
- conv ? [conv, srcElem] : srcElem],
429
+ storeIR,
424
430
  ['local.set', `$${ci}`, ['i32.add', ['local.get', `$${ci}`], ['i32.const', 1]]],
425
431
  ['br', `$tclp${cid}`]]],
426
432
  out.ptr]
@@ -975,12 +981,66 @@ export default (ctx) => {
975
981
  }
976
982
  }
977
983
 
984
+ // DataView.getFloat16 / setFloat16 (ES2025) — the 2-byte binary16 payload
985
+ // around the same descriptor/bounds/LE machinery as get/setUint16, with the
986
+ // core conversion kernels at the value edge.
987
+ ctx.core.emit['.getFloat16'] = (dv, off, leNode) => {
988
+ inc('__f16_to_f64')
989
+ const desc = tempI32('dvD')
990
+ const addr = ['i32.add', dvDataOff(desc), dvIndexChecked(off, 2, dvViewSize(desc))]
991
+ const cvt = (raw16) => typed(['call', '$__f16_to_f64', raw16], 'f64')
992
+ const le = staticLE(leNode)
993
+ let body
994
+ if (le === true) body = cvt(['i32.load16_u', addr])
995
+ else if (le === false) body = cvt(bswap16I32(typed(['i32.load16_u', addr], 'i32')))
996
+ else {
997
+ const aT = tempI32('dvga'), leT = tempI32('dvgle')
998
+ body = typed(['block', ['result', 'f64'],
999
+ ['local.set', `$${aT}`, addr],
1000
+ ['local.set', `$${leT}`, truthyIR(emit(leNode))],
1001
+ ['if', ['result', 'f64'], ['local.get', `$${leT}`],
1002
+ ['then', cvt(['i32.load16_u', ['local.get', `$${aT}`]])],
1003
+ ['else', cvt(bswap16I32(typed(['i32.load16_u', ['local.get', `$${aT}`]], 'i32')))]]], 'f64')
1004
+ }
1005
+ return typed(['block', ['result', 'f64'],
1006
+ ['local.set', `$${desc}`, dvDescriptor(dv)],
1007
+ asF64(body)], 'f64')
1008
+ }
1009
+
1010
+ ctx.core.emit['.setFloat16'] = (dv, off, val, leNode) => {
1011
+ inc('__f64_to_f16')
1012
+ const desc = tempI32('dvD'), aT = tempI32('dvsa'), vT = tempI32('dvsv16')
1013
+ const addr = ['i32.add', dvDataOff(desc), dvIndexChecked(off, 2, dvViewSize(desc))]
1014
+ const le = staticLE(leNode)
1015
+ const store = (bits) => ['i32.store16', ['local.get', `$${aT}`], bits]
1016
+ const bitsLE = ['local.get', `$${vT}`]
1017
+ const bitsBE = () => bswap16I32(typed(['local.get', `$${vT}`], 'i32'))
1018
+ let storeIR
1019
+ if (le === true) storeIR = store(bitsLE)
1020
+ else if (le === false) storeIR = store(bitsBE())
1021
+ else {
1022
+ const leT = tempI32('dvsle')
1023
+ storeIR = ['block',
1024
+ ['local.set', `$${leT}`, truthyIR(emit(leNode))],
1025
+ ['if', ['local.get', `$${leT}`], ['then', store(bitsLE)], ['else', store(bitsBE())]]]
1026
+ }
1027
+ return typed(['block', ['result', 'f64'],
1028
+ ['local.set', `$${desc}`, dvDescriptor(dv)],
1029
+ ['local.set', `$${aT}`, addr],
1030
+ ['local.set', `$${vT}`, ['call', '$__f64_to_f16', asF64(emit(val))]],
1031
+ storeIR,
1032
+ undefExpr()], 'f64')
1033
+ }
1034
+
978
1035
  // TypedArray.from(arr) — convert regular array to typed array
979
1036
  for (const [name, elemType] of Object.entries(TYPED_ELEM_CODE)) {
980
1037
  const aux = typedAux(name)
981
1038
  const stride = STRIDE[elemType], store = STORE[elemType]
982
1039
  ctx.core.emit[`${name}.from`] = (src) => {
983
1040
  ctx.features.typedarray = true
1041
+ if (name === 'Float16Array') ctx.features.f16 = true
1042
+ if (name === 'Uint8ClampedArray') ctx.features.clamped = true
1043
+ const fl = { et: elemType, isF16: name === 'Float16Array', isClamped: name === 'Uint8ClampedArray' }
984
1044
  // Bare array-literal source (`Int32Array.from([…])`, `new Int32Array([…])`): build the
985
1045
  // typed array directly — alloc + one native-typed store per element — instead of
986
1046
  // materializing an intermediate f64 ARRAY (every element a 9-byte f64.const) plus a
@@ -996,6 +1056,7 @@ export default (ctx) => {
996
1056
  for (let k = 0; k < elems.length; k++) {
997
1057
  const addr = k === 0 ? ['local.get', `$${out.local}`]
998
1058
  : ['i32.add', ['local.get', `$${out.local}`], ['i32.const', k * stride]]
1059
+ if (fl.isF16 || fl.isClamped) { body.push(elemStoreIR(fl, addr, asF64(emit(elems[k])))); continue }
999
1060
  const v = elemType <= 5 ? asI32(emit(elems[k]))
1000
1061
  : elemType === 6 ? ['f32.demote_f64', asF64(emit(elems[k]))]
1001
1062
  : asF64(emit(elems[k]))
@@ -1012,9 +1073,10 @@ export default (ctx) => {
1012
1073
  const id = ctx.func.uniq++
1013
1074
  const conv = FROM_F64[elemType]
1014
1075
  const srcF64 = ['f64.load', ['i32.add', ['local.get', `$${off}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', 3]]]]
1015
- const storeExpr = [store,
1016
- ['i32.add', ['local.get', `$${t}`], ['i32.mul', ['local.get', `$${i}`], ['i32.const', stride]]],
1017
- conv ? [conv, srcF64] : srcF64]
1076
+ const dstAddr = ['i32.add', ['local.get', `$${t}`], ['i32.mul', ['local.get', `$${i}`], ['i32.const', stride]]]
1077
+ const storeExpr = (fl.isF16 || fl.isClamped)
1078
+ ? elemStoreIR(fl, dstAddr, srcF64)
1079
+ : [store, dstAddr, conv ? [conv, srcF64] : srcF64]
1018
1080
  return typed(['block', ['result', 'f64'],
1019
1081
  ['local.set', `$${srcL}`, asF64(emit(src))],
1020
1082
  ['local.set', `$${off}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${srcL}`]]]],
@@ -1060,11 +1122,40 @@ export default (ctx) => {
1060
1122
  ? ctx.func.localReps?.get(receiver[1])?.arrayElemTypedCtor
1061
1123
  : null)
1062
1124
  || (typeof receiver === 'string' && ctx.types.typedElem?.get(receiver))
1125
+ // Direct fresh-ctor receiver: `new Int32Array([…]).map(f)` — the ctor call
1126
+ // node IS the receiver, no binding to look up. Without this the chain fell
1127
+ // to the plain-array emitters, which read f64 slots — silently wrong for
1128
+ // every element kind except Float64Array.
1129
+ || typedElemCtor(receiver)
1063
1130
  if (!ctor) return null
1064
1131
  const isView = viewOutput || (!chainOutput && ctor.endsWith('.view'))
1065
1132
  const name = ctor.endsWith('.view') ? ctor.slice(4, -5) : ctor.slice(4)
1066
1133
  const et = TYPED_ELEM_CODE[name]
1067
- return et == null ? null : { et, isView, isBigInt: name === 'BigInt64Array' || name === 'BigUint64Array' }
1134
+ if (name === 'Float16Array') ctx.features.f16 = true
1135
+ if (name === 'Uint8ClampedArray') ctx.features.clamped = true
1136
+ return et == null ? null : { et, isView, name,
1137
+ isBigInt: name === 'BigInt64Array' || name === 'BigUint64Array',
1138
+ isF16: name === 'Float16Array', isClamped: name === 'Uint8ClampedArray' }
1139
+ }
1140
+
1141
+ // Canonical element accessors — the ONE home for kind-aware load/store IR.
1142
+ // r is a resolveElem result (or {et,...} with optional flags). f16 rides the
1143
+ // u16 slot with a conversion kernel each way; clamped is a u8 with the
1144
+ // ToUint8Clamp store. Everything else keeps the exact historical opcodes.
1145
+ const elemLoadIR = (r, off) => {
1146
+ if (r.isBigInt) return ['f64.reinterpret_i64', ['i64.load', off]]
1147
+ if (r.isF16) { inc('__f16_to_f64'); return ['call', '$__f16_to_f64', ['i32.load16_u', off]] }
1148
+ if (r.et === 7) return ['f64.load', off]
1149
+ if (r.et === 6) return ['f64.promote_f32', ['f32.load', off]]
1150
+ return [(r.et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[r.et], off]]
1151
+ }
1152
+ const elemStoreIR = (r, off, valF64) => {
1153
+ if (r.isBigInt) return ['i64.store', off, ['i64.reinterpret_f64', valF64]]
1154
+ if (r.isF16) { inc('__f64_to_f16'); return ['i32.store16', off, ['call', '$__f64_to_f16', valF64]] }
1155
+ if (r.isClamped) { inc('__u8_clamp'); return ['i32.store8', off, ['call', '$__u8_clamp', valF64]] }
1156
+ if (r.et === 7) return ['f64.store', off, valF64]
1157
+ if (r.et === 6) return ['f32.store', off, ['f32.demote_f64', valF64]]
1158
+ return [STORE[r.et], off, [(r.et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', valF64]]
1068
1159
  }
1069
1160
 
1070
1161
  /** Emit the real data byte-address for a typed array IR node.
@@ -1087,7 +1178,7 @@ export default (ctx) => {
1087
1178
 
1088
1179
  // Runtime-dispatch typed index: checks ptr_type + aux to load with correct stride.
1089
1180
  // For TYPED views (aux bit 3), $off indirects through descriptor[4] to real data.
1090
- ctx.core.stdlib['__typed_set_idx'] = `(func $__typed_set_idx (param $ptr i64) (param $i i32) (param $v f64) (result f64)
1181
+ ctx.core.stdlib['__typed_set_idx'] = () => `(func $__typed_set_idx (param $ptr i64) (param $i i32) (param $v f64) (result f64)
1091
1182
  (local $off i32) (local $aux i32) (local $et i32) (local $bits i32) (local $vb i64)
1092
1183
  (local.set $aux (call $__ptr_aux (local.get $ptr)))
1093
1184
  (local.set $off (call $__ptr_offset (local.get $ptr)))
@@ -1110,6 +1201,14 @@ export default (ctx) => {
1110
1201
  (if (i32.or (i64.eq (local.get $vb) (i64.const ${FALSE_NAN}))
1111
1202
  (i64.eq (local.get $vb) (i64.const ${NULL_NAN})))
1112
1203
  (then (local.set $v (f64.const 0))))))
1204
+ ${ctx.features.f16 ? `(if (i32.and (local.get $aux) (i32.const 32)) (then
1205
+ (i32.store16 (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1)))
1206
+ (call $__f64_to_f16 (local.get $v)))
1207
+ (return (local.get $v))))` : ''}
1208
+ ${ctx.features.clamped ? `(if (i32.and (local.get $aux) (i32.const 64)) (then
1209
+ (i32.store8 (i32.add (local.get $off) (local.get $i))
1210
+ (call $__u8_clamp (local.get $v)))
1211
+ (return (local.get $v))))` : ''}
1113
1212
  (if (i32.eq (local.get $et) (i32.const 7))
1114
1213
  (then (f64.store (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 3))) (local.get $v)))
1115
1214
  (else
@@ -1150,7 +1249,7 @@ export default (ctx) => {
1150
1249
  // Integers convert by signedness (et&1 ⇒ unsigned); BigInt returns the raw i64 bits
1151
1250
  // reinterpreted as f64 so a get→set roundtrip is bit-exact (set_idx stores the bits
1152
1251
  // back unchanged). Used by the in-place algorithms below for random-access reads.
1153
- ctx.core.stdlib['__typed_get_idx'] = `(func $__typed_get_idx (param $ptr i64) (param $i i32) (result f64)
1252
+ ctx.core.stdlib['__typed_get_idx'] = () => `(func $__typed_get_idx (param $ptr i64) (param $i i32) (result f64)
1154
1253
  (local $off i32) (local $aux i32) (local $et i32)
1155
1254
  (local.set $aux (call $__ptr_aux (local.get $ptr)))
1156
1255
  (local.set $off (call $__ptr_offset (local.get $ptr)))
@@ -1168,9 +1267,11 @@ export default (ctx) => {
1168
1267
  (then (f64.convert_i32_u (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))
1169
1268
  (else (f64.convert_i32_s (i32.load (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 2))))))))
1170
1269
  (else (if (result f64) (i32.ge_u (local.get $et) (i32.const 2))
1171
- (then (if (result f64) (i32.and (local.get $et) (i32.const 1))
1270
+ (then ${ctx.features.f16 ? `(if (result f64) (i32.and (local.get $aux) (i32.const 32))
1271
+ (then (call $__f16_to_f64 (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
1272
+ (else ` : ''}(if (result f64) (i32.and (local.get $et) (i32.const 1))
1172
1273
  (then (f64.convert_i32_u (i32.load16_u (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))
1173
- (else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1))))))))
1274
+ (else (f64.convert_i32_s (i32.load16_s (i32.add (local.get $off) (i32.shl (local.get $i) (i32.const 1)))))))${ctx.features.f16 ? '))' : ''})
1174
1275
  (else (if (result f64) (i32.and (local.get $et) (i32.const 1))
1175
1276
  (then (f64.convert_i32_u (i32.load8_u (i32.add (local.get $off) (local.get $i)))))
1176
1277
  (else (f64.convert_i32_s (i32.load8_s (i32.add (local.get $off) (local.get $i)))))))))))))))))`
@@ -1339,16 +1440,57 @@ export default (ctx) => {
1339
1440
  // ADJACENT HEAP or trapped past memory (the Root F silent-corruption class).
1340
1441
  // `i32.lt_u` folds the negative case in (a negative i32 is a huge u32). The
1341
1442
  // checked result is number|undefined, so it carries NO valKind tag.
1443
+ // Elem-count for a checked site. A TypedArray's length is immutable, so for
1444
+ // a stable PARAM receiver (readable at entry, never reassigned in the body)
1445
+ // the shifted count materializes ONCE as a function-entry local shared by
1446
+ // every checked read/write guard of that receiver — the size-tier
1447
+ // complement of the speed tier's loop hoists (drained by collectParamInits,
1448
+ // the probeHoist mechanism). Non-params (mid-body inits) and reassigned
1449
+ // names keep the inline header load.
1450
+ const leanLen = (arr, et, isView) => {
1451
+ const lenIR = () => ['i32.shr_u',
1452
+ ['i32.load', isView ? typedBase(emit(arr)) : ['i32.sub', typedBase(emit(arr)), ['i32.const', 8]]],
1453
+ ['i32.const', SHIFT[et]]]
1454
+ if (!ctx.transform.optimize?.leanCheckedIdx ||
1455
+ typeof arr !== 'string' ||
1456
+ !ctx.func.current?.params?.some(p => p.name === arr) ||
1457
+ !ctx.func.body || isReassigned(ctx.func.body, arr)) return lenIR()
1458
+ const memo = (ctx.func.lenHoist ??= new Map())
1459
+ const key = `${arr} ${SHIFT[et]}${isView ? 'v' : ''}`
1460
+ let h = memo.get(key)
1461
+ if (!h) {
1462
+ h = { t: tempI32('tlen'), init: lenIR() }
1463
+ memo.set(key, h)
1464
+ }
1465
+ return ['local.get', `$${h.t}`]
1466
+ }
1342
1467
  ctx.core.emit['.typed:[]'] = (arr, i) => {
1343
1468
  const r = resolveElem(arr)
1344
1469
  if (r == null) return null // unknown type, fallback to generic
1345
1470
  const { et, isView, isBigInt } = r
1346
1471
  const proven = typedIdxProven(arr, i)
1347
- const loadOf = (off) => isBigInt ? ['f64.reinterpret_i64', ['i64.load', off]]
1348
- : et === 7 ? ['f64.load', off]
1349
- : et === 6 ? ['f64.promote_f32', ['f32.load', off]]
1350
- : [(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]]
1472
+ const loadOf = (off) => elemLoadIR(r, off)
1351
1473
  if (!proven) {
1474
+ // (A $__typed_idx call per site was tried for the size tier and REVERTED:
1475
+ // the helper + its __len/__ptr_offset chain cost ~+900 B while these
1476
+ // kernels have 1-3 unproven sites — inline wins until many sites share it.)
1477
+ // Size tier: IF-form checked read — guard hits load DIRECTLY (no address
1478
+ // clamp, no select pair), guard misses yield undefined. ~6 ops/site
1479
+ // leaner than the branchless form below, whose only reason is keeping
1480
+ // SPEED-tier kernel bodies branch-free for the SIMD lift (off at -Os).
1481
+ if (ctx.transform.optimize?.leanCheckedIdx) {
1482
+ const ti = tempI32('tbi')
1483
+ const off = ['i32.add', typedDataAddr(emit(arr), isView),
1484
+ ['i32.shl', ['local.get', `$${ti}`], ['i32.const', SHIFT[et]]]]
1485
+ const rd = typed(['if', ['result', 'f64'],
1486
+ ['i32.lt_u', ['local.tee', `$${ti}`, idx(i)], leanLen(arr, et, isView)],
1487
+ ['then', loadOf(off)],
1488
+ ['else', undefExpr()]], 'f64')
1489
+ // number|undefined with the undefined confined to a CONST arm — a numeric
1490
+ // consumer (toNumF64) folds ToNumber into that arm statically
1491
+ if (!isBigInt) rd.checkedNumRead = true
1492
+ return rd
1493
+ }
1352
1494
  // BRANCHLESS checked read: `select(load(in ? idx : 0), undefined, in)`. The
1353
1495
  // address clamp makes the load unconditionally safe (index 0 of the data
1354
1496
  // region is mapped arena even for a 0-length array — the loaded value is
@@ -1364,10 +1506,12 @@ export default (ctx) => {
1364
1506
  const off = ['i32.add', typedDataAddr(emit(arr), isView),
1365
1507
  ['i32.shl', ['select', ['local.get', `$${ti}`], ['i32.const', 0], ['local.get', `$${tin}`]],
1366
1508
  ['i32.const', SHIFT[et]]]]
1367
- return typed(['block', ['result', 'f64'],
1509
+ const rd = typed(['block', ['result', 'f64'],
1368
1510
  ['local.set', `$${ti}`, idx(i)],
1369
1511
  ['local.set', `$${tin}`, ['i32.lt_u', ['local.get', `$${ti}`], lenIR]],
1370
1512
  ['select', loadOf(off), undefExpr(), ['local.get', `$${tin}`]]], 'f64')
1513
+ if (!isBigInt) rd.checkedNumRead = true
1514
+ return rd
1371
1515
  }
1372
1516
  const objIR = emit(arr), vi = idx(i)
1373
1517
  const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
@@ -1377,6 +1521,25 @@ export default (ctx) => {
1377
1521
  const t = typed(loadOf(off), 'f64'); t.valKind = VAL.NUMBER; return t
1378
1522
  }
1379
1523
 
1524
+ // A store value that can evaluate INSIDE the bounds guard when the assignment
1525
+ // is a statement (void): pure reads/arithmetic — including a checked-read
1526
+ // if-form and compiler-owned tees (the ${'$'}+T temp namespace; their writes are
1527
+ // site-private) — but never calls or writes to user-visible names. Spec-wise
1528
+ // the RHS evaluates regardless of bounds; for a PURE value that is
1529
+ // unobservable, and dropping the temp+set per site is the -Os win on codec
1530
+ // kernels (out[op+k] = pure-int-expr).
1531
+ const PURE_STORE_OP = /^(i32|i64|f32|f64)\.(load(8_[su]|16_[su]|32_[su])?|const|add|sub|mul|div(_[su])?|and|or|xor|shl|shr_[su]|rotl|rotr|eqz|eq|ne|[lg][te](_[su])?|clz|ctz|popcnt|convert_i(32|64)_[su]|promote_f32|demote_f64|reinterpret_(i32|i64|f32|f64)|trunc_sat_f(32|64)_[su]|wrap_i64|extend(8|16|32)?_(s|i32_[su])|neg|abs|min|max|sqrt|ceil|floor|trunc|nearest|copysign)$/
1532
+ const pureStorable = (n) => {
1533
+ if (!Array.isArray(n)) return true
1534
+ const op = n[0]
1535
+ if (op === 'local.get') return typeof n[1] === 'string'
1536
+ if (op === 'local.tee') return typeof n[1] === 'string' && n[1].startsWith('$' + T) && pureStorable(n[2])
1537
+ if (op === 'select' || op === 'if' || op === 'block' || op === 'then' || op === 'else' || op === 'result')
1538
+ return n.slice(1).every(pureStorable)
1539
+ if (PURE_STORE_OP.test(op)) return n.slice(1).every(pureStorable)
1540
+ return false
1541
+ }
1542
+
1380
1543
  // Type-aware TypedArray write: arr[i] = val. Same proof gate as the read: proven
1381
1544
  // indexes keep the direct unchecked store byte-identical; unproven ones evaluate
1382
1545
  // the RHS (its effects and the assignment's value are unconditional per spec),
@@ -1399,32 +1562,65 @@ export default (ctx) => {
1399
1562
  // Wrap a store statement in the bounds guard on the unproven path. The value
1400
1563
  // temp is set OUTSIDE the guard (spec: RHS evaluates regardless).
1401
1564
  const guard = (store) => proven ? store
1402
- : ['if', ['i32.lt_u', vi, ['i32.shr_u',
1403
- ['i32.load', isView ? typedBase(emit(arr)) : ['i32.sub', typedBase(emit(arr)), ['i32.const', 8]]],
1404
- ['i32.const', SHIFT[et]]]], ['then', store]]
1405
- const objIR = emit(arr), valIR = emit(val)
1565
+ : ['if', ['i32.lt_u', vi, leanLen(arr, et, isView)], ['then', store]]
1566
+ const objIR = emit(arr); let valIR = emit(val)
1406
1567
  const off = ['i32.add', typedDataAddr(objIR, isView), ['i32.shl', vi, ['i32.const', SHIFT[et]]]]
1407
- if (isBigInt) {
1568
+ if (r.isF16 || r.isClamped) {
1569
+ // conversion is not a truncation — always through the kernel (RTNE /
1570
+ // ToUint8Clamp); the i32Backed shortcut below would store raw low bits
1408
1571
  const vt = temp('tw')
1572
+ const conv = elemStoreIR(r, off, ['local.get', `$${vt}`])
1409
1573
  return typed(void_ ? ['block', ...pre,
1410
1574
  ['local.set', `$${vt}`, asF64(valIR)],
1411
- guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]])]
1575
+ guard(conv)]
1412
1576
  : ['block', ['result', 'f64'], ...pre,
1413
1577
  ['local.set', `$${vt}`, asF64(valIR)],
1414
- guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]]),
1578
+ guard(conv),
1415
1579
  ['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
1416
1580
  }
1417
- if (et === 7) {
1581
+ if (isBigInt) {
1582
+ if (void_ && ctx.transform.optimize?.leanCheckedIdx && pureStorable(valIR)) return typed(['block', ...pre,
1583
+ guard(['i64.store', off, ['i64.reinterpret_f64', asF64(valIR)]])], 'void')
1418
1584
  const vt = temp('tw')
1419
1585
  return typed(void_ ? ['block', ...pre,
1420
1586
  ['local.set', `$${vt}`, asF64(valIR)],
1421
- guard(['f64.store', off, ['local.get', `$${vt}`]])]
1587
+ guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]])]
1422
1588
  : ['block', ['result', 'f64'], ...pre,
1423
1589
  ['local.set', `$${vt}`, asF64(valIR)],
1424
- guard(['f64.store', off, ['local.get', `$${vt}`]]),
1425
- ['local.get', `$${vt}`]], void_ ? 'void' : 'f64') // Float64Array
1590
+ guard(['i64.store', off, ['i64.reinterpret_f64', ['local.get', `$${vt}`]]]),
1591
+ ['local.get', `$${vt}`]], void_ ? 'void' : 'f64')
1592
+ }
1593
+ if (et === 7) { // Float64Array
1594
+ // ToNumber on the STORED value (spec: TypedArray stores coerce). Via
1595
+ // toNumF64, so a provably-numeric RHS keeps the raw store byte-identical
1596
+ // and a checked-read RHS folds its sentinel arm statically — raw sentinel
1597
+ // BITS in an f64 slot read back as `undefined` at the boundary where JS
1598
+ // stores (and reads back) NaN. The assignment's own VALUE (non-void
1599
+ // result) is the RHS pre-coercion per spec, so that path coerces a COPY
1600
+ // at the store only (nullish-canon on the temp — no strings can hide in
1601
+ // a no-__to_num program, so the sentinel canon IS full ToNumber there).
1602
+ const stored = toNumF64(val, valIR)
1603
+ if (void_) {
1604
+ if (ctx.transform.optimize?.leanCheckedIdx && pureStorable(stored)) return typed(['block', ...pre,
1605
+ guard(['f64.store', off, asF64(stored)])], 'void')
1606
+ const vt = temp('tw')
1607
+ return typed(['block', ...pre,
1608
+ ['local.set', `$${vt}`, asF64(stored)],
1609
+ guard(['f64.store', off, ['local.get', `$${vt}`]])], 'void')
1610
+ }
1611
+ const vt = temp('tw')
1612
+ const reread = typed(['local.get', `$${vt}`], 'f64')
1613
+ const storeV = stored === valIR ? reread
1614
+ : ctx.core.stdlib['__to_num'] ? toNumF64(val, reread)
1615
+ : coerceNullishToNum(reread)
1616
+ return typed(['block', ['result', 'f64'], ...pre,
1617
+ ['local.set', `$${vt}`, asF64(valIR)],
1618
+ guard(['f64.store', off, asF64(storeV)]),
1619
+ ['local.get', `$${vt}`]], 'f64')
1426
1620
  }
1427
1621
  if (et === 6) {
1622
+ if (void_ && ctx.transform.optimize?.leanCheckedIdx && pureStorable(valIR)) return typed(['block', ...pre,
1623
+ guard(['f32.store', off, ['f32.demote_f64', asF64(valIR)]])], 'void')
1428
1624
  const vt = temp('tw')
1429
1625
  return typed(void_ ? ['block', ...pre,
1430
1626
  ['local.set', `$${vt}`, asF64(valIR)],
@@ -1442,13 +1638,32 @@ export default (ctx) => {
1442
1638
  // and `dst[i] = src[j]` (base64, qoi, wav, blur) — where both sides are integer elements.
1443
1639
  // `store8/16` mask the low bits, so storing the convert's i32 source is bit-identical; the
1444
1640
  // non-void result reboxes that i32 to f64 (the assignment's RHS value, in element range here).
1641
+ // A lean checked READ as the store value (out[op] = src[i] — the codec
1642
+ // byte-transform class): ToInt32 composes through it — the hit arm's
1643
+ // convert peels to the raw i32 load, the undefined miss arm is NaN → 0.
1644
+ // Rebuilding as an i32 if-form keeps the store on the integer path
1645
+ // (no f64 round-trip, no temp) — the -Os pairing of the two emitters.
1646
+ if (Array.isArray(valIR) && valIR[0] === 'if' &&
1647
+ Array.isArray(valIR[1]) && valIR[1][1] === 'f64' &&
1648
+ Array.isArray(valIR[3]) && valIR[3][0] === 'then' &&
1649
+ Array.isArray(valIR[3][1]) && (valIR[3][1][0] === 'f64.convert_i32_s' || valIR[3][1][0] === 'f64.convert_i32_u') &&
1650
+ Array.isArray(valIR[4]) && valIR[4][0] === 'else' &&
1651
+ Array.isArray(valIR[4][1]) && valIR[4][1][0] === 'f64.const' && String(valIR[4][1][1]).startsWith('nan:')) {
1652
+ valIR = typed(['if', ['result', 'i32'], valIR[2],
1653
+ ['then', valIR[3][1][1]],
1654
+ ['else', ['i32.const', 0]]], 'i32')
1655
+ }
1445
1656
  const i32Backed = valIR.type === 'i32' ||
1446
1657
  (Array.isArray(valIR) && (valIR[0] === 'f64.convert_i32_s' || valIR[0] === 'f64.convert_i32_u'))
1447
1658
  if (i32Backed) {
1448
1659
  const vi32 = asI32(valIR)
1449
- const cheap = Array.isArray(vi32) &&
1450
- ((vi32[0] === 'local.get' && typeof vi32[1] === 'string') ||
1451
- (vi32[0] === 'i32.const' && (typeof vi32[1] === 'number' || typeof vi32[1] === 'string')))
1660
+ // lean (-Os) widens "cheap" to any pure value (inline into the guard, no
1661
+ // temp); SPEED tiers keep the narrow form — the temp'd store is the shape
1662
+ // the SIMD widening/in-place recognizers pattern-match (battery-caught).
1663
+ const cheap = ctx.transform.optimize?.leanCheckedIdx ? pureStorable(vi32)
1664
+ : Array.isArray(vi32) &&
1665
+ ((vi32[0] === 'local.get' && typeof vi32[1] === 'string') ||
1666
+ (vi32[0] === 'i32.const' && (typeof vi32[1] === 'number' || typeof vi32[1] === 'string')))
1452
1667
  if (void_ && cheap && proven) return typed([STORE[et], off, vi32], 'void')
1453
1668
  if (void_ && cheap) return typed(['block', ...pre, guard([STORE[et], off, vi32])], 'void')
1454
1669
  const v32 = tempI32('tw')
@@ -1493,9 +1708,7 @@ export default (ctx) => {
1493
1708
  if (r) {
1494
1709
  const { et } = r
1495
1710
  const addr = ['i32.add', ['local.get', `$${dst}`], ['i32.shl', idx, ['i32.const', SHIFT[et]]]]
1496
- store = et === 7 ? ['f64.store', addr, val]
1497
- : et === 6 ? ['f32.store', addr, ['f32.demote_f64', val]]
1498
- : [STORE[et], addr, [(et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', val]]
1711
+ store = elemStoreIR(r, addr, val)
1499
1712
  } else {
1500
1713
  store = ['drop', ['call', '$__typed_set_idx', ['i64.reinterpret_f64', ['local.get', `$${dst}`]], idx, val]]
1501
1714
  }
@@ -1522,9 +1735,7 @@ export default (ctx) => {
1522
1735
  const r = resolveElem(arr)
1523
1736
  const elemType = r?.et
1524
1737
  const isView = r?.isView
1525
- const elemName = r && (r.isBigInt
1526
- ? (elemType === 7 ? 'BigInt64Array' : 'BigUint64Array') // unreachable: SIMD path gated below
1527
- : ['Int8Array','Uint8Array','Int16Array','Uint16Array','Int32Array','Uint32Array','Float32Array','Float64Array'][elemType])
1738
+ const elemName = r?.name
1528
1739
 
1529
1740
  // BigInt typed arrays: SIMD path doesn't support them; defer to scalar map.
1530
1741
  if (r?.isBigInt) {
@@ -1533,8 +1744,9 @@ export default (ctx) => {
1533
1744
  return null
1534
1745
  }
1535
1746
 
1536
- // Try SIMD: inline arrow with recognizable pattern
1537
- if (elemType != null && Array.isArray(fn) && fn[0] === '=>') {
1747
+ // Try SIMD: inline arrow with recognizable pattern (f16/clamped decline —
1748
+ // their element conversion is a call, not a lane op)
1749
+ if (elemType != null && !r.isF16 && !r.isClamped && Array.isArray(fn) && fn[0] === '=>') {
1538
1750
  const [, rawParam, body] = fn
1539
1751
  const param = Array.isArray(rawParam) && rawParam[0] === '()' ? rawParam[1] : rawParam
1540
1752
  const pattern = analyzeSimd(body, param)
@@ -1562,15 +1774,11 @@ export default (ctx) => {
1562
1774
 
1563
1775
  const loadElem = () => {
1564
1776
  const off = ['i32.add', ['local.get', `$${ptr}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', shift]]]
1565
- if (elemType === 7) return typed(['f64.load', off], 'f64')
1566
- if (elemType === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64')
1567
- return typed([(elemType & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[elemType], off]], 'f64')
1777
+ return typed(elemLoadIR(r, off), 'f64')
1568
1778
  }
1569
1779
  const storeElem = (val) => {
1570
1780
  const off = ['i32.add', ['local.get', `$${out}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', shift]]]
1571
- if (elemType === 7) return ['f64.store', off, val]
1572
- if (elemType === 6) return ['f32.store', off, ['f32.demote_f64', val]]
1573
- return [STORE[elemType], off, [(elemType & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', val]]
1781
+ return elemStoreIR(r, off, val)
1574
1782
  }
1575
1783
 
1576
1784
  const id = ctx.func.uniq++
@@ -1618,9 +1826,7 @@ export default (ctx) => {
1618
1826
  inc('__len')
1619
1827
  const loadElem = () => {
1620
1828
  const off = ['i32.add', ['local.get', `$${ptr}`], ['i32.shl', ['local.get', `$${i}`], ['i32.const', SHIFT[et]]]]
1621
- if (et === 7) return typed(['f64.load', off], 'f64')
1622
- if (et === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64')
1623
- return typed([(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]], 'f64')
1829
+ return typed(elemLoadIR(r, off), 'f64')
1624
1830
  }
1625
1831
  const setup = [
1626
1832
  ['local.set', `$${ptr}`, typedDataAddr(asF64(va), isView)],
@@ -1904,17 +2110,13 @@ export default (ctx) => {
1904
2110
  inc('__len')
1905
2111
  const loadAt = (ptrLoc, iLoc) => {
1906
2112
  const off = ['i32.add', ['local.get', `$${ptrLoc}`], ['i32.shl', ['local.get', `$${iLoc}`], ['i32.const', SHIFT[et]]]]
1907
- if (et === 7) return typed(['f64.load', off], 'f64')
1908
- if (et === 6) return typed(['f64.promote_f32', ['f32.load', off]], 'f64')
1909
- return typed([(et & 1) ? 'f64.convert_i32_u' : 'f64.convert_i32_s', [LOAD[et], off]], 'f64')
2113
+ return typed(elemLoadIR(r, off), 'f64')
1910
2114
  }
1911
2115
  const storeAt = (ptrLoc, iLoc, valF64) => {
1912
2116
  const off = ['i32.add', ['local.get', `$${ptrLoc}`], ['i32.shl', ['local.get', `$${iLoc}`], ['i32.const', SHIFT[et]]]]
1913
- if (et === 7) return ['f64.store', off, valF64]
1914
- if (et === 6) return ['f32.store', off, ['f32.demote_f64', valF64]]
1915
- return [STORE[et], off, [(et & 1) ? 'i32.trunc_f64_u' : 'i32.trunc_f64_s', valF64]]
2117
+ return elemStoreIR(r, off, valF64)
1916
2118
  }
1917
- const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(ET_NAME[et]),
2119
+ const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(r.name || ET_NAME[et]),
1918
2120
  len: ['i32.shl', ['local.get', `$${maxLen}`], ['i32.const', SHIFT[et]]],
1919
2121
  stride: 1, tag: 'tfd' })
1920
2122
  const id = ctx.func.uniq++
@@ -1987,7 +2189,7 @@ export default (ctx) => {
1987
2189
  ['i32.gt_s', ['local.get', `$${idx}`], ['local.get', `$${srcLen}`]]],
1988
2190
  ['i32.lt_s', ['local.get', `$${idx}`], ['i32.const', 0]]]]
1989
2191
  }
1990
- const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(ET_NAME[et]),
2192
+ const dst = allocPtr({ type: PTR.TYPED, aux: typedAux(r.name || ET_NAME[et]),
1991
2193
  len: ['i32.shl', ['local.get', `$${n}`], ['i32.const', SHIFT[et]]],
1992
2194
  stride: 1, tag: 'tsd' })
1993
2195
  return typed(['block', ['result', 'f64'],
@@ -2105,4 +2307,112 @@ export default (ctx) => {
2105
2307
  ['i32.store', ['i32.add', ['local.get', `$${desc}`], ['i32.const', 8]], ['local.get', `$${root}`]],
2106
2308
  mkPtrIR(PTR.TYPED, viewAux, ['local.get', `$${desc}`])], 'f64')
2107
2309
  }
2310
+
2311
+ // === ES2026 Uint8Array base64/hex codecs ===
2312
+ // Kernels live in module/string.js (byte↔text is string domain); these
2313
+ // emitters wire the typed-array surface: u8.toBase64()/toHex()/setFrom*()
2314
+ // and the Uint8Array.fromBase64/fromHex statics. Options are compile-time
2315
+ // literals (jz doctrine: no per-call option-bag parsing) — alphabet
2316
+ // 'base64'|'base64url', omitPadding, and lastChunkHandling:'loose' (the
2317
+ // default; other modes are rejected with a clean error).
2318
+ const codecOpts = (node, method, allowPad) => {
2319
+ let url = 0, pad = 1
2320
+ if (node === undefined) return { url, pad }
2321
+ if (!Array.isArray(node) || (node[0] !== '{' && node[0] !== '{}'))
2322
+ err(`${method} options must be a literal object — jz resolves codec options at compile time`)
2323
+ // prepared literals arrive as ['{}'|'{', ...entries] (entries may also ride
2324
+ // a single ','/';' wrapper node)
2325
+ const entries = node.length === 2 && Array.isArray(node[1]) && (node[1][0] === ',' || node[1][0] === ';')
2326
+ ? node[1].slice(1) : node.slice(1).filter(e => e != null)
2327
+ const lit = (v) => Array.isArray(v) && v[0] === 'str' ? v[1]
2328
+ : Array.isArray(v) && v[0] === 'bool' ? !!v[1]
2329
+ : Array.isArray(v) && v[0] == null ? v[1] : undefined
2330
+ for (const it of entries) {
2331
+ if (!Array.isArray(it) || it[0] !== ':') err(`${method} options must use literal keys and values`)
2332
+ const k = typeof it[1] === 'string' ? it[1] : Array.isArray(it[1]) && it[1][0] === 'str' ? it[1][1] : null
2333
+ const v = lit(it[2])
2334
+ if (k === 'alphabet') {
2335
+ if (v === 'base64url') url = 1
2336
+ else if (v !== 'base64') err(`${method}: unknown alphabet ${JSON.stringify(v)} — 'base64' or 'base64url'`)
2337
+ } else if (k === 'omitPadding' && allowPad) pad = v ? 0 : 1
2338
+ else if (k === 'lastChunkHandling') {
2339
+ if (v !== 'loose') err(`${method}: only lastChunkHandling:'loose' (the default) is supported`)
2340
+ } else err(`${method}: unsupported option '${k}'`)
2341
+ }
2342
+ return { url, pad }
2343
+ }
2344
+
2345
+ // {read, written} result record for setFromBase64/setFromHex — a one-shot
2346
+ // dictionary (HASH) object, same shape TextEncoder.encodeInto returns.
2347
+ const readWrittenHash = (rwIR) => {
2348
+ inc('__hash_new', '__hash_set')
2349
+ const rw = tempI64('sfrw'), h = temp('sfh')
2350
+ const hI64 = ['i64.reinterpret_f64', ['local.get', `$${h}`]]
2351
+ const field = (hi) => ['i64.reinterpret_f64', ['f64.convert_i32_s',
2352
+ ['i32.wrap_i64', hi ? ['i64.shr_u', ['local.get', `$${rw}`], ['i64.const', 32]] : ['local.get', `$${rw}`]]]]
2353
+ return typed(['block', ['result', 'f64'],
2354
+ ['local.set', `$${rw}`, rwIR],
2355
+ ['local.set', `$${h}`, ['call', '$__hash_new']],
2356
+ ['local.set', `$${h}`, ['f64.reinterpret_i64',
2357
+ ['call', '$__hash_set', hI64, asI64(emit(['str', 'read'])), field(true)]]],
2358
+ ['local.set', `$${h}`, ['f64.reinterpret_i64',
2359
+ ['call', '$__hash_set', hI64, asI64(emit(['str', 'written'])), field(false)]]],
2360
+ ['local.get', `$${h}`]], 'f64')
2361
+ }
2362
+
2363
+ const emitToBase64 = (arr, opts) => {
2364
+ const { url, pad } = codecOpts(opts, 'toBase64', true)
2365
+ ctx.runtime.throws = true
2366
+ inc('__u8_data', '__b64_enc', '__len')
2367
+ const t = temp('tb64')
2368
+ return typed(['block', ['result', 'f64'],
2369
+ ['local.set', `$${t}`, asF64(emit(arr))],
2370
+ ['call', '$__b64_enc',
2371
+ ['call', '$__u8_data', ['i64.reinterpret_f64', ['local.get', `$${t}`]]],
2372
+ ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]],
2373
+ ['i32.const', url], ['i32.const', pad]]], 'f64')
2374
+ }
2375
+ ctx.core.emit['.typed:toBase64'] = ctx.core.emit['.toBase64'] = emitToBase64
2376
+
2377
+ const emitToHex = (arr) => {
2378
+ ctx.runtime.throws = true
2379
+ inc('__u8_data', '__hex_enc', '__len')
2380
+ const t = temp('thex')
2381
+ return typed(['block', ['result', 'f64'],
2382
+ ['local.set', `$${t}`, asF64(emit(arr))],
2383
+ ['call', '$__hex_enc',
2384
+ ['call', '$__u8_data', ['i64.reinterpret_f64', ['local.get', `$${t}`]]],
2385
+ ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]]], 'f64')
2386
+ }
2387
+ ctx.core.emit['.typed:toHex'] = ctx.core.emit['.toHex'] = emitToHex
2388
+
2389
+ const emitSetFromBase64 = (arr, str, opts) => {
2390
+ const { url } = codecOpts(opts, 'setFromBase64', false)
2391
+ ctx.runtime.throws = true
2392
+ inc('__b64_set')
2393
+ return readWrittenHash(['call', '$__b64_set', asI64(emit(arr)), asI64(emit(str)), ['i32.const', url]])
2394
+ }
2395
+ ctx.core.emit['.typed:setFromBase64'] = ctx.core.emit['.setFromBase64'] = emitSetFromBase64
2396
+
2397
+ const emitSetFromHex = (arr, str) => {
2398
+ ctx.runtime.throws = true
2399
+ inc('__hex_set')
2400
+ return readWrittenHash(['call', '$__hex_set', asI64(emit(arr)), asI64(emit(str))])
2401
+ }
2402
+ ctx.core.emit['.typed:setFromHex'] = ctx.core.emit['.setFromHex'] = emitSetFromHex
2403
+
2404
+ ctx.core.emit['Uint8Array.fromBase64'] = (str, opts) => {
2405
+ const { url } = codecOpts(opts, 'fromBase64', false)
2406
+ ctx.runtime.throws = true
2407
+ ctx.features.typedarray = true
2408
+ inc('__b64_from')
2409
+ return typed(['call', '$__b64_from', asI64(emit(str)), ['i32.const', url]], 'f64')
2410
+ }
2411
+
2412
+ ctx.core.emit['Uint8Array.fromHex'] = (str) => {
2413
+ ctx.runtime.throws = true
2414
+ ctx.features.typedarray = true
2415
+ inc('__hex_from')
2416
+ return typed(['call', '$__hex_from', asI64(emit(str))], 'f64')
2417
+ }
2108
2418
  }