LLVM-tutor 学习笔记3: InjectFuncCall

Basic

这是一个 transformer pass, 效果类似于在每个函数的定义中插入如下语句:

printf("(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n", 
FuncName, FuncNumArgs)

InjectFuncCall

pass 的注册与前 2 期类似, 不再解释。

类定义


struct InjectFuncCall : public llvm::PassInfoMixin<InjectFuncCall> {
  llvm::PreservedAnalyses run(llvm::Module &M,
                              llvm::ModuleAnalysisManager &);
  bool runOnModule(llvm::Module &M);

  static bool isRequired() { return true; }
};

继承了 PassInfoMixin, 实现了 run 方法

runOnModule()

实现了主要功能: 将 Module 中每一函数体中添加上特定 printf 语句。返回是否对 Module 中的内容进行修改。

整体函数定义如下, 后面的小节对3个主要部分进行了拆解

bool InjectFuncCall::runOnModule(Module &M) {
  bool InsertedAtLeastOnePrintf = false;

  // 1: 在 module 中插入 printf 的声明, 效果类似 C 中的 int printf(char *, ...)
  // ------------------------------------------------------------------------
  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);

  // Set attributes as per inferLibFuncAttributes in BuildLibCalls.cpp
  Function *PrintfF = dyn_cast<Function>(Printf.getCallee());
  PrintfF->setDoesNotThrow();
  PrintfF->addParamAttr(0, Attribute::NoCapture);
  PrintfF->addParamAttr(0, Attribute::ReadOnly);


  // 2: 插入全局变量, 保存 format string: "(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n"
  // ------------------------------------------------------------------------
  llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString(
      CTX, "(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n");

  Constant *PrintfFormatStrVar =
      M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType());
  dyn_cast<GlobalVariable>(PrintfFormatStrVar)->setInitializer(PrintfFormatStr);

  // 3: 遍历每个 function , 添加对 printf 的调用
  // ----------------------------------------------------------------
  for (auto &F : M) {
    if (F.isDeclaration())
      continue;

    IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt());

    auto FuncName = Builder.CreateGlobalStringPtr(F.getName());

    llvm::Value *FormatStrPtr =
        Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr");

    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;
}

1: 在 module 中插入 printf 的声明, 效果类似 C 中的 int printf(char *, ...)

 auto &CTX = M.getContext(); 

获取了 llvm 的全局 api 接口

  PointerType *PrintfArgTy = PointerType::getUnqual(Type::getInt8Ty(CTX));

通过 CTX 获取了 8 位 int 类型。

PointerType::getUnqual 方法构造了一个指向 在默认地址空间 (地址 0) 下 指定类型的 object 的指针类型。

FunctionType::get 方法的第一个参数类型为 Type 指定了返回结果的类型, 第二个参数类型为ArrayRef<Type*> 指定了各个参数的类型, 第三个参数指定函数是否  可变参数。在这段代码中指定了函数 返回结果类型为 32 位 int, 函数参数类型为只含有一个 8 位 Int 指针类型的数组, 接收可变参数。

M.getOrInsertFunction 在 module 的 symbol table 中查找同名函数原型, 不存在则插入, 存在则对比原型, 使用旧原型替换新原型, 并返回 FunctionCallee

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

  // Set attributes as per inferLibFuncAttributes in BuildLibCalls.cpp
  Function *PrintfF = dyn_cast<Function>(Printf.getCallee());
  PrintfF->setDoesNotThrow();
  PrintfF->addParamAttr(0, Attribute::NoCapture);
  PrintfF->addParamAttr(0, Attribute::ReadOnly);

2: 插入全局变量, 保存 format string: "(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n"

llvm::ConstantDataArray::getString   构造一个 CDS 并用指定的字符串对它进行初始化。 返回了对应指针。

M.getOrInsertGlobal 插入了一个全局变量, 名为 PrintfFormatStr, 类型与 PrintfFormatStr 相同。

setInitializer 指定了 PrintfFormatStrVar 的初始化值为 PrintfFormatStr 指向的常量。

llvm::Constant *PrintfFormatStr = llvm::ConstantDataArray::getString(
      CTX, "(llvm-tutor) Hello from: %s\n(llvm-tutor)   number of arguments: %d\n");

  Constant *PrintfFormatStrVar =
      M.getOrInsertGlobal("PrintfFormatStr", PrintfFormatStr->getType());
  dyn_cast<GlobalVariable>(PrintfFormatStrVar)->setInitializer(PrintfFormatStr);

3: 遍历每个 function , 添加对 printf 的调用

这一部分主要是要理解 IRBuilder 的插入点概念和 createFoo 函数的作用

for (auto &F : M) {
    // 跳过函数声明, 即对应 Module 中不存在该全局变量的定义
    if (F.isDeclaration())
      continue;
    // 获取 IRBuilder 并设置了插入点在函数的开头
    IRBuilder<> Builder(&*F.getEntryBlock().getFirstInsertionPt());
    // 创建了一个全局 string 指针, 内空为函数的名称
    auto FuncName = Builder.CreateGlobalStringPtr(F.getName());

    // 进行了类型转换 [n x i8] -> i8* 数组到指针
    llvm::Value *FormatStrPtr =
        Builder.CreatePointerCast(PrintfFormatStrVar, PrintfArgTy, "formatStr");

    LLVM_DEBUG(dbgs() << " Injecting call to printf inside " << F.getName()
                      << "\n");
    // 插入函数调用
    Builder.CreateCall(
        Printf, {FormatStrPtr, FuncName, Builder.getInt32(F.arg_size())});
    InsertedAtLeastOnePrintf = true;
  }

run()

如果没有修改Module 中的函数, 则之前所有的 analysis 结果都可以保留。 如修改了 Module 中的函数, 并且并没有更新之前的 analysis 结果, 不保留所有的分析结果。

PreservedAnalyses InjectFuncCall::run(llvm::Module &M,
                                      llvm::ModuleAnalysisManager &) {
  bool Changed = runOnModule(M);
  return (Changed ? llvm::PreservedAnalyses::none()
                  : llvm::PreservedAnalyses::all());
}

相关类

class PointerType : public Type 

class ConstantDataArray final : public ConstantDataSequential 

FunctionCallee

Module

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值