llvm pass插件必须集成到llvm源码树中构建,不可脱离源码树独立编译;须置于llvm/lib/transforms/目录下,使用llvm自定义cmake宏(如add_llvm_library),并确保cmake配置指向llvm源码根目录,否则会因找不到宏或头文件而失败。

LLVM Pass 插件不能脱离 LLVM 构建树单独 CMake
直接在空目录下跑 cmake . 或用系统安装的 LLVM 头文件和库来编译 Pass,99% 会失败。LLVM Pass 不是普通动态库,它依赖 LLVM 内部符号导出规则、构建时生成的头文件(如 Intrinsics.inc)、以及特定的链接组件顺序。官方不支持“独立编译”,只支持作为 LLVM 源码树的一部分构建。
必须放在 llvm/lib/Transforms/ 子目录下构建
这是最常被跳过的硬性前提。Pass 的 CMakeLists.txt 里写的 add_llvm_library(LLVMMyPass MODULE ...) 是 LLVM 自定义宏,只有在 LLVM 源码根目录下执行 cmake 后,这些宏才被注册。如果你把 MyPass 文件夹挪到桌面或任意外部路径,cmake 会报错找不到 add_llvm_library。
- 正确路径示例:
llvm-project/llvm/lib/Transforms/MyPass/ - 必须包含三个文件:
CMakeLists.txt、MyPass.cpp、MyPass.exports(后者控制符号可见性,缺了在 macOS/Linux 下可能加载失败) -
CMakeLists.txt中add_llvm_library的第一个参数(如LLVMMyPass)将决定最终生成的 so/dylib 文件名,也是opt -load时要指定的路径名
cmake -G Ninja 构建时必须指向 LLVM 源码根目录
构建目录(如 llvm-build)本身不包含源码,它只是缓存中间产物。CMake 配置阶段必须通过 cmake -G Ninja ../llvm 显式告诉构建系统 LLVM 源码在哪,否则无法解析 add_llvm_library、找不到 llvm-config、也无法定位 LLVMHello 这类参考模板。
CMake 4.3.2 Windows x86_64 历史版本安装包,适合旧项目兼容、构建环境回退、CMakeLists.txt 迁移验证、Visual Studio/Ninja/Makefile 生成器测试和 C/C++ 项目维护。
- 错误做法:
cd MyPass && cmake .→ 报错Unknown CMake command "add_llvm_library" - 正确流程:在
llvm-build目录执行cmake -G Ninja ../llvm -DCMAKE_BUILD_TYPE=Debug -DLLVM_ENABLE_PROJECTS="clang" - 生成后只需运行
ninja LLVMMyPass即可单独编译该插件,无需重编整个 LLVM - 生成的插件路径通常是:
llvm-build/lib/LLVMMyPass.so(Linux)或llvm-build/lib/libLLVMMyPass.dylib(macOS)
加载插件时 -load 路径必须精确匹配文件名
即使编译成功,opt -load ./LLVMMyPass.so -mypass input.bc 仍可能报错 Could not load library。这不是权限问题,而是 LLVM 对插件路径做了一层 basename 匹配,并校验导出符号前缀。
- 确保
opt使用的是你刚编译出来的版本(不是系统/usr/local/bin/opt),可用llvm-build/bin/opt --version确认 -
-load参数值必须是完整路径,不能是相对路径(尤其在脚本中容易忽略) - 插件内部必须导出
llvm::PassPluginLibraryInfo全局变量,且其name字段要与命令行中-mypass一致(大小写敏感) - macOS 上 dylib 缺少
-install_name可能导致dlopen失败,建议在CMakeLists.txt中加set_target_properties(LLVMMyPass PROPERTIES INSTALL_NAME_DIR "@rpath")
最容易被忽略的一点:LLVM Pass 插件的生命周期由 opt 或 clang 控制,不是你 dlopen 后就能随便调用的独立模块。它的入口、注册、运行时机全由 LLVM 的 PassManager 调度——这意味着你不能把它当普通共享库去链接或单元测试,调试必须走 opt -debug-pass=Structure 或在 runOnFunction 里打日志。










