GPU: gpu fn
● Advanced4 min
A gpu fn uses only what a GPU can do: 32-bit numbers and Bool, arithmetic, if, loops and other gpu fns. Anything else is a compile error that names it.
gpu fn brighten(x: Float32, gain: Float32) -> Float32 {
const k = 1.5
let v = x * k + gain
if v > 1.0 {
v = 1.0
}
v
}
fn main() {
print(brighten(0.2, 0.1))
const pixels: [Float32] = [0.1, 0.4, 0.9]
print(pixels.map(p => brighten(p, 0.1)))
}The program ran and printed:
kavach run rules.kav
0.4[0.25, 0.70000005, 1.0]
| Allowed | Not allowed | Why |
|---|---|---|
Float32, Int32, UInt32, Bool | Float, Int and other 64-bit types | GPUs compute 32-bit numbers fast: consumer NVIDIA GPUs run 64-bit floats at 1/64 of the speed, and WebGPU's shading language has none |
those types, and records and tuples of 1 to 8 of them, as parameters and result | text, lists, maps, pointers | each GPU thread holds a few fixed-size numbers; it can't make or grow a value in memory |
const, let, =, if/else, while, for over 0..n, return | for over a list, match, try, recursion | a GPU has no call stack, and nothing a thread could loop over but its own numbers |
+ - * / % and comparisons; ** on Float32 | print, debug, capabilities (files, network, AI) | thousands of threads run at once, cut off from the outside world |
Float32(n), Int32(x); abs, min, max, clamp; math.sqrt, floor, ceil, round, pow | a function that isn't a gpu fn, other std modules | the compiler can only translate what it has checked follows these rules |
Inside a gpu fn, a number with no other type is 32-bit: const k = 1.5 is a Float32. Called directly, as in brighten(0.2, 0.1), it runs on the CPU like any function. Each rule is checked when compiling:
gpu fn half(x: Float) -> Float {
x / 2.0
}The compiler refused the program:
kavach run float64.kav
error: `half` gives a `Float`, and a `gpu fn` works with `Float32`, `Int32`, `UInt32` and `Bool`, and records and tuples of 1 to 8 of them (spec 15.1)gpu fn twice(x: Float32) -> Float32 {
print(x)
x * 2.0
}The compiler refused the program:
kavach run print.kav
error: a `gpu fn` can't use `print`: it reaches outside the program (files, the network, the terminal, time, AI), and on the GPU a `gpu fn` runs alone, item by item, with only its own values (spec 15.1)Calling a plain function from a gpu fn is refused, and the message says whether that function could be one. This one could:
fn square(x: Float32) -> Float32 {
x * x
}
gpu fn area(r: Float32) -> Float32 {
3.14159 * square(r)
}The compiler refused the program:
kavach run plain-call.kav
error: a `gpu fn` can't use `square`: it isn't a `gpu fn`, but it fits the GPU's rules. Mark it `gpu fn square`; it still runs on the CPU when called there (spec 15.1)This one can't, and the message says why:
fn loud(x: Float32) -> Float32 {
print(x)
x
}
gpu fn shout(x: Float32) -> Float32 {
loud(x) * 2.0
}The compiler refused the program:
kavach run plain-print.kav
error: a `gpu fn` can't use `loud`: it isn't a `gpu fn`, and can't be one, since it uses `print`: it reaches outside the program (files, the network, the terminal, time, AI), and on the GPU a `gpu fn` runs alone, item by item, with only its own values (spec 15.1)gpu fn total(xs: [Float32]) -> Float32 {
let t: Float32 = 0.0
for x in xs {
t = t + x
}
t
}The compiler refused the program:
kavach run list.kav
error: `xs` is a `[Float32]`, and a `gpu fn` works with `Float32`, `Int32`, `UInt32` and `Bool`, and records and tuples of 1 to 8 of them (spec 15.1)So a gpu fn can never fail on the GPU in a way it couldn't on the CPU.
Limits and sizes
| What | Limit or size | Source |
|---|---|---|
Values in a gpu fn | Float32, Int32, UInt32 and Bool only | 15-gpu.md |
A record or tuple item | 1 to 8 fields of those types | 15-gpu.md |
Every limit in Kavach, on one page: Limits and Sizes.