docs / GPU Programming / Launching Kernels

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.