1. 理解LLVM IR基础结构

理解LLVM IR基础结构 学习卡片

任务目标

编写一个基于LLVM的C++工具,读取指定的LLVM IR文本文件(.ll),解析后统计并打印模块、函数、基本块和指令的数量。该工具用于建立对LLVM IR层级结构(Module → Function → BasicBlock → Instruction)的直观认识。

环境与依赖

  • LLVM(≥ 15.0),需包含核心库与IR读取库
  • C++17 编译器
  • CMake ≥ 3.13

核心实现

// ir_stats.cpp
#include <llvm/IR/Module.h>
#include <llvm/IR/Function.h>
#include <llvm/IR/BasicBlock.h>
#include <llvm/IR/Instruction.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/IRReader/IRReader.h>
#include <llvm/Support/SourceMgr.h>
#include <llvm/Support/raw_ostream.h>
#include <llvm/Support/CommandLine.h>

using namespace llvm;

static cl::opt<std::string> InputFile(cl::Positional, cl::desc("<input .ll file>"), cl::init("-"));

int main(int argc, char **argv) {
    cl::ParseCommandLineOptions(argc, argv, "LLVM IR structure statistics tool\n");

    LLVMContext Context;
    SMDiagnostic Err;

    // 从 .ll 文件解析 IR
    std::unique_ptr<Module> M = parseIRFile(InputFile, Err, Context);
    if (!M) {
        Err.print("ir_stats", errs());
        return 1;
    }

    unsigned func_count = 0, bb_count = 0, inst_count = 0;

    for (Function &F : *M) {
        if (F.isDeclaration()) continue;  // 跳过声明,只统计有实现的函数
        func_count++;
        for (BasicBlock &BB : F) {
            bb_count++;
            inst_count += BB.size();
        }
    }

    // 输出统计结果
    outs() << "Module: " << M->getName() << "\n";
    outs() << "Functions (with body): " << func_count << "\n";
    outs() << "Basic Blocks: " << bb_count << "\n";
    outs() << "Instructions: " << inst_count << "\n";

    return 0;
}

构建与运行

# CMakeLists.txt
cmake_minimum_required(VERSION 3.13)
project(ir_stats)
find_package(LLVM REQUIRED CONFIG)
add_executable(ir_stats ir_stats.cpp)
target_include_directories(ir_stats PRIVATE ${LLVM_INCLUDE_DIRS})
target_compile_definitions(ir_stats PRIVATE ${LLVM_DEFINITIONS})
target_link_libraries(ir_stats PRIVATE LLVMIRReader LLVMCore LLVMSupport)
# 构建
mkdir build && cd build
cmake ..
make

# 运行(假设已有 test.ll)
./ir_stats test.ll

验证方法

准备一个简单的测试文件 test.ll:

define i32 @add(i32 %a, i32 %b) {
entry:
  %sum = add i32 %a, %b
  ret i32 %sum
}
define void @main() {
entry:
  %r = call i32 @add(i32 1, i32 2)
  ret void
}

运行 ./ir_stats test.ll,预期输出:

Module: test.ll
Functions (with body): 2
Basic Blocks: 2
Instructions: 4

判定标准:输出与上述预期一致(模块名可能包含路径前缀),说明工具正确解析了IR的层级结构。若将 F.isDeclaration() 的过滤移除,则声明函数也会被计入,可自行对比观察差异。