想象你是一位编译器领域的侦探。深夜两点,监控室里的报警器突然响了:一份刚刚生成的 MLIR 代码,在跑完一串优化 Pass 之后,竟然悄悄变成了一堆让人看不懂的“乱码”——操作数缺失、类型错配、循环结构被搅成一团。这就是你的案发现场。MLIR 的优化流水线就像一条装配线,每个 Pass 是工位上的一个师傅,IR(中间表示)是流水线上流转的工件。总有那么一个师傅手一抖,把工件搞坏了。而 MLIR 调试工具,就是你的侦探工具箱:能调监控录像,能采集指纹,能按暂停键回放现场,还能搭一个 3D 沙盘还原案发过程。本文会带着你把这个工具箱里的每件宝贝都摸一遍,让你从一个看着报错发呆的新手,成长为一眼锁定“凶手 Pass”的老侦探。
掌握 MLIR 调试工具链,意味着你在 AI 编译框架集成中不再是“被报错牵着走”的搬运工,而是能够快速定位、精准复现、系统解决编译问题的核心工程师。
可视化演示
三步展示MLIR调试:多级IR逐级降级(Tensor→Linalg→LLVM)、Pass管道执行与验证(每Pass后IR合法)、以及验证器/PassTiming/事件日志等诊断工具。
本节配有交互动效(进入视口后自动加载,约 3KB)。
多级IR降级 Tensor: 高层算子 Linalg: 中层线性代数 LLVM: 底层IR 多Dialect共存,逐级降级 bug可定位在IR转换或Pass执行环节。
调试工具概览
侦探接到报案后第一件事是什么?不是冲进现场瞎翻,而是站在门口,打开随身工具箱,盘算一下要带什么装备:录像机、指纹刷、录音笔、手电筒,每件工具对应一种勘察手段。MLIR 调试也是这样——先记住手里有什么牌,才能在对的时间打出对的那张。
MLIR 的调试兵器谱围绕 IR 的生命周期展开。mlir-opt 是主入口,相当于警车的钥匙,它允许你精确指定 Pass 流水线并观察 IR 变化;mlir-translate 负责跨越方言边界的转换,是通往 LLVM 后端的翻译官;构建期选项(如开启断言、MLIR_ENABLE_EXPENSIVE_PATTERN_API_CHECKS)则决定了你的工具箱里是只有基础装备,还是连指纹分析仪都有。这些工具共同构成了 IR 生成、变换、校验全流程的信息通道。
若要掌握调试节奏,关键是理解 IR 是逐级精化的“案件卷宗”。每道 Pass 读取上一级的 IR,产出下一级 IR,环环相扣:
记住这个链条,后面的每一节都是在教你如何在这条链上精准找到“变坏的那一环”。侦探打开工具箱、列好装备清单,接下来就是调监控录像了。
- 初始现场\\(\text{IR}_0\\) 是刚从前端生成、尚未优化的初始中间表示,相当于案发前的整洁房间。
- 首个变换\\(\text{Pass}_{P_1}\\) 代表第一道优化,它把 IR_0 改写为 IR_1,这是房间第一次被人动过。
- 流水叠加中间省略号表示后续 Pass 继续作用,每道 Pass 都只看到上一级的产物,像接力传话。
- 最终产物\\(\text{IR}_n\\) 是所有变换叠加后的结果,也是案发后的最终现场,可能面目全非。
IR转储分析
调查命案,第一件事是调取监控录像。案发走廊里有十几个摄像头,每个摄像头记录一个时间点。MLIR 中的 IR 转储(dump)就是这套监控系统:它能把每个 Pass 执行前后的 IR 状态以文本形式完整保存下来,让你像回放录像一样逐帧比对。
核心命令是 -mlir-print-ir-after-all,它在每个 Pass 运行完毕后打印一份 IR 快照——相当于每个摄像头都在录像。你也可以用 -mlir-print-ir-after=canonicalize 只关注某个特定 Pass,就像只调取走廊 3 号摄像头的画面。更精细的是 -mlir-print-ir-before 和 -mlir-print-ir-after-change,前者看 Pass 动手前,后者只在 IR 真的发生变化时才打印,避免无关帧刷屏。
拿到连续快照之后,侦探的工作就是对帧找差异。两份快照一前一后做 diff:某个 tensor 的 shape 为什么从 [2, 4] 变成了 [4, 2]?某个 arith.addi 的消费者为什么变成了 tosa.reshape?差异点就是案发第一现场。转储的本质,是把 Pass 封装成一个可观察的纯函数:
有了监控录像,你能看到案发全貌,但录像太多也会看花眼——几十个 Pass 就产生几十份快照。所以下一节,我们要看侦探的笔记,快速跳过无关帧。
- 变换前状态\\(\mathrm{IR}_{i-1}\\) 是第 i 道 Pass 的输入,也是监控里“动手前的那一刻”。
- 变换动作\\(\operatorname{Pass}_i\\) 对输入 IR 执行改写,产生新的 IR,这是案发过程本身。
- 固化为证据\\(\text{snapshot}_i = \text{to\_string}(\mathrm{IR}_i)\\) 把内存中的 IR 结构序列化为可读文本,方可供侦探比对分析。
Pass执行追踪
只有监控录像还不够,侦探还需要一本工作日志,记录每个时间段发生了什么:谁几点到过现场、停留多久、做了什么。MLIR 的 Pass 执行追踪就是这本日志,它关注执行过程本身,而不是 IR 内容。
-mlir-pass-statistics 输出每个 Pass 触发了多少次优化规则,相当于统计每个嫌疑人触碰过几次证物;-mlir-timing 记录每个 Pass 的耗时,谁在某个环节耗了异常久,大概率它做了不可告人的动作;-mlir-print-pass-pipeline 则把整条流水线打印出来,确认案发时都有哪些 Pass 在场。最有价值的是 -mlir-pass-pipeline-crash-reproducer,它能在一个 Pass 崩溃时自动生成最小复现命令——相当于案发后立即让现场保全,把当时在场的所有 Pass 名单封存起来。
日志的核心价值在于把不可见的多线程流水线变成可审计的时间线。无论是定位崩溃点,还是分析性能瓶颈,都要从日志找到异常环节。总耗时公式:
日志能告诉你“谁在什么时间干了什么”,但有时候,问题不在于 Pass 本身,而在于某个重写模式被错误地触发了。这就需要我们采集“指纹”——深入模式匹配引擎内部。
- 单环节耗时\\(T_i\\) 表示第 i 道 Pass 的执行时间,是时间线上的一个刻度。
- 累加求和\\(\sum_{i=1}^{n}\\) 把从第 1 道到第 n 道 Pass 的耗时全部加起来,相当于把整个案发过程完整走一遍。
- 总耗时\\(T_{\text{total}}\\) 是流水线整体耗时,也是日志里呈现给你的最终数字,帮助定位耗时异常的嫌疑 Pass。
优化诊断技术
侦探采集指纹,是希望证明某个人确实触碰过某个证物。MLIR 的 Pattern 重写引擎(Rewrite Pattern)就像指纹识别系统:每个优化规则是一个模式匹配器,命中 IR 中的某个子图就会触发重写。找 bug 时,你需要知道“哪个指纹被采集了、哪个该采的没采到”。
-debug-only=mlir-rewriter 打开重写器的调试输出,显示每个 Pattern 的匹配尝试与命中结果——这是指纹数据库的实时检索记录。-mlir-debug-counter 允许你限制某个 Pattern 最多触发 N 次,相当于对嫌疑人说“你只能再碰一次证物,让我看看你伸手到底想干什么”。当你发现一个优化没生效或错误生效时,-mlir-print-ir-after-change 让你只看到真正发生改动的 IR 版本,把注意力集中在指纹留下的印痕上。
此外,对比优化前后 IR 是判断 Pattern 是否“好心办坏事”的关键:fold 之后常数算对了没有?某个 linalg.generic 被融合后语义是否保持一致?这种对比能区分两种失败:Pattern 没触发(该破的案没破)和 Pattern 误触发(破了不该破的案)。当流水线很长时,要快速缩小指纹比对范围,可以用二分定位法:
指纹比对可以快速缩小范围,但要在“犯罪瞬间”看清嫌疑人到底做了什么动作,侦探还需要一个更硬核的手段——按暂停键,进入现场。
- 嫌疑数量\\(n\\) 是流水线中 Pass 的总数,即嫌疑人的个数。
- 对半砍\\(\log_2 n\\) 表示每次二分把搜索区间缩小一半所需的次数,例如 16 个 Pass 只需比较 4 次。
- 向上取整\\(\lceil \cdot \rceil\\) 保证即使 n 不是 2 的幂,次数也取整数。
断点调试技巧
监控录像再怎么清晰,也只是二维画面。真正审案时,侦探会在关键帧按下暂停,甚至进入现场实地勘验。GDB 之于 MLIR 调试,就是这样的暂停键与实景勘验工具:它可以让你在 Pass 源码的任意一行停下来,直接审视当下的 IR 数据结构。
典型场景是:你用 gdb --args mlir-opt input.mlir -pass-pipeline='builtin.module(func.func(convert-to-llvm))' 启动调试,然后在 mlir::OpRewritePattern::matchAndRewrite 处打断点,逐行跟踪某个 Pattern 的决策过程。门控断点(条件断点)是更高阶的技巧:当你只想在第 50 次匹配时停下,可以设置 break if counter == 50,避免每次匹配都中断,这正是“我在录像里看到你 18:42:07 伸手,我只想看你伸手的那一刻”。
除了断点,MLIR 也提供了“回退”视角:-mlir-print-ir-allowlist 可以限制只打印特定函数的 IR,配合断点可以分层隔离。在源码级调试中,你可以直接观察 Operation 的 operands、users、attributes 在内存中的真实形态,这是文本转储提供不了的“实景”。调试停止条件的逻辑可以抽象为:
断点调试让你深入源码,但 IR 毕竟是一张复杂的图,光靠文本和单步还是很难看到全貌。这时候,侦探需要把证物铺满整张桌子,搭一个沙盘模型——这就是可视化剖析的价值。
- 当前证据\\(\mathrm{IR}\\) 是被调试的中间表示对象,是侦探眼前的“当前画面”。
- 暂停条件\\(\text{cond}(\cdot)\\) 是一个布尔谓词,例如“当前 Pass 是 vectorize 且 IR 包含 unsupported 操作”。
- 触发暂停双箭头 \\(\iff\\) 表示“当且仅当”,谓词为真就按下暂停键,侦探停下脚步。
可视化剖析
侦探破获复杂案件时,会把所有照片、纸条、指纹卡贴在白板上,用红线连出关系网。MLIR 中的 IR 本质是一张图:操作是节点,操作数到使用者的连线是边。可视化工具就是你的案情白板——把抽象的文本 IR 变成直观的图形。
mlir-opt --view-op-graph 可以把当前 IR 输出为 DOT 图描述文件,再用 Graphviz 渲染成有向图。你会看到每个 tosa.conv2d、每个 linalg.matmul 都是白板上的一个卡片,依赖关系用箭头连成网络。对比优化前后的两张图,融合 Pass 是否把多个矩阵乘节点合并成了一个,一目了然。数据流追踪也随之清晰:某个 tensor 从哪来、被谁使用、形状如何逐层传播,都能沿着边追踪到底。
图形化视图与文本转储互补:文本适合精确 diff,图形适合感知结构。一个狡猾的 bug 可能藏在深层嵌套的 region 里,文本里只是一连串括号,但图上可能表现为一个节点被错误地悬空引用。IR 图可定义为:
沙盘拼好了,关系图画好了,接下来就是最后一个环节:把常见案件写进手册,下次再遇到长得差不多的犯罪手法,直接翻开手册对照处理。
- 节点集合\\(V\\) 是图中所有 Operation 的集合,即白板上的每一张卡片。
- 边集合\\(E\\) 是边的集合,每条边 \\((u, v)\\) 表示节点 u 的结果被节点 v 使用。
- 使用关系\\(\text{users}(u)\\) 返回所有消费 u 的 Operation,这是构建依赖边的规则。
常见问题排查
办过上百个案子之后,侦探会发现很多犯罪手法是重复的:锁被撬的痕迹、翻窗的脚印、惯犯的指纹。MLIR 调试也有一套“惯犯档案”,值得你一一击破。
第一类是 IR 非法崩溃。Pass 产出了违反约束的 IR,verifier 直接拒绝。应对方法是打开 -mlir-verify-diagnostics 或阅读 verifier 报错位置,快速定位是哪条约束被违反——这是格式和语义双保险。第二类是断言失败(assertion)。通常是开发者对某个前提假设过强,断言崩溃会直接告诉你“我以为这里是 X,实际是 Y”。第三类是输出静默异常——IR 看起来合法、Pass 顺利跑完,但最终结果不对。这时需要用 -mlir-print-op-generic 打印通用格式,确认是否有未知操作被静默保留。第四类是内存访问错误,多见于自定义 Pass 中悬空指针或 use-after-free,配合 AddressSanitizer 加栈回溯可以快速复现。
无论哪类问题,定位区间均可抽象为“最后好 Pass 与首个坏 Pass 之间的若干道 Pass”:
至此,侦探手册七大章节齐了:工具箱、监控、日志、指纹、暂停、沙盘、惯犯档案。从此 MLIR 报错对你不再是天书,而是一份递到你手上的破案线索单。
- 最后正常点\\(\text{last\_good}\\) 是最后一个产出合法 IR 的 Pass 编号,代表“目击者最后一次确认没问题”。
- 首个异常点\\(\text{first\_bad}\\) 是第一个产出非法 IR 的 Pass 编号,代表“案发后被第一次发现异常”。
- 锁定窗口\\(\text{bug} \in [\text{last\_good}, \text{first\_bad})\\) 指出凶手就在这个半开区间内,接下来只需重点审讯这几道 Pass。
AI 应用场景
大模型推理编译器调试
阶段:模型编译与优化
解决的问题:在将 HuggingFace 导出的大模型接入自研推理框架时,传统方式是靠肉眼比对优化前后的日志,遇到 shape 不匹配往往要排查数小时。通过 IR 转储与 Pass 追踪,将每个 Pass 后的 IR 快照自动存档并 diff,shape 突变点一秒锁定。
具体案例:某团队部署 Llama 3 8B 推理服务时,量化 Pass 后出现 tensor 维度错乱。工程师启用 -mlir-print-ir-after=quantize 对比量化前后 IR,立即发现 reshape 操作未被正确折叠,随后用 -mlir-pass-pipeline-crash-reproducer 生成最小复现,定位到 Pass 内一个 pattern 的 rank 判断错误。
算子融合优化验证
阶段:算子级优化与 Kernel 生成
解决的问题:手写推理引擎时,算子融合容易破坏中间结果的精度或语义。传统做法是逐个打印中间 tensor 比对数值,效率低。利用 MLIR 调试工具中的优化前后 IR 对比与图可视化,可以系统验证融合前后计算图是否等价。
具体案例:开发自研 NPU 算子库时,将 conv2d + bias_add + relu 融合成 conv2d_relu。通过 --view-op-graph 可视化融合前后计算图,确认融合后的节点依赖完整、无悬空引用,再结合 -mlir-timing 对比融合前后耗时,最终将单算子延迟降低 35%。
多后端部署流水线排障
阶段:跨后端代码生成
解决的问题:同一份模型在 CPU 上跑得好好的,切到 GPU 就崩溃。传统方式是在两套流水线上反复切换测试,靠加日志找差异。通过在两条流水线上分别做 Pass 追踪与转储对比,可以快速找出后端特化的 Pass 之间的行为差异。
具体案例:某团队适配 ROCm 后端时,bufferize Pass 在 GPU target 下崩溃。用 -mlir-pass-statistics 对比 CPU 与 GPU 的 Pass 触发统计,发现 buffer-deallocation 在 GPU 流水线中多执行了一次,导致 double-free。门控断点确认后,修正了流水线配置。
Python 演示
"""MLIRDebug 调试工具模拟器模拟 mlir-opt 流水线中定位"搞破坏的 Pass"的流程。核心思路:侦探破案 = 全量重放 + 二分缩小嫌疑范围。"""from dataclasses import dataclass, field @dataclassclass MLIRModule: """模拟一份极简 IR:一串 op 指令""" ops: list = field(default_factory=list) def clone(self): return MLIRModule(list(self.ops)) def __str__(self): return ", ".join(self.ops) if self.ops else "(empty)" class PassManager: """模拟 mlir-opt 的 pass 流水线""" def __init__(self, passes): self._passes = passes # 元素: (name, buggy?) self._logs = [] def run(self, module, stop_after=None): """按顺序跑 pass,返回各阶段快照和首个 bug 的 pass 编号""" snapshots = [(0, module.clone())] bug_index = None for i, (name, buggy) in enumerate(self._passes, start=1): self._logs.append("Running pass: " + name) if buggy: module.ops.append("CRASH_OP") # 模拟 pass 搞坏 IR bug_index = i break module.ops.append("new_" + name) if stop_after == i: break snapshots.append((i, module.clone())) return snapshots, bug_index, bug_index is not None def binary_locate(total, is_bad): """二分定位第一个坏 pass,is_bad(k) 表示跑完前 k 个 pass 后 IR 已坏""" lo, hi = 0, total while lo < hi: mid = (lo + hi) // 2 if is_bad(mid): hi = mid else: lo = mid + 1 return lo def main(): passes = [ ("shape-inference", False), ("canonicalize", True), # 这个 pass 是"凶手" ("fold-constants", False), ("vectorize", False), ("bufferize", False), ] pm = PassManager(passes) # 全量重放:发现 bug 出现在 2 号 pass module = MLIRModule(["main"]) snapshots, bug_idx, has_bug = pm.run(module) print("=== 案发现场(IR 转储快照)===") for idx, snap in snapshots: print("after pass " + str(idx) + ": " + str(snap)) if has_bug: print("IR 在 pass #" + str(bug_idx) + " 处被破坏!") # 二分侦查:检查第 k 个 pass 之后 IR 是否已含 CRASH_OP def is_bad(k): probe = MLIRModule(["main"]) for name, flag in passes[:k]: if flag: probe.ops.append("CRASH_OP") return True probe.ops.append("new_" + name) return False first_bad = binary_locate(len(passes), is_bad) print("二分定位:第一个破坏 IR 的 pass 是 #" + str(first_bad) + " (" + passes[first_bad - 1][0] + ")") print("提示:可配合 -mlir-print-ir-after=canonicalize 观察案发第一现场。") if __name__ == "__main__": main()
snapshots = [(0, module.clone())]保留初始 IR 快照,相当于案发前监控的第一帧。module.ops.append("CRASH_OP")模拟一个坏 Pass 往 IR 里注入非法操作,制造“案发现场”。if stop_after == i: break对应-mlir-print-ir-after按需截停流水线,只看某个 Pass 后的状态。def binary_locate(total, is_bad)二分定位函数,把“逐个排插”变成“对半砍”,大幅缩短排查时间。if is_bad(mid): hi = mid若第 mid 个 Pass 后 IR 已坏,说明凶手范围在左半边,收缩右边界。probe = MLIRModule(["main"])每次重放都从初始 IR 出发,模拟 mlir-opt 用固定输入做最小复现。"(" + passes[first_bad - 1][0] + ")"把 pass 编号翻译成名字,告诉侦探凶手姓甚名谁。if __name__ == "__main__": main()可执行入口,运行后直接输出完整破案报告。
常见误区
误区我以为 `-mlir-print-ir-after-all` 只是把 IR 打印出来给我看,纯只读操作,编译产物应该和不开完全一样,性能也不会有影响。
正解转储选项会改变 PassManager 的执行时机与调度细节,打印本身有 I/O 开销,输出量可能达到 GB 级,甚至导致原本不出现的时序类 bug 消失或复现。调试手段是用完必须关闭的“临时脚手架”,完成后要像拆掉案发现场的警戒线一样彻底移除。
为何常见:打印看起来是“向外输出”,容易误以为不影响程序内部状态;但调试工具往往改动执行路径,干扰现场。
误区我写的 Pass 跑完后 `mlir-opt` 没有报错,IR 也通过了合法性检查,那这个优化肯定没问题,可以放心合入代码了。
正解verifier 校验的是 IR 的结构合法性——类型匹配、use-def 成立、dominance 约束满足等。但“合法”不等于“与原 IR 语义等价”。一个 fold 把常量算错、一个 CSE 把有副作用的操作合并,都能产出完全合法的错误 IR。必须配合等价性测试或数值比对,才算真正破案。
为何常见:编译器报错是显式的“红灯”,没有报错容易给人一种“绿灯”的错觉,忽略了语义等价性是需要额外验证的独立维度。
误区遇到段错误或断言崩溃,我第一个想到的就是用 GDB 附加进程,然后慢慢找断点,感觉这样最专业也最彻底。
正解GDB 是最后手段而非第一选择。MLIR 自带的 -mlir-pass-pipeline-crash-reproducer 能自动生成最小复现命令,-mlir-print-stacktrace-on-crash 直接输出崩溃栈。先让工具自动圈定范围,再决定要不要深入源码,效率远高于一上来就手工打断点。侦探不会一进门就把整个房间翻个底朝天,而是先看监控。
为何常见:GDB 在传统 C/C++ 调试中代表着“硬核能力”,很多人形成路径依赖,忽略了编译器框架自带的智能调试接口。
误区我的 Pass 在单测里跑通了,IR 合法、结果正确,那这条优化流水线在任何输入下都应该没问题。
正解测试通过只能说明当前输入没问题。Pattern 重写引擎的行为跟 IR 的匹配形态强相关,换一个 input shape 或 dialect 组合,可能触发完全不同的代码路径。需要配合 debug counter 做随机化压力测试、用 crash reproducer 积累回归用例,才能真正建立信心。侦探不会因为一个案子破了就关闭警局。
为何常见:单测通过给开发者强烈的正反馈,“能跑”与“在各种输入下都能跑”之间的差距容易被低估。
面试题
Q1. MLIR 调试最常用的命令行工具是哪一个?
- A.mlir-opt 命令行工具← 正确答案
- B.mlir-translate 跨方言器
- C.mlir-as 汇编工具
- D.mlir-cpu-runner 执行器
答案:A。mlir-opt 是 MLIR 调试的主入口,用于加载 IR、指定 Pass 流水线并观察 IR 变化,相当于侦探的警车钥匙。mlir-translate 主要负责跨方言翻译,mlir-cpu-runner 侧重执行,都不是调试首选。
Q2. 想在每个 Pass 执行后都打印 IR 快照,应该选哪个选项?
- A.打印所有 Pass 后的 IR← 正确答案
- B.崩溃时打印调用栈
- C.统计 Pass 执行耗时
- D.统计 Pass 触发次数
答案:A。“打印所有 Pass 后的 IR”对应 -mlir-print-ir-after-all,是调监控录像的开关。崩溃栈、耗时统计、触发统计分别对应 stacktrace、timing、pass-statistics,它们记录的是事件而非 IR 内容。
Q3. 一条流水线上有 16 个 Pass,用二分法定位首个破坏 IR 的 Pass,最少需要几次检查?
- A.2 次检查
- B.3 次检查
- C.4 次检查← 正确答案
- D.16 次检查
答案:C。二分法每次把嫌疑区间砍一半,\\(\lceil \log_2 16 \rceil = 4\\) 次即可定位。这是侦探“对半砍嫌疑人”的策略:16 个嫌疑犯,问三次“前 8 个里有没有凶手”就能缩小到唯一一人,最后一次确认,共 4 次。
Q4. MLIR verifier 校验 IR 时,除了语法与类型,还会检查以下哪一项?
- A.源码注释是否充分
- B.use-def 与支配关系约束← 正确答案
- C.Pass 执行耗时是否超限
- D.输出文件体积是否超限
答案:B。verifier 检查 IR 的结构合法性,包括 use-def 链一致性、dominance 关系、操作数类型匹配等语义约束。注释充分性、执行耗时、文件体积都不属于 verifier 职责,前两者是代码质量指标,后者是工程指标。
Q5. 哪个功能可以在 Pass 崩溃时自动产出最小复现命令?
- A.自动生成最小复现命令← 正确答案
- B.打印每个 Pass 前状态
- C.限制 Pattern 触发次数
- D.关闭多线程执行
答案:A。-mlir-pass-pipeline-crash-reproducer 会在崩溃时输出一份可复现的最小命令,相当于案发后立即把现场保全下来。打印 Pass 前状态是转储,限制触发次数是 debug-counter,关闭多线程只是提升复现稳定性。
Q6. 在 GDB 中想“只让某个 Pattern 再触发一次就停下”,应该配合哪个工具?
- A.限制模式触发的计数器← 正确答案
- B.按模块范围打印转储
- C.关闭多线程以稳定复现
- D.输出诊断信息验证
答案:A。-mlir-debug-counter 可以设置某个 Pattern 的触发上限,正好配合 GDB 条件断点实现“下一次触发就暂停”。按模块打印是转储范围控制,关多线程是复现手段,诊断信息验证是另一条路,都不是“限制触发次数”。
Q7. mlir-opt --view-op-graph 会把当前 IR 输出为什么格式?
- A.DOT 图描述文件← 正确答案
- B.LLVM IR 文本
- C.JSON 语法树
- D.汇编代码文件
答案:A。--view-op-graph 输出 DOT 格式,供 Graphviz 渲染成有向图。侦探需要把证物贴到白板上连线,DOT 就是白板的文件格式。LLVM IR 是下游翻译产物,JSON/汇编都不是 MLIR 原生调试格式。
Q8. 下列哪些属于 MLIR 调试输出的信息通道?
- A.文本转储快照← 正确答案
- B.统计计数器← 正确答案
- C.执行时序报告← 正确答案
- D.机器码反汇编
答案:ABC。文本转储是 IR 快照,统计计数器是 pass-statistics,时序报告是 timing,这三个都是 MLIR 层级的调试通道。机器码反汇编属于后端(如 LLVM MC)产物,不是 MLIR 中间表示的调试信息。
Q9. 关于 IR 转储选项,下列说法正确的有哪些?
- A.可指定单个 Pass 名← 正确答案
- B.只打印发生变化的 IR
- C.改动后才打印转储← 正确答案
- D.打印所有 Pass 前状态← 正确答案
答案:ACD。A 对应 -mlir-print-ir-after=pass-name,可以精确指定;C 对应 -mlir-print-ir-after-change,只在 IR 被改动时输出;D 对应 -mlir-print-ir-before-all。B 错误,-mlir-print-ir-module-scope 控制的是打印范围(模块级),而非“只打印变化”。
Q10. 下列哪些是 MLIR 崩溃排查的常用手段?
- A.崩溃复现命令输出← 正确答案
- B.统计 Pass 执行时间
- C.打印调用栈回溯← 正确答案
- D.断言与内存检查← 正确答案
答案:ACD。崩溃复现命令(crash reproducer)、调用栈回溯(stacktrace)、断言与内存检查(assert/ASAN)都是直接针对崩溃的排查手段。统计 Pass 执行时间属于性能分析工具,与崩溃定位无关,适合性能调优而非正确性排障。