2. IR指令分类统计

任务说明

本案例要求实现一个 LLVM FunctionPass,遍历函数内所有指令,按指令类别(算术、内存、控制流、其他)统计数量,并在每次函数处理结束后输出统计报告。该 Pass 可用于快速了解 IR 中各类操作的分布情况,辅助优化决策或教学演示。

核心实现

// InstCategory.cpp
#include "llvm/IR/Function.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/InstVisitor.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
using namespace llvm;

namespace {
struct InstCategory : public FunctionPass {
  static char ID;
  InstCategory() : FunctionPass(ID) {}

  bool runOnFunction(Function &F) override {
    unsigned arith = 0, mem = 0, ctrl = 0, other = 0;

    for (BasicBlock &BB : F) {
      for (Instruction &I : BB) {
        if (isa<BinaryOperator>(I) || isa<CastInst>(I) ||
            isa<CmpInst>(I) || isa<SelectInst>(I) ||
            isa<PHINode>(I) || isa<ExtractElementInst>(I) ||
            isa<InsertElementInst>(I) || isa<ShuffleVectorInst>(I)) {
          ++arith;
        } else if (isa<LoadInst>(I) || isa<StoreInst>(I) ||
                   isa<AllocaInst>(I) || isa<GetElementPtrInst>(I) ||
                   isa<FenceInst>(I) || isa<AtomicCmpXchgInst>(I) ||
                   isa<AtomicRMWInst>(I)) {
          ++mem;
        } else if (isa<TerminatorInst>(I)) {
          ++ctrl;
        } else {
          ++other;
        }
      }
    }

    errs() << "Function: " << F.getName() << "\n";
    errs() << "  Arithmetic: " << arith << "\n";
    errs() << "  Memory:     " << mem << "\n";
    errs() << "  Control:    " << ctrl << "\n";
    errs() << "  Other:      " << other << "\n";
    errs() << "  Total:      " << (arith + mem + ctrl + other) << "\n";
    return false; // 不修改 IR
  }
};
} // namespace

char InstCategory::ID = 0;
static RegisterPass<InstCategory> X("inst-category",
                                    "IR Instruction Category Statistics",
                                    false /* Only looks at CFG */,
                                    false /* Analysis pass */);

构建与运行

  1. 编译 Pass(以 LLVM 15+ 为例):
clang++ -shared -fPIC -o libInstCategory.so InstCategory.cpp \
    $(llvm-config --cxxflags --ldflags --libs)
  1. 生成待分析的 IR 文件(示例输入 test.c):
int sum(int n) {
  int s = 0;
  for (int i = 0; i < n; ++i)
    s += i;
  return s;
}
clang -S -emit-llvm test.c -o test.ll
  1. 运行 Pass 并观察输出:
opt -load-pass-plugin=./libInstCategory.so -passes=inst-category test.ll -disable-output

验证方法

运行上述命令后,标准错误输出应显示每个函数的分类统计。以 sum 函数为例,预期输出(由代码逻辑直接推出)为:

Function: sum
  Arithmetic: 5
  Memory:     3
  Control:    3
  Other:      0
  Total:      11

验证要点:

  • 算术类包含 add、icmp、zext 等指令;
  • 内存类包含 alloca、load、store;
  • 控制流类包含 br 和 ret;
  • 总数与 opt -passes=inst-count 或 llvm-dis 人工计数一致。

若输出与预期不符,请检查 IR 中实际指令序列(cat test.ll)并核对分类逻辑。

常见问题

  • TerminatorInst 已废弃:LLVM 15+ 中应使用 isa<BranchInst>(I) || isa<ReturnInst>(I) || ... 或直接判断 I.isTerminator()。本代码为兼容旧版使用 TerminatorInst,若编译报错请改用 I.isTerminator()。
  • 统计粒度:本 Pass 按函数统计,如需模块级汇总可在外层 ModulePass 中累加。
  • 不修改 IR:返回 false 表示未改变函数内容,保证 Pass 可安全用于分析链。