# gizhw:kernel

GPU kernels written once and printed for every GPU. A Kernel is a small structured program (values, variables, if, loop, buffers, workgroup memory, barriers) built by calls; `llvm` prints it as LLVM IR for AMD (compiled by comgr), `ptx` as PTX for NVIDIA. A value is used only inside the block that made it and the blocks within; what crosses blocks goes through a variable.

```gizlang
import * as Kernel from "gizhw:kernel";
```

## Types

### `KIns`

```gizlang
type KIns = { op: i64; t: i64; a: i64; b: i64; c: i64; e: i64; n: i64; k: f64; }
```

One instruction: `t` the type it makes, `a`, `b`, `c` operands (values, or a
buffer/param/shared index), `e` an element type, `n` an integer constant, `k` a float one.

- `op`: `i64`
- `t`: `i64`
- `a`: `i64`
- `b`: `i64`
- `c`: `i64`
- `e`: `i64`
- `n`: `i64`
- `k`: `f64`

### `Kernel`

```gizlang
type Kernel = { name: string; threads: i64; params: Vec<Param>; shared: Vec<Shared>; code: Vec<KIns>; counters: Vec<i64>; }
```

A kernel: one dimension of workgroups of `threads` threads.

- `name`: `string`
- `threads`: `i64`
- `params`: `Vec<M12.Param>`
- `shared`: `Vec<M12.Shared>`
- `code`: `Vec<M12.KIns>`
- `counters`: `Vec<i64>`

## Functions

### `valueOf`

```gizlang
function valueOf(elem: i64): i64
```

The value type an element loads as.

### `sizeOf`

```gizlang
function sizeOf(elem: i64): i64
```

Bytes of one element.

### `kernel`

```gizlang
function kernel(name: string, threads: i64): Kernel
```

An empty kernel NAME that runs THREADS threads in each workgroup.

### `regularSign`

```gizlang
function regularSign(j: i64): i64
```

-1 where an odd number of the base-4 digits of index j are 3, else 1 (hadamard's `regular`).

Effects: fail.

### `regularSource`

```gizlang
function regularSource(j: i64): i64
```

Index j with the two bits of each base-4 digit swapped (hadamard's `regular`; its own inverse).

Effects: fail.

### `hasTma`

```gizlang
function hasTma(kind: string, arch: string): bool
```

Whether the copy engine ops run on this GPU: the tensor and bulk loads with their barriers (mbarrierInit to
tensorLoad) and the grid dependency (awaitGrid, releaseGrid). NVIDIA sm_90 and later.

Effects: fail.

### `hasMmaPack`

```gizlang
function hasMmaPack(kind: string, arch: string): bool
```

Whether mmaPackA runs on this GPU: NVIDIA sm_80 and later, where a lane's accumulator elements are an A tile's
(the m16n8k16 fragments).

Effects: fail.

### `hasMmaWords`

```gizlang
function hasMmaWords(kind: string, arch: string): bool
```

Whether mmaWords runs on this GPU: AMD gfx11 (RDNA 3), where a lane holds all 16 elements of its row (A) or column
(B) l % 16.

Effects: fail.

### `sharedLimit`

```gizlang
function sharedLimit(kind: string, arch: string): i64
```

The workgroup memory a workgroup may use on a GPU of `kind` ("cuda", "hip", "metal") and `arch`, in bytes.

Effects: fail.

### `hasMma`

```gizlang
function hasMma(kind: string, arch: string): bool
```

Whether the printers emit mma for this GPU: AMD gfx11 and gfx120x (RDNA 3 and 4, WMMA in waves of 32), NVIDIA sm_80 and later, or any Apple GPU (simdgroup_matrix, 8x8 in a simdgroup of 32).

Effects: fail.

### `hasFp8Mma`

```gizlang
function hasFp8Mma(kind: string, arch: string): bool
```

Whether mma takes E4M3 pairs (E_E4M3X2) on this GPU: NVIDIA sm_89 and later (such a kernel is PTX 8.4 or later).

Effects: fail.

### `hasI8Mma`

```gizlang
function hasI8Mma(kind: string, arch: string): bool
```

Whether mma takes pairs of 8-bit integers (E_I8X2) on this GPU: NVIDIA sm_80 and later.

Effects: fail.

### `hasI4Mma`

```gizlang
function hasI4Mma(kind: string, arch: string): bool
```

Whether mma takes quads of 4-bit integers (E_I4X4) on this GPU: NVIDIA sm_80 to sm_89 (Hopper and later drop or
emulate it).

Effects: fail.

### `hasFp4Mma`

```gizlang
function hasFp4Mma(kind: string, arch: string): bool
```

Whether mmaScaled runs on this GPU: NVIDIA sm_120 and sm_121 (its PTX targets sm_120a, sm_121a).

### `ptxVersion`

```gizlang
function ptxVersion(arch: string): string
```

The PTX version a target needs.

Effects: fail.

## Methods

### `Kernel.buffer`

```gizlang
function Kernel.buffer(self: mut Kernel, name: string, elem: i64): i64
```

A buffer parameter of `elem` elements: its index for load and store. Buffers come before scalars.

Effects: fail.

### `Kernel.scalar`

```gizlang
function Kernel.scalar(self: mut Kernel, name: string): i64
```

A 64-bit integer parameter, as a value.

Effects: fail.

### `Kernel.workgroup`

```gizlang
function Kernel.workgroup(self: mut Kernel, elem: i64, count: i64): i64
```

Workgroup memory of `count` elements: its index for sload and sstore.

Effects: fail.

### `Kernel.float`

```gizlang
function Kernel.float(self: mut Kernel, x: f64): i64
```

The F32 constant X.

Effects: fail.

### `Kernel.double`

```gizlang
function Kernel.double(self: mut Kernel, x: f64): i64
```

The F64 constant X.

Effects: fail.

### `Kernel.int`

```gizlang
function Kernel.int(self: mut Kernel, x: i64): i64
```

The I32 constant X.

Effects: fail.

### `Kernel.long`

```gizlang
function Kernel.long(self: mut Kernel, x: i64): i64
```

The I64 constant X.

Effects: fail.

### `Kernel.truth`

```gizlang
function Kernel.truth(self: mut Kernel, x: bool): i64
```

The BOOL constant X.

Effects: fail.

### `Kernel.tid`

```gizlang
function Kernel.tid(self: mut Kernel): i64
```

The thread's index in its workgroup, and the workgroup's index (I64).

Effects: fail.

### `Kernel.gid`

```gizlang
function Kernel.gid(self: mut Kernel): i64
```

The workgroup's index in the grid (I64).

Effects: fail.

### `Kernel.global`

```gizlang
function Kernel.global(self: mut Kernel): i64
```

The global index: gid * threads + tid.

Effects: fail.

### `Kernel.add`

```gizlang
function Kernel.add(self: mut Kernel, a: i64, b: i64): i64
```

A + B (both of one type).

Effects: fail.

### `Kernel.sub`

```gizlang
function Kernel.sub(self: mut Kernel, a: i64, b: i64): i64
```

A - B (both of one type).

Effects: fail.

### `Kernel.mul`

```gizlang
function Kernel.mul(self: mut Kernel, a: i64, b: i64): i64
```

A * B (both of one type).

Effects: fail.

### `Kernel.div`

```gizlang
function Kernel.div(self: mut Kernel, a: i64, b: i64): i64
```

Division: IEEE rounded for floats on every GPU, truncating for signed integers.

Effects: fail.

### `Kernel.rem`

```gizlang
function Kernel.rem(self: mut Kernel, a: i64, b: i64): i64
```

The remainder of A / B, with the sign of A (signed integers and floats).

Effects: fail.

### `Kernel.udiv`

```gizlang
function Kernel.udiv(self: mut Kernel, a: i64, b: i64): i64
```

Unsigned integer division, remainder and compare. By a constant power of two they are a shift and a mask (a
64-bit division is a called routine on NVIDIA).

Effects: fail.

### `Kernel.urem`

```gizlang
function Kernel.urem(self: mut Kernel, a: i64, b: i64): i64
```

The remainder of A / B for unsigned integers.

Effects: fail.

### `Kernel.ult`

```gizlang
function Kernel.ult(self: mut Kernel, a: i64, b: i64): i64
```

A < B for unsigned integers.

Effects: fail.

### `Kernel.shuffleXor`

```gizlang
function Kernel.shuffleXor(self: mut Kernel, v: i64, mask: i64): i64
```

The value V of the lane whose index in the warp (32 lanes) is this lane's xor MASK (a butterfly exchange; F32 or
I32). Every lane of the warp must run it.

Effects: fail.

### `Kernel.warpSum`

```gizlang
function Kernel.warpSum(self: mut Kernel, v: i64): i64
```

The sum, the greatest and the least of V (F32 or I32) over the warp's 32 lanes, in every lane (butterfly exchanges).
Every lane of the warp must run them.

Effects: fail.

### `Kernel.warpMax`

```gizlang
function Kernel.warpMax(self: mut Kernel, v: i64): i64
```

The greatest of V over the warp's 32 lanes, in every lane. Every lane of the warp must run it.

Effects: fail.

### `Kernel.warpMin`

```gizlang
function Kernel.warpMin(self: mut Kernel, v: i64): i64
```

The least of V over the warp's 32 lanes, in every lane. Every lane of the warp must run it.

Effects: fail.

### `Kernel.groupArgmax`

```gizlang
function Kernel.groupArgmax(self: mut Kernel, v: i64, i: i64): Vec<i64>
```

The largest of the workgroup's values v (F32) with its index i (I32), the lowest index of equal ones, in every
thread: [value, index]. A NaN never wins. Every thread of the workgroup must run it.

Effects: fail.

### `Kernel.slicesSum`

```gizlang
function Kernel.slicesSum(self: mut Kernel, b: i64, i: i64, total: i64, slices: i64): i64
```

The sum of element i of the `slices` slices of `total` elements of b, added in order (split-K's partial sums).

Effects: fail.

### `Kernel.shuffle`

```gizlang
function Kernel.shuffle(self: mut Kernel, v: i64, from: i64): i64
```

The value V of lane `from` (I32 or I64, 0 to 31) of the warp (F32 or I32). Every lane of the warp must run it.

Effects: fail.

### `Kernel.hadamard`

```gizlang
function Kernel.hadamard(self: mut Kernel, v: Vec<i64>, lane: i64, block: i64, regular: bool = false): Vec<i64>
```

The Hadamard transform of each block of `block` values of a warp, divided by sqrt(block): lane l holds the n values
nl to nl + n - 1 (n = v.length a power of 2, block a power of 2 from n to 32 n). The transform keeps dot products
and spreads a block's outliers over it (rotation before quantization, as QuaRot). With `regular` (block a power of
4), each value first takes its regularSign: output j is then regularSign(j) times output regularSource(j) of the
regular Hadamard matrix (the Kronecker power of H4, whose row a has -1 at column 3 - a; ConvRot's), so a product
with weights rotated by that matrix takes them in that order and sign.

Effects: fail.

### `Kernel.regularIndex`

```gizlang
function Kernel.regularIndex(self: mut Kernel, j: i64, block: i64): Vec<i64>
```

regularSource of index j (I64) within its block of `block` (a power of 4), and whether its regularSign is -1
(BOOL): their GPU form.

Effects: fail.

### `Kernel.fma`

```gizlang
function Kernel.fma(self: mut Kernel, a: i64, b: i64, c: i64): i64
```

a * b + c rounded once.

Effects: fail.

### `Kernel.min`

```gizlang
function Kernel.min(self: mut Kernel, a: i64, b: i64): i64
```

Floats: the number when the other is NaN (maxnum); integers signed.

Effects: fail.

### `Kernel.max`

```gizlang
function Kernel.max(self: mut Kernel, a: i64, b: i64): i64
```

The larger of A and B. Floats: the number when the other is NaN (maxnum); integers signed.

Effects: fail.

### `Kernel.neg`

```gizlang
function Kernel.neg(self: mut Kernel, a: i64): i64
```

-A.

Effects: fail.

### `Kernel.abs`

```gizlang
function Kernel.abs(self: mut Kernel, a: i64): i64
```

The absolute value of A.

Effects: fail.

### `Kernel.sqrt`

```gizlang
function Kernel.sqrt(self: mut Kernel, a: i64): i64
```

The square root of A.

Effects: fail.

### `Kernel.rint`

```gizlang
function Kernel.rint(self: mut Kernel, a: i64): i64
```

Round to the nearest integer, ties to even.

Effects: fail.

### `Kernel.floor`

```gizlang
function Kernel.floor(self: mut Kernel, a: i64): i64
```

A rounded down to an integer.

Effects: fail.

### `Kernel.lt`

```gizlang
function Kernel.lt(self: mut Kernel, a: i64, b: i64): i64
```

A < B: ordered for floats (false with a NaN).

Effects: fail.

### `Kernel.le`

```gizlang
function Kernel.le(self: mut Kernel, a: i64, b: i64): i64
```

A <= B: ordered for floats (false with a NaN).

Effects: fail.

### `Kernel.gt`

```gizlang
function Kernel.gt(self: mut Kernel, a: i64, b: i64): i64
```

A > B: ordered for floats (false with a NaN).

Effects: fail.

### `Kernel.ge`

```gizlang
function Kernel.ge(self: mut Kernel, a: i64, b: i64): i64
```

A >= B: ordered for floats (false with a NaN).

Effects: fail.

### `Kernel.eq`

```gizlang
function Kernel.eq(self: mut Kernel, a: i64, b: i64): i64
```

Ordered float compares (false with a NaN); eq/ne also on integers and bools.

Effects: fail.

### `Kernel.ne`

```gizlang
function Kernel.ne(self: mut Kernel, a: i64, b: i64): i64
```

A != B; for floats, true with a NaN.

Effects: fail.

### `Kernel.isNan`

```gizlang
function Kernel.isNan(self: mut Kernel, a: i64): i64
```

Whether A is NaN.

Effects: fail.

### `Kernel.select`

```gizlang
function Kernel.select(self: mut Kernel, c: i64, a: i64, b: i64): i64
```

A where C is true, else B (A and B of one type).

Effects: fail.

### `Kernel.and`

```gizlang
function Kernel.and(self: mut Kernel, a: i64, b: i64): i64
```

A AND B: bitwise for integers, logical for bools.

Effects: fail.

### `Kernel.both`

```gizlang
function Kernel.both(self: mut Kernel, a: i64, b: i64): i64
```

Whether truths a and b both hold.

Effects: fail.

### `Kernel.or`

```gizlang
function Kernel.or(self: mut Kernel, a: i64, b: i64): i64
```

A OR B: bitwise for integers, logical for bools.

Effects: fail.

### `Kernel.xor`

```gizlang
function Kernel.xor(self: mut Kernel, a: i64, b: i64): i64
```

A XOR B: bitwise for integers, logical for bools.

Effects: fail.

### `Kernel.not`

```gizlang
function Kernel.not(self: mut Kernel, a: i64): i64
```

NOT A: bitwise for integers, logical for bools.

Effects: fail.

### `Kernel.shl`

```gizlang
function Kernel.shl(self: mut Kernel, a: i64, b: i64): i64
```

A shifted left by B bits.

Effects: fail.

### `Kernel.shr`

```gizlang
function Kernel.shr(self: mut Kernel, a: i64, b: i64): i64
```

Arithmetic and logical shifts right.

Effects: fail.

### `Kernel.ushr`

```gizlang
function Kernel.ushr(self: mut Kernel, a: i64, b: i64): i64
```

A shifted right by B bits, with zeros (logical).

Effects: fail.

### `Kernel.to`

```gizlang
function Kernel.to(self: mut Kernel, t: i64, a: i64): i64
```

A conversion: floats round to nearest even, floats to integers toward zero,
integers widen signed; BOOL to a number is 1 or 0.

Effects: fail.

### `Kernel.bits`

```gizlang
function Kernel.bits(self: mut Kernel, t: i64, a: i64): i64
```

The same bits as another type of the same size (F32/I32, F64/I64).

Effects: fail.

### `Kernel.load`

```gizlang
function Kernel.load(self: mut Kernel, b: i64, index: i64): i64
```

Element `index` (I64) of buffer `b`, as its value type.

Effects: fail.

### `Kernel.e4m3`

```gizlang
function Kernel.e4m3(self: mut Kernel, x: i64): i64
```

The E4M3 byte (I32, 0 to 255) of F32 x, as a store into E4M3 memory gives it: two of them make an E4M3X2 element.

Effects: fail.

### `Kernel.toByte`

```gizlang
function Kernel.toByte(self: mut Kernel, v: i64): i64
```

v (F32) rounded and clamped to a byte, as I32.

Effects: fail.

### `Kernel.roundTo`

```gizlang
function Kernel.roundTo(self: mut Kernel, e: i64, x: i64): i64
```

F32 X rounded to the values of element type E and back to F32, as a store and a load of E give it: F16 and BF16 to
nearest even (F16 past 65504 to infinity, its subnormals exact), E4M3 as store encodes it; F32 unchanged. NaN stays
NaN. gizml:tile's casts use it, and its CPU side has the same arithmetic.

Effects: fail.

### `Kernel.store`

```gizlang
function Kernel.store(self: mut Kernel, b: i64, index: i64, value: i64): void
```

Stores `value` (the element's value type) as element `index` of buffer `b`; F16 and BF16 round to nearest even. An
I32 value into F16 or BF16 elements stores its low 16 bits unchanged (the bits of a U16 load), so a copy converts
nothing; sstore does the same.

Effects: fail.

### `Kernel.store2`

```gizlang
function Kernel.store2(self: mut Kernel, b: i64, index: i64, x: i64, y: i64): void
```

Stores F32 values x and y as elements `index` (even) and index + 1 of F32, F16 or BF16 buffer `b`, rounded as
store does: one store on NVIDIA (sm_80 and later for F16 and BF16).

Effects: fail.

### `Kernel.sstore2`

```gizlang
function Kernel.sstore2(self: mut Kernel, s: i64, index: i64, x: i64, y: i64): void
```

As store2, into F16 or BF16 workgroup memory `s`.

Effects: fail.

### `Kernel.sload`

```gizlang
function Kernel.sload(self: mut Kernel, s: i64, index: i64): i64
```

The element INDEX of the workgroup memory S.

Effects: fail.

### `Kernel.sstore`

```gizlang
function Kernel.sstore(self: mut Kernel, s: i64, index: i64, value: i64): void
```

Stores VALUE at element INDEX of the workgroup memory S.

Effects: fail.

### `Kernel.sload4`

```gizlang
function Kernel.sload4(self: mut Kernel, s: i64, index: i64): i64
```

16 bytes of workgroup memory `s` from element `index` (its byte offset a multiple of 16) in one load. Its 4
lanes (lane(v, j)) are F32 elements for F32 memory, else 32-bit words of raw bits (I32: two F16, BF16 or U16
elements, the first in the low half).

Effects: fail.

### `Kernel.load4`

```gizlang
function Kernel.load4(self: mut Kernel, b: i64, index: i64, ok: i64): i64
```

16 bytes of buffer `b` from element `index` (its byte offset a multiple of 16) in one load, with lanes as
sload4. Where `ok` is false nothing is read and every lane is 0 (the edge of a tile).

Effects: fail.

### `Kernel.lane`

```gizlang
function Kernel.lane(self: Kernel, v: i64, j: i64): i64
```

Lane J (0 to 3) of the wide load V.

Effects: fail.

### `Kernel.sstore4`

```gizlang
function Kernel.sstore4(self: mut Kernel, s: i64, index: i64, v: i64): void
```

The 16 bytes of `v` (a load4 or sload4) unchanged at element `index` of workgroup memory `s` (its byte offset
a multiple of 16).

Effects: fail.

### `Kernel.copy`

```gizlang
function Kernel.copy(self: mut Kernel, s: i64, slot: i64, b: i64, index: i64, ok: i64): void
```

Copies 16 bytes of buffer `b` from element `index` to workgroup memory `s` at element `slot` (both byte offsets
multiples of 16), or 16 zero bytes where `ok` is false. On NVIDIA sm_80 and later the copy runs in the background
(cp.async): it is done after `commit` closes its group and `wait` lets at most that many later groups run, and the
others see it after a barrier. Elsewhere it is done at once.

Effects: fail.

### `Kernel.copyElement`

```gizlang
function Kernel.copyElement(self: mut Kernel, s: i64, slot: i64, b: i64, index: i64, ok: i64): void
```

As copy, for one 4-byte element (its byte offsets multiples of 4).

Effects: fail.

### `Kernel.commit`

```gizlang
function Kernel.commit(self: mut Kernel): void
```

Closes the group of the copies started since the last commit.

Effects: fail.

### `Kernel.wait`

```gizlang
function Kernel.wait(self: mut Kernel, pending: i64): void
```

Waits until at most `pending` groups of this thread's copies are still running.

Effects: fail.

### `Kernel.mbarrierInit`

```gizlang
function Kernel.mbarrierInit(self: mut Kernel, s: i64, slot: i64, count: i64): void
```

Sets barrier `slot` of `s` (a workgroup array of E_I64) to wait for `count` arrivals; then fenceBarriers once,
and a workgroup barrier before another thread uses it.

Effects: fail.

### `Kernel.fenceBarriers`

```gizlang
function Kernel.fenceBarriers(self: mut Kernel): void
```

Makes the barriers set by mbarrierInit visible to the copy engine.

Effects: fail.

### `Kernel.mbarrierExpect`

```gizlang
function Kernel.mbarrierExpect(self: mut Kernel, s: i64, slot: i64, bytes: i64): void
```

Arrives at barrier `slot` of `s` and adds `bytes` to the bytes its phase waits for (the copies to it).

Effects: fail.

### `Kernel.mbarrierArrive`

```gizlang
function Kernel.mbarrierArrive(self: mut Kernel, s: i64, slot: i64): void
```

Arrives at barrier `slot` of `s`.

Effects: fail.

### `Kernel.mbarrierWait`

```gizlang
function Kernel.mbarrierWait(self: mut Kernel, s: i64, slot: i64, parity: i64): void
```

Waits until the phase of barrier `slot` of `s` with parity `parity` (0 or 1, a value) has completed.

Effects: fail.

### `Kernel.tensorLoad`

```gizlang
function Kernel.tensorLoad(self: mut Kernel, s: i64, slot: i64, map: i64, x: i64, y: i64, bars: i64, bar: i64): void
```

Starts the copy engine's load of the box at byte x of row y of the 2-D tensor whose map is at global address
`map` (Gpu.tensorMap) into `s` from element `slot`; barrier `bar` of `bars` counts its bytes.
Elements outside the tensor are zeros. One thread issues it.

Effects: fail.

### `Kernel.bulkLoad`

```gizlang
function Kernel.bulkLoad(self: mut Kernel, s: i64, slot: i64, b: i64, index: i64, bytes: i64, bars: i64, bar: i64): void
```

Starts the copy engine's load of `bytes` (a multiple of 16) from element `index` of buffer `b` into `s` from
element `slot` (both 16-byte aligned); barrier `bar` of `bars` counts them. One thread issues it.

Effects: fail.

### `Kernel.awaitGrid`

```gizlang
function Kernel.awaitGrid(self: mut Kernel): void
```

Waits for the grid before this one on the stream to finish or call releaseGrid. A kernel with awaitGrid starts
while that grid still runs (Gpu.launch gives it the programmatic dependency): the work before awaitGrid, such as
its barriers and prefetches, overlaps the end of the grid before.

Effects: fail.

### `Kernel.awaitsGrid`

```gizlang
function Kernel.awaitsGrid(self: Kernel): bool
```

Whether the kernel waits at awaitGrid.

Effects: fail.

### `Kernel.releaseGrid`

```gizlang
function Kernel.releaseGrid(self: mut Kernel): void
```

Lets the next grid on the stream start (it still waits at its awaitGrid).

Effects: fail.

### `Kernel.sharedBytes`

```gizlang
function Kernel.sharedBytes(self: Kernel): i64
```

Bytes of workgroup memory the kernel uses.

Effects: fail.

### `Kernel.barrier`

```gizlang
function Kernel.barrier(self: mut Kernel): void
```

Every thread of the workgroup waits here, and sees the others' workgroup memory writes.

Effects: fail.

### `Kernel.variable`

```gizlang
function Kernel.variable(self: mut Kernel, init: i64): i64
```

A variable of `init`'s type (a tile's element type too), set to it.

Effects: fail.

### `Kernel.get`

```gizlang
function Kernel.get(self: mut Kernel, v: i64): i64
```

The value of the variable V.

Effects: fail.

### `Kernel.set`

```gizlang
function Kernel.set(self: mut Kernel, v: i64, value: i64): void
```

Sets the variable V to VALUE.

Effects: fail.

### `Kernel.when`

```gizlang
function Kernel.when(self: mut Kernel, c: i64): void
```

if (c) { ... } [else { ... }] end

Effects: fail.

### `Kernel.otherwise`

```gizlang
function Kernel.otherwise(self: mut Kernel): void
```

Starts the else block of the open when.

Effects: fail.

### `Kernel.end`

```gizlang
function Kernel.end(self: mut Kernel): void
```

Ends the open when.

Effects: fail.

### `Kernel.loop`

```gizlang
function Kernel.loop(self: mut Kernel): void
```

loop { ... breakIf(c) ... } endLoop: breakIf only directly in the loop's body.

Effects: fail.

### `Kernel.breakIf`

```gizlang
function Kernel.breakIf(self: mut Kernel, c: i64): void
```

Leaves the loop when C is true; only directly in the loop's body.

Effects: fail.

### `Kernel.endLoop`

```gizlang
function Kernel.endLoop(self: mut Kernel): void
```

Ends the loop.

Effects: fail.

### `Kernel.repeat`

```gizlang
function Kernel.repeat(self: mut Kernel, from: i64, to: i64, step: i64): i64
```

for (i = from; i < to; i += step) { ... } next(): returns i (I64); `to` is read each turn.

Effects: fail.

### `Kernel.unroll`

```gizlang
function Kernel.unroll(self: mut Kernel, count: i64): void
```

Asks the compiler to unroll the loop just ended `count` times (a hint).

Effects: fail.

### `Kernel.next`

```gizlang
function Kernel.next(self: mut Kernel): void
```

The end of the innermost repeat.

Effects: fail.

### `Kernel.mmaZero`

```gizlang
function Kernel.mmaZero(self: mut Kernel): i64
```

The 16x16 F32 accumulator of zeros.

Effects: fail.

### `Kernel.mmaLoadA`

```gizlang
function Kernel.mmaLoadA(self: mut Kernel, s: i64, at: i64, stride: i64, swizzled: bool = false, elem: i64 = -1): i64
```

The 16x16 tile of A at element `at` of F16, BF16 or packed (E4M3X2, E2M1X4, I8X2, I4X4) workgroup memory `s`, row-major: A[i][p] at at + i stride + p.
`swizzled` (stride 32, `at` a multiple of 16 rows): the 8-element chunk of p is XOR (i / 2) % 4 in its row, so an
8x8 matrix meets every bank once with no padding. `elem` (-1: s's) reads s's 16-bit elements as another type.

Effects: fail.

### `Kernel.mmaLoadB`

```gizlang
function Kernel.mmaLoadB(self: mut Kernel, s: i64, at: i64, stride: i64, swizzled: bool = false, elem: i64 = -1): i64
```

The 16x16 tile of B at element `at` of F16, BF16 or packed (E4M3X2, E2M1X4, I8X2, I4X4) workgroup memory `s`, stored by columns: B[p][j] at at + j stride + p;
`swizzled` and `elem` as mmaLoadA's, by column j.

Effects: fail.

### `Kernel.mmaLoadAT`

```gizlang
function Kernel.mmaLoadAT(self: mut Kernel, s: i64, at: i64, stride: i64): i64
```

The tile of A stored by columns (along k): A[i][p] at at + p stride + i.

Effects: fail.

### `Kernel.mmaLoadBT`

```gizlang
function Kernel.mmaLoadBT(self: mut Kernel, s: i64, at: i64, stride: i64): i64
```

The tile of B stored by rows: B[p][j] at at + p stride + j.

Effects: fail.

### `Kernel.mma`

```gizlang
function Kernel.mma(self: mut Kernel, a: i64, b: i64, c: i64): i64
```

c + a b, with the f32 sums of the hardware (not the order of fma): A and B tiles of one element type.

Effects: fail.

### `Kernel.mmaHalf`

```gizlang
function Kernel.mmaHalf(self: mut Kernel, a: i64, b: i64, c: i64): i64
```

c + a b of E4M3X2 tiles with the F16 sums of the hardware (twice the F32 rate on GeForce Ada): c is mmaZero's
or mmaHalf's, and mmaGet gives the sums as F32. Each sum must stay within F16's range (65504).

Effects: fail.

### `Kernel.mmaScaled`

```gizlang
function Kernel.mmaScaled(self: mut Kernel, a: i64, b: i64, c: i64, scales: i64): i64
```

c + a b of E2M1X4 tiles (16 rows of 64 FP4 values), each value times the scale of its block of 16 along k:
FP8 E4M3 bytes (positive), 4 to a 32-bit word in the order of k. `scales` (I64) holds two words: in its low half
the word of the tile's row g + 8 (t % 2) of A, in its high half that of column g + 8 (t % 2) of B (lane l is in
group g = l / 4 at t = l % 4).

Effects: fail.

### `Kernel.mmaGet`

```gizlang
function Kernel.mmaGet(self: mut Kernel, c: i64, r: i64, integer: bool = false): i64
```

This lane's element r (0 to 7) of accumulator `c` as F32: C[mmaRow(r)][mmaCol(r)]. With `integer` the
accumulator holds the I32 sums of I8X2 or I4X4 products, converted here.

Effects: fail.

### `Kernel.mmaRow`

```gizlang
function Kernel.mmaRow(self: mut Kernel, r: i64): i64
```

The row (I64, 0 to 15) of this lane's accumulator element r.

Effects: fail.

### `Kernel.mmaCol`

```gizlang
function Kernel.mmaCol(self: mut Kernel, r: i64): i64
```

The column (I64, 0 to 15) of this lane's accumulator element r.

Effects: fail.

### `Kernel.mmaPackA`

```gizlang
function Kernel.mmaPackA(self: mut Kernel, values: Vec<i64>, e: i64): i64
```

The F16 or BF16 A tile of the 16x16 values the lanes hold in accumulator order (`values[r]` at mmaRow(r),
mmaCol(r)), rounded to nearest even, in registers: FlashAttention's weights P go from the scores' accumulators to
the next product without workgroup memory. Only where hasMmaPack. E_E4M3X2 takes 16 values, the 32 x 16 tile of
m16n8k32 (hasFp8Mma): register j holds values 4 j to 4 j + 3, one row's 4 adjacent inputs, so a caller passes
elements 0, 1, 4, 5 of a 16-column accumulator for row g and 2, 3, 6, 7 for row g + 8, and orders the B rows to match.

Effects: fail.

### `Kernel.mmaWords`

```gizlang
function Kernel.mmaWords(self: mut Kernel, words: Vec<i64>, e: i64): i64
```

The F16 or BF16 tile whose row (A) or column (B) l % 16 holds, in lane l, the 16 elements of the 8 I32 `words` in the
order of k, element 2 j in the low half of word j: a tile made in registers. Only where hasMmaWords. On NVIDIA
(hasFp8Mma) an E4M3X2 A tile of 4 `words`, register j as mmaPackA's: its E4M3 bytes as they are, low byte first.

Effects: fail.

### `Kernel.exp`

```gizlang
function Kernel.exp(self: mut Kernel, x: i64): i64
```

e^x within 2 ulp (Cephes expf), as tensor's vexp.

Effects: fail.

### `Kernel.sin`

```gizlang
function Kernel.sin(self: mut Kernel, x: i64): i64
```

sin x (Cephes sinf: x reduced by multiples of pi/4 in three parts, then the sine or cosine polynomial), within
2 ulp for |x| below 8192.

Effects: fail.

### `Kernel.log`

```gizlang
function Kernel.log(self: mut Kernel, x: i64): i64
```

ln x within 2 ulp (Cephes logf), as tensor's vlog.

Effects: fail.

### `Kernel.gelu`

```gizlang
function Kernel.gelu(self: mut Kernel, x: i64): i64
```

GELU's tanh form, as tensor's vgelu.

Effects: fail.

### `Kernel.geluGrad`

```gizlang
function Kernel.geluGrad(self: mut Kernel, x: i64, dy: i64): i64
```

dy times gelu's derivative at x, as tensor's vgeluGrad.

Effects: fail.

### `Kernel.erf`

```gizlang
function Kernel.erf(self: mut Kernel, x0: i64): i64
```

erf x, as tensor's verf.

Effects: fail.

### `Kernel.geluErf`

```gizlang
function Kernel.geluErf(self: mut Kernel, x: i64): i64
```

GELU's erf form, as tensor's vgeluErf.

Effects: fail.

### `Kernel.geluErfGrad`

```gizlang
function Kernel.geluErfGrad(self: mut Kernel, x: i64, dy: i64): i64
```

dy times its derivative, as tensor's vgeluErfGrad.

Effects: fail.

### `Kernel.exp2`

```gizlang
function Kernel.exp2(self: mut Kernel, x: i64): i64
```

2^x by the GPU's approximation (NVIDIA ex2.approx within 2 ulp, subnormal results 0; AMD v_exp_f32): a few
times faster than exp, and not the CPU's bits. For softmax weights, with log2(e) folded into the scale.

Effects: fail.

### `Kernel.sigmoid`

```gizlang
function Kernel.sigmoid(self: mut Kernel, x: i64): i64
```

The sigmoid 1 / (1 + e^-x) of the F32 X, without overflow.

Effects: fail.

### `Kernel.silu`

```gizlang
function Kernel.silu(self: mut Kernel, x: i64): i64
```

x sigmoid(x) (SiLU, swish).

Effects: fail.

### `Kernel.tanh`

```gizlang
function Kernel.tanh(self: mut Kernel, x: i64): i64
```

The hyperbolic tangent of the F32 X.

Effects: fail.

### `Kernel.llvm`

```gizlang
function Kernel.llvm(self: Kernel, arch: string): string
```

The kernel as LLVM IR for AMD `arch` (gfx1151): the matrix operations take the layout of its generation.

Effects: fail.

### `Kernel.ptx`

```gizlang
function Kernel.ptx(self: Kernel, arch: string): string
```

The kernel as PTX for `arch` (sm_XY).

Effects: fail.

### `Kernel.msl`

```gizlang
function Kernel.msl(self: Kernel, arch: string): string
```

The kernel as Metal Shading Language for an Apple GPU `arch` ("apple7" to "apple10", the GPU family). Compiled at
run time with fast math off: the arithmetic is IEEE and a result equals the CPU's, except exp2, sums of mma and
the F32 division and square root, whose rounding Apple's compiler states and the Mac tests check. An F64 is a
double-float (msDouble).

Effects: fail.

## Constants

### `F32`

```gizlang
const F32 = 0
```

The value type of 32-bit floats.

### `F64`

```gizlang
const F64 = 1
```

The value type of 64-bit floats.

### `I32`

```gizlang
const I32 = 2
```

The value type of 32-bit integers.

### `I64`

```gizlang
const I64 = 3
```

The value type of 64-bit integers.

### `BOOL`

```gizlang
const BOOL = 4
```

The value type of booleans.

### `TILE`

```gizlang
const TILE = 5
```

The value type of a 16x16 F16 or BF16 operand tile of matrix multiply-accumulate (see mma).

### `ACC`

```gizlang
const ACC = 6
```

The value type of a 16x16 F32 accumulator tile of matrix multiply-accumulate (see mma).

### `E_F32`

```gizlang
const E_F32 = 0
```

The element type of 32-bit floats.

### `E_F64`

```gizlang
const E_F64 = 1
```

The element type of 64-bit floats.

### `E_I32`

```gizlang
const E_I32 = 2
```

The element type of 32-bit integers.

### `E_I64`

```gizlang
const E_I64 = 3
```

The element type of 64-bit integers.

### `E_F16`

```gizlang
const E_F16 = 4
```

The element type of IEEE half-precision floats; it loads as F32.

### `E_BF16`

```gizlang
const E_BF16 = 5
```

The element type of bfloat16 floats; it loads as F32.

### `E_U8`

```gizlang
const E_U8 = 6
```

The element type of unsigned bytes; it loads as I32.

### `E_I8`

```gizlang
const E_I8 = 7
```

The element type of signed bytes; it loads as I32.

### `E_U16`

```gizlang
const E_U16 = 8
```

The element type of unsigned 16-bit integers; it loads as I32.

### `E_E4M3`

```gizlang
const E_E4M3 = 9
```

FP8 E4M3 (OCP: bias 7, no infinities, 0x7F the NaN, largest 448): loads as F32; an F32 stores rounded to nearest
even and saturated to ±448 (NaN stays NaN), an I32 stores its low 8 bits unchanged. E_E4M3X2 is a pair of them as
one 16-bit element of raw bits (I32, as U16): the operand of mma on NVIDIA (hasFp8Mma).

### `E_E4M3X2`

```gizlang
const E_E4M3X2 = 10
```

A pair of FP8 E4M3 values as one 16-bit element of raw bits: the operand of mma on NVIDIA (hasFp8Mma).

### `E_E2M1X4`

```gizlang
const E_E2M1X4 = 11
```

Four FP4 E2M1 values (1 sign, 2 exponent and 1 mantissa bits: 0, 0.5, 1, 1.5, 2, 3, 4, 6), two a byte with the
first in the low half, as one 16-bit element of raw bits (I32, as U16): the operand of mmaScaled (hasFp4Mma).

### `E_I8X2`

```gizlang
const E_I8X2 = 12
```

A pair of signed 8-bit integers as one 16-bit element of raw bits (I32, as U16): the operand of mma on NVIDIA
(hasI8Mma), whose accumulator holds I32 sums (mmaGet with `integer`).

### `E_I4X4`

```gizlang
const E_I4X4 = 13
```

Four signed 4-bit integers, two a byte with the first in the low half, as one 16-bit element of raw bits (I32, as
U16): the operand of mma on NVIDIA (hasI4Mma, m16n8k64), whose accumulator holds I32 sums as I8X2's.

### `STATIC_SHARED_BYTES`

```gizlang
const STATIC_SHARED_BYTES = 49152
```

The workgroup memory that a kernel declares statically on NVIDIA; more is dynamic, opted in at launch.

### `APPLE_SHARED_BYTES`

```gizlang
const APPLE_SHARED_BYTES = 32768
```

Workgroup memory a threadgroup may use on an Apple GPU (32 KiB on every generation shipped).

