-opt -passes参数必须用逗号分隔,空格或换行会导致后续pass被忽略;仅llvm 10+支持,顺序即执行顺序,不支持重复名,需查opt -help-list-only=optimizations确认可用名,不可与旧flag混用。

opt -passes 参数必须用逗号分隔,不能空格或换行
直接写 -passes="instcombine,deadargelim,dce" 才生效;写成 -passes="instcombine deadargelim dce" 或分行、加引号外空格都会被 opt 忽略中间部分,只执行第一个 pass。
这是最常踩的坑:很多人复制文档示例时没注意引号内格式,结果以为 pass 没起作用,其实是语法解析失败导致后续 pass 被丢弃。
-
-passes是 LLVM 新 Pass 管理器(PM)的接口,从 LLVM 10+ 开始逐步替代旧的-instcombine这类独立 flag - 顺序即执行顺序,比如
mem2reg,instcombine,gvn中mem2reg必须在instcombine前,否则后者可能无法合并因未提升而残留的 load/store - 不支持重复 pass 名——
instcombine,instcombine不会运行两次,只执行一次
哪些 pass 名字能用,得查当前 LLVM 版本支持的列表
opt -help-list-passes 输出的是“分析类 pass”为主,真正可用的优化 pass 名需要运行 opt -help-list-only=optimizations(LLVM 14+)或 opt -O2 -debug-pass=Structure 看实际 pipeline 构成。
常见可用名包括:mem2reg、instcombine、gvn、simplifycfg、loop-simplify、licm,但像 inline 实际对应的是 function-attrs,inline 组合,单独写 inline 会报错 “Pass 'inline' is not registered”。
- 函数级 pass(如
instcombine)默认作用于每个函数;模块级 pass(如globaldce)需确保它在 pipeline 中处于合适层级 - 有些 pass 有隐式依赖,例如
gvn要求 IR 已做mem2reg和simplifycfg,否则可能跳过优化或触发断言失败 - LLVM 17+ 开始,
-passes支持嵌套语法,如default<o3>,loop-vectorize</o3>,但自定义组合仍建议显式列出
和旧式 -flag 写法混用会出问题
不能同时用 -passes 和 -instcombine 这类老参数。一旦出现 -passes,opt 就完全切换到新 PM 模式,所有传统 flag(如 -dce、-inline)会被静默忽略,不报错也不执行。
比如这条命令:opt -passes="dce" -dce input.ll,只有 dce 在 -passes 里生效,后面的 -dce 完全无效——这不是 bug,是设计如此。
- 调试时可用
-debug-pass=Arguments查看最终解析出的 pass 列表 - 若要保留旧习惯,坚持用单 flag,就别碰
-passes;想用 pipeline 控制力,就得彻底放弃-instcombine等独立选项 - Clang 编译时加
-mllvm -passes=...也能透传,但要注意 Clang 自身插入的默认 pass 会与你指定的叠加,顺序不可控
验证组合是否真按预期运行了
光看输出 IR 不够,得用 -verify-each 或 -debug-pass=Executions 确认每个 pass 是否被调用。特别是 gvn、licm 这类条件性强的 pass,IR 不变不代表没运行,可能只是没找到优化点。
-
opt -passes="instcombine,gvn" -verify-each input.ll 2>&1 | grep "GVN"可确认 GVN 是否触发 - 加
-stats能看到各 pass 修改的指令数,比肉眼查 IR 更可靠 - 如果某 pass 总不生效,先检查输入 IR 是否满足前提:比如
loop-vectorize要求循环已做loop-simplify和indvars,否则直接跳过
真正麻烦的不是怎么写,而是每个 pass 对 IR 形态有隐式要求,组合前得清楚它们的输入契约——这没法靠试错,得查对应 Pass 的源码注释或 llvm/lib/Transforms/ 下的文档片段。











