
llvm 是什么
low level virtual machine , 是个编译器。
llvm的设计
计算机常用解决方法。百分之80的性能和功能问题都可以通过添加一个缓冲区来完成,llvm也是使用了这种方法,添加了IR中间语言。

source code -> IR -> IR -> target-indepent code (代码层面)。
学习的目的
在函数中插入指令
该指令为框架相关的指令。
需要的知识点
IR 语法和结构
语法
以helloworld为例
#include <stdio.h>
int main(void){
printf("Hello World\n");
return 0;
}
编译为IR中间语言

结构
模块module
个人理解 一个文件就可以当作一个模块
函数
函数就是函数
块
一个函数由代码块组成
指令
块由指令组成
遍历HelloWorld.ll 的模块、函数、块、指令,得到以下结果:
Module Name: HelloWorld.ll
Function Name: main
BasicBlock Name:
OpccodeName: alloca
OpccodeName: store
OpccodeName: call
OpccodeName: ret
Function Name: printf
遍历代码:
errs() << "Module Name:\t" << M.getName() << "\n";
for(auto &F : M){
errs() << "Function Name:\t" <<F.getName() << "\n";
for(auto &B : F){
errs() << "BasicBlock Name:\t" <<B.getName() << "\n";
B.setName("mc");
for (Instruction &I : B)
errs() << "OpccodeName:\t" <<I.getOpcodeName() << "\n";
}
}
PASS
IR到IR的处理,看个简单的PASS
用InjectFuncCall实例来分析pass,源代码在https://github.com/banach-space/llvm-tutor/blob/master/lib/InjectFuncCall.cpp , InjectFuncCall的功能如下:
(llvm-tutor) Hello from: main
(llvm-tutor) number of arguments: 0
Hello World
从C代码来看就是插入了类似这样的代码。
printf(“(llvm-tutor) Hello from: %s\n(llvm-tutor) number of arguments: %s”,funcname ,arg_num);
再看源码之前,我们先来分析下在插入一个函数调用需要什么,如下:
- 调用指令
- 函数返回值
- 函数名称
- 参数
- 参数类型
- 参数名称
- 参数个数
然后再来看PASS 操作IR 添加函数调用就很简单了。
bool InjectFuncCall::runOnModule(Module &M) {
bool InsertedAtLeastOnePrintf = false;
//获取llvmContext
auto &CTX = M.getContext();
//创建一个函数参数类型
PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX));
FunctionType *PrintfTy = FunctionType::get(
IntegerType::getInt32Ty(CTX),
PrintfArgTy,
/*IsVarArgs=*/true);
FunctionCallee Printf = M.getOrInsertFunction("printf", PrintfTy);
//获取调用printf的函数
Function *PrintfF = dyn_cast<Function>(Printf.getCallee());
errs()<<PrintfF.getCallee()<<"\n";
// 插入名称为PrintfFormatStr 的全局变量
llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString(
CTX, "(llvm-tutor) Hello from11: %s\n(llvm-tutor) number of arguments: %d\n");
Constant *PrintfFormatStrVar =
M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType());
dyn_cast<GlobalVariable>(PrintfFormatStrVar)->setInitializer(PrintfFormatStr);
for (auto &F : M) {
//用来区分是否为引用函数。
if (F.isDeclaration())
continue;
// Get an IR builder. Sets the insertion point to the top of the function
IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt());
// Inject a global variable that contains the function name
//创建全局变量并获取他的指针。
auto FuncName = Builder.CreateGlobalStringPtr(F.getName());
// Printf requires i8*, but PrintfFormatStrVar is an array: [n x i8]. Add
// 创建指向PrintfFormatStrVar 参数值 PrintfArgTy 参数类型的指针。 第三个参数"formatStr"似乎是可选参数,删除后不影响操作。
llvm::Value *FormatStrPtr =
Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr");
// The following is visible only if you pass -debug on the command line
// *and* you have an assert build.
LLVM_DEBUG(dbgs() << " Injecting call to printf inside " << F.getName()
<< "\n");
// 这段代码比较明显,创建一个函数调用,结合参数了解到创建函数调用我们需要调用的函数和函数参数。
Builder.CreateCall(
Printf, {FormatStrPtr, FuncName, Builder.getInt32(F.arg_size())});
InsertedAtLeastOnePrintf = true;
}
return InsertedAtLeastOnePrintf;
}
0 条评论