@xenolithengine/graph-runtime-as 0.7.0-beta.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/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/compile.d.ts +10 -0
- package/dist/compile.d.ts.map +1 -0
- package/dist/compile.js +48 -0
- package/dist/compile.js.map +1 -0
- package/dist/emit.d.ts +31 -0
- package/dist/emit.d.ts.map +1 -0
- package/dist/emit.js +293 -0
- package/dist/emit.js.map +1 -0
- package/dist/index.bench.d.ts +2 -0
- package/dist/index.bench.d.ts.map +1 -0
- package/dist/index.bench.js +47 -0
- package/dist/index.bench.js.map +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +56 -0
- package/dist/index.js.map +1 -0
- package/dist/mandelbrot.bench.d.ts +2 -0
- package/dist/mandelbrot.bench.d.ts.map +1 -0
- package/dist/mandelbrot.bench.js +43 -0
- package/dist/mandelbrot.bench.js.map +1 -0
- package/package.json +57 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 strelok2012
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @xenolithengine/graph-runtime-as
|
|
2
|
+
|
|
3
|
+
[](https://github.com/XenolithEngine/xenolith-graph#status)
|
|
4
|
+
[](https://github.com/XenolithEngine/xenolith-graph/blob/main/LICENSE)
|
|
5
|
+
|
|
6
|
+
AssemblyScript-based WASM code generator for the XenolithGraph Blueprint runtime. Emits AS source from an `RtGraph` + `NodeDef` set, compiles to a WebAssembly module via `asc`, exposes the same call surface as the JS codegen.
|
|
7
|
+
|
|
8
|
+
> **Experimental** — supports the numeric subset only (`canCompileToAS(graph)` gates eligibility). Non-numeric primitives fall back to `@xenolithengine/graph-plugin-runtime`'s JS codegen.
|
|
9
|
+
|
|
10
|
+
Part of [XenolithGraph](https://github.com/XenolithEngine/xenolith-graph) — an AI-native, embeddable node-graph editor for the web with its own visual design language (Xen).
|
|
11
|
+
|
|
12
|
+
## Install
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
pnpm add @xenolithengine/graph-runtime-as @xenolithengine/graph-plugin-runtime
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Requires `assemblyscript` at compile time (the package builds the WASM module in-process).
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
import { compile, canCompileToAS } from '@xenolithengine/graph-runtime-as'
|
|
24
|
+
import { BUILTIN_PRIMITIVES, type RtGraph } from '@xenolithengine/graph-plugin-runtime'
|
|
25
|
+
|
|
26
|
+
if (canCompileToAS(myGraph)) {
|
|
27
|
+
const wasm = await compile(myGraph, BUILTIN_PRIMITIVES)
|
|
28
|
+
wasm.setVar('x', 0.3)
|
|
29
|
+
wasm.tick() // executes the WASM module
|
|
30
|
+
console.log(wasm.getVar('y'))
|
|
31
|
+
if (wasm.tickArgs) console.log(wasm.tickArgs(0.3, 0.6)) // ~2× faster on hot loops
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
`wasm.wasm` and `wasm.source` are exposed for inspection — the raw bytes and the generated AS source.
|
|
36
|
+
|
|
37
|
+
## What's exported
|
|
38
|
+
|
|
39
|
+
- `compile(graph, defs)` → `Promise<ASWasmGraph>` — emit + compile + instantiate
|
|
40
|
+
- `canCompileToAS(graph)` — gate for eligibility (numeric primitives only)
|
|
41
|
+
- `emitASSource(graph, defs)` — generate AS source without compiling
|
|
42
|
+
- `compileAS(source)` — compile a hand-authored AS source string
|
|
43
|
+
- `ASWasmGraph` — `{ tick, getVar, setVar, reset, tickArgs?, wasm, source }`
|
|
44
|
+
|
|
45
|
+
## Docs
|
|
46
|
+
|
|
47
|
+
- [API reference](https://xenolithengine.github.io/xenolith-graph/guides/api/) — every method exposed by `XenolithEditor`
|
|
48
|
+
- [GitHub](https://github.com/XenolithEngine/xenolith-graph)
|
|
49
|
+
|
|
50
|
+
MIT © XenolithEngine
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export interface CompileResult {
|
|
2
|
+
wasm: Uint8Array;
|
|
3
|
+
/** AS compiler stdout — useful for debugging when something blows up. */
|
|
4
|
+
stdout: string;
|
|
5
|
+
/** AS compiler stderr — actual errors land here. */
|
|
6
|
+
stderr: string;
|
|
7
|
+
}
|
|
8
|
+
/** Compile the given AS source to a WASM binary. Throws if asc reports any error. */
|
|
9
|
+
export declare function compileAS(source: string): Promise<CompileResult>;
|
|
10
|
+
//# sourceMappingURL=compile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.d.ts","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,UAAU,CAAA;IAChB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAA;IACd,oDAAoD;IACpD,MAAM,EAAE,MAAM,CAAA;CACf;AAED,qFAAqF;AACrF,wBAAsB,SAAS,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC,CAsCtE"}
|
package/dist/compile.js
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
// AssemblyScript compilation pipeline. Takes the AS source produced by `emit.ts`, hands it to
|
|
2
|
+
// `asc` (the AssemblyScript compiler) via its programmatic API, and returns the resulting WASM
|
|
3
|
+
// bytes. asc is async, so this function is too.
|
|
4
|
+
//
|
|
5
|
+
// Build flags: `--optimize` gives release-grade output (Binaryen passes), `--runtime stub` skips
|
|
6
|
+
// the AS GC runtime (we don't allocate AS objects in the numeric subset, so we don't need it —
|
|
7
|
+
// shaves ~5 KB off the module). `--initialMemory 1` keeps the module's memory section minimal.
|
|
8
|
+
import asc from 'assemblyscript/asc';
|
|
9
|
+
/** Compile the given AS source to a WASM binary. Throws if asc reports any error. */
|
|
10
|
+
export async function compileAS(source) {
|
|
11
|
+
// Output sinks: asc invokes our `writeFile` for every artefact it generates (.wasm, .wat, .d.ts).
|
|
12
|
+
// We only care about the wasm.
|
|
13
|
+
let wasm = null;
|
|
14
|
+
// Buffer stdout/stderr per-compile so concurrent compiles don't interleave.
|
|
15
|
+
const stdoutChunks = [];
|
|
16
|
+
const stderrChunks = [];
|
|
17
|
+
const { error } = await asc.main([
|
|
18
|
+
'input.ts',
|
|
19
|
+
'--outFile', 'output.wasm',
|
|
20
|
+
'--runtime', 'stub', // no GC — numeric subset doesn't allocate
|
|
21
|
+
'--optimize',
|
|
22
|
+
'--optimizeLevel', '3',
|
|
23
|
+
'--shrinkLevel', '0',
|
|
24
|
+
'--converge', // run Binaryen passes to fixpoint
|
|
25
|
+
], {
|
|
26
|
+
stdout: { write: (s) => { stdoutChunks.push(s); return true; } },
|
|
27
|
+
stderr: { write: (s) => { stderrChunks.push(s); return true; } },
|
|
28
|
+
readFile: (name, _baseDir) => {
|
|
29
|
+
if (name === 'input.ts')
|
|
30
|
+
return source;
|
|
31
|
+
// Returning null tells asc to fall back to its bundled stdlib resolver.
|
|
32
|
+
return null;
|
|
33
|
+
},
|
|
34
|
+
writeFile: (name, data) => {
|
|
35
|
+
if (name === 'output.wasm' && data instanceof Uint8Array)
|
|
36
|
+
wasm = data;
|
|
37
|
+
},
|
|
38
|
+
listFiles: (_dir, _baseDir) => null,
|
|
39
|
+
});
|
|
40
|
+
const stdout = stdoutChunks.join('');
|
|
41
|
+
const stderr = stderrChunks.join('');
|
|
42
|
+
if (error)
|
|
43
|
+
throw new Error(`asc compile failed: ${String(error)}\n${stderr}`);
|
|
44
|
+
if (!wasm)
|
|
45
|
+
throw new Error(`asc produced no wasm output\n${stderr}`);
|
|
46
|
+
return { wasm, stdout, stderr };
|
|
47
|
+
}
|
|
48
|
+
//# sourceMappingURL=compile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compile.js","sourceRoot":"","sources":["../src/compile.ts"],"names":[],"mappings":"AAAA,8FAA8F;AAC9F,+FAA+F;AAC/F,gDAAgD;AAChD,EAAE;AACF,iGAAiG;AACjG,+FAA+F;AAC/F,+FAA+F;AAE/F,OAAO,GAAG,MAAM,oBAAoB,CAAA;AAUpC,qFAAqF;AACrF,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,MAAc;IAC5C,kGAAkG;IAClG,+BAA+B;IAC/B,IAAI,IAAI,GAAsB,IAAI,CAAA;IAClC,4EAA4E;IAC5E,MAAM,YAAY,GAAa,EAAE,CAAA;IACjC,MAAM,YAAY,GAAa,EAAE,CAAA;IAEjC,MAAM,EAAE,KAAK,EAAE,GAAG,MAAM,GAAG,CAAC,IAAI,CAC9B;QACE,UAAU;QACV,WAAW,EAAQ,aAAa;QAChC,WAAW,EAAQ,MAAM,EAAU,0CAA0C;QAC7E,YAAY;QACZ,iBAAiB,EAAE,GAAG;QACtB,eAAe,EAAI,GAAG;QACtB,YAAY,EAAwB,kCAAkC;KACvE,EACD;QACE,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAS,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAA,CAAC,CAAC,EAAmC;QACxG,MAAM,EAAE,EAAE,KAAK,EAAE,CAAC,CAAS,EAAE,EAAE,GAAG,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,IAAI,CAAA,CAAC,CAAC,EAAmC;QACxG,QAAQ,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAiB,EAAE;YAC1D,IAAI,IAAI,KAAK,UAAU;gBAAE,OAAO,MAAM,CAAA;YACtC,wEAAwE;YACxE,OAAO,IAAI,CAAA;QACb,CAAC;QACD,SAAS,EAAE,CAAC,IAAY,EAAE,IAAyB,EAAQ,EAAE;YAC3D,IAAI,IAAI,KAAK,aAAa,IAAI,IAAI,YAAY,UAAU;gBAAE,IAAI,GAAG,IAAI,CAAA;QACvE,CAAC;QACD,SAAS,EAAE,CAAC,IAAY,EAAE,QAAgB,EAAmB,EAAE,CAAC,IAAI;KACrE,CACF,CAAA;IAED,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpC,MAAM,MAAM,GAAG,YAAY,CAAC,IAAI,CAAC,EAAE,CAAC,CAAA;IACpC,IAAI,KAAK;QAAE,MAAM,IAAI,KAAK,CAAC,uBAAuB,MAAM,CAAC,KAAK,CAAC,KAAK,MAAM,EAAE,CAAC,CAAA;IAC7E,IAAI,CAAC,IAAI;QAAE,MAAM,IAAI,KAAK,CAAC,gCAAgC,MAAM,EAAE,CAAC,CAAA;IACpE,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,CAAA;AACjC,CAAC"}
|
package/dist/emit.d.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { RtGraph, RtPin, NodeDef } from '@xenolithengine/graph-plugin-runtime';
|
|
2
|
+
interface NodeIR {
|
|
3
|
+
id: string;
|
|
4
|
+
type: string;
|
|
5
|
+
index: number;
|
|
6
|
+
state: Record<string, unknown> | undefined;
|
|
7
|
+
dataIns: RtPin[];
|
|
8
|
+
dataOuts: RtPin[];
|
|
9
|
+
execOuts: RtPin[];
|
|
10
|
+
incoming: Array<{
|
|
11
|
+
srcIdx: number;
|
|
12
|
+
outIdx: number;
|
|
13
|
+
} | null>;
|
|
14
|
+
execTarget: number[];
|
|
15
|
+
}
|
|
16
|
+
export interface IR {
|
|
17
|
+
nodes: NodeIR[];
|
|
18
|
+
varIndex: Map<string, number>;
|
|
19
|
+
entriesByType: Map<string, number[]>;
|
|
20
|
+
/** Populated during emit walk — node indices of `Output` nodes the exec flow actually reaches. */
|
|
21
|
+
reachableOutputs: Set<number>;
|
|
22
|
+
}
|
|
23
|
+
export declare function buildIR(graph: RtGraph): IR;
|
|
24
|
+
export declare const AS_SUPPORTED: Set<string>;
|
|
25
|
+
export declare function canCompileToAS(graph: RtGraph): boolean;
|
|
26
|
+
/** Produce a complete AssemblyScript source file for the graph. The caller hands this to `asc`. */
|
|
27
|
+
export declare function emitASSource(graph: RtGraph, _defs: ReadonlyArray<NodeDef>): string;
|
|
28
|
+
/** Stable lookup `name → slot` exposed so callers know which i32 to pass to getVar/setVar. */
|
|
29
|
+
export declare function varIndexOf(graph: RtGraph): Map<string, number>;
|
|
30
|
+
export {};
|
|
31
|
+
//# sourceMappingURL=emit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.d.ts","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,OAAO,EAAU,KAAK,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAM3F,UAAU,MAAM;IACd,EAAE,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;IACvC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;IAC1C,OAAO,EAAE,KAAK,EAAE,CAAC;IAAC,QAAQ,EAAE,KAAK,EAAE,CAAC;IAAC,QAAQ,EAAE,KAAK,EAAE,CAAA;IACtD,QAAQ,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC,CAAA;IAC1D,UAAU,EAAE,MAAM,EAAE,CAAA;CACrB;AACD,MAAM,WAAW,EAAE;IACjB,KAAK,EAAE,MAAM,EAAE,CAAA;IACf,QAAQ,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAC7B,aAAa,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;IACpC,kGAAkG;IAClG,gBAAgB,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;CAC9B;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,EAAE,CAmC1C;AAID,eAAO,MAAM,YAAY,aAGvB,CAAA;AAEF,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAGtD;AAyHD,mGAAmG;AACnG,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,MAAM,CA4GlF;AAED,8FAA8F;AAC9F,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAE9D"}
|
package/dist/emit.js
ADDED
|
@@ -0,0 +1,293 @@
|
|
|
1
|
+
// AssemblyScript source emitter. Walks an RtGraph, builds an IR (same shape as the JS codegen's
|
|
2
|
+
// IR), then emits AS source — TypeScript-like with explicit f64 / i32 types — that the `asc`
|
|
3
|
+
// compiler hands us back as a WebAssembly module. Numeric subset only for this first cut:
|
|
4
|
+
// Tick / Init / Sequence / SetVar / Branch + Const / GetVar / Add / Sub / Mul / Floor / Gt / Gte / Eq.
|
|
5
|
+
// String/array/object primitives need AS-side types and live in a follow-up.
|
|
6
|
+
const dataIns = (n) => n.pins.filter((p) => p.kind === 'data' && p.direction === 'in');
|
|
7
|
+
const dataOuts = (n) => n.pins.filter((p) => p.kind === 'data' && p.direction === 'out');
|
|
8
|
+
const execOuts = (n) => n.pins.filter((p) => p.kind === 'exec' && p.direction === 'out');
|
|
9
|
+
export function buildIR(graph) {
|
|
10
|
+
const nodes = graph.nodes.map((n, i) => ({
|
|
11
|
+
id: n.id, type: n.type, index: i, state: n.state,
|
|
12
|
+
dataIns: dataIns(n), dataOuts: dataOuts(n), execOuts: execOuts(n),
|
|
13
|
+
incoming: [], execTarget: [],
|
|
14
|
+
}));
|
|
15
|
+
const byId = new Map(nodes.map((n) => [n.id, n]));
|
|
16
|
+
for (const n of nodes) {
|
|
17
|
+
n.incoming = new Array(n.dataIns.length).fill(null);
|
|
18
|
+
n.execTarget = new Array(n.execOuts.length).fill(-1);
|
|
19
|
+
}
|
|
20
|
+
for (const e of graph.edges) {
|
|
21
|
+
const dst = byId.get(e.to.node);
|
|
22
|
+
const src = byId.get(e.from.node);
|
|
23
|
+
if (!dst || !src)
|
|
24
|
+
continue;
|
|
25
|
+
const inIdx = dst.dataIns.findIndex((p) => p.id === e.to.pin);
|
|
26
|
+
const outIdx = src.dataOuts.findIndex((p) => p.id === e.from.pin);
|
|
27
|
+
if (inIdx >= 0 && outIdx >= 0 && dst.incoming[inIdx] === null)
|
|
28
|
+
dst.incoming[inIdx] = { srcIdx: src.index, outIdx };
|
|
29
|
+
const eOut = src.execOuts.findIndex((p) => p.id === e.from.pin);
|
|
30
|
+
if (eOut >= 0)
|
|
31
|
+
src.execTarget[eOut] = dst.index;
|
|
32
|
+
}
|
|
33
|
+
const varIndex = new Map();
|
|
34
|
+
const REFS_VAR = new Set(['GetVar', 'SetVar', 'GraphInput', 'GraphOutput', 'Local']);
|
|
35
|
+
for (const n of nodes) {
|
|
36
|
+
if (REFS_VAR.has(n.type) && typeof n.state?.['name'] === 'string') {
|
|
37
|
+
const name = n.state['name'];
|
|
38
|
+
if (!varIndex.has(name))
|
|
39
|
+
varIndex.set(name, varIndex.size);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
const entriesByType = new Map();
|
|
43
|
+
for (const n of nodes) {
|
|
44
|
+
if (n.type !== 'Tick' && n.type !== 'Init')
|
|
45
|
+
continue;
|
|
46
|
+
const arr = entriesByType.get(n.type) ?? [];
|
|
47
|
+
arr.push(n.index);
|
|
48
|
+
entriesByType.set(n.type, arr);
|
|
49
|
+
}
|
|
50
|
+
return { nodes, varIndex, entriesByType, reachableOutputs: new Set() };
|
|
51
|
+
}
|
|
52
|
+
// --- supported subset ----------------------------------------------------------------------------
|
|
53
|
+
export const AS_SUPPORTED = new Set([
|
|
54
|
+
'Tick', 'Init', 'Sequence', 'SetVar', 'GraphOutput', 'Local', 'Branch', 'Loop',
|
|
55
|
+
'GetVar', 'GraphInput', 'Const', 'Add', 'Sub', 'Mul', 'Floor', 'Gt', 'Gte', 'Eq',
|
|
56
|
+
]);
|
|
57
|
+
export function canCompileToAS(graph) {
|
|
58
|
+
for (const n of graph.nodes)
|
|
59
|
+
if (!AS_SUPPORTED.has(n.type))
|
|
60
|
+
return false;
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
// --- emitters ------------------------------------------------------------------------------------
|
|
64
|
+
/** Emit an AS expression for `dataIn[i]` of node `n`. Pure-source nodes inline; exec-source nodes
|
|
65
|
+
* would read from a tick-scoped override array (not implemented here — numeric subset has no
|
|
66
|
+
* exec-data outputs, so the lookup is never needed). */
|
|
67
|
+
function emitInput(ir, n, i) {
|
|
68
|
+
const inc = n.incoming[i];
|
|
69
|
+
if (!inc)
|
|
70
|
+
return '0.0'; // unwired numeric default
|
|
71
|
+
return emitPureExpr(ir, ir.nodes[inc.srcIdx], inc.outIdx);
|
|
72
|
+
}
|
|
73
|
+
/** Emit a *boolean-context* expression for input(i): collapse `Gt/Gte/Eq` (which normally return
|
|
74
|
+
* f64 0.0/1.0) into a plain `(a OP b)` comparison when consumed by Branch/Loop. Saves a select +
|
|
75
|
+
* ne-zero per hot-path iteration — measurable on Mandelbrot. */
|
|
76
|
+
function emitBoolInput(ir, n, i) {
|
|
77
|
+
const inc = n.incoming[i];
|
|
78
|
+
if (!inc)
|
|
79
|
+
return 'false';
|
|
80
|
+
const src = ir.nodes[inc.srcIdx];
|
|
81
|
+
switch (src.type) {
|
|
82
|
+
case 'Gt': return `(${emitInput(ir, src, 0)} > ${emitInput(ir, src, 1)})`;
|
|
83
|
+
case 'Gte': return `(${emitInput(ir, src, 0)} >= ${emitInput(ir, src, 1)})`;
|
|
84
|
+
case 'Eq': return `(${emitInput(ir, src, 0)} == ${emitInput(ir, src, 1)})`;
|
|
85
|
+
default: return `(${emitInput(ir, n, i)} != 0.0)`;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
function emitPureExpr(ir, n, _outIdx) {
|
|
89
|
+
switch (n.type) {
|
|
90
|
+
case 'Const': {
|
|
91
|
+
const v = n.state?.['value'];
|
|
92
|
+
return formatF64(typeof v === 'number' ? v : 0);
|
|
93
|
+
}
|
|
94
|
+
case 'GetVar':
|
|
95
|
+
case 'GraphInput':
|
|
96
|
+
case 'Local': {
|
|
97
|
+
const name = n.state?.['name'];
|
|
98
|
+
if (typeof name !== 'string')
|
|
99
|
+
return '0.0';
|
|
100
|
+
const vi = ir.varIndex.get(name) ?? -1;
|
|
101
|
+
// Reads from a module-level `let` — AS lowers to a WASM global → register-allocated by V8.
|
|
102
|
+
// Input vs GetVar: identical at the AS level — `tickArgs()` seeds Input vars from params
|
|
103
|
+
// before running tickBody, so they read like any other var.
|
|
104
|
+
return vi < 0 ? '0.0' : `v${vi}`;
|
|
105
|
+
}
|
|
106
|
+
case 'Add': return `(${emitInput(ir, n, 0)} + ${emitInput(ir, n, 1)})`;
|
|
107
|
+
case 'Sub': return `(${emitInput(ir, n, 0)} - ${emitInput(ir, n, 1)})`;
|
|
108
|
+
case 'Mul': return `(${emitInput(ir, n, 0)} * ${emitInput(ir, n, 1)})`;
|
|
109
|
+
case 'Floor': return `Math.floor(${emitInput(ir, n, 0)})`;
|
|
110
|
+
case 'Gt': return `(${emitInput(ir, n, 0)} > ${emitInput(ir, n, 1)} ? 1.0 : 0.0)`;
|
|
111
|
+
case 'Gte': return `(${emitInput(ir, n, 0)} >= ${emitInput(ir, n, 1)} ? 1.0 : 0.0)`;
|
|
112
|
+
case 'Eq': return `(${emitInput(ir, n, 0)} == ${emitInput(ir, n, 1)} ? 1.0 : 0.0)`;
|
|
113
|
+
default: return '0.0';
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** AS doesn't accept JS literals like `1e-10` for very small magnitudes the same way? It does —
|
|
117
|
+
* but force a `.0` suffix on integers so the literal is typed f64, not i32. */
|
|
118
|
+
function formatF64(v) {
|
|
119
|
+
if (!Number.isFinite(v))
|
|
120
|
+
return v === Infinity ? 'f64.MAX_VALUE' : v === -Infinity ? '-f64.MAX_VALUE' : '0.0';
|
|
121
|
+
if (Number.isInteger(v) && Math.abs(v) < 1e16)
|
|
122
|
+
return `${v}.0`;
|
|
123
|
+
return String(v);
|
|
124
|
+
}
|
|
125
|
+
// All exec-flow emission is now INLINE — no per-node WASM functions, no call/return overhead.
|
|
126
|
+
// `seen` guards against pathological cycles (shouldn't happen in normal RtGraphs); a node
|
|
127
|
+
// appearing twice via fan-in is *duplicated* (intentional — same cost as a function call would have
|
|
128
|
+
// inlined to the same place anyway, but no return jump).
|
|
129
|
+
const MAX_INLINE_DEPTH = 256;
|
|
130
|
+
function emitNodeBody(ir, n, seen, depth) {
|
|
131
|
+
if (depth > MAX_INLINE_DEPTH)
|
|
132
|
+
throw new Error(`AS-codegen: exec-flow depth > ${MAX_INLINE_DEPTH} at n${n.index} (${n.type}) — likely a cycle`);
|
|
133
|
+
// Reachability collector — populated as we walk exec flow from Tick/Init entries.
|
|
134
|
+
if (n.type === 'GraphOutput') {
|
|
135
|
+
// GraphOutput behaves like SetVar AND marks the node as a reachable graph output.
|
|
136
|
+
ir.reachableOutputs.add(n.index);
|
|
137
|
+
}
|
|
138
|
+
switch (n.type) {
|
|
139
|
+
case 'Tick':
|
|
140
|
+
case 'Init': return emitFlow(ir, n, 0, seen, depth + 1);
|
|
141
|
+
case 'Sequence': return n.execOuts.map((_, i) => emitFlow(ir, n, i, seen, depth + 1)).join('\n ');
|
|
142
|
+
case 'Branch': {
|
|
143
|
+
return `if (${emitBoolInput(ir, n, 0)}) {\n ${emitFlow(ir, n, 0, seen, depth + 1)}\n } else {\n ${emitFlow(ir, n, 1, seen, depth + 1)}\n }`;
|
|
144
|
+
}
|
|
145
|
+
case 'SetVar':
|
|
146
|
+
case 'GraphOutput':
|
|
147
|
+
case 'Local': {
|
|
148
|
+
const name = n.state?.['name'];
|
|
149
|
+
if (typeof name !== 'string')
|
|
150
|
+
return emitFlow(ir, n, 0, seen, depth + 1);
|
|
151
|
+
const vi = ir.varIndex.get(name);
|
|
152
|
+
if (vi === undefined)
|
|
153
|
+
return emitFlow(ir, n, 0, seen, depth + 1);
|
|
154
|
+
return `v${vi} = ${emitInput(ir, n, 0)};\n ${emitFlow(ir, n, 0, seen, depth + 1)}`;
|
|
155
|
+
}
|
|
156
|
+
case 'Loop': {
|
|
157
|
+
const maxExpr = emitInput(ir, n, 0);
|
|
158
|
+
const condExpr = emitBoolInput(ir, n, 1);
|
|
159
|
+
const body = emitFlow(ir, n, 0, seen, depth + 1);
|
|
160
|
+
const done = emitFlow(ir, n, 1, seen, depth + 1);
|
|
161
|
+
return `{
|
|
162
|
+
const _lmax = i32(${maxExpr});
|
|
163
|
+
for (let _li: i32 = 0; _li < _lmax; _li++) {
|
|
164
|
+
if (!(${condExpr})) break;
|
|
165
|
+
${body}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
${done}`;
|
|
169
|
+
}
|
|
170
|
+
default: return '';
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
function emitFlow(ir, n, execIdx, seen, depth) {
|
|
174
|
+
const t = n.execTarget[execIdx];
|
|
175
|
+
if (t === undefined || t < 0)
|
|
176
|
+
return '';
|
|
177
|
+
if (seen.has(t))
|
|
178
|
+
return `/* cycle skip n${t} */`;
|
|
179
|
+
// Fan-in duplication is fine — each emission site gets its own inlined copy, no shared seen state.
|
|
180
|
+
const next = new Set(seen);
|
|
181
|
+
next.add(t);
|
|
182
|
+
return emitNodeBody(ir, ir.nodes[t], next, depth);
|
|
183
|
+
}
|
|
184
|
+
// --- top-level source ----------------------------------------------------------------------------
|
|
185
|
+
/** Produce a complete AssemblyScript source file for the graph. The caller hands this to `asc`. */
|
|
186
|
+
export function emitASSource(graph, _defs) {
|
|
187
|
+
if (!canCompileToAS(graph)) {
|
|
188
|
+
throw new Error('AS-codegen: graph uses non-numeric primitives — only the numeric subset is supported in this version');
|
|
189
|
+
}
|
|
190
|
+
const ir = buildIR(graph);
|
|
191
|
+
const varCount = Math.max(1, ir.varIndex.size);
|
|
192
|
+
// Resolve I/O for `tickArgs(...)`: prefer explicit `meta.inputs/outputs`, otherwise auto-derive
|
|
193
|
+
// from declared `Input`/`Output` nodes (their `state.name` is the var slot they read/write).
|
|
194
|
+
// Auto-derivation keeps the order stable across recompiles by walking nodes in IR order.
|
|
195
|
+
const declaredInputs = ir.nodes.filter((n) => n.type === 'GraphInput' && typeof n.state?.['name'] === 'string').map((n) => n.state['name']);
|
|
196
|
+
const declaredOutputs = ir.nodes.filter((n) => n.type === 'GraphOutput' && typeof n.state?.['name'] === 'string').map((n) => n.state['name']);
|
|
197
|
+
const inputNames = graph.meta?.inputs ?? declaredInputs;
|
|
198
|
+
const outputNames = graph.meta?.outputs ?? declaredOutputs;
|
|
199
|
+
// Local nodes: collect (varIndex, initial) pairs to seed at the top of every tick — Local is
|
|
200
|
+
// tick-scoped state, must reset to `initial` regardless of what persistent storage holds.
|
|
201
|
+
const localSeed = ir.nodes
|
|
202
|
+
.filter((n) => n.type === 'Local' && typeof n.state?.['name'] === 'string')
|
|
203
|
+
.map((n) => ({ vi: ir.varIndex.get(n.state['name']), initial: Number(n.state['initial'] ?? 0) }))
|
|
204
|
+
.filter((s) => s.vi !== undefined);
|
|
205
|
+
const seedLocals = localSeed.map((s) => `v${s.vi} = ${formatF64(s.initial)};`).join('\n ');
|
|
206
|
+
const inputIdxs = inputNames.map((n) => ir.varIndex.get(n)).filter((i) => i !== undefined);
|
|
207
|
+
const outputIdxs = outputNames.map((n) => ir.varIndex.get(n)).filter((i) => i !== undefined);
|
|
208
|
+
const hasArgs = inputIdxs.length === inputNames.length && outputIdxs.length === outputNames.length && (inputIdxs.length > 0 || outputIdxs.length > 0);
|
|
209
|
+
// Inline every reachable exec-node body directly into tick()/init(). No per-node WASM functions.
|
|
210
|
+
// This is the single biggest perf win on the Mandelbrot inner loop: AS/Binaryen will happily
|
|
211
|
+
// generate one fat function with all the locals it needs, V8 register-allocates them, and there's
|
|
212
|
+
// exactly one host↔WASM crossing per tick.
|
|
213
|
+
const tickIdxs = ir.entriesByType.get('Tick') ?? [];
|
|
214
|
+
const initIdxs = ir.entriesByType.get('Init') ?? [];
|
|
215
|
+
const inlineEntry = (i) => emitNodeBody(ir, ir.nodes[i], new Set([i]), 0);
|
|
216
|
+
const tickBody = tickIdxs.map(inlineEntry).join('\n ') || '// no Tick entries';
|
|
217
|
+
const initBody = initIdxs.map(inlineEntry).join('\n ') || '// no Init entries';
|
|
218
|
+
// Storage strategy: persistent vars live in WASM globals `g0…gN`. At the top of tick()/init()
|
|
219
|
+
// we COPY them into function-local `v0…vN` (which the emitter references everywhere), run the
|
|
220
|
+
// body on locals, then write back. Locals are guaranteed register-allocated by Binaryen+V8;
|
|
221
|
+
// raw globals can spill in hot loops. Verified: doubled Mandelbrot throughput on the inner loop.
|
|
222
|
+
const globalsDecl = Array.from({ length: varCount }, (_, i) => `let g${i}: f64 = 0;`).join('\n');
|
|
223
|
+
const loadLocals = Array.from({ length: varCount }, (_, i) => `let v${i}: f64 = g${i};`).join('\n ');
|
|
224
|
+
const storeBack = Array.from({ length: varCount }, (_, i) => `g${i} = v${i};`).join('\n ');
|
|
225
|
+
const getCases = Array.from({ length: varCount }, (_, i) => ` case ${i}: return g${i};`).join('\n');
|
|
226
|
+
const setCases = Array.from({ length: varCount }, (_, i) => ` case ${i}: g${i} = value; break;`).join('\n');
|
|
227
|
+
// Optional `tickArgs(...)`: inputs come in as WASM locals (parameters), outputs returned directly.
|
|
228
|
+
// First output is the function's return value; additional outputs are written to subsequent
|
|
229
|
+
// globals (caller reads via getVar after). Bypasses set/getVar host trampolines entirely on the
|
|
230
|
+
// hot path — single host↔WASM crossing per call, params live in registers from the get-go.
|
|
231
|
+
let tickArgsFn = '';
|
|
232
|
+
if (hasArgs) {
|
|
233
|
+
const params = inputNames.map((n, i) => `${n}_in: f64`).concat(['']).join(', ').replace(/, $/, '');
|
|
234
|
+
// Pre-seed locals: persistent vars from globals (so SetVar/GetVar inside the body see them),
|
|
235
|
+
// input vars from parameters (override globals — fresh value per call).
|
|
236
|
+
const seedFromGlobals = Array.from({ length: varCount }, (_, i) => `let v${i}: f64 = g${i};`).join('\n ');
|
|
237
|
+
const seedFromArgs = inputIdxs.map((vi, k) => `v${vi} = ${inputNames[k]}_in;`).join('\n ');
|
|
238
|
+
const firstOut = outputIdxs[0];
|
|
239
|
+
const returnExpr = firstOut === undefined ? '0.0' : `v${firstOut}`;
|
|
240
|
+
const writeOtherOuts = outputIdxs.slice(1).map((vi) => `g${vi} = v${vi};`).join('\n ');
|
|
241
|
+
tickArgsFn = `
|
|
242
|
+
export function tickArgs(${params}): f64 {
|
|
243
|
+
${seedFromGlobals}
|
|
244
|
+
${seedFromArgs}
|
|
245
|
+
${seedLocals}
|
|
246
|
+
${tickBody}
|
|
247
|
+
${writeOtherOuts}
|
|
248
|
+
return ${returnExpr};
|
|
249
|
+
}
|
|
250
|
+
`;
|
|
251
|
+
}
|
|
252
|
+
return `// AUTOGENERATED — RtGraph → AssemblyScript. Do not hand-edit.
|
|
253
|
+
${globalsDecl}
|
|
254
|
+
|
|
255
|
+
export function tick(): void {
|
|
256
|
+
${loadLocals}
|
|
257
|
+
${seedLocals}
|
|
258
|
+
${tickBody}
|
|
259
|
+
${storeBack}
|
|
260
|
+
}
|
|
261
|
+
${tickArgsFn}
|
|
262
|
+
|
|
263
|
+
export function init(): void {
|
|
264
|
+
${loadLocals}
|
|
265
|
+
${seedLocals}
|
|
266
|
+
${initBody}
|
|
267
|
+
${storeBack}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
export function getVar(idx: i32): f64 {
|
|
271
|
+
switch (idx) {
|
|
272
|
+
${getCases}
|
|
273
|
+
default: return 0;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export function setVar(idx: i32, value: f64): void {
|
|
278
|
+
switch (idx) {
|
|
279
|
+
${setCases}
|
|
280
|
+
default: break;
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
export function varCount(): i32 {
|
|
285
|
+
return ${varCount};
|
|
286
|
+
}
|
|
287
|
+
`;
|
|
288
|
+
}
|
|
289
|
+
/** Stable lookup `name → slot` exposed so callers know which i32 to pass to getVar/setVar. */
|
|
290
|
+
export function varIndexOf(graph) {
|
|
291
|
+
return buildIR(graph).varIndex;
|
|
292
|
+
}
|
|
293
|
+
//# sourceMappingURL=emit.js.map
|
package/dist/emit.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"emit.js","sourceRoot":"","sources":["../src/emit.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.bench.d.ts","sourceRoot":"","sources":["../src/index.bench.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
// AS-WASM vs raw-WASM vs JS-codegen vs interp on numeric graphs. The runtime should match raw
|
|
2
|
+
// WASM (or beat it — Binaryen's optimiser is aggressive) and crush JS by 1-2 orders of magnitude.
|
|
3
|
+
import { bench, describe } from 'vitest';
|
|
4
|
+
import { Runtime, BUILTIN_PRIMITIVES } from '@xenolithengine/graph-plugin-runtime';
|
|
5
|
+
// Import the in-package codegen + wasm-codegen via the source paths — they're not exported.
|
|
6
|
+
import { codegen } from '../../plugin-runtime/src/vm/codegen.js';
|
|
7
|
+
import { wasmCodegen } from '../../plugin-runtime/src/vm/wasm-codegen.js';
|
|
8
|
+
import { compile } from './index.js';
|
|
9
|
+
const DEFS = [...BUILTIN_PRIMITIVES];
|
|
10
|
+
const ein = (id) => ({ id, kind: 'exec', direction: 'in', type: 'exec' });
|
|
11
|
+
const eout = (id) => ({ id, kind: 'exec', direction: 'out', type: 'exec' });
|
|
12
|
+
const din = (id) => ({ id, kind: 'data', direction: 'in', type: 'scalar' });
|
|
13
|
+
const dout = (id) => ({ id, kind: 'data', direction: 'out', type: 'scalar' });
|
|
14
|
+
const edge = (fn, fp, tn, tp) => ({ from: { node: fn, pin: fp }, to: { node: tn, pin: tp } });
|
|
15
|
+
function bigAdd(n) {
|
|
16
|
+
const nodes = [
|
|
17
|
+
{ id: 'tick', type: 'Tick', pins: [eout('out')] },
|
|
18
|
+
{ id: 'c0', type: 'Const', pins: [dout('out')], state: { value: 1 } },
|
|
19
|
+
];
|
|
20
|
+
const edges = [];
|
|
21
|
+
let prev = 'c0';
|
|
22
|
+
for (let i = 1; i < n; i++) {
|
|
23
|
+
const cId = `c${i}`, aId = `a${i}`;
|
|
24
|
+
nodes.push({ id: cId, type: 'Const', pins: [dout('out')], state: { value: i + 1 } });
|
|
25
|
+
nodes.push({ id: aId, type: 'Add', pins: [din('a'), din('b'), dout('out')] });
|
|
26
|
+
edges.push(edge(prev, 'out', aId, 'a'));
|
|
27
|
+
edges.push(edge(cId, 'out', aId, 'b'));
|
|
28
|
+
prev = aId;
|
|
29
|
+
}
|
|
30
|
+
nodes.push({ id: 'sv', type: 'SetVar', pins: [ein('in'), din('v'), eout('out')], state: { name: 'sum' } });
|
|
31
|
+
edges.push(edge('tick', 'out', 'sv', 'in'));
|
|
32
|
+
edges.push(edge(prev, 'out', 'sv', 'v'));
|
|
33
|
+
return { nodes, edges };
|
|
34
|
+
}
|
|
35
|
+
const G = bigAdd(200);
|
|
36
|
+
// Pre-compile all engines outside the bench loop. The async AS compile happens at module-load.
|
|
37
|
+
const rt = new Runtime(DEFS);
|
|
38
|
+
const cg = codegen(G, DEFS);
|
|
39
|
+
const wg = wasmCodegen(G, DEFS);
|
|
40
|
+
const ag = await compile(G, DEFS);
|
|
41
|
+
describe('200-node Add chain — interp vs JS codegen vs raw WASM vs AS WASM', () => {
|
|
42
|
+
bench('interp', () => { rt.tick(G); });
|
|
43
|
+
bench('codegen-js', () => { cg.tick(); });
|
|
44
|
+
bench('codegen-wasm', () => { wg.tick(); });
|
|
45
|
+
bench('codegen-as', () => { ag.tick(); });
|
|
46
|
+
});
|
|
47
|
+
//# sourceMappingURL=index.bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.bench.js","sourceRoot":"","sources":["../src/index.bench.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { RtGraph, NodeDef } from '@xenolithengine/graph-plugin-runtime';
|
|
2
|
+
export { canCompileToAS, emitASSource } from './emit.js';
|
|
3
|
+
export { compileAS } from './compile.js';
|
|
4
|
+
export interface ASWasmGraph {
|
|
5
|
+
tick(entryType?: string): void;
|
|
6
|
+
getVar(name: string): number | undefined;
|
|
7
|
+
setVar(name: string, value: number): void;
|
|
8
|
+
reset(): void;
|
|
9
|
+
/** Present only when the graph declares `meta.inputs`/`meta.outputs`. Pass inputs in order, get
|
|
10
|
+
* the first declared output back. ~2× faster on hot loops vs set+tick+get (no host trampolines
|
|
11
|
+
* per var). */
|
|
12
|
+
tickArgs?: (...args: number[]) => number;
|
|
13
|
+
/** Raw module bytes — exposed for sanity checks + the bench's "we actually shipped WASM" line. */
|
|
14
|
+
readonly wasm: Uint8Array;
|
|
15
|
+
/** Generated AS source — handy when something looks wrong and you want to inspect the emit. */
|
|
16
|
+
readonly source: string;
|
|
17
|
+
}
|
|
18
|
+
export declare function compile(graph: RtGraph, defs: ReadonlyArray<NodeDef>): Promise<ASWasmGraph>;
|
|
19
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,sCAAsC,CAAA;AAI5E,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,MAAM,WAAW,WAAW;IAC1B,IAAI,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IAC9B,MAAM,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAA;IACxC,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACzC,KAAK,IAAI,IAAI,CAAA;IACb;;oBAEgB;IAChB,QAAQ,CAAC,EAAE,CAAC,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,MAAM,CAAA;IACxC,kGAAkG;IAClG,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;IACzB,+FAA+F;IAC/F,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;CACxB;AAED,wBAAsB,OAAO,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,aAAa,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,WAAW,CAAC,CA8ChG"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
// Public entry: `compile(graph, defs)` → an async-instantiated WASM module that runs the graph.
|
|
2
|
+
// Same call surface as the JS codegen so a host can swap engines per-graph.
|
|
3
|
+
import { emitASSource, varIndexOf, canCompileToAS } from './emit.js';
|
|
4
|
+
import { compileAS } from './compile.js';
|
|
5
|
+
export { canCompileToAS, emitASSource } from './emit.js';
|
|
6
|
+
export { compileAS } from './compile.js';
|
|
7
|
+
export async function compile(graph, defs) {
|
|
8
|
+
if (!canCompileToAS(graph))
|
|
9
|
+
throw new Error('compile: graph uses non-numeric primitives');
|
|
10
|
+
const source = emitASSource(graph, defs);
|
|
11
|
+
const { wasm } = await compileAS(source);
|
|
12
|
+
const mod = new WebAssembly.Module(wasm);
|
|
13
|
+
const instance = new WebAssembly.Instance(mod, {
|
|
14
|
+
// AS abort handler — fires on `unreachable` (assertions, OOB). For now just throw.
|
|
15
|
+
env: {
|
|
16
|
+
abort: (_msg, _file, line, col) => {
|
|
17
|
+
throw new Error(`AS abort at ${line}:${col}`);
|
|
18
|
+
},
|
|
19
|
+
},
|
|
20
|
+
});
|
|
21
|
+
const exports = instance.exports;
|
|
22
|
+
const varIndex = varIndexOf(graph);
|
|
23
|
+
const tickArgsExport = exports.tickArgs;
|
|
24
|
+
return {
|
|
25
|
+
wasm, source,
|
|
26
|
+
tick(entryType = 'Tick') {
|
|
27
|
+
if (entryType === 'Init')
|
|
28
|
+
exports.init();
|
|
29
|
+
else
|
|
30
|
+
exports.tick();
|
|
31
|
+
},
|
|
32
|
+
// Forward `tickArgsExport` directly — no `(...args) => fn(...args)` wrapper. Variadic
|
|
33
|
+
// forwarding allocates an args array per call which dominates the 100-iter Mandelbrot cost.
|
|
34
|
+
...(tickArgsExport ? { tickArgs: tickArgsExport } : {}),
|
|
35
|
+
getVar(name) {
|
|
36
|
+
const vi = varIndex.get(name);
|
|
37
|
+
if (vi === undefined)
|
|
38
|
+
return undefined;
|
|
39
|
+
return exports.getVar(vi);
|
|
40
|
+
},
|
|
41
|
+
setVar(name, value) {
|
|
42
|
+
let vi = varIndex.get(name);
|
|
43
|
+
if (vi === undefined) {
|
|
44
|
+
vi = varIndex.size;
|
|
45
|
+
varIndex.set(name, vi);
|
|
46
|
+
}
|
|
47
|
+
exports.setVar(vi, value);
|
|
48
|
+
},
|
|
49
|
+
reset() {
|
|
50
|
+
const count = exports.varCount();
|
|
51
|
+
for (let i = 0; i < count; i++)
|
|
52
|
+
exports.setVar(i, 0);
|
|
53
|
+
},
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,gGAAgG;AAChG,4EAA4E;AAG5E,OAAO,EAAE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,WAAW,CAAA;AACpE,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAExC,OAAO,EAAE,cAAc,EAAE,YAAY,EAAE,MAAM,WAAW,CAAA;AACxD,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAiBxC,MAAM,CAAC,KAAK,UAAU,OAAO,CAAC,KAAc,EAAE,IAA4B;IACxE,IAAI,CAAC,cAAc,CAAC,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IACzF,MAAM,MAAM,GAAG,YAAY,CAAC,KAAK,EAAE,IAAI,CAAC,CAAA;IACxC,MAAM,EAAE,IAAI,EAAE,GAAG,MAAM,SAAS,CAAC,MAAM,CAAC,CAAA;IACxC,MAAM,GAAG,GAAG,IAAI,WAAW,CAAC,MAAM,CAAC,IAA+B,CAAC,CAAA;IACnE,MAAM,QAAQ,GAAG,IAAI,WAAW,CAAC,QAAQ,CAAC,GAAG,EAAE;QAC7C,mFAAmF;QACnF,GAAG,EAAE;YACH,KAAK,EAAE,CAAC,IAAY,EAAE,KAAa,EAAE,IAAY,EAAE,GAAW,EAAE,EAAE;gBAChE,MAAM,IAAI,KAAK,CAAC,eAAe,IAAI,IAAI,GAAG,EAAE,CAAC,CAAA;YAC/C,CAAC;SACF;KACF,CAAC,CAAA;IACF,MAAM,OAAO,GAAG,QAAQ,CAAC,OAOxB,CAAA;IACD,MAAM,QAAQ,GAAG,UAAU,CAAC,KAAK,CAAC,CAAA;IAClC,MAAM,cAAc,GAAG,OAAO,CAAC,QAAQ,CAAA;IAEvC,OAAO;QACL,IAAI,EAAE,MAAM;QACZ,IAAI,CAAC,SAAS,GAAG,MAAM;YACrB,IAAI,SAAS,KAAK,MAAM;gBAAE,OAAO,CAAC,IAAI,EAAE,CAAC;;gBAAM,OAAO,CAAC,IAAI,EAAE,CAAA;QAC/D,CAAC;QACD,sFAAsF;QACtF,4FAA4F;QAC5F,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,cAAc,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACvD,MAAM,CAAC,IAAI;YACT,MAAM,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;YAAC,IAAI,EAAE,KAAK,SAAS;gBAAE,OAAO,SAAS,CAAA;YACrE,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC,CAAA;QAC3B,CAAC;QACD,MAAM,CAAC,IAAI,EAAE,KAAK;YAChB,IAAI,EAAE,GAAG,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;YAC3B,IAAI,EAAE,KAAK,SAAS,EAAE,CAAC;gBAAC,EAAE,GAAG,QAAQ,CAAC,IAAI,CAAC;gBAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,EAAE,EAAE,CAAC,CAAA;YAAC,CAAC;YACpE,OAAO,CAAC,MAAM,CAAC,EAAE,EAAE,KAAK,CAAC,CAAA;QAC3B,CAAC;QACD,KAAK;YACH,MAAM,KAAK,GAAG,OAAO,CAAC,QAAQ,EAAE,CAAA;YAChC,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE;gBAAE,OAAO,CAAC,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA;QACtD,CAAC;KACF,CAAA;AACH,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandelbrot.bench.d.ts","sourceRoot":"","sources":["../src/mandelbrot.bench.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
// Mandelbrot per-pixel throughput — the headline numbers. Each engine renders the same pixel
|
|
2
|
+
// (a point inside the set so all 100 iterations actually execute), repeated. ops/s = pixels/s.
|
|
3
|
+
import { bench, describe } from 'vitest';
|
|
4
|
+
import { Runtime, BUILTIN_PRIMITIVES, mandelbrotPixelGraph, mandelbrotPixelReference } from '@xenolithengine/graph-plugin-runtime';
|
|
5
|
+
import { codegen } from '../../plugin-runtime/src/vm/codegen.js';
|
|
6
|
+
import { compile } from './index.js';
|
|
7
|
+
const DEFS = [...BUILTIN_PRIMITIVES];
|
|
8
|
+
const MAX_ITER = 100;
|
|
9
|
+
// Origin is INSIDE the set → runs the full MAX_ITER per call. Worst case for the engine,
|
|
10
|
+
// fairest comparison (no early-exit shortcuts).
|
|
11
|
+
const CX = 0.0, CY = 0.0;
|
|
12
|
+
const G = mandelbrotPixelGraph();
|
|
13
|
+
const rt = new Runtime(DEFS);
|
|
14
|
+
const cg = codegen(G, DEFS);
|
|
15
|
+
const ag = await compile(G, DEFS);
|
|
16
|
+
// Seed inputs once. The bench calls tick() repeatedly without re-setting vars (zx/zy/iter are
|
|
17
|
+
// re-zeroed each tick by the graph's init phase, so the result is deterministic).
|
|
18
|
+
for (const e of [rt, cg, ag]) {
|
|
19
|
+
e.setVar('cx', CX);
|
|
20
|
+
e.setVar('cy', CY);
|
|
21
|
+
e.setVar('max_iter', MAX_ITER);
|
|
22
|
+
}
|
|
23
|
+
const tickArgs = ag.tickArgs; // mandelbrot graph declares meta.inputs/outputs so it's defined
|
|
24
|
+
// Pre-computed unrolled pixel coords inside the period-2 bulb (so all MAX_ITER iters run, no
|
|
25
|
+
// early-escape shortcut). Using Math.random()-ish jitter prevents V8 from constant-folding the
|
|
26
|
+
// reference call site — otherwise the inlined `mandelbrotPixelReference(0, 0, 100)` becomes a
|
|
27
|
+
// no-op return after JIT and the comparison is meaningless.
|
|
28
|
+
const COORDS = Array.from({ length: 256 }, (_, i) => {
|
|
29
|
+
const t = (i / 256) * 0.001;
|
|
30
|
+
return { cx: -1.0 + t, cy: t * 0.5 };
|
|
31
|
+
});
|
|
32
|
+
let _idx = 0;
|
|
33
|
+
const nextPixel = () => { const p = COORDS[_idx++ & 255]; return p; };
|
|
34
|
+
// Sink to defeat dead-store elimination on the reference branch.
|
|
35
|
+
let _sink = 0;
|
|
36
|
+
describe(`Mandelbrot per-pixel (period-2 bulb, ${MAX_ITER} iters) — pixels/sec`, () => {
|
|
37
|
+
bench('reference (plain JS, no graph)', () => { const p = nextPixel(); _sink += mandelbrotPixelReference(p.cx, p.cy, MAX_ITER); });
|
|
38
|
+
bench('interp', () => { const p = nextPixel(); rt.setVar('cx', p.cx); rt.setVar('cy', p.cy); rt.tick(G); _sink += rt.getVar('iter'); });
|
|
39
|
+
bench('codegen-js', () => { const p = nextPixel(); cg.setVar('cx', p.cx); cg.setVar('cy', p.cy); cg.tick(); _sink += cg.getVar('iter'); });
|
|
40
|
+
bench('codegen-as-wasm (tick + vars)', () => { const p = nextPixel(); ag.setVar('cx', p.cx); ag.setVar('cy', p.cy); ag.tick(); _sink += ag.getVar('iter'); });
|
|
41
|
+
bench('codegen-as-wasm (tickArgs)', () => { const p = nextPixel(); _sink += tickArgs(p.cx, p.cy, MAX_ITER); });
|
|
42
|
+
});
|
|
43
|
+
//# sourceMappingURL=mandelbrot.bench.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mandelbrot.bench.js","sourceRoot":"","sources":["../src/mandelbrot.bench.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@xenolithengine/graph-runtime-as",
|
|
3
|
+
"version": "0.7.0-beta.0",
|
|
4
|
+
"description": "AssemblyScript-based WASM code generator for the XenolithGraph Blueprint runtime. Emits AS source from an RtGraph + NodeDef set, compiles to a WebAssembly module via asc, exposes the same call surface as the JS codegen.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"module": "./dist/index.js",
|
|
9
|
+
"types": "./dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js"
|
|
14
|
+
}
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md"
|
|
19
|
+
],
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@xenolithengine/graph-plugin-runtime": "0.7.0-beta.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"assemblyscript": "^0.28.0",
|
|
26
|
+
"typescript": "^5.7.0",
|
|
27
|
+
"vitest": "^3.0.0"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"assemblyscript",
|
|
31
|
+
"wasm",
|
|
32
|
+
"webassembly",
|
|
33
|
+
"node-graph-runtime",
|
|
34
|
+
"codegen"
|
|
35
|
+
],
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
},
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "git+https://github.com/XenolithEngine/xenolith-graph.git",
|
|
42
|
+
"directory": "packages/runtime-as"
|
|
43
|
+
},
|
|
44
|
+
"homepage": "https://xenolithengine.github.io/xenolith-graph/",
|
|
45
|
+
"bugs": {
|
|
46
|
+
"url": "https://github.com/XenolithEngine/xenolith-graph/issues"
|
|
47
|
+
},
|
|
48
|
+
"author": "XenolithEngine",
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -b",
|
|
51
|
+
"dev": "tsc -b --watch",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"test:watch": "vitest",
|
|
54
|
+
"lint": "eslint src",
|
|
55
|
+
"typecheck": "tsc --noEmit"
|
|
56
|
+
}
|
|
57
|
+
}
|