跳转至

编译器入门

本页面向编译器开发者,前置内容是 Python 编程模型。 运行程序或定位执行问题时,先阅读执行与排错。

为什么需要内部表示

Python 程序描述结果;编译器还需要决定如何访问边、划分工作和搬运数据。 中间表示(IR)把这些决策记录为可供后续变换 检查的结构。IR 是编译器的内部数据,不是额外的用户 API。

以邻居加权求和为例:乘法与求和定义了计算含义;按 CSR 行遍历,或将工作 分配给 GPU block,则是在决定执行方式。不同 IR 层把这些问题分开表达。

层次 真实操作名举例 回答的问题
Domain IR gf.relation、gf.apply、gf.reducer 程序描述哪张图、什么计算?
Iter IR gf_iter.traverse 按什么顺序访问关系?
Kernel IR gf_kernel.launch 如何安排计算与局部资源?
Task / Storage IR gf_task.launch、gf_storage.transfer 任务依赖哪些数据或事件?
Tensor IR gf_tensor.mul、gf_tensor.reduce_sum 需要求值哪些 Tensor 表达式与梯度?

旧架构材料中的 gf.domain、gf.iter、gf.kernel 是层次标签, 不是实际操作名。dialect 把一组相关操作组织在一起;gf_iter.traverse 是其中一条具体指令。 某些检查接口还接受 "gf.iter" 这样的阶段别名;它是 API 参数,不是 MLIR 语法。

下一步阅读用实例读懂 IR,对照真实输入、输出、符号含义 和逐步复现命令。

不是每个程序都经过同一条路线

  • 原生 Tensor 程序(包括原生 CSR message passing)捕获 Tensor 表达式。 受支持的 CPU 编译路径将 gf_tensor 操作逐步转换到 LLVM,梯度也有对应表达。
  • 关系编译路径保留 gf.apply,再 lowering 到 gf_iter 和 gf_kernel。实例教程从编译器测试输入直接验证这条路线。
  • Task 与 storage 规划在选定路径需要时增加依赖。 Task IR 不是每次调用都必须经过的“第四步”。
  • 参考求值可能在 auto 下被用于小规模原生表达式;它不证明任何编译 路线实际执行过。

支持矩阵记录具体入口的覆盖范围。架构能力图不能当作一次调用的执行轨迹。

provider 是什么?

backend面向具体执行环境。 在 Tiga 中,provider 是目标工具链与运行时的适配层,不是云服务商。

当前 NVIDIA 路径把序列化的 Triton IR(TTIR)交给 Triton,继续生成设备代码; CPU 路径使用 LLVM。 序列化用于隔离 Tiga 和 provider 可能不兼容的 MLIR 构建。 此前的调度也会使用目标硬件能力,并非所有目标决策都从 provider 边界才开始。

查看编译产物

先检查结果实际如何执行,不要预设它经过了全部阶段:

问题 检查方式
这个原生结果是否通过 JIT 执行? 先物化,再检查 output.execution
原生 Tensor 生成了什么代码? output.generated_code();output.mlir(verify=True) 查看并验证 Tensor IR
MessagePassing 选了什么计划? kernel.explain()
当前变体是否包含 Iter / Kernel IR? 仅在产物存在时调用 kernel.ir("iter") / kernel.ir("kernel")

kernel.ir("domain") 返回变体的语义计划,不一定是可以解析的 MLIR。 "gf.kernel.ttir" 是可用的检查接口参数,不是实际操作名。 不存在的产物会报错;循环索取全部阶段并不是通用的检查方法。

原生梯度通过反向模式自动微分推导 VJP, 其表示方法见运行时与 autograd。

继续深入

开发问题 下一章
真实操作经过 pass 后怎样改变? 用实例读懂 IR
源码在哪里,修改后如何测试? 构建与贡献
动态邻居和不规则度数如何处理? 动态关系
梯度与延迟 Tensor 如何实现? Tensor 运行时与 autograd
传输和通信如何表示? 内存与分布式执行
求解器循环与隐式梯度如何工作? 线性求解器