我特别需要在我的c ++代码运行期间逐行解析LLVM IR代码,我需要知道每行上哪些操作数发生了什么操作。
例如,如果IR代码是:
%0 = load i32* %a, align 4
我想知道在我的c ++代码运行期间%a
的值被加载到%0
。
我已经考虑使用简单的文本解析c ++程序来执行此操作(解析IR并搜索IR关键字),但想知道是否有任何现有的库(可能来自LLVM本身)将帮助我避免这样做。
答案 0 :(得分:7)
理论上,我们可以直接利用LLVM::LLLexer
为LLVM IR编写我们自己的解析器,逐行解析。
以下答案假设您只对LLVM IR文件的每个函数内部的操作感兴趣,因为LLVM IR文件中的其他信息不包含该操作。操作只能位于一个功能中。对于IR的其他部分,例如结构定义,函数声明等,它们只有类型的信息,并且不包含任何有关操作的信息。
基于上述假设,您关于在IR文件中逐行解析LLVM IR的问题可以转换为解析LLVM IR文件的每个函数中的每个操作。
LLVM确实有一个现有的实现,可以直接解析LLVM IR文件,直接获取有关操作的信息,并且由于IR文件的功能序列是它们在LLVM IR文件中出现的,操作顺序以下实现的输出只是给定LLVM IR文件中的操作序列。
因此我们可以利用llvm提供的parseBitcodeFile
接口。此类接口首先使用LLVM::LLLexer
将LLVM IR文件拆分为令牌,然后将令牌提供给Parser
进行分析,最后生成ErrorOr<llvm::Module *>
模块信息,功能序列模块中的列表与llvm ir文件中的序列相同。
然后我们可以LLVM::BasicBlock
中的每个LLVM::Function
LLVM::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;
}
请使用以下命令生成可执行文件:
clang++ ReadBitCode.cpp -o reader `llvm-config --cxxflags --libs --ldflags --system-libs`
以下面的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;
}
在以下命令之后,我们可以获得一些输出:
clang -emit-llvm -o foo.bc -c foo.c
./reader foo.bc
输出应如下所示:
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,
为了更好地了解上述输出,请注意。
在内部,对于每个LLVM指令,LLVM将直接使用其指令的地址来表示返回值。当返回值用于另一条指令时,它将直接使用该指令的地址。
对于由clang
生成的人类可读IR,返回值(例如%0
,%add
,%conv
由LLVM IR写入生成,仅供阅读
Instruction
类没有LLVM IR文件行号信息LLVM IR仅包含有关原始C源代码的行号信息。这意味着我们无法了解LLVM IR代码中每个操作的行号。
因此,虽然我们可以逐行解析操作,但我们无法知道操作位于哪一行。
上述源代码是从How to write a custom intermodular pass in LLVM?借用的,并且也针对此问题进行了修改。