Reimplement tiled matmul using the block-pointer abstraction instead of raw pointer arithmetic. For each output entry:
C[i,j]=k=0∑K−1A[i,k]⋅B[k,j]The arithmetic is unchanged. The differentiator is the pointer abstraction. The standard tile matmul builds 2D pointer tensors by hand and uses explicit per-axis masks. \texttt{tl.make_block_ptr} packages the base, shape, strides, current offsets, block shape, and traversal order into a single descriptor, and tl.advance walks it along the K axis.
Why it matters: the compiler sees the full tensor descriptor and can lower it directly to TMA descriptors on Hopper, emit out-of-bounds masking implicitly via boundary_check, and pick a layout that matches the tl.dot MMA tiles. The user-side code becomes shorter and the generated PTX tends to be cleaner because the bounds reasoning is centralized.
Input: A = [[1, 2], [3, 4]], B = [[5, 6], [7, 8]]
Output: [[19, 22], [43, 50]]
Input: A = [[1, 0, 1], [0, 1, 0]], B = [[1, 2], [3, 4], [5, 6]]
Output: [[6, 8], [3, 4]]
Build a_block_ptr with shape=(M,K), strides=(stride_am,stride_ak), offsets=(pid_m⋅BLOCK_M,0), and block_shape=(BLOCK_M,BLOCK_K). Mirror it for B and C.
Inside the K-loop, advance the block pointers by the K-block size instead of recomputing offsets. tl.advance returns a new block pointer at the next position.
With boundary_check on each load and the store, no manual M or N mask is needed - the block-pointer mechanism handles partial tiles implicitly.
torch.float32 on cuda.atol=1e-2 + rtol=1e-2 * abs(expected) for K≥128 to absorb fp32 accumulation drift.Accepts: array
Accepts: array
Submit a correct solution to see your kernel performance.
Reimplement tiled matmul using the block-pointer abstraction instead of raw pointer arithmetic. For each output entry:
C[i,j]=k=0∑K−1A[i,k]⋅B[k,j]The arithmetic is unchanged. The differentiator is the pointer abstraction. The standard tile matmul builds 2D pointer tensors by hand and uses explicit per-axis masks. \texttt{tl.make_block_ptr} packages the base, shape, strides, current offsets, block shape, and traversal order into a single descriptor, and tl.advance walks it along the K axis.
Why it matters: the compiler sees the full tensor descriptor and can lower it directly to TMA descriptors on Hopper, emit out-of-bounds masking implicitly via boundary_check, and pick a layout that matches the tl.dot MMA tiles. The user-side code becomes shorter and the generated PTX tends to be cleaner because the bounds reasoning is centralized.
Input: A = [[1, 2], [3, 4]], B = [[5, 6], [7, 8]]
Output: [[19, 22], [43, 50]]
Input: A = [[1, 0, 1], [0, 1, 0]], B = [[1, 2], [3, 4], [5, 6]]
Output: [[6, 8], [3, 4]]
Build a_block_ptr with shape=(M,K), strides=(stride_am,stride_ak), offsets=(pid_m⋅BLOCK_M,0), and block_shape=(BLOCK_M,BLOCK_K). Mirror it for B and C.
Inside the K-loop, advance the block pointers by the K-block size instead of recomputing offsets. tl.advance returns a new block pointer at the next position.
With boundary_check on each load and the store, no manual M or N mask is needed - the block-pointer mechanism handles partial tiles implicitly.
torch.float32 on cuda.atol=1e-2 + rtol=1e-2 * abs(expected) for K≥128 to absorb fp32 accumulation drift.Accepts: array
Accepts: array
Submit a correct solution to see your kernel performance.