FBTriton View on GitHub ↗

TLX documentation

Global memory access

Address global memory directly with scalar-base buffer operations.

[gfx950] — available on AMD MI350 (CDNA4) only; not available on MI300.

Buffer operations access global memory via a scalar base pointer and a tensor of i32 element offsets, rather than a tensor of pointers. This maps directly to AMD's hardware buffer instructions, which use a resource descriptor and byte offsets, enabling the hardware to do out-of-bounds checking and cache optimization.

tlx.buffer_load

Load a tensor of values from global memory.

result = tlx.buffer_load(
    ptr, offsets, mask=None, other=None, cache=None, contiguity=1
)
ArgumentTypeDescription
ptrscalar pointerBase address in global memory.
offsetsi32 tensorPer-element byte offsets from ptr.
maskbool tensor, optionalWhen mask[i] is False, the element is not loaded.
othertensor or scalar, optionalValue used for masked-out elements (where mask[i] is False).
cachestr, optionalCache modifier (e.g. ".ca", ".cg").
contiguityconstexpr intTrusted positive power-of-two vectorization width; it must divide each thread's element count.

Returns: A tensor with the same shape as offsets and element type matching the pointee type of ptr.

Lowers to amdg.buffer_load, which is eventually lowered to rocdl.raw.ptr.buffer.load.

tlx.buffer_store

Store a tensor of values to global memory.

tlx.buffer_store(
    stored_value,
    ptr,
    offsets,
    mask=None,
    cache=None,
    contiguity=1,
)
ArgumentTypeDescription
stored_valuetensorValues to write.
ptrscalar pointerBase address in global memory.
offsetsi32 tensorPer-element byte offsets from ptr.
maskbool tensor, optionalWhen mask[i] is False, the element is not written.
cachestr, optionalCache modifier.
contiguityconstexpr intTrusted positive power-of-two lower bound on the number of adjacent elements available for vectorization. Values greater than one require stored_value to be pinned with tlx.require_layout.

contiguity is a correctness contract, not a best-effort hint. For every group of contiguity elements owned by one thread, the caller promises that the offsets are consecutive, the mask predicate is identical, and the first address meets the packed access's alignment. The compiler verifies that the width is a positive power of two, divides the number of elements owned by each thread, and does not exceed the physical adjacency of the pinned layout; it cannot verify the runtime address or mask promises. An incorrect promise can change which elements are written.

Pinning stored_value anchors the store layout; the store's operand constraint keeps offsets and mask in the same encoding. The final instruction width is also capped by AMD's 128-bit buffer-store limit. For example, BF16 contiguity=16 is emitted as legal eight-element buffer_store_dwordx4 operations. The default contiguity=1 retains the existing scalar-contract behavior.

store_layout: tl.constexpr = tlx.amd_mfma_layout(
    version=4,
    instr_shape=[16, 16, 32],
    transposed=True,
    warps_per_cta=[2, 2],
)
mask_boundary = mask_boundary - mask_boundary % 4
mask = offsets < mask_boundary
values = tlx.require_layout(values, store_layout)
tlx.buffer_store(
    values,
    out_ptr,
    offsets,
    mask=mask,
    contiguity=4,
)

Returns: Nothing.

Lowers to amdg.buffer_store, which is eventually lowered to rocdl.raw.ptr.buffer.store.

tlx.buffer_atomic_add

Atomically add a tensor through an AMD scalar resource descriptor.

previous = tlx.buffer_atomic_add(
    ptr, offsets, value, mask=None, sem=None, scope=None, contiguity=1
)

contiguity is the same trusted per-thread adjacency width accepted by buffer_load; values greater than one also preserve the selected layout. The operation returns the values observed before the additions. FP16 and BF16 use packed two-element instructions, so contiguity must be at least two and any mask must be uniform for each adjacent pair. Compilation fails if mask analysis reduces the legal vector width below two.

Example: buffer load and store

import triton.language.extra.tlx as tlx

@triton.jit
def kernel(src_ptr, dst_ptr, BLOCK_SIZE: tl.constexpr):
    offsets = tl.arange(0, BLOCK_SIZE).to(tl.int32)
    mask = offsets < BLOCK_SIZE

    # Load from global memory using buffer semantics
    data = tlx.buffer_load(src_ptr, offsets, mask=mask, other=0.0)

    # Store to global memory using buffer semantics
    tlx.buffer_store(data, dst_ptr, offsets, mask=mask)

For the async global-to-shared variant, see the warp-pipeline GEMM example (third_party/amd/python/examples/gluon/f16_gemm_warp_pipeline_gfx1250.py).