wave-gpu 0.1.1
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 +33 -0
- package/dist/array.d.ts +22 -0
- package/dist/array.js +53 -0
- package/dist/array.js.map +1 -0
- package/dist/device.d.ts +9 -0
- package/dist/device.js +51 -0
- package/dist/device.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/kernel.d.ts +16 -0
- package/dist/kernel.js +38 -0
- package/dist/kernel.js.map +1 -0
- package/dist/runtime.d.ts +5 -0
- package/dist/runtime.js +115 -0
- package/dist/runtime.js.map +1 -0
- package/package.json +27 -0
- package/src/array.ts +65 -0
- package/src/device.ts +60 -0
- package/src/index.ts +11 -0
- package/src/kernel.ts +58 -0
- package/src/runtime.ts +146 -0
- package/tests/test_vector_add.ts +60 -0
- package/tsconfig.json +14 -0
package/README.md
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# WAVE TypeScript/JavaScript SDK
|
|
2
|
+
|
|
3
|
+
Write GPU kernels in TypeScript, run on any GPU.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install wave-gpu
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { device, kernel, array, zeros } from 'wave-gpu';
|
|
15
|
+
|
|
16
|
+
const dev = await device();
|
|
17
|
+
const vectorAdd = kernel(`
|
|
18
|
+
function vector_add(a: f32[], b: f32[], out: f32[], n: u32) {
|
|
19
|
+
const gid = thread_id();
|
|
20
|
+
if (gid < n) { out[gid] = a[gid] + b[gid]; }
|
|
21
|
+
}
|
|
22
|
+
`, 'typescript');
|
|
23
|
+
|
|
24
|
+
const a = array([1, 2, 3, 4]);
|
|
25
|
+
const b = array([5, 6, 7, 8]);
|
|
26
|
+
const out = zeros(4);
|
|
27
|
+
await vectorAdd.launch(dev, [a, b, out], [4], [1,1,1], [256,1,1]);
|
|
28
|
+
console.log(out.toArray());
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## License
|
|
32
|
+
|
|
33
|
+
Apache 2.0 - see [LICENSE](../../LICENSE)
|
package/dist/array.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Supported element data types. */
|
|
2
|
+
export type DType = "f32" | "u32" | "i32" | "f16" | "f64";
|
|
3
|
+
/** CPU-side array that can be passed to WAVE GPU kernels. */
|
|
4
|
+
export declare class WaveArray {
|
|
5
|
+
data: number[];
|
|
6
|
+
dtype: DType;
|
|
7
|
+
constructor(data: number[], dtype?: DType);
|
|
8
|
+
/** Return the array contents as a plain JavaScript array. */
|
|
9
|
+
toArray(): number[];
|
|
10
|
+
/** Number of elements. */
|
|
11
|
+
get length(): number;
|
|
12
|
+
/** Convert array data to a binary Buffer (little-endian f32). */
|
|
13
|
+
toBuffer(): Buffer;
|
|
14
|
+
/** Read data from a binary Buffer (little-endian f32). */
|
|
15
|
+
static fromBuffer(buf: Buffer, count: number, dtype?: DType): WaveArray;
|
|
16
|
+
}
|
|
17
|
+
/** Create a WaveArray from a JavaScript array. */
|
|
18
|
+
export declare function array(data: number[], dtype?: DType): WaveArray;
|
|
19
|
+
/** Create a zero-filled WaveArray. */
|
|
20
|
+
export declare function zeros(n: number, dtype?: DType): WaveArray;
|
|
21
|
+
/** Create a WaveArray filled with ones. */
|
|
22
|
+
export declare function ones(n: number, dtype?: DType): WaveArray;
|
package/dist/array.js
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Ojima Abraham
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.WaveArray = void 0;
|
|
6
|
+
exports.array = array;
|
|
7
|
+
exports.zeros = zeros;
|
|
8
|
+
exports.ones = ones;
|
|
9
|
+
/** CPU-side array that can be passed to WAVE GPU kernels. */
|
|
10
|
+
class WaveArray {
|
|
11
|
+
constructor(data, dtype = "f32") {
|
|
12
|
+
this.data = [...data];
|
|
13
|
+
this.dtype = dtype;
|
|
14
|
+
}
|
|
15
|
+
/** Return the array contents as a plain JavaScript array. */
|
|
16
|
+
toArray() {
|
|
17
|
+
return [...this.data];
|
|
18
|
+
}
|
|
19
|
+
/** Number of elements. */
|
|
20
|
+
get length() {
|
|
21
|
+
return this.data.length;
|
|
22
|
+
}
|
|
23
|
+
/** Convert array data to a binary Buffer (little-endian f32). */
|
|
24
|
+
toBuffer() {
|
|
25
|
+
const buf = Buffer.alloc(this.data.length * 4);
|
|
26
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
27
|
+
buf.writeFloatLE(this.data[i], i * 4);
|
|
28
|
+
}
|
|
29
|
+
return buf;
|
|
30
|
+
}
|
|
31
|
+
/** Read data from a binary Buffer (little-endian f32). */
|
|
32
|
+
static fromBuffer(buf, count, dtype = "f32") {
|
|
33
|
+
const data = [];
|
|
34
|
+
for (let i = 0; i < count; i++) {
|
|
35
|
+
data.push(buf.readFloatLE(i * 4));
|
|
36
|
+
}
|
|
37
|
+
return new WaveArray(data, dtype);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
exports.WaveArray = WaveArray;
|
|
41
|
+
/** Create a WaveArray from a JavaScript array. */
|
|
42
|
+
function array(data, dtype = "f32") {
|
|
43
|
+
return new WaveArray(data, dtype);
|
|
44
|
+
}
|
|
45
|
+
/** Create a zero-filled WaveArray. */
|
|
46
|
+
function zeros(n, dtype = "f32") {
|
|
47
|
+
return new WaveArray(new Array(n).fill(0), dtype);
|
|
48
|
+
}
|
|
49
|
+
/** Create a WaveArray filled with ones. */
|
|
50
|
+
function ones(n, dtype = "f32") {
|
|
51
|
+
return new WaveArray(new Array(n).fill(1), dtype);
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=array.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"array.js","sourceRoot":"","sources":["../src/array.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,sCAAsC;;;AAmDtC,sBAEC;AAGD,sBAEC;AAGD,oBAEC;AAxDD,6DAA6D;AAC7D,MAAa,SAAS;IAIpB,YAAY,IAAc,EAAE,QAAe,KAAK;QAC9C,IAAI,CAAC,IAAI,GAAG,CAAC,GAAG,IAAI,CAAC,CAAC;QACtB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC;IACrB,CAAC;IAED,6DAA6D;IAC7D,OAAO;QACL,OAAO,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;IACxB,CAAC;IAED,0BAA0B;IAC1B,IAAI,MAAM;QACR,OAAO,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,iEAAiE;IACjE,QAAQ;QACN,MAAM,GAAG,GAAG,MAAM,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC/C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC1C,GAAG,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC;QACxC,CAAC;QACD,OAAO,GAAG,CAAC;IACb,CAAC;IAED,0DAA0D;IAC1D,MAAM,CAAC,UAAU,CACf,GAAW,EACX,KAAa,EACb,QAAe,KAAK;QAEpB,MAAM,IAAI,GAAa,EAAE,CAAC;QAC1B,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,EAAE,CAAC,EAAE,EAAE,CAAC;YAC/B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;QACpC,CAAC;QACD,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;CACF;AAxCD,8BAwCC;AAED,kDAAkD;AAClD,SAAgB,KAAK,CAAC,IAAc,EAAE,QAAe,KAAK;IACxD,OAAO,IAAI,SAAS,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;AACpC,CAAC;AAED,sCAAsC;AACtC,SAAgB,KAAK,CAAC,CAAS,EAAE,QAAe,KAAK;IACnD,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC;AAED,2CAA2C;AAC3C,SAAgB,IAAI,CAAC,CAAS,EAAE,QAAe,KAAK;IAClD,OAAO,IAAI,SAAS,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;AACpD,CAAC"}
|
package/dist/device.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/** GPU vendor classification. */
|
|
2
|
+
export type GpuVendor = "apple" | "nvidia" | "amd" | "intel" | "emulator";
|
|
3
|
+
/** Detected GPU device information. */
|
|
4
|
+
export interface DeviceInfo {
|
|
5
|
+
vendor: GpuVendor;
|
|
6
|
+
name: string;
|
|
7
|
+
}
|
|
8
|
+
/** Detect the best available GPU device. */
|
|
9
|
+
export declare function device(): Promise<DeviceInfo>;
|
package/dist/device.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Ojima Abraham
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.device = device;
|
|
6
|
+
/// GPU detection for the WAVE JavaScript/TypeScript SDK.
|
|
7
|
+
const child_process_1 = require("child_process");
|
|
8
|
+
const os_1 = require("os");
|
|
9
|
+
/** Detect the best available GPU device. */
|
|
10
|
+
async function device() {
|
|
11
|
+
if ((0, os_1.platform)() === "darwin") {
|
|
12
|
+
return { vendor: "apple", name: "Apple GPU (Metal)" };
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
const out = (0, child_process_1.execSync)("nvidia-smi --query-gpu=name --format=csv,noheader", {
|
|
16
|
+
encoding: "utf-8",
|
|
17
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
18
|
+
});
|
|
19
|
+
const name = out.trim().split("\n")[0];
|
|
20
|
+
return { vendor: "nvidia", name: `${name} (CUDA)` };
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// nvidia-smi not available
|
|
24
|
+
}
|
|
25
|
+
try {
|
|
26
|
+
const out = (0, child_process_1.execSync)("rocminfo", {
|
|
27
|
+
encoding: "utf-8",
|
|
28
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
29
|
+
});
|
|
30
|
+
if (out.includes("gfx")) {
|
|
31
|
+
return { vendor: "amd", name: "AMD GPU (ROCm)" };
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
// rocminfo not available
|
|
36
|
+
}
|
|
37
|
+
try {
|
|
38
|
+
const out = (0, child_process_1.execSync)("sycl-ls", {
|
|
39
|
+
encoding: "utf-8",
|
|
40
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
41
|
+
});
|
|
42
|
+
if (out.includes("level_zero:gpu") || out.includes("opencl:gpu")) {
|
|
43
|
+
return { vendor: "intel", name: "Intel GPU (SYCL)" };
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
// sycl-ls not available
|
|
48
|
+
}
|
|
49
|
+
return { vendor: "emulator", name: "WAVE Emulator (no GPU)" };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=device.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device.js","sourceRoot":"","sources":["../src/device.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,sCAAsC;;AAiBtC,wBAyCC;AAxDD,yDAAyD;AAEzD,iDAAyC;AACzC,2BAA8B;AAW9B,4CAA4C;AACrC,KAAK,UAAU,MAAM;IAC1B,IAAI,IAAA,aAAQ,GAAE,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,mBAAmB,EAAE,CAAC;IACxD,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,wBAAQ,EAAC,mDAAmD,EAAE;YACxE,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,MAAM,IAAI,GAAG,GAAG,CAAC,IAAI,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC;QACvC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,IAAI,SAAS,EAAE,CAAC;IACtD,CAAC;IAAC,MAAM,CAAC;QACP,2BAA2B;IAC7B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,wBAAQ,EAAC,UAAU,EAAE;YAC/B,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;YACxB,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,gBAAgB,EAAE,CAAC;QACnD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,yBAAyB;IAC3B,CAAC;IAED,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAA,wBAAQ,EAAC,SAAS,EAAE;YAC9B,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;QACH,IAAI,GAAG,CAAC,QAAQ,CAAC,gBAAgB,CAAC,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC,EAAE,CAAC;YACjE,OAAO,EAAE,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,kBAAkB,EAAE,CAAC;QACvD,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,wBAAwB;IAC1B,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,IAAI,EAAE,wBAAwB,EAAE,CAAC;AAChE,CAAC"}
|
package/dist/index.d.ts
ADDED
package/dist/index.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Ojima Abraham
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.CompiledKernel = exports.kernel = exports.ones = exports.zeros = exports.array = exports.WaveArray = exports.device = void 0;
|
|
6
|
+
/// WAVE GPU SDK for JavaScript/TypeScript.
|
|
7
|
+
///
|
|
8
|
+
/// Write GPU kernels in TypeScript, run on any GPU. Supports Apple Metal,
|
|
9
|
+
/// NVIDIA CUDA, AMD ROCm, Intel SYCL, and a built-in emulator.
|
|
10
|
+
var device_1 = require("./device");
|
|
11
|
+
Object.defineProperty(exports, "device", { enumerable: true, get: function () { return device_1.device; } });
|
|
12
|
+
var array_1 = require("./array");
|
|
13
|
+
Object.defineProperty(exports, "WaveArray", { enumerable: true, get: function () { return array_1.WaveArray; } });
|
|
14
|
+
Object.defineProperty(exports, "array", { enumerable: true, get: function () { return array_1.array; } });
|
|
15
|
+
Object.defineProperty(exports, "zeros", { enumerable: true, get: function () { return array_1.zeros; } });
|
|
16
|
+
Object.defineProperty(exports, "ones", { enumerable: true, get: function () { return array_1.ones; } });
|
|
17
|
+
var kernel_1 = require("./kernel");
|
|
18
|
+
Object.defineProperty(exports, "kernel", { enumerable: true, get: function () { return kernel_1.kernel; } });
|
|
19
|
+
Object.defineProperty(exports, "CompiledKernel", { enumerable: true, get: function () { return kernel_1.CompiledKernel; } });
|
|
20
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,sCAAsC;;;AAEtC,2CAA2C;AAC3C,GAAG;AACH,0EAA0E;AAC1E,+DAA+D;AAE/D,mCAAyD;AAAhD,gGAAA,MAAM,OAAA;AACf,iCAA+D;AAAtD,kGAAA,SAAS,OAAA;AAAE,8FAAA,KAAK,OAAA;AAAE,8FAAA,KAAK,OAAA;AAAE,6FAAA,IAAI,OAAA;AACtC,mCAA4D;AAAnD,gGAAA,MAAM,OAAA;AAAE,wGAAA,cAAc,OAAA"}
|
package/dist/kernel.d.ts
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { DeviceInfo } from "./device";
|
|
2
|
+
import { WaveArray } from "./array";
|
|
3
|
+
/** Supported source languages. */
|
|
4
|
+
export type Language = "python" | "rust" | "cpp" | "typescript";
|
|
5
|
+
/** A compiled WAVE kernel ready for launch. */
|
|
6
|
+
export declare class CompiledKernel {
|
|
7
|
+
private source;
|
|
8
|
+
private language;
|
|
9
|
+
private wbin;
|
|
10
|
+
constructor(source: string, language?: Language);
|
|
11
|
+
private ensureCompiled;
|
|
12
|
+
/** Launch the kernel on the given device. */
|
|
13
|
+
launch(dev: DeviceInfo, buffers: WaveArray[], scalars: number[], grid?: [number, number, number], workgroup?: [number, number, number]): Promise<void>;
|
|
14
|
+
}
|
|
15
|
+
/** Create a compiled kernel from source code. */
|
|
16
|
+
export declare function kernel(source: string, language?: Language): CompiledKernel;
|
package/dist/kernel.js
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Ojima Abraham
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.CompiledKernel = void 0;
|
|
6
|
+
exports.kernel = kernel;
|
|
7
|
+
const runtime_1 = require("./runtime");
|
|
8
|
+
/** A compiled WAVE kernel ready for launch. */
|
|
9
|
+
class CompiledKernel {
|
|
10
|
+
constructor(source, language = "typescript") {
|
|
11
|
+
this.wbin = null;
|
|
12
|
+
this.source = source;
|
|
13
|
+
this.language = language;
|
|
14
|
+
}
|
|
15
|
+
async ensureCompiled() {
|
|
16
|
+
if (this.wbin === null) {
|
|
17
|
+
this.wbin = await (0, runtime_1.compileKernel)(this.source, this.language);
|
|
18
|
+
}
|
|
19
|
+
return this.wbin;
|
|
20
|
+
}
|
|
21
|
+
/** Launch the kernel on the given device. */
|
|
22
|
+
async launch(dev, buffers, scalars, grid = [1, 1, 1], workgroup = [256, 1, 1]) {
|
|
23
|
+
const wbin = await this.ensureCompiled();
|
|
24
|
+
if (dev.vendor === "emulator") {
|
|
25
|
+
await (0, runtime_1.launchEmulator)(wbin, buffers, scalars, grid, workgroup);
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw new Error(`Direct ${dev.vendor} launch not yet implemented in JS SDK. ` +
|
|
29
|
+
"Use the emulator or the Rust SDK.");
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
exports.CompiledKernel = CompiledKernel;
|
|
34
|
+
/** Create a compiled kernel from source code. */
|
|
35
|
+
function kernel(source, language = "typescript") {
|
|
36
|
+
return new CompiledKernel(source, language);
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=kernel.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"kernel.js","sourceRoot":"","sources":["../src/kernel.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,sCAAsC;;;AAmDtC,wBAKC;AAlDD,uCAA0D;AAK1D,+CAA+C;AAC/C,MAAa,cAAc;IAKzB,YAAY,MAAc,EAAE,WAAqB,YAAY;QAFrD,SAAI,GAAkB,IAAI,CAAC;QAGjC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC;QACrB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;IAC3B,CAAC;IAEO,KAAK,CAAC,cAAc;QAC1B,IAAI,IAAI,CAAC,IAAI,KAAK,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,IAAI,GAAG,MAAM,IAAA,uBAAa,EAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,QAAQ,CAAC,CAAC;QAC9D,CAAC;QACD,OAAO,IAAI,CAAC,IAAI,CAAC;IACnB,CAAC;IAED,6CAA6C;IAC7C,KAAK,CAAC,MAAM,CACV,GAAe,EACf,OAAoB,EACpB,OAAiB,EACjB,OAAiC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAC1C,YAAsC,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,CAAC;QAEjD,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,cAAc,EAAE,CAAC;QAEzC,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;YAC9B,MAAM,IAAA,wBAAc,EAAC,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,CAAC,CAAC;QAChE,CAAC;aAAM,CAAC;YACN,MAAM,IAAI,KAAK,CACb,UAAU,GAAG,CAAC,MAAM,yCAAyC;gBAC3D,mCAAmC,CACtC,CAAC;QACJ,CAAC;IACH,CAAC;CACF;AApCD,wCAoCC;AAED,iDAAiD;AACjD,SAAgB,MAAM,CACpB,MAAc,EACd,WAAqB,YAAY;IAEjC,OAAO,IAAI,cAAc,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;AAC9C,CAAC"}
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { WaveArray } from "./array";
|
|
2
|
+
/** Compile kernel source to WAVE binary (.wbin) bytes. */
|
|
3
|
+
export declare function compileKernel(source: string, language: string): Promise<Buffer>;
|
|
4
|
+
/** Launch a kernel on the WAVE emulator via subprocess. */
|
|
5
|
+
export declare function launchEmulator(wbin: Buffer, buffers: WaveArray[], scalars: number[], grid: [number, number, number], workgroup: [number, number, number]): Promise<void>;
|
package/dist/runtime.js
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Copyright 2026 Ojima Abraham
|
|
3
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
+
exports.compileKernel = compileKernel;
|
|
6
|
+
exports.launchEmulator = launchEmulator;
|
|
7
|
+
/// Full pipeline orchestration for the WAVE JavaScript/TypeScript SDK.
|
|
8
|
+
const child_process_1 = require("child_process");
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const os_1 = require("os");
|
|
11
|
+
const path_1 = require("path");
|
|
12
|
+
const EXT_MAP = {
|
|
13
|
+
python: ".py",
|
|
14
|
+
rust: ".rs",
|
|
15
|
+
cpp: ".cpp",
|
|
16
|
+
typescript: ".ts",
|
|
17
|
+
};
|
|
18
|
+
/** Find a WAVE tool binary, checking target/ directories first, then PATH. */
|
|
19
|
+
function findTool(name) {
|
|
20
|
+
const repoRoot = (0, path_1.resolve)(__dirname, "..", "..", "..", "..");
|
|
21
|
+
const candidates = [
|
|
22
|
+
(0, path_1.join)(repoRoot, "target", "release", name),
|
|
23
|
+
(0, path_1.join)(repoRoot, "target", "debug", name),
|
|
24
|
+
];
|
|
25
|
+
for (const path of candidates) {
|
|
26
|
+
if ((0, fs_1.existsSync)(path)) {
|
|
27
|
+
return path;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
return name;
|
|
31
|
+
}
|
|
32
|
+
/** Compile kernel source to WAVE binary (.wbin) bytes. */
|
|
33
|
+
async function compileKernel(source, language) {
|
|
34
|
+
const compiler = findTool("wave-compiler");
|
|
35
|
+
const ext = EXT_MAP[language] || ".py";
|
|
36
|
+
const dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "wave-"));
|
|
37
|
+
const srcPath = (0, path_1.join)(dir, `kernel${ext}`);
|
|
38
|
+
const wbinPath = (0, path_1.join)(dir, "kernel.wbin");
|
|
39
|
+
(0, fs_1.writeFileSync)(srcPath, source);
|
|
40
|
+
try {
|
|
41
|
+
(0, child_process_1.execSync)(`${compiler} ${srcPath} -o ${wbinPath} -l ${language}`, {
|
|
42
|
+
encoding: "utf-8",
|
|
43
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
catch (e) {
|
|
47
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
48
|
+
throw new Error(`Compilation failed: ${msg}`);
|
|
49
|
+
}
|
|
50
|
+
const wbin = (0, fs_1.readFileSync)(wbinPath);
|
|
51
|
+
try {
|
|
52
|
+
(0, fs_1.unlinkSync)(srcPath);
|
|
53
|
+
(0, fs_1.unlinkSync)(wbinPath);
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
// cleanup best-effort
|
|
57
|
+
}
|
|
58
|
+
return wbin;
|
|
59
|
+
}
|
|
60
|
+
/** Launch a kernel on the WAVE emulator via subprocess. */
|
|
61
|
+
async function launchEmulator(wbin, buffers, scalars, grid, workgroup) {
|
|
62
|
+
const emulator = findTool("wave-emu");
|
|
63
|
+
const dir = (0, fs_1.mkdtempSync)((0, path_1.join)((0, os_1.tmpdir)(), "wave-emu-"));
|
|
64
|
+
const wbinPath = (0, path_1.join)(dir, "kernel.wbin");
|
|
65
|
+
const memPath = (0, path_1.join)(dir, "memory.bin");
|
|
66
|
+
(0, fs_1.writeFileSync)(wbinPath, wbin);
|
|
67
|
+
const offsets = [];
|
|
68
|
+
const memParts = [];
|
|
69
|
+
let offset = 0;
|
|
70
|
+
for (const buf of buffers) {
|
|
71
|
+
offsets.push(offset);
|
|
72
|
+
const binBuf = buf.toBuffer();
|
|
73
|
+
memParts.push(binBuf);
|
|
74
|
+
offset += binBuf.length;
|
|
75
|
+
}
|
|
76
|
+
(0, fs_1.writeFileSync)(memPath, Buffer.concat(memParts));
|
|
77
|
+
let cmd = `${emulator} ${wbinPath}`;
|
|
78
|
+
cmd += ` --memory-file ${memPath}`;
|
|
79
|
+
cmd += ` --grid ${grid[0]},${grid[1]},${grid[2]}`;
|
|
80
|
+
cmd += ` --workgroup ${workgroup[0]},${workgroup[1]},${workgroup[2]}`;
|
|
81
|
+
for (let i = 0; i < offsets.length; i++) {
|
|
82
|
+
cmd += ` --reg ${i}=${offsets[i]}`;
|
|
83
|
+
}
|
|
84
|
+
for (let i = 0; i < scalars.length; i++) {
|
|
85
|
+
cmd += ` --reg ${buffers.length + i}=${scalars[i]}`;
|
|
86
|
+
}
|
|
87
|
+
try {
|
|
88
|
+
(0, child_process_1.execSync)(cmd, {
|
|
89
|
+
encoding: "utf-8",
|
|
90
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
catch (e) {
|
|
94
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
95
|
+
throw new Error(`Emulator execution failed: ${msg}`);
|
|
96
|
+
}
|
|
97
|
+
const resultMem = (0, fs_1.readFileSync)(memPath);
|
|
98
|
+
let readOffset = 0;
|
|
99
|
+
for (const buf of buffers) {
|
|
100
|
+
const size = buf.length * 4;
|
|
101
|
+
const chunk = resultMem.subarray(readOffset, readOffset + size);
|
|
102
|
+
for (let i = 0; i < buf.length; i++) {
|
|
103
|
+
buf.data[i] = chunk.readFloatLE(i * 4);
|
|
104
|
+
}
|
|
105
|
+
readOffset += size;
|
|
106
|
+
}
|
|
107
|
+
try {
|
|
108
|
+
(0, fs_1.unlinkSync)(wbinPath);
|
|
109
|
+
(0, fs_1.unlinkSync)(memPath);
|
|
110
|
+
}
|
|
111
|
+
catch {
|
|
112
|
+
// cleanup best-effort
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
//# sourceMappingURL=runtime.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runtime.js","sourceRoot":"","sources":["../src/runtime.ts"],"names":[],"mappings":";AAAA,+BAA+B;AAC/B,sCAAsC;;AAyCtC,sCAiCC;AAGD,wCAmEC;AA9ID,uEAAuE;AAEvE,iDAAyC;AACzC,2BAMY;AACZ,2BAA4B;AAC5B,+BAAqC;AAGrC,MAAM,OAAO,GAA2B;IACtC,MAAM,EAAE,KAAK;IACb,IAAI,EAAE,KAAK;IACX,GAAG,EAAE,MAAM;IACX,UAAU,EAAE,KAAK;CAClB,CAAC;AAEF,8EAA8E;AAC9E,SAAS,QAAQ,CAAC,IAAY;IAC5B,MAAM,QAAQ,GAAG,IAAA,cAAO,EAAC,SAAS,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;IAC5D,MAAM,UAAU,GAAG;QACjB,IAAA,WAAI,EAAC,QAAQ,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,CAAC;QACzC,IAAA,WAAI,EAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,CAAC;KACxC,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,UAAU,EAAE,CAAC;QAC9B,IAAI,IAAA,eAAU,EAAC,IAAI,CAAC,EAAE,CAAC;YACrB,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,0DAA0D;AACnD,KAAK,UAAU,aAAa,CACjC,MAAc,EACd,QAAgB;IAEhB,MAAM,QAAQ,GAAG,QAAQ,CAAC,eAAe,CAAC,CAAC;IAC3C,MAAM,GAAG,GAAG,OAAO,CAAC,QAAQ,CAAC,IAAI,KAAK,CAAC;IAEvC,MAAM,GAAG,GAAG,IAAA,gBAAW,EAAC,IAAA,WAAI,EAAC,IAAA,WAAM,GAAE,EAAE,OAAO,CAAC,CAAC,CAAC;IACjD,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,SAAS,GAAG,EAAE,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAE1C,IAAA,kBAAa,EAAC,OAAO,EAAE,MAAM,CAAC,CAAC;IAE/B,IAAI,CAAC;QACH,IAAA,wBAAQ,EAAC,GAAG,QAAQ,IAAI,OAAO,OAAO,QAAQ,OAAO,QAAQ,EAAE,EAAE;YAC/D,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,uBAAuB,GAAG,EAAE,CAAC,CAAC;IAChD,CAAC;IAED,MAAM,IAAI,GAAG,IAAA,iBAAY,EAAC,QAAQ,CAAC,CAAC;IAEpC,IAAI,CAAC;QACH,IAAA,eAAU,EAAC,OAAO,CAAC,CAAC;QACpB,IAAA,eAAU,EAAC,QAAQ,CAAC,CAAC;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,2DAA2D;AACpD,KAAK,UAAU,cAAc,CAClC,IAAY,EACZ,OAAoB,EACpB,OAAiB,EACjB,IAA8B,EAC9B,SAAmC;IAEnC,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,IAAA,gBAAW,EAAC,IAAA,WAAI,EAAC,IAAA,WAAM,GAAE,EAAE,WAAW,CAAC,CAAC,CAAC;IACrD,MAAM,QAAQ,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,aAAa,CAAC,CAAC;IAC1C,MAAM,OAAO,GAAG,IAAA,WAAI,EAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IAExC,IAAA,kBAAa,EAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAE9B,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,OAAO,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACrB,MAAM,MAAM,GAAG,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QACtB,MAAM,IAAI,MAAM,CAAC,MAAM,CAAC;IAC1B,CAAC;IAED,IAAA,kBAAa,EAAC,OAAO,EAAE,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;IAEhD,IAAI,GAAG,GAAG,GAAG,QAAQ,IAAI,QAAQ,EAAE,CAAC;IACpC,GAAG,IAAI,kBAAkB,OAAO,EAAE,CAAC;IACnC,GAAG,IAAI,WAAW,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;IAClD,GAAG,IAAI,gBAAgB,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IAEtE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,GAAG,IAAI,UAAU,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACrC,CAAC;IACD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACxC,GAAG,IAAI,UAAU,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,OAAO,CAAC,CAAC,CAAC,EAAE,CAAC;IACtD,CAAC;IAED,IAAI,CAAC;QACH,IAAA,wBAAQ,EAAC,GAAG,EAAE;YACZ,QAAQ,EAAE,OAAO;YACjB,KAAK,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC;SAChC,CAAC,CAAC;IACL,CAAC;IAAC,OAAO,CAAU,EAAE,CAAC;QACpB,MAAM,GAAG,GAAG,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;QACvD,MAAM,IAAI,KAAK,CAAC,8BAA8B,GAAG,EAAE,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,SAAS,GAAG,IAAA,iBAAY,EAAC,OAAO,CAAC,CAAC;IAExC,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC1B,MAAM,IAAI,GAAG,GAAG,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,UAAU,EAAE,UAAU,GAAG,IAAI,CAAC,CAAC;QAChE,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;QACzC,CAAC;QACD,UAAU,IAAI,IAAI,CAAC;IACrB,CAAC;IAED,IAAI,CAAC;QACH,IAAA,eAAU,EAAC,QAAQ,CAAC,CAAC;QACrB,IAAA,eAAU,EAAC,OAAO,CAAC,CAAC;IACtB,CAAC;IAAC,MAAM,CAAC;QACP,sBAAsB;IACxB,CAAC;AACH,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "wave-gpu",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"description": "Write GPU kernels in TypeScript, run on any GPU",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"license": "Apache-2.0",
|
|
8
|
+
"author": "Ojima Abraham",
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/Oabraham1/wave"
|
|
12
|
+
},
|
|
13
|
+
"homepage": "https://wave.ojima.me",
|
|
14
|
+
"keywords": ["gpu", "cuda", "metal", "hip", "sycl", "compute", "kernel"],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"build": "tsc",
|
|
17
|
+
"test": "node --experimental-vm-modules tests/test_vector_add.js"
|
|
18
|
+
},
|
|
19
|
+
"devDependencies": {
|
|
20
|
+
"@types/node": "^25.5.0",
|
|
21
|
+
"prettier": "^3.8.1",
|
|
22
|
+
"typescript": "^5.0.0"
|
|
23
|
+
},
|
|
24
|
+
"engines": {
|
|
25
|
+
"node": ">=18.0.0"
|
|
26
|
+
}
|
|
27
|
+
}
|
package/src/array.ts
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// Array types for the WAVE JavaScript/TypeScript SDK.
|
|
5
|
+
|
|
6
|
+
/** Supported element data types. */
|
|
7
|
+
export type DType = "f32" | "u32" | "i32" | "f16" | "f64";
|
|
8
|
+
|
|
9
|
+
/** CPU-side array that can be passed to WAVE GPU kernels. */
|
|
10
|
+
export class WaveArray {
|
|
11
|
+
data: number[];
|
|
12
|
+
dtype: DType;
|
|
13
|
+
|
|
14
|
+
constructor(data: number[], dtype: DType = "f32") {
|
|
15
|
+
this.data = [...data];
|
|
16
|
+
this.dtype = dtype;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Return the array contents as a plain JavaScript array. */
|
|
20
|
+
toArray(): number[] {
|
|
21
|
+
return [...this.data];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/** Number of elements. */
|
|
25
|
+
get length(): number {
|
|
26
|
+
return this.data.length;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** Convert array data to a binary Buffer (little-endian f32). */
|
|
30
|
+
toBuffer(): Buffer {
|
|
31
|
+
const buf = Buffer.alloc(this.data.length * 4);
|
|
32
|
+
for (let i = 0; i < this.data.length; i++) {
|
|
33
|
+
buf.writeFloatLE(this.data[i], i * 4);
|
|
34
|
+
}
|
|
35
|
+
return buf;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/** Read data from a binary Buffer (little-endian f32). */
|
|
39
|
+
static fromBuffer(
|
|
40
|
+
buf: Buffer,
|
|
41
|
+
count: number,
|
|
42
|
+
dtype: DType = "f32",
|
|
43
|
+
): WaveArray {
|
|
44
|
+
const data: number[] = [];
|
|
45
|
+
for (let i = 0; i < count; i++) {
|
|
46
|
+
data.push(buf.readFloatLE(i * 4));
|
|
47
|
+
}
|
|
48
|
+
return new WaveArray(data, dtype);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Create a WaveArray from a JavaScript array. */
|
|
53
|
+
export function array(data: number[], dtype: DType = "f32"): WaveArray {
|
|
54
|
+
return new WaveArray(data, dtype);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/** Create a zero-filled WaveArray. */
|
|
58
|
+
export function zeros(n: number, dtype: DType = "f32"): WaveArray {
|
|
59
|
+
return new WaveArray(new Array(n).fill(0), dtype);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/** Create a WaveArray filled with ones. */
|
|
63
|
+
export function ones(n: number, dtype: DType = "f32"): WaveArray {
|
|
64
|
+
return new WaveArray(new Array(n).fill(1), dtype);
|
|
65
|
+
}
|
package/src/device.ts
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// GPU detection for the WAVE JavaScript/TypeScript SDK.
|
|
5
|
+
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import { platform } from "os";
|
|
8
|
+
|
|
9
|
+
/** GPU vendor classification. */
|
|
10
|
+
export type GpuVendor = "apple" | "nvidia" | "amd" | "intel" | "emulator";
|
|
11
|
+
|
|
12
|
+
/** Detected GPU device information. */
|
|
13
|
+
export interface DeviceInfo {
|
|
14
|
+
vendor: GpuVendor;
|
|
15
|
+
name: string;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** Detect the best available GPU device. */
|
|
19
|
+
export async function device(): Promise<DeviceInfo> {
|
|
20
|
+
if (platform() === "darwin") {
|
|
21
|
+
return { vendor: "apple", name: "Apple GPU (Metal)" };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
try {
|
|
25
|
+
const out = execSync("nvidia-smi --query-gpu=name --format=csv,noheader", {
|
|
26
|
+
encoding: "utf-8",
|
|
27
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
28
|
+
});
|
|
29
|
+
const name = out.trim().split("\n")[0];
|
|
30
|
+
return { vendor: "nvidia", name: `${name} (CUDA)` };
|
|
31
|
+
} catch {
|
|
32
|
+
// nvidia-smi not available
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const out = execSync("rocminfo", {
|
|
37
|
+
encoding: "utf-8",
|
|
38
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
39
|
+
});
|
|
40
|
+
if (out.includes("gfx")) {
|
|
41
|
+
return { vendor: "amd", name: "AMD GPU (ROCm)" };
|
|
42
|
+
}
|
|
43
|
+
} catch {
|
|
44
|
+
// rocminfo not available
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try {
|
|
48
|
+
const out = execSync("sycl-ls", {
|
|
49
|
+
encoding: "utf-8",
|
|
50
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
51
|
+
});
|
|
52
|
+
if (out.includes("level_zero:gpu") || out.includes("opencl:gpu")) {
|
|
53
|
+
return { vendor: "intel", name: "Intel GPU (SYCL)" };
|
|
54
|
+
}
|
|
55
|
+
} catch {
|
|
56
|
+
// sycl-ls not available
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return { vendor: "emulator", name: "WAVE Emulator (no GPU)" };
|
|
60
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// WAVE GPU SDK for JavaScript/TypeScript.
|
|
5
|
+
///
|
|
6
|
+
/// Write GPU kernels in TypeScript, run on any GPU. Supports Apple Metal,
|
|
7
|
+
/// NVIDIA CUDA, AMD ROCm, Intel SYCL, and a built-in emulator.
|
|
8
|
+
|
|
9
|
+
export { device, DeviceInfo, GpuVendor } from "./device";
|
|
10
|
+
export { WaveArray, array, zeros, ones, DType } from "./array";
|
|
11
|
+
export { kernel, CompiledKernel, Language } from "./kernel";
|
package/src/kernel.ts
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// Kernel compilation and launch for the WAVE JavaScript/TypeScript SDK.
|
|
5
|
+
|
|
6
|
+
import { DeviceInfo } from "./device";
|
|
7
|
+
import { WaveArray } from "./array";
|
|
8
|
+
import { compileKernel, launchEmulator } from "./runtime";
|
|
9
|
+
|
|
10
|
+
/** Supported source languages. */
|
|
11
|
+
export type Language = "python" | "rust" | "cpp" | "typescript";
|
|
12
|
+
|
|
13
|
+
/** A compiled WAVE kernel ready for launch. */
|
|
14
|
+
export class CompiledKernel {
|
|
15
|
+
private source: string;
|
|
16
|
+
private language: Language;
|
|
17
|
+
private wbin: Buffer | null = null;
|
|
18
|
+
|
|
19
|
+
constructor(source: string, language: Language = "typescript") {
|
|
20
|
+
this.source = source;
|
|
21
|
+
this.language = language;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private async ensureCompiled(): Promise<Buffer> {
|
|
25
|
+
if (this.wbin === null) {
|
|
26
|
+
this.wbin = await compileKernel(this.source, this.language);
|
|
27
|
+
}
|
|
28
|
+
return this.wbin;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Launch the kernel on the given device. */
|
|
32
|
+
async launch(
|
|
33
|
+
dev: DeviceInfo,
|
|
34
|
+
buffers: WaveArray[],
|
|
35
|
+
scalars: number[],
|
|
36
|
+
grid: [number, number, number] = [1, 1, 1],
|
|
37
|
+
workgroup: [number, number, number] = [256, 1, 1],
|
|
38
|
+
): Promise<void> {
|
|
39
|
+
const wbin = await this.ensureCompiled();
|
|
40
|
+
|
|
41
|
+
if (dev.vendor === "emulator") {
|
|
42
|
+
await launchEmulator(wbin, buffers, scalars, grid, workgroup);
|
|
43
|
+
} else {
|
|
44
|
+
throw new Error(
|
|
45
|
+
`Direct ${dev.vendor} launch not yet implemented in JS SDK. ` +
|
|
46
|
+
"Use the emulator or the Rust SDK.",
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** Create a compiled kernel from source code. */
|
|
53
|
+
export function kernel(
|
|
54
|
+
source: string,
|
|
55
|
+
language: Language = "typescript",
|
|
56
|
+
): CompiledKernel {
|
|
57
|
+
return new CompiledKernel(source, language);
|
|
58
|
+
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// Full pipeline orchestration for the WAVE JavaScript/TypeScript SDK.
|
|
5
|
+
|
|
6
|
+
import { execSync } from "child_process";
|
|
7
|
+
import {
|
|
8
|
+
existsSync,
|
|
9
|
+
mkdtempSync,
|
|
10
|
+
readFileSync,
|
|
11
|
+
unlinkSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from "fs";
|
|
14
|
+
import { tmpdir } from "os";
|
|
15
|
+
import { join, resolve } from "path";
|
|
16
|
+
import { WaveArray } from "./array";
|
|
17
|
+
|
|
18
|
+
const EXT_MAP: Record<string, string> = {
|
|
19
|
+
python: ".py",
|
|
20
|
+
rust: ".rs",
|
|
21
|
+
cpp: ".cpp",
|
|
22
|
+
typescript: ".ts",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
/** Find a WAVE tool binary, checking target/ directories first, then PATH. */
|
|
26
|
+
function findTool(name: string): string {
|
|
27
|
+
const repoRoot = resolve(__dirname, "..", "..", "..", "..");
|
|
28
|
+
const candidates = [
|
|
29
|
+
join(repoRoot, "target", "release", name),
|
|
30
|
+
join(repoRoot, "target", "debug", name),
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
for (const path of candidates) {
|
|
34
|
+
if (existsSync(path)) {
|
|
35
|
+
return path;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return name;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Compile kernel source to WAVE binary (.wbin) bytes. */
|
|
43
|
+
export async function compileKernel(
|
|
44
|
+
source: string,
|
|
45
|
+
language: string,
|
|
46
|
+
): Promise<Buffer> {
|
|
47
|
+
const compiler = findTool("wave-compiler");
|
|
48
|
+
const ext = EXT_MAP[language] || ".py";
|
|
49
|
+
|
|
50
|
+
const dir = mkdtempSync(join(tmpdir(), "wave-"));
|
|
51
|
+
const srcPath = join(dir, `kernel${ext}`);
|
|
52
|
+
const wbinPath = join(dir, "kernel.wbin");
|
|
53
|
+
|
|
54
|
+
writeFileSync(srcPath, source);
|
|
55
|
+
|
|
56
|
+
try {
|
|
57
|
+
execSync(`${compiler} ${srcPath} -o ${wbinPath} -l ${language}`, {
|
|
58
|
+
encoding: "utf-8",
|
|
59
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
60
|
+
});
|
|
61
|
+
} catch (e: unknown) {
|
|
62
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
63
|
+
throw new Error(`Compilation failed: ${msg}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const wbin = readFileSync(wbinPath);
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
unlinkSync(srcPath);
|
|
70
|
+
unlinkSync(wbinPath);
|
|
71
|
+
} catch {
|
|
72
|
+
// cleanup best-effort
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return wbin;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** Launch a kernel on the WAVE emulator via subprocess. */
|
|
79
|
+
export async function launchEmulator(
|
|
80
|
+
wbin: Buffer,
|
|
81
|
+
buffers: WaveArray[],
|
|
82
|
+
scalars: number[],
|
|
83
|
+
grid: [number, number, number],
|
|
84
|
+
workgroup: [number, number, number],
|
|
85
|
+
): Promise<void> {
|
|
86
|
+
const emulator = findTool("wave-emu");
|
|
87
|
+
const dir = mkdtempSync(join(tmpdir(), "wave-emu-"));
|
|
88
|
+
const wbinPath = join(dir, "kernel.wbin");
|
|
89
|
+
const memPath = join(dir, "memory.bin");
|
|
90
|
+
|
|
91
|
+
writeFileSync(wbinPath, wbin);
|
|
92
|
+
|
|
93
|
+
const offsets: number[] = [];
|
|
94
|
+
const memParts: Buffer[] = [];
|
|
95
|
+
let offset = 0;
|
|
96
|
+
|
|
97
|
+
for (const buf of buffers) {
|
|
98
|
+
offsets.push(offset);
|
|
99
|
+
const binBuf = buf.toBuffer();
|
|
100
|
+
memParts.push(binBuf);
|
|
101
|
+
offset += binBuf.length;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
writeFileSync(memPath, Buffer.concat(memParts));
|
|
105
|
+
|
|
106
|
+
let cmd = `${emulator} ${wbinPath}`;
|
|
107
|
+
cmd += ` --memory-file ${memPath}`;
|
|
108
|
+
cmd += ` --grid ${grid[0]},${grid[1]},${grid[2]}`;
|
|
109
|
+
cmd += ` --workgroup ${workgroup[0]},${workgroup[1]},${workgroup[2]}`;
|
|
110
|
+
|
|
111
|
+
for (let i = 0; i < offsets.length; i++) {
|
|
112
|
+
cmd += ` --reg ${i}=${offsets[i]}`;
|
|
113
|
+
}
|
|
114
|
+
for (let i = 0; i < scalars.length; i++) {
|
|
115
|
+
cmd += ` --reg ${buffers.length + i}=${scalars[i]}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
try {
|
|
119
|
+
execSync(cmd, {
|
|
120
|
+
encoding: "utf-8",
|
|
121
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
122
|
+
});
|
|
123
|
+
} catch (e: unknown) {
|
|
124
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
125
|
+
throw new Error(`Emulator execution failed: ${msg}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const resultMem = readFileSync(memPath);
|
|
129
|
+
|
|
130
|
+
let readOffset = 0;
|
|
131
|
+
for (const buf of buffers) {
|
|
132
|
+
const size = buf.length * 4;
|
|
133
|
+
const chunk = resultMem.subarray(readOffset, readOffset + size);
|
|
134
|
+
for (let i = 0; i < buf.length; i++) {
|
|
135
|
+
buf.data[i] = chunk.readFloatLE(i * 4);
|
|
136
|
+
}
|
|
137
|
+
readOffset += size;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
unlinkSync(wbinPath);
|
|
142
|
+
unlinkSync(memPath);
|
|
143
|
+
} catch {
|
|
144
|
+
// cleanup best-effort
|
|
145
|
+
}
|
|
146
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// Copyright 2026 Ojima Abraham
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
|
|
4
|
+
/// End-to-end test for the WAVE JavaScript/TypeScript SDK.
|
|
5
|
+
|
|
6
|
+
import { device, array, zeros, WaveArray } from "../src/index";
|
|
7
|
+
|
|
8
|
+
async function testDevice(): Promise<void> {
|
|
9
|
+
const dev = await device();
|
|
10
|
+
console.assert(dev.vendor !== undefined, "vendor should be defined");
|
|
11
|
+
console.assert(dev.name.length > 0, "name should be non-empty");
|
|
12
|
+
console.log(` Device: ${dev.name}`);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function testArray(): void {
|
|
16
|
+
const a = array([1, 2, 3, 4]);
|
|
17
|
+
console.assert(a.length === 4, "length should be 4");
|
|
18
|
+
console.assert(
|
|
19
|
+
JSON.stringify(a.toArray()) === JSON.stringify([1, 2, 3, 4]),
|
|
20
|
+
"toArray should match",
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
const z = zeros(5);
|
|
24
|
+
console.assert(z.length === 5, "zeros length should be 5");
|
|
25
|
+
console.assert(
|
|
26
|
+
z.toArray().every((v) => v === 0),
|
|
27
|
+
"zeros should all be 0",
|
|
28
|
+
);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function testBufferRoundtrip(): void {
|
|
32
|
+
const a = array([1.5, 2.5, 3.5]);
|
|
33
|
+
const buf = a.toBuffer();
|
|
34
|
+
const b = WaveArray.fromBuffer(buf, 3);
|
|
35
|
+
const diff = a
|
|
36
|
+
.toArray()
|
|
37
|
+
.map((v, i) => Math.abs(v - b.toArray()[i]))
|
|
38
|
+
.reduce((s, d) => s + d, 0);
|
|
39
|
+
console.assert(diff < 0.001, "buffer roundtrip should preserve values");
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function main(): Promise<void> {
|
|
43
|
+
console.log("Running WAVE JS/TS SDK tests...");
|
|
44
|
+
|
|
45
|
+
await testDevice();
|
|
46
|
+
console.log(" [PASS] device detection");
|
|
47
|
+
|
|
48
|
+
testArray();
|
|
49
|
+
console.log(" [PASS] array creation");
|
|
50
|
+
|
|
51
|
+
testBufferRoundtrip();
|
|
52
|
+
console.log(" [PASS] buffer roundtrip");
|
|
53
|
+
|
|
54
|
+
console.log("All JS/TS SDK tests passed!");
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
main().catch((e) => {
|
|
58
|
+
console.error(e);
|
|
59
|
+
process.exit(1);
|
|
60
|
+
});
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"lib": ["ES2020"],
|
|
6
|
+
"outDir": "./dist",
|
|
7
|
+
"rootDir": "./src",
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"declaration": true,
|
|
11
|
+
"sourceMap": true
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*"]
|
|
14
|
+
}
|