不到 10 天,用大模型“搓”一个编译器
在不到 10 天的时间里,和大模型一起,把九齿 Ninetoothed 从一个主要生成 Triton 代码的 DSL,推进成了一个具备 SSA IR、lowering pipeline、pass registry 和多后端代码生成雏形的编译器框架。
这一过程最有意思的地方并非“大模型写了多少代码”,而是“大模型并非天然会做编译器”。若仅提出“支持 CUDA、TileLang、TVM 后端”的需求,它极易倾向于选择最直接却也最危险的路径——为每个算子编写独立的 Kernel 模板。
例如,遇到 matmul,就写 lower_matmul;遇到 reduction,就写 lower_reduction;遇到 flash attention,就写 lower_flash_attention。这样看起来很快能跑通几个 demo,但它不是编译器,只是一堆特化代码生成器。
真正的目标是:输入只有九齿代码,编译器需要理解计算过程,将其降低为通用 IR,随后逐步 lower 至不同后端语言,而非在中间环节识别“这是 attention”,然后替换成一个手写模板。
第一步:重新定义问题
一开始给大模型的需求很宽泛:九齿要支持 Triton、CUDA、TileLang、TVM 多个后端。但很快会发现,若问题定义不够精确,大模型往往会采用“工程捷径”,倾向于生成针对 Kernel 的字符串模板,使 add、matmul、reduction、attention 这些典型算子看起来能过。
于是需求约束被不断收紧:任务不再是编写几个后端 Kernel,不再是识别算子名称,也不再是引入 AttentionOpIR 这种粗粒度的 IR。
核心要求变为从九齿的 Python application 里抽象出计算过程本身,形成细粒度的 IR,涵盖算术、访存、shape、index、loop、if、reduce、dot、store 等基本操作。这才是编译器。
第二步:把“大模型写 kernel”的冲动拉回编译器路线
这个过程中最关键的纠偏环节,在于明确要求 IR 的粒度不能宽泛到“一个算子一个节点”。
也就是说,不应出现:
AttentionOpIR
FlashAttentionOpIR
MatmulKernelTemplate
lower_flash_attention
应当构建的是:
arith.add
arith.mul
tensor.extract
linalg.dot
reduce.max
reduce.sum
scf.for
scf.if
select.where
mem.store
这样,flash attention 不再是一个特殊算子,而是一段由 dot、mask、online softmax、循环状态和归约组成的计算图。
此时,大模型才开始真正沿着编译器的逻辑路径进行思考与代码生成。
第三步:引入 SSA IR
把九齿的 application Python AST lower 到 SSA 风格 IR。
一个最简单的 add:
def application(x, y, out):
out = x + y
会变成类似:
ssa @elementwise_add {
inputs:
x : tensor<BLOCK_SIZE>
y : tensor<BLOCK_SIZE>
out : tensor<BLOCK_SIZE>
outputs:
out : tensor<BLOCK_SIZE>
^entry:
%0 = arith.add x, y
mem.store %0, out
}
matmul 则先表示成结构化 linalg:
%0 = linalg.matmul lhs, rhs
mem.store %0, output
后续再通过 pass lower 成 index、loop、extract、mul、add、store。
flash attention 的参考实现也能被拆成:
scf.for
scf.if
linalg.dot
linalg.transpose
reduce.max
reduce.sum
math.exp2
select.where
tensor.extract
tensor.view
mem.store
此时, IR 已不再依赖于“算子名称”,而是在表达计算本身,从而为后续的优化与后端适配奠定了基础。
第四步:设计 Pass Pipeline
有了 IR 之后,下一步是 Pass。
把优化与 lowering 划分为三类:
hardware-independent passes
hardware-dependent passes
backend-specific passes
默认 pipeline 类似:
ssa.canonicalize
ssa.analyze_effects
ssa.select_schedule
ssa.<backend>.optimize_schedule
ssa.decompose_linalg
ssa.lower_memory_scopes
ssa.lower_backend_intrinsics
每个 pass 都只做一件事:
canonicalize确认 IR 进入 generic SSA 形态。analyze_effects分析是否有 dot、reduction、loop、store、attention-like exp-reduction-dot pattern。select_schedule根据 IR 形态选择 elementwise-grid、blocked-linalg、exp-reduction-dot-region 等调度粒度。backend.optimize_schedule为 Triton、CUDA、TileLang、TVM 分别附加后端策略。decompose_linalg把高层 linalg 下降成更低层 loop/index/tensor/arith。lower_memory_scopes加入 register/shared/global 的后端映射。lower_backend_intrinsics选择tl.program_id、blockIdx/threadIdx、T.thread_binding等目标后端概念。
第五步:统一后端代码生成
在此架构下,后端不再是:
if matmul: lower_matmul()
if reduction: lower_reduction()
if flash_attention: lower_flash_attention()
而是统一走:
lower_unified_ssa_artifact(kernel, backend)
区别只在 target spelling:
Triton 生成
tl.load、tl.store、tl.where。CUDA 生成 pointer load/store、
blockIdx.x、threadIdx.x、C++ loop。TileLang 生成
T.prim_func、T.thread_binding、T.match_buffer。TVM 生成 TVMScript/TIR。
这才使得多后端支持开始具备泛化性。
第六步:测试和反复纠偏
在整个过程中,测试不仅用于验证正确性,同时也是防止大模型回归“模板化捷径”的约束。
测试策略包含多个维度:
- 检查后端文件中是否存在特化的 Lowering 入口,如
lower_matmul、lower_flash_attention; - 验证 add、matmul、reduction、rowwise、fused expression、attention 各类算子是否统一走 SSA Lowering 流程;
- 确认四个后端是否能从同一份 SSA 生成可运行代码;
- 检查 pass trace,确保每一层 IR 都是逐步 Lower 而来。
这些测试确保了交付成果不仅“看起来能用”,而且在结构上符合编译器的标准。
最有价值的经验
此次实践表明,大模型擅长代码生成,但对“系统边界”的理解往往不够深刻。若指令仅停留在“支持某功能”,模型倾向于寻找捷径;若目标是“设计编译器”,则必须持续引导其关注核心概念:
- IR 是什么?
- IR 的粒度应该多细?
- Lowering 每一步增加什么信息?
- Pass 是否语义清晰?
- 后端差异应该在哪里进入?
- 优化策略是写死在 emitter,还是沉淀成 pass?
- 有没有测试证明这不是模板拼接?
通过明确这些约束,大模型得以从单纯的代码生成器转变为编译器开发的协作者。
最后沉淀成 Skill
此次开发过程还沉淀出一组面向编译器开发的 skill,例如:
- compiler-ir-architecture
- compiler-ssa-lowering
- compiler-pass-pipeline
- compiler-backend-emitter
- compiler-lowering-audit
- compiler-multibackend-porting
- compiler-performance-tuning
- compiler-validation-reporting
这些 skill 的目标,是让之后的大模型不再从“写 kernel 模板”开始,而是默认按照编译器的方式工作。
目前,这套专门为编译器开发使用的 skill 也已经开源成为 Ninetoothed.skill 仓库(https://github.com/InfiniTensor/ninetoothed.skill)。
综上,“不到10天,用大模型搓了一个编译器”这句话,其价值不在于开发速度,而在于验证了一个事实:大模型能够快速生成代码,但要让它写出一个像样的系统,人需要不断定义边界、纠正抽象层级、补足验证方法,并把这些经验沉淀成可复用的工作流。
这可能才是大模型辅助基础软件开发最有价值的地方。
🔥2026 夏季 InfiniTensor 训练营报名火热进行中!
欢迎访问 InfiniTensor 官网(https://www.infinitensor.com )查看报名信息,关注公众号获取更多相关内容。
扫描下方二维码,加入训练营官方社群:

关注与交流

