Operations

Most scalar operations listed here work on both scalars and tiles. Use Julia's broadcast syntax (the . operator) to apply any scalar function element-wise over tiles: sqrt.(tile), max.(a, b), cld.(tile, 4), and so on.

Operations prefixed with ct. are cuTile intrinsics without a direct Julia equivalent; everything else is standard Julia syntax overlaid on Base. Operations that need a particular Tile IR version or GPU architecture are noted inline and collected in Compatibility.

Arithmetic

OperationDescription
+, -Element-wise (same shape only)
tile * scalar, tile / scalarScalar multiply/divide
.+, .-, .*, ./, .^Broadcasting element-wise
mod.(x, y)Floored remainder with Julia sign semantics
ct.divmod(x, y)Floored quotient and remainder

Note that + and - apply directly to tiles of matching shape, and * and / to a tile and a scalar, but combining tiles of different shapes requires broadcast syntax. a * b between two tiles is matrix multiplication, not an element-wise product.

Construction

OperationDescription
zeros(T, dims...)Zero-filled tile
ones(T, dims...)One-filled tile
fill(value, dims...)Constant-filled tile
ct.arange(n; dtype, start, step)Configurable arithmetic sequence (defaults to 1:n)

Shape

OperationDescription
reshape(tile, shape)Reshape (same element count)
transpose(tile)Transpose 2D tile
permutedims(tile, perm)Permute dimensions
repeat(tile, counts...) repeat(tile; inner, outer)Repeat values along dimensions
ct.extract(tile, index, shape)Extract sub-tile
ct.insert(tile, index, value)Replace a non-overlapping sub-tile (Tile IR v13.4+)
ct.cat((a, b), axis)Concatenate tiles
ct.broadcast_to(tile, shape)Broadcast to target shape
dropdims(tile; dims)Remove singleton dimensions

Matrix multiplication

OperationDescription
a * bMatrix multiplication: a @ b
muladd(a, b, acc; fast_acc=false)Matrix multiply-accumulate: a * b + acc
ct.muladd_scaled(a, a_scale, b, b_scale, acc)Block-scaled multiply-accumulate

Each operation follows Base.:* / Base.muladd's shape rules, with the addition of allowing trailing batch dimensions.

fast_acc=true enables fast accumulation for FP8 inputs, and has an effect only on Hopper (sm_90; silently ignored on other architectures), and requires Tile IR v13.3+.

ct.muladd_scaled multiplies each operand by a low-precision block scale before the matmul: each scale element covers a contiguous block of B = K ÷ K_s elements along the K dimension. Requires Blackwell. The supported operand/scale/accumulator dtypes and block sizes are:

Input (a/b)ScaleAcc/OutputB
Float8_E4M3FN, Float8_E5M2Float8_E8M0FNUFloat3232
Float4_E2M1FNFloat8_E8M0FNUFloat3216, 32
Float4_E2M1FNFloat8_E4M3FNFloat3216

Reductions and scans

OperationDescription
sum(tile; dims)Sum along axis
prod(tile; dims)Product along axis
maximum(tile; dims, propagate_nan)Maximum along axis
minimum(tile; dims, propagate_nan)Minimum along axis
any(tile; dims)Logical OR along axis
all(tile; dims)Logical AND along axis
count(tile; dims)Count true elements along axis
argmax(tile; dims, propagate_nan)1-based index of maximum along axis
argmin(tile; dims, propagate_nan)1-based index of minimum along axis
cumsum(tile; dims, rev)Cumulative sum
cumprod(tile; dims, rev)Cumulative product

Following Base semantics, reductions keep the reduced dimension as size 1. Use dropdims to remove it.

Higher-order functions

OperationDescription
map(f, tiles...)Apply function element-wise (same shape)
f.(tiles...), broadcast(f, tiles...)Apply function with shape broadcasting
reduce(f, tile; dims, init)Reduction with arbitrary function
mapreduce(f, op, tile; dims, init)Map then reduce
accumulate(f, tile; dims, init, rev)Scan/prefix-sum with arbitrary function

Any function that works on scalars "just works" when broadcast over tiles, so these cover element-wise operations that have no dedicated entry in the tables above.

Broadcasting shape alignment

cuTile.jl uses Julia's standard left-aligned broadcast shape rules: dimensions are matched starting from the first (leftmost) dimension. A 1D (N,) tile therefore cannot broadcast with a 2D (M, N) tile, because dimension 1 has size N vs M. Use reshape to align dimensions, just as with regular Julia arrays:

a = ct.load(...)              # (N,)
b = ct.load(...)              # (M, N)
result = reshape(a, (1, N)) .+ b  # (1, N) .+ (M, N) → (M, N)

This differs from NumPy's (and cuTile Python's) right-aligned rules; see Comparison with cuTile Python.

Math

OperationDescription
sqrt(x)Square root
rsqrt(x)Reciprocal square root
exp(x), exp2(x)Exponential
log(x), log2(x)Logarithm
sin(x), cos(x), tan(x)Trigonometric functions
sinh(x), cosh(x), tanh(x)Hyperbolic functions
fma(a, b, c)Fused multiply-add
abs(x)Absolute value
isnan(x)NaN test
max(a, b), min(a, b)Maximum/minimum
ceil(x), floor(x)Rounding
ct.@fpmode rounding_mode=ct.Rounding.Approx flush_to_zero=true begin ... endScoped FP rounding mode and flush-to-zero

Comparison

OperationDescription
.<, .>, .<=, .>=Element-wise comparison (returns a Bool tile)
.==, .!=Element-wise equality
ifelse.(cond, x, y)Element-wise conditional selection

Integer and bitwise

OperationDescription
cld(a, b)Ceiling division
fld(a, b)Floor division
div(a, b)Truncating division
mul_hi(a, b)High bits of integer multiply (Base.mul_hi on Julia 1.13+)
.~xElement-wise bitwise NOT
.&, .|, xor.Element-wise bitwise AND, OR, XOR

Type conversion

Element-type conversion and reinterpretation are covered in Element Types.

Reference

The entries below are the operations with cuTile-specific behaviour or no Base counterpart. Everything else in this chapter is a Base function that cuTile overlays, documented by Base itself.

cuTile.arangeFunction
arange(shape; dtype=Int32, start=1, step=1) -> Tile{dtype, shape}
arange(n; dtype=Int32, start=1, step=1) -> Tile{dtype, Tuple{n}}

Create a 1D tile with values [1, 2, 3, ..., n] (1-indexed).

Example

indices = ct.arange(16)              # Int32 [1, 2, ..., 16]
indices = ct.arange(16; dtype=Int64) # Int64 [1, 2, ..., 16]
odd = ct.arange(16; start=1, step=2) # Int32 [1, 3, ..., 31]
source
cuTile.catFunction
cat(tiles::Tuple{Tile, Tile}, axis::Int) -> Tile

Concatenate two tiles along the specified axis (1-indexed). Supports negative axis (e.g., -1 for last dimension).

Example

tile_a = ct.load(arr_a, (1,), (4, 8))  # Shape (4, 8)
tile_b = ct.load(arr_b, (1,), (4, 8))  # Shape (4, 8)
# Concatenate along axis 1: (4, 8) + (4, 8) -> (8, 8)
combined = ct.cat((tile_a, tile_b), 1)
# Concatenate along axis -1 (last): (4, 8) + (4, 8) -> (4, 16)
combined_last = ct.cat((tile_a, tile_b), -1)
source
cuTile.broadcast_toFunction
broadcast_to(tile::Tile{T, S}, shape::NTuple{N, Int}) -> Tile{T, shape}

Explicitly broadcast a tile to a target shape.

Example

row = ct.load(arr, (1, 1), (1, 128))  # Shape (1, 128)
expanded = ct.broadcast_to(row, (64, 128))  # Shape (64, 128)
source
cuTile.extractFunction
extract(tile::Tile{T, S}, index::NTuple{N, Int}, shape::NTuple{N, Int}) -> Tile{T, shape}

Extract a sub-tile from a tile at the given slice indices.

IMPORTANT: The index parameter specifies SLICE INDICES, not element offsets!

For each dimension, the source tile is divided into S[i] ÷ shape[i] non-overlapping slices. The index[i] selects which slice to extract (1-indexed).

Example: Extracting quadrants from an 8×8 tile

tile = ct.load(arr, (1, 1), (8, 8))
# 8÷4 = 2 slices per dimension, so valid indices are {1, 2} × {1, 2}
tl = ct.extract(tile, (1, 1), (4, 4))  # Top-left (rows 1-4, cols 1-4)
bl = ct.extract(tile, (2, 1), (4, 4))  # Bottom-left (rows 5-8, cols 1-4)
tr = ct.extract(tile, (1, 2), (4, 4))  # Top-right (rows 1-4, cols 5-8)
br = ct.extract(tile, (2, 2), (4, 4))  # Bottom-right (rows 5-8, cols 5-8)
source
cuTile.insertFunction
insert(tile::Tile, index, value::Tile) -> Tile

Return a copy of tile with the non-overlapping subtile at the 1-indexed slice index replaced by value. This is the inverse of extract and requires Tile IR v13.4 or newer.

source
Base.reinterpretMethod
Base.reinterpret(::Type{T}, x::Tile) -> Tile{T}

Reinterpret the whole tile x as a tile of element type T, like reinterpret(T, ::AbstractArray): the underlying bits are viewed as a contiguous (column-major) block and the leading dimension is rescaled by the ratio of element widths. Lowers to cuda_tile.bitcast for equal widths and to cuda_tile.pack/unpack (via reshape to rank-1) when widths differ.

This is how sub-byte formats move through global memory: a Tile{UInt8,Tuple{N}} reinterprets to a Tile{Float4_E2M1FN,Tuple{2N}} and back, so FP4 data can be stored in a UInt8 array. The total bit-width is preserved, so it must divide evenly.

Note reinterpret.(T, x) (with a dot) is the unrelated element-wise broadcast, which keeps the shape and requires T to be the same width as eltype(x).

bytes = ct.load(a, pid, (8,))                 # Tile{UInt8,Tuple{8}}
fp4   = reinterpret(Float4_E2M1FN, bytes)     # Tile{Float4_E2M1FN,Tuple{16}}
vals  = convert(ct.Tile{Float32}, fp4)        # widen for compute
source
Base.reinterpretMethod
Base.reinterpret(reshape, ::Type{T}, x::Tile) -> Tile{T}

The reshape-form whole-tile reinterpret, mirroring reinterpret(reshape, T, ::AbstractArray): instead of rescaling the leading dimension it removes it when widening (the leading dim must equal bitwidth(T) ÷ bitwidth(eltype(x))) and prepends one when narrowing.

source
cuTile.divmodFunction
divmod(x, y) -> (q, r)

Floored quotient and remainder, i.e. (fld(x, y), mod(x, y)). Accepts integer scalars or integer tiles of matching shape, and follows Julia's sign conventions: the remainder takes the sign of the divisor.

source
cuTile.rsqrtFunction
rsqrt(x)

Reciprocal square root, 1 / sqrt(x), computed as a single Tile IR operation rather than a division followed by a square root. Broadcast it (rsqrt.(tile)) to apply it element-wise over a tile.

source
Base.muladdMethod
muladd(a::Tile, b::Tile, acc::Tile; fast_acc::Bool=false) -> Tile

Matrix multiply-accumulate a * b + acc over tiles, lowering to cuda_tile.mmaf (float) or cuda_tile.mmai (i8 × i8 → i32).

a/b are 2-D matrices (M, K) × (K, N)(M, N); a 1-D operand is promoted (vec-mat / mat-vec) and any trailing dimensions (≥3-D) are treated as broadcast batch dims, lifting Base.muladd's shape rules to tiles. acc carries the result dtype, which must be one tileiras allows for the input dtype (f16/f32 for f16 and f8; f32 for bf16/tf32; f64 for f64; i32 for i8).

fast_acc enables fast accumulation (lower accumulator precision for throughput); it is valid only for FP8 inputs and requires Tile IR v13.3+.

source
cuTile.muladd_scaledFunction
muladd_scaled(a, a_scale, b, b_scale, acc) -> Tile

Block-scaled matrix multiply-accumulate (a ⊙ a_scale) * (b ⊙ b_scale) + acc, lowering to cuda_tile.mmaf_scaled (Tile IR v13.3+, Blackwell). Each scale element multiplies a contiguous block of B = K ÷ K_s elements along the K dimension of its operand, so a_scale/b_scale match a/b in every dimension except K, where they have K_s ≤ K entries.

a/b are low-precision floats (f8e4m3fn, f8e5m2, or f4e2m1fn), a_scale/b_scale are f8e8m0fnu or f8e4m3fn, and acc is f32. Shapes follow muladd: 2-D (M, K) × (K, N), mat-vec, and trailing batch dims; vec-mat is unsupported (it would collapse K, leaving nothing to scale).

source