0%

Pass入门(一)Pass 入门

Basic

想做一个有关llvm-pass的lab: 试试康奈尔 CS 6120: The Self-Guided Course (cornell.edu)

  1. 环境: Ubuntu + llvm 14 + clang 14

  2. clone lab repo :

    1
    git clone https://github.com/sampsyo/llvm-pass-skeleton.git 
  3. 进入这个repo,创建build文件夹,进入文件夹 LLVM_DIR=/usr/lib/llvm-14/cmake cmake .. 来生成makefile,注意LLVM_DIR 取决于你LLVM的安装路径。然后make编译,他会编译出一个libSkeletonPass.so 文件

  4. 我们随便写一个main.c,然后使用下列命令行编译 clang -flegacy-pass-manager -Xclang -load -Xclang ./libSkeletonPass.so main.c

  5. 另一种方案

    • 使用clang将 main.c 编译为 llvm-ir clang -emit-llvm -c main.c 生成 main.bc

    • 修改Skeleton.cpp 为

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      // #include <iostream>

      #include "llvm/Pass.h"
      #include "llvm/IR/Function.h"
      #include "llvm/Support/raw_ostream.h"
      #include "llvm/IR/LegacyPassManager.h"
      #include "llvm/Transforms/IPO/PassManagerBuilder.h"
      using namespace llvm;

      namespace
      {
      struct SkeletonPass : public FunctionPass
      {
      static char ID;
      SkeletonPass() : FunctionPass(ID) {}

      virtual bool runOnFunction(Function &F)
      {
      // std::cout<<"HELLO!";
      errs() << "I saw a function called " << F.getName() << "!\n";
      errs() << F;
      return false;
      }
      };
      }

      char SkeletonPass::ID = 0;
      // Register for opt
      static RegisterPass<SkeletonPass> X("hello", "Hello World Pass");

      // Register for clang
      static RegisterStandardPasses Y(PassManagerBuilder::EP_EarlyAsPossible,
      [](const PassManagerBuilder &Builder, legacy::PassManagerBase &PM)
      {
      PM.add(new SkeletonPass());
      });

      make重新编译

    • 使用 opt 加载 pass 动态共享库 opt -load ./skeleton/libSkeletonPass.so -hello -enable-new-pm=0 main.bc

Example 1: 遍历IR

打印每条命令,每个BB。runOnFunction 返回值含义即函数是否被修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
virtual bool runOnFunction(Function &F)
{
auto &os = errs();
os <<F.getName()<<": Function body:\n";
for (auto &B : F)
{
os << "Basic block:" << B << "\n";
for (auto &I : B)
{
os << "ins: " << I << "\n";
}
}
return false;
}

Example 2: 修改运算指令

打印所有的运算指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
virtual bool runOnFunction(Function &F)
{
auto &os = errs();
// os <<F.getName()<<": Function body:\n";
for (auto &B : F)
{
// os << "Basic block:" << B << "\n";
for (auto &I : B)
{
if (auto *op = dyn_cast<BinaryOperator>(&I))
os << *op << "\n";
}
}
return false;
}

下面我们尝试将所有运算指令换成乘法指令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
for (auto &I : B)
{
if (auto *op = dyn_cast<BinaryOperator>(&I))
{
IRBuilder<> builder(op);

Value *first = op->getOperand(0);
Value *second = op->getOperand(1);
Value *mul = builder.CreateMul(first, second);
for (auto &U : op->uses())
{
auto *usr = U.getUser();
usr->setOperand(U.getOperandNo(), mul);

}
return true; // 返回true 表面该函数被修改
}
}

还是比较清晰的。

Example 3: 插入函数

注意者不仅仅需要main.c 还需要编写logop 的实现并和main.c 链接。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
virtual bool runOnFunction(Function &F)
{
LLVMContext &cxt = F.getContext();
auto logFunc = F.getParent()->getOrInsertFunction(
"logop", Type::getVoidTy(cxt), Type::getInt32Ty(cxt));

auto &os = errs();
for (auto &B : F)
{
for (auto &I : B)
{
if (auto *op = dyn_cast<BinaryOperator>(&I))
{
IRBuilder<> builder(op);
builder.SetInsertPoint(&B, ++builder.GetInsertPoint());
Value *args = {op};
builder.CreateCall(logFunc, args);
return true;
}
}
}
return false;
}

References:

  1. LLVM 后端 : LLVM 后端实践笔记 0:序 - 知乎 (zhihu.com)