mir是llvm后端中寄存器分配前短暂存在的内存中间表示,不可直接保存重放;导出需用llc配合-mirc-print-after或-start-before/-stop-after生成.mir文件,但该文件依赖targetmachine状态且无abi上下文,无法被llc重新输入执行。

MIR 本身不是可直接保存/重放的“快照”,而是 LLVM 后端流水线中一个短暂存在的、内存中的中间表示。 它在 SelectionDAG 之后、MachineInstr 之前生成,只存在于寄存器分配前的优化阶段(如 mir-opt、regalloc 前的 pass)。你不能像保存 IR 那样用 llc -S 直接输出 MIR 文本并指望它被原样重放——LLVM 没有内置的 “MIR replay” 执行引擎。
怎么导出 MIR 文本用于调试或分析
LLVM 提供了 -print-machineinstrs 和 -mirc-print-after-all 等调试开关,但它们只打印到 stderr,不生成可复用文件。真正能落地为可读、可检查的 MIR 文本的方式是:
- 用
llc -save-temp-labels -mirc-print-after=regbankselect(或其它合法 MIR pass 名,如legalizer、phielem)配合-o /dev/null,再重定向 stderr 到文件; - 更稳定的做法是启用
-start-before=regbankselect -stop-after=regbankselect,此时llc会在该 pass 后把当前 MIR 写入.mir文件(需配合-debug-pass-manager或观察日志确认路径); - 注意:生成的
.mir文件默认不含函数体外的全局定义(如define、@g = global),它只包含machine function节点,且依赖当前 target 的TargetMachine初始化状态(比如寄存器类、指令定义); - 导出的 MIR 不是独立可执行格式,它没有 ABI、调用约定、栈帧布局等上下文,仅反映某次编译中某个 pass 后的机器级 SSA 形式。
为什么不能直接用 .mir 文件重新触发代码生成
因为 MIR 是 LLVM 后端内部的“工作语言”,不是设计为输入接口的格式。关键限制包括:
-
MIRParser只在测试和调试中使用,不参与正式后端流水线;llc不接受.mir作为输入源(会报错unrecognized file type); - MIR 中大量引用
TargetInstrInfo、TargetRegisterInfo等运行时对象,这些在解析 MIR 时无法重建; - 同一段 MIR 在不同 LLVM 版本、不同
-mcpu/-mattr下可能因指令合法化规则变化而无法通过legalizer; - 缺失 CFG 连接信息(如
bb.<n></n>的 successor 列表有时被省略)、缺少隐式物理寄存器定义(如%RAX是否 live-in)等细节,导致重放时调度或分配失败。
替代方案:保存 IR + 复现完整后端流程
如果你目标是“重放生成过程”,实际可行路径是保留源头和控制点:
- 保存原始
.ll(LLVM IR)和完整命令行(含-mtriple、-mcpu、-mattr、-O2等所有 flag); - 用
llc -debug-pass-manager -debug-only=machineverifier记录每步 MIR 变化,比单个 .mir 更具可追溯性; - 若需修改 MIR 并验证效果,可用
llvm-mir-opt(来自llvm-project/utils/mir-opt)对 .mir 做局部变换,但它不驱动后续后端,只能辅助分析; - 真要“重放”,唯一可靠方式是重新跑
llc—— 因为 MIR 的语义绑定在TargetMachine实例生命周期内,脱离这个上下文就失去意义。
最常被忽略的一点:MIR 的文本格式本身不稳定。LLVM 不承诺 .mir 文件向后兼容,哪怕 patch 版本升级也可能导致 parser 失败。别把它当配置文件存,只当一次性诊断快照用。











