Launching kernels — forall
A forall body is a data-parallel loop: every index runs independently. Where it runs is decided
by what it captures:
forall(bare) — routes automatically. If the body captures a device-resident binding, it launches on the GPU. If it captures only host data, it runs as a plain CPU loop. Same syntax, both worlds.gpu forall— explicitly a GPU launch. One GPU thread per index.
use system.gpu
use system.collections.array
fn main()
gpu let g = [1, 2, 3]
gpu var result = [0, 0, 0]
forall i in 0..3
result[i] = g[i] * 2
let h = result
print(f"{h[0]}")
print(f"{h[1]}")
print(f"{h[2]}")
Output: 246. The bare forall captures g and result, both
device-resident, so it launches on the GPU. Change both bindings to plain let/var
and the identical loop runs on the CPU.
What a kernel body may do
A forall kernel may capture device-resident bindings and index them, capture host
scalars (int, bool, f32 — passed as read-only uniforms), call
scalar functions and system.math intrinsics, and use arithmetic,
if, and while. It may not do I/O or allocate.
2D and 3D launches
Multi-dimensional domains launch with one index per axis — the compiler picks the workgroup shape (256×1×1 for 1D, 16×16×1 for 2D, 8×8×4 for 3D):
gpu forall i, j in 0..W, 0..H // 2D: one thread per (i, j)
img[j * W + i] = i + j
gpu forall i, j, k in 0..2, 0..2, 0..2 // 3D
voxels[i * 4 + j * 2 + k] = i + j + k
Bounds may be literals, consts, or runtime values — including a runtime start
(forall i in a..n). Runtime bounds travel to the kernel as uniforms. Threads past the bound
exit immediately, so over-dispatch is safe.
Safety checks at compile time
The compiler rejects kernels where two threads could write the same non-atomic element
(dst[i / 2] = … is an error. dst[i] = … is fine). Scatter writes — indices computed
from data — need atomics. It also validates kernel names against WGSL reserved words
and checks that integer division operands fit the device's 32-bit range.