Verified on develop (branch HEAD as of 2026-08-02), Chrome headless, Apple Metal-3 adapter.
Summary
mangleFunctionName() in src/backend/web-gpu/function-node.js rewrites a user function name to fn_<name> when it appears in the module-level reservedNames list. That list has 98 entries and is missing 64 WGSL reserved words that are legal JavaScript function names, so gpu.addFunction(function filter(...)) — and 63 others — emits the bare name into the generated WGSL and the shader fails to compile.
This is a papercut, not a correctness hazard: it fails loudly, because WebGPUKernel already checks getCompilationInfo() and throws with the line number and the generated WGSL attached. Nothing silently returns a wrong answer. The fix is to extend one array.
Reproduction
const gpu = new GPU({ mode: 'webgpu' });
gpu.addFunction(function self(x) { return x * 2; }); // any of the 64 below
const k = gpu.createKernel(function (a) {
return self(a[this.thread.x]);
}).setOutput([4]);
await k(new Float32Array([1, 2, 3, 4]));
Error compiling WGSL compute shader:
15:4 'self' is a reserved keyword
--- generated WGSL ---
A function named double in the same harness works, and a kernel local named self also works — locals are emitted as user_<name> (function-node.js:218, :667), so only function names reach mangleFunctionName unprefixed.
The 64, measured not guessed
Each was compiled individually as an addFunction name; these are the ones that failed specifically with a reserved-keyword diagnostic:
common compile consteval constexpr crate enum explicit extern filter final
friend from get goto impl implements inline interface layout macro match mod
module move mut namespace nil of operator package partition pass premerge priv
protected pub public readonly ref regardless resource restrict self set shared
sizeof smooth static subroutine template trait type union unless unsafe unsized
use using virtual volatile wgsl where writeonly yield
Several are names people really do use for helper functions — filter, get, set, type, match, from, module, interface, static, public, shared, ref.
A further 13 candidates (do, export, finally, import, …) are JavaScript reserved words too, so they cannot be function names in the first place and are correctly out of scope. 10 more compiled fine and are not WGSL-reserved.
Suggested fix
Add the list above to reservedNames in src/backend/web-gpu/function-node.js. The existing fn_ mangling then handles them, and the comment above mangleFunctionName ("mangle rather than reject") already describes the intended behaviour — the list simply hasn't caught up with the full WGSL reserved-word set.
Worth considering: mangling every user function name unconditionally (fn_<name> always) removes the need to track the spec's reserved list at all, the way user_<name> already does for variables. That is how the variable path avoids this entire class.
Context
Found while building a benchmark suite that runs the same workloads through gpu.js on WebGPU/WebGL2/WebGL/CPU and against hand-written baselines, on ~35 kernels. Happy to test a patch against that suite.
Verified on
develop(branch HEAD as of 2026-08-02), Chrome headless, Apple Metal-3 adapter.Summary
mangleFunctionName()insrc/backend/web-gpu/function-node.jsrewrites a user function name tofn_<name>when it appears in the module-levelreservedNameslist. That list has 98 entries and is missing 64 WGSL reserved words that are legal JavaScript function names, sogpu.addFunction(function filter(...))— and 63 others — emits the bare name into the generated WGSL and the shader fails to compile.This is a papercut, not a correctness hazard: it fails loudly, because
WebGPUKernelalready checksgetCompilationInfo()and throws with the line number and the generated WGSL attached. Nothing silently returns a wrong answer. The fix is to extend one array.Reproduction
A function named
doublein the same harness works, and a kernel local namedselfalso works — locals are emitted asuser_<name>(function-node.js:218,:667), so only function names reachmangleFunctionNameunprefixed.The 64, measured not guessed
Each was compiled individually as an
addFunctionname; these are the ones that failed specifically with a reserved-keyword diagnostic:Several are names people really do use for helper functions —
filter,get,set,type,match,from,module,interface,static,public,shared,ref.A further 13 candidates (
do,export,finally,import, …) are JavaScript reserved words too, so they cannot be function names in the first place and are correctly out of scope. 10 more compiled fine and are not WGSL-reserved.Suggested fix
Add the list above to
reservedNamesinsrc/backend/web-gpu/function-node.js. The existingfn_mangling then handles them, and the comment abovemangleFunctionName("mangle rather than reject") already describes the intended behaviour — the list simply hasn't caught up with the full WGSL reserved-word set.Worth considering: mangling every user function name unconditionally (
fn_<name>always) removes the need to track the spec's reserved list at all, the wayuser_<name>already does for variables. That is how the variable path avoids this entire class.Context
Found while building a benchmark suite that runs the same workloads through gpu.js on WebGPU/WebGL2/WebGL/CPU and against hand-written baselines, on ~35 kernels. Happy to test a patch against that suite.