jz 0.7.0 → 0.8.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 +37 -33
- package/bench/README.md +176 -73
- package/bench/bench.svg +58 -71
- package/cli.js +12 -5
- package/dist/interop.js +1 -1
- package/dist/jz.js +1156 -984
- package/index.js +40 -9
- package/interop.js +193 -138
- package/layout.js +29 -18
- package/module/array.js +29 -73
- package/module/collection.js +83 -25
- package/module/console.js +1 -1
- package/module/core.js +161 -15
- package/module/json.js +3 -3
- package/module/math.js +26 -3
- package/module/number.js +247 -13
- package/module/object.js +11 -5
- package/module/regex.js +8 -7
- package/module/string.js +162 -156
- package/module/typedarray.js +169 -105
- package/package.json +7 -3
- package/src/abi/string.js +29 -29
- package/src/ast.js +19 -2
- package/src/compile/analyze.js +64 -2
- package/src/compile/cse-load.js +200 -0
- package/src/compile/emit-assign.js +73 -14
- package/src/compile/emit.js +253 -23
- package/src/compile/index.js +198 -61
- package/src/compile/loop-divmod.js +12 -58
- package/src/compile/loop-model.js +91 -0
- package/src/compile/loop-square.js +102 -0
- package/src/compile/narrow.js +169 -32
- package/src/compile/peel-stencil.js +18 -64
- package/src/compile/plan/common.js +29 -0
- package/src/compile/plan/index.js +4 -1
- package/src/compile/plan/inline.js +176 -21
- package/src/compile/plan/literals.js +93 -19
- package/src/ctx.js +51 -12
- package/src/helper-counters.js +137 -0
- package/src/ir.js +102 -13
- package/src/kind-traits.js +7 -3
- package/src/kind.js +14 -1
- package/src/op-policy.js +5 -2
- package/src/ops.js +119 -0
- package/src/optimize/index.js +960 -136
- package/src/optimize/recurse.js +182 -0
- package/src/optimize/vectorize.js +1292 -144
- package/src/prepare/index.js +11 -7
- package/src/reps.js +4 -1
- package/src/type.js +53 -45
- package/src/wat/assemble.js +92 -9
- package/src/widen.js +21 -0
- package/src/wat/optimize.js +0 -3938
package/module/string.js
CHANGED
|
@@ -22,6 +22,38 @@ import { ssoBitI64Hex, sliceBitI64Hex, ptrNanHex, STR_INTERN_BIT } from '../layo
|
|
|
22
22
|
const SSO_BIT_I64 = ssoBitI64Hex()
|
|
23
23
|
const SLICE_BIT_I64 = sliceBitI64Hex()
|
|
24
24
|
|
|
25
|
+
// === SSO codec — single source of truth for the inline-string bit layout ===
|
|
26
|
+
// ASCII chars packed at 7 bits each into the NaN-box payload: char i at payload bit
|
|
27
|
+
// i*7 (uniform `(ptr >> i*7) & 0x7f`), length at payload bits 42-44, SSO_BIT at bit 46
|
|
28
|
+
// (SLICE_BIT at 45 stays 0). 6 chars fit (6*7=42, + 3-bit len). ASCII-only — a byte
|
|
29
|
+
// ≥0x80 falls back to a heap string. The 7-bit-uniform layout makes equal short strings
|
|
30
|
+
// content-bit-equal (so `op === 'tag'` is a bare i64.eq) and never touches memory.
|
|
31
|
+
const MAX_SSO = 6
|
|
32
|
+
const SSO_LEN_SHIFT = 10 // length occupies aux bits 10-12 (= payload bits 42-44)
|
|
33
|
+
// JS: ASCII string → { aux, offset }, or null when ineligible (too long / non-ASCII).
|
|
34
|
+
// BigInt-free (i32 ops only) so the self-hosted compiler — which runs this to encode
|
|
35
|
+
// every string literal — stays on jz's numeric core. offset = payload bits 0-31, aux =
|
|
36
|
+
// bits 32-46; char i at bit i*7 (chars 0-3 fit the offset, char 4 straddles, char 5 → aux).
|
|
37
|
+
const ssoEncode = (str) => {
|
|
38
|
+
if (str.length > MAX_SSO || !/^[\x00-\x7f]*$/.test(str)) return null
|
|
39
|
+
let offset = 0, auxChars = 0
|
|
40
|
+
for (let i = 0; i < str.length; i++) {
|
|
41
|
+
const c = str.charCodeAt(i), bit = i * 7
|
|
42
|
+
if (bit <= 24) offset |= c << bit // chars 0-3 (bits 0-27), wholly in offset
|
|
43
|
+
else if (bit < 32) { offset |= (c & 0xF) << 28; auxChars |= c >> 4 } // char 4: bits 28-34 straddle
|
|
44
|
+
else auxChars |= c << (bit - 32) // char 5: bits 35-41 → aux bits 3-9
|
|
45
|
+
}
|
|
46
|
+
return { aux: LAYOUT.SSO_BIT | (str.length << SSO_LEN_SHIFT) | auxChars, offset: offset >>> 0 }
|
|
47
|
+
}
|
|
48
|
+
// aux for an SSO string whose chars all fit in the offset (len ≤ 4: 4*7=28 ≤ 32 bits).
|
|
49
|
+
const ssoAux = (len) => LAYOUT.SSO_BIT | (len << SSO_LEN_SHIFT)
|
|
50
|
+
// WAT: char i (i32 expr) of SSO ptr (i64 expr) — 7-bit at payload bit i*7.
|
|
51
|
+
const ssoCharWat = (ptr, i) => `(i32.wrap_i64 (i64.and (i64.shr_u ${ptr} (i64.mul (i64.extend_i32_u ${i}) (i64.const 7))) (i64.const 0x7f)))`
|
|
52
|
+
// WAT: length (i32) of SSO ptr (i64 expr) — payload bits 42-44.
|
|
53
|
+
const ssoLenWat = (ptr) => `(i32.wrap_i64 (i64.and (i64.shr_u ${ptr} (i64.const 42)) (i64.const 7)))`
|
|
54
|
+
// WAT: length (i32) from an already-extracted aux (i32 expr).
|
|
55
|
+
const ssoLenFromAux = (aux) => `(i32.and (i32.shr_u ${aux} (i32.const ${SSO_LEN_SHIFT})) (i32.const 7))`
|
|
56
|
+
|
|
25
57
|
// WAT (no-locals expression): byte length of a heap STRING given its raw $-local
|
|
26
58
|
// names for the offset and the i64 ptr. A view (SLICE_BIT) carries its length in
|
|
27
59
|
// aux[12:0]; an own heap string reads the i32 header at off-4. Callers must have
|
|
@@ -43,26 +75,28 @@ const heapLenExpr = (ptrLocal, offLocal) => `(if (result i32)
|
|
|
43
75
|
// bit-equal (SSO content IS the bits). Bails to the caller's heap path on a
|
|
44
76
|
// non-ASCII byte (SSO is ASCII-only). Locals: $sb scratch byte, $sp packed.
|
|
45
77
|
const sliceSsoPackWat = () => `
|
|
46
|
-
(if (i32.le_u (local.get $nlen) (i32.const
|
|
78
|
+
(if (i32.le_u (local.get $nlen) (i32.const ${MAX_SSO}))
|
|
47
79
|
(then (block $heap8
|
|
48
80
|
(local.set $srcOff (i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
49
81
|
(local.set $isSso (i32.wrap_i64 (i64.shr_u
|
|
50
82
|
(i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64}))
|
|
51
83
|
(i64.const ${LAYOUT.AUX_SHIFT}))))
|
|
52
|
-
(local.set $i (i32.const 0)) (local.set $
|
|
53
|
-
(loop $
|
|
84
|
+
(local.set $i (i32.const 0)) (local.set $sp64 (i64.const 0))
|
|
85
|
+
(loop $pk7
|
|
54
86
|
(if (i32.lt_u (local.get $i) (local.get $nlen))
|
|
55
87
|
(then
|
|
56
88
|
(local.set $sb (if (result i32) (local.get $isSso)
|
|
57
|
-
(then (i32.
|
|
58
|
-
(i32.shr_u (local.get $srcOff) (i32.shl (i32.add (local.get $start) (local.get $i)) (i32.const 3)))
|
|
59
|
-
(i32.const 0xFF)))
|
|
89
|
+
(then ${ssoCharWat('(local.get $ptr)', '(i32.add (local.get $start) (local.get $i))')})
|
|
60
90
|
(else (i32.load8_u (i32.add (i32.add (local.get $srcOff) (local.get $start)) (local.get $i))))))
|
|
61
91
|
(br_if $heap8 (i32.ge_u (local.get $sb) (i32.const 0x80)))
|
|
62
|
-
(local.set $
|
|
92
|
+
(local.set $sp64 (i64.or (local.get $sp64)
|
|
93
|
+
(i64.shl (i64.extend_i32_u (local.get $sb)) (i64.mul (i64.extend_i32_u (local.get $i)) (i64.const 7)))))
|
|
63
94
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
64
|
-
(br $
|
|
65
|
-
(return (
|
|
95
|
+
(br $pk7))))
|
|
96
|
+
(return (f64.reinterpret_i64 (i64.or (i64.or
|
|
97
|
+
(i64.const ${ptrNanHex(PTR.STRING, LAYOUT.SSO_BIT)})
|
|
98
|
+
(i64.shl (i64.extend_i32_u (local.get $nlen)) (i64.const 42)))
|
|
99
|
+
(local.get $sp64)))))))`
|
|
66
100
|
|
|
67
101
|
// Probe the static intern index (buildInternTable, compile/index.js): a 5..32
|
|
68
102
|
// byte slice whose content equals a static literal returns the CANONICAL
|
|
@@ -112,8 +146,13 @@ export default (ctx) => {
|
|
|
112
146
|
__str_concat_raw: ['__str_byteLen', '__alloc', '__memgrow', '__mkptr', '__str_copy'],
|
|
113
147
|
__str_append_byte: ['__str_byteLen', '__alloc', '__memgrow', '__mkptr', '__str_copy'],
|
|
114
148
|
__str_copy: [],
|
|
115
|
-
__str_slice:
|
|
116
|
-
|
|
149
|
+
// __str_slice/_view are FUNCTION templates: resolveIncludes' auto-dep scan realizes the
|
|
150
|
+
// factory (v()) to discover body calls, but that realization DIVERGES under self-host
|
|
151
|
+
// (jz.wasm) — so a body-called helper not also listed here is dropped from the kernel
|
|
152
|
+
// ("Unknown func $__clamp_idx" on `str.slice`). FN templates must declare body deps
|
|
153
|
+
// manually; pinned by test/selfhost-includes.js.
|
|
154
|
+
__str_slice: ['__str_byteLen', '__alloc', '__clamp_idx', '__mkptr'],
|
|
155
|
+
__str_slice_view: ['__str_byteLen', '__mkptr', '__str_slice', '__clamp_idx'],
|
|
117
156
|
__str_indexof: ['__str_byteLen'],
|
|
118
157
|
__str_lastindexof: ['__str_byteLen'],
|
|
119
158
|
__wrap1: ['__alloc', '__mkptr'],
|
|
@@ -128,15 +167,15 @@ export default (ctx) => {
|
|
|
128
167
|
__str_replace: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
129
168
|
__str_replaceall: ['__str_indexof', '__str_slice', '__str_concat'],
|
|
130
169
|
__str_split: ['__str_slice', '__str_byteLen', '__char_at', '__alloc'],
|
|
131
|
-
__str_idx: [],
|
|
170
|
+
__str_idx: ['__char1byte'],
|
|
132
171
|
__str_eq: ['__str_eq_cold'],
|
|
133
172
|
__str_eq_cold: ['__char_at', '__str_byteLen'],
|
|
134
173
|
__str_cmp: ['__char_at', '__str_byteLen'],
|
|
135
174
|
__str_range_eq: ['__char_at', '__str_byteLen'],
|
|
136
175
|
__str_substring_eq: ['__str_byteLen', '__str_range_eq'],
|
|
137
|
-
__str_slice_eq: ['__str_byteLen', '__str_range_eq'],
|
|
176
|
+
__str_slice_eq: ['__str_byteLen', '__str_range_eq', '__clamp_idx'], // body-calls __clamp_idx; declare it (self-host auto-scan unreliable — test/selfhost-includes.js)
|
|
138
177
|
__str_pad: ['__str_byteLen', '__str_copy', '__alloc'],
|
|
139
|
-
__str_join: ['__str_concat', '__to_str', '__str_byteLen', '__len', '__ptr_offset'],
|
|
178
|
+
__str_join: ['__str_concat', '__to_str', '__str_byteLen', '__len', '__ptr_offset', '__mkptr'], // FN template: __mkptr body-called, must be manual (self-host auto-scan diverges)
|
|
140
179
|
__str_encode: ['__str_byteLen', '__str_copy'],
|
|
141
180
|
__encodeURIComponent: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr'],
|
|
142
181
|
__decodeURIComponent: ['__to_str', '__str_byteLen', '__char_at', '__alloc', '__mkptr', '__uri_hex'],
|
|
@@ -162,11 +201,9 @@ export default (ctx) => {
|
|
|
162
201
|
// === String literal: "abc" → SSO if ≤4 ASCII, else static data ===
|
|
163
202
|
|
|
164
203
|
bind('str', (str) => {
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
for (let i = 0; i < str.length; i++) packed |= str.charCodeAt(i) << (i * 8)
|
|
169
|
-
return mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT | str.length, packed)
|
|
204
|
+
if (ctx.features.sso) {
|
|
205
|
+
const sso = ssoEncode(str)
|
|
206
|
+
if (sso) return mkPtrIR(PTR.STRING, sso.aux, sso.offset)
|
|
170
207
|
}
|
|
171
208
|
const bytes = new TextEncoder().encode(str)
|
|
172
209
|
const len = bytes.length
|
|
@@ -223,11 +260,7 @@ export default (ctx) => {
|
|
|
223
260
|
// SSO/STRING ptrs never have forwarding pointers (only ARRAY does), so we extract
|
|
224
261
|
// the raw offset directly instead of paying the __ptr_offset function-call overhead.
|
|
225
262
|
wat('__sso_char', `(func $__sso_char (param $ptr i64) (param $i i32) (result i32)
|
|
226
|
-
(
|
|
227
|
-
(i32.shr_u
|
|
228
|
-
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
229
|
-
(i32.mul (local.get $i) (i32.const 8)))
|
|
230
|
-
(i32.const 0xFF)))`)
|
|
263
|
+
${ssoCharWat('(local.get $ptr)', '(local.get $i)')})`)
|
|
231
264
|
|
|
232
265
|
wat('__str_char', `(func $__str_char (param $ptr i64) (param $i i32) (result i32)
|
|
233
266
|
(i32.load8_u (i32.add
|
|
@@ -254,16 +287,9 @@ export default (ctx) => {
|
|
|
254
287
|
(i64.ne (i64.and (local.get $ptr) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
255
288
|
(then
|
|
256
289
|
(if (result i32)
|
|
257
|
-
(i32.ge_u (local.get $i)
|
|
258
|
-
(i32.and
|
|
259
|
-
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
260
|
-
(i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
290
|
+
(i32.ge_u (local.get $i) ${ssoLenWat('(local.get $ptr)')})
|
|
261
291
|
(then (i32.const 0))
|
|
262
|
-
(else (
|
|
263
|
-
(i32.shr_u
|
|
264
|
-
(i32.wrap_i64 (i64.and (local.get $ptr) (i64.const ${LAYOUT.OFFSET_MASK})))
|
|
265
|
-
(i32.mul (local.get $i) (i32.const 8)))
|
|
266
|
-
(i32.const 0xFF)))))
|
|
292
|
+
(else ${ssoCharWat('(local.get $ptr)', '(local.get $i)')})))
|
|
267
293
|
(else
|
|
268
294
|
(if (result i32)
|
|
269
295
|
(i32.ge_u (local.get $i)
|
|
@@ -301,9 +327,7 @@ export default (ctx) => {
|
|
|
301
327
|
(i32.const ${LAYOUT.SSO_BIT})))
|
|
302
328
|
(local.set $len
|
|
303
329
|
(if (result i32) (local.get $isSso)
|
|
304
|
-
(then (
|
|
305
|
-
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
306
|
-
(i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
330
|
+
(then ${ssoLenWat('(local.get $ptr)')})
|
|
307
331
|
(else
|
|
308
332
|
(if (result i32)
|
|
309
333
|
(i64.ne (i64.and (local.get $ptr) (i64.const ${SLICE_BIT_I64})) (i64.const 0))
|
|
@@ -318,14 +342,15 @@ export default (ctx) => {
|
|
|
318
342
|
(i32.or (i32.lt_s (local.get $i) (i32.const 0)) (i32.ge_u (local.get $i) (local.get $len)))
|
|
319
343
|
(then (f64.const nan:${UNDEF_NAN}))
|
|
320
344
|
(else
|
|
321
|
-
(f64.
|
|
322
|
-
(
|
|
323
|
-
|
|
324
|
-
(i64.
|
|
325
|
-
|
|
326
|
-
(
|
|
327
|
-
|
|
328
|
-
|
|
345
|
+
(if (result f64) (local.get $isSso)
|
|
346
|
+
;; SSO source: chars are ASCII (<0x80) → inline 1-char SSO (hot path, no branch).
|
|
347
|
+
(then (f64.reinterpret_i64
|
|
348
|
+
(i64.or
|
|
349
|
+
(i64.const ${ptrNanHex(PTR.STRING, ssoAux(1))})
|
|
350
|
+
(i64.extend_i32_u ${ssoCharWat('(local.get $ptr)', '(local.get $i)')}))))
|
|
351
|
+
;; Heap source: the byte may be ≥0x80 (non-ASCII), which can't be a 7-bit SSO,
|
|
352
|
+
;; so __char1byte routes those to a heap 1-byte string.
|
|
353
|
+
(else (call $__char1byte (i32.load8_u (i32.add (local.get $off) (local.get $i)))))))))`)
|
|
329
354
|
|
|
330
355
|
// Hot: ~53M calls in watr self-host. Bit-eq covers identity. SSO/SSO with !bit-eq
|
|
331
356
|
// guarantees content differs (high 32 bits encode type+len; both equal → low 32 differs
|
|
@@ -372,9 +397,12 @@ export default (ctx) => {
|
|
|
372
397
|
(local $ta i32) (local $tb i32)
|
|
373
398
|
(local $offA i32) (local $offB i32)
|
|
374
399
|
(local $ssoA i32) (local $ssoB i32)
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
400
|
+
;; Sole caller is __str_eq, which already returned for bit-equal pointers, both-SSO
|
|
401
|
+
;; (bit-ne ⇒ content-ne), both-canonical-interned (bit-ne ⇒ content-ne) and both-plain-
|
|
402
|
+
;; heap length mismatch. Re-testing them here is dead work on every cold call — skip to
|
|
403
|
+
;; the byte walk. (__str_eq/__str_eq_cold are ~14% of self-host runtime; this trims the
|
|
404
|
+
;; per-call fixed cost.) The heap/mixed paths below still decide every case correctly
|
|
405
|
+
;; on their own, so this stays correct even if a future caller skips the prelude.
|
|
378
406
|
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $a) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
379
407
|
(local.set $tb (i32.wrap_i64 (i64.and (i64.shr_u (local.get $b) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
380
408
|
(local.set $offA (i32.wrap_i64 (i64.and (local.get $a) (i64.const ${LAYOUT.OFFSET_MASK}))))
|
|
@@ -385,22 +413,6 @@ export default (ctx) => {
|
|
|
385
413
|
(local.set $ssoB (i32.and
|
|
386
414
|
(i32.wrap_i64 (i64.shr_u (local.get $b) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
387
415
|
(i32.const ${LAYOUT.SSO_BIT})))
|
|
388
|
-
;; Both SSO with !bit-eq ⇒ content differs (high 32 bits hold tag+aux; both equal here).
|
|
389
|
-
(if (i32.and (local.get $ssoA) (local.get $ssoB))
|
|
390
|
-
(then (return (i32.const 0))))
|
|
391
|
-
;; Both CANONICAL interned heap strings (STR_INTERN_BIT, SSO/SLICE clear):
|
|
392
|
-
;; canonicals are deduped, so bit-ne ⇒ content-ne — the V8 pointer-compare
|
|
393
|
-
;; equivalent. This is the dominant unequal case in tag-dispatch chains
|
|
394
|
-
;; (op === 'literal' ladders over static literals).
|
|
395
|
-
(local.set $axA (i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT}))))
|
|
396
|
-
(local.set $axB (i32.wrap_i64 (i64.shr_u (local.get $b) (i64.const ${LAYOUT.AUX_SHIFT}))))
|
|
397
|
-
(if (i32.and
|
|
398
|
-
(i32.and (i32.eq (local.get $ta) (i32.const ${PTR.STRING}))
|
|
399
|
-
(i32.eq (local.get $tb) (i32.const ${PTR.STRING})))
|
|
400
|
-
(i32.and
|
|
401
|
-
(i32.eq (i32.and (local.get $axA) (i32.const ${LAYOUT.SSO_BIT | LAYOUT.SLICE_BIT | STR_INTERN_BIT})) (i32.const ${STR_INTERN_BIT}))
|
|
402
|
-
(i32.eq (i32.and (local.get $axB) (i32.const ${LAYOUT.SSO_BIT | LAYOUT.SLICE_BIT | STR_INTERN_BIT})) (i32.const ${STR_INTERN_BIT}))))
|
|
403
|
-
(then (return (i32.const 0))))
|
|
404
416
|
;; Both heap STRING fast path: inline len from header. Chunk by 4 bytes via unaligned
|
|
405
417
|
;; i32.load (wasm guarantees unaligned-OK), then byte-tail. Most string comparisons fail
|
|
406
418
|
;; early on the first 4-byte word, so this collapses the per-byte branch overhead into a
|
|
@@ -489,7 +501,7 @@ export default (ctx) => {
|
|
|
489
501
|
(i32.wrap_i64 (i64.shr_u (local.get $ptr) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
490
502
|
(i32.const ${LAYOUT.AUX_MASK})))
|
|
491
503
|
(if (result i32) (i32.and (local.get $aux) (i32.const ${LAYOUT.SSO_BIT}))
|
|
492
|
-
(then (
|
|
504
|
+
(then ${ssoLenFromAux('(local.get $aux)')})
|
|
493
505
|
(else
|
|
494
506
|
(if (result i32) (i32.and (local.get $aux) (i32.const ${LAYOUT.SLICE_BIT}))
|
|
495
507
|
;; view: length lives in aux[12:0], not a header.
|
|
@@ -507,21 +519,11 @@ export default (ctx) => {
|
|
|
507
519
|
// (single bulk op instead of nlen × __char_at).
|
|
508
520
|
wat('__str_slice', () => `(func $__str_slice (param $ptr i64) (param $start i32) (param $end i32) (result f64)
|
|
509
521
|
(local $len i32) (local $nlen i32) (local $off i32) (local $i i32)
|
|
510
|
-
(local $srcOff i32) (local $isSso i32) (local $sb i32) (local $sp i32)
|
|
522
|
+
(local $srcOff i32) (local $isSso i32) (local $sb i32) (local $sp i32) (local $sp64 i64)
|
|
511
523
|
(local $ip i32) (local $h i32) (local $j i32) (local $slot i32) (local $cand i32) (local $k i32)
|
|
512
524
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
513
|
-
(
|
|
514
|
-
|
|
515
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
516
|
-
(then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
517
|
-
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
518
|
-
(then (local.set $start (i32.const 0))))
|
|
519
|
-
(if (i32.gt_s (local.get $start) (local.get $len))
|
|
520
|
-
(then (local.set $start (local.get $len))))
|
|
521
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
522
|
-
(then (local.set $end (i32.const 0))))
|
|
523
|
-
(if (i32.gt_s (local.get $end) (local.get $len))
|
|
524
|
-
(then (local.set $end (local.get $len))))
|
|
525
|
+
(local.set $start (call $__clamp_idx (local.get $start) (local.get $len)))
|
|
526
|
+
(local.set $end (call $__clamp_idx (local.get $end) (local.get $len)))
|
|
525
527
|
(if (i32.ge_s (local.get $start) (local.get $end))
|
|
526
528
|
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
527
529
|
(local.set $nlen (i32.sub (local.get $end) (local.get $start)))
|
|
@@ -560,18 +562,8 @@ export default (ctx) => {
|
|
|
560
562
|
(local $len i32) (local $nlen i32) (local $srcOff i32) (local $tag i32)
|
|
561
563
|
(local $ip i32) (local $h i32) (local $j i32) (local $slot i32) (local $cand i32) (local $k i32)
|
|
562
564
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
563
|
-
(
|
|
564
|
-
|
|
565
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
566
|
-
(then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
567
|
-
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
568
|
-
(then (local.set $start (i32.const 0))))
|
|
569
|
-
(if (i32.gt_s (local.get $start) (local.get $len))
|
|
570
|
-
(then (local.set $start (local.get $len))))
|
|
571
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
572
|
-
(then (local.set $end (i32.const 0))))
|
|
573
|
-
(if (i32.gt_s (local.get $end) (local.get $len))
|
|
574
|
-
(then (local.set $end (local.get $len))))
|
|
565
|
+
(local.set $start (call $__clamp_idx (local.get $start) (local.get $len)))
|
|
566
|
+
(local.set $end (call $__clamp_idx (local.get $end) (local.get $len)))
|
|
575
567
|
(if (i32.ge_s (local.get $start) (local.get $end))
|
|
576
568
|
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${LAYOUT.SSO_BIT}) (i32.const 0)))))
|
|
577
569
|
(local.set $nlen (i32.sub (local.get $end) (local.get $start)))
|
|
@@ -676,18 +668,8 @@ export default (ctx) => {
|
|
|
676
668
|
wat('__str_slice_eq', `(func $__str_slice_eq (param $ptr i64) (param $start i32) (param $end i32) (param $other i64) (result i32)
|
|
677
669
|
(local $len i32)
|
|
678
670
|
(local.set $len (call $__str_byteLen (local.get $ptr)))
|
|
679
|
-
(
|
|
680
|
-
|
|
681
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
682
|
-
(then (local.set $end (i32.add (local.get $len) (local.get $end)))))
|
|
683
|
-
(if (i32.lt_s (local.get $start) (i32.const 0))
|
|
684
|
-
(then (local.set $start (i32.const 0))))
|
|
685
|
-
(if (i32.gt_s (local.get $start) (local.get $len))
|
|
686
|
-
(then (local.set $start (local.get $len))))
|
|
687
|
-
(if (i32.lt_s (local.get $end) (i32.const 0))
|
|
688
|
-
(then (local.set $end (i32.const 0))))
|
|
689
|
-
(if (i32.gt_s (local.get $end) (local.get $len))
|
|
690
|
-
(then (local.set $end (local.get $len))))
|
|
671
|
+
(local.set $start (call $__clamp_idx (local.get $start) (local.get $len)))
|
|
672
|
+
(local.set $end (call $__clamp_idx (local.get $end) (local.get $len)))
|
|
691
673
|
(call $__str_range_eq (local.get $ptr) (local.get $start) (local.get $end) (local.get $other)))`)
|
|
692
674
|
|
|
693
675
|
// Hoist SSO/heap dispatch for hay and ndl out of the inner byte loop. Inner
|
|
@@ -719,10 +701,10 @@ export default (ctx) => {
|
|
|
719
701
|
(br_if $nomatch (i32.ge_s (local.get $j) (local.get $nlen)))
|
|
720
702
|
(local.set $k (i32.add (local.get $i) (local.get $j)))
|
|
721
703
|
(local.set $hb (if (result i32) (local.get $hsso)
|
|
722
|
-
(then (
|
|
704
|
+
(then ${ssoCharWat('(local.get $hay)', '(local.get $k)')})
|
|
723
705
|
(else (i32.load8_u (i32.add (local.get $hoff) (local.get $k))))))
|
|
724
706
|
(local.set $nb (if (result i32) (local.get $nsso)
|
|
725
|
-
(then (
|
|
707
|
+
(then ${ssoCharWat('(local.get $ndl)', '(local.get $j)')})
|
|
726
708
|
(else (i32.load8_u (i32.add (local.get $noff) (local.get $j))))))
|
|
727
709
|
(if (i32.ne (local.get $hb) (local.get $nb))
|
|
728
710
|
(then (local.set $match (i32.const 0)) (br $nomatch)))
|
|
@@ -771,10 +753,10 @@ export default (ctx) => {
|
|
|
771
753
|
(br_if $nomatch (i32.ge_s (local.get $j) (local.get $nlen)))
|
|
772
754
|
(local.set $k (i32.add (local.get $i) (local.get $j)))
|
|
773
755
|
(local.set $hb (if (result i32) (local.get $hsso)
|
|
774
|
-
(then (
|
|
756
|
+
(then ${ssoCharWat('(local.get $hay)', '(local.get $k)')})
|
|
775
757
|
(else (i32.load8_u (i32.add (local.get $hoff) (local.get $k))))))
|
|
776
758
|
(local.set $nb (if (result i32) (local.get $nsso)
|
|
777
|
-
(then (
|
|
759
|
+
(then ${ssoCharWat('(local.get $ndl)', '(local.get $j)')})
|
|
778
760
|
(else (i32.load8_u (i32.add (local.get $noff) (local.get $j))))))
|
|
779
761
|
(if (i32.ne (local.get $hb) (local.get $nb))
|
|
780
762
|
(then (local.set $match (i32.const 0)) (br $nomatch)))
|
|
@@ -804,10 +786,10 @@ export default (ctx) => {
|
|
|
804
786
|
(br_if $done (i32.ge_s (local.get $i) (local.get $plen)))
|
|
805
787
|
(if (i32.ne
|
|
806
788
|
(if (result i32) (local.get $ssso)
|
|
807
|
-
(then (
|
|
789
|
+
(then ${ssoCharWat('(local.get $str)', '(local.get $i)')})
|
|
808
790
|
(else (i32.load8_u (i32.add (local.get $soff) (local.get $i)))))
|
|
809
791
|
(if (result i32) (local.get $psso)
|
|
810
|
-
(then (
|
|
792
|
+
(then ${ssoCharWat('(local.get $pfx)', '(local.get $i)')})
|
|
811
793
|
(else (i32.load8_u (i32.add (local.get $poff) (local.get $i))))))
|
|
812
794
|
(then (return (i32.const 0))))
|
|
813
795
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
@@ -835,10 +817,10 @@ export default (ctx) => {
|
|
|
835
817
|
(local.set $k (i32.add (local.get $off) (local.get $i)))
|
|
836
818
|
(if (i32.ne
|
|
837
819
|
(if (result i32) (local.get $ssso)
|
|
838
|
-
(then (
|
|
820
|
+
(then ${ssoCharWat('(local.get $str)', '(local.get $k)')})
|
|
839
821
|
(else (i32.load8_u (i32.add (local.get $soff) (local.get $k)))))
|
|
840
822
|
(if (result i32) (local.get $fsso)
|
|
841
|
-
(then (
|
|
823
|
+
(then ${ssoCharWat('(local.get $sfx)', '(local.get $i)')})
|
|
842
824
|
(else (i32.load8_u (i32.add (local.get $foff) (local.get $i))))))
|
|
843
825
|
(then (return (i32.const 0))))
|
|
844
826
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
@@ -860,9 +842,7 @@ export default (ctx) => {
|
|
|
860
842
|
(then
|
|
861
843
|
(block $dsso (loop $lsso
|
|
862
844
|
(br_if $dsso (i32.ge_s (local.get $i) (local.get $len)))
|
|
863
|
-
(local.set $c (
|
|
864
|
-
(i32.shr_u (local.get $srcOff) (i32.shl (local.get $i) (i32.const 3)))
|
|
865
|
-
(i32.const 0xFF)))
|
|
845
|
+
(local.set $c ${ssoCharWat('(local.get $ptr)', '(local.get $i)')})
|
|
866
846
|
(if (i32.and (i32.ge_u (local.get $c) (local.get $lo)) (i32.le_u (local.get $c) (local.get $hi)))
|
|
867
847
|
(then (local.set $c (i32.add (local.get $c) (local.get $delta)))))
|
|
868
848
|
(i32.store8 (i32.add (local.get $off) (local.get $i)) (local.get $c))
|
|
@@ -964,7 +944,7 @@ export default (ctx) => {
|
|
|
964
944
|
;; Array (type=1) → join(",") like JS Array.toString()
|
|
965
945
|
(if (i32.eq (local.get $type) (i32.const ${PTR.ARRAY}))
|
|
966
946
|
(then (return (i64.reinterpret_f64 (call $__str_join (local.get $val)
|
|
967
|
-
(i64.reinterpret_f64 (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${
|
|
947
|
+
(i64.reinterpret_f64 (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${ssoAux(1)}) (i32.const 44))))))))
|
|
968
948
|
(local.get $val))`)
|
|
969
949
|
|
|
970
950
|
// Copy bytes of a string (SSO or heap) into memory at dst. Uses memory.copy for
|
|
@@ -973,18 +953,15 @@ export default (ctx) => {
|
|
|
973
953
|
(local $w i32)
|
|
974
954
|
(if (i64.ne (i64.and (local.get $src) (i64.const ${SSO_BIT_I64})) (i64.const 0))
|
|
975
955
|
(then
|
|
976
|
-
;; SSO:
|
|
977
|
-
;;
|
|
978
|
-
(local.set $w (i32.
|
|
979
|
-
(
|
|
980
|
-
(
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
(i32.store8 offset=1 (local.get $dst) (i32.shr_u (local.get $w) (i32.const 8)))
|
|
986
|
-
(if (i32.eq (local.get $len) (i32.const 2)) (then (return)))
|
|
987
|
-
(i32.store8 offset=2 (local.get $dst) (i32.shr_u (local.get $w) (i32.const 16))))))
|
|
956
|
+
;; SSO: write $len ASCII chars, each 7-bit-packed at payload bit i*7 (chars 4-5
|
|
957
|
+
;; span into aux, so read via the 7-bit codec, not the low 32 bits). $w = index.
|
|
958
|
+
(local.set $w (i32.const 0))
|
|
959
|
+
(loop $ssocp
|
|
960
|
+
(if (i32.lt_u (local.get $w) (local.get $len))
|
|
961
|
+
(then
|
|
962
|
+
(i32.store8 (i32.add (local.get $dst) (local.get $w)) ${ssoCharWat('(local.get $src)', '(local.get $w)')})
|
|
963
|
+
(local.set $w (i32.add (local.get $w) (i32.const 1)))
|
|
964
|
+
(br $ssocp)))))
|
|
988
965
|
(else
|
|
989
966
|
;; Heap STRING: memory.copy directly from string data
|
|
990
967
|
(memory.copy (local.get $dst)
|
|
@@ -1016,12 +993,12 @@ export default (ctx) => {
|
|
|
1016
993
|
(then
|
|
1017
994
|
(return (call $__mkptr
|
|
1018
995
|
(i32.const ${PTR.STRING})
|
|
1019
|
-
(i32.or (i32.const ${LAYOUT.SSO_BIT}) (local.get $total))
|
|
996
|
+
(i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.shl (local.get $total) (i32.const ${SSO_LEN_SHIFT})))
|
|
1020
997
|
(i32.or
|
|
1021
998
|
(i32.wrap_i64 (i64.and (local.get $a) (i64.const 0xFFFFFFFF)))
|
|
1022
999
|
(i32.shl
|
|
1023
1000
|
(i32.wrap_i64 (i64.and (local.get $b) (i64.const 0xFFFFFFFF)))
|
|
1024
|
-
(i32.
|
|
1001
|
+
(i32.mul (local.get $alen) (i32.const 7))))))))`
|
|
1025
1002
|
|
|
1026
1003
|
const concatFast = !ctx.memory.shared && ctx.transform.alloc !== false ? `
|
|
1027
1004
|
(local.set $ta (i32.wrap_i64 (i64.and (i64.shr_u (local.get $a) (i64.const ${LAYOUT.TAG_SHIFT})) (i64.const ${LAYOUT.TAG_MASK}))))
|
|
@@ -1086,19 +1063,17 @@ export default (ctx) => {
|
|
|
1086
1063
|
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
1087
1064
|
(i32.const ${LAYOUT.SSO_BIT})))
|
|
1088
1065
|
(then
|
|
1089
|
-
(local.set $alen (
|
|
1090
|
-
(i32.wrap_i64 (i64.shr_u (local.get $a) (i64.const ${LAYOUT.AUX_SHIFT})))
|
|
1091
|
-
(i32.const ${LAYOUT.SSO_BIT - 1})))
|
|
1066
|
+
(local.set $alen ${ssoLenWat('(local.get $a)')})
|
|
1092
1067
|
(if (i32.and
|
|
1093
1068
|
(i32.lt_u (local.get $alen) (i32.const 4))
|
|
1094
1069
|
(i32.lt_u (local.get $byte) (i32.const 0x80)))
|
|
1095
1070
|
(then
|
|
1096
1071
|
(return (call $__mkptr
|
|
1097
1072
|
(i32.const ${PTR.STRING})
|
|
1098
|
-
(i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.add (local.get $alen) (i32.const 1)))
|
|
1073
|
+
(i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.shl (i32.add (local.get $alen) (i32.const 1)) (i32.const ${SSO_LEN_SHIFT})))
|
|
1099
1074
|
(i32.or
|
|
1100
1075
|
(local.get $aoff)
|
|
1101
|
-
(i32.shl (local.get $byte) (i32.
|
|
1076
|
+
(i32.shl (local.get $byte) (i32.mul (local.get $alen) (i32.const 7))))))))))
|
|
1102
1077
|
;; Slow path: allocate new heap STRING with original bytes + 1 new byte
|
|
1103
1078
|
(local.set $alen (call $__str_byteLen (local.get $a)))
|
|
1104
1079
|
(local.set $total (i32.add (local.get $alen) (i32.const 1)))
|
|
@@ -1379,11 +1354,11 @@ export default (ctx) => {
|
|
|
1379
1354
|
(then
|
|
1380
1355
|
(local.set $sb (i32.load8_u (i32.add (local.get $off) (local.get $i))))
|
|
1381
1356
|
(br_if $heap (i32.ge_u (local.get $sb) (i32.const 0x80)))
|
|
1382
|
-
(local.set $sp (i32.or (local.get $sp) (i32.shl (local.get $sb) (i32.
|
|
1357
|
+
(local.set $sp (i32.or (local.get $sp) (i32.shl (local.get $sb) (i32.mul (local.get $i) (i32.const 7)))))
|
|
1383
1358
|
(local.set $i (i32.add (local.get $i) (i32.const 1)))
|
|
1384
1359
|
(br $pk))))
|
|
1385
1360
|
(global.set $__heap (i32.sub (local.get $off) (i32.const 4)))
|
|
1386
|
-
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.or (i32.const ${LAYOUT.SSO_BIT}) (local.get $target)) (local.get $sp))))))` : ''}
|
|
1361
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.or (i32.const ${LAYOUT.SSO_BIT}) (i32.shl (local.get $target) (i32.const 10))) (local.get $sp))))))` : ''}
|
|
1387
1362
|
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (local.get $off)))`)
|
|
1388
1363
|
|
|
1389
1364
|
// Base helpers (__sso_char/__str_char/__char_at/__str_byteLen) are referenced
|
|
@@ -1621,7 +1596,7 @@ export default (ctx) => {
|
|
|
1621
1596
|
|
|
1622
1597
|
const padMethod = (start) => (str, len, pad) => {
|
|
1623
1598
|
inc('__str_pad')
|
|
1624
|
-
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING,
|
|
1599
|
+
const vpad = pad != null ? asI64(emit(pad)) : ['i64.reinterpret_f64', mkPtrIR(PTR.STRING, ssoAux(1), 32)]
|
|
1625
1600
|
return typed(['call', '$__str_pad', asI64(emit(str)), asI32(emit(len)), vpad, ['i32.const', start]], 'f64')
|
|
1626
1601
|
}
|
|
1627
1602
|
bind('.padStart', padMethod(1))
|
|
@@ -1688,14 +1663,17 @@ export default (ctx) => {
|
|
|
1688
1663
|
// index; otherwise `oobIR`. Without the bounds check `__char_at` returns 0 for an
|
|
1689
1664
|
// out-of-range index, which would wrap to a bogus `"\x00"` string (charAt → "",
|
|
1690
1665
|
// at → undefined). `sLocal` is the receiver as an f64 local.
|
|
1691
|
-
const charAtOr = (sLocal, iLocal, lenIR, oobIR) =>
|
|
1666
|
+
const charAtOr = (sLocal, iLocal, lenIR, oobIR) => (
|
|
1667
|
+
inc('__char1byte'),
|
|
1692
1668
|
['if', ['result', 'f64'],
|
|
1693
1669
|
['i32.and',
|
|
1694
1670
|
['i32.ge_s', ['local.get', iLocal], ['i32.const', 0]],
|
|
1695
1671
|
['i32.lt_s', ['local.get', iLocal], lenIR]],
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
['
|
|
1672
|
+
// __char_at yields a raw byte (≥0x80 for non-ASCII heap strings) → __char1byte
|
|
1673
|
+
// builds an SSO byte when ASCII, else a heap 1-byte string.
|
|
1674
|
+
['then', ['call', '$__char1byte',
|
|
1675
|
+
['call', '$__char_at', ['i64.reinterpret_f64', ['local.get', sLocal]], ['local.get', iLocal]]]],
|
|
1676
|
+
['else', oobIR]])
|
|
1699
1677
|
const emptyStr = mkPtrIR(PTR.STRING, LAYOUT.SSO_BIT, 0)
|
|
1700
1678
|
|
|
1701
1679
|
// .charAt(i) → 1-char string at index i, or "" when out of range (JS spec: no
|
|
@@ -1742,13 +1720,27 @@ export default (ctx) => {
|
|
|
1742
1720
|
return typed(['f64.reinterpret_i64', toStrI64(value, emit(value))], 'f64')
|
|
1743
1721
|
})
|
|
1744
1722
|
|
|
1723
|
+
// 1-byte string from a raw byte: SSO when ASCII (<0x80, fits the 7-bit codec),
|
|
1724
|
+
// else a heap 1-byte string (the 7-bit SSO can't represent bytes ≥0x80).
|
|
1725
|
+
wat('__char1byte', `(func $__char1byte (param $b i32) (result f64)
|
|
1726
|
+
(local $off i32)
|
|
1727
|
+
(local.set $b (i32.and (local.get $b) (i32.const 0xFF)))
|
|
1728
|
+
(if (result f64) (i32.lt_u (local.get $b) (i32.const 0x80))
|
|
1729
|
+
(then (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${ssoAux(1)}) (local.get $b)))
|
|
1730
|
+
(else
|
|
1731
|
+
(local.set $off (call $__alloc (i32.const 8)))
|
|
1732
|
+
(i32.store (local.get $off) (i32.const 1))
|
|
1733
|
+
(i32.store8 (i32.add (local.get $off) (i32.const 4)) (local.get $b))
|
|
1734
|
+
(call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (i32.add (local.get $off) (i32.const 4))))))`)
|
|
1735
|
+
|
|
1745
1736
|
// String.fromCharCode(...codes) — variadic; each arg is ToUint16(ToNumber(code))
|
|
1746
1737
|
// → a 1-byte string, concatenated left to right (mirrors String.fromCodePoint).
|
|
1747
1738
|
bind('String.fromCharCode', (...codes) => {
|
|
1748
1739
|
if (codes.length === 0) return emit(['str', ''])
|
|
1749
1740
|
// ToUint16(ToNumber(code)): `toNumF64` performs ToPrimitive on an object
|
|
1750
|
-
// argument, so a throwing valueOf/toString propagates per spec.
|
|
1751
|
-
|
|
1741
|
+
// argument, so a throwing valueOf/toString propagates per spec. A byte ≥0x80
|
|
1742
|
+
// can't be a 7-bit-ASCII SSO, so __char1byte routes it to a heap 1-byte string.
|
|
1743
|
+
const one = (node) => { inc('__char1byte'); return typed(['call', '$__char1byte', asI32(toNumF64(node, emit(node)))], 'f64') }
|
|
1752
1744
|
let r = one(codes[0])
|
|
1753
1745
|
for (let i = 1; i < codes.length; i++) {
|
|
1754
1746
|
inc('__str_concat_raw')
|
|
@@ -1771,27 +1763,41 @@ export default (ctx) => {
|
|
|
1771
1763
|
(local.set $cp (i32.trunc_sat_f64_s (local.get $cpf)))
|
|
1772
1764
|
;; ASCII: 1 byte SSO
|
|
1773
1765
|
(if (i32.lt_u (local.get $cp) (i32.const 128))
|
|
1774
|
-
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${
|
|
1775
|
-
;;
|
|
1766
|
+
(then (return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const ${ssoAux(1)}) (local.get $cp)))))
|
|
1767
|
+
;; multi-byte (cp ≥ 0x80): UTF-8 bytes are ≥0x80 → can't be 7-bit ASCII SSO, so
|
|
1768
|
+
;; build a heap STRING (len header at $off, bytes at $off+4). Over-alloc to 8 so the
|
|
1769
|
+
;; i32.store of the packed bytes never runs past the buffer; the len word bounds reads.
|
|
1770
|
+
;; 2-byte: 0x80-0x7FF
|
|
1776
1771
|
(if (i32.lt_u (local.get $cp) (i32.const 0x800))
|
|
1777
|
-
(then
|
|
1778
|
-
(i32.
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1772
|
+
(then
|
|
1773
|
+
(local.set $off (call $__alloc (i32.const 8)))
|
|
1774
|
+
(i32.store (local.get $off) (i32.const 2))
|
|
1775
|
+
(i32.store (i32.add (local.get $off) (i32.const 4))
|
|
1776
|
+
(i32.or
|
|
1777
|
+
(i32.or (i32.const 0xC0) (i32.shr_u (local.get $cp) (i32.const 6)))
|
|
1778
|
+
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 8))))
|
|
1779
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (i32.add (local.get $off) (i32.const 4))))))
|
|
1780
|
+
;; 3-byte: 0x800-0xFFFF
|
|
1782
1781
|
(if (i32.lt_u (local.get $cp) (i32.const 0x10000))
|
|
1783
|
-
(then
|
|
1784
|
-
(
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
(i32.
|
|
1788
|
-
|
|
1789
|
-
|
|
1782
|
+
(then
|
|
1783
|
+
(local.set $off (call $__alloc (i32.const 8)))
|
|
1784
|
+
(i32.store (local.get $off) (i32.const 3))
|
|
1785
|
+
(i32.store (i32.add (local.get $off) (i32.const 4))
|
|
1786
|
+
(i32.or (i32.or
|
|
1787
|
+
(i32.or (i32.const 0xE0) (i32.shr_u (local.get $cp) (i32.const 12)))
|
|
1788
|
+
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))) (i32.const 8)))
|
|
1789
|
+
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 16))))
|
|
1790
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (i32.add (local.get $off) (i32.const 4))))))
|
|
1791
|
+
;; 4-byte: 0x10000-0x10FFFF
|
|
1792
|
+
(local.set $off (call $__alloc (i32.const 8)))
|
|
1793
|
+
(i32.store (local.get $off) (i32.const 4))
|
|
1794
|
+
(i32.store (i32.add (local.get $off) (i32.const 4))
|
|
1790
1795
|
(i32.or (i32.or (i32.or
|
|
1791
1796
|
(i32.or (i32.const 0xF0) (i32.shr_u (local.get $cp) (i32.const 18)))
|
|
1792
1797
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 12)) (i32.const 0x3F))) (i32.const 8)))
|
|
1793
1798
|
(i32.shl (i32.or (i32.const 0x80) (i32.and (i32.shr_u (local.get $cp) (i32.const 6)) (i32.const 0x3F))) (i32.const 16)))
|
|
1794
|
-
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 24))))
|
|
1799
|
+
(i32.shl (i32.or (i32.const 0x80) (i32.and (local.get $cp) (i32.const 0x3F))) (i32.const 24))))
|
|
1800
|
+
(return (call $__mkptr (i32.const ${PTR.STRING}) (i32.const 0) (i32.add (local.get $off) (i32.const 4)))))`)
|
|
1795
1801
|
|
|
1796
1802
|
// String.fromCodePoint(...codePoints) — variadic; each arg is ToNumber-coerced
|
|
1797
1803
|
// then validated/encoded by __fromCodePoint, results concatenated left to right.
|