jz 0.8.0 → 0.9.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 +56 -9
- package/bench/README.md +121 -50
- package/bench/bench.svg +27 -27
- package/cli.js +3 -1
- package/dist/interop.js +1 -1
- package/dist/jz.js +7541 -6178
- package/index.js +165 -74
- package/interop.js +189 -17
- package/jzify/arguments.js +32 -1
- package/jzify/async.js +409 -0
- package/jzify/classes.js +136 -18
- package/jzify/generators.js +639 -0
- package/jzify/hoist-vars.js +73 -1
- package/jzify/index.js +123 -4
- package/jzify/names.js +1 -0
- package/jzify/transform.js +239 -1
- package/layout.js +48 -3
- package/module/array.js +318 -43
- package/module/atomics.js +144 -0
- package/module/collection.js +1429 -155
- package/module/core.js +431 -40
- package/module/date.js +142 -120
- package/module/fs.js +144 -0
- package/module/function.js +6 -3
- package/module/index.js +4 -1
- package/module/json.js +270 -49
- package/module/math.js +1032 -145
- package/module/number.js +532 -163
- package/module/object.js +353 -95
- package/module/regex.js +157 -7
- package/module/schema.js +100 -2
- package/module/string.js +428 -93
- package/module/typedarray.js +264 -72
- package/module/web.js +36 -0
- package/package.json +5 -5
- package/src/abi/string.js +82 -11
- package/src/ast.js +11 -5
- package/src/autoload.js +28 -1
- package/src/bridge.js +7 -2
- package/src/compile/analyze-scans.js +22 -7
- package/src/compile/analyze.js +136 -30
- package/src/compile/dyn-closure-tables.js +277 -0
- package/src/compile/emit-assign.js +281 -15
- package/src/compile/emit.js +1055 -70
- package/src/compile/index.js +277 -29
- package/src/compile/infer.js +42 -4
- package/src/compile/inplace-store.js +329 -0
- package/src/compile/loop-recurrence.js +167 -0
- package/src/compile/narrow.js +555 -18
- package/src/compile/peel-stencil.js +5 -0
- package/src/compile/plan/index.js +45 -3
- package/src/compile/plan/inline.js +8 -1
- package/src/compile/plan/literals.js +39 -0
- package/src/compile/plan/scope.js +430 -23
- package/src/compile/program-facts.js +732 -38
- package/src/ctx.js +64 -9
- package/src/helper-counters.js +7 -1
- package/src/ir.js +113 -5
- package/src/kind-traits.js +66 -3
- package/src/kind.js +84 -12
- package/src/op-policy.js +7 -4
- package/src/optimize/index.js +1179 -704
- package/src/optimize/recurse.js +2 -2
- package/src/optimize/vectorize.js +982 -67
- package/src/prepare/index.js +809 -67
- package/src/prepare/math-kernel.js +331 -0
- package/src/prepare/pre-eval.js +714 -0
- package/src/snapshot.js +194 -0
- package/src/type.js +1170 -56
- package/src/wat/assemble.js +403 -65
- package/src/wat/codegen.js +74 -14
- package/transform.js +113 -4
- package/wasi.js +3 -0
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Atomics — wasm threads (0xfe atomics) mapping for shared-memory SPMD kernels.
|
|
3
|
+
*
|
|
4
|
+
* v1 contract (Workers v1, .work/extension-surface.md): shared TYPED ARRAYS +
|
|
5
|
+
* scalars only. Receivers must be PROVEN Int32Array — the one JS type whose
|
|
6
|
+
* Atomics semantics map 1:1 onto i32.atomic.* (BigInt64Array can join later).
|
|
7
|
+
* Strings/objects/hashes stay thread-local by documented contract.
|
|
8
|
+
*
|
|
9
|
+
* Same source runs as plain JS: host Atomics accepts non-shared Int32Array
|
|
10
|
+
* (ES2024) exactly like these lowerings on non-shared memory; only wait()
|
|
11
|
+
* requires a shared buffer on both sides.
|
|
12
|
+
*
|
|
13
|
+
* @module atomics
|
|
14
|
+
*/
|
|
15
|
+
import { typed, asI64, asI32, toNumF64 } from '../src/ir.js'
|
|
16
|
+
import { valTypeOf } from '../src/kind.js'
|
|
17
|
+
import { emit, deps } from '../src/bridge.js'
|
|
18
|
+
import { inc, err, PTR } from '../src/ctx.js'
|
|
19
|
+
import { VAL } from '../src/reps.js'
|
|
20
|
+
|
|
21
|
+
export default (ctx) => {
|
|
22
|
+
deps({
|
|
23
|
+
__atomics_addr: ['__typed_data', '__len', '__ptr_type', '__ptr_aux'],
|
|
24
|
+
__atomics_addr64: ['__typed_data', '__len', '__ptr_type', '__ptr_aux'],
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Element address of an Int32Array. Guards BEFORE touching memory: the boxed
|
|
28
|
+
// value must actually be an Int32Array (tag TYPED, elem code 4 — the compile
|
|
29
|
+
// proof may come from the default-arg annotation, which a host caller can
|
|
30
|
+
// violate) and the index in range (spec RangeError) — either failure throws.
|
|
31
|
+
ctx.core.stdlib['__atomics_addr'] = `(func $__atomics_addr (param $arr i64) (param $i i32) (result i32)
|
|
32
|
+
(if (i32.or
|
|
33
|
+
(i32.ne (call $__ptr_type (local.get $arr)) (i32.const ${PTR.TYPED}))
|
|
34
|
+
(i32.ne (i32.and (call $__ptr_aux (local.get $arr)) (i32.const 7)) (i32.const 4)))
|
|
35
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
36
|
+
(if (i32.ge_u (local.get $i) (call $__len (local.get $arr)))
|
|
37
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
38
|
+
(i32.add (call $__typed_data (local.get $arr)) (i32.shl (local.get $i) (i32.const 2))))`
|
|
39
|
+
|
|
40
|
+
// BigInt64Array twin: elem code 7 + the BIGINT aux flag (16), stride 8.
|
|
41
|
+
ctx.core.stdlib['__atomics_addr64'] = `(func $__atomics_addr64 (param $arr i64) (param $i i32) (result i32)
|
|
42
|
+
(if (i32.or
|
|
43
|
+
(i32.ne (call $__ptr_type (local.get $arr)) (i32.const ${PTR.TYPED}))
|
|
44
|
+
(i32.or
|
|
45
|
+
(i32.ne (i32.and (call $__ptr_aux (local.get $arr)) (i32.const 7)) (i32.const 7))
|
|
46
|
+
(i32.eqz (i32.and (call $__ptr_aux (local.get $arr)) (i32.const 16)))))
|
|
47
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
48
|
+
(if (i32.ge_u (local.get $i) (call $__len (local.get $arr)))
|
|
49
|
+
(then (throw $__jz_err (f64.const 0))))
|
|
50
|
+
(i32.add (call $__typed_data (local.get $arr)) (i32.shl (local.get $i) (i32.const 3))))`
|
|
51
|
+
|
|
52
|
+
// v1 receiver gate: a name whose element ctor is PROVEN Int32Array, or a
|
|
53
|
+
// direct `new Int32Array(...)` expression. Anything else is a clean reject —
|
|
54
|
+
// an unproven receiver would need runtime elem dispatch the contract excludes.
|
|
55
|
+
const recvWidth = (arr) => {
|
|
56
|
+
let ctor = null
|
|
57
|
+
if (Array.isArray(arr) && arr[0] === 'new' && typeof arr[1] === 'string') ctor = 'new.' + arr[1]
|
|
58
|
+
else if (typeof arr === 'string')
|
|
59
|
+
ctor = ctx.func.localTypedElemsOverlay?.get(arr) ?? ctx.types.typedElem?.get(arr)
|
|
60
|
+
?? ctx.func.localReps?.get(arr)?.typedCtor // narrowed param facts (typed default-arg seed)
|
|
61
|
+
?? ctx.scope?.globalTypedElem?.get(arr)
|
|
62
|
+
if (ctor === 'new.Int32Array' || ctor === 'new.Int32Array.view') return 'i32'
|
|
63
|
+
if (ctor === 'new.BigInt64Array' || ctor === 'new.BigInt64Array.view') return 'i64'
|
|
64
|
+
err(`Atomics: receiver must be a proven Int32Array or BigInt64Array (shared-memory v1 contract) — got ${typeof arr === 'string' ? `'${arr}' (${ctor ?? 'unproven'})` : 'an expression'}`)
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const addr = (arr, i, w) => {
|
|
68
|
+
inc(w === 'i64' ? '__atomics_addr64' : '__atomics_addr')
|
|
69
|
+
ctx.runtime.throws = true
|
|
70
|
+
return ['call', w === 'i64' ? '$__atomics_addr64' : '$__atomics_addr', asI64(emit(arr)), asI32(toNumF64(i, emit(i)))]
|
|
71
|
+
}
|
|
72
|
+
const i32val = (v) => asI32(toNumF64(v, emit(v)))
|
|
73
|
+
// BigInt64 lanes carry raw i64 — the value must be a proven BigInt expression.
|
|
74
|
+
const i64val = (v) => {
|
|
75
|
+
if (valTypeOf(v) !== VAL.BIGINT) err('Atomics on a BigInt64Array takes BigInt values — wrap with BigInt(…)')
|
|
76
|
+
return asI64(emit(v))
|
|
77
|
+
}
|
|
78
|
+
const val = (v, w) => w === 'i64' ? i64val(v) : i32val(v)
|
|
79
|
+
const out = (irF, w) => w === 'i64'
|
|
80
|
+
? typed(['f64.reinterpret_i64', irF], 'f64') // raw BIGINT carrier
|
|
81
|
+
: typed(['f64.convert_i32_s', irF], 'f64')
|
|
82
|
+
|
|
83
|
+
ctx.core.emit['Atomics.load'] = (arr, i) => {
|
|
84
|
+
const w = recvWidth(arr)
|
|
85
|
+
return out([`${w}.atomic.load`, addr(arr, i, w)], w)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// store returns the stored value (spec: the coerced input)
|
|
89
|
+
ctx.core.emit['Atomics.store'] = (arr, i, v) => {
|
|
90
|
+
const w = recvWidth(arr)
|
|
91
|
+
const t = `atst${ctx.func.uniq++}`
|
|
92
|
+
ctx.func.locals.set(t, w)
|
|
93
|
+
return typed(['block', ['result', 'f64'],
|
|
94
|
+
['local.set', `$${t}`, val(v, w)],
|
|
95
|
+
[`${w}.atomic.store`, addr(arr, i, w), ['local.get', `$${t}`]],
|
|
96
|
+
out(['local.get', `$${t}`], w)], 'f64')
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// RMW family — each returns the OLD value.
|
|
100
|
+
for (const [name, op] of [['add', 'add'], ['sub', 'sub'], ['and', 'and'], ['or', 'or'], ['xor', 'xor'], ['exchange', 'xchg']])
|
|
101
|
+
ctx.core.emit[`Atomics.${name}`] = (arr, i, v) => {
|
|
102
|
+
const w = recvWidth(arr)
|
|
103
|
+
return out([`${w}.atomic.rmw.${op}`, addr(arr, i, w), val(v, w)], w)
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
ctx.core.emit['Atomics.compareExchange'] = (arr, i, expected, replacement) => {
|
|
107
|
+
const w = recvWidth(arr)
|
|
108
|
+
return out([`${w}.atomic.rmw.cmpxchg`, addr(arr, i, w), val(expected, w), val(replacement, w)], w)
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// wait(arr, i, value, timeoutMs?) → 'ok' | 'not-equal' | 'timed-out'
|
|
112
|
+
// (static strings 9/10/11). Timeout converts ms → ns; absent/Infinity →
|
|
113
|
+
// -1 (infinite). Only valid off the main thread on a SHARED memory — a
|
|
114
|
+
// non-shared memory traps, the wasm analogue of the host's TypeError.
|
|
115
|
+
ctx.core.emit['Atomics.wait'] = (arr, i, value, timeout) => {
|
|
116
|
+
const w = recvWidth(arr)
|
|
117
|
+
inc('__static_str')
|
|
118
|
+
const tmo = timeout === undefined
|
|
119
|
+
? ['i64.const', -1]
|
|
120
|
+
: ['i64.trunc_sat_f64_s', ['f64.mul', toNumF64(timeout, emit(timeout)), ['f64.const', 1e6]]]
|
|
121
|
+
return typed(['call', '$__static_str',
|
|
122
|
+
['i32.add', ['i32.const', 9],
|
|
123
|
+
[w === 'i64' ? 'memory.atomic.wait64' : 'memory.atomic.wait32', addr(arr, i, w), val(value, w), tmo]]], 'f64')
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// notify(arr, i, count?) → number of woken waiters; count defaults to all.
|
|
127
|
+
ctx.core.emit['Atomics.notify'] = (arr, i, count) => {
|
|
128
|
+
const w = recvWidth(arr)
|
|
129
|
+
return typed(['f64.convert_i32_s',
|
|
130
|
+
['memory.atomic.notify', addr(arr, i, w), count === undefined ? ['i32.const', -1] : i32val(count)]], 'f64')
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// isLockFree(n) — wasm i32 atomics are lock-free at 1/2/4 (and 8 via i64).
|
|
134
|
+
ctx.core.emit['Atomics.isLockFree'] = (n) => {
|
|
135
|
+
const t = `atlf${ctx.func.uniq++}`
|
|
136
|
+
ctx.func.locals.set(t, 'i32')
|
|
137
|
+
const v = () => ['local.get', `$${t}`]
|
|
138
|
+
return typed(['block', ['result', 'i32'],
|
|
139
|
+
['local.set', `$${t}`, asI32(toNumF64(n, emit(n)))],
|
|
140
|
+
['i32.or',
|
|
141
|
+
['i32.or', ['i32.eq', v(), ['i32.const', 4]], ['i32.eq', v(), ['i32.const', 1]]],
|
|
142
|
+
['i32.or', ['i32.eq', v(), ['i32.const', 2]], ['i32.eq', v(), ['i32.const', 8]]]]], 'i32')
|
|
143
|
+
}
|
|
144
|
+
}
|