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是否与命令行参数一致
  • 无输出:确认输入文件包含函数定义,且未被优化掉