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/function.js
CHANGED
|
@@ -10,13 +10,54 @@
|
|
|
10
10
|
* @module fn
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import {
|
|
14
|
-
import { isReassigned } from '../src/emit.js'
|
|
15
|
-
import {
|
|
13
|
+
import { typed, asF64, asI32, mkPtrIR, temp, tempI32, MAX_CLOSURE_ARITY, UNDEF_NAN } from '../src/ir.js'
|
|
14
|
+
import { emit, isReassigned } from '../src/emit.js'
|
|
15
|
+
import { T, lookupValType, repOf, findFreeVars } from '../src/analyze.js'
|
|
16
|
+
import { PTR, LAYOUT, inc, err } from '../src/ctx.js'
|
|
17
|
+
|
|
18
|
+
const intConstExpr = (node) => {
|
|
19
|
+
if (typeof node === 'number' && Number.isInteger(node)) return node
|
|
20
|
+
if (Array.isArray(node) && node[0] == null && Number.isInteger(node[1])) return node[1]
|
|
21
|
+
if (!Array.isArray(node)) return null
|
|
22
|
+
const [op, a, b] = node
|
|
23
|
+
const av = intConstExpr(a)
|
|
24
|
+
if (op === 'u-' || (op === '-' && b === undefined)) return av == null ? null : -av
|
|
25
|
+
const bv = intConstExpr(b)
|
|
26
|
+
if (av == null || bv == null) return null
|
|
27
|
+
switch (op) {
|
|
28
|
+
case '+': return av + bv
|
|
29
|
+
case '-': return av - bv
|
|
30
|
+
case '*': return av * bv
|
|
31
|
+
case '&': return av & bv
|
|
32
|
+
case '|': return av | bv
|
|
33
|
+
case '^': return av ^ bv
|
|
34
|
+
case '<<': return av << bv
|
|
35
|
+
case '>>': return av >> bv
|
|
36
|
+
case '>>>': return av >>> bv
|
|
37
|
+
default: return null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const topLevelIntConsts = (body) => {
|
|
42
|
+
const inner = Array.isArray(body) && body[0] === '{}' ? body[1] : body
|
|
43
|
+
const stmts = Array.isArray(inner) && inner[0] === ';' ? inner.slice(1) : []
|
|
44
|
+
const out = new Map()
|
|
45
|
+
for (const stmt of stmts) {
|
|
46
|
+
if (!Array.isArray(stmt) || (stmt[0] !== 'const' && stmt[0] !== 'let')) continue
|
|
47
|
+
for (let i = 1; i < stmt.length; i++) {
|
|
48
|
+
const decl = stmt[i]
|
|
49
|
+
if (!Array.isArray(decl) || decl[0] !== '=' || typeof decl[1] !== 'string') continue
|
|
50
|
+
if (stmt[0] === 'let' && isReassigned(body, decl[1])) continue
|
|
51
|
+
const v = intConstExpr(decl[2])
|
|
52
|
+
if (v != null && v >= -2147483648 && v <= 2147483647) out.set(decl[1], v)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return out
|
|
56
|
+
}
|
|
16
57
|
|
|
17
58
|
|
|
18
59
|
export default (ctx) => {
|
|
19
|
-
inc('__mkptr', '__alloc', '__len', '__ptr_offset')
|
|
60
|
+
inc('__mkptr', '__alloc', '__len', '__ptr_offset', '__ptr_type')
|
|
20
61
|
|
|
21
62
|
// Uniform closure convention: (env f64, argc i32, a0..a{MAX-1} f64) → f64
|
|
22
63
|
if (!ctx.closure.types) ctx.closure.types = new Set()
|
|
@@ -42,6 +83,13 @@ export default (ctx) => {
|
|
|
42
83
|
if (restParam && fixedN >= MAX_CLOSURE_ARITY) err(`Closure with rest param needs at least one free slot — ${fixedN} fixed params leaves none (MAX_CLOSURE_ARITY=${MAX_CLOSURE_ARITY})`)
|
|
43
84
|
// Generate closure body function name
|
|
44
85
|
const fnName = `${T}closure${ctx.closure.table.length}`
|
|
86
|
+
const localIntConsts = ctx.func.body ? topLevelIntConsts(ctx.func.body) : new Map()
|
|
87
|
+
const captureIntConsts = new Map()
|
|
88
|
+
for (const name of captures) {
|
|
89
|
+
const v = ctx.scope.constInts?.get(name) ?? localIntConsts.get(name)
|
|
90
|
+
if (v != null && !ctx.func.boxed?.has(name)) captureIntConsts.set(name, v)
|
|
91
|
+
}
|
|
92
|
+
const envCaptures = captureIntConsts.size ? captures.filter(name => !captureIntConsts.has(name)) : captures
|
|
45
93
|
const captureValTypes = new Map()
|
|
46
94
|
const captureSchemaVars = new Map()
|
|
47
95
|
const captureTypedElems = new Map()
|
|
@@ -50,7 +98,7 @@ export default (ctx) => {
|
|
|
50
98
|
// call_indirect on the captured pointer). Gated on isReassigned over the inner body
|
|
51
99
|
// so a local rewrite of the captured name disables propagation.
|
|
52
100
|
const captureDirectClosures = new Map()
|
|
53
|
-
for (const name of
|
|
101
|
+
for (const name of envCaptures) {
|
|
54
102
|
const vt = lookupValType(name)
|
|
55
103
|
if (vt != null) captureValTypes.set(name, vt)
|
|
56
104
|
const schemaId = ctx.schema.idOf(name)
|
|
@@ -61,13 +109,26 @@ export default (ctx) => {
|
|
|
61
109
|
if (bodyName && !isReassigned(body, name)) captureDirectClosures.set(name, bodyName)
|
|
62
110
|
}
|
|
63
111
|
|
|
112
|
+
const schemaNames = ctx.schema.vars?.size ? new Set(ctx.schema.vars.keys()) : null
|
|
113
|
+
if (schemaNames?.size) {
|
|
114
|
+
const refs = []
|
|
115
|
+
findFreeVars(body, new Set(params), refs, schemaNames)
|
|
116
|
+
for (const def of Object.values(defaults || {})) findFreeVars(def, new Set(params), refs, schemaNames)
|
|
117
|
+
for (const name of refs) {
|
|
118
|
+
if (captureSchemaVars.has(name)) continue
|
|
119
|
+
const schemaId = ctx.schema.idOf(name)
|
|
120
|
+
if (schemaId != null) captureSchemaVars.set(name, schemaId)
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
64
124
|
// All closures use uniform convention: (env: f64, args_array: f64) → f64
|
|
65
125
|
// The body unpacks individual params from the args array
|
|
66
|
-
const boxedCaptures =
|
|
67
|
-
const bodyFn = { name: fnName, params, body, captures, arity: 1,
|
|
126
|
+
const boxedCaptures = envCaptures.filter(c => ctx.func.boxed?.has(c))
|
|
127
|
+
const bodyFn = { name: fnName, params, body, captures: envCaptures, arity: 1,
|
|
68
128
|
...(restParam && { rest: restParam }),
|
|
69
129
|
...(defaults && { defaults }),
|
|
70
130
|
...(boxedCaptures.length && { boxed: new Set(boxedCaptures) }),
|
|
131
|
+
...(captureIntConsts.size && { intConsts: captureIntConsts }),
|
|
71
132
|
...(captureValTypes.size && { valTypes: captureValTypes }),
|
|
72
133
|
...(captureSchemaVars.size && { schemaVars: captureSchemaVars }),
|
|
73
134
|
...(captureTypedElems.size && { typedElems: captureTypedElems }),
|
|
@@ -80,7 +141,7 @@ export default (ctx) => {
|
|
|
80
141
|
// Tag IR with .closureBodyName so emitDecl can register the binding for direct dispatch
|
|
81
142
|
// (skip call_indirect on a const-bound, non-escaping closure local). See emit.js '()' handler.
|
|
82
143
|
ctx.features.closure = true
|
|
83
|
-
if (
|
|
144
|
+
if (envCaptures.length === 0) {
|
|
84
145
|
// No captures — just a function reference
|
|
85
146
|
const ir = mkPtrIR(PTR.CLOSURE, tableIdx, 0)
|
|
86
147
|
ir.closureBodyName = fnName
|
|
@@ -91,16 +152,16 @@ export default (ctx) => {
|
|
|
91
152
|
const t = tempI32('env')
|
|
92
153
|
|
|
93
154
|
const block = [
|
|
94
|
-
['local.set', `$${t}`, ['call', '$__alloc', ['i32.const',
|
|
155
|
+
['local.set', `$${t}`, ['call', '$__alloc', ['i32.const', envCaptures.length * 8]]],
|
|
95
156
|
]
|
|
96
157
|
// Store captured values in env: boxed cells as raw i32 in low 4 bytes, others as f64.
|
|
97
158
|
// Avoids i32↔f64 roundtrip; body loads via i32.load/f64.load using the same branch.
|
|
98
|
-
for (let i = 0; i <
|
|
159
|
+
for (let i = 0; i < envCaptures.length; i++) {
|
|
99
160
|
const addr = ['i32.add', ['local.get', `$${t}`], ['i32.const', i * 8]]
|
|
100
|
-
if (ctx.func.boxed?.has(
|
|
101
|
-
block.push(['i32.store', addr, ['local.get', `$${ctx.func.boxed.get(
|
|
161
|
+
if (ctx.func.boxed?.has(envCaptures[i]))
|
|
162
|
+
block.push(['i32.store', addr, ['local.get', `$${ctx.func.boxed.get(envCaptures[i])}`]])
|
|
102
163
|
else
|
|
103
|
-
block.push(['f64.store', addr, asF64(emit(
|
|
164
|
+
block.push(['f64.store', addr, asF64(emit(envCaptures[i]))])
|
|
104
165
|
}
|
|
105
166
|
block.push(mkPtrIR(PTR.CLOSURE, tableIdx, ['local.get', `$${t}`]))
|
|
106
167
|
|
|
@@ -127,15 +188,15 @@ export default (ctx) => {
|
|
|
127
188
|
const arrT = tempI32('sa')
|
|
128
189
|
const lenL = tempI32('sl')
|
|
129
190
|
const setup = [
|
|
130
|
-
['local.set', `$${arrT}`, ['call', '$__ptr_offset', asF64(args[0])]],
|
|
131
|
-
['local.set', `$${lenL}`, ['call', '$__len', ['local.get', `$${t}`]]], // placeholder — set below
|
|
191
|
+
['local.set', `$${arrT}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', asF64(args[0])]]],
|
|
192
|
+
['local.set', `$${lenL}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${t}`]]]], // placeholder — set below
|
|
132
193
|
]
|
|
133
194
|
// Rebuild setup properly since we need the array ptr before len call
|
|
134
195
|
setup.length = 0
|
|
135
196
|
const arrPtrF64 = temp('sp')
|
|
136
197
|
setup.push(['local.set', `$${arrPtrF64}`, asF64(args[0])])
|
|
137
|
-
setup.push(['local.set', `$${arrT}`, ['call', '$__ptr_offset', ['local.get', `$${arrPtrF64}`]]])
|
|
138
|
-
setup.push(['local.set', `$${lenL}`, ['call', '$__len', ['local.get', `$${arrPtrF64}`]]])
|
|
198
|
+
setup.push(['local.set', `$${arrT}`, ['call', '$__ptr_offset', ['i64.reinterpret_f64', ['local.get', `$${arrPtrF64}`]]]])
|
|
199
|
+
setup.push(['local.set', `$${lenL}`, ['call', '$__len', ['i64.reinterpret_f64', ['local.get', `$${arrPtrF64}`]]]])
|
|
139
200
|
|
|
140
201
|
const W = ctx.closure.width ?? MAX_CLOSURE_ARITY
|
|
141
202
|
const slots = []
|
|
@@ -152,10 +213,10 @@ export default (ctx) => {
|
|
|
152
213
|
['local.get', `$${t}`],
|
|
153
214
|
['local.get', `$${lenL}`],
|
|
154
215
|
...slots,
|
|
155
|
-
// Inline __ptr_aux for CLOSURE pointer: aux
|
|
216
|
+
// Inline __ptr_aux for CLOSURE pointer: aux holds funcIdx.
|
|
156
217
|
['i32.wrap_i64', ['i64.and',
|
|
157
|
-
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const',
|
|
158
|
-
['i64.const',
|
|
218
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
219
|
+
['i64.const', LAYOUT.AUX_MASK]]]]], 'f64')
|
|
159
220
|
}
|
|
160
221
|
|
|
161
222
|
// Inline path: emit each arg, pad missing slots with UNDEF
|
|
@@ -172,9 +233,9 @@ export default (ctx) => {
|
|
|
172
233
|
['local.get', `$${t}`],
|
|
173
234
|
['i32.const', n],
|
|
174
235
|
...slots,
|
|
175
|
-
// Inline __ptr_aux for CLOSURE pointer: aux
|
|
236
|
+
// Inline __ptr_aux for CLOSURE pointer: aux holds funcIdx.
|
|
176
237
|
['i32.wrap_i64', ['i64.and',
|
|
177
|
-
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const',
|
|
178
|
-
['i64.const',
|
|
238
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
239
|
+
['i64.const', LAYOUT.AUX_MASK]]]]], 'f64')
|
|
179
240
|
}
|
|
180
241
|
}
|
package/module/index.js
CHANGED
|
@@ -12,4 +12,5 @@ import console from './console.js'
|
|
|
12
12
|
import json from './json.js'
|
|
13
13
|
import regex from './regex.js'
|
|
14
14
|
import timer from './timer.js'
|
|
15
|
-
|
|
15
|
+
import date from './date.js'
|
|
16
|
+
export { math, core, array, object, string, number, fn, typedarray, collection, symbol, console, json, regex, timer, date }
|