如何逐行解析LLVM IR

Sak*_*ain 5 c++ parsing llvm llvm-ir

我特别需要在我的c ++代码运行期间逐行解析LLVM IR代码,我需要知道每行上哪些操作数发生了什么操作.

例如,如果IR代码是:

%0 = load i32* %a, align 4
Run Code Online (Sandbox Code Playgroud)

我想知道在我的c ++代码运行期间%a加载的值%0.我曾考虑使用简单的文本解析c ++程序来执行此操作(解析IR并搜索IR关键字),但想知道是否有任何现有的库(可能来自LLVM本身)将帮助我避免这样做.

Kun*_*ing 7

假设

从理论上讲,我们可以直接利用LLVM::LLLexer为LLVM IR编写自己的解析器来逐行解析.

以下答案假设您只对LLVM IR文件的每个函数内部的操作感兴趣,因为LLVM IR文件中的其他信息不包含该操作.操作只能位于一个功能中.对于IR的其他部分,例如结构定义,函数声明等,它们仅包含有关类型的信息,并且不包含任何有关操作的信息.

履行

基于上述假设,您关于为IR文件中的操作信息逐行解析LLVM IR的问题可以转换为解析LLVM IR文件的每个函数中的每个操作.

LLVM确实有一个现有的实现,可以直接解析LLVM IR文件,直接获取有关操作的信息,并且由于IR文件的功能序列是它们出现在LLVM IR文件中的,所以操作顺序由以下实现只是给定LLVM IR文件中的操作序列.

因此,我们可以利用parseBitcodeFilellvm提供的接口.这样的接口首先使用一个LLVM::LLLexer将LLVM IR文件拆分成令牌,然后输入令牌Parser进行分析,最后生成一个ErrorOr<llvm::Module *>模块信息,模块中的函数列表序列与llvm ir文件中的序列相同.

然后,我们可以在每个LLVM::BasicBlockLLVM::FunctionLLVM::Module.然后迭代每个LLVM::Instruction,并获取有关每个操作数的信息LLVM::Value.以下是实现代码.

#include <iostream>
#include <string>
#include <llvm/Support/MemoryBuffer.h>
#include <llvm/Support/ErrorOr.h>
#include <llvm/IR/Module.h>
#include <llvm/IR/LLVMContext.h>
#include <llvm/Bitcode/ReaderWriter.h>
#include <llvm/Support/raw_ostream.h>

using namespace llvm;

int main(int argc, char *argv[]) {
  if (argc != 2) {
    std::cerr << "Usage: " << argv[0] << "bitcode_filename" << std::endl;
    return 1;
  }
  StringRef filename = argv[1];
  LLVMContext context;

  ErrorOr<std::unique_ptr<MemoryBuffer>> fileOrErr =
    MemoryBuffer::getFileOrSTDIN(filename);
  if (std::error_code ec = fileOrErr.getError()) {
    std::cerr << " Error opening input file: " + ec.message() << std::endl;
    return 2;
  }
  ErrorOr<llvm::Module *> moduleOrErr =
      parseBitcodeFile(fileOrErr.get()->getMemBufferRef(), context);
  if (std::error_code ec = fileOrErr.getError()) {
    std::cerr << "Error reading Moduule: " + ec.message() << std::endl;
    return 3;
  }

  Module *m = moduleOrErr.get();
  std::cout << "Successfully read Module:" << std::endl;
  std::cout << " Name: " << m->getName().str() << std::endl;
  std::cout << " Target triple: " << m->getTargetTriple() << std::endl;

  for (auto iter1 = m->getFunctionList().begin();
       iter1 != m->getFunctionList().end(); iter1++) {
    Function &f = *iter1;
    std::cout << " Function: " << f.getName().str() << std::endl;
    for (auto iter2 = f.getBasicBlockList().begin();
         iter2 != f.getBasicBlockList().end(); iter2++) {
      BasicBlock &bb = *iter2;
      std::cout << "  BasicBlock: " << bb.getName().str() << std::endl;
      for (auto iter3 = bb.begin(); iter3 != bb.end(); iter3++) {
        Instruction &inst = *iter3;
        std::cout << "   Instruction " << &inst << " : " << inst.getOpcodeName();

    unsigned int  i = 0;
    unsigned int opnt_cnt = inst.getNumOperands();
        for(; i < opnt_cnt; ++i)
        {
          Value *opnd = inst.getOperand(i);
          std::string o;
          //          raw_string_ostream os(o);
          //         opnd->print(os);
          //opnd->printAsOperand(os, true, m);
          if (opnd->hasName()) {
            o = opnd->getName();
            std::cout << " " << o << "," ;
          } else {
            std::cout << " ptr" << opnd << ",";
          }
        }
        std:: cout << std::endl;
      }
    }
  }
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

请使用以下命令生成可执行文件:

clang++ ReadBitCode.cpp -o reader `llvm-config --cxxflags --libs --ldflags --system-libs`
Run Code Online (Sandbox Code Playgroud)

以下面的c代码为例:

struct a {
  int f_a;
  int f_b;
  char f_c:5;
  char f_d:4;
};

int my_func( int arg1, struct a obj_a) {
  int x = arg1;
  return x+1 + obj_a.f_c;
}

int main() {
  int a = 11;
  int b = 22;
  int c = 33;
  int d = 44;
  struct a obj_a;
  obj_a.f_a = 1;
  obj_a.f_b = 2;
  obj_a.f_c = 3;
  obj_a.f_c = 4;
  if ( a > 10 ) {
    b = c;
  } else {
    b = my_func(d, obj_a);
  }
  return b;
}
Run Code Online (Sandbox Code Playgroud)

在以下命令之后,我们可以获得一些输出:

clang -emit-llvm -o foo.bc -c foo.c
./reader foo.bc
Run Code Online (Sandbox Code Playgroud)

输出应该类似于以下内容:

 Name: foo.bc
 Target triple: x86_64-unknown-linux-gnu
 Function: my_func
  BasicBlock: entry
   Instruction 0x18deb68 : alloca ptr0x18db940,
   Instruction 0x18debe8 : alloca ptr0x18db940,
   Instruction 0x18dec68 : alloca ptr0x18db940,
   Instruction 0x18dece8 : alloca ptr0x18db940,
   Instruction 0x18de968 : getelementptr coerce, ptr0x18de880, ptr0x18de880,
   Instruction 0x18de9f0 : store obj_a.coerce0, ptr0x18de968,
   Instruction 0x18df0a8 : getelementptr coerce, ptr0x18de880, ptr0x18db940,
   Instruction 0x18df130 : store obj_a.coerce1, ptr0x18df0a8,
   Instruction 0x18df1a8 : bitcast obj_a,
   Instruction 0x18df218 : bitcast coerce,
   Instruction 0x18df300 : call ptr0x18df1a8, ptr0x18df218, ptr0x18de8d0, ptr0x18de1a0, ptr0x18de1f0, llvm.memcpy.p0i8.p0i8.i64,
   Instruction 0x18df3a0 : store arg1, arg1.addr,
   Instruction 0x18df418 : load arg1.addr,
   Instruction 0x18df4a0 : store ptr0x18df418, x,
   Instruction 0x18df518 : load x,
   Instruction 0x18df5a0 : add ptr0x18df518, ptr0x18db940,
   Instruction 0x18df648 : getelementptr obj_a, ptr0x18de880, ptr0x18deab0,
   Instruction 0x18df6b8 : load f_c,
   Instruction 0x18df740 : shl bf.load, ptr0x18deb00,
   Instruction 0x18df7d0 : ashr bf.shl, ptr0x18deb00,
   Instruction 0x18df848 : sext bf.ashr,
   Instruction 0x18df8d0 : add add, conv,
   Instruction 0x18df948 : ret add1,
 Function: llvm.memcpy.p0i8.p0i8.i64
 Function: main
  BasicBlock: entry
   Instruction 0x18e0078 : alloca ptr0x18db940,
   Instruction 0x18e00f8 : alloca ptr0x18db940,
   Instruction 0x18e0178 : alloca ptr0x18db940,
   Instruction 0x18e01f8 : alloca ptr0x18db940,
   Instruction 0x18e0278 : alloca ptr0x18db940,
   Instruction 0x18e02f8 : alloca ptr0x18db940,
   Instruction 0x18e0378 : alloca ptr0x18db940,
   Instruction 0x18e0410 : store ptr0x18de880, retval,
   Instruction 0x18e04a0 : store ptr0x18dfe30, a,
   Instruction 0x18e0530 : store ptr0x18dfe80, b,
   Instruction 0x18e05c0 : store ptr0x18dfed0, c,
   Instruction 0x18e0650 : store ptr0x18dff20, d,
   Instruction 0x18e06f8 : getelementptr obj_a, ptr0x18de880, ptr0x18de880,
   Instruction 0x18e0780 : store ptr0x18db940, f_a,
   Instruction 0x18e0828 : getelementptr obj_a, ptr0x18de880, ptr0x18db940,
   Instruction 0x18e08b0 : store ptr0x18deab0, f_b,
   Instruction 0x18e0958 : getelementptr obj_a, ptr0x18de880, ptr0x18deab0,
   Instruction 0x18e09c8 : load f_c,
   Instruction 0x18e0a50 : and bf.load, ptr0x18dff70,
   Instruction 0x18e0ae0 : or bf.clear, ptr0x18deb00,
   Instruction 0x18e0b70 : store bf.set, f_c,
   Instruction 0x18e0c18 : getelementptr obj_a, ptr0x18de880, ptr0x18deab0,
   Instruction 0x18e0c88 : load f_c1,
   Instruction 0x18e0d10 : and bf.load2, ptr0x18dff70,
   Instruction 0x18e0da0 : or bf.clear3, ptr0x18dffc0,
   Instruction 0x18ded80 : store bf.set4, f_c1,
   Instruction 0x18dedf8 : load a,
   Instruction 0x18dee80 : icmp ptr0x18dedf8, ptr0x18e0010,
   Instruction 0x18def28 : br cmp, if.else, if.then,
  BasicBlock: if.then
   Instruction 0x18def98 : load c,
   Instruction 0x18e1440 : store ptr0x18def98, b,
   Instruction 0x18df008 : br if.end,
  BasicBlock: if.else
   Instruction 0x18e14b8 : load d,
   Instruction 0x18e1528 : bitcast obj_a.coerce,
   Instruction 0x18e1598 : bitcast obj_a,
   Instruction 0x18e1680 : call ptr0x18e1528, ptr0x18e1598, ptr0x18de8d0, ptr0x18de880, ptr0x18de1f0, llvm.memcpy.p0i8.p0i8.i64,
   Instruction 0x18e1738 : getelementptr obj_a.coerce, ptr0x18de880, ptr0x18de880,
   Instruction 0x18e17a8 : load ptr0x18e1738,
   Instruction 0x18e1848 : getelementptr obj_a.coerce, ptr0x18de880, ptr0x18db940,
   Instruction 0x18e18b8 : load ptr0x18e1848,
   Instruction 0x18e1970 : call ptr0x18e14b8, ptr0x18e17a8, ptr0x18e18b8, my_func,
   Instruction 0x18e1a10 : store call, b,
   Instruction 0x18e1a88 : br if.end,
  BasicBlock: if.end
   Instruction 0x18e1af8 : load b,
   Instruction 0x18e1b68 : ret ptr0x18e1af8,
Run Code Online (Sandbox Code Playgroud)

说明

为了更好地了解上述输出,请注意.

LLVM使用指令地址作为返回值id

在内部,对于每个LLVM指令,LLVM将直接使用其指令的地址来表示返回值.当返回值用于另一条指令时,它将直接使用该指令的地址.

对于人类可读的IR通过生成clang,返回值,例如%0,%add,%conv由LLVM IR写入仅供方便阅读生成.

LLVM Instruction类没有LLVM IR文件行号信息

LLVM IR仅包含有关原始C源代码的行号信息.这意味着我们无法了解LLVM IR代码中每个操作的行号.

因此,虽然我们可以逐行解析操作,但我们无法知道操作位于哪一行.

参考

上面的源代码是从如何在LLVM中编写自定义的模块间传递中借用的,也针对这个问题进行了修改.