|
| 1 | +// SPDX-License-Identifier: AGPL-3.0-or-later |
| 2 | + |
| 3 | +/** |
| 4 | + * Multi-target code generation for GPU-accelerated tensor operations. |
| 5 | + * |
| 6 | + * Generates hardware-specific parallel kernels from the fused tensor AST: |
| 7 | + * - WebGPU compute shaders (.wgsl) |
| 8 | + * - WASM SIMD-optimized modules |
| 9 | + * - CUDA/OpenCL device kernels |
| 10 | + * |
| 11 | + * The compilation target is selected via a CLI flag: |
| 12 | + * modelscript optimize --target=<js|wasm|c|cuda|webgpu> |
| 13 | + */ |
| 14 | + |
| 15 | +// ───────────────────────────────────────────────────────────────────── |
| 16 | +// Compilation Target |
| 17 | +// ───────────────────────────────────────────────────────────────────── |
| 18 | + |
| 19 | +export type CompilationTarget = "js" | "wasm" | "c" | "cuda" | "webgpu"; |
| 20 | + |
| 21 | +export interface GpuKernel { |
| 22 | + name: string; |
| 23 | + target: CompilationTarget; |
| 24 | + source: string; |
| 25 | + workgroupSize?: number; |
| 26 | + gridDim?: [number, number, number]; |
| 27 | +} |
| 28 | + |
| 29 | +// ───────────────────────────────────────────────────────────────────── |
| 30 | +// WebGPU (.wgsl) Code Generation |
| 31 | +// ───────────────────────────────────────────────────────────────────── |
| 32 | + |
| 33 | +export interface WgslKernelOpts { |
| 34 | + workgroupSize?: number; |
| 35 | +} |
| 36 | + |
| 37 | +/** |
| 38 | + * Generate a WebGPU compute shader for a fused elementwise tensor kernel. |
| 39 | + * |
| 40 | + * @param name Kernel function name |
| 41 | + * @param nInputs Number of input buffers |
| 42 | + * @param bodyGlsl WGSL body computing `out[i]` from `in0[i]`, `in1[i]`, etc. |
| 43 | + * @param opts Optional workgroup configuration |
| 44 | + */ |
| 45 | +export function generateWgslKernel(name: string, nInputs: number, bodyWgsl: string, opts?: WgslKernelOpts): GpuKernel { |
| 46 | + const wgSize = opts?.workgroupSize ?? 256; |
| 47 | + const lines: string[] = []; |
| 48 | + |
| 49 | + // Bindings: input buffers + output buffer + uniforms |
| 50 | + for (let i = 0; i < nInputs; i++) { |
| 51 | + lines.push(`@group(0) @binding(${i}) var<storage, read> in${i}: array<f32>;`); |
| 52 | + } |
| 53 | + lines.push(`@group(0) @binding(${nInputs}) var<storage, read_write> out: array<f32>;`); |
| 54 | + lines.push(`@group(0) @binding(${nInputs + 1}) var<uniform> n: u32;`); |
| 55 | + lines.push(``); |
| 56 | + lines.push(`@compute @workgroup_size(${wgSize})`); |
| 57 | + lines.push(`fn ${name}(@builtin(global_invocation_id) gid: vec3<u32>) {`); |
| 58 | + lines.push(` let i = gid.x;`); |
| 59 | + lines.push(` if (i >= n) { return; }`); |
| 60 | + lines.push(` ${bodyWgsl}`); |
| 61 | + lines.push(`}`); |
| 62 | + |
| 63 | + return { |
| 64 | + name, |
| 65 | + target: "webgpu", |
| 66 | + source: lines.join("\n"), |
| 67 | + workgroupSize: wgSize, |
| 68 | + }; |
| 69 | +} |
| 70 | + |
| 71 | +/** |
| 72 | + * Generate a WebGPU matrix multiplication compute shader. |
| 73 | + */ |
| 74 | +export function generateWgslMatmul(M: number, K: number, N: number): GpuKernel { |
| 75 | + const TILE = 16; |
| 76 | + const lines: string[] = []; |
| 77 | + |
| 78 | + lines.push(`@group(0) @binding(0) var<storage, read> A: array<f32>;`); |
| 79 | + lines.push(`@group(0) @binding(1) var<storage, read> B: array<f32>;`); |
| 80 | + lines.push(`@group(0) @binding(2) var<storage, read_write> C: array<f32>;`); |
| 81 | + lines.push(``); |
| 82 | + lines.push(`@compute @workgroup_size(${TILE}, ${TILE})`); |
| 83 | + lines.push(`fn matmul(@builtin(global_invocation_id) gid: vec3<u32>) {`); |
| 84 | + lines.push(` let row = gid.y;`); |
| 85 | + lines.push(` let col = gid.x;`); |
| 86 | + lines.push(` if (row >= ${M}u || col >= ${N}u) { return; }`); |
| 87 | + lines.push(` var sum: f32 = 0.0;`); |
| 88 | + lines.push(` for (var k: u32 = 0u; k < ${K}u; k = k + 1u) {`); |
| 89 | + lines.push(` sum = sum + A[row * ${K}u + k] * B[k * ${N}u + col];`); |
| 90 | + lines.push(` }`); |
| 91 | + lines.push(` C[row * ${N}u + col] = sum;`); |
| 92 | + lines.push(`}`); |
| 93 | + |
| 94 | + return { |
| 95 | + name: "matmul", |
| 96 | + target: "webgpu", |
| 97 | + source: lines.join("\n"), |
| 98 | + workgroupSize: TILE * TILE, |
| 99 | + gridDim: [Math.ceil(N / TILE), Math.ceil(M / TILE), 1], |
| 100 | + }; |
| 101 | +} |
| 102 | + |
| 103 | +// ───────────────────────────────────────────────────────────────────── |
| 104 | +// CUDA Code Generation |
| 105 | +// ───────────────────────────────────────────────────────────────────── |
| 106 | + |
| 107 | +/** |
| 108 | + * Generate a CUDA kernel for a fused elementwise tensor operation. |
| 109 | + */ |
| 110 | +export function generateCudaKernel(name: string, nInputs: number, bodyCuda: string): GpuKernel { |
| 111 | + const lines: string[] = []; |
| 112 | + lines.push(`#include <math.h>`); |
| 113 | + lines.push(``); |
| 114 | + |
| 115 | + // Kernel signature |
| 116 | + const params: string[] = []; |
| 117 | + for (let i = 0; i < nInputs; i++) { |
| 118 | + params.push(`const double* __restrict__ in${i}`); |
| 119 | + } |
| 120 | + params.push(`double* __restrict__ out`); |
| 121 | + params.push(`int n`); |
| 122 | + |
| 123 | + lines.push(`__global__ void ${name}(${params.join(", ")}) {`); |
| 124 | + lines.push(` int i = blockIdx.x * blockDim.x + threadIdx.x;`); |
| 125 | + lines.push(` if (i >= n) return;`); |
| 126 | + lines.push(` ${bodyCuda}`); |
| 127 | + lines.push(`}`); |
| 128 | + lines.push(``); |
| 129 | + |
| 130 | + // Host launcher |
| 131 | + lines.push(`void ${name}_launch(${params.join(", ")}) {`); |
| 132 | + lines.push(` int threads = 256;`); |
| 133 | + lines.push(` int blocks = (n + threads - 1) / threads;`); |
| 134 | + lines.push(` ${name}<<<blocks, threads>>>(${[...Array(nInputs).keys()].map((i) => `in${i}`).join(", ")}, out, n);`); |
| 135 | + lines.push(`}`); |
| 136 | + |
| 137 | + return { |
| 138 | + name, |
| 139 | + target: "cuda", |
| 140 | + source: lines.join("\n"), |
| 141 | + gridDim: [1, 1, 1], |
| 142 | + }; |
| 143 | +} |
| 144 | + |
| 145 | +/** |
| 146 | + * Generate a CUDA matrix multiplication kernel with tiling. |
| 147 | + */ |
| 148 | +export function generateCudaMatmul(M: number, K: number, N: number): GpuKernel { |
| 149 | + const TILE = 16; |
| 150 | + const lines: string[] = []; |
| 151 | + |
| 152 | + lines.push(`#define TILE_SIZE ${TILE}`); |
| 153 | + lines.push(``); |
| 154 | + lines.push(`__global__ void matmul_kernel(const double* A, const double* B, double* C, int M, int K, int N) {`); |
| 155 | + lines.push(` __shared__ double As[TILE_SIZE][TILE_SIZE];`); |
| 156 | + lines.push(` __shared__ double Bs[TILE_SIZE][TILE_SIZE];`); |
| 157 | + lines.push(` int row = blockIdx.y * TILE_SIZE + threadIdx.y;`); |
| 158 | + lines.push(` int col = blockIdx.x * TILE_SIZE + threadIdx.x;`); |
| 159 | + lines.push(` double sum = 0.0;`); |
| 160 | + lines.push(` for (int t = 0; t < (K + TILE_SIZE - 1) / TILE_SIZE; t++) {`); |
| 161 | + lines.push(` int ak = t * TILE_SIZE + threadIdx.x;`); |
| 162 | + lines.push(` int bk = t * TILE_SIZE + threadIdx.y;`); |
| 163 | + lines.push(` As[threadIdx.y][threadIdx.x] = (row < M && ak < K) ? A[row * K + ak] : 0.0;`); |
| 164 | + lines.push(` Bs[threadIdx.y][threadIdx.x] = (bk < K && col < N) ? B[bk * N + col] : 0.0;`); |
| 165 | + lines.push(` __syncthreads();`); |
| 166 | + lines.push(` for (int k = 0; k < TILE_SIZE; k++) sum += As[threadIdx.y][k] * Bs[k][threadIdx.x];`); |
| 167 | + lines.push(` __syncthreads();`); |
| 168 | + lines.push(` }`); |
| 169 | + lines.push(` if (row < M && col < N) C[row * N + col] = sum;`); |
| 170 | + lines.push(`}`); |
| 171 | + |
| 172 | + return { |
| 173 | + name: "matmul_kernel", |
| 174 | + target: "cuda", |
| 175 | + source: lines.join("\n"), |
| 176 | + gridDim: [Math.ceil(N / TILE), Math.ceil(M / TILE), 1], |
| 177 | + }; |
| 178 | +} |
| 179 | + |
| 180 | +// ───────────────────────────────────────────────────────────────────── |
| 181 | +// OpenCL Code Generation |
| 182 | +// ───────────────────────────────────────────────────────────────────── |
| 183 | + |
| 184 | +/** |
| 185 | + * Generate an OpenCL kernel for a fused elementwise tensor operation. |
| 186 | + */ |
| 187 | +export function generateOpenCLKernel(name: string, nInputs: number, bodyCL: string): GpuKernel { |
| 188 | + const lines: string[] = []; |
| 189 | + |
| 190 | + const params: string[] = []; |
| 191 | + for (let i = 0; i < nInputs; i++) { |
| 192 | + params.push(`__global const double* in${i}`); |
| 193 | + } |
| 194 | + params.push(`__global double* out`); |
| 195 | + params.push(`int n`); |
| 196 | + |
| 197 | + lines.push(`__kernel void ${name}(${params.join(", ")}) {`); |
| 198 | + lines.push(` int i = get_global_id(0);`); |
| 199 | + lines.push(` if (i >= n) return;`); |
| 200 | + lines.push(` ${bodyCL}`); |
| 201 | + lines.push(`}`); |
| 202 | + |
| 203 | + return { |
| 204 | + name, |
| 205 | + target: "c", // OpenCL is a C-target variant |
| 206 | + source: lines.join("\n"), |
| 207 | + }; |
| 208 | +} |
| 209 | + |
| 210 | +// ───────────────────────────────────────────────────────────────────── |
| 211 | +// FMU GPU Bundle |
| 212 | +// ───────────────────────────────────────────────────────────────────── |
| 213 | + |
| 214 | +export interface FmuGpuBundle { |
| 215 | + /** GPU kernel source files to include in the FMU archive. */ |
| 216 | + kernelSources: { filename: string; content: string }[]; |
| 217 | + /** Whether the FMU uses GPU acceleration. */ |
| 218 | + gpuAccelerated: boolean; |
| 219 | + /** Target GPU API. */ |
| 220 | + gpuApi: "cuda" | "opencl" | "webgpu"; |
| 221 | +} |
| 222 | + |
| 223 | +/** |
| 224 | + * Create an FMU GPU bundle from generated kernels. |
| 225 | + */ |
| 226 | +export function createFmuGpuBundle(kernels: GpuKernel[], gpuApi: "cuda" | "opencl" | "webgpu"): FmuGpuBundle { |
| 227 | + const ext = gpuApi === "cuda" ? ".cu" : gpuApi === "opencl" ? ".cl" : ".wgsl"; |
| 228 | + return { |
| 229 | + kernelSources: kernels.map((k) => ({ |
| 230 | + filename: `${k.name}${ext}`, |
| 231 | + content: k.source, |
| 232 | + })), |
| 233 | + gpuAccelerated: kernels.length > 0, |
| 234 | + gpuApi, |
| 235 | + }; |
| 236 | +} |
0 commit comments