jz 0.1.1 → 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 +257 -90
- package/cli.js +34 -8
- package/index.js +102 -13
- package/module/array.js +396 -133
- package/module/collection.js +455 -212
- package/module/console.js +169 -88
- package/module/core.js +246 -144
- package/module/date.js +691 -0
- package/module/function.js +81 -21
- package/module/index.js +2 -1
- package/module/json.js +483 -138
- package/module/math.js +79 -39
- package/module/number.js +188 -78
- package/module/object.js +227 -47
- package/module/regex.js +33 -30
- package/module/schema.js +14 -17
- package/module/string.js +434 -235
- package/module/symbol.js +4 -3
- package/module/timer.js +89 -31
- package/module/typedarray.js +174 -44
- package/package.json +3 -10
- package/src/analyze.js +626 -132
- package/src/autoload.js +14 -6
- package/src/compile.js +399 -70
- package/src/ctx.js +41 -12
- package/src/emit.js +634 -145
- package/src/host.js +244 -51
- package/src/ir.js +81 -31
- package/src/jzify.js +181 -24
- package/src/narrow.js +32 -4
- package/src/optimize.js +628 -42
- package/src/plan.js +1132 -4
- package/src/prepare.js +321 -75
- package/src/vectorize.js +1016 -0
- package/wasi.js +13 -0
- package/src/key.js +0 -73
- package/src/source.js +0 -76
package/module/function.js
CHANGED
|
@@ -12,8 +12,48 @@
|
|
|
12
12
|
|
|
13
13
|
import { typed, asF64, asI32, mkPtrIR, temp, tempI32, MAX_CLOSURE_ARITY, UNDEF_NAN } from '../src/ir.js'
|
|
14
14
|
import { emit, isReassigned } from '../src/emit.js'
|
|
15
|
-
import { T, lookupValType, repOf } from '../src/analyze.js'
|
|
16
|
-
import { PTR, inc, err } from '../src/ctx.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
|
+
}
|
|
17
57
|
|
|
18
58
|
|
|
19
59
|
export default (ctx) => {
|
|
@@ -43,6 +83,13 @@ export default (ctx) => {
|
|
|
43
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})`)
|
|
44
84
|
// Generate closure body function name
|
|
45
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
|
|
46
93
|
const captureValTypes = new Map()
|
|
47
94
|
const captureSchemaVars = new Map()
|
|
48
95
|
const captureTypedElems = new Map()
|
|
@@ -51,7 +98,7 @@ export default (ctx) => {
|
|
|
51
98
|
// call_indirect on the captured pointer). Gated on isReassigned over the inner body
|
|
52
99
|
// so a local rewrite of the captured name disables propagation.
|
|
53
100
|
const captureDirectClosures = new Map()
|
|
54
|
-
for (const name of
|
|
101
|
+
for (const name of envCaptures) {
|
|
55
102
|
const vt = lookupValType(name)
|
|
56
103
|
if (vt != null) captureValTypes.set(name, vt)
|
|
57
104
|
const schemaId = ctx.schema.idOf(name)
|
|
@@ -62,13 +109,26 @@ export default (ctx) => {
|
|
|
62
109
|
if (bodyName && !isReassigned(body, name)) captureDirectClosures.set(name, bodyName)
|
|
63
110
|
}
|
|
64
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
|
+
|
|
65
124
|
// All closures use uniform convention: (env: f64, args_array: f64) → f64
|
|
66
125
|
// The body unpacks individual params from the args array
|
|
67
|
-
const boxedCaptures =
|
|
68
|
-
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,
|
|
69
128
|
...(restParam && { rest: restParam }),
|
|
70
129
|
...(defaults && { defaults }),
|
|
71
130
|
...(boxedCaptures.length && { boxed: new Set(boxedCaptures) }),
|
|
131
|
+
...(captureIntConsts.size && { intConsts: captureIntConsts }),
|
|
72
132
|
...(captureValTypes.size && { valTypes: captureValTypes }),
|
|
73
133
|
...(captureSchemaVars.size && { schemaVars: captureSchemaVars }),
|
|
74
134
|
...(captureTypedElems.size && { typedElems: captureTypedElems }),
|
|
@@ -81,7 +141,7 @@ export default (ctx) => {
|
|
|
81
141
|
// Tag IR with .closureBodyName so emitDecl can register the binding for direct dispatch
|
|
82
142
|
// (skip call_indirect on a const-bound, non-escaping closure local). See emit.js '()' handler.
|
|
83
143
|
ctx.features.closure = true
|
|
84
|
-
if (
|
|
144
|
+
if (envCaptures.length === 0) {
|
|
85
145
|
// No captures — just a function reference
|
|
86
146
|
const ir = mkPtrIR(PTR.CLOSURE, tableIdx, 0)
|
|
87
147
|
ir.closureBodyName = fnName
|
|
@@ -92,16 +152,16 @@ export default (ctx) => {
|
|
|
92
152
|
const t = tempI32('env')
|
|
93
153
|
|
|
94
154
|
const block = [
|
|
95
|
-
['local.set', `$${t}`, ['call', '$__alloc', ['i32.const',
|
|
155
|
+
['local.set', `$${t}`, ['call', '$__alloc', ['i32.const', envCaptures.length * 8]]],
|
|
96
156
|
]
|
|
97
157
|
// Store captured values in env: boxed cells as raw i32 in low 4 bytes, others as f64.
|
|
98
158
|
// Avoids i32↔f64 roundtrip; body loads via i32.load/f64.load using the same branch.
|
|
99
|
-
for (let i = 0; i <
|
|
159
|
+
for (let i = 0; i < envCaptures.length; i++) {
|
|
100
160
|
const addr = ['i32.add', ['local.get', `$${t}`], ['i32.const', i * 8]]
|
|
101
|
-
if (ctx.func.boxed?.has(
|
|
102
|
-
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])}`]])
|
|
103
163
|
else
|
|
104
|
-
block.push(['f64.store', addr, asF64(emit(
|
|
164
|
+
block.push(['f64.store', addr, asF64(emit(envCaptures[i]))])
|
|
105
165
|
}
|
|
106
166
|
block.push(mkPtrIR(PTR.CLOSURE, tableIdx, ['local.get', `$${t}`]))
|
|
107
167
|
|
|
@@ -128,15 +188,15 @@ export default (ctx) => {
|
|
|
128
188
|
const arrT = tempI32('sa')
|
|
129
189
|
const lenL = tempI32('sl')
|
|
130
190
|
const setup = [
|
|
131
|
-
['local.set', `$${arrT}`, ['call', '$__ptr_offset', asF64(args[0])]],
|
|
132
|
-
['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
|
|
133
193
|
]
|
|
134
194
|
// Rebuild setup properly since we need the array ptr before len call
|
|
135
195
|
setup.length = 0
|
|
136
196
|
const arrPtrF64 = temp('sp')
|
|
137
197
|
setup.push(['local.set', `$${arrPtrF64}`, asF64(args[0])])
|
|
138
|
-
setup.push(['local.set', `$${arrT}`, ['call', '$__ptr_offset', ['local.get', `$${arrPtrF64}`]]])
|
|
139
|
-
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}`]]]])
|
|
140
200
|
|
|
141
201
|
const W = ctx.closure.width ?? MAX_CLOSURE_ARITY
|
|
142
202
|
const slots = []
|
|
@@ -153,10 +213,10 @@ export default (ctx) => {
|
|
|
153
213
|
['local.get', `$${t}`],
|
|
154
214
|
['local.get', `$${lenL}`],
|
|
155
215
|
...slots,
|
|
156
|
-
// Inline __ptr_aux for CLOSURE pointer: aux
|
|
216
|
+
// Inline __ptr_aux for CLOSURE pointer: aux holds funcIdx.
|
|
157
217
|
['i32.wrap_i64', ['i64.and',
|
|
158
|
-
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const',
|
|
159
|
-
['i64.const',
|
|
218
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
219
|
+
['i64.const', LAYOUT.AUX_MASK]]]]], 'f64')
|
|
160
220
|
}
|
|
161
221
|
|
|
162
222
|
// Inline path: emit each arg, pad missing slots with UNDEF
|
|
@@ -173,9 +233,9 @@ export default (ctx) => {
|
|
|
173
233
|
['local.get', `$${t}`],
|
|
174
234
|
['i32.const', n],
|
|
175
235
|
...slots,
|
|
176
|
-
// Inline __ptr_aux for CLOSURE pointer: aux
|
|
236
|
+
// Inline __ptr_aux for CLOSURE pointer: aux holds funcIdx.
|
|
177
237
|
['i32.wrap_i64', ['i64.and',
|
|
178
|
-
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const',
|
|
179
|
-
['i64.const',
|
|
238
|
+
['i64.shr_u', ['i64.reinterpret_f64', ['local.get', `$${t}`]], ['i64.const', LAYOUT.AUX_SHIFT]],
|
|
239
|
+
['i64.const', LAYOUT.AUX_MASK]]]]], 'f64')
|
|
180
240
|
}
|
|
181
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 }
|