1、简介
本文将实现一个用于统计LLVM IR中每个函数的二元运算的种类与数目的分析pass。
2、 文件创建
- llvm目录结构如下
进入到llvm-project/llvm/lib/Transforms目录,创建目录OpcodeCount,并在该目录下创建OpcodeCount.cpp和CMakeLists.txt两个文件。
3、CMakeLists.txt文件配置
- 在llvm-project/llvm/lib/Transforms/CMakeLists.txt中添加子目录OpcodeCount。
- 在llvm-project/llvm/lib/Transforms/OpcodeCount/CMakeLists.txt中添加如下代码,用于生成OpcodeCount.cpp文件对应的动态库。
add_llvm_library( LLVMOpcodeCount MODULE BUILDTREE_ONLY
OpcodeCount.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
4、OpcodeCount.cpp结构
- 定义当前pass的debug信息
#define DEBUG_TYPE "OpcodeCount"
STATISTIC(OpcodeCount, "Count number of opcode in all functions");
-
创建结构体Opcode,Opcode继承自FunctionPass。结构体中有两个成员变量:ID、opcodeCounter。ID为当前定义的pass的序列号,由llvm框架自动初始化。opcodeCounter用于记录每个二元运算的名称和其出现的次数。
-
同时,该结构体有两个方法Opcode()、bool runOnFunction(Function &F) 。其中Opcode()用于初始化ID, runOnFunction方法用于处理输入的ir代码。
5、runOnFunction方法实现
- 打印当前处理的函数名称。
errs()<< "Function "<< F.getName()<< '\n';
- 外层循环通过迭代器遍历当前函数的所有基本块,内层循环遍历每个基本块的每条指令,获取每条指令的二元运算的名称,并在map集合中修改二元运算出现的次数。
for(Function::iterator bb = F.begin(),e = F.end();bb!=e;++bb){
for(BasicBlock::iterator i = bb->begin(),e=bb->end();i!=e;++i){
if(opcodeCounter.find(i->getOpcodeName()) == opcodeCounter.end()){
opcodeCounter[i->getOpcodeName()] = 1;
}else{
opcodeCounter[i->getOpcodeName()] += 1;
}
}
}
- 遍历map集合,打印每个二元运算的名称和次数。每次分析完一个函数后调用clear()清空map,防止map数据对下一个函数的分析产生干扰。
std::map< std::string, int>::iterator i =opcodeCounter.begin();
std::map< std::string, int>::iterator e =opcodeCounter.end();
while (i != e){
llvm::outs() << i->first << ": " << i->second << "\n";
i++;
}
llvm::outs() << "\n";
opcodeCounter.clear();
return false;
6、在llvm框架中注册该pass
- 初始化ID。(这里的初始化ID是无效的,最终由llvm框架配置ID的值)
char Opcode::ID = 0;
- 注册pass。第一个参数是当前pass的名称,用于之后通过命令行调用该pass。第二个参数用于对当前pass进行说明。
static RegisterPass<Opcode>
X("opcodeCounter", "Count number of opcode in a functions");
7、生成动态库LLVMOpcodeCount.so
进入到llvm-project/build目录,运行ninja(如果是用ninja编译的llvm源码),将在目录llvm-project/build/lib下生成LLVMOpcodeCount.so动态链接库。
8、使用自定义pass分析test.ll文件
-
test.ll文件生成。编写test.c文件,运行命令clang -emit-llvm -S test.c -o test.ll生成test.ll文件。
test.c代码如下:
#include <stdio.h>
int func(int a,int b){
int c = a + b;
return c;
}
int main(){
int sum = 0;
for(int a = 0; a < 5;a++){
sum += a;
}
printf("%d",sum);
}
- 利用llvm的优化器调用自定义的pass。运行如下命令:
opt -load /opt/llvm-project/build/lib/LLVMFuncBlockCount.so -opcodeCounter -disable-output test.ll
- 运行结果如下。
9、OpcodeCount.cpp完整代码
#include "llvm/ADT/Statistic.h"
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include <map>
using namespace llvm;
#define DEBUG_TYPE "OpcodeCount"
STATISTIC(OpcodeCount, "Count number of opcode in a functions");
namespace {
struct Opcode : public FunctionPass {
static char ID;
std::map< std::string, int> opcodeCounter;
Opcode() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
errs()<< "Function "<< F.getName()<< '\n';
for(Function::iterator bb = F.begin(),e = F.end();bb!=e;++bb){
for(BasicBlock::iterator i = bb->begin(),e=bb->end();i!=e;++i){
if(opcodeCounter.find(i->getOpcodeName()) == opcodeCounter.end()){
opcodeCounter[i->getOpcodeName()] = 1;
}else{
opcodeCounter[i->getOpcodeName()] += 1;
}
}
}
std::map< std::string, int>::iterator i =opcodeCounter.begin();
std::map< std::string, int>::iterator e =opcodeCounter.end();
while (i != e){
llvm::outs() << i->first << ": " << i->second << "\n";
i++;
}
llvm::outs() << "\n";
opcodeCounter.clear();
return false;
}
};
}
char Opcode::ID = 0;
static RegisterPass<Opcode>
X("opcodeCounter", "Count number of opcode in a functions");