跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 24 章编译期、后端与芯片38 分钟15 习题8 自测题

NVIDIA 后端:代码生成与硬件特性

NVIDIA 后端如何把 tile 程序翻译成 CUDA 与 PTX:指令选择的谓词链、TMA 判定、warp specialization、cluster/DSM,以及 Blackwell 的 tcgen05 与 TMEM 带来的新约束。

本章进度状态 未读完习题 0 / 15自测 未作答

学习目标

读完本章,你应当能够:

  1. 说清一次 TileLang 编译在 NVIDIA 侧具体分成了哪几种产物(device 的 CUDA C / PTX / cubin、host 的 C wrapper、Python 侧的 adapter module),以及它们分别在哪个文件、哪个函数里被生成。
  2. 给定一段 T.gemm(A_shared, B_shared, C_local),判断它在 sm_75 / sm_80 / sm_90 / sm_100 上分别会被选成哪一种指令(MMA / WGMMA / TCGEN05 / FMA),并说出决定这件事的谓词顺序在哪个函数里。
  3. 判断一段 T.copy 会不会被降成 TMA(cp.async.bulk.tensor):知道默认路径下只有 store 会自动走 TMA,知道 load 想走 TMA 需要满足哪些显式条件。
  4. 读懂 TMA 的三段式:host 侧描述符编码 → device 侧单线程发射 + mbarrier 完成通知 → 消费者等待;能指出描述符结构体在生成的 CUDA 里叫什么、由哪一行 wrapper 代码写出。
  5. 说出 T.ws(role)、T.tma_copy(..., barrier=...)、T.barrier_wait/arrive 三者如何组合成一个 producer/consumer 流水线,以及编译器里的哪几个 pass 参与其中。
  6. 描述 Blackwell 的 tcgen05 / TMEM / block-scaled MMA 与 Hopper 的 WGMMA 在数据落在哪里这件事上的区别,以及 cluster / DSM 的用法与硬限制。
  7. 说出 TileLang 的 CuTe DSL 后端与 NVIDIA 官方 CuTe DSL、cuTile 的关系与差别。
  8. 用 _vendor/tilelang-v0.1.14 做对照,指出 v0.1.15 里与 NV 代码生成有关的具体变化,并说明你是怎么查的。

前置知识

  • 第 4 章「架构总览与一次编译的全流程」:Python 前端 → TIR → pass pipeline → codegen 的总图。
  • 第 20 章「IR 与编译期管线精读」:TIR 的结构、pass 的挂载方式、如何 dump IR。
  • 第 22 章「流水线、多缓冲与同步插入」:T.Pipelined、多缓冲、ThreadSync 的插入规则。
  • 第 6 章「芯片导论:NV GPU 与昇腾 NPU 的抽象对照」:SM / warp / shared memory 的基本概念。
  • 第 14 章「算子实战 II:GEMM 全家族」:你需要先知道 T.gemm 在算子层面是干什么的。
  • 术语前提:PTX 是 NVIDIA 的虚拟指令集,cubin / fatbin 是针对具体架构的机器码容器,nvcc 是离线编译器,NVRTC 是运行期编译器。本章会解释它们各自出现在哪。

本章不要求你有 NVIDIA GPU:所有结论都能用锁定版本的源码和 corpus 里的厂商文档核对。需要真卡的实验都标了「需要 GPU」。


1. 问题的由来

用户写的是这样一段东西(_vendor/tilelang-v0.1.15/examples/quickstart.py 风格的 GEMM+ReLU):

with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=threads) as (bx, by):
    A_shared = T.alloc_shared((block_M, block_K), in_dtype)
    B_shared = T.alloc_shared((block_K, block_N), in_dtype)
    C_local = T.alloc_fragment((block_M, block_N), accum_dtype)
    for ko in T.Pipelined(T.ceildiv(K, block_K), num_stages=3):
        T.copy(A[by * block_M, ko * block_K], A_shared)
        T.copy(B[ko * block_K, bx * block_N], B_shared)
        T.gemm(A_shared, B_shared, C_local)

这段代码里没有任何一行是 CUDA。它没有写 threadIdx,没有写 __syncthreads(),没有写 cp.async.bulk.tensor,也没有指定用 mma.sync 还是 wgmma。但最终执行的必须是三个具体的硬件事实的组合:

  1. 数据怎么搬——是用 SIMT 的 ld.global/st.shared,还是 cp.async,还是 TMA 的 cp.async.bulk.tensor;
  2. 算力怎么用——是用第 3/4 代 Tensor Core(mma.sync / wgmma),还是第 5 代(tcgen05),还是干脆退化成 FMA;
  3. 谁去做——是一个 block 里 256 个线程一起干,还是把其中一些 warp 专门分出来当 producer(只发拷贝)、另一些当 consumer(只算)。

NVIDIA 后端的职责,就是把上面这段 tile 级程序,在这三个维度上做决策,并把决策翻译成合法的 CUDA C 与 PTX。这一章讲的就是这三个决策在哪里发生、依据是什么、产物长什么样。

先给出一张全局图(图:01-codegen-path):TileLang 的 NVIDIA 路径不是「TIR → 一个后端」,而是「TIR → 若干个 pass → 一次 host/device 切分 → 两条并行的 codegen(device 走 CUDA C,host 走 C wrapper 或 TVM-FFI)→ 若干种运行期装载方式」。

NVIDIA 后端的全局路径:从 TIR 到可装载产物Python frontendtilelang/languageTIR module@T.prim_funcengine/lower.py::lower选后端、起流水线backend-agnostic passesPipelinePlanning → InjectSoftwarePipeline → LayoutInference → LowerTileOp(cu tile ops) → …tilelang/transformSplitHostDevice把模块切成 host_mod 与 device_modhost_moddevice_modhost_codegen(C / LLVM)target.build.tilelang_c_hostdevice_codegen(CUDA / CuTeDSL)target.build.tilelang_cuda → src/cuda/codegen/codegen_cuda.cc → CUDA C 源码nvcc / NVRTCContrib::nvcc.compile_cuda 或运行期编译 → cubin / fatbinTLCUDASourceWrapperjit/adapter/wrapper.py:把 device 源码与 host 的 call() 拼成一份 C 源码execution_backendcython / tvm_ffi / nvrtc / cutedsl
TIR 先过与后端无关的 pass 链,在 SplitHostDevice 处分成 host 与 device 两棵树:device 出 CUDA C(或 CuTe DSL),host 出 wrapper 的 call(),最后汇到执行后端。
  • 原块在 host 树下还单列了 extern "C" TL_EXPORT int call(...) 与 cuTensorMapEncodeTiled(...) 两个方框(有 TMA 时才有),本图并入 TLCUDASourceWrapper 的说明以免节点过多。
  • 这张图与正文 §1 引的 figures/01-codegen-path 是同一件事:那份展开到具体文件与函数名,这份是正文里的简版。

后面每一节都会把这张图里的某个方框打开。


一次编译的产物路径(NVIDIA)Python frontendtilelang/language/*TIR module@T.prim_funcengine/lower.py::lowercreate_backend_context → 选后端CUDA_PIPELINEtilelang/cuda/pipeline.pyPipelinePlanning → InjectSoftwarePipelineLayoutInference → LowerTileOpT.gemm / T.copy 的指令选择LowerHopperIntrin → SplitHostDevice描述符提升 + host/device 切分host_modTLCUDASourceWrapper → call()device_modCodeGenTileLangCUDA → CUDA C 源码nvcc / NVRTC编出 cubin 或 fatbinsource + init() + call()TLCUDASourceWrapper 拼装的模块execution_backendcython / tvm_ffi / nvrtc / cutedsl
TIR 走完 CUDA pass 链后在 SplitHostDevice 分成两棵产物树:device 出 CUDA C(或 CuTe DSL),host 出 wrapper 的 call()。
  • device 与 host 是两棵独立的产物树,在 SplitHostDevice 处分开;device 函数是 calling_conv = kDeviceKernelLaunch 的那一半。
  • 默认 enable_host_codegen 与 enable_device_compile 都是 False:device 只产源码不调 nvcc,host 由 adapter 自己生成 wrapper。
  • 三种 execution backend(cython / tvm_ffi / nvrtc)共用同一个 TLCUDASourceWrapper,所以生成的 kernel 源码相同。

2. 概念与模型

2.1 一次编译,两种产物

关键在于:TileLang 的 device 代码和 host 代码是分开生成的,而且默认不在同一步生成。

看 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py 里的 _lower_with_context_impl(_vendor/tilelang-v0.1.15/tilelang/engine/lower.py:174-213)。它的核心顺序是:

host_mod, device_mod, params, target, target_host = lower_to_host_device_ir(func_or_mod, context, runtime_only=...)
codegen_mod = device_codegen(device_mod, context) if enable_device_compile else device_codegen_without_compile(device_mod, context)
kernel_source = codegen_mod.inspect_source()
if enable_host_codegen:
    host_mod = host_codegen(host_mod, context)

注意两个默认值(_vendor/tilelang-v0.1.15/tilelang/engine/lower.py:144-170 的 docstring 说得很直白):enable_host_codegen 和 enable_device_compile 默认都是 False。原因是——JIT adapter 会自己接管这两件事。

  • device 默认走「只生成 CUDA C 源码、不调用 nvcc」的路径(device_codegen_without_compile),源码保存在 module 的 source_map["cuda"] 里(见 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:151-176)。
  • host 默认由 adapter(cython / tvm_ffi / nvrtc)自己生成 wrapper,而不是走 TVM 的 codegen_c_host。

host/device 的切分点在 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:239-240:

mod = tilelang.transform.AnnotateDeviceRegions()(mod)
mod = tilelang.transform.SplitHostDevice()(mod)

切完之后,lower_to_host_device_ir 用两个 Filter 把模块一分为二(_vendor/tilelang-v0.1.15/tilelang/engine/lower.py:138-139):带 calling_conv = kDeviceKernelLaunch 的函数进 device_mod,其余进 host_mod。这个判据在 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:26-51 的 has_device_kernel_launch / is_device_call_c_device 里。

于是「设备核函数」这一段是纯粹的 __global__,而「怎么启动它、怎么把 cudaStream_t 传进来、怎么设置 dynamic shared memory 大小」全部落在 host 侧的 wrapper 上。

2.2 三层指令选择

「T.gemm 会变成什么指令」这个问题,其实要穿过三层:

层 位置 它决定什么
语言层 _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py 用户能写哪些参数(transpose_A、policy、mbar、use_2cta),以及 T.gemm / T.wgmma_gemm / T.tcgen05_gemm 这几个显式变体的语义
图选择层 _vendor/tilelang-v0.1.15/src/op/gemm.cc → _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc 按 target 分派到 CUDA 实现,并在 Gemm::SelectInst 里按谓词顺序选出 "cuda.mma" / "cuda.wgmma" / "cuda.tcgen05" / "cuda.fma" 等
宏生成层 _vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/gemm_*.py + _vendor/tilelang-v0.1.15/tilelang/cuda/intrinsics/macro/*.py 把选中的指令展开成具体的 PTX 内联汇编序列、寄存器分配、shared memory swizzle 布局

三层里,图选择层是最值得先读的,因为它是一个几十行、可以完整读完的谓词链。

2.3 四代「搬运」

同一个 T.copy,可能落到四代不同的搬运机制上:

  1. SIMT 同步搬运(ld.global + st.shared):所有架构都有,CopyInst::kNormal。
  2. cp.async(Ampere / sm_80+):异步但仍是「每个线程负责一段」的老式异步拷贝,对应 CopyInst::kCPAsync。
  3. TMA(Hopper / sm_90+):cp.async.bulk.tensor,由一个线程发射、硬件按描述符做地址生成,对应 CopyInst::kBulkLoad / kBulkStore / ...1D。
  4. DSM 集群内搬运(Hopper+):cp.async.bulk.shared::cluster 或 map_shared_rank,把数据直接推进另一个 CTA 的 shared memory。

「TMA 到底什么时候被选中」在 4.5 节;这里先记住结论:默认的 T.copy 只有 global←shared 的 store 会自动走 TMA,global→shared 的 load 不会。

2.4 角色划分(warp specialization)

Hopper 之后,「谁来发拷贝」这件事本身成了一个可编程的维度。CUDA 编程指南里对这件事的解释来自 Hopper 的架构文档(NVIDIA Hopper Architecture In-Depth 第 295 行):

The TMA operation is asynchronous and leverages the shared memory-based asynchronous barriers introduced in A100. Also, the TMA programming model is single-threaded, where a single thread in a warp is elected to issue an asynchronous TMA operation ... To further improve performance, the H100 SM adds hardware to accelerate these asynchronous-barrier wait operations.

FlashAttention-3 的论文把这件事讲成了方法论(arXiv:2407.08608 第 173 行):

Hardware support for asynchrony allows for warp-specialized kernels, where the warps of a CTA are divided into producer or consumer roles that only ever issue either data movement or computation.

TileLang 把这件事暴露成了 T.ws(role)(2.4 节的「语言层」),并提供了一条「用户自己写角色划分」和一条「编译期自动划分」的路。细节在 4.6 节。


3. 用法

本节只讲「你写什么、会看到什么」。所有需要 GPU 的示例都在 code/ 里给了静态核对版本。

3.1 打印生成的 CUDA

kernel = matmul(a, b, block_M, block_N, block_K, ...)   # @tilelang.jit 产物
print(kernel.get_kernel_source())

get_kernel_source() 拿到的是 device 侧的 CUDA C。你会看到几件固定特征:

想看到 PTX 和 cubin 需要真卡/真工具链:_vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:271-296 提供了 get_ptx_from_source,_vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:309-366 提供了 get_sass_from_source。

3.2 让 T.copy 变成(或明确不要)TMA

CUDA 方言给 T.copy 加了三个提示关键字(_vendor/tilelang-v0.1.15/tilelang/cuda/language/copy_op.py:19-70):

T.copy(A_g, A_s)                                 # 默认:load 一般不会走 TMA
T.copy(A_g, A_s, prefer_instruction="tma")       # 明确要求 load 走 TMA(约束不满足则编译报错)
T.copy(C_s, C_g, prefer_instruction="tma")       # store 本来就默认会尝试 TMA
T.copy(A_g, A_s, disable_tma=True)               # 明确禁止这一条 copy 走 TMA
T.copy(A_g, A_s, eviction_policy="evict_last")   # L2 驱逐提示

需要显式异步语义、自己管同步时:

T.async_copy(A_g, A_s)            # 只发 cp.async + commit_group,不插入 wait
T.ptx_wait_group(0)               # 你自己负责在消费前等

mbar = T.alloc_barrier(arrive_count=1)
T.tma_copy(A_g, A_s, barrier=mbar)   # 只发 expect_tx + tma_load,不插入 wait
T.mbarrier_wait_parity(mbar, k % 2)  # 你自己等

T.tma_copy 与 T.copy 的差别在 docstring 里写得很清楚(_vendor/tilelang-v0.1.15/tilelang/language/copy_op.py:232-300):T.copy 发的是完整同步序列(arrive + load + wait),T.tma_copy 只发 producer 那半边,等待由你写。

3.3 手写一个 warp-specialized GEMM

官方示例 _vendor/tilelang-v0.1.15/examples/warp_specialize/example_warp_specialize_gemm_copy_0_gemm_1.py 是最小版本:

with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=256) as (bx, by):
    A_shared = T.alloc_shared((block_M, block_K), dtype)
    B_shared = T.alloc_shared((block_K, block_N), dtype)
    C_local  = T.alloc_fragment((block_M, block_N), accum_dtype)
    data_is_ready   = T.alloc_barrier(arrive_count=128)
    compute_is_done = T.alloc_barrier(arrive_count=128)

    with T.ws(1):
        T.clear(C_local)

    for ko in T.Pipelined(T.ceildiv(K, block_K), num_stages=0):
        with T.ws(0):                                  # producer 角色
            T.barrier_wait(compute_is_done, (ko + 1) % 2)
            T.tma_copy(A[by * block_M, ko * block_K], A_shared, barrier=data_is_ready)
            T.tma_copy(B[ko * block_K, bx * block_N], B_shared, barrier=data_is_ready)
            T.barrier_arrive(data_is_ready)
        with T.ws(1):                                  # consumer 角色
            T.barrier_wait(data_is_ready, ko % 2)
            T.gemm(A_shared, B_shared, C_local)
            T.barrier_arrive(compute_is_done)

    with T.ws(1):
        T.copy(C_local, C[by * block_M, bx * block_N])

三个要点:

  1. T.ws(i) 的 i 是 warp group 序号(一个 warp group = 128 线程),不是线程号。T.ws(0) 展开成 tx < 128,T.ws(1) 展开成 128 <= tx < 256(_vendor/tilelang-v0.1.15/tilelang/cuda/language/warpgroup.py:19-59,注释里直接写了这个映射)。
  2. 两个角色之间用 两个 mbarrier 交替握手:data_is_ready 由 producer 发、consumer 等;compute_is_done 由 consumer 发、producer 等。
  3. T.ws 的分支里用的是 T.tma_copy(而不是 T.copy),因为同步由 mbarrier 显式承担。

注意上面这个例子用 num_stages=0:多缓冲不再由 T.Pipelined 插入,而是由 mbarrier 的双缓冲语义自己承担((ko+1) % 2 与 ko % 2 的奇偶翻转)。同一目录下的 example_warp_specialize_gemm_softpipe_stage2.py 则保留 num_stages=2,让软件流水与角色划分叠加。

3.4 Blackwell:TMEM 与 block-scaled GEMM

Blackwell(sm_100)上,第 5 代 Tensor Core 的累加器不再放在寄存器里,而放在 Tensor Memory(TMEM)。用法(_vendor/tilelang-v0.1.15/examples/gemm_tcgen05/gemm_tcgen5mma.py):

C_tmem = T.alloc_tmem([block_M, block_N], accum_dtype)
mbar   = T.alloc_barrier(1)                      # TCGEN5MMA 完成通知,arrive 数 = 1
for k in T.Pipelined(T.ceildiv(K, block_K), num_stages=num_stages):
    T.copy(A[by * block_M, k * block_K], A_shared)
    T.copy(B[bx * block_N, k * block_K], B_shared)
    T.tcgen05_gemm(A_shared, B_shared, C_tmem, trans_A, trans_B, mbar=mbar, clear_accum=k == 0)
    T.mbarrier_wait_parity(mbar, k % 2)
T.copy(C_tmem, C_local)                          # TMEM → fragment

如果你不想要手写的 mbarrier_wait_parity,用 T.gemm(..., mbar=mbar),编译器会在发射之后隐式插入一次 mbarrier_wait_parity(_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py 的 gemm docstring,第 41-76 行)。

Block-scaled MMA(MXFP8/MXFP4 这类「每 32 个元素共享一个指数」的低精度格式)在 CUDA 方言里是:

T.gemm_blockscaled(A, B, C, SFA, SFB,
                   k_start=..., sf_a_granularity_k=128, sf_b_granularity_k=128,
                   mbar=mbar)          # SM100:C 在 TMEM
T.gemm_blockscaled(A, B, C, SFA, SFB, ..., sf_layout="rowmajor")
                                       # SM120:C 在 fragment

对应的官方示例是 _vendor/tilelang-v0.1.15/examples/blockscaled_gemm_sm100/gemm_mxfp8_blockscaled_1d1d.py(SM100,C 在 TMEM)与 _vendor/tilelang-v0.1.15/examples/gemm_sm120/sm120_nvfp4_blockscaled_gemm.py(SM120,C 在 fragment)。

3.5 Cluster 与 DSM

with T.ClusterKernel(grid_x, grid_y, threads=128, cluster_dims=(4, 1, 1)) as (bx, by):
    rank = T.block_rank_in_cluster()          # 0..3
    T.cluster_sync()
    T.copy_cluster(A[by * BM, bx * BN], A_s, cluster_mask=0b0011)   # TMA multicast

T.Kernel(..., cluster_dims=(4,1,1)) 与 T.ClusterKernel(...) 等价,后者只是把 cluster_dims 变成必填(_vendor/tilelang-v0.1.15/tilelang/cuda/language/kernel.py 的 Kernel docstring,第 26-40 行)。

T.copy_cluster 有两个用途(官方文档 _vendor/tilelang-v0.1.15/docs/programming_guides/cluster_tma.md):

  • cluster_mask=<bitmask>:TMA multicast,一个 CTA 把同一块数据广播给 mask 里的所有 CTA,省 DRAM 带宽;
  • dst_block=<rank>[, remote_barrier=...]:SM-to-SM,把本 CTA 的 shared 直接推进邻居 CTA 的 shared。

3.6 CuTe DSL 后端

把 target 指定成带 cutedsl key 的 CUDA target,或把 execution_backend 指定成 "cutedsl":

kernel = tilelang.compile(func, target={"kind": "cuda", "arch": "sm_90"}, execution_backend="cutedsl")

它和普通 CUDA 后端的差别只在产物形态:device 端不再生成 CUDA C,而是生成 CuTe DSL 的 Python;_vendor/tilelang-v0.1.15/tilelang/jit/adapter/cutedsl/checks.py 会先检查 nvidia-cutlass-dsl>=4.7.0 与 cutlass.cute 是否可导入。


4. 实现机制

T.gemm 的指令选择判定树T.gemm / T.wgmma_gemm / T.tcgen05_gemm显式 wgmma?是 → cuda.wgmma否 → 显式 tcgen05?是 → cuda.tcgen05否 → use_2cta?否 → AllowTcgen5Mma?sm_100 且 C 在 TMEM 且形状在 meta 白名单否 → AllowWgmma?sm_90 且 M≥64 且 warp 数是 4 的倍数否 → sm_70 / sm_75 且 native MMA 不可用?是 → cuda.fma否 → cuda.mma
Gemm::SelectInst 按固定谓词顺序依次排除:显式变体 → use_2cta → TCGEN05 → WGMMA → FMA → MMA。
  • 判定树里每一层的「否 →」指向下一个谓词,「是 →」指向选中的指令,顺序与 Gemm::SelectInst 完全一致。
  • 显式变体与 use_2cta 的约束不满足时是编译期报错(FatalWgmmaUnavailable / FatalTcgen5Unavailable),不静默降级。
  • AllowTcgen5Mma 的三条硬条件:TargetIsSm100、A 在 shared 或 TMEM 且 B 在 shared 且 C 在 TMEM、形状在 GetTCGEN5MMAMeta 白名单里。
  • AllowWgmma 的三条硬条件:Hopper、M ≥ 64、num_warps % 4 == 0,外加 CheckWgmma 的 dtype / 转置 / K 整除约束。
T.copy 的搬运指令选择顺序1. gather4 / scatter4 标注是 → BulkLoadGather4 / BulkStoreScatter42. cluster_mask != 0是 → 必须 kBulkLoad(multicast),否则报错3. 显式 T.tma_copy→ TMA;约束不满足直接报错,不降级4. 显式 async / force_cp_async→ kCPAsync;不满足则报错5. prefer_instructiontma → TMA;cp_async → cp.async;sync → 同步拷贝6. 无任何提示(默认路径)只对 store 自动试 TMA:allow_load=false, allow_store=true7. 兜底 SelectSyncLikeInstLDSM → STSM → TMem → Normal,注意不是 cp.async
SelectCopyInstForLowering 按标注、cluster、显式 TMA、显式 async、prefer_instruction 的顺序选路,最后才走默认兜底。
  • load 想走 TMA,只有显式 T.tma_copy、prefer_instruction="tma"、或 warp-specialized producer 分支三种途径。
  • TMA 硬条件:arch ≥ 90、作用域必须是 global ↔ shared、最后一维是 128 位的整数倍、最内层 stride 为 1。
  • 1D bulk 路径额外要求 global 区域连续、shared 布局语义线性、两侧元素数相等、总位数 16 字节对齐。
TMA 描述符的流转发射arrive前端(Python)T.copy(A_g, A_s) / T.tma_copy(...)Copy::LowerBulk选 CopyInst,构造 TMADescLowerHopperIntrin收集 create_tma_descriptor 语句TLCUDASourceWrapper(host)生成 cuTensorMapEncodeTileddevice kernel__grid_constant__ const CUtensorMap 参数TMA 硬件单元elected 线程发 cp.async.bulk.tensor.Nd消费者等 mbarrier 的 paritymbarrier_wait_parity
描述符在 device IR 里构造、由 LowerHopperIntrin 提升到 host 编码,再以 __grid_constant__ 参数传入 kernel,由单线程发射并用 mbarrier 完成通知。
  • LowerHopperIntrin 把描述符初始化语句收集成 host 函数的属性 tma_descriptor_args。
  • 描述符在生成的 CUDA 里就是 __grid_constant__ const CUtensorMap 参数,由 codegen 从 storage_scope == "grid_constant" 打印出来。
  • mbarrier 原语在 tl_templates/cuda/barrier.h:mbarrier_init / mbarrier_arrive_expect_tx / mbarrier_wait;store 方向用 tma_store_arrive / tma_store_wait。
  • 普通 T.copy 走 TMA load 时由 lower_args.alloc_mbarrier(1) 自动分配 barrier;T.tma_copy 没给 barrier 则 LOG(FATAL)。
warp specialization 的角色与握手producer:T.ws(0),tx < 128consumer:T.ws(1),128 ≤ tx < 256第 1 步:等待barrier_wait(compute_is_done, (ko+1)%2)barrier_wait(data_is_ready, ko%2)第 2 步:发射 / 计算tma_copy(A_g → A_s, barrier=data_is_ready)T.gemm(A_s, B_s, C_local)第 3 步:发射 / 通知tma_copy(B_g → B_s, barrier=data_is_ready)barrier_arrive(compute_is_done)第 4 步:通知barrier_arrive(data_is_ready)下一轮回到第 1 步
producer warp group 只发 TMA、consumer warp group 只算,两者用 data_is_ready 与 compute_is_done 两个 mbarrier 交替握手。
  • T.ws(i) 的 i 是 warp group 序号,一个 warp group = 128 线程;T.ws(0) 展开成 tx < 128。
  • 两个 mbarrier 交替握手:producer 等 compute_is_done、发 data_is_ready,consumer 等 data_is_ready、发 compute_is_done。
  • 编译期顺序:AutoWarpSpecialization → MaterializeWSSchedule → ProducerConsumerWarpSpecialized → … → AnnotateWarpGroupRegAlloc;WS 只在 CUDA 且 have_tma(target)(arch ≥ 9)时开启。
v0.1.14 → v0.1.15 在 NV codegen 上的变化图目录文件变化src/cuda/opgemm_blockscaled.cc新增:blockscaled 专用分派(从 gemm.cc 移出)src/cuda/opcopy_analysis.ccCopyInstIsTMA 拆成 Load/Store 两个;ClassifyWarpSpecializedCopy 泛化src/cuda/transformauto_schedule.cc新增:自动 warp specialization 入口与 role_based 调度器src/cuda/transformws_analysis.{h,cc}新增:TileStmtKind 分类(从 producer_consumer_ws.cc 抽出)src/cuda/transformmaterialize_ws_schedule.cc大幅扩容(该文件 diff 有 3312 行)src/cuda/codegencodegen_cuda.{cc,h}新增 EmitPackedX2Call / EmitPerLaneScalarCall;int8x64 改为 longlong4tilelang/cudapipeline.py插入 AutoWarpSpecialization;MaterializeKernelLaunch 参数化tilelang/transformpass_config.py新增 tl.enable_auto_warp_specializationtilelang/jitadapter/wrapper.pyNV 部分只有 uint64 类型映射;其余变化来自 Ascendtilelang/contribnvcc.py / nvrtc.py两版之间无差异(diff -rq 未列出)
用 diff -rq 对比两棵浅克隆的 vendor 树得到的文件级改动;因为仓库没有提交历史,结论全部来自文件 diff。
  • TMEM 判断从字面量 scope 改成谓词:op.c_.scope() == "shared.tmem" 变成 IsTmemBuffer(op.c_)。
  • 两个 vendor 树都是浅克隆,没有提交历史,无法用 git log 看改动原因。

4.1 代码生成路径:入口与结构

4.1.1 Python 侧的注册表

从 v0.1.15 起,后端是显式注册的。_vendor/tilelang-v0.1.15/tilelang/cuda/backend.py:123-143 用 register_backend 声明了 CUDA 后端的全部组件:

BACKEND = register_backend(
    BackendModule(
        name="cuda",
        target_kinds=("cuda",),
        supports_target=codegen.is_plain_cuda_target,
        pipelines={"cuda": pipeline.CUDA_PIPELINE},
        device_codegens={"cuda": DeviceCodegen("cuda",
                          build=codegen.build_cuda,
                          build_without_compile=codegen.build_cuda_without_compile)},
        execution_backends=execution_backend.CUDA_EXECUTION_BACKENDS,
        host_codegens=STANDARD_HOST_CODEGENS,
        callbacks={"tilelang_callback_cuda_validate": ...,
                   "tilelang_callback_cuda_compile": ...},
    )
)

读这张表你就知道 NV 路径一共有哪些可选择的东西:

4.1.2 设备端:CUDA C 是怎么印出来的

真正的打印器是 C++ 的 CodeGenTileLangCUDA(_vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc,6631 行)。它的注册入口在 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:182-184:

refl::GlobalDef()
    .def("target.build.tilelang_cuda", BuildTileLangCUDA)
    .def("target.build.tilelang_cuda_without_compile", BuildTileLangCUDAWithoutCompile);

BuildTileLangCUDA(_vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:99-139)做四件事:

  1. ValidateUniqueDeviceGlobalSymbols(mod):保证同一模块里没有重名的 __global__。
  2. 调 Python 注册的 tilelang_callback_cuda_validate。
  3. 对每个 PrimFunc 调 cg.AddFunction(gvar, f);AddFunction 是打印器的主体(_vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:6509-6628)。
  4. 调 tilelang_callback_cuda_compile 把源码编成 cubin/fatbin,然后用 target::CUDAModuleCreateWithFallback 包成 TVM CUDA module。

AddFunction 里几个值得记住的细节:

4.1.3 编译成 cubin:nvcc 还是 NVRTC

两条路都通:

生成 SASS/PTX 的辅助函数在 _vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:271-366。这些函数在本章的写作环境里没有运行(无 CUDA 工具链),只做了源码核对。

4.2 host/device 切分与 wrapper 生成

4.2.1 为什么必须切

MakePackedAPI(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:273)把 host 侧的 PrimFunc 变成带 tvm_access_ptr 参数、带 launch 参数的 packed 函数;LowerDeviceKernelLaunch(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:275)把「启动」这一步变成对设备核函数的调用记录。之后 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:138-139 才用 Filter 把它们分开。

切分之后,device 模块里只剩下 calling_conv = kDeviceKernelLaunch 的函数。这就是为什么 TLCUDASourceWrapper.parse_source_information 可以断言「device module 至少一个函数、host module 恰好一个函数」——_vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:466-470。

4.2.2 wrapper 干的事

_vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py 里的 TLCUDASourceWrapper(第 222 行起)负责把「一块 __global__ 源码」包装成一个可以被 Python 调用的 call()。它的 update_lib_code(第 585-632 行)把三段拼起来:

lib_code = self.source + self.get_cuda_host_adapter_include() + init_func + host_func
  • self.source 是 device 侧 CUDA C;
  • init_func 来自 PREDEF_INIT_FUNC(第 68-79 行),生成 extern "C" TL_EXPORT int init(),里面按需生成 cudaFuncSetAttribute(..., cudaFuncAttributeMaxDynamicSharedMemorySize, N)(模板见 PREDEF_ATTRIBUTE_SET_DYNAMIC_MEMORY,第 26-33 行);
  • host_func 来自 PREDEF_HOST_FUNC(第 81-85 行),生成 extern "C" TL_EXPORT int call(...)。

call() 的正文由 create_dispatch_func(第 289-393 行)生成。启动部分有两种模板:

  • 普通启动 → KERNEL_LAUNCH_FUNC_CODE(第 161-176 行):
cudaLaunchConfig_t config;
cudaLaunchAttribute attribute[1];
attribute[0].id = cudaLaunchAttributeProgrammaticStreamSerialization;
attribute[0].val.programmaticStreamSerializationAllowed = 1;
config.gridDim = ...; config.blockDim = ...; config.dynamicSmemBytes = ...;
cudaLaunchKernelEx(&config, kernel, args...);
  • 带 cluster → KERNEL_CLUSTER_LAUNCH_FUNC_CODE(第 178-200 行):多一条 cudaLaunchAttributeClusterDimension,并且先调 cudaFuncSetAttribute(kernel, cudaFuncAttributeNonPortableClusterSizeAllowed, 1);这个 attribute 名与 Blackwell Tuning Guide 第 73 行 里厂商文档说的一致。

选择哪一个由 get_kernel_launch_code(第 567-571 行)根据 self.cluster_dims[function_name] 是否为 None 决定;cluster_dims 又是从 device 函数的 cluster_dims 属性读出来的(第 494-497 行)。

grid/block 的数值来自 device 函数的 thread_extent 属性(第 487-493 行)——这正是 MaterializeKernelLaunch 在 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:70 写进去的那份信息。注意 v0.1.15 这里传了 lower_thread_binding=True, default_threads=128,v0.1.14 是裸调用(见 4.10 节)。

4.2.3 三种 execution backend

_vendor/tilelang-v0.1.15/tilelang/cuda/execution_backend.py:24-31 定义了三种:

backend 它拿什么 谁来调 call()
tvm_ffi(默认) TVM-FFI 的 host module(enable_host_codegen=True) TVM 的 packed 调用协议
cython TLCUDASourceWrapper 生成的 C 源码 → LibraryGenerator.compile_lib 编成 .so(_vendor/tilelang-v0.1.15/tilelang/jit/adapter/libgen.py:41-80) CythonKernelWrapper(_vendor/tilelang-v0.1.15/tilelang/jit/adapter/cython/adapter.py:31-35)
nvrtc 运行期用 NVRTC 编 NVRTCLibraryGenerator 生成的 Python launcher(_vendor/tilelang-v0.1.15/tilelang/jit/adapter/nvrtc/libgen.py:37-50)

三种 backend 共用同一个 TLCUDASourceWrapper,所以生成的 kernel 源码是一样的,区别只在装载与调用方式。这一点很重要:你读 get_kernel_source() 得到的结论对三种 backend 都成立。

4.3 T.gemm 的指令选择

4.3.1 分派:先按 target 找实现

T.gemm 在前端是一个 tl.tileop.gemm 调用(_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:32-76 的 _gemm_impl 路径)。LowerTileOp 阶段,_vendor/tilelang-v0.1.15/src/op/gemm.cc:33-48 的 ResolveGemmImpl 遍历注册表找匹配目标的那一份实现;CUDA 由 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:407-422 注册:

RegisterGemmImpl(GemmImpl{"cuda.Gemm", MatchCudaGemmTarget,
                          cuda::Gemm::SelectInst,
                          cuda::Gemm::ComputeWarpPartition,
                          cuda::Gemm::ReuseExistingSharedLayout});

GemmImpl 把「选指令」「算 warp 划分」「是否复用已有 shared layout」三件事拆成三个函数指针,这是这段代码好读的原因。

4.3.2 谓词链

Gemm::SelectInst(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:336-375)的完整顺序是:

if (op.isWgmma_)   { if (!AllowWgmma(...)) FatalWgmmaUnavailable(...); return kCudaWGMMA; }
if (op.isTcgen05_) { if (!AllowTcgen5Mma(...)) FatalTcgen5Unavailable(...); return kCudaTCGEN05; }
if (Use2CtaRequested(op)) { ... return kCudaTCGEN05; }   // 2CTA 没有单 CTA 回退
if (AllowTcgen5Mma(op, target))  return kCudaTCGEN05;
if (AllowWgmma(op, block_size, target)) return kCudaWGMMA;
if (TargetIsVolta(target)  && !AllowVoltaMma(op))  return kCudaFMA;
if (TargetIsTuring(target) && !AllowTuringMma(op)) return kCudaFMA;
return kCudaMMA;

翻译成人话:

  1. 用户显式点名(T.wgmma_gemm / T.tcgen05_gemm)的,就必须走对应指令;约束不满足就编译期报错,不静默降级(FatalWgmmaUnavailable / FatalTcgen5Unavailable,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:157-181)。
  2. 标了 use_2cta 的,必须是 TCGEN05,且同样没有回退——注释写明了原因(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:347-353):「公开的 2CTA 形状约定每个 CTA 只提供 B 的一半 N 范围,回退到单 CTA 指令会变成静默越界,而不是合法降级」。
  3. 否则先试 TCGEN05(AllowTcgen5Mma),再试 WGMMA(AllowWgmma)。
  4. sm_70 / sm_75 上,如果连 Volta/Turing 的 MMA 形状都不满足,就退到 cuda.fma(纯 FMA)。
  5. 其余(Ampere)走 cuda.mma。

4.3.3 每个谓词看什么

AllowTcgen5Mma(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:80-87):

bool scope_ok = (IsSharedBuffer(op.a_) || IsTmemBuffer(op.a_)) &&
                IsSharedBuffer(op.b_) && IsTmemBuffer(op.c_);
if (!TargetIsSm100(target) || !scope_ok) return false;
DataType ab_dtype = IsTmemBuffer(op.a_) ? op.b_->dtype : op.a_->dtype;
return GetTCGEN5MMAMeta(op.m_, op.n_, op.k_, ab_dtype, op.c_->dtype).first;

三件事同时成立才会选 TCGEN05:目标是 sm_100;A 在 shared 或 TMEM、B 在 shared、C 在 TMEM;并且 (M,N,K,dtype) 组合在 GetTCGEN5MMAMeta 的白名单里。所以「我的 T.gemm 为什么没用 tcgen05」的第一诊断点就是 C 是不是 T.alloc_tmem 出来的。

GetTCGEN5MMAMeta(_vendor/tilelang-v0.1.15/src/op/tcgen5_meta.h:22-177)里,f16/bf16 → f32 的合法形状是 atom_k=16,atom_n 按 8 或 16 的步长下降,atom_m ∈ {32,64,128,256};enable_ws 只在 M % 64 == 0 或 M % 32 == 0(且没禁用 ws)时才置位,enable_2cta 只在 M % 128 == 0 或 M % 64 == 0 且没禁用 2cta 时才置位——这与 PTX ISA 文档里的 shape 表完全对应(Parallel Thread Execution ISA 第 29795–29810 行:.ws 形态 32xNxK/64xNxK/128xNxK,N = {64,128,256},K=16)。

AllowWgmma(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:89-97):

return !pass_ctx->GetConfig(kDisableWGMMA, false) && TargetIsHopper(target) &&
       op.m_ >= 64 && num_warps % 4 == 0 && CheckWgmma(op);

三个条件:Hopper(sm_90x)、M >= 64、warp 数能被 4 整除(因为 WGMMA 是 warpgroup 级、一次 128 线程)。CheckWgmma(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:40-78)再看 B 是否在 shared、以及 dtype 与 K 的整除关系,例如:

  • f16×f16:K % 16 == 0,不限制转置;
  • fp8×fp8:要求 !transA_ && transB_(即 A 是 K-major、B 是 N-major)且 K % 32 == 0;
  • tf32:要求 !transA_ && transB_ 且 K % 8 == 0;
  • int8:要求 (!transA_) && transB_ 且 K % 32 == 0。

这就是「transpose 如何影响指令」的第一处答案:fp16/bf16 对转置不敏感,但低精度格式(fp8/tf32/int8)在 WGMMA 上只支持特定的 MN-major/K-major 组合——这与 FlashAttention-3 论文 §2.2 的表述一致(arXiv:2407.08608 第 186–190 行:FP16 WGMMA 两种主序都接受,FP8 WGMMA 只支持 k-major)。

4.3.4 warp 划分

选完指令之后,ComputeWarpPartition(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:377-397)决定 m_warp × n_warp:

policy 参数(GemmWarpPolicy.FullRow / FullCol / Square)就作用在这里:FullRow 尽量把 num_warps 全分给 M 方向,FullCol 全给 N 方向,Square 取「每 warp 的 M/N 比最接近 M/N」的那一档。找不到合法划分时 LOG(FATAL) 并给出具体形状与建议(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:195-212)。

4.3.5 python 侧的宏生成

选中的指令字符串决定用哪个 python 实现——_vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/__init__.py:54-63 的注册表:

指令 python 实现 关键行为
cuda.mma gemm_mma.py → GemmMMA 需要 ldmatrix 把 operand 搬进 fragment
cuda.wgmma gemm_wgmma.py → GemmWGMMA 直接从 shared 读 operand,只需要 swizzle 布局
cuda.tcgen05 gemm_tcgen05.py → GemmTCGEN5 累加器在 TMEM,SS/TS 两种变体
cuda.tcgen05.blockscaled gemm_tcgen05_blockscaled.py block-scaled(v0.1.15 新增,见 4.10 节)
cuda.mma.blockscaled gemm_mma_sm120.py SM120 的 warp 级 block-scaled
cuda.fma gemm_fma.py 无 Tensor Core 的标量路径

GemmWGMMA.infer_shared_layout(_vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/gemm_wgmma.py:22-40)是「layout 由指令决定」的典型例子:它按连续维长度选 128B/64B/32B swizzle 或线性布局——

vectorized_size = 128 // self.a_dtype.bits
if continuity % (vectorized_size * 8) == 0: return make_full_bank_swizzled_layout
elif continuity % (vectorized_size * 4) == 0: return make_half_bank_swizzled_layout
elif continuity % (vectorized_size * 2) == 0: return make_quarter_bank_swizzled_layout
else: return make_linear_layout

这是「transpose/layout 如何影响指令」的第二处答案:因为 WGMMA 直接以 shared memory 为源,它的 swizzle 布局必须匹配 tensor core 的读取模式;而 MMA 路径可以复用已有的 shared layout(ReuseExistingSharedLayout 只对 cuda.mma / cuda.mma.blockscaled 返回 true,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:398-400),因为它先经过寄存器。

更底层的展开在 _vendor/tilelang-v0.1.15/tilelang/cuda/intrinsics/macro/:wgmma_macro_generator.py 生成 WGMMA 的 descriptor 与 atom 偏移,tcgen05_macro_generator.py 生成 TCGEN05 的 descriptor(含 leading_byte_offset / stride_byte_offset / swizzle_atom_elems 等参数,见该文件第 35-60 行的 TCGEN05DescriptorParams),mma_macro_generator.py 生成 ldmatrix + mma.sync 序列。这些文件是「宏生成层」,量很大但结构单一,读的时候抓住「descriptor 参数 → 偏移公式」这条线即可。

4.3.6 一个源码层面的不一致:显式 GEMM 变体没有导出

读源码时你会注意到一件事:_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:20-30 的 __all__ 里列了四个显式变体——

__all__ = ["gemm", "gemm_blockscaled", "gemm_sp", "make_blockscaled_gemm_layout",
           "mma_gemm_blockscaled", "tcgen05_gemm", "tcgen05_gemm_blockscaled", "wgmma_gemm"]

但 _vendor/tilelang-v0.1.15/tilelang/cuda/language/__init__.py:88 只把 gemm, gemm_blockscaled, gemm_sp 引进包命名空间:

from .gemm_op import gemm, gemm_blockscaled, gemm_sp

而 _vendor/tilelang-v0.1.15/tilelang/language/__init__.py:11 用的是 from tilelang.cuda.language import *,star-import 受 __all__ 限制,_CUDA_API_ALL(_vendor/tilelang-v0.1.15/tilelang/cuda/language/__init__.py:108-196)里也没有这几个名字。结论:在这份锁定版本里,T.wgmma_gemm / T.tcgen05_gemm / T.tcgen05_gemm_blockscaled / T.mma_gemm_blockscaled 不在这套 facade 的导出名单上。

与此同时,同一版本里的官方示例仍在直接调用 T.tcgen05_gemm(...)(例如 _vendor/tilelang-v0.1.15/examples/gemm_tcgen05/gemm_tcgen5mma.py:48),gemm 的 docstring 也在提示用 T.tcgen05_gemm(_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:39-56)。两边对不上。

我们没有 GPU,无法运行验证到底哪一边是真的(写本章的环境没有安装 TileLang,也没有 CUDA)。所以这里只陈述三条可复核的源码事实(__all__ 内容、__init__ 的显式 import、facade 的 star-import),并给出一个保守建议:要用显式变体,先在目标环境里确认 T.<name> 真的存在(一行 python3 -c "import tilelang.language as T; print(hasattr(T, 'tcgen05_gemm'))" 就能验);如果不存在,本节 code/05-tcgen05-gemm/ 里用的是导出的 T.gemm(..., mbar=mbar) 同步形式,语义等价(编译器会隐式插入 mbarrier_wait_parity)。

4.4 TMA:什么时候 T.copy 会变成 cp.async.bulk.tensor

4.4.1 决策函数

唯一入口是 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:716-807 的 SelectCopyInstForLowering,结果由 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:791-800 的 Copy::Lower 消费。把谓词按顺序展开:

  1. is_gather4 / is_scatter4 标注 → 强制走 gather4/scatter4 专用路径。
  2. cluster_mask != 0 → 必须是 kBulkLoad(multicast),否则报错并说明原因。
  3. is_tma_copy(来自 T.tma_copy)→ SelectTmaInst(allow_load=True, allow_store=True, check_last_dim=True);不满足就报错(不允许回退)。
  4. is_async_copy / force_cp_async / no_implicit_commit_wait → 强制 cp.async,不满足就报错。
  5. prefer_instruction="tma" → 尝试 TMA(load 或 store)。
  6. prefer_instruction="cp_async" / "sync" → 对应路径。
  7. 没有任何提示时(_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:797-803):
if (!facts.disable_tma && !facts.pass_context_disables_tma) {
    CopyInst inst = SelectTmaInst(facts, /*allow_load=*/false, /*allow_store=*/true,
                                  /*check_last_dim=*/true);
    if (inst != CopyInst::kInvalid) return Supported(inst);
}
return Supported(SelectSyncLikeInst(facts));

注意 allow_load=false:默认路径只把 shared → global 的 store 自动降成 TMA,global → shared 的 load 不会。这是本章最容易被误解的一点。load 只有在三种情况下走 TMA:显式 T.tma_copy、prefer_instruction="tma"、或者处于 warp-specialized 的 producer 分支里(见 4.4.4)。

还有一处同样容易被误解:走到最后的兜底是 SelectSyncLikeInst,它的结果是 SIMT 同步拷贝(Normal),而不是 cp.async。cp.async 是第 4 步选出来的,前提是人给这个 copy 打了 no_implicit_async_commit_wait 或 is_async_copy 标注——而给流水里的 copy 打这个标注的是 InjectSoftwarePipeline(_vendor/tilelang-v0.1.15/src/transform/inject_pipeline.cc:288-299)。所以:不在 T.Pipelined 里的一句裸 T.copy(A_g, A_s) 在 sm_90 上会降成同步 SIMT 拷贝;只有在流水里、或者你显式写 T.async_copy 时才会变成 cp.async。

4.4.2 约束

CheckBulkLoad / CheckBulkStore(_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:195-261)要求:

1D 路径(CheckBulkCopy1D,_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:326-365)额外要求:global 区域连续、shared 已有 layout 是「语义线性」的、两侧元素数相等、总位数 16 字节对齐。1D 路径不要求最后一维 16B 对齐(它用字节地址而不是描述符坐标),只要求总大小对齐。

4.4.3 描述符在哪儿构建

Copy::LowerBulk(_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:2108-2215)构造 descriptor:

TMADesc desc = plan.desc;
desc.data_type = TensorMapDataTypeForTMA(global_tensor->dtype, shared_tensor->dtype);
desc.l2_promotion = CU_TENSOR_MAP_L2_PROMOTION_L2_128B;
desc.oob_fill = CU_TENSOR_MAP_FLOAT_OOB_FILL_NONE;
desc.interleave = CU_TENSOR_MAP_INTERLEAVE_NONE;
Call create_descriptor = Call(DataType::Handle(), create_tma_descriptor(), desc.EncodeCallArgs());

create_tma_descriptor 是一个 TIR 内建 op,它不在 device 上执行:LowerHopperIntrin(_vendor/tilelang-v0.1.15/src/cuda/transform/lower_hopper_intrin.cc:27-44)把这些 descriptor 初始化语句收集起来,作为 tma_descriptor_args 属性挂到 host 函数上:

f = WithAttr(std::move(f), "tma_descriptor_args", substituter.init_desc_arg_map_);

wrapper 读这个属性(_vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:524-525),并在 generate_tma_descriptor_args(第 415-459 行)里按 TMA_DESC_INIT_FUNC(第 111-131 行)生成 host 侧 的:

CUtensorMap <name>;
...
CUresult r = CUTLASS_CUDA_DRIVER_WRAPPER_CALL(cuTensorMapEncodeTiled)(
    &name, type, rank, globalAddress, globalDim, globalStride + 1, boxDim,
    elementStrides, interleave, swizzle, l2Promotion, oobFill);

(另有 TMA_IM2COL_DESC_INIT_FUNC,第 133-159 行,对应 cuTensorMapEncodeIm2col。)所以完整链路是:

TMA 描述符从 device IR 到 host 生成create_tma_descriptor(TIR op)device IR 里对描述符的请求LowerHopperIntrin 收集提升成 host 函数的 tma_descriptor_args 属性wrapper:cuTensorMapEncodeTiled在 host 侧生成描述符的调用__grid_constant__ const CUtensorMap描述符以 kernel 参数传给 devicecp.async.bulk.tensor 使用它device 上单线程发起 TMA 拷贝
描述符的完整链路:device IR 里的 create_tma_descriptor 被 LowerHopperIntrin 提升成 host 函数的 tma_descriptor_args 属性,wrapper 在 host 侧调 cuTensorMapEncodeTiled(...) 造出描述符,作为 kernel 参数交给 device 上的 TMA 拷贝。
  • 另有 TMA_IM2COL_DESC_INIT_FUNC 对应 cuTensorMapEncodeIm2col,是同一套链路里的 IM2COL 变体。
  • device 侧的 PTX 在 tl_templates/cuda/copy_sm90.h 的模板里:1D/2D 的 tma_load、multicast 变体与 tma_store_cluster 都是 cp.async.bulk.tensor.* 的内联汇编。

device 侧的 PTX 在模板里:_vendor/tilelang-v0.1.15/src/tl_templates/cuda/copy_sm90.h:212-270 是 1D/2D 的 tma_load,第 88-210 行是 multicast 变体,第 51-86 行是 tma_store_cluster。它们都是 asm volatile("cp.async.bulk.tensor.Nd....") 的内联汇编。

4.4.4 mbarrier 配合

load 必须有一个 mbarrier。LowerBulk(_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:2131-2155)的处理顺序是:

  1. 如果用户给了 barrier= 注解(T.tma_copy(..., barrier=mbar))→ 用它;若它是 shared.cluster_barrier,标记 is_cluster_barrier。
  2. 否则,如果 T.tma_copy 没给 barrier → LOG(FATAL),提示「Use T.tma_copy(src, dst, barrier=mbar[idx])」。
  3. 否则(普通 T.copy 走了 TMA load)→ 由 lower_args.alloc_mbarrier(1, ...) 自动分配一个 barrier,命名规则见 MakeCopyMBarrierName(_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:135-138)。

device 侧的 mbarrier 原语在 _vendor/tilelang-v0.1.15/src/tl_templates/cuda/barrier.h:mbarrier_init(第 11 行)、mbarrier_arrive_expect_tx(第 112、120 行)、mbarrier_wait(第 34 行)、tma_store_arrive(第 184 行)、tma_store_wait(第 194 行)。还有一个专门的融合 pass:FuseMBarrierArriveExpectTx(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:199-202,只在模块里真的出现了 TMA 时运行,判据是 module_has_tma,_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:39-46),把 expect_tx → tma issue → arrive 合成一条 arrive_and_expect_tx。

store 不需要 barrier:它用 tma_store_arrive / tma_store_wait 这些 bulk group 原语(T.tma_store_wait(count=0, read=True),_vendor/tilelang-v0.1.15/tilelang/language/builtin.py:370-380)。

4.5 warp specialization 的实现

4.5.1 语言层

两个入口:

WSPipeline 的 docstring(_vendor/tilelang-v0.1.15/tilelang/language/warp_specialize.py:69-80)说清了它的语义:

A full/empty mbarrier pair protecting multi-versioned buffers. The producer waits for the empty barrier and signals the full barrier; the consumer waits for the full barrier and signals the empty barrier. ... Multiple buffers can share one pipeline; conversely, a buffer may be bound to several pipelines whose scopes are strictly nested — its version count is then the product of their depths (outer-major slots).

WSRole 还带一个 max_nreg 字段(_vendor/tilelang-v0.1.15/tilelang/language/warp_specialize.py:49-66),对应 Hopper 的 setmaxnreg——即 FlashAttention-3 里提到的「做 MMA 的 warp 拿更多寄存器、只发 TMA 的 warp 让出寄存器」(arXiv:2407.08608 第 176–178 行)。

4.5.2 编译期

三个 pass 按顺序跑(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:101-109),且只在 allow_warp_specialized(target) 为真时才跑:

def allow_warp_specialized(pass_ctx=None, target=None) -> bool:
    if (not is_cuda_target(target)) or (not have_tma(target)):
        return False
    return not pass_ctx.config.get("tl.disable_warp_specialized", False)

(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:27-38。)have_tma 要求 arch >= 9(_vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:634-650),所以warp specialization 在 Hopper 之前不存在,这是编译器层面挂上去的限制,不是风格选择。

三个 pass 的分工:

分类逻辑被抽到了 _vendor/tilelang-v0.1.15/src/cuda/transform/ws_analysis.h(v0.1.15 新增),它把语句分成 kTmaProducer / kCpAsyncProducer / kCpAsyncRaw / kSimtProducer / kTmaStore / kTcgen05Mma / kConsumer(_vendor/tilelang-v0.1.15/src/cuda/transform/ws_analysis.h:33-41)。这个枚举就是 4.4.1 里那个「warp-specialized producer 分支」判据的来源:ClassifyWarpSpecializedCopy(_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:810-891)对 producer 侧的 load 传 allow_load=True,所以只有在这一分支里,T.copy 的 global→shared load 才会自动变成 TMA。

最后还有 AnnotateWarpGroupRegAlloc(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:268-271),把 set_max_nreg 注入到 producer/consumer 两侧(_vendor/tilelang-v0.1.15/tilelang/cuda/transform/__init__.py:143-156)。

4.6 Blackwell:tcgen05、TMEM、block-scaled

4.6.1 TMEM 是什么、怎么用

PTX ISA 文档的定义(Parallel Thread Execution ISA 第 29726–29756 行):

On architecture sm_100a/sm_100f, the 5th generation TensorCore's Tensor Memory has a two-dimensional structure of 512 columns and 128 rows per CTA, with each cell being 32-bits in size.

以及分配规则(同文件 29758-29768):

The Tensor Memory is dynamically allocated. The Tensor Memory must be allocated by a single warp in a CTA ... The unit of allocation is 32 columns and the number of columns being allocated must be a power of 2. When a column is allocated, all 128 lanes of the column are allocated. All of the Tensor Memory that was allocated in a kernel, must be explicitly deallocated before the kernel exits.

TileLang 的 T.alloc_tmem docstring(_vendor/tilelang-v0.1.15/tilelang/language/allocate.py:254-300)几乎是这段的逐条翻译,并多说了几件工程约束:列数必须是 2 的幂且 ≥32;分配与释放必须由同一个 warp 完成;TMEM 基地址存在 shared memory 里,作为 TCGEN5.MMA 累加器的偏移;只有 TCGEN5.MMA 和特定的 TMEM load/store 能访问 TMEM,所以前后处理必须发生在数据进出 TMEM 之外。最后一条是理解 Blackwell kernel 结构的钥匙:Blackwell 上的典型形态是 shared → (tcgen05.mma) → TMEM → (TMEM load) → 寄存器 → 后处理。

编译期配套:

4.6.2 2SM(use_2cta)

LowerBlackwell2SM(_vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:110-114)必须在 LayoutInference 之前跑,注释说明了原因:use_2cta 标记得让 infer_layout 看得见。use_2cta=True 要求 cluster_dims 是 (2,1,1) 或 (1,2,1)(_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:100-120 的 gemm_blockscaled docstring)。为什么不给回退,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:347-353 的注释已经解释了。

4.6.3 Block-scaled MMA

T.gemm_blockscaled 的 CUDA docstring(_vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:103-135)把两条路写得很清楚:

  • SM100:C 在 TMEM → TCGEN05 kind::mxf8f6f4.block_scale,A/B 是 shared 里的 FP8/FP6/FP4,SFA/SFB 是已在 TMEM 里的 E8M0 scale factor;mbar 必填,编译器隐式插入 mbarrier_wait_parity。
  • SM120:C 是 fragment → 同步的 mma.sync.m16n8k64.kind::mxf4nvf4.block_scale,操作数是 E2M1,scale 是 UE4M3,FP32 累加;A/B 与打包的 scale word 都在 shared;sf_layout 描述 scale 的排布;这条路径忽略 mbar。

「不支持的组合直接编译失败,而不是丢 scale」——docstring 里明确写了。这一点值得记住:低精度量化最容易出的错是「scale 被悄悄忽略」,TileLang 在这里选择了显式失败。

4.7 cluster 与 DSM

4.7.1 启动路径

cluster_dims 从 T.Kernel / T.ClusterKernel 记录到 launch block(_vendor/tilelang-v0.1.15/tilelang/language/kernel.py:397-420,_normalize_cluster_dims 会把 (1,1,1) 归一成 None)。之后:

DSM 侧的硬件事实来自 Hopper 架构文档(NVIDIA Hopper Architecture In-Depth 第 263 行):

With clusters, it is possible for all the threads to directly access other SM's shared memory with load, store, and atomic operations. This feature is called distributed shared memory (DSMEM) because shared memory virtual address space is logically distributed across all the blocks in the cluster.

4.7.2 三种 SM-to-SM 路径

官方文档 _vendor/tilelang-v0.1.15/docs/programming_guides/cluster_tma.md 给了一张表,非常值得直接记住:

路径 条件 硬件指令 arrive 数
TMA 快路径 给了 remote_barrier 且区域连续 一条 tl::tma_store_cluster 1
Multi-TMA 给了 remote_barrier 且 ND 区域不连续 每行一条 tl::tma_store_cluster 行数
SIMT 回退 没给 remote_barrier,或区域无法分解 map_shared_rank 逐元素存 若给了 remote_barrier 则自动注入 arrive

这里的「连续」判据是「最内维是否覆盖整个 buffer 宽度」。实现上,Copy::LowerCluster(_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:913-1040)先看 bulk 能不能整块搬(tma_store_cluster,第 968 行),不能就调 MakeTMARows(_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:270-380)逐行分解,再不能就落到 SIMT(ClusterCopyReplacer,_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:1050-1120)。

4.7.3 限制

  • cluster_mask 必须是编译期常量,不支持动态 mask(cluster_tma.md「Notes」节)。
  • cluster_mask 与 dst_block 在同一次 copy_cluster 里互斥。
  • 可移植 cluster 上限是 8;B200 可通过 cudaFuncAttributeNonPortableClusterSizeAllowed 开到 16(Blackwell Tuning Guide 第 73 行)。
  • 集群场景下建议用 cudaOccupancyMaxActiveClusters 算占用率,而不是普通 occupancy calculator(同文件第 52 行)。
  • DSM 访问应尽量合并并 32 字节对齐,避免非单位步长(同文件第 60-68 行)。

4.8 CuTe DSL 后端

4.8.1 它在 TileLang 里怎么接

4.8.2 它和官方 CuTe DSL / cuTile 的关系

_vendor/tilelang-v0.1.15/tilelang/contrib/cutedsl/__init__.py 的第一件事就是 import cutlass / import cutlass.cute,然后重导出 cutlass.cute.arch 的 sync_threads、alloc_smem、warpgroup_reg_alloc、wgmma_wait_group 等等。也就是说:

TileLang 的 CuTe DSL 后端 = 「TileLang 的 TIR pass 链」+「生成 CuTe DSL Python 源码」+「交给 NVIDIA 的 CUTLASS/CuTe 去编译」。

它不是 TileLang 自己重写了一遍 CuTe,而是把 CuTe DSL 当成 codegen target。_vendor/tilelang-v0.1.15/tilelang/contrib/cutedsl/ 下自己实现的 gemm_v1.py / gemm_v2.py / gemm_tcgen05.py / cpasync.py / ldsm.py / ptx_mma.py / cluster.py 等,是把 CUTLASS 里零散的 atom 与 helper 组织成 TileLang 需要的那几个入口。

另一条 NVIDIA 官方的路线是 cuTile Python,它和 CuTe DSL 并不是一回事。corpus 里 cuTile 的 README(NVIDIA/cutile-python 第 64–70 行)写着:

cuTile Python generates kernels based on [Tile IR] which requires NVIDIA Driver r580 or later to run. Furthermore, the tileiras compiler (version 13.2) only supports Blackwell GPU and Ampere/Ada GPU. Hopper GPU will be supported in the coming versions.

所以三者可以这样定位:

路线 前端抽象 编译目标 与 TileLang 的关系
TileLang CUDA 后端 T.Kernel / T.copy / T.gemm CUDA C → nvcc → cubin TileLang 自己的主线
TileLang CuTe DSL 后端 同上(同一套 pass) CuTe DSL Python → CUTLASS TileLang 把 CUTLASS 当 codegen target
cuTile Python ct.load / ct.store / tile 运算 Tile IR → tileiras 同一抽象层次的竞争路线,编译器与运行时完全独立

一个诚实的限制:corpus 里 CuTe DSL 文档(CUTLASS Python DSL) 只有 22 行,是文档站的导航壳,没有正文。所以本章关于 CuTe DSL「官方怎么描述自己」的部分只做了定位性引用,正文判断全部来自 TileLang 侧的 checks.py 与 contrib/cutedsl/。这一点记在 本章引用 的「待补资料」里。

4.9 一张「用户写法 → 架构 → 生成指令」对照表

下面这张表是本章的核心速查。所有「生成指令」列的内容都能在上面的源码位置核对到;「架构限制」列来自 _vendor/tilelang-v0.1.15/src/cuda/target_utils.cc 的 arch 阈值与 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc 的谓词。

用户写法 目标架构 选中的指令 依据(v0.1.15)
T.gemm(A_s, B_s, C_frag),f16→f32 sm_70 mma.sync(Volta 形态) AllowVoltaMma,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:99-117
同上 sm_75 mma.sync(Turing 形态) AllowTuringMma,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:131-145
同上,M>=64,256 线程 sm_90/90a wgmma.mma_async AllowWgmma + CheckWgmma,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:89-97、40-78
同上,M<64 或线程数不是 4 的倍数个 warp sm_90 mma.sync AllowWgmma 的 op.m_ >= 64、num_warps % 4 == 0
T.gemm(A_s, B_s, C_tmem, mbar=mbar) sm_100 tcgen05.mma AllowTcgen5Mma,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:80-87 + _vendor/tilelang-v0.1.15/src/op/tcgen5_meta.h
T.tcgen05_gemm(..., mbar=mbar) sm_100 tcgen05.mma(不隐式等待) SelectInst 第一段,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:336-346
T.wgmma_gemm(...) sm_90 wgmma.mma_async(不隐式等待) 同上,且不满足即 FatalWgmmaUnavailable
T.tcgen05_gemm(..., use_2cta=True) sm_100,cluster (2,1,1) tcgen05.mma.cta_group::2 Use2CtaRequested,_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:119-129、354-361
T.gemm_blockscaled(..., mbar=...) sm_100 tcgen05.mma.kind::mxf8f6f4.block_scale _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:110-116
T.gemm_blockscaled(..., sf_layout=...) sm_120 mma.sync.m16n8k64.kind::mxf4nvf4.block_scale _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:118-123
T.gemm 形状/精度都不满足 sm_70/sm_75 fma(无 Tensor Core) _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:368-373
T.copy(A_g, A_s)(裸写,不在流水里) sm_80+ SIMT 同步 Normal SelectSyncLikeInst,_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:612-626、:797-807
T.copy(A_g, A_s)(在 T.Pipelined 里) sm_80+ cp.async + commit_group _vendor/tilelang-v0.1.15/src/transform/inject_pipeline.cc:288-299 打标注 → _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:757-760
T.copy(A_s, C_g) sm_90+ cp.async.bulk.tensor(TMA store) _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:797-803
T.copy(A_g, A_s, prefer_instruction="tma") sm_90+ TMA load(约束不满足则报错) _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:762-780
T.tma_copy(A_g, A_s, barrier=mbar) sm_90+ cp.async.bulk.tensor(只发 producer 半边) _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:748-756、_vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:2108-2215
T.async_copy(A_g, A_s) sm_80+ cp.async + commit_group _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:830-905
T.copy(A_s, B_s)(shared→fragment) sm_75+ ldmatrix CheckLDSMCopy,_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:407-410
T.copy(C_frag, B_s) sm_90+ stmatrix(m16n8 形态要 sm_100) TargetHasStmatrix,_vendor/tilelang-v0.1.15/src/cuda/target_utils.cc:104-110
T.copy(C_tmem, C_frag) sm_100 TMEM load CheckTMemLoad,_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:417-420
T.copy_cluster(A_g, A_s, cluster_mask=0b11) sm_90+ cp.async.bulk.tensor...multicast::cluster _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:718-736、_vendor/tilelang-v0.1.15/src/tl_templates/cuda/copy_sm90.h:88-210
T.copy_cluster(A_s, B_s, dst_block=1, remote_barrier=b) sm_90+ cp.async.bulk.shared::cluster _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:913-1040、_vendor/tilelang-v0.1.15/src/tl_templates/cuda/copy_sm90.h:51-86

表里 T.tcgen05_gemm(...) 与 T.wgmma_gemm(...) 两行的「用户写法」指的是这类语义: 如 4.3.6 节所述,这两个名字在 v0.1.15 的 tilelang.language facade 上取不到,要写显式变体 请先在目标环境里确认名字存在;否则用 T.gemm(..., mbar=mbar) 等价的同步形式。

4.10 版本差异:v0.1.14 → v0.1.15(NV codegen 部分)

方法:_vendor/tilelang-v0.1.14/ 与 _vendor/tilelang-v0.1.15/ 都是 git clone --depth 1 --branch <tag>,工作树干净。所以直接用 diff -rq A B 找差异文件,再对有差异的文件跑 diff -u。下面是这条命令筛出来的、与 NV codegen 有关的部分。

diff -rq _vendor/tilelang-v0.1.14/tilelang/cuda _vendor/tilelang-v0.1.15/tilelang/cuda
diff -rq _vendor/tilelang-v0.1.14/src/cuda      _vendor/tilelang-v0.1.15/src/cuda
  1. block-scaled 从 gemm.cc 独立出去。 新增 _vendor/tilelang-v0.1.15/src/cuda/op/gemm_blockscaled.cc(128 行),_vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/gemm_tcgen05_blockscaled.py,以及整个新包 _vendor/tilelang-v0.1.15/tilelang/tileop/gemm_blockscaled/。对照 diff 可见 v0.1.14 的 gemm.cc 里有一段内联的 blockscaled 分派(if (op.sfaRegion_.defined() || ...) return kCudaMMABlockScaled;)与 tl.get_tcgen5_blockscaled_instr_desc 的注册,在 v0.1.15 里都从 gemm.cc 删掉,移到新文件。_vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/__init__.py 也多注册了 cuda.tcgen05_blockscaled。
  2. TMEM 判断从字面量 scope 改成谓词。 AllowTcgen5Mma 里 op.c_.scope() == "shared.tmem" 变成 IsTmemBuffer(op.c_);CheckTMemLoad/Store 同样(_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc diff)。
  3. TMA 判定函数改名/拆分。 CopyInstIsTMA(inst) 拆成 CopyInstIsTMALoad 与 CopyInstIsTMAStore(copy_analysis.cc diff);
  4. warp-specialized copy 分类泛化。 ClassifyWarpSpecializedProducerCopy → ClassifyWarpSpecializedCopy,并新增一条「src 在 shared、dst 在 global 时直接走 SelectCopyInstForLowering」的旁路——也就是说 store 现在也走通用判定,而不是原来的 producer-only 路径。
  5. 新增自动 warp specialization。 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py 在 MaterializeWSSchedule() 之前插入 AutoWarpSpecialization();新增 pass config tl.enable_auto_warp_specialization(_vendor/tilelang-v0.1.15/tilelang/transform/pass_config.py diff);新增 _vendor/tilelang-v0.1.15/src/cuda/transform/auto_schedule.cc、_vendor/tilelang-v0.1.15/src/cuda/transform/auto_schedule/(含 role_based_scheduler.cc、preprocess_ir.cc、memory_detector.h)。
  6. WS 分析抽成公共头文件。 新增 _vendor/tilelang-v0.1.15/src/cuda/transform/ws_analysis.{h,cc},TileStmtKind 枚举(kTmaProducer 到 kConsumer)被抽出来供自动调度器复用——注释原文是「Extracted from producer_consumer_ws.cc so automatic schedulers reuse the same classification the legacy producer/consumer rewriter has been validated with」。
  7. MaterializeWSSchedule 大幅扩容(_vendor/tilelang-v0.1.15/src/cuda/transform/materialize_ws_schedule.cc 的 diff 有 3312 行),这是 v0.1.15 NV 侧最大的单点变化。
  8. MaterializeKernelLaunch 参数化。 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:70 从 MaterializeKernelLaunch() 变成 MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128)。
  9. CodeGenTileLangCUDA 的向量展开。 新增 EmitPackedX2Call / EmitPerLaneScalarCall(声明见 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.h diff),新增 VisitStmt_(const AssertStmtNode *);t lanes=64 的整型向量不再打印 int8(不存在的类型)而是 longlong4;fp4/fp8 的多 lane 向量改用 tl::make_fp4_vec<> / tl::make_vec<> 的逐 lane 打包构造(_vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:986-994、1132+)。
  10. 可向量化 cast 增加 fp4→fp8。 _vendor/tilelang-v0.1.15/src/cuda/target_utils.cc 的 IsCudaVectorizableCast 新增 float4_e2m1fn → float8(e4m3) 一条。
  11. deprecation 文案更新。 tl.disable_tma_lower 的提示从「will be removed in v0.1.10」改成不带版本号;docstring 同步(_vendor/tilelang-v0.1.15/tilelang/transform/pass_config.py diff)。语义没变,但说明这个兼容开关至今没被删。
  12. examples:新增 _vendor/tilelang-v0.1.15/examples/ascend/ 与 _vendor/tilelang-v0.1.15/examples/kpool/(例目录数 55 → 57);_vendor/tilelang-v0.1.15/examples/gemm_tcgen05/README.md、_vendor/tilelang-v0.1.15/examples/blockscaled_gemm_sm100/mxfp8_illustrated.md、_vendor/tilelang-v0.1.15/examples/deepseek_v32/sparse_mla_fwd.py 等有改动。
  13. 纯 NV 的 wrapper.py 变化很少。 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py 的 diff 主要是新增 TLAscendSourceWrapper(昇腾),NV 部分只有 _TYPE_MAP 加了 uint64 → uint64_t、get_stream_type 的返回类型放宽成 dict[str, str] | None。这说明 v0.1.15 的 host wrapper 形态对 NV 而言基本冻结。

找不到的部分也说清楚:两个 vendor 树都是浅克隆,没有 .git 历史,所以无法用 git log 看提交信息;上面全部结论来自文件级 diff。_vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py、_vendor/tilelang-v0.1.15/tilelang/contrib/nvrtc.py 在两版之间没有差异(diff -rq 未列出),说明这两个工具封装在 v0.1.15 里没有变化。


5. 进阶与陷阱

  1. 「我明明写了 T.copy,为什么没有 TMA」 ——先看方向:默认只有 store 自动走 TMA。load 要 TMA,请用 T.tma_copy 或 prefer_instruction="tma",并确认最后一维是 16 字节的整数倍、global stride 是 16 字节对齐且最内层 stride 为 1。
  2. 「T.gemm 没有用 5 代 Tensor Core」 ——先看 C 的作用域:TCGEN05 要求 C 是 T.alloc_tmem。C 在 fragment 里时,即使目标是 sm_100 也不会选 TCGEN05。
  3. 显式变体不降级。 T.wgmma_gemm / T.tcgen05_gemm 在上约束不满足时是 LOG(FATAL),不是回退。它们的定位是「我确知要什么,帮我到底」,而不是「尽量给我这个」。反过来,T.copy 的 prefer_instruction="tma" 也是「要么 TMA,要么报错」。 3b. 但先确认这些名字在你的环境里能从 T 上取到。 4.3.6 节给出的源码事实是:在 v0.1.15 里 T.wgmma_gemm / T.tcgen05_gemm 等显式变体不在 facade 的导出名单上,而官方示例仍在调用它们。别照着示例直接抄。
  4. use_2cta 没有单 CTA 回退。 因为形状约定本身就建立在「B 的 N 一半在邻居 CTA」这个前提上(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:347-353)。
  5. TMA 描述符在 host 上编码,所以 global base 必须是 host 能看到的地址。 如果 global buffer 的基址是在 device 函数体里算出来的(device-bound),描述符 TMA 路径不可用——GetTmaDescriptorBaseIsDeviceBound 与 MakeTmaUnavailableReason(_vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:81-84、522-535)。此时 1D bulk 路径(直接用 device 算出的地址)仍然可用,因为它不建 TensorMap。
  6. mbarrier 的 arrive 计数必须和实际到达者一致。 T.alloc_barrier(arrive_count=...) 里 0 是非法值(_vendor/tilelang-v0.1.15/tilelang/language/allocate.py:194-222 会 raise)。Multi-TMA 路径会把 arrive_count 自动改成行数——这是编译器替你做的事,你要知道它发生了,否则手工调 arrive_count 会和它打架。
  7. TMEM 必须显式释放,且必须在同一个 warp 上分配/释放。 不写 T.deallocate_tmem 时 TileLang 会在分配块结束时自动释放(_vendor/tilelang-v0.1.15/tilelang/language/allocate.py:260-282),但如果你手工提前释放,就要自己保证「同一 warp」这条约束。
  8. warp specialization 只在 Hopper 及以后存在。 allow_warp_specialized 会先检查 have_tma(target)(arch >= 9),不满足直接跳过整个 WS 分支。
  9. cluster_mask 是编译期常量。 想按运行期数据决定 multicast 形状,必须写多个 kernel 或者用 dst_block(它可以是运行期表达式)。
  10. cluster 大小不是随便开的。 可移植上限 8,B200 可开到 16(需设 NonPortableClusterSizeAllowed,wrapper 已经替你设了),但更大的 cluster 会降低可并行 block 数(blackwell-tuning-guide.md:73)。
  11. 架构条件特性会破坏前向兼容。 用 sm_100a / compute_100a 编译出的 PTX 既不前向也不后向兼容(blackwell-compatibility-guide.md:28)。这意味着给 B200 编的 cubin 不能直接丢到 H100 上跑——反过来也不行。跨代部署要靠 PTX JIT 或多 -gencode,这也是为什么 target 的 code 列表支持多个条目。
  12. 本章所有结论都是源码级的,不是实测的。 本章写作环境没有 NVIDIA GPU,因此没有任何一条「这样写更快」的性能断言;凡是能看到的性能口径,都来自 corpus 里的厂商文档,且在本章里只作架构描述使用。

6. 本章小结

  • TileLang 的 NV 路径是「一条 TIR pass 链 + 一次 host/device 切分 + 两条独立的 codegen」。device 出 CUDA C(或 CuTe DSL Python),host 出 wrapper 的 call()(或 TVM-FFI packed 模块)。
  • 后端的全部可选项写在一张注册表里:_vendor/tilelang-v0.1.15/tilelang/cuda/backend.py:123-143。三种 execution backend(tvm_ffi / cython / nvrtc)共用同一个 TLCUDASourceWrapper,所以生成的 kernel 源码相同。
  • T.gemm 的指令选择是一个可完整读完的谓词链(_vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:336-375):显式变体 → use_2cta → TCGEN05 → WGMMA → Volta/Turing 检查 → cuda.mma。决定 TCGEN05 的关键是 C 在不在 TMEM;决定 WGMMA 的关键是 sm_90 + M≥64 + warp 数是 4 的倍数 + dtype/转置约束。
  • transpose 与 policy 通过两条路影响结果:低精度 dtype 在 WGMMA 上对主序有硬约束(CheckWgmma),而 policy 决定 m_warp × n_warp 的划分(ComputeWarpPartition)。layout 则由指令反推(GemmWGMMA.infer_shared_layout 的 swizzle 档位)。
  • T.copy 默认只把 store 降成 TMA;load 想走 TMA 需要显式提示或处在 warp-specialized producer 分支。TMA 的成功条件集中在 CheckBulkLoad/Store 与 CheckGlobalStrides:架构 ≥ sm_90、global↔shared、16 字节对齐、最内层 stride 为 1。
  • TMA 是「host 编描述符 + device 单线程发射 + mbarrier 完成通知」。描述符由 LowerHopperIntrin 提升到 host 函数属性 tma_descriptor_args,再由 wrapper 生成 cuTensorMapEncodeTiled(...)。
  • warp specialization 在语言层是 T.ws(i) / T.annotate_ws_schedule,在编译期是 AutoWarpSpecialization → MaterializeWSSchedule → ProducerConsumerWarpSpecialized(外加 AnnotateWarpGroupRegAlloc),并且只在 Hopper 及以后开启。
  • Blackwell 的新东西是 TMEM 与 tcgen05:累加器不再在寄存器里,只有 TCGEN05 与 TMEM load/store 能碰它;block-scaled MMA 在 SM100 走 TCGEN05、在 SM120 走 mma.sync。
  • cluster 与 DSM 通过 T.ClusterKernel(cluster_dims=...) + T.copy_cluster(cluster_mask=... | dst_block=...) 使用,硬限制是「mask 编译期常量」「可移植 cluster ≤ 8」「DSM 访问要合并对齐」。
  • CuTe DSL 后端与 CUDA 后端共用同一条 TIR pass 链,只是把 codegen target 换成了 CuTe DSL Python;它与 NVIDIA 官方的 cuTile(走 Tile IR + tileiras)是完全独立的两条路。

延伸阅读

习题

15 题 · 答案在提交后展开

先在纸上或输入框里写下你的答案,再展开参考答案对照。客观判断请自己打勾:这三颗按钮 只影响本机保存的学习进度。

E1概念难度 ★无需硬件概念题 不需要硬件

一次 tilelang.compile(...) 之后,NVIDIA 侧会得到哪几种产物?请分别说明:

  1. device 侧的 CUDA C 源码是在哪个函数、哪个文件里生成的?
  2. 默认情况下,device 源码会被编译成 cubin 吗?为什么?
  3. device 的 cubin / PTX 与 host 的 call() 是在什么时候被拼到一起的?

E2概念难度 ★无需硬件概念题 不需要硬件

tilelang/cuda/execution_backend.py:24-31 注册了三种 execution backend。请回答:

  1. 三种分别是什么?各自的默认可用性与依赖是什么?
  2. 为什么说「读 get_kernel_source() 得到的结论对三种 backend 都成立」?
  3. 如果你的机器上只装了 GPU 驱动、没有完整 CUDA toolkit,哪一种 backend 仍然可用? 依据是什么?

E3代码阅读难度 ★★无需硬件代码阅读题 不需要硬件

阅读 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:336-375,回答:

  1. 为什么 op.isWgmma_ 这一支在约束不满足时是 LOG(FATAL) 而不是返回 kCudaMMA?
  2. Use2CtaRequested(op) 为什么不设「回退到单 CTA」的分支?请在源码注释里找到理由。
  3. TargetIsVolta(target) && !AllowVoltaMma(op) 这一支返回 kCudaFMA;那 TargetIsVolta(target) && AllowVoltaMma(op) 会走到哪里?返回值是什么?

E4概念难度 ★★无需硬件概念题 不需要硬件

「默认的 T.copy 只有 store 会自动降成 TMA」这句话:

  1. 在 SelectCopyInstForLowering 的哪一行代码里体现出来?请写出那一行。
  2. 一个 T.copy(A_global, A_shared) 想走 TMA,有哪三种写法/场景?分别对应哪一段代码?
  3. 一句裸写、不在 T.Pipelined 里的 T.copy(A_global, A_shared) 在 sm_90 上最终会被 降成什么?为什么不是 cp.async?

E5代码阅读难度 ★★无需硬件代码阅读题 不需要硬件

阅读 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:143-261,判断下面五个 copy 能否 走描述符型 TMA(kBulkLoad / kBulkStore),并写出被拒绝的那一条具体违反了哪条检查:

  1. sm_90,global → shared.dyn,dtype=f16,最后一维 64 个元素,row-major;
  2. sm_90,global → shared.dyn,dtype=f16,最后一维 30 个元素;
  3. sm_90,global → shared.dyn,dtype=f16,shape=(4096,4096) 但最内层 stride = 2;
  4. sm_90,global → shared.dyn,dtype=f16,stride 为 (12, 1)(单位:元素);
  5. sm_80,shared.dyn → global,dtype=f16,其余条件与 1 相同。

E6代码阅读难度 ★★无需硬件代码阅读题 不需要硬件

阅读 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:40-97,回答:

  1. AllowWgmma 要求哪三个条件?(提示:一个是架构,一个是 M,一个是 warp 数)
  2. 同样是 fp8×fp8 → f32,trans_A=True, trans_B=True 与 trans_A=False, trans_B=True 哪个能被 WGMMA 接受?为什么?
  3. f16×f16 → f32 对 trans_A / trans_B 有要求吗?为什么这两类 dtype 的规则不一样?

E7概念难度 ★无需硬件概念题 不需要硬件

GemmWarpPolicy 的 FullRow / FullCol / Square 三种取值,分别影响输出里的什么? 请在 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc 里指出:

  1. 哪一个函数用到了 policy;
  2. TCGEN05 与 FMA 两种指令为什么不看 policy(提示:看 ComputeWarpPartition 的前两个分支)。

E8动手难度 ★★无需硬件动手题 不需要硬件

用 code/01-copy-inst-selection/copy_inst_selection.py:

  1. 先运行一次,记录全部判定结果;
  2. 把其中一个场景的 arch 从 sm_90 改成 sm_100,观察结果是否变化,并解释原因 (提示:target_has_tmem);
  3. 构造一个能触发 cluster_mask requires descriptor-based TMA 报错的场景,并说明是哪条 检查拒绝了它。

E9动手难度 ★★无需硬件动手题 不需要硬件

用 code/02-gemm-inst-selection/gemm_inst_selection.py:

  1. 找出一组 (M, N, K, dtype, scope) 使得在 sm_100 上选 cuda.tcgen05,在 sm_90 上选 cuda.wgmma,并说明原因;
  2. 找出一组使得在 sm_100 上仍选 cuda.mma 的配置,并说明是哪条条件把它挡在 TCGEN05 之外;
  3. M=32 时 sm_100 上的 TCGEN05 还能用吗?如果可以,GetTCGEN5MMAMeta 返回的 atom_m 与 enable_ws 是什么?(提示:读 src/op/tcgen5_meta.h:22-177)

E10动手难度 ★★无需硬件动手题 不需要硬件

用 code/03-tma-descriptor/tma_descriptor.py:

  1. 为 shape=(8192, 8192)、dtype=bf16、box=(256, 64) 的 tile 打印出 cuTensorMapEncodeTiled 的全部参数;
  2. 解释 globalStride 为什么只有 1 项(rank=2),以及它的单位是什么;
  3. 把 box 的最内层改成 8,会发生什么?为什么?

E11概念难度 ★★无需硬件概念题 不需要硬件

对照 _vendor/tilelang-v0.1.15/examples/warp_specialize/example_warp_specialize_gemm_copy_0_gemm_1.py:

  1. T.ws(0) 与 T.ws(1) 分别展开成什么条件?
  2. 两个 mbarrier 的 arrive_count=128 为什么是 128 而不是 256?
  3. 如果去掉 producer 分支里的 T.barrier_wait(compute_is_done, (ko + 1) % 2), 会发生什么类型的错误?为什么要用 (ko + 1) % 2 而不是 ko % 2?
  4. 这一整套变换在编译期由哪三个 pass 完成?它们的前置门禁是什么?

E12概念难度 ★★无需硬件概念题 不需要硬件

关于 Blackwell 的 TMEM:

  1. TMEM 的物理尺寸是多少?分配单位与列数有什么约束?(引用 PTX ISA 文档位置)
  2. 有哪两类操作可以访问 TMEM?这对 kernel 的结构意味着什么?
  3. 要让 T.gemm 在 sm_100 上选 tcgen05,C 必须满足什么作用域条件?
  4. T.gemm_blockscaled 在 SM100 与 SM120 上分别走哪条指令?两者对 mbar 的要求有何不同?

E13概念难度 ★★无需硬件概念题 不需要硬件

关于 cluster 与 DSM:

  1. T.Kernel(..., cluster_dims=(4,1,1)) 与 T.ClusterKernel(..., cluster_dims=(4,1,1)) 有区别吗?
  2. 生成代码里,带 cluster 的启动为什么要额外调用 cudaFuncSetAttribute(..., cudaFuncAttributeNonPortableClusterSizeAllowed, 1)? 厂商文档对可移植 cluster 上限怎么说?
  3. T.copy_cluster 的「TMA 快路径 / Multi-TMA / SIMT 回退」分别在什么条件下触发? 各自的 mbarrier arrive 数是多少?

E14概念难度 ★★无需硬件概念题 不需要硬件

关于 CuTe DSL 后端:

  1. TileLang 的 CuTe DSL 后端复用了哪一条 pass pipeline?在哪一行可以看出来?
  2. 它生成的 device 代码是什么形态?由谁最终编译成 cubin?
  3. 它与 NVIDIA 官方的 cuTile Python 是同一件事吗?如果不是,请说出两者在 「编译目标」上的区别,并给出 corpus 里的依据位置。

E15动手难度 ★★★无需硬件动手题 不需要硬件

关于版本差异与 API 可用性:

  1. 用 diff -rq 比较 _vendor/tilelang-v0.1.14/ 与 _vendor/tilelang-v0.1.15/ 的 src/cuda 与 tilelang/cuda 两个目录,列出与 NV codegen 有关的差异文件;
  2. 至少举出三条具体变化,并给出对应的源码位置;
  3. 核对 T.tcgen05_gemm 是否在 tilelang.language 的导出名单里(读 tilelang/cuda/language/__init__.py 与 tilelang/language/__init__.py), 并解释为什么「官方示例里能写」与「facade 的 __all__ 里没有」会同时成立。

章节自测

8 题 · 提交后本地评分

单选题。提交后立即给出分数与逐题解析;重做会清空选择,但会保留上一次的成绩记录。

  1. 1. TileLang 的 NVIDIA 路径默认在 lowering 阶段做哪两件事?

  2. 2. 一段 T.gemm 在 sm_100 上没有被选成 tcgen05,最该先检查什么?

  3. 3. AllowWgmma 要求哪些条件同时成立?

  4. 4. 关于 T.copy 默认路径下的 TMA 选择,下列哪一项正确?

  5. 5. TMA 描述符在哪里编码、又怎么传给 kernel?

  6. 6. 关于 T.ws(role) 与 warp specialization,下列哪一项正确?

  7. 7. TileLang 的 CuTe DSL 后端与 NVIDIA 官方 cuTile 的关系是?

  8. 8. 关于 T.wgmma_gemm / T.tcgen05_gemm 这类显式变体,下列哪一项正确?

本章代码

11 个文件

正文里提到的每个示例都能在这里直接阅读(带语法高亮),也可以整章浏览。

浏览本章全部代码 →

本章引用

源码链接指向锁定 tag

本章写到的每个源码位置与外部资料都在下面列出:源码链接指向锁定 tag 的 GitHub 文件, 资料链接指向原文。查看全部资料索引 →

展开引用清单

第 24 章 参考资料

本章所有结论都基于下表中的本地资料或锁定版本源码。版本以 版本锁定 为准:TileLang v0.1.15 (commit a35f8ddf45eba16c21211ec8822d56ce5363036f);对照版本 v0.1.14 (commit 7e3bbf3703e769479757eb7ecec2f2626f3cd0a8)。 文中不出现「最新」这类不带版本号的 模糊表述;凡版本一律写明 v0.1.15 或 v0.1.14。

写作环境:macOS (Apple Silicon)、Python 3.11(miniconda);没有 NVIDIA GPU, 没有昇腾 NPU,也没有安装 TileLang 或 CUDA toolkit。因此:

  • 本章所有代码示例的最高验证级别是「仅静态核对」(API 存在性与签名逐个在 _vendor/ 中比对过,python3 -m py_compile 通过),除 code/01-copy-inst-selection/–code/03-tma-descriptor/ 三个纯标准库模型属于 「CPU 逻辑验证」(实际运行过);
  • 没有任何一条性能数字来自本机实测;本章正文里没有给出任何「这样写更快」的断言。

锁定的源码引用(_vendor/tilelang-v0.1.15/)

「已核验」的含义:在锁定版本的工作树上用 grep -n / sed -n 定位到该行区间,并阅读了 完整内容;行号区间与文件内容一致。这不是运行验证。 本章共核对 155 处源码引用。

后端注册、编译入口与 host/device 切分

id 引用位置 用途 核验方式与状态
S1 _vendor/tilelang-v0.1.15/tilelang/cuda/backend.py:123-143 CUDA 后端的完整注册清单(pipeline / device codegen / execution backend / host codegen / callbacks) 已核验(2026-10-01):sed -n '120,143p';文件共 143 行
S2 _vendor/tilelang-v0.1.15/tilelang/cuda/backend.py:59-121 tilelang_callback_cuda_compile:gencode / fatbin / cubin 的选择与 nvcc 选项 已核验(2026-10-01):grep -n "def tilelang_callback_cuda_compile" → 59
S3 _vendor/tilelang-v0.1.15/tilelang/cuda/codegen.py:8-19 is_cutedsl_target / is_plain_cuda_target / 四个 build 回调 已核验(2026-10-01)
S4 _vendor/tilelang-v0.1.15/tilelang/backend/device_codegen.py:17-31 global_func_device_codegen:把 TVM 全局函数包成 Python 回调 已核验(2026-10-01)
S5 _vendor/tilelang-v0.1.15/tilelang/backend/host_codegen.py:55-59 STANDARD_HOST_CODEGENS = c / llvm 已核验(2026-10-01)
S6 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:174-213 _lower_with_context_impl:host 与 device 的分叉点 已核验(2026-10-01)
S7 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:144-170 lower_with_context docstring:enable_host_codegen / enable_device_compile 默认 False 的理由 已核验(2026-10-01)
S8 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:26-51 has_device_kernel_launch / is_device_call_c_device / get_host_call 已核验(2026-10-01)
S9 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:138-139 用 Filter 把 lowering 结果切成 host_mod / device_mod 已核验(2026-10-01)
S10 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:239-240 AnnotateDeviceRegions → SplitHostDevice 已核验(2026-10-01)
S11 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:284 CUDA_PIPELINE = PassPipeline("cuda", CUDAPassPipelineBody);文件共 284 行 已核验(2026-10-01)
S12 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:70 MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128)(v0.1.15 变化) 已核验(2026-10-01)

device codegen(CUDA C)

id 引用位置 用途 核验方式与状态
S13 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:182-184 target.build.tilelang_cuda / ..._without_compile 的注册 已核验(2026-10-01)
S14 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:99-139 BuildTileLangCUDA 四步:校验符号 → validate → AddFunction → 编译 已核验(2026-10-01)
S15 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:151-176 BuildTileLangCUDAWithoutCompile:源码留在 source_map["cuda"],用 dummy PTX 占位 已核验(2026-10-01)
S16 _vendor/tilelang-v0.1.15/src/cuda/codegen/rt_mod_cuda.cc:74-82 cluster_dims → kClusterDimX/Y/Z launch param tags 已核验(2026-10-01)
S17 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:563-565 PrintFuncPrefix 固定打印 extern "C" __global__ 已核验(2026-10-01)
S18 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:629-646 PrintExtraAttrs:__launch_bounds__(threads, min_blocks) 已核验(2026-10-01)
S19 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:596-627 ClusterInfoExtractor:从 PrimFunc attrs 读 cluster_dims 已核验(2026-10-01)
S20 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:6560-6561 this->cluster_dims = ClusterInfoExtractor().extract(f) 已核验(2026-10-01)
S21 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:6562-6580 AddFunction 里 __grid_constant__ const <type> 参数打印 已核验(2026-10-01):grep -n "__grid_constant__" → 6465 / 6574
S22 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:6542-6548 PDL 时抑制 __restrict__ 的判据 已核验(2026-10-01)
S23 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:986-994 64-lane 整型向量退化成 longlong4(v0.1.15 变化) 已核验(2026-10-01)
S24 _vendor/tilelang-v0.1.15/src/cuda/codegen/codegen_cuda.cc:6509-6628 AddFunction 全体 已核验(2026-10-01):grep -n "void CodeGenTileLangCUDA::AddFunction" → 6509;文件共 6631 行

host wrapper 与 execution backend

id 引用位置 用途 核验方式与状态
S25 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:111-131 TMA_DESC_INIT_FUNC:cuTensorMapEncodeTiled(...) 模板与 globalStride + 1 已核验(2026-10-01)
S26 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:133-159 TMA_IM2COL_DESC_INIT_FUNC:cuTensorMapEncodeIm2col(...) 已核验(2026-10-01)
S27 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:161-200 普通启动与 cluster 启动两个模板(含 NonPortableClusterSizeAllowed) 已核验(2026-10-01)
S28 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:68-85 PREDEF_INIT_FUNC / PREDEF_HOST_FUNC 已核验(2026-10-01)
S29 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:289-393 create_dispatch_func:逐个 kernel 拼启动代码 已核验(2026-10-01)
S30 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:415-459 generate_tma_descriptor_args 已核验(2026-10-01)
S31 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:466-497 parse_source_information:从 device/host 模块读 grid/block/smem/cluster 已核验(2026-10-01)
S32 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:524-525 读 tma_descriptor_args 属性 已核验(2026-10-01)
S33 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:567-571 get_kernel_launch_code:按 cluster_dims 选启动模板 已核验(2026-10-01)
S34 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:585-632 update_lib_code:source + include + init + host_func 已核验(2026-10-01)
S35 _vendor/tilelang-v0.1.15/tilelang/cuda/execution_backend.py:24-35 CUDA_EXECUTION_BACKENDS(tvm_ffi/nvrtc/cython)与 CUTEDSL_EXECUTION_BACKENDS 已核验(2026-10-01):文件共 37 行
S36 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/libgen.py:41-80 LibraryGenerator.compile_lib:cython 路径把 C 源码编成 .so 已核验(2026-10-01)
S37 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/nvrtc/libgen.py:1-20、:37-50 NVRTC 路径的动机与生命周期 已核验(2026-10-01)
S38 _vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:271-296、:309-366 get_ptx_from_source / get_sass_from_source 已核验(2026-10-01);未运行(无 CUDA 工具链)
S39 _vendor/tilelang-v0.1.15/tilelang/contrib/nvcc.py:634-650 have_tma:arch >= 9 已核验(2026-10-01)

T.gemm 的指令选择

id 引用位置 用途 核验方式与状态
S40 _vendor/tilelang-v0.1.15/src/op/gemm.cc:33-48 ResolveGemmImpl:按 target 找实现 已核验(2026-10-01)
S41 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:336-375 Gemm::SelectInst 的完整谓词链 已核验(2026-10-01)
S42 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:40-78 CheckWgmma:dtype / 转置 / K 整除 已核验(2026-10-01)
S43 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:80-87 AllowTcgen5Mma:sm_100 + 作用域 + meta 白名单 已核验(2026-10-01)
S44 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:89-97 AllowWgmma:Hopper + M≥64 + warp 数 4 的倍数 已核验(2026-10-01)
S45 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:99-145 AllowVoltaMma / AllowTuringMma 已核验(2026-10-01)
S46 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:119-129 Use2CtaRequested 已核验(2026-10-01)
S47 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:157-181 FatalWgmmaUnavailable / FatalTcgen5Unavailable 已核验(2026-10-01)
S48 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:347-353 2CTA 无单 CTA 回退的注释 已核验(2026-10-01)
S49 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:180-216 ComputeDefaultWarpPartition(含 policy 语义) 已核验(2026-10-01)
S50 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:218-300 ComputeWgmmaWarpPartition(m_warp % 4 == 0) 已核验(2026-10-01)
S51 _vendor/tilelang-v0.1.15/src/cuda/op/gemm.cc:377-422 ComputeWarpPartition / ReuseExistingSharedLayout / RegisterCudaGemm 已核验(2026-10-01)
S52 _vendor/tilelang-v0.1.15/src/op/tcgen5_meta.h:22-177 GetTCGEN5MMAMeta:atom_m/n/k 与 ws/2cta 的合法组合 已核验(2026-10-01):grep -n "GetTCGEN5MMAMeta" → 23
S53 _vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/__init__.py:54-63 指令名 → python 实现的注册表 已核验(2026-10-01)
S54 _vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/gemm_wgmma.py:22-40 infer_shared_layout:按连续性选 swizzle 档位 已核验(2026-10-01)
S55 _vendor/tilelang-v0.1.15/tilelang/cuda/op/gemm/gemm_tcgen05.py:34-60 GemmTCGEN5:SS/TS 变体、use_2cta、_warp_partition 已核验(2026-10-01)
S56 _vendor/tilelang-v0.1.15/tilelang/cuda/intrinsics/macro/tcgen05_macro_generator.py:35-60 TCGEN05DescriptorParams(LBO/SBO/swizzle atom) 已核验(2026-10-01)
S57 _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:32-76 CUDA 方言 gemm:mbar 与隐式等待的说明 已核验(2026-10-01)
S58 _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:103-135 gemm_blockscaled:SM100 vs SM120 两条路 已核验(2026-10-01):grep -n "Block-scaled GEMM for CUDA" → 103
S59 _vendor/tilelang-v0.1.15/tilelang/cuda/language/gemm_op.py:20-30 __all__ 含 tcgen05_gemm / wgmma_gemm 等(导出不一致,见 S116) 已核验(2026-10-01)
S60 _vendor/tilelang-v0.1.15/src/cuda/target_utils.cc:117-122 TargetHasBulkCopy(arch ≥ 90) 已核验(2026-10-01)
S61 _vendor/tilelang-v0.1.15/src/cuda/target_utils.cc:104-116 TargetHasStmatrix / TargetHasTmem 已核验(2026-10-01)
S62 _vendor/tilelang-v0.1.15/src/cuda/target_utils.cc:85-90 TargetCudaHasAsyncCopy(arch ≥ 80) 已核验(2026-10-01)

T.copy / TMA

id 引用位置 用途 核验方式与状态
S63 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:716-807 SelectCopyInstForLowering 主判定链(含 allow_load=false) 已核验(2026-10-01)
S64 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:810-891 ClassifyWarpSpecializedCopy(producer 侧 load 允许 TMA) 已核验(2026-10-01)
S65 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:143-261 CheckGlobalStrides / CheckBulkLoad / CheckBulkStore 已核验(2026-10-01)
S66 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:326-365 CheckBulkCopy1D 已核验(2026-10-01)
S67 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:407-425 CheckLDSMCopy(:407-410)与 CheckTMemLoad/Store(:417-425) 已核验(2026-10-01)
S68 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:531-570 MakeTmaUnavailableReason(device-bound base、FP4 方向限制) 已核验(2026-10-01)
S69 _vendor/tilelang-v0.1.15/src/cuda/op/copy_analysis.cc:593-626 SelectTmaInst / SelectSyncLikeInst 已核验(2026-10-01)
S70 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:791-905 Copy::Lower 分派与 LowerCPAsync 已核验(2026-10-01)
S71 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:913-1040 LowerCluster:bulk / 逐行 / SIMT 三条 SM-to-SM 路径 已核验(2026-10-01)
S72 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:1050-1120 ClusterCopyReplacer(SIMT 回退) 已核验(2026-10-01)
S73 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:2108-2215 Copy::LowerBulk:描述符构造、mbarrier 选择、multicast 参数 已核验(2026-10-01)
S74 _vendor/tilelang-v0.1.15/src/cuda/op/copy.cc:2061-2091 desc.global_stride 用字节填充,并校验最内层 stride 为 1 已核验(2026-10-01)
S75 _vendor/tilelang-v0.1.15/src/cuda/op/tma_layout.h:40-46 kTmaMaxBoxDim = 256 / kTmaSmemAddrAlign = 128 已核验(2026-10-01)
S76 _vendor/tilelang-v0.1.15/src/tl_templates/cuda/copy_sm90.h:51-210 tma_store_cluster 与 tma_load_multicast 系列 已核验(2026-10-01)
S77 _vendor/tilelang-v0.1.15/src/tl_templates/cuda/copy_sm90.h:212-270 1D/2D tma_load 已核验(2026-10-01)
S78 _vendor/tilelang-v0.1.15/src/tl_templates/cuda/barrier.h:11-194 mbarrier 原语与 tma_store_arrive/wait 已核验(2026-10-01)
S79 _vendor/tilelang-v0.1.15/src/transform/inject_pipeline.cc:288-299 给流水里的 copy 打 no_implicit_async_commit_wait(→ cp.async) 已核验(2026-10-01)
S80 _vendor/tilelang-v0.1.15/tilelang/language/copy_op.py:232-300 T.tma_copy docstring:producer 半边与 store 的批量语义 已核验(2026-10-01)
S81 _vendor/tilelang-v0.1.15/tilelang/cuda/language/copy_op.py:19-70 CUDA 方言 copy 的三个提示关键字 已核验(2026-10-01)
S82 _vendor/tilelang-v0.1.15/tilelang/language/builtin.py:370-380、:457-532 T.tma_store_wait / T.mbarrier_wait_parity / T.mbarrier_arrive 已核验(2026-10-01)
S83 _vendor/tilelang-v0.1.15/docs/programming_guides/cluster_tma.md TMA multicast / SM-to-SM 的官方说明与三路径表 已核验(2026-10-01):随锁定版本的 docs 一起发布;未在硬件上运行
S84 _vendor/tilelang-v0.1.15/docs/programming_guides/instructions.md T.copy 各变体的语义合同(同步 / 显式异步 / 严格回退) 已核验(2026-10-01)

warp specialization

id 引用位置 用途 核验方式与状态
S85 _vendor/tilelang-v0.1.15/tilelang/cuda/language/warpgroup.py:19-59 T.ws / WarpSpecialize:warp group = 128 线程 已核验(2026-10-01)
S86 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:27-38 allow_warp_specialized:CUDA + have_tma + 未被禁用 已核验(2026-10-01)
S87 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:39-65 module_has_tma / _module_has_shared_barrier 已核验(2026-10-01)
S88 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:101-109 三个 WS pass 的调用顺序 已核验(2026-10-01)
S89 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:268-271 AnnotateWarpGroupRegAlloc 已核验(2026-10-01)
S90 _vendor/tilelang-v0.1.15/tilelang/cuda/transform/__init__.py:6-22、:35-48、:125-139、:143-156、:158-172 AutoWarpSpecialization / ProducerConsumerWarpSpecialized / InjectTcgen05Fence / AnnotateWarpGroupRegAlloc / MaterializeWSSchedule 的 docstring 已核验(2026-10-01):文件共 196 行
S91 _vendor/tilelang-v0.1.15/src/cuda/transform/ws_analysis.h:33-41 TileStmtKind 分类枚举 已核验(2026-10-01)
S92 _vendor/tilelang-v0.1.15/src/cuda/transform/auto_schedule.cc:1-24 自动 WS 入口:opt-in、把语句打上 tl.ws_op_id 已核验(2026-10-01)
S93 _vendor/tilelang-v0.1.15/tilelang/language/warp_specialize.py:33-66 WSSyncKind / WSRole(含 max_nreg) 已核验(2026-10-01)
S94 _vendor/tilelang-v0.1.15/tilelang/language/warp_specialize.py:69-80 WSPipeline:full/empty mbarrier 对与多版本缓冲语义 已核验(2026-10-01)
S95 _vendor/tilelang-v0.1.15/tilelang/language/warp_specialize.py:107-172 WSSync / WSScope / WSSchedule 已核验(2026-10-01)
S96 _vendor/tilelang-v0.1.15/tilelang/language/annotations.py:27、:151-165 T.WSID 与 T.annotate_ws_schedule 已核验(2026-10-01)
S97 _vendor/tilelang-v0.1.15/examples/warp_specialize/example_warp_specialize_gemm_copy_0_gemm_1.py 本章 3.3 节与 code/04-warp-specialize-gemm/ 的原型 已核验(2026-10-01):文件存在,代码逐行比对;未运行
S98 _vendor/tilelang-v0.1.15/examples/warp_specialize/example_warp_specialize_gemm_softpipe_stage2.py num_stages=2 与角色划分叠加的例子 已核验(2026-10-01);未运行
S99 _vendor/tilelang-v0.1.15/src/cuda/transform/producer_consumer_ws.cc:483-500、:620-632 producer/consumer 分类与 kAsyncCopyNoImplicitCommitWait 的注入 已核验(2026-10-01)

Blackwell / cluster / CuTe DSL

id 引用位置 用途 核验方式与状态
S100 _vendor/tilelang-v0.1.15/tilelang/language/allocate.py:194-222 alloc_barrier:arrive_count ≥ 1、shared.barrier scope 已核验(2026-10-01)
S101 _vendor/tilelang-v0.1.15/tilelang/language/allocate.py:227-251 alloc_cluster_barrier 已核验(2026-10-01)
S102 _vendor/tilelang-v0.1.15/tilelang/language/allocate.py:254-300 alloc_tmem docstring:TMEM 规格、分配规则、访问限制、自动释放 已核验(2026-10-01):grep -n "def alloc_tmem" → 254
S103 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:110-114 LowerBlackwell2SM 必须在 LayoutInference 之前 已核验(2026-10-01)
S104 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:175-202 LowerSharedTmem / mbarrier 前置检查 / LowerSharedBarrier / FuseMBarrierArriveExpectTx 已核验(2026-10-01)
S105 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:256-266 InjectFenceProxy → ThreadSync → InjectTcgen05Fence 已核验(2026-10-01)
S106 _vendor/tilelang-v0.1.15/tilelang/cuda/language/kernel.py:12-40 CUDA 方言 Kernel:cluster_dims 与「T.ClusterKernel 是必填版本」 已核验(2026-10-01)
S107 _vendor/tilelang-v0.1.15/tilelang/language/kernel.py:397-420 launch_kernel:cluster_dims 归一化与写入 launch block 已核验(2026-10-01)
S108 _vendor/tilelang-v0.1.15/examples/gemm_tcgen05/README.md TCGEN05 的「preview」定位、手工 mbarrier 协议、InjectTcgen05Fence 的边界 已核验(2026-10-01);未运行
S109 _vendor/tilelang-v0.1.15/examples/gemm_tcgen05/gemm_tcgen5mma.py 本章 3.4 节与 code/05-tcgen05-gemm/ 的原型(注意其 T.tcgen05_gemm 调用,见 S116) 已核验(2026-10-01);未运行
S110 _vendor/tilelang-v0.1.15/examples/blockscaled_gemm_sm100/gemm_mxfp8_blockscaled_1d1d.py MXFP8 block-scaled 的 SM100 形态(C 在 TMEM) 已核验(2026-10-01):读取前 70 行;未运行
S111 _vendor/tilelang-v0.1.15/examples/gemm_sm120/sm120_nvfp4_blockscaled_gemm.py SM120 的 NVFP4 block-scaled 形态(C 在 fragment) 已核验(2026-10-01):文件存在;未运行
S112 _vendor/tilelang-v0.1.15/tilelang/cuda/cutedsl_backend.py:9-23 CuTe DSL 后端复用 CUDA_PIPELINE 已核验(2026-10-01):文件共 23 行
S113 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/cutedsl/checks.py:1-88 依赖门禁:nvidia-cutlass-dsl >= 4.7.0、禁止 4.3.4、要求 cutlass.cute 已核验(2026-10-01)
S114 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/cutedsl/libgen.py:37-60 CuTe DSL 库生成:写临时目录再 import 已核验(2026-10-01)
S115 _vendor/tilelang-v0.1.15/tilelang/contrib/cutedsl/__init__.py、src/cuda/codegen/codegen_cutedsl.cc:210-270、src/cuda/codegen/rt_mod_cutedsl.cc:86 重导出 CUTLASS/CuTe、Python 打印器、codegen 注册 已核验(2026-10-01):grep -n "tilelang_cutedsl" → rt_mod_cutedsl.cc:86
S116 _vendor/tilelang-v0.1.15/tilelang/cuda/language/__init__.py:88、:108-196、tilelang/language/__init__.py:11-12 显式 GEMM 变体未导出(4.3.6 节的结论) 已核验(2026-10-01):grep -n "from .gemm_op import" → 88;grep -n "_CUDA_API_ALL = (" → 108;facade 的 star-import 在 11 行。未运行验证(无 TileLang 环境)

版本对照(_vendor/tilelang-v0.1.14/ 与 v0.1.15 的差异)

核验方式:两个 tag 的工作树都用 git clone --depth 1 --branch <tag> 取得,没有提交历史, 因此差异全部来自文件级比较:

diff -rq _vendor/tilelang-v0.1.14/tilelang/cuda _vendor/tilelang-v0.1.15/tilelang/cuda
diff -rq _vendor/tilelang-v0.1.14/src/cuda      _vendor/tilelang-v0.1.15/src/cuda
diff -rq _vendor/tilelang-v0.1.14/tilelang/tileop _vendor/tilelang-v0.1.15/tilelang/tileop
diff -u  <v0.1.14 文件> <v0.1.15 文件>
id 差异 核验方式与状态
D1 新增 src/cuda/op/gemm_blockscaled.cc;gemm.cc 里的 blockscaled 分派与 tl.get_tcgen5_blockscaled_instr_desc 被移出 已核验(2026-10-01):diff -u .../src/cuda/op/gemm.cc ...
D2 新增 tilelang/cuda/op/gemm/gemm_tcgen05_blockscaled.py、tilelang/tileop/gemm_blockscaled/;gemm/__init__.py 多注册 cuda.tcgen05_blockscaled 已核验(2026-10-01)
D3 IsTmemBuffer() 取代字面量 "shared.tmem"(gemm.cc、copy_analysis.cc) 已核验(2026-10-01)
D4 CopyInstIsTMA → CopyInstIsTMALoad / CopyInstIsTMAStore;ClassifyWarpSpecializedProducerCopy → ClassifyWarpSpecializedCopy(新增 shared→global 旁路) 已核验(2026-10-01)
D5 tilelang/cuda/pipeline.py:70 的 MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128);插入 AutoWarpSpecialization() 已核验(2026-10-01)
D6 新增 pass config tl.enable_auto_warp_specialization;新增 src/cuda/transform/auto_schedule.cc 与 auto_schedule/;新增 ws_analysis.{h,cc} 已核验(2026-10-01)
D7 src/cuda/transform/materialize_ws_schedule.cc 大幅扩容(diff 3312 行) 已核验(2026-10-01):`diff -u ...
D8 codegen_cuda.{cc,h}:新增 EmitPackedX2Call / EmitPerLaneScalarCall / VisitStmt_(AssertStmtNode);int8x64 → longlong4;fp4/fp8 逐 lane 打包构造 已核验(2026-10-01)
D9 src/cuda/target_utils.cc:新增 float4_e2m1fn → float8_e4m3 可向量化 cast 已核验(2026-10-01)
D10 tilelang/contrib/nvcc.py、tilelang/contrib/nvrtc.py 两版无差异 已核验(2026-10-01):diff -rq 未列出
D11 tilelang/jit/adapter/wrapper.py 的差异几乎全在 TLAscendSourceWrapper(昇腾);NV 侧只有 uint64 类型映射与 get_stream_type 返回类型放宽 已核验(2026-10-01)
D12 examples:diff 显示 v0.1.15 新增 ascend/ 与 kpool/(顶层条目 55 → 57) 已核验(2026-10-01):`ls

corpus 资料引用(资料库索引)

抓取日期:2026-09-30(Asia/Shanghai),cuTile 一条为 2026-10-01,见 corpus 的 README.md。

id 本地路径 上游链接 用途 核验状态
R1 NVIDIA Hopper Architecture In-Depth https://developer.nvidia.com/blog/nvidia-hopper-architecture-in-depth/ TMA 的单线程发射与异步 barrier 语义(第 295 行)、thread block cluster 与 DSM 的定义(第 249、263 行) 已核验(2026-10-01):本地为全文;行号已 grep -n 定位。厂商公开资料,非实测
R2 Parallel Thread Execution ISA https://docs.nvidia.com/cuda/parallel-thread-execution/index.html TMEM 的物理规格与分配规则(29726-29768)、tcgen05.mma 的 shape 表(29785-29810) 已核验(2026-10-01):本地为全文 Markdown(约 4 万行);厂商公开资料
R3 Blackwell Tuning Guide https://docs.nvidia.com/cuda/blackwell-tuning-guide/index.html 可移植 cluster 上限 8 / B200 非可移植 16(第 73 行)、cudaOccupancyMaxActiveClusters 建议(第 52 行)、DSM 的合并与 32 字节对齐建议(第 60-68 行)、SM 共享内存 228 KB(第 50 行) 已核验(2026-10-01):本地为全文;厂商公开资料
R4 CUDA Blackwell Compatibility Guide https://docs.nvidia.com/cuda/blackwell-compatibility-guide/index.html sm_100a / compute_100a 的 PTX 既不前向也不后向兼容(第 28 行);CUDA_FORCE_PTX_JIT=1 的验证方法(第 39-46 行) 已核验(2026-10-01):本地为全文;厂商公开资料
R5 arXiv:2407.08608 https://arxiv.org/abs/2407.08608 FlashAttention-3:warp specialization 的 producer/consumer 定义(第 173 行)、setmaxnreg 与寄存器再分配(第 176-178 行)、FP8 WGMMA 只支持 k-major(第 186-190 行) 已核验(2026-10-01):本地为全文;数字均为论文自述,本章未引用其性能数值
R6 NVIDIA/cutile-python https://github.com/NVIDIA/cutile-python cuTile 走 Tile IR、需要 r580+ 驱动、tileiras 13.2 目前只支持 Blackwell 与 Ampere/Ada(第 64-70 行) 已核验(2026-10-01):本地为仓库 README 快照
R7 CuTe DSL 文档(CUTLASS Python DSL) https://docs.nvidia.com/cutlass/latest/media/docs/pythonDSL/cute_dsl.html 仅用于确认「NVIDIA 官方有 CuTe DSL 这条路线」;本地文件只有 22 行的导航壳 部分核验:文件存在但其内容只是文档站导航,没有正文。见「待补资料」第 1 条
R8 CUDA C++ Programming Guide https://docs.nvidia.com/cuda/cuda-c-programming-guide/index.html 仅用于「CUDA 编程模型对 DSL 使用者也重要」这一句定位(文档目录结构说明) 部分核验:本地 48 行是文档的总览页,不是完整编程指南;对应的 .html(29 KB)也只是落地页。见「待补资料」第 2 条
R9 资料库索引 (corpus 自述,无独立上游链接) corpus 的目录地图与抓取日期 已核验(2026-10-01)
R10 资料库索引 (corpus 的源清单) R1–R8 各条目的 id / 类别 / trust 等级 / 备注 已核验(2026-10-01):sed -n '345,560p'、:1150,1180p

上游链接核验说明

  • 上表标注「已核验」的含义是:确认对应 corpus 文件在本地存在,且其头部记录了抓取日期与 原始 URL;本章写作时没有重新发起网络请求(corpus 抓取日期为 2026-09-30,早于本章 写作日期 2026-10-01,差异在 1 天内)。
  • R7、R8 是本章最弱的两条资料:它们对应的 corpus 文件只有文档站的导航壳/总览页。 因此本章没有基于这两条资料做任何技术论断——CUDA 编程模型的表述一律改引 R2 (PTX ISA,本地全文)或 TileLang 自己的源码与 docs/。
  • _vendor/tilelang-v0.1.15/docs/ 是随锁定版本发布的文档,属于锁定版本的一部分, 不是外部链接,因此在上表中归入源码引用(S83、S84)。

关于性能数字的说明

本章正文中没有任何性能数字。 原因有两层:

  1. 写作环境没有 NVIDIA GPU,无法做任何实测;
  2. 本章的主题是「代码生成与硬件特性」,回答的是「会选哪条指令、产物长什么样」, 不是「哪条更快」。

正文中出现的与性能相关的表述全部是定性且可溯源的,例如「TMA 把地址生成交给硬件、 只需要一个线程发射」(R1 第 295 行)、「更大的 cluster 会降低可并行的 block 数」 (R3 第 73 行)。这些是厂商文档的陈述,不是本章的实测结论。

Parallel Thread Execution ISA 里出现的算力/带宽表格本章 没有引用;arxiv-2407-08608-fulltext.md 里的 740 TFLOPs / 1.2 PFLOPs 等数字也未被引用。


待补资料

  1. CuTe DSL 官方文档正文缺失。 corpus 的 CuTe DSL 文档(CUTLASS Python DSL) 只有 22 行,是文档站的导航壳; 正文在 cute_dsl_general/*.html 下,未被抓取。建议后续版本补抓 https://docs.nvidia.com/cutlass/latest/media/docs/pythonDSL/cute_dsl_general/dsl_introduction.html 与同目录的 dsl_code_generation.html。本章因此只能对 CuTe DSL 做定位性描述, 技术判断全部来自 TileLang 侧的 checks.py 与 contrib/cutedsl/。
  2. CUDA C++ Programming Guide 全文缺失。 corpus 里的 CUDA C++ Programming Guide(48 行)与同名 .html (29 KB)都只是文档落地页,没有编程模型、cp.async、TMA、mbarrier 的章节正文。 建议补抓 https://docs.nvidia.com/cuda/cuda-c-programming-guide/ 的 01-introduction / 02-basics / 04-special-topics 各章。本章涉及的 CUDA 语义因此改引 PTX ISA 全文(R2)与 TileLang 的 docs/programming_guides/*(S83、S84)。
  3. T.tcgen05_gemm 导出不一致未做运行验证。 4.3.6 节与 S116 的结论完全来自 __all__ / import / star-import 三处源码;因为没有可用的 TileLang 环境, 无法用 hasattr(T, "tcgen05_gemm") 实际确认。建议后续版本在有 CUDA 的机器上跑一次 一行验证,并把结论写进 audit/。
  4. examples/ 未运行。 本章引用了 6 个官方 example(S97、S98、S108–S111), 全部只做了静态核对。它们的正确性、以及 T.tcgen05_gemm 那类调用能否真正跑通, 都需要在 sm_90 / sm_100 机器上验证。
  5. NVIDIA Blackwell 架构白皮书未在 corpus。 本章关于 Blackwell 的描述全部来自 tuning guide 与 compatibility guide(R3、R4)加 PTX ISA(R2)。如果要写更细的 SM 微架构(如每 SM 的 Tensor Core 数量、TMEM 带宽),需要补厂商白皮书。

本章共 9 个二级小节;右上角目录在桌面端会跟随滚动高亮。