我主要是學習修改,而不是學習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());
}