llvm ir是前端生成的稳定、可优化的ssa中间表示,位于ast之后、优化器之前;mir是后端指令选择后生成的含目标平台信息的低层调试表示,仅在内存中流转且不可跨架构复用。

LLVM IR 出现在前端到优化器之间
LLVM IR 是前端(如 clang)输出的第一种稳定、可优化的中间表示,它位于词法/语法分析、AST 构建之后,优化流水线开始之前。所有前端(C/C++/Rust/…)最终都必须生成合法的 LLVM IR(通常是 .ll 文本格式或 bitcode 二进制格式),才能进入后续通用优化阶段。
关键点:
- IR 是 SSA 形式,变量只赋值一次,便于做常量传播、死代码消除等通用优化
- IR 层不绑定任何目标架构,
llc或opt都能直接读取和处理它 - 常见错误:前端生成非法 IR(比如未定义的
%val引用、类型不匹配的store),会导致llc在指令选择前就报错Invalid operand for instruction
MIR 出现在后端代码生成早期,紧贴目标机器
MIR(Machine IR)不是前端产物,而是 llc 启动后端流程后,在完成指令选择(Instruction Selection)之后、寄存器分配(Register Allocation)之前生成的一种低层中间表示。它已经携带了目标平台信息:寄存器名(如 %rax)、物理寄存器约束、帧偏移、调用约定细节等。
典型路径:LLVM IR → SelectionDAG → MachineInstr → MIR (.mir 文件)
你只有显式启用 -mllvm -print-machineinstrs 或用 llc -debug-only=asmprinter 才能看到 MIR;它默认不落地为文件,仅在内存中流转。MIR 的主要用途是调试后端行为,比如检查某条 add 是否被正确 lowering 成 ADD64rr,或者确认 spill 指令是否插入在预期位置。
- MIR 不是跨平台的——
x86_64的MIR无法喂给ARM后端 - 它比 MachineInstr 更“可读”,但比 LLVM IR 更难手写或修改,因为寄存器生命周期、stack slot 分配已部分固化
- 常见误用:试图用
opt处理.mir文件——会失败,因为opt只认 LLVM IR
为什么不能把 MIR 当成“更底层的 IR”来复用
MIR 和 LLVM IR 定位完全不同:前者是后端内部调试视图,后者是模块化编译器的契约接口。LLVM IR 被设计成可长期保存、跨工具链传递(例如 LTO、ThinLTO、bitcode in iOS app)、支持多轮优化;而 MIR 是瞬态数据结构,仅服务于单次后端执行,没有稳定的 ABI 或序列化规范。
- LLVM IR 的
Module类型可在不同 pass 间安全共享;MIR 的MachineFunction绑定具体TargetMachine实例,不可跨 target 复用 - 想做跨架构优化?必须在 LLVM IR 层做,而不是等落到 MIR 再动手
- 自定义后端时,你可以重写
SelectionDAG到MachineInstr的逻辑,但绕不开 MIR 所依赖的TargetInstrInfo和TargetRegisterInfo等平台专属类
llc --help 都不提它的名字。真正需要它的时候,往往是因为你在调试一条诡异的寄存器冲突,或发现某处 stack frame 布局和预期不符。这时候翻出 .mir 输出,比对着汇编一行行猜要快得多。











