tcgen05.cp#
Call parameters#
T.cuda.tile.apply(T.ptx[opcode], *operands, scope="thread", **parameters)
Operand order: dst_tmem, src_shared.
Parameter |
Type / default |
Meaning |
|---|---|---|
|
static str; default “thread” |
Execution scope; group axes follow Explicit CUDA tile patterns. It does not elect an issuing thread. |
|
runtime bool; default None |
Execute the instruction when true; None is unconditional. A skipped asynchronous issue performs no completion accounting. |
Only the parameters listed here are instruction parameters. Opcode qualifiers are
written in T.ptx[opcode].
Data representation#
Both regions use scalar 32-bit storage words. Opcode shape gives (rows,words); multicast determines destination replicas. Decompression uses the same b32 footprint but explicitly changes the packed payload semantics.
Operand specification#
tcgen05.cp copies a descriptor-defined shared image into TMEM. It is issued by a
single thread, not collectively by the warps whose TMEM lanes receive the data. Select
shape, CTA group, multicast and optional decompression directly in the opcode.
TMEM shape and replicas#
Operands are dst_tmem, src_shared, both with suffix (row,b32_word) and 32-bit
storage elements. An earlier axis is an explicit repeat.
Shape / multicast |
Atom extents |
TMEM lanes |
|---|---|---|
|
|
Consecutive 128 lanes |
|
|
Consecutive 128 lanes |
|
|
0, 32, 64, 96 relative to the origin |
|
|
64 consecutive lanes, replicated 64 lanes apart |
|
|
Logical row r maps to |
|
|
32 consecutive lanes, four replicas 32 lanes apart |
The destination layout must state these replicas explicitly; the pattern does not infer multicast. Columns are consecutive b32 cells with a 128-bit aligned origin. The whole footprint, including replicas, must stay inside the 128-lane, 512-column TMEM address space. A CTA-group-2 opcode makes both CTAs copy from their own corresponding shared storage into their own TMEM. The launch, allocation and commit must use the matching CTA-group protocol.
Legality conditions#
Both regions end in the selected (rows,words). Shared must match one of the descriptor address walks in the operand specification. TMEM row/column mapping and replicas match the opcode exactly. Column origin is 128-bit aligned and the full image fits 128 lanes and 512 columns. Shared origins are 16B aligned, descriptor strides are nonnegative 16B multiples below 2^18, and swizzled allocations align to their full period.
Code generation#
Extract LDO/SDO/swizzle from the shared region using Shared matrix descriptors. For each repeat, encode the shared atom origin, compute the TMEM address from TLane/TCol, and emit one selected tcgen05.cp. A single elected thread issues it; allocation, proxy fencing, commit, wait and deallocation remain explicit caller code.
Examples#
Example: one 128x128b copy and readback#
from tvm.script import tirx as T
from tvm.backend.cuda.axis import TLane, TCol
from tvm.tirx.layout import S, TileLayout
tmem_layout = TileLayout(S[(128, 4) : (1 @ TLane, 1 @ TCol)])
@T.function
def copy_to_tmem(A: T.Tensor((128, 4), "uint32"), Out: T.Tensor((128, 4), "uint32")):
T.device_entry(launch=T.cuda.LaunchConfig(grid=1, block=128))
tid = T.cuda.thread_idx("x")
warp = T.cuda.warp_id()
shared = T.alloc_shared((128, 4), "uint32", layout="default", align=16)
address = T.alloc_shared((1,), "uint32", layout="default")
mbar = T.alloc_shared((1,), "uint64", layout="default")
if warp == 0:
T.ptx.tcgen05.alloc.cta_group__1.sync.aligned.shared__cta.b32(
T.address_of(address), T.uint32(32)
)
if tid == 0:
T.ptx.mbarrier.init.shared.b64(mbar.ptr_to([0]), T.uint32(1))
for j in range(4):
shared[tid, j] = A[tid, j]
T.cuda.cta_sync()
T.ptx.fence.proxy.async_.shared__cta()
mem = T.cuda.decl_tmem(
address[0], ty_args=[T.Tensor((128, 4), "uint32", scope="tmem", layout=tmem_layout)]
)
if tid == 0:
T.cuda.tile.apply(
T.ptx["tcgen05.cp.cta_group::1.128x128b"],
mem,
shared,
)
T.ptx.tcgen05.commit.cta_group__1.mbarrier__arrive__one.shared__cluster.b64(
mbar.ptr_to([0])
)
T.cuda.mbarrier_wait(mbar.ptr_to([0]), 0)
T.ptx.tcgen05.fence__after_thread_sync()
word = T.alloc_local((1,), "uint32", layout="default")
for j in range(4):
T.ptx["tcgen05.ld.sync.aligned.32x32b.x1.b32"](word[0], address[0] + T.uint32(j))
T.ptx.tcgen05.wait__ld.sync.aligned()
Out[tid, j] = word[0]
T.cuda.cta_sync()
if warp == 0:
T.ptx.tcgen05.relinquish_alloc_permit.cta_group__1.sync.aligned()
T.ptx.tcgen05.dealloc.cta_group__1.sync.aligned.b32(address[0], T.uint32(32))
One cp instruction copies 2048 bytes. Its commit triggers one barrier arrival, rather than a TMA-style expected-byte transaction. The scalar x1 readback issues are caller code and independently expose each TMEM word.
import numpy as np
import tvm
x = np.arange(512, dtype="uint32").reshape(128, 4)
compiled = tvm.tirx.compile(copy_to_tmem, target="cuda", pipeline="tirx")
out = tvm.runtime.tensor(np.zeros_like(x), tvm.cuda())
compiled(tvm.runtime.tensor(x, tvm.cuda()), out)
np.testing.assert_array_equal(out.numpy(), x)
Native FP4/FP6 decompression#
Append .b8x16.b4x16_p64 or .b8x16.b6x16_p32 to the opcode. The b32 footprint
above stays unchanged: each 16-byte shared block contains sixteen packed values followed
by eight bytes (FP4) or four bytes (FP6) of padding. The destination contains sixteen
expanded bytes. Four-bit values gain two low zero bits; six-bit values gain two high
zero bits. A 256-bit row contains two such blocks. The pattern emits one native
decompression instruction per repeat, with no software unpacking loop.
For instance, the example’s opcode can be explicitly replaced with
tcgen05.cp.cta_group::1.128x128b.b8x16.b4x16_p64 when the source data has been
prepared in that packed format. The reference then compares the expanded values, not
the original words or padding. A dtype name alone neither prepares the payload nor
chooses this qualifier.
Allocation, shared proxy fencing, election, commit, wait and deallocation are caller
code. pred gates the cp but does not adjust a surrounding completion protocol.
Implementation: patterns/tmem_copy.py and patterns/descriptors.py under
python/tvm/backend/cuda/tile.