1. 为什么多数优化器以 IR(如 LLVM IR、GCC GIMPLE)而非机器码作为主要优化对象?
为什么多数优化器以 IR(如 LLVM IR、GCC GIMPLE)而非机器码作为主要优化对象?IR 优化相比机器码层优化有何优势?
- IR 保留高层语义(类型、变量、控制流结构),优化规则简单通用
- 与目标无关:一份 IR 优化覆盖多架构,机器码优化需逐目标实现
- 多次降级(lowering)与 O0/O1/O2/O3 档位控制
优化器选择 IR 而非机器码作为主战场的原因有三层。第一,IR 保留高层语义:LLVM IR 中的变量、类型、SSA 值、phi 节点、内存访问(load/store)与调用关系直接反映程序结构,常量传播、死代码消除、内联等 pass 可以在"抽象数据流"层面以简单通用的规则实现;机器码则是寄存器、寻址、调度交织的底层表示,同一程序逻辑的机器码形态随架构千变万化,规则需逐目标特化。第二,目标无关性:优化在 IR 上做一次,多个后端(x86/ARM/RISC-V/GPU)共享成果;若在机器码上优化,每个新目标都要重新实现全套 pass。第三,可控制性与调试性:IR 与源码结构对应清晰(可打印、可验证),pass 顺序与档位(-O2)可精细控制,而机器码层只做目标特有优化(指令选择、寄存器分配、指令调度、peephole)。
IR 优化的代价:IR 层看不到目标特有信息(寄存器压力、指令延迟、缓存行为),因此 IR 优化后仍需目标相关 pass 收尾(LLVM 的 SelectionDAG/GlobalISel 层做指令选择与调度);且"高到低"的多次降级(IR→SDAG→MachineInstr→MCInst)本身有开销。现代编译器(LLVM/GCC)的实际做法是分层:高层优化在 IR(GIMPLE/LLVM IR)完成,中低层优化(寄存器分配、调度、重排)在机器相关表示完成,两者互补——这正是"为什么优化器以 IR 为主"而不"只用机器码"的完整答案。
从"语义保留、目标无关、可控可调试"三个动机解释 IR 主战场地位,再补充机器码层 pass 的必要性,形成"分层优化"的完整认知。