想象你是大型快递转运中心的调度员。清晨,顺丰、京东、邮政的货车同时停靠收货口,每家面单格式完全不同:喷码、电子标签、五联单,乱七八糟。但你没慌,因为这座中心的核心能力不是认识所有面单,而是把任何面单的包裹都送上同一条传送带——先统一成一式三联的标准单,再逐级细化成街道、楼栋、门牌,最后交到只认门牌的快递员手里。AI 编译世界的 MLIR(多级中间表示)工具链,就是这样的转运中心。PyTorch、TensorFlow、ONNX 是各家快递,计算图是面单,各部门的标签体系叫方言,一道道分拣工序叫编译优化,末端快递员叫 LLVM。这篇文章,我们沿着这条传送带走一遍,看一个模型如何从框架手里,被工具链一步步“翻译”成能在芯片上奔跑的机器码。

岗位关联

学会 MLIR 工具链,你就掌握了 AI 编译器的“总装车间”:无论模型来自哪个框架、要跑到哪块芯片,你都能读懂中转流程、参与优化、完成落地。

目录
  1. 可视化演示
  2. 工具链全景
  3. Dialect方言体系
  4. 核心工具命令
  5. 框架前端集成
  6. 转换下降机制
  7. 优化策略
  8. 代码生成链路
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示MLIR工具链核心机制:多级中间表示逐层降级、优化/转换/折叠等工具链流水线、MLIR多级IR带来的性能提升,通过交互理解MLIR工具链原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

多级IR:逐层降级到后端 高层IR逐步降级到低层IR 每级表示保留合适抽象层次 L0 → L1 → L2 → L3 多级IR兼顾优化与代码生成

工具链全景

傍晚六点,转运中心的传送带嗡嗡转动。收货口刚卸下的包裹贴着五花八门的面单,可几分钟后,它们都变成了同一套条形码,在传送带上匀速前进。你问调度员秘诀,他指了指头顶的大屏:“我们不是认识每一张面单,而是把'任何格式'先翻译成'中心格式',再逐级翻译成'派送格式'。”MLIR 工具链,就是 AI 编译器世界里的这套传送系统。

“中心格式”就是 MLIR 的多级中间表示。之所以叫“多级”,是因为同一份程序可以同时以不同抽象等级存在:高层还能看出“这是一个卷积”,低层已经变成“读取这几块内存、做乘加、写回”。每一级被封装成一种 dialect(方言)。框架集成要做的第一件事,就是把 PyTorch、TensorFlow、ONNX 手里的计算图接进来,送上这条传送带。

整条工具链由三个环节组成:前端导入(frontend)把框架图转换为高层方言;中端优化由 mlir-opt 驱动一系列 pass,把高层方言逐级下降(lowering)到更低层;后端导出由 mlir-translate 对接 LLVM 等后端,最终生成机器码。这套设计理念可以概括为两点:可扩展——新硬件只需新增方言与转换规则;渐进式下降——每层只做自己擅长的事。这条“逐级下降”的链条可以用一句公式概括:

graph  →  tosa  →  linalg  →  affine  →  llvm  →  asm\displaystyle \text{graph} \;\rightarrow\; \text{tosa} \;\rightarrow\; \text{linalg} \;\rightarrow\; \text{affine} \;\rightarrow\; \text{llvm} \;\rightarrow\; \text{asm}

1. graph:框架侧的高层计算图,例如 PyTorch 导出的 torch 算子序列,此时还带着 Python 层语义。

graph\text{graph}

2. tosa:统一面单层,把不同框架的算子标准化成可移植的张量算子集合。

tosa\text{tosa}

3. linalg:结构化线性代数层,算子变成带显式循环结构的张量运算,暴露出可优化的形状信息。

linalg\text{linalg}

4. affine:循环结构显式化,借助仿射映射精确分析数组访问与依赖关系。

affine\text{affine}

5. llvm:下降到 LLVM 方言,操作与 LLVM IR 指令一一对应,是交给后端前的最后一站。

llvm\text{llvm}

6. asm:最终产物——目标机器的汇编/机器码,由 LLVM 后端生成。

asm\text{asm}

从收货口到装车口,包裹每过一个环节就换一张更精确的标签。框架集成不是把某个框架“宠上天”,而是给所有框架一条同等的传送带——这正是 MLIR 工具链最迷人的地方。

Dialect方言体系

转运中心里并不只有一种标签。收货部用“大区编号”,仓储部用“库位层架”,派送部用“街道门牌”。同一个包裹,在不同部门眼里有不同的“方言”,但谁都不会搞混,因为每套标签都贴在同一个包裹上。

MLIR 的 dialect(方言)正是这样的标签体系。一个方言定义了一组合法的 Operation(操作)、Type(类型)和 Attribute(属性)。比如 func 方言描述函数骨架,arith 方言描述加减乘除,scf 方言描述结构化控制流,tosa 方言描述可移植张量算子,linalg 方言描述带有循环结构的线性代数运算,llvm 方言则直接对应 LLVM 指令。每一个方言,都是一套自洽的“词汇表”。

方言机制让 MLIR 变成乐高积木:任何团队可以自定义方言,再声明它与其他方言之间的转换规则,完全不必改动中心本身。以 affine 方言为例,它把数组访问限定为仿射映射——下标与地址之间必须是线性关系,这样编译器才能精确分析依赖、做循环变换。仿射映射的一般形式是:

f(d0,d1,…,dk−1)=α0d0+α1d1+⋯+αk−1dk−1+β\displaystyle f(d_0, d_1, \ldots, d_{k-1}) = \alpha_0 d_0 + \alpha_1 d_1 + \cdots + \alpha_{k-1} d_{k-1} + \beta

1. d0…dk-1:循环变量或数组下标,它们是仿射映射的输入参数。

d0,d1,…,dk−1d_0, d_1, \ldots, d_{k-1}

2. α0…αk-1:各下标的系数,对应内存布局中的 stride(步长),例如行优先时行下标系数等于列数。

αidi\alpha_i d_i

3. Σ 求和:把每一维的贡献累加,得到一维内存偏移的核心部分。

∑i=0k−1αidi\sum_{i=0}^{k-1} \alpha_i d_i

4. β:常数偏移量,对应数组起始位置的修正(如偏置、padding)。

+β+ \beta

仓储部往货架上贴“库位层架”,派送部往包裹上贴“街道门牌”,标签虽多,但每套都精确服务于一个环节。MLIR 的高明之处,就是让这些标签在同一个包裹上共存,并且随时可以互相翻译。

核心工具命令

中控室里并排摆着三台机器。调度员每天的工作就围着它们转:第一台是“分拣程序控制器”,负责按顺序执行一道道分拣工序;第二台是“翻译机”,负责把外来单据翻译成内部格式;第三台是“模拟演练台”,在正式发车前先试跑一遍路线。

这三台机器对应 MLIR 工具链的三个核心命令:mlir-opt、mlir-translate、mlir-cpu-runner。mlir-opt 是主将,读取 .mlir 文件,按命令行参数指定的顺序执行 pass,输出优化后的 .mlir;mlir-translate 是翻译官,在 MLIR 内部表示与外部格式(LLVM IR、SPIR-V、Graphviz 文本等)之间互译;mlir-cpu-runner 是演练台,不经过完整编译链也能直接执行 MLIR 模块,适合快速验证。调试时还可以给 mlir-opt 加 --mlir-print-ir-after-all 或 --debug-only 这类调试选项,观察每一道工序之后的 IR 长相。

一条典型的命令形如 mlir-opt file.mlir --tosa-to-linalg --linalg-tile --convert-linalg-to-affine -o out.mlir,每个 flag 就是一个 pass。pass 的执行顺序决定了优化效果,这本质上是一个函数复合:

IRn=Pn∘Pn−1∘⋯∘P1(IR0)\displaystyle \mathrm{IR}_n = P_n \circ P_{n-1} \circ \cdots \circ P_1 (\mathrm{IR}_0)

1. IR0:mlir-opt 读入的初始中间表示,对应传送带起始处的原始包裹。

IR0\mathrm{IR}_0

2. P1…Pn:按顺序注册的 pass,如 --tosa-to-linalg、--linalg-tile。

P1,P2,…,PnP_1, P_2, \ldots, P_n

3. ∘ 复合:表示“先执行 P1,再执行 P2”,相邻 pass 的输入输出首尾相连。

Pn∘Pn−1∘⋯∘P1P_n \circ P_{n-1} \circ \cdots \circ P_1

4. IRn:最终产物,所有工序完成后的 IR,可直接交给翻译机或后端。

IRn\mathrm{IR}_n

分拣工序必须“先分大区、再分街道、最后分楼栋”,顺序错了包裹就会迷路。mlir-opt 的存在,就是让你把这道顺序牢牢抓在自己手里。

框架前端集成

转运中心的收货口分成好几条车道:1 号口是顺丰(PyTorch),2 号口是京东(TensorFlow),3 号口是邮政小包(ONNX)。各家车道形状不同,但卸下来的包裹,最后都要先贴上一张行业通用的“统一面单”——TOSA。

这就是框架前端集成做的事。Torch-MLIR 项目负责把 PyTorch 模型经 torch 方言逐步合法化到 MLIR 高层方言;TensorFlow 官方通过 TOSA 或 MLIR-HLO 路径接入;ONNX 模型则借助 importer 导入。TOSA(Tensor Operator Set Architecture)是一套精心设计的“可移植张量算子”方言,像统一面单一样,让不同框架的算子先归一化,再进入 MLIR 的优化流水线。

从数据结构看,前端集成要完成一次“图到模块”的映射:计算图的每个节点(算子)变成一个 dialect 操作,每条边(张量)变成操作数或结果,整张图被包进 func 方言的函数体。可以记作:

G=(V,E)  ⟼  module{ func{ opv:v∈V } }\displaystyle \mathcal{G} = (\mathcal{V}, \mathcal{E}) \;\longmapsto\; \mathrm{module}\{\, \mathrm{func}\{\, \mathrm{op}_v : v \in \mathcal{V} \,\} \,\}

1. G=(V,E):计算图,V 是算子节点,E 是张量数据边。

G=(V,E)\mathcal{G} = (\mathcal{V}, \mathcal{E})

2. module:MLIR 的顶层容器,相当于一个编译单元,容纳多个函数。

module\mathrm{module}

3. func:函数方言,把一张子图包装成可调用、可重用的函数。

func\mathrm{func}

4. op_v:把图节点逐个映射为方言操作,边则映射为操作数或结果。

opv:v∈V\mathrm{op}_v : v \in \mathcal{V}

面单格式再乱,只要进了统一面单通道,后面的传送带就对它一视同仁。前端集成把“各家快递”变成“统一包裹”,MLIR 工具链的价值,正是从这一刻开始积累。

转换下降机制

面单统一之后,真正的重头戏才开始:把“这是一箱易碎玻璃器皿,送往朝阳区”翻译成“送往 XX 街道 YY 号,轻拿轻放,置 3 号货架”。越接近派送,指令就越具体。方言下降(dialect lowering)做的就是这件事——从“描述做什么”逐级降到“描述怎么做”。

MLIR 的下降由两个核心机制驱动:RewritePattern(重写模式)和 ConversionDriver(转换驱动)。RewritePattern 声明“看到什么样的操作,就替换成什么样的操作序列”;ConversionDriver 则按合法化规则(legality)反复应用模式,直到所有操作都落在目标方言内。合法化规则告诉驱动:转换目标(conversion target)里哪些操作是“天生的好公民”(already legal),哪些必须被转换。

一个高层操作往往展开成多个低层操作,这种一对多映射是下降的基本形态:

Op(x1,x2,…,xn)  ⟹  op1(⋯ ),  op2(⋯ ),  …,  opm(⋯ )\displaystyle \text{Op}(x_1, x_2, \ldots, x_n) \;\Longrightarrow\; \text{op}_1(\cdots),\; \text{op}_2(\cdots),\; \ldots,\; \text{op}_m(\cdots)

1. Op(x1,…,xn):源方言中的一个高层操作,带若干操作数。

Op(x1,x2,…,xn)\text{Op}(x_1, x_2, \ldots, x_n)

2. ⟹:模式重写符号,表示匹配成功后触发整体替换。

⟹\Longrightarrow

3. op1…opm:替换产物,一组合法的低层操作,共享或重排原操作数。

op1(⋯ ),  op2(⋯ ),  …,  opm(⋯ )\text{op}_1(\cdots),\; \text{op}_2(\cdots),\; \ldots,\; \text{op}_m(\cdots)

4. m:展开系数,由模式设计者决定,体现“一词多义”的拆分逻辑。

mm

从“送往朝阳区”到“送到 3 号货架”,每一步翻译都在增加可执行性、减少歧义。转换下降机制就是这条翻译流水线的“语法规则”,保证任何一句话都不会翻出歧义。

优化策略

面单落地后,分拣员开始琢磨怎么装车更省。大件拆成小件,方便塞进车厢缝隙——这是 tiling(循环分块);几个小包捆成一捆,少贴几张签——这是 fusion(算子融合);一辆大车一次拉走一条街的货——这是 vectorization(向量化);按派送路线重新码放货架——这是 memory layout(内存布局)。

这些优化大多发生在 linalg/affine 等结构化方言层,因为此时循环和访存还“看得见”。循环分块把一个大循环切成小块,让数据块能留在 CPU 缓存里,避免反复访问内存;算子融合把 element-wise 链合并成一个算子,减少中间张量的写出和读入;向量化让一条 SIMD 指令同时处理多个数;内存布局则选择行优先、列优先或分块布局,匹配硬件的访存习惯。

分块的本质是对循环空间的重参数化。一个双重循环 (i, j) 被改写为四重循环:

i=i0×Ti+i1(0≤i1<Ti),j=j0×Tj+j1(0≤j1<Tj)\displaystyle i = i_0 \times T_i + i_1 \quad (0 \le i_1 < T_i), \qquad j = j_0 \times T_j + j_1 \quad (0 \le j_1 < T_j)

1. i, j:原始大循环的循环变量,遍历整个数据空间。

i,  ji,\; j

2. i0, j0:外层循环变量,表示当前处理第几个数据块。

i0,  j0i_0,\; j_0

3. Ti, Tj:分块尺寸,决定每块处理多少数据,是调优的关键旋钮。

Ti,  TjT_i,\; T_j

4. i1, j1:内层循环变量,表示块内的局部偏移。

i1,  j1i_1,\; j_1

5. 0 ≤ i1 < Ti:约束条件,保证块内偏移不越界,块与块不重叠。

0≤i1<Ti0 \le i_1 < T_i

装车方案千变万化,但原则只有一条:让搬运工少跑路、让车厢不空载。MLIR 把优化放在结构化方言层做,正因为此时“车厢和路线”还清晰可见,编译器才有机会施展这些装车智慧。

代码生成链路

最后一公里,包裹被交到末端快递员手里。快递员不关心“易碎”“保鲜”这些业务词,他只认两件事:送到哪个小区,几号楼几单元。LLVM 就是那位快递员——它只认最底层的指令,但胜在路熟、覆盖广。

代码生成链路把 llvm 方言翻译成 LLVM IR,再由 LLVM 后端生成目标机器码,最后链接运行时完成交付。这条链路有两种打开方式:JIT(Just-In-Time,运行时编译再执行)适合快速验证和动态形状场景;AOT(Ahead-Of-Time,预先编译成可执行文件或 .so)适合追求稳定部署的生产环境。mlir-cpu-runner 就是典型的 JIT 路径。交付后还可以借助 perf 或 MLIR 的 timing pass 做性能分析,量化每一步优化到底值多少。

到达 LLVM 层后,所有看似“高级”的数组访问都必须退化为赤裸裸的地址计算。行优先布局下,二维下标 (d0, d1) 的内存地址是:

addr(d0,d1)=base+(d0×S0+d1×S1)×size\displaystyle \mathrm{addr}(d_0, d_1) = \mathrm{base} + (d_0 \times S_0 + d_1 \times S_1) \times \mathrm{size}

1. d0, d1:数组的逻辑下标,例如卷积输出特征图的行和列。

d0,  d1d_0,\; d_1

2. S0, S1:各维度的 stride,行优先时 S0 = 每行元素数,S1 = 1。

S0,  S1S_0,\; S_1

3. size:单个元素的字节数,float32 为 4,int8 为 1。

size\mathrm{size}

4. base:数组在内存中的起始地址,由运行时分配或静态指定。

base\mathrm{base}

从框架到机器码,包裹终于走完了整条传送带:统一面单、逐级细化、装车优化、最后交给只认门牌的快递员。MLIR 工具链的价值,就是让这一切“对任何快递公司、任何小区”都成立。

AI 应用场景

大模型推理引擎的算子融合优化

阶段:中端优化(IR 层优化)

解决的问题:传统推理引擎按算子逐个调用底层库,add 的结果要写回显存、relu 再读出来,访存开销巨大。借助 MLIR,在 linalg 层把 add+relu 这类 element-wise 链融合成单个算子,减少中间张量落盘,推理延迟可降低 20%~40%。

具体案例:某团队用 Torch-MLIR 把 LLaMA-7B 的 decoder 层导入 MLIR,在 linalg 层对残差连接的 add+LayerNorm+SiLU 链做融合,再下降至 LLVM 生成 .so,最终在 A100 上达到 1.8 倍端到端加速。

芯片公司用 MLIR 构建自家编译工具链

阶段:后端代码生成

解决的问题:传统自研编译器要从零写前端,工作量巨大。MLIR 允许复用前端导入与高层优化,芯片公司只需实现从 linalg/llvm 方言到自身指令集的转换目标与代码生成,工具链交付周期从数年缩短到数月。

具体案例:某 NPU 创业公司基于 MLIR 的 TOSA→linalg→目标方言流水线,为自家 DSP 实现一个 custom 方言与 30 个 conversion pattern,两周内跑通 ResNet-50 的编译与部署。

云厂商统一多框架优化中台

阶段:前端集成

解决的问题:云上同时有 PyTorch、TensorFlow、Paddle 的推理负载,每套都要单独优化,成本高。把三者统一接入 TOSA 中间层,之后共享同一套 linalg 优化与代码生成,一套优化处处受益。

具体案例:某云厂商的推理平台对 ONNX 模型做 ONNX→TOSA→linalg 导入,在该层统一做 tiling 与内存布局优化,使三种框架来源的模型在 CPU 上达到一致的性能水位,运维人力下降 60%。

Python 演示

python68 行
# -*- coding: utf-8 -*-# 一个极简的 MLIR 工具链模拟器:# 用字符串形式的 IR 演示 方言下降 与 算子融合 两个 pass class Op:    def __init__(self, name, args, result):        self.name = name        self.args = list(args)        self.result = result     def __repr__(self):        return "%" + self.result + " = " + self.name + "(" + ", ".join(self.args) + ")"  LOWER_MAP = {    "tosa.add": "linalg.add",    "tosa.mul": "linalg.mul",    "tosa.relu": "linalg.relu",}  def lower_tosa(module):    lowered = []    for op in module:        if op.name in LOWER_MAP:            lowered.append(Op(LOWER_MAP[op.name], op.args, op.result))        else:            lowered.append(op)    return lowered  def fuse_elemwise(module):    fused = []    i = 0    while i < len(module):        op = module[i]        if op.name == "linalg.add" and i + 1 < len(module):            nxt = module[i + 1]            if nxt.name == "linalg.relu" and nxt.args[0] == op.result:                fused.append(Op("linalg.add_relu", op.args, nxt.result))                i += 2                continue        fused.append(op)        i += 1    return fused  def run_pipeline(module, passes):    current = module    for pass_name, pass_fn in passes:        current = pass_fn(current)        print("---- pass:", pass_name)        for op in current:            print(op)    return current  if __name__ == "__main__":    src = [        Op("tosa.add", ["a", "b"], "c"),        Op("tosa.relu", ["c"], "d"),        Op("tosa.mul", ["d", "e"], "f"),    ]    print("== 原始 TOSA 模块 ==")    for op in src:        print(op)    print()    run_pipeline(src, [("lower_tosa", lower_tosa), ("fuse_elemwise", fuse_elemwise)])
目的:用约 50 行 Python 模拟 MLIR 工具链“读取 IR → 方言下降 → 算子融合”的核心流水线,帮助你建立对 pass 管线的直觉。
  1. class Op:定义 IR 的最小单元:操作名、操作数、结果。
  2. LOWER_MAP = {一张“方言映射表”,模拟从 tosa 到 linalg 的下降规则。
  3. def lower_tosa(module):第一个 pass:把高层 tosa 算子逐一下降为 linalg 算子。
  4. if op.name in LOWER_MAP:模式匹配:命中下降规则的操作被替换,未命中的原样保留。
  5. def fuse_elemwise(module):第二个 pass:把 add 与紧随的 relu 合并成 add_relu。
  6. if nxt.name == "linalg.relu" and nxt.args[0] == op.result:检查数据依赖,确保后面的 relu 确实消费 add 的结果。
  7. def run_pipeline(module, passes):模拟 mlir-opt:按注册顺序逐个执行 pass。
  8. if __name__ == "__main__":入口,构建一个小模块并用两张 pass 串联。

常见误区

误区我以为 MLIR 跟 LLVM IR 一样,是一门固定的中间语言,学会它的指令集就会用了。

正解MLIR 是一个“容器框架”,里面能装无数种 IR——每种 dialect 都是一门 IR,LLVM IR 只是其中最低层那一级的方言。学 MLIR 学的不是一套指令,而是“如何定义方言 + 如何下降”的方法论。好比转运中心的核心不是某一种面单,而是“能处理所有面单”的机制。

为何常见:名字里带 IR,直觉上容易和 LLVM IR 类比;实际上它更像“IR 的操作系统”。

误区既然最终都要到 LLVM,那不如跳过 tosa、linalg、affine,直接翻译过去,省时省力。

正解高层方言承载着语义信息:linalg 知道这是个矩阵乘、affine 知道循环边界,这些信息是循环分块、算子融合等优化的依据。一步到位等于把优化空间全部扔掉,生成代码可能慢好几倍。

为何常见:只看“结果等价”而忽略“优化空间”;编译器新手常低估中间表示的信息价值。

误区我把 .mlir 丢给 mlir-opt,期待它吐出一个能在服务器上跑的二进制。

正解mlir-opt 只做 IR→IR 的变换,输入输出都是 .mlir。要得到可执行文件,需要 mlir-translate 翻译成 LLVM IR,再由 LLVM 后端生成机器码,最后链接运行时。各司其职,缺一不可。

为何常见:很多人把它和 gcc 这类“一条命令出可执行文件”的编译器混淆。

误区lower 一个 pass 就是把 `tosa.add` 替换成 `linalg.add`,这不就是字符串替换吗?

正解真正的方言转换要处理三件事:操作数与结果类型的转换(tensor 变 memref)、1:N 展开(一个 op 变多个 op)、合法化检查(确保转换后所有 op 都是目标方言的合法公民)。字符串改名只是最简单情况的一小步。

为何常见:看多了简单的示例,误以为 pattern 只是“改名规则”。

误区我拿到一个 ONNX 模型就觉得万事大吉,剩下的随便找个 runtime 一跑就行。

正解ONNX 只是“外包纸箱”,里面的算子仍是高层语义。要让它跑得又快又好,还要经 ONNX→TOSA→linalg 的导入与下降,再做融合、分块、向量化,最后才生成目标代码。ONNX 是起点,不是终点。

为何常见:ONNX 生态太普及,让人误以为它是“最终标准”,而忘了它只是统一格式的一环。

面试题

Q1. MLIR 中“多级中间表示”最核心的含义是什么?

单选题 · easy

  • A.用多个文件保存程序的不同片段
  • B.在不同抽象层级之间分层表达计算← 正确答案
  • C.为每种硬件单独编写一套新翻译器
  • D.把多种高级语言统一编译成字节码

答案:B。“多级”不是“多个文件”,而是同一程序的多种抽象等级。B 准确描述了 MLIR 的核心设计:高层看语义、低层看实现。A 混淆了“多级”与“多文件”;C 恰恰是 MLIR 想避免的(不用为每个硬件重写前端);D 描述的是统一字节码策略,与 MLIR 的多级表示无关。

Q2. 下列哪个工具负责按顺序执行一系列优化 pass?

单选题 · easy

  • A.mlir-opt 运行 pass 流水线← 正确答案
  • B.mlir-run 直接运行调试器
  • C.mlir-tune 自动搜索超参数
  • D.mlir-debug 逐行跟踪执行

答案:A。mlir-opt 是 MLIR 官方提供的 pass 执行器,专门跑优化与转换流水线,相当于转运中心的中控台。mlir-run、mlir-tune 并不是 MLIR 标准工具名称;调试通常靠 --debug-only 等选项而非 mlir-debug。

Q3. 关于 dialect(方言)的说法,正确的是哪一个?

单选题 · easy

  • A.每种方言都对应一门编程语言
  • B.方言定义操作类型与属性的集合← 正确答案
  • C.不同方言之间不允许相互转换
  • D.方言只能由官方团队新增扩展

答案:B。dialect 由 Operation、Type、Attribute 三要素定义,B 正确。A 混淆“方言”与“编程语言”;C 错误,方言之间正是靠转换规则协作;D 错误,MLIR 的设计目标就是让任何团队都能自定义方言。

Q4. Torch-MLIR 项目的主要职责是什么?

单选题 · mid

  • A.把 PyTorch 模型接入 MLIR 世界← 正确答案
  • B.把 MLIR 代码导出回 PyTorch
  • C.在 PyTorch 内部重写所有算子
  • D.替代 PyTorch 的自动求导引擎

答案:A。Torch-MLIR 把 torch 前端方言逐步合法化到 MLIR 高层方言,是“导入”方向,A 正确。B 方向反了;C 混淆了编译器前端与框架内部实现;D 与自动求导无关。

Q5. 在方言下降中,“合法化(legalization)”指的是什么?

单选题 · hard

  • A.让所有操作通过代码版权审核
  • B.确保转换结果符合目标方言规范← 正确答案
  • C.过滤掉模型中的非法数值输入
  • D.保证多次运行得到完全一致结果

答案:B。合法化是方言下降的验收标准:转换驱动反复应用 pattern,直到所有操作都落在目标方言的许可集合内,B 正确。A、C 是望文生义;D 说的是确定性,与合法化无关。

Q6. 关于 TOSA 方言,下列描述错误的是哪一项?

单选题 · mid

  • A.提供一套稳定且可移植的算子集合
  • B.作为框架与 MLIR 之间的中间层
  • C.专门描述 GPU 底层线程级指令← 正确答案
  • D.位于高层框架图与低层实现之间

答案:C。TOSA 是一套可移植的高层张量算子集合,通常作为框架与 MLIR 的中间层,位于高层图与低层实现之间,A、B、D 都正确。C 错误:TOSA 完全不涉及 GPU 底层线程级指令,那是 llvm 方言或目标后端的职责。

Q7. 循环分块(tiling)提升性能的最主要原因是什么?

单选题 · hard

  • A.减少浮点运算总量提高算术密度
  • B.提高数据局部性以利于缓存利用← 正确答案
  • C.将循环变量全部变成编译期常量
  • D.降低指令流水线中的分支预测失败

答案:B。分块把大循环切成小块,让正在处理的数据块能完整留在缓存中,核心收益是数据局部性,B 正确。A 错误,浮点运算总量没变;C 错误,循环变量仍是运行时变量;D 与分支预测关系不大。

Q8. mlir-translate 可以完成下列哪些任务?

多选题 · mid

  • A.将 MLIR 字节码编译成可执行文件
  • B.把外部格式导入为 MLIR 方言← 正确答案
  • C.把 MLIR 方言导出为外部格式← 正确答案
  • D.对 MLIR 循环结构执行自动分块

答案:BC。mlir-translate 负责 MLIR 与外部格式的双向互译,B(导入)和 C(导出)正确。A 错误,生成可执行文件还需要 LLVM 后端与链接器;D 是 mlir-opt 里 pass 的职责。

Q9. 下列哪些属于 MLIR 中常见的方言?

多选题 · mid

  • A.func 与 arith 等基础计算方言← 正确答案
  • B.linalg 结构化线性代数方言← 正确答案
  • C.tosa 可移植张量算子方言← 正确答案
  • D.numpy 张量库脚本化方言

答案:ABC。func、arith、linalg、tosa 都是 MLIR 内置常用方言,A、B、C 正确。D 的 numpy 方言在 MLIR 中并不存在,MLIR 没有“张量库脚本化方言”这一说。

Q10. 关于框架前端集成,下列说法正确的有哪些?

多选题 · hard

  • A.Torch-MLIR 把 torch 方言逐步合法化← 正确答案
  • B.TensorFlow 经 TOSA 或 MHLO 接入 MLIR← 正确答案
  • C.ONNX 模型可直接由 mlir-opt 执行优化
  • D.IREE 在 IR 层完成融合与内存规划← 正确答案

答案:ABD。Torch-MLIR 把 torch 方言逐步合法化,A 正确;TensorFlow 官方走 TOSA 或 MHLO 接入,B 正确;IREE 在 IR 层做算子融合与内存规划,D 正确。C 错误:ONNX 必须先导入为 MLIR 方言,mlir-opt 只读 .mlir,不能直接消费 ONNX。