gpt4 book ai didi

c++ - 如何逐行解析LLVM IR

转载 作者:塔克拉玛干 更新时间:2023-11-03 00:19:34 33 4
gpt4 key购买 nike

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

例如,如果 IR 代码是:

%0 = load i32* %a, align 4

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

最佳答案

假设

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

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

实现

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

LLVM 确实有一个现有的实现,可以直接逐行解析 LLVM IR 文件以直接获取有关操作的信息,并且由于 IR 文件的函数顺序是它们在 LLVM IR 文件中出现的顺序,因此操作顺序以下实现的输出只是给定 LLVM IR 文件中的操作序列。

因此我们可以利用 parseBitcodeFile llvm提供的接口(interface)。这样的接口(interface)将首先使用一个LLVM::LLLexer。将 LLVM IR 文件拆分为 token ,然后将 token 提供给 Parser进行分析,最后生成一个ErrorOr<llvm::Module *>模块信息,函数列表在模块中的顺序与llvm ir文件中的顺序相同。

然后我们可以每个LLVM::BasicBlock每个 LLVM::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;
}

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

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使用指令地址作为返回值id

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

对于 clang 生成的人类可读 IR , 返回值,如%0 , %add , %conv由LLVM IR编写生成,仅供阅读。

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

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

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

引用

以上源码借自How to write a custom intermodular pass in LLVM? ,也针对这个问题进行了修改。

关于c++ - 如何逐行解析LLVM IR,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30195204/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com