跳到主要内容
TileLang 系统教程

tilelang-tutorial.huandu.me

第 04 章背景、设计思想与架构25 分钟12 习题8 自测题

架构总览与一次编译的全流程

给出一张可随时重画的仓库结构图与一次编译的完整路径:前端 → TIR → pass pipeline → device codegen → host wrapper,并对比 CUDA 与 Ascend 两条管线。

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

学习目标

读完本章,你应当能够:

  1. 说出 v0.1.15 仓库里 16 个顶层模块各自负责什么,并在 30 秒内判断「我要改的这个东西应该落在哪个目录」。
  2. 独立画出并讲清 Python kernel → IRModule → 后端 pass pipeline → 设备源码 → host wrapper → 可调用对象 这条链,并指出每一跳的入口函数与文件:行号。
  3. 打开 tilelang/cuda/pipeline.py 或 tilelang/ascend/pipeline.py,按源码顺序列出完整 pass 序列,并指出哪些 pass 是条件执行的、条件由谁决定。
  4. 用 tilelang/backend/ 里的五类组件解释这句话:「加一个新后端不需要改 engine/lower.py」。
  5. 严格区分 target backend 与 execution backend,并各举 v0.1.15 里的两个真实例子。
  6. 拿到一条关于 TileLang 的说法时,能判断它属于「v0.1.15 里的事实」还是「仅 main / 未合入」, 并给出可复现的核验命令。

前置知识

  • 第 3 章「设计哲学与编程模型」:知道 tile / dataflow / schedule 三件事分别指什么, 知道 TileLang 有 beginner / developer / expert 三层接口。
  • 会写 Python、看过装饰器(@decorator);知道「编译」大致是把高级代码变成低级代码。
  • 不需要读过 TVM,也不需要写过 CUDA。本章出现的 IR、pass、codegen 都会先用一句话解释再给术语。

本章只回答一个问题:我写的那几十行 Python,到底经过了谁,最后变成了什么? 第 20 章会把本章列出的承重 pass 逐个剖开;第 24、25 章分别展开 NVIDIA 与昇腾的代码生成。


1. 问题的由来

1.1 这张地图要回答的三个问题

第一次读到 TileLang 代码的人,通常会卡在三个具体的位置:

  • T.gemm(A_shared, B_shared, C_local) 是一行 Python,它凭什么变成硬件指令?在哪个文件里变?
  • 只用公共语言表面写的同一份 kernel,可以分别以 target="cuda" 和 target="ascend" 编译。 分岔点在哪一行?
  • 官方说「多后端是解耦的」「加后端不用改 engine」。这句话在仓库里对应哪些文件?

这三个问题没有一个是「怎么用 API」的问题,它们都是结构问题。结构问题的答案不该靠背, 而该靠一张能随时重画的地图。本章就是把这张地图画出来,并且把地图上的每个地名都换成 _vendor/tilelang-v0.1.15/ 里真实存在的路径与行号。

1.2 本章的版本纪律

全书锁定 TileLang v0.1.15(commit a35f8dd)与 TileLang-Ascend TileLang-ascend-v0.1.2.000-release(见 版本锁定)。 本章严格遵守下面四条:

  1. 源码只引 _vendor/tilelang-v0.1.15/,行号用 grep -n / sed -n 现场核对, 不凭记忆。
  2. 不把 main 当成 v0.1.15。_vendor 之外的任何 checkout(包括 corpus 里那份 TileLang 主干仓库 · 资料清单 的 main 快照)都只用于「对照」,且在正文里显式标注。
  3. 不宣称未验证的运行结果。本机是 macOS(Apple Silicon),没有 NVIDIA GPU、 没有昇腾 NPU,也没有安装 tilelang/torch,所以任何「跑起来是什么样」的结论都要 写成「应当是什么样」并给出验证步骤。
  4. 不写没有出处的性能数字。本章不讨论性能,只讨论结构。

顺手说清一个容易混的点:v0.1.15 是 tag,不是「最新的 main」。 在写这一章时(2026-10-01):

_vendor/tilelang-v0.1.15/            git describe --tags → v0.1.15
                                     git log -1 → a35f8dd "[Release] Bump version into 0.1.15 (#3309)"
../tilelang-corpus/02-tilelang-core-repo/raw/tilelang
                                     git log -1 → 994b44e "[Ascend] Add PTO backend for Ascend950 (#3310)"

也就是说,两份 checkout 只差一个已合入的 PR:corpus 的 main 快照停在 PR #3310 (Ascend 950 的 PTO backend),而 v0.1.15 对应的是 PR #3309(bump version)。 这个差异在下面 4.9 与 5.3 节会变成一个具体的判据。


2. 概念与模型

v0.1.15 仓库地图(模块 → 职责)tilelang/language/语言表面:tile op、frame、parser;两条前端在 eager/ 与 ast/tilelang/engine/lower.py编译入口:lower / semantic_check / paramtilelang/backend/后端注册与上下文解析tilelang/<backend>/pipeline.pypass 的「顺序」写在这里(Python)TIR IRModulepass 反复改写的中间表示src/transform/*.ccpass 的本体:42 个 .ccsrc/layout/layout inference 的 C++ 实现src/<backend>/codegen/CUDA C / Ascend C codegentilelang/jit/adapter/tvm_ffi / nvrtc / cython / torch可调用 kernel
v0.1.15 的仓库地图:语言表面 → 编译入口 → 后端注册 → pass pipeline → codegen → adapter → 可调用 kernel。
  • 最反直觉、但一定要接受的安排:pass 的「顺序」写在后端包的 Python 里,pass 的「行为」写在 C++ 的 src/transform/ 里。
  • Python 侧:language/(语言表面,47 个 .py)、engine/(编译入口,5 个 .py)、transform/(通用 pass 的 Python 包装,7 个 .py)、layout/(Layout / Fragment / swizzle 代数,10 个 .py)、backend/(多后端抽象,9 个 .py + pass_pipeline/)、jit/(装饰器、JITKernel、adapter,31 个 .py)。
  • 后端包:cuda/(language / op / intrinsics / pipeline / codegen,55 个 .py)、ascend/(language / op / transform / pipeline / codegen,35 个 .py)、{rocm,metal,cpu,webgpu}/。
  • C++ 侧:src/ir.cc 与 src/op/(IR 定义与 tile op 实现)、src/transform/(42 个 .cc)、src/layout/layout_inference/、src/cuda/ 与 src/ascend/(codegen + runtime module,共 384 个 .cc/.h)。
  • 横向支撑:cache/(kernel 级 + 二进制级缓存,5 个 .py)、autotuner/(参数搜索)、carver/(tile 配置推荐,34 个 .py)、tools/(lower_trace / plot_layout / pass_timing,20 个 .py)、examples/(54 个子目录)、docs/(37 个 Markdown)。

2.1 五个阶段,一条竖线

不管后端是 CUDA 还是 Ascend,一次编译都可以切成五段。请把这张竖线记住,本章后面所有内容 都是它的展开:

①  前端(frontend)       Python 函数          →  TIR IRModule
②  编译期(lowering)     IRModule             →  IRModule(被 pass 反复改写)
③  代码生成(codegen)    IRModule             →  设备源码(CUDA C / Ascend C / …)
④  宿主封装(host)       host IRModule        →  host wrapper + 参数/ABI 描述
⑤  运行时(runtime)      源码 + wrapper       →  可调用对象(Adapter / torch 函数)

每一段都有明确的「入口函数」,而且入口函数所在的位置在两条后端上是同一个:

阶段 入口 位置
① 前端 tilelang.language.parser / tilelang.language.eager tilelang/language/
② 编译期 tilelang.engine.lower.lower_with_context tilelang/engine/lower.py:144
② 中的后端分岔 BackendContext.lower → PassPipeline.lower tilelang/backend/module.py:211、tilelang/backend/pass_pipeline/pipeline.py:22
③ 代码生成 tilelang.engine.lower.device_codegen tilelang/engine/lower.py:99
④ 宿主封装 tilelang.engine.lower.host_codegen tilelang/engine/lower.py:65
⑤ 运行时 JITKernel._create_adapter_from_artifact tilelang/jit/kernel.py:324

这张表是本章最重要的一页:它说明「后端差异」被压缩进了第 ② 阶段的一个函数调用里 (context.lower(mod),tilelang/engine/lower.py:136)。其余所有阶段都是共享的。

2.2 仓库地图

下图见 图:fig-01-repo-map(Mermaid 版 + ASCII 版)。这里给一眼能看完的版本:

                          tilelang/jit/  (装饰器, JITKernel, adapter)
                                 |
                                 v
tilelang/language/  -->  tilelang/engine/lower.py  -->  tilelang/backend/  (注册 + 上下文)
   | 语言表面             编译入口                       |
   |                                                  +--> tilelang/<backend>/pipeline.py
   v                                                  |
TIR IRModule <----------------------------------------+
   |
   +--> src/transform/*.cc            (pass 本体, 42 个 .cc)
   +--> src/layout/layout_inference/  (layout 求解)
   +--> src/<backend>/codegen/        (CUDA C / Ascend C codegen)
   |
   v
tilelang/jit/adapter/  (tvm_ffi / nvrtc / cython / torch / cutedsl)
   |
   v
可调用 kernel

横向支撑:
  tilelang/cache/     kernel 缓存与 CUDA / Ascend 二进制缓存
  tilelang/autotuner/ 参数搜索            tilelang/carver/  tile 配置推荐
  tilelang/tools/     lower_trace / plot_layout / pass_timing / pass_visualizer
  examples/ (54 子目录)   docs/ (37 Markdown)

注意这张图里有一处非常反直觉、但一定要接受的安排:pass 的「顺序」写在后端包的 Python 里 (tilelang/cuda/pipeline.py、tilelang/ascend/pipeline.py),pass 的「行为」写在 C++ 里 (src/transform/*.cc)。初学者经常在 src/transform/ 里找「pass 的执行顺序」,那是找不到的; 反过来,在 tilelang/cuda/pipeline.py 里找「LayoutInference 到底做了什么」,也找不到。

2.3 十六个模块的职责

下表每一行的「数量」列都来自 code/repo-map/inventory.py 的实测输出(v0.1.15)。

模块 职责 实测规模
tilelang/language/ 语言表面。T.Kernel、T.alloc_*、T.copy、T.gemm、T.Pipelined 等 tile op 与 frame 的定义;另有 parser/(AST 路线)、eager/(builder 路线)、ast/、overrides/ 两类前端实现。 47 个 .py,6 个子目录
tilelang/engine/ 编译入口。lower() / lower_with_context() / host_codegen() / device_codegen() 全在这里;semantic_check.py 放后端无关的前端语义检查;param.py 定义 CompiledArtifact。 5 个 .py
tilelang/transform/ 通用 pass 的 Python 包装。每个 def LayoutInference() 都只做一件事:调 _ffi_api.LayoutInference() 把 C++ pass 取出来。另有 simplify.py、pass_config.py。 7 个 .py
src/transform/ pass 的 C++ 本体。layout_inference/、inject_pipeline.cc、lower_tile_op.cc、make_packed_api.cc … 都在这里。 42 个 .cc,66 个文件
tilelang/layout/ 布局代数。Layout、Fragment、PartialFragment 与 swizzle 构造函数(make_swizzled_layout、make_tcgen05mma_swizzled_layout…),以及昇腾的 make_ascend_nz_layout 等。 10 个 .py
tilelang/backend/ 多后端抽象。注册表、五类组件接口、BackendContext 解析、目标探测。本章 2.4 与 4.4 节的主角。 9 个 .py + pass_pipeline/
tilelang/cuda/ CUDA 后端垂直切片:language/(CUDA 方言)、op/、intrinsics/、pipeline.py、codegen.py、backend.py(manifest)。 55 个 .py
tilelang/ascend/ 昇腾后端垂直切片:language/(alloc_l1/l0a/l0b/l0c、SimtVF、dual_copy…)、transform/、pipeline.py、codegen.py、backend.py。 35 个 .py
src/ascend/ 昇腾的 C++ 侧:codegen/codegen_ascend.cc、op/、layout/、transform/、runtime/。 80 个 .cc/.h
tilelang/jit/ 装饰器与运行时装配:jit()、JITImpl、JITKernel、adapter/(tvm_ffi / cython / nvrtc / torch / cutedsl)、wrapper.py(生成 host wrapper 源码)。 31 个 .py
tilelang/cache/ 两级缓存:KernelCache(整个 kernel 的磁盘缓存)+ CUDABinaryCache / AscendBinaryCache(编译产物缓存)。 5 个 .py
tilelang/autotuner/ 参数搜索:autotune / AutoTuner / capture / grouped_compile。 5 个 .py
tilelang/carver/ tile 配置推荐:给 GEMM / reduction / elementwise 生成并排序 tiling 候选,可转给 TVM / Triton / TileLang。 34 个 .py
tilelang/tools/ 开发者工具:plot_layout、lower_trace/(逐 pass 对比 IR)、pass_visualizer/、pass_timing.py、Analyzer.py、compile_only.py。 20 个 .py
examples/ 官方示例。GEMM 家族、FlashAttention、DeepSeek MLA/NSA/MoE、quickstart.py 等。 54 个子目录 + 3 个顶层文件(共 57 项)
docs/ 官方文档源文件(Sphinx)。compiler_internals/、runtime_internals/、programming_guides/、deeplearning_operators/、tutorials/、tools/、get_started/。 37 个 .md,9 个子目录

两个容易被忽略的事实:

  • tilelang/language/__init__.py 的第 11–12 行写着 from tilelang.cuda.language import *,文件末尾还有 __tilelang_dialect__ = "cuda" (tilelang/language/__init__.py:1-32)。也就是说,import tilelang.language as T 拿到的 其实是「公共表面 + CUDA 扩展」;昇腾要用 from tilelang.ascend import language as T。 这是 v0.1.13 引入「多后端语言方言」之后的现状,不是临时 hack。
  • tilelang/transform/__init__.py 与 src/transform/ 是成对的:想在 pipeline 里用一个新的 C++ pass,必须先在这里加一个薄包装函数,否则 Python 侧拿不到它。

2.4 两类「后端」

这是本章最容易混、也最有价值的一个区分(图见 图:fig-03-backend-abstraction):

target backend(决定「生成什么」)            execution backend(决定「怎么编译和启动」)
------------------------------------------    ------------------------------------------
cuda      → tilelang/cuda/pipeline.py         tvm_ffi  / nvrtc / cython
cutedsl   → 复用 CUDA pipeline,换 codegen     cutedsl
ascend    → tilelang/ascend/pipeline.py       tvm_ffi  / cython
hip       → tilelang/rocm/pipeline.py         见 tilelang/rocm/execution_backend.py
metal / c / llvm / webgpu → 各自               见各包 execution_backend.py
  • target backend 拥有:语言方言、pass pipeline、host/device codegen、目标工具链回调。
  • execution backend 拥有:缓存编排、wrapper 构造、产物加载、参数与 stream 绑定、启动、 运行时生命周期。

它们为什么必须分开?看一个具体的组合就明白了:同一个 CUDA target backend,可以走 tvm_ffi、nvrtc、cython 三种执行路径(tilelang/cuda/execution_backend.py:24-33)。 换执行后端不会换掉 pass pipeline,也不会换掉生成的 CUDA C。反过来,换 target backend (CUDA → Ascend)会换掉 pipeline,但执行层可以继续用同一个 tvm_ffi。

官方把这条设计原则写在 资料库索引: 「CUDA can execute through tvm_ffi, nvrtc, or cython; selecting nvrtc does not select a different target architecture or pass pipeline.」


3. 用法:一次编译到底发生了什么

一次编译的全流程(Python kernel → 可调用对象)1–2. 用户写 kernel + 装饰器@tilelang.jit 包成 JITImpl(jit/__init__.py:588)3. get_tir() 取值取 shape把 Python 函数跑成 PrimFunc(jit/__init__.py:379)4. 缓存查询cache/__init__.py:75-98;未命中才继续5. 创建 BackendContext(只一次)backend/module.py:310-3356. 前端语义检查engine/semantic_check.py:21-317. 后端 pass pipelinecuda/pipeline.py:284 或 ascend/pipeline.py:1908. host / device 切分engine/lower.py:109-1419. device codegenengine/lower.py:99-10610. host codegen(可选)engine/lower.py:65-8911. CompiledArtifactengine/param.py:153-16612. adapter 构造jit/kernel.py:324-42413. 可调用对象JITKernel.__call__ 转发给 adapter
每个方框后面标注了实际实现所在的「文件:行号」(核验日期 2026-10-01);虚线是缓存命中时的短路。
  • 关键分界线:Python 侧写「顺序」(tilelang/<backend>/pipeline.py),C++ 侧写「做什么」(src/transform/)。
  • 想改 pass 的顺序、开关、目标相关裁剪 → 改 tilelang/<backend>/pipeline.py;想改某个 pass 的行为 → 改 src/transform/。
  • 缓存短路:kernel_cache 命中时整个 pipeline 都不会跑,直接返回旧的 JITKernel——这也是「改了 pass 但结果没变」的常见原因。
  • 三条容易忽略的支路:adapter 由 BackendContext.execution_backend 决定(不由 target 决定);host codegen 默认关闭(enable_host_codegen=False 时只产出 device 源码 + IR);SplitHostDevice 之后 IR 分成 host 与 device 两个模块,调试时要分开看。

3.1 最小的 kernel

用官方 examples/quickstart.py:8-48 的 GEMM+ReLU(本章 code/quickstart-gemm/ 有带插桩版本):

import tilelang
import tilelang.language as T


@tilelang.jit
def matmul(A, B, block_M: int, block_N: int, block_K: int):
    M, N, K = T.const("M, N, K")
    dtype = T.float16
    A: T.Tensor((M, K), dtype)
    B: T.Tensor((K, N), dtype)
    C = T.empty((M, N), dtype)

    with T.Kernel(T.ceildiv(N, block_N), T.ceildiv(M, block_M), threads=128) 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), T.float32)

        T.clear(C_local)
        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)

        for i, j in T.Parallel(block_M, block_N):
            C_local[i, j] = T.max(C_local[i, j], 0)
        T.copy(C_local, C[by * block_M, bx * block_N])

    return C


kernel = matmul.compile(M=1024, N=1024, K=1024, block_M=128, block_N=128, block_K=32)

这 20 行里有 4 个「语义级」的声明,编译器后面所有工作都是为了兑现它们:

声明 语义 谁来兑现
T.Kernel(..., threads=128) 一个 2-D 网格,每块 128 线程 MaterializeKernelLaunch(tilelang/transform/__init__.py:204)
T.alloc_shared / T.alloc_fragment 数据放在 shared / fragment(寄存器) LayoutInference + LowerTileOp
T.copy tile 级搬运 LowerTileOp → TMA / cp.async / LDG-STG / 昇腾搬运
T.gemm tile 级矩阵乘 LowerTileOp → mma / wgmma / tcgen05 / Cube
T.Pipelined(..., num_stages=3) 软件流水 + 多缓冲 PipelinePlanning + InjectSoftwarePipeline(两侧都用)

注意最后一列:用户写的每一个高层原语,都能对应到一个具体的 pass。这条「原语 ↔ pass」 的对应关系是第 20 章的主线;本章只保证你能定位到它们。

3.2 按图走一遍

完整流程见 图:fig-02-compile-flow。这里用文字讲一遍,每一步都给入口:

  1. 装饰器阶段。 @tilelang.jit 调用 jit()(tilelang/jit/__init__.py:588), 把用户函数包成 JITImpl(tilelang/jit/__init__.py:270)。此时还没有编译, 只是拿到了源码与签名。
  2. 取值阶段。 matmul.compile(M=1024, ...) 触发 JITImpl.compile (tilelang/jit/__init__.py:466),它先调 get_tir()(:379)把 Python 函数 真的执行一遍,得到 PrimFunc。
  3. 缓存阶段。 compile() 最终走 tilelang.cache.cached()(tilelang/cache/__init__.py:75), 由 _resolve_cache_dispatch()(:38)先解析出 BackendContext,再在 KernelCache(tilelang/cache/kernel_cache.py:35)里查 key。命中就直接返回旧的 JITKernel,整个 pipeline 一步都不跑。
  4. 上下文阶段。 未命中时构造 JITKernel(tilelang/jit/kernel.py:41), 它在 :113 处 create_backend_context(target, target_host, execution_backend) (tilelang/backend/module.py:310)——整个编译过程只解析一次后端。
  5. 编译阶段。 JITKernel._compile_artifact(tilelang/jit/kernel.py:288)在 tvm.transform.PassContext(opt_level=3, config=pass_configs, ...) 里调用 tilelang.engine.lower.lower_with_context(tilelang/engine/lower.py:144)。
  6. 语义检查。 lower_to_host_device_ir(:109)先跑 PreLowerSemanticCheck(tilelang/engine/semantic_check.py:21):后端无关的 NestedLoopChecker / ParallelLocalIndexChecker / FragmentLoopChecker。
  7. 后端 pipeline。 紧接着 mod = context.lower(mod)(tilelang/engine/lower.py:136) → BackendModule.lower → PassPipeline.lower → 具体后端的 pipeline.py。 这就是唯一的后端分岔点。
  8. host/device 切分。 pipeline 内部已经跑过 SplitHostDevice (tilelang/transform/__init__.py:285),回到 engine 后用 tirx.transform.Filter 把 host 与 device 两个 IRModule 分开 (tilelang/engine/lower.py:138-139)。
  9. 设备代码生成。 device_codegen()(:99)→ context.codegen_device() → 后端 manifest 里声明的 DeviceCodegen。CUDA 会调到 native 的 target.build.tilelang_cuda。
  10. 宿主代码生成(可选)。 host_codegen()(:65)只在 enable_host_codegen=True 或所选 execution backend 声明需要时执行。 tvm_ffi 需要它(tilelang/cuda/execution_backend.py:24-30 里 enable_host_codegen=True),nvrtc / cython 不需要。
  11. 产物。 结果打包成 CompiledArtifact(tilelang/engine/param.py:154): host_mod / device_mod / params / kernel_source / rt_mod。
  12. 适配器。 _create_adapter_from_artifact(tilelang/jit/kernel.py:324)按 execution_backend 选择 TVMFFIKernelAdapter / NVRTCKernelAdapter / CythonKernelAdapter / CuTeDSLKernelAdapter / MetalKernelAdapter (tilelang/jit/adapter/__init__.py:1-6)。
  13. 可调用对象。 JITKernel.torch_function = adapter.func(tilelang/jit/kernel.py:149), JITKernel.__call__(:206)直接转发给它。用户写的 kernel(a, b) 走的就是这一行。

3.3 我想自己看一遍

三个由浅入深的做法,从「不用装任何东西」到「要真实设备」:

# (a) 不装 tilelang,只看结构:本章自带的三个离线脚本
cd chapters/04-architecture-and-compilation-flow/code
python3 compile-path-check/check_compile_path.py   # 30 跳是否都对得上
python3 pass-order/dump_pass_order.py | head -40   # CUDA/Ascend 的真实 pass 顺序
python3 repo-map/inventory.py                      # 模块规模

# (b) 装了 tilelang 且有 GPU:编出来但不跑,只看生成的源码与回写的 TIR
python3 code/quickstart-gemm/quickstart_gemm.py --out /tmp/tl-debug
#     会在 /tmp/tl-debug 下写出 tilelang_jit_kernel_<name>.c 与
#     tilelang_jit_program_<name>.py(后者是 prim_func.script() 的文本)

# (c) 要看每个 pass 前后 IR 的变化
#     官方工具在 tilelang/tools/lower_trace/(逐 pass 对比)
#     与 tilelang/tools/pass_visualizer/(结构化浏览),
#     也可用 PassConfigKey.TL_ENABLE_DUMP_IR + TL_DUMP_IR_DIR 直接 dump

更具体地说,第 12 章的工具路线是: TILELANG_PASS_DIFF=1 / tilelang.tools.lower_trace 打印每次 pass 前后的 IR diff; PassConfigKey.TL_ENABLE_DUMP_IR + TL_DUMP_IR_DIR 把 IR dump 到磁盘 (见 code/quickstart-gemm/quickstart_gemm.py 里对 debug_root_path 的用法, 以及 tilelang/jit/kernel.py:298-301 对 TL_ENABLE_DUMP_IR 的判断)。


4. 实现机制

多后端抽象:注册清单、四类组件与 BackendContext四类可插拔组件BackendContext(一次解析,之后不可变)BackendModule(注册清单)一个后端声明自己提供什么registrymodule.py:234-307PassPipelinepass_pipeline/pipeline.py:11-39DeviceCodegendevice_codegen.py:31-44HostCodegen / Hookhost_codegen.py:33-52ExecutionBackendSpecexecution_backend.py:18-32BackendContext每次编译解析一次,之后不可变(frozen dataclass)module: BackendModuletarget: device Targettarget_host: host Targetexecution_backendExecutionBackendSpec
注册清单声明一个后端提供什么;registry 解析出只读的 BackendContext,四类组件再从 Context 里取自己需要的那一份。
  • BackendModule 的字段:target_kinds + supports_target、pipelines: {kind: PassPipeline}、device_codegens: {kind: DeviceCodegen}、host_codegens + host_codegen_hooks、execution_backends: tuple[ExecutionBackendSpec]、callbacks (FFI)。
  • 注册表的硬校验:每个 target_kinds 必须有恰好一个 pipeline 与一个 device codegen,pipeline 的 name 必须等于 target kind,且至少有一个 execution backend;同一 target kind 上多个 manifest 共存时必须都提供 supports_target 谓词。
  • CUDA 与 CuTeDSL 都占 cuda 这个 target kind,靠 is_plain_cuda_target / is_cutedsl_target 区分,而且 CuTeDSL 显式复用 CUDA 的 pipeline。
  • 共享的 host codegen 只有 c 与 llvm 两种(host_codegen.py:55-59);两个后端共用的开关注入点在 pass_pipeline/pipeline_utils.py(allow_vectorize / should_force_let_inline / LayoutVisual 等)。
CUDA 与 Ascend 两条 pipeline 的阶段对照CUDA pipeline(cuda/pipeline.py)Ascend pipeline(ascend/pipeline.py)共同起点BindTarget → MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128)BindTarget → MaterializeKernelLaunch(lower_grid_binding=True, launch_dim_tags=["cthread"])规范化与小 pass 前置LetInline / AddWrapperForSingleBufStore / LegalizeNegativeIndex / VerifyParallelLoop / InjectAssumes / Simplify;CanonicalizeLegacyReducer / VerifyReducerEpoch / VerifyBufferInit同样的一段(两侧共同),但顺序与守卫不同分岔 1:后端专属前处理AutoWarpSpecialization → MaterializeWSSchedule → ProducerConsumerWarpSpecialized → LowerBlackwell2SMRewriteDualCopy → LegalizeSimdMerging分岔 2:流水 / 布局 / 调度PipelinePlanning + InjectSoftwarePipeline 在 LayoutInference 之前;之后 LowerTileOpAscendLayoutInference 先推,再进 16 步 AutoSchedule(含 Z3 求解);之后 AscendLowerTileOp共同:标准 loweringDecoupleTypeCast / LegalizeVectorizedLoop / LegalizeSafeMemoryAccess / LowerAccessPtr / HoistGlobalBufferAllocations / LowerOpaqueBlock / FlattenBuffer / ConfigIndexBitwidth / VectorizeLoop / LoopUnswitching / UnrollLoop / VerifyMemory同一段(39 个同名 pass 构成的走廊)分岔 3:同步与存储合并LowerThreadAllreduce → LowerLDGSTG → LowerHopperIntrin → MarkCudaSyncCalls → MergeSharedMemoryAllocations → ThreadSync(shared) ×2 → InjectTcgen05FenceLowerThreadAllreduce → AscendThreadSync(global) → MarkScalarDcacheBypass → MergeUBAllocations → RewriteFlagToBuf → AscendThreadSync(shared) ×2共同收尾MergeIfStmt → MakePackedAPI → Simplify → LowerDeviceKernelLaunch;额外一步 PersistThreadblockMergeIfStmt → MakePackedAPI → Simplify → LowerDeviceKernelLaunch
两条 pipeline 共享同一条「tile op 之后」的标准 lowering 走廊,差异集中在三处:网格物化的参数、流水 / 调度由谁负责、同步与片上存储怎么合并。
  • 完整清单:CUDA 73 步、Ascend 80 步,同名 pass 39 个,带条件守卫的分别是 CUDA 7 步、Ascend 17 步(脚本 ../code/pass-order/dump_pass_order.py)。
  • 步数口径:正文 4.5 / 4.7 节按 mod = <pass>(...) 语句统计;源图 fig-04 写的是 74 / 80,差异来自统计口径,以正文为准。
  • pass 序列是一段带分支的程序,不是一张静态表:Ascend 的 13 步 AutoSchedule 段由 if auto_schedule_enabled 包住,关闭时才会补一个 InferBufferAliases。
  • 本图只画阶段,不画每个 pass 的参数;逐行清单见正文 4.5、4.6 节。

4.1 前端:从装饰器到 IRModule

前端有两条独立路线,都在 tilelang/language/ 之下:

路线 用户写法 实现位置 产物
lazy / parser 路线 @T.prim_func + def main(A: T.Tensor(...)) tilelang/language/parser/entry.py:37 的 prim_func PrimFunc
eager / builder 路线 直接写 @tilelang.jit def f(A, B): A: T.Tensor[...] ... tilelang/language/eager/builder.py(:1760 的 prim_func,内部 mutate(func) 改写 AST) PrimFunc 或 JITFunc

两条路线的共同产物是 PrimFunc;tilelang/jit/__init__.py:394-415 的 _infer_jit_mode / initialize_jit_mode 决定一个装饰过的函数走哪条路 (「函数返回 PrimFunc」= lazy;「函数体使用 builder 模式」= eager)。

这一步不产生硬件信息。 前端只建立三类事实:buffer(形状、dtype、scope)、 访问关系(谁读谁写)、并行域(网格与循环结构)。把「数据怎么放、指令怎么发」留白, 正是第 3 章讲的「dataflow 与 schedule 解耦」。

4.2 engine:BackendContext 只解析一次

tilelang/engine/lower.py 只有 244 行,但它是全书的枢纽。它的四个函数值得记住:

def lower(func_or_mod, target="auto", target_host=None, ...) -> CompiledArtifact   # :216
def lower_with_context(func_or_mod, context, ...) -> CompiledArtifact              # :144
def lower_to_host_device_ir(func_or_mod, context, ...)                             # :109
def host_codegen(host_mod, context) -> tvm.IRModule                                # :65
def device_codegen(device_mod, context) -> tvm.IRModule                            # :99

lower() 只做一件事:context = create_backend_context(target, target_host, "auto") (:237),然后转交 lower_with_context。为什么要拆成两个函数? 因为 lower() 是「用户入口」,而 lower_with_context() 是「已经知道后端的人用的入口」—— JIT 层、缓存层、autotuner 都走后者,避免重复解析(tilelang/jit/kernel.py:113-115 明确写了「Direct callers may omit it and let this public entry resolve one context」)。

BackendContext 本身是 frozen=True, slots=True 的 dataclass (tilelang/backend/module.py:188-231),并在 __post_init__ 里断言 module.matches(target)、execution_backend 属于该 module。也就是说: 一个 Context 一旦构造成功,就不可能自相矛盾。

4.3 后端 pipeline:顺序写在 Python 里

以 CUDA 为例,tilelang/cuda/pipeline.py 只有三个值得关注的东西:

def CUDAPassPipelineBodyPrologue(mod, target):   # :68   —— 33 步
    ...

def CUDAPassPipelineBody(mod, target):           # :168  —— 先调 Prologue,再 40 步
    mod = CUDAPassPipelineBodyPrologue(mod, target)   # :171
    ...

CUDA_PIPELINE = PassPipeline("cuda", CUDAPassPipelineBody)   # :284

PassPipeline(tilelang/backend/pass_pipeline/pipeline.py:11-39)只是个薄壳: 它把执行包在 pass_pipeline(self.name) 的 instrumentation 会话里, 并在异常时调用 enrich_error(exc) 补上源码位置。它不做任何 pass 调度—— 顺序完全由传进去的 lower 函数(即 CUDAPassPipelineBody)决定。

这句「不做任何调度」很重要,它意味着:

想给 CUDA 后端加一个 pass,你不需要学一套描述语言,只需要在 tilelang/cuda/pipeline.py 里找到合适的位置插一行 mod = XxxPass()(mod)。

4.4 多后端抽象:tilelang/backend/

这是本章的第 4 个必答点。tilelang/backend/ 只做三件事(细节见 图:fig-03-backend-abstraction):

(a)注册表。 后端包在 import 时构造一个 BackendModule(tilelang/backend/module.py:28-99) 并调 register_backend()(:238)。注册表有两个全局字典:_BACKENDS(name → manifest) 与 _TARGET_KIND_INDEX(target kind → name 列表)。注册时做的校验很硬:

  • 每个 target_kinds 必须有恰好一个 pipeline 和恰好一个 device codegen;
  • pipeline 的 name 必须等于 target kind;
  • 必须有至少一个 execution backend;
  • 如果声明了 enable_host_codegen,就必须提供 host codegen;
  • 同一个 target kind 上多个 manifest 共存时,必须都提供 supports_target 谓词 (:247-251)。

最后一条正是 CUDA / CuTeDSL 共存的方式:

# tilelang/cuda/backend.py:123-143
BackendModule(name="cuda", target_kinds=("cuda",),
              supports_target=codegen.is_plain_cuda_target, ...)

# tilelang/cuda/cutedsl_backend.py:8-23
BackendModule(name="cutedsl", target_kinds=("cuda",),
              supports_target=codegen.is_cutedsl_target,
              pipelines={"cuda": pipeline.CUDA_PIPELINE}, ...)

两个 manifest 都占 cuda 这个 kind,靠 is_plain_cuda_target / is_cutedsl_target 区分(tilelang/cuda/codegen.py:8-13), 而且 CuTeDSL 显式复用 CUDA 的 pipeline。这正是 README 说的 「Pipeline reuse must be explicit; it must not come from target-specific branching in the engine」 (资料库索引)。

(b)五类组件。 每个组件都是一个 frozen dataclass 或小类,只描述「怎么调」, 不描述「调什么」:

组件 类型定义 职责
PassPipeline tilelang/backend/pass_pipeline/pipeline.py:11 一个 name + 一个 lower(mod, target)
DeviceCodegen tilelang/backend/device_codegen.py:31 build(带编译)与 build_without_compile(只出源码)两个入口
HostCodegen / HostCodegenHook tilelang/backend/host_codegen.py:33 / :44 host 侧 codegen;Hook 让设备后端在 host codegen 前预处理 host IR
ExecutionBackendSpec tilelang/backend/execution_backend.py:18 名称、可用性检查、target 谓词、是否需要 host codegen / 设备编译
BackendModule tilelang/backend/module.py:28 把上面四类组装成一个 manifest

共享的 host codegen 只有两个,写在 tilelang/backend/host_codegen.py:55-59:

STANDARD_HOST_CODEGENS = {
    "c":    HostCodegen("c",    build=global_func_host_codegen("target.build.tilelang_c_host")),
    "llvm": HostCodegen("llvm", build=global_func_host_codegen("target.build.llvm")),
}

(c)共享的小工具。 pass_pipeline/pipeline_utils.py 里的 allow_vectorize / should_force_let_inline / should_enable_race_check / should_disable_shared_memory_reuse / LayoutVisual 是两个后端都在用的开关注入点。 例如 LayoutVisual(mod) 在 CUDA pipeline 的 :140 与 Ascend pipeline 的 :85 各出现一次。

4.5 CUDA pipeline 的 73 步

下面的表来自 code/pass-order/dump_pass_order.py 在 v0.1.15 上的实际输出: 它逐行扫 tilelang/cuda/pipeline.py,只认 mod = <pass>(...) 语句,记录 1-based 行号与 当前 if 守卫。共 73 步 = CUDAPassPipelineBodyPrologue(33 步)+ Body 中 prologue 之后的 40 步。表中「条件」列写 (无条件) 表示该 pass 一定会跑。

# 行号 pass 条件
1 69 tirx.transform.BindTarget (无条件)
2 70 tilelang.transform.MaterializeKernelLaunch (无条件)
3 74 tilelang.cuda.transform.AnnotateDeviceBoundTmaCopies (无条件)
4 77 tilelang.transform.LetInline should_force_let_inline()
5 79 tilelang.transform.AddWrapperForSingleBufStore (无条件)
6 81 tilelang.transform.LegalizeNegativeIndex (无条件)
7 84 tilelang.transform.VerifyParallelLoop should_enable_race_check()
8 86 tilelang.transform.InjectAssumes (无条件)
9 88 tilelang.transform.Simplify (无条件)
10 91 tilelang.transform.CanonicalizeLegacyReducer (无条件)
11 92 tilelang.transform.VerifyReducerEpoch (无条件)
12 98 tilelang.transform.VerifyBufferInit (无条件)
13 107 tilelang.cuda.transform.AutoWarpSpecialization allow_warp_specialized(target)
14 108 tilelang.cuda.transform.MaterializeWSSchedule allow_warp_specialized(target)
15 109 tilelang.cuda.transform.ProducerConsumerWarpSpecialized allow_warp_specialized(target)
16 114 tilelang.cuda.transform.LowerBlackwell2SM (无条件)
17 118 tilelang.transform.IfStmtBinding (无条件)
18 123 tilelang.transform.UnrollLoop (无条件)
19 125 tilelang.transform.Simplify (无条件)
20 129 tilelang.transform.PipelinePlanning (无条件)
21 130 tilelang.transform.InjectSoftwarePipeline (无条件)
22 131 tilelang.transform.Simplify (无条件)
23 134 tilelang.transform.LayoutInference (无条件)
24 138 tilelang.transform.ReducerPlanAndMaterialize (无条件)
25 142 tilelang.transform.LowerTileOp (无条件)
26 144 tilelang.transform.VerifyReducerConsumed (无条件)
27 148 tilelang.cuda.transform.LowerL2Persistent (无条件)
28 150 tilelang.transform.DecoupleTypeCast (无条件)
29 152 tilelang.transform.LegalizeVectorizedLoop (无条件)
30 154 tilelang.transform.LegalizeSafeMemoryAccess (无条件)
31 156 tilelang.transform.LowerAccessPtr (无条件)
32 162 tilelang.transform.Simplify (无条件)
33 164 tilelang.transform.HoistNonRestrictParams (无条件)
34 175 tilelang.cuda.transform.LowerSharedTmem (无条件)
35 180 tilelang.transform.PlanAndUpdateBufferAllocationLocation (无条件)
36 196 tilelang.cuda.transform.LowerSharedBarrier (无条件)
37 201 tilelang.cuda.transform.FuseMBarrierArriveExpectTx has_tma
38 203 tilelang.transform.HoistGlobalBufferAllocations (无条件)
39 204 tilelang.transform.LowerOpaqueBlock (无条件)
40 205 tilelang.transform.Simplify (无条件)
41 206 tirx.transform.NarrowDataType (无条件)
42 207 tilelang.transform.FlattenBuffer (无条件)
43 210 tilelang.transform.ConfigIndexBitwidth (无条件)
44 211 tirx.transform.Simplify (无条件)
45 212 tilelang.transform.VectorizeLoop (无条件)
46 213 tilelang.transform.StorageRewrite (无条件)
47 214 tilelang.transform.LoopUnswitching (无条件)
48 215 tilelang.transform.UnrollLoop (无条件)
49 216 s_tir.transform.RenormalizeSplitPattern (无条件)
50 217 tirx.transform.Simplify (无条件)
51 218 tirx.transform.RemoveNoOp (无条件)
52 219 s_tir.transform.HoistIfThenElse (无条件)
53 221 tirx.transform.VerifyMemory (无条件)
54 222 tirx.transform.AnnotateEntryFunc (无条件)
55 232 s_tir.transform.InferFragment (无条件)
56 233 tilelang.transform.LowerThreadAllreduce (无条件)
57 236 tilelang.cuda.transform.LowerLDGSTG (无条件)
58 237 tilelang.cuda.transform.LowerHopperIntrin (无条件)
59 239 tilelang.transform.AnnotateDeviceRegions (无条件)
60 240 tilelang.transform.SplitHostDevice (无条件)
61 244 tilelang.cuda.transform.MarkCudaSyncCalls (无条件)
62 245 tilelang.transform.AnnotateReadOnlyParams (无条件)
63 251 tilelang.transform.MergeSharedMemoryAllocations (无条件)
64 256 tilelang.cuda.transform.InjectFenceProxy (无条件)
65 258 tilelang.transform.ThreadSync("shared") (无条件)
66 259 tilelang.transform.ThreadSync("shared.dyn") (无条件)
67 266 tilelang.cuda.transform.InjectTcgen05Fence (无条件)
68 268 tilelang.transform.MergeIfStmt (无条件)
69 271 tilelang.cuda.transform.AnnotateWarpGroupRegAlloc allow_warp_specialized(pass_ctx, target)
70 273 tilelang.transform.MakePackedAPI (无条件)
71 274 tilelang.transform.Simplify (无条件)
72 275 tilelang.transform.LowerDeviceKernelLaunch (无条件)
73 279 tilelang.cuda.transform.PersistThreadblock (无条件)

三点读表提示:

  • 第 36 步 LowerSharedBarrier 是无条件的,但紧邻它的 if not have_mbarrier(target) and _module_has_shared_barrier(mod): raise ValueError(...) (:186-195)会在 pre-Hopper 上直接报错。也就是说:「pass 一定会跑」与 「这段代码一定会成功」是两回事。
  • 第 20–25 步(PipelinePlanning → InjectSoftwarePipeline → LayoutInference → LowerTileOp)是 CUDA 侧最核心的六步,第 20–22 章的正文就落在这里。
  • 第 60 步 SplitHostDevice 之后,剩下 13 步都是在已经分开的 device IR 上做同步、 存储合并与启动封装。

4.6 Ascend pipeline 的 80 步

同样方法扫 tilelang/ascend/pipeline.py,得到 80 步(全部来自 AscendPassPipelineBody)。为了可读性,下面用「分段」呈现,但每一段内部严格保持源码顺序, 行号也标出来。完整可复制版本用 python3 code/pass-order/dump_pass_order.py 生成。

段 A:绑定与规范化(1–13)

# 行号 pass 条件
1 37 tirx.transform.BindTarget (无条件)
2 45 MaterializeKernelLaunch(lower_grid_binding=True, lower_thread_binding=False, launch_dim_tags=["cthread"]) (无条件)
3 57 LetInline should_force_let_inline(pass_ctx)
4 58 AddWrapperForSingleBufStore (无条件)
5 59 LegalizeNegativeIndex (无条件)
6 61 VerifyParallelLoop should_enable_race_check(pass_ctx)
7 62 InjectAssumes (无条件)
8 68 ascend_transform.RewriteDualCopy (无条件)
9 72 ascend_transform.LegalizeSimdMerging (无条件)
10 73 tilelang.transform.Simplify (无条件)
11 74 CanonicalizeLegacyReducer (无条件)
12 75 VerifyReducerEpoch (无条件)
13 76 VerifyBufferInit (无条件)

段 B:layout 与物理存储(14–20)

# 行号 pass 条件
14 80 ascend_transform.UnrollLoopSkipVF (无条件)
15 81 tilelang.transform.Simplify (无条件)
16 83 ascend_transform.AscendLayoutInference (无条件)
17 84 ReducerPlanAndMaterialize (无条件)
18 86 ascend_transform.NormalizeAscendFractalStorage (无条件)
19 91 ascend_transform.InsertNd2Nz (无条件)
20 101 ascend_transform.AscendInsertOOBPadding (无条件)

段 C:自动调度(21–33,整段受 auto_schedule_enabled 守卫)

# 行号 pass 条件
21 104 NormalizeControlFlowForSchedule auto_schedule_enabled
22 105 NormalizeConflictHints auto_schedule_enabled
23 106 MaterializeScheduleUnits auto_schedule_enabled
24 107 AnnotateMultiBufferEligible auto_schedule_enabled
25 108 EstimateLatency auto_schedule_enabled
26 109 AutoSchedule(Z3/SMT 求解) auto_schedule_enabled
27 110 AssignCore auto_schedule_enabled
28 111 PrepareMultiBuffer auto_schedule_enabled
29 112 ResolveCore auto_schedule_enabled
30 113 InsertSync auto_schedule_enabled
31 114 MaterializeMultiBuffer auto_schedule_enabled
32 115 LowerScheduledTIR auto_schedule_enabled
33 116 RestoreWhileLoops auto_schedule_enabled

段 D:tile op 下沉(34–44)

# 行号 pass 条件
34 118 tilelang.transform.Simplify (无条件)
35 120 ascend_transform.NormalizeBufferVersion (无条件)
36 121 ascend_transform.AscendSimdVFLowerParallel (无条件)
37 122 ascend_transform.AscendLowerTileOp (无条件)
38 123 VerifyReducerConsumed (无条件)
39 125 DecoupleTypeCast (无条件)
40 126 LegalizeVectorizedLoop (无条件)
41 127 LegalizeSafeMemoryAccess (无条件)
42 128 LowerAccessPtr (无条件)
43 129 tilelang.transform.Simplify (无条件)
44 130 HoistNonRestrictParams (无条件)

段 E:扁平化与向量化(45–56)

# 行号 pass 条件
45 132 HoistGlobalBufferAllocations (无条件)
46 133 LowerOpaqueBlock (无条件)
47 134 tilelang.transform.Simplify (无条件)
48 135 ascend_transform.RewriteAscendBufferVersionLayout (无条件)
49 136 tirx.transform.NarrowDataType (无条件)
50 137 FlattenBuffer (无条件)
51 138 ConfigIndexBitwidth (无条件)
52 139 tirx.transform.Simplify (无条件)
53 140 VectorizeLoop (无条件)
54 146 ascend_transform.RewriteFp4ToFp4x2 (无条件)
55 148 LoopUnswitching (无条件)
56 149 UnrollLoop (无条件)

段 F:收尾、切分与同步(57–80)

# 行号 pass 条件
57 150 s_tir.transform.RenormalizeSplitPattern (无条件)
58 151 tirx.transform.Simplify (无条件)
59 152 ascend_transform.AscendRemoveNoOp (无条件)
60 153 s_tir.transform.HoistIfThenElse (无条件)
61 154 tirx.transform.Simplify (无条件)
62 155 ascend_transform.AscendRemoveNoOp (无条件)
63 157 tirx.transform.VerifyMemory (无条件)
64 158 tirx.transform.AnnotateEntryFunc (无条件)
65 159 s_tir.transform.InferFragment (无条件)
66 160 LowerThreadAllreduce (无条件)
67 163 ascend_transform.AscendThreadSync("global") allow_global_thread_synchronization(pass_ctx)
68 164 AnnotateDeviceRegions (无条件)
69 165 ascend_transform.MarkScalarDcacheBypass (无条件)
70 166 SplitHostDevice (无条件)
71 167 AnnotateReadOnlyParams (无条件)
72 170 ascend_transform.InferBufferAliases not auto_schedule_enabled
73 171 ascend_transform.MergeUBAllocations (无条件)
74 179 ascend_transform.RewriteFlagToBuf (无条件)
75 181 ascend_transform.AscendThreadSync("shared") (无条件)
76 182 ascend_transform.AscendThreadSync("shared.dyn") (无条件)
77 183 MergeIfStmt (无条件)
78 184 MakePackedAPI (无条件)
79 185 tilelang.transform.Simplify (无条件)
80 186 LowerDeviceKernelLaunch (无条件)

注意第 72 步:InferBufferAliases 只在自动调度关闭时跑。这是个典型的 「关闭 A 就要打开 B」的例子——调度器自己会推导 buffer 别名,所以人写逻辑时需要显式补上。

4.7 两条 pipeline 对照

统计(脚本输出):CUDA 73 步 / Ascend 80 步,两侧同名出现的 pass 有 39 个, 带条件守卫的分别是 CUDA 7 步、Ascend 17 步。阶段图见 图:fig-04-pipeline-compare。

阶段 CUDA Ascend
设备绑定 BindTarget BindTarget
网格物化 MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128) MaterializeKernelLaunch(lower_grid_binding=True, lower_thread_binding=False, launch_dim_tags=["cthread"])
后端前处理 AnnotateDeviceBoundTmaCopies、LowerBlackwell2SM、warp specialization 三连 RewriteDualCopy、LegalizeSimdMerging
流水 PipelinePlanning + InjectSoftwarePipeline(通用 pass) 由 AutoSchedule 段统一决定(含多缓冲)
布局 LayoutInference(通用) AscendLayoutInference + NormalizeAscendFractalStorage + InsertNd2Nz
边界 LegalizeSafeMemoryAccess 额外有 AscendInsertOOBPadding
tile op 下沉 LowerTileOp AscendLowerTileOp + AscendSimdVFLowerParallel
存储合并 MergeSharedMemoryAllocations MergeUBAllocations
同步 ThreadSync("shared") ×2 + InjectFenceProxy + InjectTcgen05Fence AscendThreadSync("global"/"shared"/"shared.dyn") + RewriteFlagToBuf
收尾 MakePackedAPI → LowerDeviceKernelLaunch → PersistThreadblock MakePackedAPI → LowerDeviceKernelLaunch

一句话总结:两侧共享的是一条「tile op 之后」的标准 lowering 走廊(39 个同名 pass), 真正的差异集中在三处——网格物化的参数、流水/调度由谁负责、同步与片上存储怎么合并。

4.8 codegen、host wrapper 与可调用对象

pipeline 结束时,mod 里的函数已经被 SplitHostDevice 分成了 host 与 device 两族。 engine 用 tirx.transform.Filter 把它们分开(tilelang/engine/lower.py:138-139), 判据是 calling_conv == CallingConv.DEVICE_KERNEL_LAUNCH (tilelang/engine/lower.py:26-44)。

设备侧走 context.codegen_device(..., compile_device=...)。CUDA 的 DeviceCodegen 由两个全局函数支撑(tilelang/cuda/codegen.py:16-17):

target.build.tilelang_cuda                    # 生成并编译
target.build.tilelang_cuda_without_compile    # 只生成源码

Ascend 同理(tilelang/ascend/codegen.py:5-9):

target.build.tilelang_ascend
target.build.tilelang_ascend_without_compile

宿主侧的关键 pass 是 MakePackedAPI——它的 C++ 本体在 src/transform/make_packed_api.cc,文件头注释写着 「Lower PrimFunc to use the packed function API」(:2)。 这一步把 PrimFunc 改写成符合 TVM-FFI 打包调用约定的入口:新的入口形如 (args, num_args)(:434-437),用 MakeAssertEQ 一类断言检查参数个数与合法性 (:421-475),并按 tilelang_out_idx / tilelang_callee_allocated_outputs 两个属性决定输出张量由调用方还是被调方分配(:38-42、:342-355)。

最后一步是 adapter。它不属于 target backend,而属于 execution backend:

tilelang/jit/adapter/
  tvm_ffi.py       TVMFFIKernelAdapter      # 与 tvm_ffi runtime module 对接
  nvrtc/           NVRTCKernelAdapter       # 源码 + driver 启动
  cython/          CythonKernelAdapter      # 生成 Cython wrapper
  torch/           MetalKernelAdapter       # 由框架提供 compile/launch
  cutedsl/         CuTeDSLKernelAdapter
  wrapper.py       生成 host wrapper 源码的通用实现

资料库索引 把这条契约写得很清楚: target backend 需要提供「compiled 或 source-only 的 device codegen、按需的 host codegen、 source/artifact/global-symbol/argument/launch metadata、目标相关的校验与工具链回调」, 然后由 execution backend 负责利用它们完成缓存、加载、绑定与启动。

4.9 缓存:两级

tilelang/cache/ 里的缓存有两层,用途完全不同:

层 类 键里有什么 命中后的效果
kernel 级 KernelCache(tilelang/cache/kernel_cache.py:35) PrimFunc + out_idx + target + target_host + execution_backend + pass_configs + compile_flags 直接返回一个 JITKernel,pipeline 完全不跑
二进制级 CUDABinaryCache / AscendBinaryCache 源码文本 + target kind + arch + 编译选项 直接返回编译产物 bytearray,跳过 nvcc / bisheng

派发逻辑在 tilelang/cache/__init__.py:24-35:五条通用路径 (tvm_ffi / cython / nvrtc / cutedsl / torch)加两条昇腾专用路径 (AscendTVMFFIKernelCache / AscendCythonKernelCache)。注意派发依据是 resolved backend module 名,不是 target kind(:68-69 的注释 「Dispatch on the resolved backend module rather than on the target kind」)。

CUDA pipeline 第 34 步是 LowerSharedTmem(tilelang/cuda/pipeline.py:175); 它的 C++ 本体做的事是「Convert shared.tmem buffers to plain shared + ptx init」 (src/cuda/transform/lower_shared_tmem.cc:3),而 tmem 属于需要特定架构才有的资源 (仓库根 资料库索引 的原话是「TMA, WGMMA, and TMEM features require the corresponding GPU architecture」)。架构相关的 lowering 正是二进制缓存必须把 arch 写进 key 的原因:同一份 CUDA C 在不同 sm_* 上要编出不同 cubin。 tilelang/cuda/backend.py:59-120 的 tilelang_callback_cuda_compile 把 target_arch / target_code_list / compile_format / options 全塞进 make_key。


5. 进阶与陷阱

5.1 pass 序列是带分支的程序,不是静态表

最容易犯的错误,是把 4.5 / 4.6 的表格背下来当成「TileLang 的 pass 顺序」。 它其实是在默认 pass_configs 下、针对一个普通 kernel 的顺序。至少有三类因素会改变它:

(a)pass_configs。 表中所有带条件列的 pass 都由 tilelang/backend/pass_pipeline/pipeline_utils.py 里的函数决定,而这些函数全部读同一份 PassContext.config:

# tilelang/backend/pass_pipeline/pipeline_utils.py
def allow_vectorize(pass_ctx=None) -> bool:                 # :25
    return not pass_ctx.config.get("tirx.disable_vectorize", False)

def should_force_let_inline(pass_ctx=None) -> bool:         # :46
    return bool(pass_ctx.config.get(PassConfigKey.TL_FORCE_LET_INLINE, False))

def should_enable_race_check(pass_ctx=None) -> bool:        # :58
    default_disable = not _env_data_race_check_enabled()     # 环境变量 TILELANG_ENABLE_DATA_RACE_CHECK
    disable = pass_ctx.config.get(PassConfigKey.TL_DISABLE_DATA_RACE_CHECK, default_disable)
    return not disable

(b)目标硬件。 allow_warp_specialized(tilelang/cuda/pipeline.py:27-36)要求 「是 CUDA target」且「有 TMA」;has_tma 又让 FuseMBarrierArriveExpectTx 单独成一步(表格第 37 步)。所以同一份 kernel 在 sm_80 与 sm_90 上跑的 pass 数目并不相同。

(c)kernel 自身的内容。 Ascend 的 13 步自动调度只在 TL_ENABLE_AUTO_SCHEDULE 为真时执行(tilelang/ascend/pipeline.py:22-33), 关闭时第 72 步的 InferBufferAliases 才会补位。

可复现的结论:任何「TileLang 的 pass 顺序是 X」的说法都必须附带 target + pass_configs + 是否开启的开关。这也是本书第 20 章会坚持给每条结论加条件的原因。

5.2 五个具体陷阱

  1. import tilelang.language as T 拿到的是 CUDA 方言。 tilelang/language/__init__.py:11-12 直接 from tilelang.cuda.language import *, 并在 :32 声明 __tilelang_dialect__ = "cuda"。写昇腾 kernel 要 from tilelang.ascend import language as T;混用两套 T 会拿到不同签名的 T.Kernel。
  2. 不要在 engine/lower.py 里找后端分支。 那里只有一次 context.lower(mod) (:136)。任何「按 target 分支」的逻辑都应该在对应后端的 pipeline.py 或 backend.py 里。如果你在 engine 里加了 target 分支,等于把 资料库索引 明令禁止的事情做了(「The engine must not contain a target.kind.name dispatch table for backend codegen.」)。
  3. 改完 pass 没生效,先怀疑缓存。 KernelCache 命中时 pipeline 一步都不跑; 调试编译器时应当设 TILELANG_CACHE_DIR 或直接 tilelang.disable_cache(), 否则很容易把「缓存命中」误判成「pass 没起作用」。
  4. SplitHostDevice 之后 IR 是两个模块。 在 pipeline 后半段做调试打印时, 如果只打印一个模块,会以为「代码丢了」。engine 在 tilelang/engine/lower.py:138-139 用两个 Filter 分开,调试时也要分开看。
  5. 别把「共享前端」理解成「共享 IR」。 前端(tilelang/language/)确实共享, 但已经进到 AscendLayoutInference 的 IR 与进了 LayoutInference 的 IR 已经是两套不同的抽象(前者带 fractal / NZ 信息,后者带 fragment / swizzle 信息)。 第 21 章会把这两套 layout 代数并排讲。

5.3 v0.1.15 事实 vs 仅 main / 未合入

这一节是本章的「版本边界」。判据全部可以现场复现(命令按顺序给):

cd _vendor/tilelang-v0.1.15
cat VERSION                       # 0.1.15
git describe --tags               # v0.1.15
git log -1 --oneline              # a35f8dd [Release] Bump version into 0.1.15 (#3309)
ls tilelang/jit/adapter/          # 没有 pto/
grep -rn "AutoSimtVF" tilelang src    # 无输出
grep -rn "PersistentKernel" tilelang src  # 无输出
能力 v0.1.15 里有吗 依据
tilelang/ascend/(Ascend 950 后端)、src/ascend/ 有 ls tilelang/ascend、ls src/ascend;tilelang/ascend/backend.py:52-63 的 manifest
tilelang/backend/ 多后端抽象(BackendModule / BackendContext / PassPipeline) 有 tilelang/backend/ 下 9 个 .py + 资料库索引
CUDA / CuTeDSL 两个 manifest 共用 cuda target kind 有 tilelang/cuda/backend.py:123-143、tilelang/cuda/cutedsl_backend.py:8-23
Ascend 自动调度(EstimateLatency → AutoSchedule → InsertSync → MaterializeMultiBuffer) 有 tilelang/ascend/pipeline.py:104-116
tilelang/jit/adapter/pto/(PTO 执行路径) 没有;在 main(≥ 994b44e) ls _vendor/tilelang-v0.1.15/tilelang/jit/adapter/ 无 pto;corpus 的 main 快照 994b44e 有
AutoSimtVF pass(PR #3320) 没有,也未进写这份材料时的 main grep -rn AutoSimtVF 在两个 checkout 都无输出
T.PersistentKernel + AutoPersistent(PR #3325) 没有,也未进写这份材料时的 main grep -rn PersistentKernel 无输出

两点补充:

  • PR 编号只是线索,判据永远是「在哪个 checkout 里 grep 得到什么」。 v0.1.15 对应的 PR 是 #3309(bump version),而 corpus 的 main 快照停在 #3310。
  • 昇腾还有另一条完整路线:独立仓库 tile-ai/tilelang-ascend (本章锁定 _vendor/tilelang-ascend-v0.1.2.000/,其 VERSION 文件写 0.1.4, 以 tag 为准)。它不是 _vendor/tilelang-v0.1.15/tilelang/ascend/ 的同义词, 两者的关系在第 25 章展开。

6. 本章小结

把本章压缩成一页:

① 结构:16 个顶层模块,可分为「前端 / 编译入口 / 后端切片 / C++ 本体 / 运行时 / 工具」六类。
        语言表面在 tilelang/language/(默认 re-export CUDA 方言)。

② 一次编译:decorator → JITImpl → get_tir → cache → BackendContext(一次)
            → PreLowerSemanticCheck → context.lower(mod)  ← 唯一后端分岔点
            → SplitHostDevice → device/host codegen → CompiledArtifact → adapter → 可调用对象

③ 顺序在哪:tilelang/<backend>/pipeline.py(Python,可读可改)
   行为在哪:src/transform/*.cc + src/<backend>/*

④ 后端抽象:BackendModule(注册清单)+ BackendContext(一次解析、冻结)
            + PassPipeline / DeviceCodegen / HostCodegen(+Hook) / ExecutionBackendSpec
   两条硬规则:同 kind 多 manifest 必须给 supports_target 谓词;
              engine 里不许按 target.kind.name 分派。

⑤ 实测数字(脚本可复现):CUDA 73 步 / Ascend 80 步;同名 pass 39 个;
   条件执行 CUDA 7 步 / Ascend 17 步。

⑥ 版本边界:v0.1.15 有 ascend 后端与多后端抽象;没有 pto adapter、
           没有 AutoSimtVF、没有 T.PersistentKernel。判断一律靠 grep,不靠 PR 号。

如果只能记住一句话:后端差异被压缩在 context.lower(mod) 这一行里, 顺序写在 Python 的 pipeline 里,行为写在 C++ 的 src/transform 里。

延伸阅读

本章所有引用的完整清单与核验状态见 本章引用。

习题

12 题 · 答案在提交后展开

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

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

本章 2.1 节给出了一次编译的五个阶段(前端 → 编译期 → 代码生成 → 宿主封装 → 运行时) 以及每个阶段的入口函数。

  1. 写出这五个阶段的名称,以及第 ② 阶段的入口函数全名。
  2. 为什么说「后端差异被压缩进了第 ② 阶段的一个函数调用里」?请指出那个调用的 文件与行号,并说明其余四个阶段为什么是共享的。
  3. 「前端共享」是否意味着「IR 也共享」?请举出本章给出的一个反例。

E2概念难度 ★无需硬件概念题 不需要硬件
  1. 用自己的话区分 target backend 与 execution backend,各写出它的职责清单。
  2. 从 v0.1.15 中各举两个例子,并给出源码位置。
  3. 判断下列说法正误,并说明理由:
    • 「选择 nvrtc 执行后端,意味着走的是另一条 CUDA pass pipeline。」
    • 「CuTeDSL 与 CUDA 是两个不同的 target backend,因此它们的 IR lowering 也不同。」

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

阅读 tilelang/backend/module.py:42-92(BackendModule.__post_init__ 的校验) 与 :238-272(register_backend)。

  1. 列出 __post_init__ 至少四条硬校验,并说明每条想防住什么错误。
  2. 为什么「同一个 target kind 上允许多个 manifest」时,必须要求这些 manifest 都提供 supports_target 谓词?如果不加这条约束,_resolve_backend_module(:300-307) 会发生什么?
  3. register_backend 在注册 FFI callback 失败时会做回滚(:257-270)。 如果去掉回滚,会发生什么后果?

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

下面是 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:120-144 的节选(注释已删):

mod = tilelang.transform.IfStmtBinding()(mod)
mod = tilelang.transform.UnrollLoop()(mod)
mod = tilelang.transform.Simplify()(mod)
mod = tilelang.transform.PipelinePlanning()(mod)
mod = tilelang.transform.InjectSoftwarePipeline()(mod)
mod = tilelang.transform.Simplify()(mod)
mod = tilelang.transform.LayoutInference()(mod)
mod = tilelang.transform.ReducerPlanAndMaterialize()(mod)
LayoutVisual(mod)
mod = tilelang.transform.LowerTileOp()(mod)
  1. 结合源码注释回答:为什么 PipelinePlanning / InjectSoftwarePipeline 必须排在 LayoutInference 之前?如果交换顺序,layout 推导会看到什么样的 IR?
  2. LayoutVisual(mod) 为什么不需要重新赋值 mod?(提示:看 tilelang/backend/pass_pipeline/pipeline_utils.py:105-109。)
  3. LowerTileOp 之后,T.gemm / T.copy 还存在吗?这一步对调试有什么意义?

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

阅读 _vendor/tilelang-v0.1.15/tilelang/ascend/pipeline.py:103-118 与 :169-170:

if auto_schedule_enabled:
    mod = ascend_transform.NormalizeControlFlowForSchedule()(mod)
    ...
    mod = ascend_transform.AutoSchedule()(mod)
    ...
    mod = ascend_transform.RestoreWhileLoops()(mod)

mod = tilelang.transform.Simplify()(mod)
...
if not auto_schedule_enabled:
    mod = ascend_transform.InferBufferAliases()(mod)
  1. 这 13 步自动调度分别在解决什么问题?请至少说出「任务化」「资源建模」「求解」「落地」 四个词各对应哪几步。
  2. AutoSchedule 之前的 EstimateLatency 和 AnnotateMultiBufferEligible 为什么必须 排在求解之前?
  3. 第 72 步(:169-170)为什么写成 if not auto_schedule_enabled:?这说明 「关掉自动调度」对编译器意味着什么?

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

_vendor/tilelang-v0.1.15/tilelang/language/__init__.py 的开头是:

from tilelang.cuda.language import *  # noqa: F401,F403
from tilelang.cuda.language import __all__ as __all__
from tilelang.cuda.language import (
    Kernel, Parallel, Unroll, atomic_add, copy, gemm, ...
)
__tilelang_dialect__ = "cuda"
  1. 这三段代码各自解决什么问题?(提示:__all__ 与静态类型检查器。)
  2. 如果一份昇腾 kernel 文件里写了 import tilelang.language as T, 会发生什么?请给出正确写法。
  3. 对比 tilelang/language/kernel.py:431 与 tilelang/cuda/language/kernel.py:13 两个 Kernel 的签名,说出它们的关系。ClusterKernel 为什么放在 CUDA 那一侧?

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

运行本章的离线脚本,回答下面的问题(全部答案都能从输出里直接读出来):

cd chapters/04-architecture-and-compilation-flow/code
python3 pass-order/dump_pass_order.py
  1. CUDA pipeline 一共多少步?其中 CUDAPassPipelineBodyPrologue 贡献多少步?
  2. Ascend pipeline 一共多少步?
  3. 两侧同名的 pass 有多少个?至少列出 10 个你认识的。
  4. 带条件守卫的 pass,CUDA 有几个、Ascend 有几个?把 CUDA 的 7 个全部列出来, 并写出各自的条件表达式。

E8动手难度 ★★无需硬件动手题 不需要硬件
  1. 运行 python3 repo-map/inventory.py,把 tilelang_top_level_dirs / tilelang_python_files / src_transform_cc_files / examples_subdirs / examples_top_level_files / docs_markdown_files 六个数字抄下来。
  2. 版本锁定 写「examples/ 有 57 个目录」,脚本给出的是 54 个目录 + 3 个文件。 请解释这两个口径的差异,并说明章为什么采用脚本口径。
  3. 运行 python3 compile-path-check/check_compile_path.py。如果有跳失败, 用 sed -n '<行号>p' <文件> 现场核对并给出你的判断(是章节写错了,还是 vendor 目录版本不对)。把脚本改成 1 行,让它打印 Hop 的数量(提示:len(HOPS))。

E9动手难度 ★★需要加速器动手题 需要硬件

在一台有 NVIDIA GPU、且已安装 tilelang==0.1.15 的机器上:

  1. 运行 python3 code/quickstart-gemm/quickstart_gemm.py --out /tmp/tl-debug。
  2. 打开 /tmp/tl-debug/tilelang_jit_kernel_*.c,找出 __global__、__shared__、cp.async 或 TMA 相关的行各一处,并说明它们分别对应 本章表格里的哪个 pass 产物。
  3. 打开 /tmp/tl-debug/tilelang_jit_program_*.py,与原始 kernel 对比: T.gemm / T.copy / T.Pipelined 还在吗?这说明了什么?
  4. 把 --block-k 从 32 改成 64 再跑一次,观察生成的 __shared__ 大小与 pipeline 步数的变化。

没有 GPU 时:请只完成第 3 题的静态部分——在 _vendor/tilelang-v0.1.15/ 里找出 LowerTileOp 前后 IR 里 tile op 是否还存在(提示:grep -rn "tile_op" src/op/), 并明确写出你没有验证的部分。


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

对比两侧的 MaterializeKernelLaunch 调用:

# tilelang/cuda/pipeline.py:70
MaterializeKernelLaunch(lower_thread_binding=True, default_threads=128)

# tilelang/ascend/pipeline.py:45-51
MaterializeKernelLaunch(lower_grid_binding=True,
                        lower_thread_binding=False,
                        default_threads=None,
                        unsupported_annotations=["cluster_dims"],
                        launch_dim_tags=["cthread"])
  1. 这两组参数分别会产生什么 IR 结构(blockIdx / threadIdx / cthread 各在哪里出现)?
  2. 这个差异对用户能写什么代码有什么直接影响?请用 T.SimtVF 解释昇腾侧 「线程域只存在于向量区域内」这句话。
  3. unsupported_annotations=["cluster_dims"] 的作用是什么?如果用户对昇腾目标写 T.Kernel(..., cluster_dims=2) 会发生什么?

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

有人告诉你:「TileLang 现在已经支持 T.PersistentKernel,也有 PTO 执行后端了。」 你手头有 _vendor/tilelang-v0.1.15/ 与 corpus 的 main 快照 tile-ai/tilelang。

  1. 写出至少三条核验命令,用来判断这两个说法在哪个 checkout 里成立。
  2. 分别给出结论:T.PersistentKernel 与 PTO 到底在哪个版本里?
  3. 为什么本书要求「讨论 main 上的能力时必须显式标注,并且放在第 30 章」?
  4. 判断这条说法属于本章 5.3 节表格的哪一行,并说明你自己的核验路径。

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

你要给 TileLang 增加一个虚构的新后端 foo(假设它的 target kind 就叫 foo, 代码生成走一个已有的 native 函数 target.build.tilelang_foo)。

  1. 按 资料库索引 的「Backend Package Layout」, 写出你要新建的文件清单,并说明每个文件的职责。
  2. 在 BackendModule 里你必须提供哪些字段?哪些字段是可选的?给出一个最小 manifest 草稿(Python 代码,不用真的可运行,但要字段名正确)。
  3. 如果你的 foo 想复用 CUDA 的 pipeline,应该怎么写?这个做法在 v0.1.15 里有没有 先例?给出先例的文件与行号。
  4. 按 :374-392 的 Ownership Rules 逐条检查你的方案,指出最容易违规的一条并说明 怎么避免。

章节自测

8 题 · 提交后本地评分

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

  1. 1. 按 §2.1 的五阶段划分,「后端分岔」被压缩在哪一行代码里?

  2. 2. 想改某个后端 pass 的执行顺序,应该改哪里?

  3. 3. 写 import tilelang.language as T 之后,T 里到底有什么?

  4. 4. 关于 target backend 与 execution backend,下面哪一句正确?

  5. 5. 改完一个 pass、重新编译却发现结果没变,本章建议先怀疑什么?

  6. 6. 按 §4.7 的实测统计,CUDA 与 Ascend 两条 pipeline 的步数与同名 pass 数是多少?

  7. 7. 下面哪一项在锁定的 v0.1.15 里**不存在**?

  8. 8. 关于「在 engine 里按 target 分支」这件事,本章的结论是什么?

本章代码

9 个文件

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

浏览本章全部代码 →

本章引用

源码链接指向锁定 tag

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

展开引用清单

第 04 章 参考资料

本章所有结论都基于下表中的本地资料或锁定版本源码。版本以 版本锁定 为准:TileLang v0.1.15 (commit a35f8ddf45eba16c21211ec8822d56ce5363036f)、TileLang-Ascend TileLang-ascend-v0.1.2.000-release(commit 89a9364e47a341fffdd65ed94786b1d4f9b6a78c)。 文中不使用任何模糊的版本措辞;凡涉及版本,一律写出具体 tag 或 commit。

写作环境:macOS(Apple Silicon)、Python 3.11;没有 NVIDIA GPU,没有昇腾 NPU, 也没有安装 tilelang / torch。因此:

  • 标题为「已核验」的源码引用,指的是在锁定版本工作树上用 grep -n / sed -n / cat -n 定位并逐行阅读,不是运行验证;
  • 本章唯一涉及真实硬件的示例是 code/quickstart-gemm/,其验证级别为 仅静态核对(python3 -m py_compile 通过 + API 名与签名逐个比对)。

一、本章自带的离线工具(可复现全部数字)

id 位置 用途 状态
T1 code/pass-order/dump_pass_order.py 从 tilelang/cuda/pipeline.py 与 tilelang/ascend/pipeline.py 逐行提取真实 pass 顺序与条件守卫;生成正文 4.5 / 4.6 / 4.7 节的数字 在本机运行过(2026-10-01):输出 CUDA 73 步、Ascend 80 步、同名 39 个、条件 CUDA 7 / Ascend 17
T2 code/repo-map/inventory.py 统计模块清单与规模,生成正文 2.3 节表格与图 1 的数字 在本机运行过(2026-10-01)
T3 code/compile-path-check/check_compile_path.py 逐跳校验图 2 与 4.1–4.4 节声称的 30 个「文件:行号」 在本机运行过(2026-10-01):30/30 verified,退出码 0
T4 code/quickstart-gemm/quickstart_gemm.py 用官方 GEMM+ReLU 示例走一遍完整编译流程(kernel 主体取自 examples/quickstart.py:8-48) 仅静态核对:py_compile 通过;本机无 GPU、无 torch/tilelang,未运行
F1 图:fig-01-repo-map 仓库地图(Mermaid + ASCII)与数量口径 自绘;数据由 T2 生成
F2 图:fig-02-compile-flow 一次编译的全流程图 自绘;每跳行号由 T3 校验
F3 图:fig-03-backend-abstraction 多后端抽象对象图 自绘
F4 图:fig-04-pipeline-compare 两条 pipeline 的阶段对照 自绘;阶段划分由 T1 输出归纳

所有图都是本章作者自绘的 Mermaid / ASCII 文本,没有从外部复制图片,因此不需要 教程首页。


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

表中「行号」均已用 sed -n '<行号>p' <文件> 或 cat -n 现场核对。 「已核验(2026-10-01)」= 定位到该行并阅读了上下文,不代表运行过。

2.1 仓库结构与模块职责

id 引用位置 用途 核验状态
S1 _vendor/tilelang-v0.1.15/VERSION 版本号 0.1.15;git describe --tags → v0.1.15;git log -1 → a35f8dd 已核验(2026-10-01)
S2 _vendor/tilelang-v0.1.15/tilelang/__init__.py:211-230 import 时加载 analysis/transform/language/engine/tools/autotuner 与五个后端包(cpu/ascend/cuda/rocm/metal/webgpu) 已核验(2026-10-01)
S3 _vendor/tilelang-v0.1.15/tilelang/language/__init__.py:1-32 tilelang.language 复合 CUDA 方言;__tilelang_dialect__ = "cuda"(:32) 已核验(2026-10-01)
S4 _vendor/tilelang-v0.1.15/tilelang/language/kernel.py:431-455 target-neutral T.Kernel(「a grid of tile programs」) 已核验(2026-10-01)
S5 _vendor/tilelang-v0.1.15/tilelang/cuda/language/kernel.py:13-55 CUDA 方言 T.Kernel(..., threads, prelude, cluster_dims) 已核验(2026-10-01)
S6 _vendor/tilelang-v0.1.15/tilelang/engine/lower.py:26-44, :65-106, :109-141, :144-213, :216-244 engine 的四个入口与 host/device 判定 已核验(2026-10-01)
S7 _vendor/tilelang-v0.1.15/tilelang/engine/semantic_check.py:9-31 PreLowerSemanticCheck 与它的开关、四个检查器 已核验(2026-10-01)
S8 _vendor/tilelang-v0.1.15/tilelang/engine/param.py:153-166 CompiledArtifact 字段定义 已核验(2026-10-01)
S9 _vendor/tilelang-v0.1.15/tilelang/transform/__init__.py:19-471 通用 pass 的 Python 包装(PipelinePlanning:19、LayoutInference:30、LowerTileOp:41、LowerDeviceKernelLaunch:408、MakePackedAPI:190、MaterializeKernelLaunch:204、MergeSharedMemoryAllocations:345、SplitHostDevice:285、ThreadSync:108、UnrollLoop:471 等) 已核验(2026-10-01)
S10 _vendor/tilelang-v0.1.15/src/transform/ pass 的 C++ 本体;42 个 .cc、共 66 个文件、含 layout_inference/ 子目录 已核验(2026-10-01):ls src/transform/*.cc | wc -l → 42
S11 _vendor/tilelang-v0.1.15/tilelang/layout/__init__.py:1-36 Layout / Fragment / PartialFragment / swizzle 家族 / 昇腾 NZ 家族 已核验(2026-10-01)
S12 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/__init__.py:1-6 五个 adapter 类 已核验(2026-10-01)
S13 _vendor/tilelang-v0.1.15/tilelang/cache/__init__.py:24-35, :38-72, :75-98 七条缓存派发路径(5 通用 + 2 昇腾);按 module 名而非 target kind 派发(:68-69) 已核验(2026-10-01)
S14 _vendor/tilelang-v0.1.15/tilelang/cache/kernel_cache.py:35-58 KernelCache 的缓存文件布局(device_kernel.cu / host_kernel.cu / kernel_lib.so / params.json) 已核验(2026-10-01)
S15 _vendor/tilelang-v0.1.15/tilelang/autotuner/__init__.py:1-8 autotune / AutoTuner / set_autotune_inputs 等导出 已核验(2026-10-01)
S16 _vendor/tilelang-v0.1.15/tilelang/carver/README.md:1-10 Carver 的定位:tile 配置推荐,可对接 TVM / Triton / TileLang 已核验(2026-10-01)
S17 _vendor/tilelang-v0.1.15/tilelang/tools/__init__.py:1-20 工具包导出与 lower_trace 的惰性加载 已核验(2026-10-01)
S18 _vendor/tilelang-v0.1.15/examples/quickstart.py:8-48, :59, :79, :82-87 官方 GEMM+ReLU 示例、compile、get_kernel_source、get_profiler 已核验(2026-10-01);未运行
S19 _vendor/tilelang-v0.1.15/tilelang/ascend/README.md:1-60 Ascend 950 后端的定位、安装前提(CANN / bisheng / torch_npu)、Quick Start 已核验(2026-10-01);未在 NPU 上运行
S20 _vendor/tilelang-v0.1.15/tilelang/ascend/target.py:57-59 注册 ascend / asc 的 detector 与 normalizer 已核验(2026-10-01)

2.2 多后端抽象

id 引用位置 用途 核验状态
S21 _vendor/tilelang-v0.1.15/tilelang/backend/README.md:14-27, :56-78, :196-220, :240-242, :244-282, :284-293, :325-345, :374-392 Design Model、术语(target backend vs execution backend)、Pass Pipeline、codegen 分派禁令、execution backend 契约、已注册后端表、包布局、Ownership Rules 已核验(2026-10-01)
S22 _vendor/tilelang-v0.1.15/tilelang/backend/pass_pipeline/pipeline.py:11-39 PassPipeline 的全部实现(含 instrumentation 与 enrich_error) 已核验(2026-10-01)
S23 _vendor/tilelang-v0.1.15/tilelang/backend/pass_pipeline/pipeline_utils.py:12-109 allow_vectorize / should_force_let_inline / should_enable_race_check / should_disable_shared_memory_reuse / LayoutVisual 已核验(2026-10-01)
S24 _vendor/tilelang-v0.1.15/tilelang/backend/module.py:28-99, :188-231, :234-272, :291-307, :310-335 BackendModule 校验、BackendContext、注册表、按 target kind 解析、上下文工厂 已核验(2026-10-01)
S25 _vendor/tilelang-v0.1.15/tilelang/backend/device_codegen.py:14-44 DeviceCodegen 的 build / build_without_compile 两个入口 已核验(2026-10-01)
S26 _vendor/tilelang-v0.1.15/tilelang/backend/host_codegen.py:33-59 HostCodegen / HostCodegenHook / STANDARD_HOST_CODEGENS(只有 c 与 llvm) 已核验(2026-10-01)
S27 _vendor/tilelang-v0.1.15/tilelang/backend/execution_backend.py:14-32 ExecutionBackendSpec 字段(含 supports_callee_allocated_outputs) 已核验(2026-10-01)
S28 _vendor/tilelang-v0.1.15/tilelang/backend/target.py:28-134 target detector / normalizer 注册(:32、:45)与 determine_target(:122) 已核验(2026-10-01)
S29 _vendor/tilelang-v0.1.15/tilelang/cuda/backend.py:59-120, :123-143 CUDA manifest 与 tilelang_callback_cuda_compile(把 arch / code / options 塞进二进制缓存 key) 已核验(2026-10-01)
S30 _vendor/tilelang-v0.1.15/tilelang/cuda/cutedsl_backend.py:8-23 CuTeDSL 单独 manifest 却显式复用 CUDA pipeline 已核验(2026-10-01)
S31 _vendor/tilelang-v0.1.15/tilelang/cuda/codegen.py:8-19 is_plain_cuda_target / is_cutedsl_target 与两个 device codegen 入口 已核验(2026-10-01)
S32 _vendor/tilelang-v0.1.15/tilelang/cuda/execution_backend.py:24-37 CUDA 的 tvm_ffi → nvrtc → cython 优先级;CuTeDSL 单独一条 已核验(2026-10-01)
S33 _vendor/tilelang-v0.1.15/tilelang/ascend/backend.py:15-63 Ascend manifest 与 tilelang_callback_ascend_compile(bisheng 编译 + 二进制缓存) 已核验(2026-10-01)
S34 _vendor/tilelang-v0.1.15/tilelang/ascend/codegen.py:5-9 Ascend 的两个 native codegen 入口 已核验(2026-10-01)
S35 _vendor/tilelang-v0.1.15/tilelang/ascend/execution_backend.py:5-21 Ascend 的 tvm_ffi → cython 与 supports_callee_allocated_outputs=True 已核验(2026-10-01)

2.3 两条 pipeline(正文表格的一手来源)

id 引用位置 用途 核验状态
S36 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:68-165 CUDAPassPipelineBodyPrologue(33 步)、warp specialization 门控、mbarrier 前置检查(:186-195 的报错在 Body 中) 已核验(2026-10-01);顺序由 T1 提取
S37 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:168-281 CUDAPassPipelineBody(调 Prologue + 40 步) 已核验(2026-10-01);顺序由 T1 提取
S38 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:284 CUDA_PIPELINE = PassPipeline("cuda", CUDAPassPipelineBody) 已核验(2026-10-01)
S39 _vendor/tilelang-v0.1.15/tilelang/cuda/pipeline.py:27-46 allow_warp_specialized 与 module_has_tma 两个门控函数 已核验(2026-10-01)
S40 _vendor/tilelang-v0.1.15/tilelang/ascend/pipeline.py:22-33, :36-51 allow_autoschedule 与昇腾版 MaterializeKernelLaunch 参数 已核验(2026-10-01)
S41 _vendor/tilelang-v0.1.15/tilelang/ascend/pipeline.py:52-187 AscendPassPipelineBody(80 步,含 13 步自动调度段与 not auto_schedule_enabled 分支) 已核验(2026-10-01);顺序由 T1 提取
S42 _vendor/tilelang-v0.1.15/tilelang/ascend/pipeline.py:190 ascend_pipeline = PassPipeline("ascend", AscendPassPipelineBody) 已核验(2026-10-01)
S43 _vendor/tilelang-v0.1.15/tilelang/cuda/transform/__init__.py:1-196 16 个 CUDA 专属 pass 的 Python 前端 已核验(2026-10-01)
S44 _vendor/tilelang-v0.1.15/tilelang/ascend/transform/__init__.py:1-256 31 个昇腾专属 pass 的 Python 前端(含 z3_scheduler.py 同目录) 已核验(2026-10-01)

2.4 JIT、adapter 与 host wrapper

id 引用位置 用途 核验状态
S45 _vendor/tilelang-v0.1.15/tilelang/jit/__init__.py:94-173, :269-415, :417-497, :519-564, :588-652 compile()、JITImpl、get_tir / _infer_jit_mode、JITImpl.compile、__call__、jit() 装饰器 已核验(2026-10-01)
S46 _vendor/tilelang-v0.1.15/tilelang/jit/__init__.py:283-313 lazy / eager 两种模式的官方说明 已核验(2026-10-01)
S47 _vendor/tilelang-v0.1.15/tilelang/jit/kernel.py:41-149, :206-222, :224-286, :288-322, :324-424 JITKernel 构造、__call__、_compile_and_create_adapter、_compile_artifact(含 TL_ENABLE_DUMP_IR 分支)、adapter 选择 已核验(2026-10-01)
S48 _vendor/tilelang-v0.1.15/tilelang/jit/adapter/wrapper.py:1-70 host wrapper 源码模板(含 shared memory 属性设置与错误缓冲) 已核验(2026-10-01)
S49 _vendor/tilelang-v0.1.15/src/transform/make_packed_api.cc:1-42 MakePackedAPI 的 C++ 本体与 tilelang_callee_allocated_outputs 等属性名 已核验(2026-10-01)
S50 _vendor/tilelang-v0.1.15/src/transform/lower_device_kernel_launch.cc:26-31 LaunchGridOnly(target):grid-only 目标不把线程域当 launch 维 已核验(2026-10-01)
S51 _vendor/tilelang-v0.1.15/tilelang/engine/param.py:26-68 KernelParam.from_buffer / from_var:参数元数据的来源 已核验(2026-10-01)
S52 _vendor/tilelang-v0.1.15/tilelang/language/eager/builder.py:1760-1800 eager 前端的 prim_func(内部 mutate(func) 改写 AST) 已核验(2026-10-01)
S53 _vendor/tilelang-v0.1.15/tilelang/language/parser/entry.py:37 parser 前端的 prim_func 已核验(2026-10-01)
S58 _vendor/tilelang-v0.1.15/tilelang/env.py:368 TILELANG_CACHE_DIR(默认 ~/.tilelang/cache);正文 5.2 节陷阱 3 引用 已核验(2026-10-01)
S59 _vendor/tilelang-v0.1.15/tilelang/env.py:435-445 env.is_cache_enabled / enable_cache / disable_cache(经 tilelang/__init__.py:161-167 导出为 tilelang.disable_cache) 已核验(2026-10-01)
S60 _vendor/tilelang-v0.1.15/tilelang/jit/kernel.py:532-543 JITKernel.get_kernel_source(正文 3.3 与 E9 引用) 已核验(2026-10-01)
S61 _vendor/tilelang-v0.1.15/tilelang/ascend/transform/__init__.py:9-11, :240 昇腾侧 pass 的正确名字是 InsertNd2Nz(不是 InsertNd);正文 4.6 段 B 与 R10 的口径说明引用 已核验(2026-10-01)
S62 _vendor/tilelang-v0.1.15/src/transform/make_packed_api.cc:38-42, :342-355, :421-475 打包入口 (args, num_args)、tilelang_out_idx / tilelang_callee_allocated_outputs 两个属性、参数断言;正文 4.8 节引用 已核验(2026-10-01)
S63 _vendor/tilelang-v0.1.15/src/cuda/transform/lower_shared_tmem.cc:3 LowerSharedTmem 的职责(shared.tmem → plain shared + PTX init);正文 4.9 节引用 已核验(2026-10-01)
S64 _vendor/tilelang-v0.1.15/README.md:113-135 官方后端支持表:CUDA 为 Primary、Ascend 950 为 Supported、「TMA, WGMMA, and TMEM features require the corresponding GPU architecture」(:123);正文 4.9 节与 references 4.3 节引用 已核验(2026-10-01)

2.5 版本边界(v0.1.15 vs main)

id 引用位置 / 命令 用途 核验状态
S54 ls _vendor/tilelang-v0.1.15/tilelang/jit/adapter/ v0.1.15 没有 pto/ 目录 已核验(2026-10-01):输出为 base.py cutedsl cython kernel_cache.py libgen.py nvrtc torch tvm_ffi.py utils.py wrapper.py
S55 grep -rn "AutoSimtVF" _vendor/tilelang-v0.1.15/tilelang _vendor/tilelang-v0.1.15/src v0.1.15 没有 AutoSimtVF 已核验(2026-10-01):无输出
S56 grep -rn "PersistentKernel|AutoPersistent" _vendor/tilelang-v0.1.15/tilelang _vendor/tilelang-v0.1.15/src v0.1.15 没有 T.PersistentKernel / AutoPersistent 已核验(2026-10-01):无输出
S57 _vendor/tilelang-v0.1.15/.gitmodules 依赖 3rdparty/tvm(fork)与 3rdparty/cutlass 已核验(2026-10-01)

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

抓取日期:2026-09-30(Asia/Shanghai),见 corpus 的 资料库索引。

id 本地路径 上游链接 用途 核验状态
R1 TileLang 官方文档 · 资料清单 https://tilelang.com/get_started/overview.html 官方给出的六步 Compilation Flow(Tile Program → Tile Program with Tile Library → … → Hardware-Specific Executable/Runtime) 已核验(2026-10-01):本地为站点源文件 _sources;链接可达
R2 TileLang 官方文档 · 资料清单 https://tilelang.com/get_started/targets.html 常见 target 列表与 auto 探测顺序(CUDA → HIP → Metal) 已核验(2026-10-01)
R3 TileLang 官方文档 · 资料清单 https://tilelang.com/get_started/targets.html target 的四种输入形式(字符串 / dict / Target / auto) 已核验(2026-10-01)
R4 tile-ai/PROVENANCE.md https://github.com/tile-ai/tilelang main 快照的 commit 是 994b44eca1a8(2026-09-30),与 v0.1.15(a35f8dd)不是同一个提交;本表用于版本对照,不作为 v0.1.15 的证据 已核验(2026-10-01):git log -1 → 994b44e [Ascend] Add PTO backend for Ascend950 (#3310)
R5 [Public Release 9/30] Introduce Ascend 950 backend https://github.com/tile-ai/tilelang/pull/3308 「[Public Release 9/30] Introduce Ascend 950 backend」——昇腾后端进入主线的那个 PR 已核验(2026-10-01):本地为抓取的 PR 页面正文;PR 标题仅作线索,判据以 S54–S56 的 grep 为准
R6 [Ascend] Add PTO backend for Ascend950 https://github.com/tile-ai/tilelang/pull/3310 「[Ascend] Add PTO backend for Ascend950」——正文 5.3 节用它解释「main 有、v0.1.15 没有」的 PTO 路径 已核验(2026-10-01):同上
R7 feat(ascend): Add AutoSimtVF pass and register it i… https://github.com/tile-ai/tilelang/pull/3320 AutoSimtVF pass(未合入) 已核验(2026-10-01):本地文件存在;未合入状态以 S55 的 grep 为准
R8 [Ascend] Add T.PersistentKernel and AutoPersistent … https://github.com/tile-ai/tilelang/pull/3325 T.PersistentKernel + AutoPersistent(未合入) 已核验(2026-10-01):同上;判据见 S56
R9 [RoadMap] Multi-Backend Refactoring/Decoupling https://github.com/tile-ai/tilelang/issues/2115 「[RoadMap] Multi-Backend Refactoring/Decoupling」——本章 4.4 节所讲抽象的设计动机 已核验(2026-10-01):本地为抓取的 issue 正文;本章只引用其结构性描述
R10 TileLang 编译期内部地图(编者笔记) 第 7–35、94–118 行 (编者综合稿,非上游) 与本章 4.1–4.6 节互为对照:同一份「编译参与者」与「昇腾调度段」的另一版叙述 部分核验:这是 corpus 编者的综合文档,属于二手材料;本章只把它当对照,所有行号仍以 _vendor/ 为准。另注:该文档正文所依据的是 corpus 的 main 快照(994b44e),其中 InsertNd 的表述与 v0.1.15 里实际存在的 InsertNd2Nz 略有出入,本章采用 InsertNd2Nz
R11 昇腾路线:现状、差异与入手点(编者笔记) 第 1–14、48–65、97–111 行 (编者综合稿,非上游) 昇腾两条路线(AscendC/PTO 与 AscendNPU IR)的定位,以及未合入前沿清单 部分核验:同样属于编者综合材料;本章 5.3 节的结论已用 S54–S56 独立复核

四、口径与待补

4.1 已登记的口径差异

项 版本锁定 的说法 本章实测 处理方式
examples/ 规模 「有 57 个目录」 54 个目录 + 3 个顶层文件(conftest.py / pytest.ini / quickstart.py),共 57 项 本章采用实测口径,并在正文 2.3 节与图 1 中说明;建议后续版本把 版本锁定 的措辞改为「57 项(54 目录 + 3 文件)」

4.2 尚需在真实硬件上验证的事项(本章不宣称)

本章没有任何运行期结论,但下面几条只有在真实设备上才能闭环,留给第 12、20–25 章:

  1. code/quickstart-gemm/ 实际编译出的 CUDA C 形态(是否走 TMA / cp.async)、 __shared__ 大小与 MergeSharedMemoryAllocations 的关系。
  2. Ascend 侧 13 步自动调度在真实 NPU 上解出的多缓冲份数与同步指令形态。
  3. unsupported_annotations=["cluster_dims"] 在昇腾目标上的具体报错信息(本章只说明该 注解不会参与物化)。

4.3 待补资料

缺什么 为什么需要 建议来源
TileLang 官方对「backend 抽象」的设计文档(RFC 级别) 本章 4.4 节目前只能引用 资料库索引 与 issue #2115;缺少一份跨版本的正式设计说明,难判断哪些字段是稳定契约 tile-ai/tilelang 的 docs/developer_guide/ 或 RFC 目录
v0.1.15 官方 release note 的完整文本 本章用 git log -1 确认了 tag 的 commit,但缺少 release note 来核对「本版新增/移除」的官方表述 https://github.com/tile-ai/tilelang/releases/tag/v0.1.15
昇腾 950 后端在 v0.1.15 中的官方支持等级说明 _vendor/tilelang-v0.1.15/README.md:113-135 的表格标注 Ascend 950 为 Supported,但缺少与 CI 覆盖范围对应的说明 tile-ai/tilelang 的 CI 配置与 资料库索引 更新

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