CUDA C++/PTX intrinsics#
When no tile primitive covers what you need, two escape hatches reach the hardware
directly: call a backend intrinsic (the Tx.cuda.* / Tx.ptx.* namespaces
from tvm.backend.cuda), or inline raw CUDA source.
Calling backend intrinsics#
Tx.cuda.* and Tx.ptx.* expose the CUDA backend’s device intrinsics directly —
synchronization, mbarriers, reductions, and the PTX data-movement / MMA families:
Tx.cuda.cta_sync() # block barrier (__syncthreads)
Tx.cuda.warp_sync() # __syncwarp
Tx.cuda.warpgroup_sync(8) # warpgroup named-barrier ID 8
Tx.cuda.cta_sum(val, num_warps, scratch.ptr_to([0])) # block-level reduction
bar = Tx.alloc_shared((1,), "uint64")
if Tx.cuda.thread_rank() == 0: # one thread initializes the CTA-shared barrier
Tx.ptx.mbarrier.init.shared.b64(bar.data, Tx.uint32(1))
Tx.cuda.cta_sync() # initialization completes before any thread uses bar
Tx.cuda.mbarrier_wait(bar.data, phase)
A complete, runnable example — a warp all-reduce via Tx.tvm_warp_shuffle_xor:
@Tx.prim_func
def warp_reduce(A_ptr: Tx.handle):
A = Tx.match_buffer(A_ptr, (32,), "float32", align=16)
Tx.device_entry()
cta_id = Tx.cta_id([1]); warp_id = Tx.warp_id([1]); lane_id = Tx.lane_id([32])
v = Tx.alloc_local((1,), "float32"); i = Tx.alloc_local((1,), "int32")
v[0] = Tx.float32(31 - lane_id)
i[0] = 16
while i[0] >= 1:
v[0] += Tx.tvm_warp_shuffle_xor(0xFFFFFFFF, v[0], i[0], 32, 32)
i[0] = i[0] // 2
A[lane_id] = v[0]
The shuffle lowers straight to __shfl_xor_sync:
v_ptr[0] = v_ptr[0] + __shfl_xor_sync(0xFFFFFFFF, v_ptr[0], i_ptr[0], 32);
Other families under Tx.ptx.* / Tx.cuda.*: cp.async (LDGSTS),
cp.async.bulk.tensor (TMA), ldmatrix / stmatrix, tcgen05.*
(Blackwell MMA), atomic_add, fence … See CUDA Authoring and Support APIs for CUDA
helpers and Direct PTX Instructions for the registered PTX forms.
Inlining raw CUDA#
For something with no intrinsic at all, inject a __device__ function from a
source string with Tx.cuda.func_call(name, *args, source_code=..., return_type=...):
SRC = r"""
__device__ __forceinline__ float my_relu(float x) { return x > 0.f ? x : 0.f; }
"""
@Tx.prim_func
def k(A_ptr: Tx.handle, B_ptr: Tx.handle):
A = Tx.match_buffer(A_ptr, (256,), "float32")
B = Tx.match_buffer(B_ptr, (256,), "float32")
Tx.device_entry(); bx = Tx.cta_id([1]); tx = Tx.thread_id([256])
B[tx] = Tx.cuda.func_call("my_relu", A[tx], source_code=SRC, return_type="float32")
The source is emitted verbatim and the call is wired in:
__device__ __forceinline__ float my_relu(float x) { return x > 0.f ? x : 0.f; }
// ...
B_ptr[tx] = my_relu(A_ptr[tx]);