MLIR中文翻译项目:术语统一与Pass流水线实战指南 简介一份面向编译器开发者与MLIR初学者的官方文档中文翻译资源包系统涵盖核心概念、多级中间表示基础设施、方言、操作、属性、类型转换、Pass管理器与代码生成等主题可帮助中文读者无障碍理解MLIR的架构层次与编译流水线设计。压缩包共316个文件以Markdown教程文档为主体辅以C头文件与实现、Toy语言示例、MLIR方言定义、测试配置及SVG架构示意图便于对照原文和可运行样例同步阅读整包仅1.05MB轻巧而结构清晰。翻译内容不仅逐篇对应官方教程还深入解释了方言如何表达特定领域概念、Pass管理器如何插入和组合转换、以及从类型安全转换到目标代码生成的具体路径并提供相关案例与说明尤其适合希望从理论走向实践的编译器学习者和研究者。目前已有155人浏览学习作为中文圈内少见的系统性MLIR参考资料具有较强的阅读与查阅价值。1. 为什么“MLIR官方文档中文翻译项目”值得你认真读一遍MLIRMulti-Level Intermediate Representation多级中间表示是 LLVM 生态里最接近“下一代编译基础设施”的组件围绕它形成了方言Dialect、操作Operation、属性Attribute、类型Type、转换模式Pattern与 Pass 管理器这一整套概念体系。官方文档覆盖面全、概念密度高英文原版让不少想用 MLIR 做 AI 芯片代码生成或自定义编译器的工程师望而却步。这类中文翻译项目要解决的正是从零读懂 MLIR 核心概念、再动手搭一条 Pass 流水线这条路上最实际的问题。适合谁想从“会写 LLVM 后端”推进到“能驾驭多级中间表示”的编译器工程师以及需要在异构硬件上做算子落地的 AI 编译团队都该拿它当第一条中文主线再回英文原版查细节。2. 翻译前先拆文档骨架MLIR 官方文档的四个知识层与组织主线2.1 四个知识层核心语言参考、方言目录、Pass 基础设施、教程案例MLIR 官方文档的组织方式和绝大多数程序员熟悉的“手册式”文档很不一样。它不是一篇从“安装”讲到“高级用法”的线性说明文而是按抽象层级分成四块彼此纠缠的内容。第一块是语言参考与核心概念描述 MLIR 本身由哪些构件组成Operation操作、Value值、Block块、Region区域、Attribute属性、Type类型以及它们如何在语法和内存布局上相互关联。第二块是方言目录把 arith、math、tensor、linalg、func、scf、cf 这些内置方言逐个展开每个方言页就是一个“操作、类型、属性”的汇编。第三块是 Pass 基础设施与转换模式讲清楚编译器中间层如何做降级和优化、pass 管理器如何调度和分析复用。第四块是教程以 Toy 语言为线索从零演示一个方言从定义到代码生成的全过程。做全量中文翻译时如果按官方目录从头线性排下去进度会卡死在第二块——因为方言目录的每一页都会引用前文的概念性术语先翻它等于让读者直接掉进术语黑洞。我一般建议翻译项目按“核心概念 → Pass 与转换机制 → 教程 → 方言目录”的顺序来组织主线官方文档里章节页面的跳转关系正好映射成一份读者学习地图。先让读者理解“MLIR 里的操作是什么”再让他们看到操作怎么被重写、怎么被降级最后再展开每个方言的细节目录阅读曲线的坡度会平缓得多。这里有一个容易被低估的细节官方文档的交叉引用是网状的而中文版作为学习资料更适合拉成一条主线索。常见做法是单独做一份“官方目录对照表”左右两列分别是官方英文链接和中文章节编号正文则按线性主线索排列。这样读者既不会在网络状跳转里迷路又能在需要深挖时反查原文。翻译顺序其实就是在替读者规划阅读顺序这步做坏了后面所有章节的翻译质量再好读者也会在中途放弃。2.2 翻译前先定术语表Operation、Value、Block、Region 的译名不能靠直译把 MLIR 官方文档翻译成中文最大的风险不是某个句子翻得不通顺而是同一个英文术语在不同文档页被译成不同中文词。Operation 有的地方译“操作”有的地方译“算子”还有的地方直接保留英文Value 被译成“值”或“数值”都算客气译成“变量”就完全偏离了 MLIR 的 SSA 语义Region 有“区域”和“作用域”两种常见译法而两者表达的抽象层级并不相同。这种不一致对零基础读者是灾难性的当他们读到“该操作作用于某个区域”时根本无法分辨这里的“操作”是 Operation 还是动作。所以翻译项目的第二道工序是先建一张术语表glossary把语言参考和高频教程里出现次数最多的三四十个术语定死唯一译名再规定在所有文档中强制统一。常见做法是Operation 译为“操作”Dialect 译为“方言”Attribute 译为“属性”Type 译为“类型”Value 译为“值”Block 译为“块”Region 译为“区域”Pattern 译为“重写模式”Pass 保留英文原形并加译注“编译器优化与转换的一次独立过程”。这张表应当公开在项目首页同时每篇文档的标题或首段标注英文原词。术语表的价值不止于翻译本身它也是读者日后用英文搜索官方资源时的反查字典。我自己做这类文档项目时会把术语表做成一张三列的 Markdown 表英文原词、中文定稿译名、首次出现的文档锚点。锚点这个细节很容易被忽略但它能帮后续审校的人快速定位第一个用了这个译名的位置避免三个人各改各的。官方文档里的“Dialect”首次出现在语言参考的那一章“Operation”在语法章节首次出现锚点记下章节号即可不需要精细到行号因为原文版本会升级。一张可维护的术语表长这样英文原词中文定稿译名首次出处锚点Dialect方言命名空间机制核心概念Operation操作语言参考-语法Value值语言参考-值Attribute属性语言参考-属性与类型Type类型语言参考-属性与类型Block / Region块 / 区域语言参考-结构Pattern重写模式转换模式章节PassPass保留英文Pass 基础设施章节Lower / Lowering降级 / 降级过程代码生成教程术语表定稿后还要有一套执行机制让它真正落地。我见过最省事的做法是在文档仓库里放一个glossary.md翻译时遇到拿不准的词先查表表里没有的再开会讨论讨论结果回填到表里并标注日期。任何人不允许为了语感优美临时换译名这是翻译质量的生命线。MLIR 文档里术语出现的密度非常高术语表不先定稿后面返工的成本会大到让项目直接停摆。3. 方言、操作、属性、类型四个最容易翻歪的核心概念怎么统一3.1 方言Dialect不是“语言”是带语义的命名空间“方言”这个词在 MLIR 语境里很容易被误解。新手刚看到“MLIR 支持多种方言”时通常的第一反应是“它像 JVM 支持 Java、Kotlin、Scala 那样支持多种语言”这个类比方向虽然不算全错但会让人忽略 MLIR 方言的关键性质方言不是独立源语言的前端产物而是中间表示上的可扩展命名空间。一个方言定义一组操作、类型、属性这些定义可以叠加在同一个 IR 上不同方言的操作可以在同一个函数里混排再由转换模式逐步降到单一目标方言——比如最终降到 llvm 方言。翻译时如果只用“方言”一个词不带解释读者会带着“语言变体”的先验来读整篇文档越读越偏。做法是在中文文档里保留“方言Dialect”双标并在它首次出现处加一段括注Dialect 是 MLIR 组织操作与类型的一种命名空间机制不是一门独立的高级语言。后续所有需要讲方言间关系的章节都尽量用具体例子而不是抽象描述例如“arith.addi 属于 arith 方言它表示整数加法tosa.add 属于 TOSA 方言表示同一运算在特定硬件抽象层的表达”。这种并排示例能把“命名空间”落到可感知的语法上。3.2 操作与值翻译“Operation/Value”时要守住 SSA 语义MLIR 文档里最频繁出现的两个词就是操作和值。官方定义里Operation 是 IR 的基本计算单元一条操作可以产生零个或多个结果值而 Value 是由操作产生、被后续操作消费的 SSA 数据流对象。关键点在于“SSA 形式”每个值只能被定义一次之后只能读不能改。翻译时如果把 Value 译成“变量”读者会下意识把它当成可重新赋值的寄存器这对理解后续的支配关系、别名分析和重写模式是致命的误导。实际操作中我一般要求翻译者在涉及 Value 的段落里至少保留一处英文原词并加脚注例如“该值Value由上方操作定义只能在支配它的区域内被读取。”代码块里的注释也按这个规则处理。官方文档里大量示例长这样%0 arith.addi %arg0, %arg1 : i32 %1 func.call foo(%0) : (i32) - i32中文注释建议写成“arith 方言的整数加法操作定义值 %0”和“调用 func 方言中的函数 foo消费 %0”而不是笼统写“把两个数加起来的变量”。一句话原则操作和值是 IR 的结构事实不是程序里的概念翻译时一定不要出现“变量”“赋值”这类误导词。类似的问题也出现在“参数”这个译名上MLIR 里的 operand 和 attribute 都是附着在操作上的前者是输入值后者是编译期元信息中文里都叫“参数”会让读者完全分不清。3.3 属性与类型把“编译期事实”和“运行时类型”分开属性Attribute和类型Type是 MLIR 文档里另一对容易翻混的词因为中文里的“属性”范围很广。MLIR 语境下的 Attribute 指附加在操作上的编译期常量信息比如 arith.constant 里的常量值、张量布局参数它们可以被查询、比较、序列化但不参与数据流运算Type 则是值的静态类型例如 tensor4xf32、memref4xf32决定一个值在内存和寄存器里的表示方式。翻译时若把 Attribute 译成“特征”或“参数”会模糊它与类型之间的边界反过来把 Type 译成“数据类型”虽然没有大错但在 MLIR 文档里它还有维度、布局、地址空间等更深一层的语义。一个容易踩中的细节是属性可以“属于”类型例如 tensor4xf32, #dense 里的 #dense 就是一个编码存储布局的属性它紧凑地镶在类型语法里。文档翻译到这种示例时光改文字没用保留原文语法并在注释里解释属性的作用域才是正解。拿 GPU 工作负载来说tensor 类型上常附有 memory_space 属性直接决定算子映射到哪类存储——这类语义完全没法靠汉字对译传达只有把“属性是编译期元信息”这条主线讲清楚读者才能自己推导。这段内容还牵扯到代码生成codegen场景里的一个常见误区很多人以为代码生成是“从高级语言编译一次”但在 MLIR 语境下代码生成是多级降级链的末端产物——从高层方言逐步降到中间方言最后落到 LLVM 方言或者目标机器指令。属性在这一路上不断被解释和折叠类型则一路被精确化。中文文档需要在讲属性与类型的第一页就把这个视角立住否则读者到了代码生成章节会以为自己进了另一个项目。4. 转换模式与 Pass 管理器中文文档里最难啃的两块骨头4.1 转换模式Pattern声明式 DRR 与 C 手写模式的翻译分工MLIR 的转换模式Pattern是官方文档里理论密度最高的一部分因为它涉及两套写法并行存在。一套是声明式重写规则DRRDeclarative Rewrite Rules用 TableGen 写例如下面这个示意规则——把“连续取两次负号”折叠掉// 示意规则neg(neg(x)) 直接替换为 x def NegNegFold : Pat (arith::NegFOp (arith::NegFOp $x)), (replaceWithValue $x) ;这类模式在翻译时文字说明承担的是“为什么”而不是“怎么做”DRR 的好处是规则可读、可自动生成匹配代码适合大规模声明式改写坏处是复杂约束比如要检查类型是否合法、维数是否匹配写起来吃力需要退回 C 手写matchAndRewrite。翻译官方文档里的 DRR 章节时正文可以直译但代码块必须保留原始内容只允许在行内或代码块上方加中文注释否则读者没法拿你的译文对照官方示例跑结果。另一套是过程式写法伪代码大致长这样struct MyRewritePattern : public OpRewritePatternarith::AddIOp { LogicalResult matchAndRewrite(arith::AddIOp op, PatternRewriter rewriter) const override { // 只在加法的一个操作数是常量时触发 if (!matchPattern(op.getRhs(), m_Constant())) return failure(); // 这里进行常量折叠的具体替换 rewriter.replaceOpWithNewOparith::ConstantOp(op, /* 新值 */); return success(); } };翻译这段的逻辑说明别去逐行解释 C 语法而是要讲清三个中文读者最容易忽略的点matchAndRewrite在匹配成功后对 IR 的修改必须通过rewriter不能直接改操作数返回failure()表示规则不适用返回success()表示已经完成替换PatternRewriter的接口设计保证了重写过程可以被追踪和回滚。这三点不点破新手照着文档写自定义转换模式十个里有八个会在“改了 IR 但没有产生新操作”的困惑里耗掉半天。我一般会在这一章节末尾加一张小表把两类写法的适用场景列清楚DRR 适合结构简单的局部重写C 适合需要依赖分析结果或复杂上下文判断的重写。4.2 Pass 管理器从单条 pass 到流水线调度的中文解释Pass 管理器PassManager是 MLIR 文档里另一个让中文读者读不下去的章节因为它同时包含“某个 pass 做什么”和“多个 pass 如何被调度”两层语义。前者相对好翻译比如-convert-linalg-to-loops这个 pass 名本身就在说“把 linalg 方言操作转成 scf 循环结构”后者则牵扯到 pass 的依赖分析、分析缓存和跨 pass 复用的机制纯文字翻译很容易变成名词堆砌。文档里对 PassManager 的典型用法命令行形态是这样的mlir-opt --convert-linalg-to-loops --canonicalize input.mlir -o output.mlir翻译这段示例时我一般会配一栏参数说明表把每个 flag 跟官方文档里的 pass 名称对齐明确“括号里的英文是你在代码或 CI 脚本里必须原样写的名字”。因为 pass 名称属于命令行接口翻译后如果被本地化成“转换-线性代数-到-循环”读者在真实工具里根本无法对应。中文正文可以用中文解释语义但命令字面量和 pass 名必须保持和官方一致。更隐蔽的难点是 pass 与 pass 之间的顺序。文档里常见的一句话“先 canonicalize 再 convert-to-llvm效果优于反序”翻译时如果只译字面意思读者无法判断为什么。要补一句原理canonicalize 会做常量折叠和模式匹配化简先跑一遍可以减少后续转换需要处理的操作数形态而 convert-to-llvm 一旦把高层的方言结构抹平canonicalize 就再也看不见那些高层的语义模式了。这类“顺序即语义”的注释是我在中文文档项目里要求每个 pass 章节都加的标准段落比任何翻译准确率都重要。说到底pass 管理器这块内容玄学成分其实不多只是官方文档默认读者已经理解编译原理里“分析-变换”的节奏中文版必须把这个前提交代清楚。4.3 代码生成内容教程语境下的“降级”不要翻成“优化”MLIR 文档里与代码生成相关的内容核心动词是 lower降级配套名词包括 lowering、conversion、legalization。翻译时最常犯的错误是把“lower”翻成“优化”。这两个词的语义差很远优化是在同一抽象层级改善代码质量降级是把高层方言逐步翻译到低层方言例如把 linalg 算子展开成循环嵌套再降到 LLVM 方言最终变成目标机器的指令模式。文档翻译项目里如果把“to lower”统一译成“转换”虽然不算错但会丢失“高层变低层”的方向感读者没法建立“多条降级路径最终汇聚到 LLVM 方言”的心智模型。在官方教程Toy 语言那个系列里最后一章就是“从 Toy 方言降到 LLVM 方言”的完整演示涉及大量代码块和命令行实例。翻译时唯一可操作的做法是正文全部中文但命令行、pass 名、代码符号、调试输出保持原样中文只出现在注释和说明段落。因为教程的真正价值是让读者在自己机器上复现而不是读懂一篇被汉字包裹的英文代码。官方文档在这一点上非常固执它把大量语义放进名字里比如toy.print、toy.generic_call中文译者要做的不是翻译这些名字而是解释这些名字背后的方言归属和调用约定。这类“代码生成”内容翻译得好不好判断标准其实只有一个读者读完能不能在本地把那条降级链跑通。如果读者把文档里的命令逐条执行下去能从 Toy 源文件一路得到 LLVM IR 和可执行的机器码翻译就是合格的如果读者读完只会复述“降级是什么”而不敢动手说明翻译者把命令细节写丢了。我通常会在代码生成章节末尾附一张“命令对照表”把每一步的源文件、输入方言、输出方言和关键 pass 名排列成行让读者像核对清单一样过一遍。5. 避坑指南翻译 MLIR 文档时最容易“翻车”的五个场景5.1 现象一术语在多个文档中表述不一致现象一篇文档里把 Pattern 译成“模式”另一篇把它译成“模板”读者读到转换模式章节开始怀疑这是两个不同的机制。原因翻译者没有在开工前建全局术语索引按官方文档的原始顺序顺次翻译每个术语都按当下语感选词。MLIR 文档里 Pattern 出现的频率极高在不同上下文里感觉自然的中文词不同顺着翻一定会漂移。解决强制在项目里推术语表并且把翻译顺序定为“核心概念 → Pass 与转换机制 → 教程 → 方言目录”。术语首次出现一定落在已定稿章节后面遇到直接复制不现场造词。实在拿不准的词宁可保留英文也不要临时替换。审校时专门抽十个高频词全库搜索看译名分布是否集中。5.2 现象二把“Dialect”直译为“语言”导致概念整体失真现象读者以为“MLIR 支持好几种语言”并用日常语言编译器的模型去理解 MLIR 里的混合方言 IR比如以为必须先把每个方言单独编译再链接。原因“dialect”在英文里本身和“language”有模糊的重叠译者若没有编译原理背景很容易在“语言变体”的直觉下顺手翻成“语言”。解决在术语表第一行写死“Dialect 方言命名空间机制”并在语言参考章节的引言里加一段专门的解释用表格对比“编程语言”和“MLIR 方言”在语法、编译入口、互操作方式上的差异。这道工序做好了后面所有方言章节的翻译难题会大幅下降。5.3 现象三Pass 名称被“本地化”后无法与官方命令对齐现象译文里出现“转换-线性代数-到-循环”这种 pass 名读者照抄到mlir-opt的命令行里直接报错。原因翻译者为了追求“纯中文体验”把命令行参数也一并翻译了。这属于好心办坏事。解决把 pass 名、命令行参数、环境变量、代码符号全部列入“禁用翻译清单”正文里可以解释但示例必须原样引用。审校时用一个简单的 shell 命令把 doc 里的代码块全部抽出来跑一遍mlir-opt --help对比参数能快速发现这类问题。5.4 现象四代码块并入中文注释后示例代码“跑不动”现象读者从中文文档里复制示例代码结果编译器报语法错误或非法操作名。原因多数不是翻译本身的问题而是文档从英文搬运到中文项目时代码块里的换行和空白被打散或者注释符号在中文输入法状态下变成了全角字符。这类问题在 Markdown 渲染后极难肉眼发现。解决代码块一律用单独的文件存放不让 Markdown 渲染器参与格式重组翻译中只允许在代码行上方加注释不改动代码内部的空格。审校时用git diff --word-diff对比英文原文与中文译文的代码区能精确看到哪个字符被动过。这个坑不踩一次很难意识到全角引号和半角引号在代码里就是两种字符渲染出来却几乎一样。5.5 现象五版本漂移——官方文档更新后中文翻译还停在旧语义现象读者查阅某个方言页面看到的接口签名与本地安装的 MLIR 版本对不上花大量时间排查后才发现是文档过期。原因翻译项目没有锁定上游版本基线长期零散更新导致译文和官方版渐行渐远。MLIR 本身还在快速演进方言操作和 pass 名都在变半年不更新就可能出现大面积的过期内容。解决在项目首页明确声明“本文档对应 MLIR 官方仓库的某一提交commit hash”并在每个文档头部标注最后同步日期。每次更新官方文档时先用git diff高亮改动条目只增量翻译不改动未涉及部分的措辞避免一次大规模重写引入新的术语不一致。版本漂移是这类文档项目最常见的慢性病不靠机制约束光靠热情撑不了多久。6. 让翻译成果可验证术语表公开、锁定基线提交、跑通一段官方教程中文翻译项目最容易产生的错觉是“翻完了 学完了”对文档项目本身来说翻完也不等于质量过关。我见过的血泪经验是译文读起来通顺但读者按文档跑命令时在第一步就失败。要避免这种局面翻译项目至少要做三件事收尾。一是把术语表作为独立文件放进仓库并且在 README 里直接展示核心条目。这样读者不需要翻到正文某页才能查到术语定义遇到歧义时自己就能按表对照。二是锁定上游基线版本在文档头部写清“翻译自官方仓库某次提交”后续增量更新用 diff 驱动而不是靠目视整篇重读。三是挑一篇官方教程作为“验收实验”通常选 Toy 教程的最后一章——把译文中出现的所有命令原样执行一遍从mlir-opt到mlir-cpu-runner确保每一个 pass 名和选项都真实存在并把执行结果附在文档末尾。我个人还有一个习惯在每次交付文档之前从主索引页开始把文档里所有的“参考上一章”“见 xx 章节”这类交叉引用全部点一遍确认锚点没有因为翻译时调整目录结构而失效。这个动作很机械但确实能暴露最多问题。交叉引用断链是中文文档项目里最难自动检测的坑因为 Markdown 的段落标题一旦被翻译和重排锚点就全变了。这些工作做完这份中文 MLIR 文档才不只是“能读”而是“能跟着跑”。翻译的价值在于是不是真的帮读者节约了理解时间而不是把英文换了一层皮。如果你也想做类似的方向记住一件事先定术语再定版本最后用命令验证。希望帮到你。本文还有配套的精品资源点击获取