返回值决定ir修改状态:true触发分析失效和迭代终止,false导致陈旧分析引发错误;所有pass的run函数返回值语义统一,隐式修改(如replacealluseswith、setoperand)也需返回true。

runOnFunction 等 Pass 的 run 函数必须返回 bool,且语义明确:返回 true 表示 IR 被修改过,返回 false 表示未修改。
为什么返回值影响后续优化流程
LLVM Pass 管理器(PassManager)依赖这个返回值做两件事:
- 决定是否触发 CFG(控制流图)或分析结果的无效化 —— 若返回
true,所有依赖该函数结构的分析(如 DominatorTree、LoopInfo)会被自动标记为过期,下次需要时重新计算 - 影响 Pass 执行顺序和复用 —— 某些 Pass(如
InstructionCombiningPass)会反复运行直到收敛,每次运行后都检查返回值;若连续几次都返回false,就认为已稳定,停止迭代
常见错误:把打印调试当修改,却返回 false
很多新手写完日志输出就直接 return false,但实际已经调用了 F.getBasicBlockList().push_back(...) 或 Inst->eraseFromParent() —— 这类操作确实改变了 IR,却没告诉 PassManager。
后果是:
- 后续 Pass 读到 stale(陈旧)的分析结果,比如 LoopInfo 仍认为某循环存在,但你刚把它拆成了两个基本块
- 优化不生效或产生错误代码,尤其在组合多个 Pass 时(例如
opt -mem2reg -my-pass) - 调试时发现行为不一致:单独跑
my-pass没问题,加在管线里就 crash
不同 Pass 类型的 run 函数签名与返回逻辑
不是所有 run 函数都叫 runOnFunction,返回值含义一致,但参数和触发粒度不同:
-
ModulePass::runOnModule(Module &M):作用于整个模块,返回true表示任何函数、全局变量或 metadata 被改动 -
FunctionPass::runOnFunction(Function &F):只承诺该函数内 IR 变更,不影响其他函数 -
BasicBlockPass::runOnBasicBlock(BasicBlock &BB):仅对该基本块负责,改了 BB 内指令就得返回true -
AnalysisPass(如DominatorTreeWrapperPass):重写的是runOnFunction,但注册时第 4 个参数为true,此时返回值被忽略 —— 它不改 IR,只提供分析结果
最容易被忽略的边界情况
有些操作看似“只读”,实则隐式修改 IR:
- 调用
F.replaceAllUsesWith(...):明显改了 use-list,必须返回true - 调用
Inst->setOperand(0, NewVal):改了指令 operand,IR 已变 - 调用
Builder.CreateCall(...)插入新指令:哪怕只是 log,也新增了 Instruction,需返回true - 但
errs() 或 <code>F.empty()这类纯读取,可安全返回false
真正难判断的,是那些间接修改 —— 比如你调用了一个封装函数,它内部调用了 eraseFromParent() 却没文档说明。这时候别猜,看源码或加断点验证 IR 的 getNumInstructions() 是否变化。











