llvm 是什么

low level virtual machine , 是个编译器。

llvm的设计

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

image-20200903112945927

source code -> IR -> IR -> target-indepent code (代码层面)。

学习的目的

在函数中插入指令

该指令为框架相关的指令。

需要的知识点

IR 语法和结构

语法

以helloworld为例

#include <stdio.h>
int main(void){
      printf("Hello World\n");
      return 0;
}            

编译为IR中间语言

image-20200903144821210

结构

模块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);

再看源码之前,我们先来分析下在插入一个函数调用需要什么,如下:

  1. 调用指令
  2. 函数返回值
  3. 函数名称
  4. 参数
    1. 参数类型
    2. 参数名称
    3. 参数个数

然后再来看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;
}
​
分类: llvm

pareto

未来什么方向不管,先做自己喜欢做的事情。

0 条评论

发表回复

您的电子邮箱地址不会被公开。 必填项已用*标注