我主要是学习修改,而不是学习LLVM IR
所以我先学习的是LLVM的pass
pass分analysis pass, transform pass和Utility Passes。
pass有很多种类,用法也不同,详细的可以看官方说明http://llvm.org/docs/Passes.html#introduction
先尝试自己编写一个transform pass,具体也可以看官方教材http://llvm.org/docs/WritingAnLLVMPass.html
在/llvm/lib/Transforms/下面有很多文件夹,这些是LLVM自带的pass,比如官方例子Hello。
先复制整个Hello文件夹,修改名称为Readd
接着修改CMakeLists.txt,增加一行,作用是编译时候编译Readd文件夹
add_subdirectory(Readd)
进入Readd文件夹,可以看到三个文件。
先修改CMakeList.txt,将里面的hello改成Readd
if( NOT LLVM_REQUIRES_RTTI )
if( NOT LLVM_REQUIRES_EH )
set(LLVM_EXPORTED_SYMBOL_FILE ${CMAKE_CURRENT_SOURCE_DIR}/Readd.exports)
endif()
endif()
if(WIN32 OR CYGWIN)
set(LLVM_LINK_COMPONENTS Core Support)
endif()
add_llvm_loadable_module( LLVMReadd
Readd.cpp
DEPENDS
intrinsics_gen
PLUGIN_TOOL
opt
)
主要的是修改hello.cpp,先修改文件名为Readd.cpp。这里面是pass的功能,是将加法改成减去被加数的负数。
#include "llvm/IR/Function.h"
#include "llvm/Pass.h"
#include "llvm/Support/raw_ostream.h"
#include "llvm/IR/Intrinsics.h"
#include "llvm/IR/Instructions.h"
using namespace llvm;
#define DEBUG_TYPE "readd"
namespace {
struct readd : public FunctionPass {
static char ID; // Pass identification, replacement for typeid
readd() : FunctionPass(ID) {}
bool runOnFunction(Function &F) override {
Function *tmp = &F;
// 遍历函数中的所有基本块
for (Function::iterator bb = tmp->begin(); bb != tmp->end(); ++bb) {
// 遍历基本块中的每条指令
for (BasicBlock::iterator inst = bb->begin(); inst != bb->end(); ++inst) {
// 是否是add指令
if (inst->isBinaryOp()) {
if (inst->getOpcode() == Instruction::Add) {
ob_add(cast<BinaryOperator>(inst));
}
}
}
}
return false;
}
// a+b === a-(-b)
bool ob_add(BinaryOperator *bo) {
BinaryOperator *op = NULL;
if (bo->getOpcode() == Instruction::Add) {
// 生成 (-b)
op = BinaryOperator::CreateNeg(bo->getOperand(1), "", bo);
// 生成 a-(-b)
op = BinaryOperator::Create(Instruction::Sub, bo->getOperand(0), op, "", bo);
op->setHasNoSignedWrap(bo->hasNoSignedWrap());
op->setHasNoUnsignedWrap(bo->hasNoUnsignedWrap());
}
// 替换所有出现该指令的地方
bo->replaceAllUsesWith(op);
}
};
}
char readd::ID = 0;
static RegisterPass<readd> X("readd", "Readd Pass");
第三个文件是Hello.exports,修改名称为Readd.exports就行,这个我还不是很明白,这个空文件夹有什么作用。
修改完毕,需要重新编译,在llvm/build中重新make(这里我不知道要不要make install,因为我安装了两个llvm,一个make install了,一个没有,修改的是那没有的。)
)
然后在llvm/build/lib/中出现了LLVMReadd.so,这个名称在之前那个cmakelist中修改。
下面来测试一下,先建个.c文件里面是整数加法
#include <stdio.h>
int main() {
int a=1,b=2;
int c;
c=a+b;
return 0;
}
先编译成.bc文件,注意,优化不要太高
clang -O0 -emit-llvm test.c -c -o test.bc
下面这句是使用哪个pass -readd是根据之前的那个Readd.cpp里这句中(static RegisterPass<readd> X("readd", "Readd Pass");)X括号里的确定的,是说用pass里那个功能,因为一个pass可以有多个功能,而这个只有一个。
opt -load ../build/lib/LLVMReadd.so -readd <test.bc> chtest.bc
转化成可视的.ll
llvm-dis chtest.bc
可以从LLVM IR看到变化。
如果编写的是全局的pass,而不是opt调用的,那就需要增加几个步骤。
首先是初始化,llvm/InitializePasses.h的头文件中增加自己的pass
void initializeMYADCEPass(PassRegistry&);
然后在include/llvm-c/scalar.h/Transform/scalar.h文件下添加Pass的条目
void LLVMAddMYAggressiveDCEPass(LLVMPassManagerRef PM);
在include/llvm/Transform/scalar.h文件中,在llvm命名空间添加Pass的条目:
FunctionPass *createMYAggressiveDCEPass();
在lib/Transforms/Scalar/scalar.cpp文件的两个地方添加Pass的条目,并在void llvm::initializeScalarOpts(PassRegistry &Registry)函数中添加如下代码:
initializeMergedLoadStoreMotionPass(Registry); //已存在于文件
initializeMYADCEPass(Registry); //增加此行
initializeNaryReassociatePass(Registry); //已存在于文件
...
...
void LLVMAddMemCpyOptPass(LLVMPassManagerRef PM) {
unwrap(PM)->add(createMemCpyOptPass());
}
// 增加以下代码
void LLVMAddMYAggressiveDCEPass(LLVMPassManagerRef PM) {
unwrap(PM)->add(createMYAggressiveDCEPass());
}
void LLVMAddPartiallyInlineLibCallsPass(LLVMPassManagerRef PM) {
unwrap(PM)->add(createPartiallyInlineLibCallsPass());
}