2. 使用opt工具运行Pass
任务目标
编写一个LLVM分析Pass,统计函数基本块数量,并通过opt工具加载运行,验证Pass能够被正确识别并输出统计结果。
环境准备
需要LLVM开发环境(版本≥14),确保opt和llvm-config可用。使用CMake构建Pass插件,生成动态库供opt加载。
Pass实现代码
创建BlockCountPass.cpp,实现一个继承FunctionPass的分析Pass:
```cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Instructions.h"
using namespace llvm;
namespace {
struct BlockCountPass : public FunctionPass {
static char ID;
BlockCountPass() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
unsigned blockCount = 0;
for (BasicBlock &BB : F) {
blockCount++;
// 统计每条基本块中的指令数
unsigned instCount = std::distance(BB.begin(), BB.end());
errs() << " BasicBlock #" << blockCount
<< " (instructions: " << instCount << ")\n";
}
errs() << "Function '" << F.getName()
<< "' has " << blockCount << " basic blocks\n";
return false; // 分析Pass不修改代码
}
};
}
char BlockCountPass::ID = 0;
static RegisterPass
X("block-count",
"Count basic blocks in a function", false, true);
```
构建配置
创建CMakeLists.txt:
```cmake
cmake_minimum_required(VERSION 3.13)
project(BlockCountPass)
find_package(LLVM REQUIRED CONFIG)
message(STATUS "Found LLVM ${LLVM_PACKAGE_VERSION}")
include_directories(${LLVM_INCLUDE_DIRS})
add_library(BlockCountPass MODULE BlockCountPass.cpp)
target_link_libraries(BlockCountPass PRIVATE ${LLVM_LIBRARIES})
```
编译与运行
按以下步骤构建并测试:
```bash
# 1. 构建Pass插件
mkdir build && cd build
cmake .. -DLLVM_DIR=$(llvm-config --cmakedir)
make
# 2. 生成测试用LLVM IR文件
cat > test.ll << 'EOF'
define i32 @add(i32 %a, i32 %b) {
%sum = add i32 %a, %b
ret i32 %sum
}
define i32 @max(i32 %x, i32 %y) {
%cmp = icmp sgt i32 %x, %y
br i1 %cmp, label %then, label %else
then:
ret i32 %x
else:
ret i32 %y
}
EOF
# 3. 运行Pass
opt -load ./BlockCountPass.so -block-count < test.ll
```
验证方法
运行后观察标准错误输出,应看到:
- 对
add函数输出1个基本块,包含2条指令
- 对
max函数输出3个基本块(入口、then、else),每个块包含1条指令
- 每行输出格式为
BasicBlock #N (instructions: M),函数统计行以Function 'xxx' has N basic blocks结尾
若输出与上述结构一致,说明Pass被正确加载并执行。注意opt默认不输出IR,仅Pass的errs()信息会显示;如需保留IR可加-S参数。
常见问题
- 加载失败:确认
-load路径正确,且LLVM版本与编译时一致
- 未执行Pass:检查Pass注册名
block-count是否与命令行参数一致
- 无输出:确认输入文件包含函数定义,且未被优化掉