常见操作的有用提示

本节描述如何执行一些非常简单的LLVM代码转换。这意味着要给出常用习惯用法的示例,展示LLVM转换的实用方面。

因为这是一个“how-to”部分,所以您还应该阅读将要使用的主要类。核心LLVM类层次结构参考资料包含您应该了解的主要类的详细信息和描述。

基本检查和遍历例程

LLVM编译器基础架构有许多可以遍历的不同数据结构。以C++标准模板库为例,用于遍历这些不同数据结构的技术基本上是相同的。对于一个可枚举的值序列,XXXbegin()函数(或方法)返回一个序列开头的迭代器,在XXXend()函数返回一个迭代器,该迭代器指向序列最后一个有效元素之后的元素,这两个操作之间有一些XXXiterator数据类型是常见的。

因为迭代的模式在程序表示的许多不同方面都是通用的,所以可以在它们上使用标准模板库算法,并且更容易记住如何迭代。首先,我们展示一些需要遍历的数据结构的常见示例。以非常相似的方式遍历其他数据结构。

遍历一个Function中的BasicBlock

有一个你想要以某种方式转换的函数实例是很常见的;特别是,您希望操作它的基本块。为了实现这一点,您需要遍历构成该Function的所有BasicBlocks。下面是打印一个BasicBlock的名称和它包含的Instructions数的例子:

Function &Func = ...
for (BasicBlock &BB : Func)
// Print out the name of the basic block if it has one, and then the
// number of instructions that it contains
errs() << "Basic block (name=" << BB.getName() << ") has "
<< BB.size() << " instructions.\n";

遍历一个BasicBlock中的Instruction

就像在函数中处理基本块一样,很容易遍历组成基本块的各个指令。这是一个代码片段,打印出在一个基本块的每个指令:

BasicBlock& BB = ...
for (Instruction &I : BB)
// The next statement works since operator<<(ostream&,...)
// is overloaded for Instruction&
errs() << I << "\n";

然而,这并不是打印BasicBlock内容的最佳方式!由于ostream操作符实际上重载了您所关心的所有内容,所以您可以调用基本块本身上的打印例程:errs() << BB << "\n";

遍历一个Function中的Instruction

如果您发现您通常遍历函数的基本块,然后遍历基本块的指令,那么应该使用InstIterator。您需要include llvm/IR/InstIterator.h,然后在代码中显式实例化InstIterator。下面是一个小例子,展示了如何将函数中的所有指令转储到标准错误流:

#include "llvm/IR/InstIterator.h"

// F is a pointer to a Function instance
for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
errs() << *I << "\n";

很容易,不是吗?您还可以使用InstIterator来用工作列表的初始内容填充工作列表。例如,如果你想初始化一个工作列表来包含函数F中的所有指令,你需要做的就是:

std::set<Instruction*> worklist;
// or better yet, SmallPtrSet<Instruction*, 64> worklist;

for (inst_iterator I = inst_begin(F), E = inst_end(F); I != E; ++I)
worklist.insert(&*I);

worklist现在将包含F指向的函数中的所有指令。

将一个 iterator 转换为一个类指针(反之亦然)

有时候,当您手头只有一个iterator时,获取一个类实例的引用(或指针)是很有用的。从 iterator 中提取引用或指针非常简单。假设 i 是一个BasicBlock::iterator,j是一个BasicBlock::const_iterator:

Instruction& inst = *i;   // Grab reference to instruction reference
Instruction* pinst = &*i; // Grab pointer to instruction reference
const Instruction& inst = *j;

但是,您将在LLVM框架中使用的 iterator 是特殊的:它们将在需要的时候自动转换为 ptr-to-instance 类型。原本是取消对 iterator 的引用,然后获取结果的地址;代替的是:您可以简单地将 iterator 分配给适当的指针类型,然后您将获得操作的dereference和address作为分配的结果(在幕后,这是重载转换机制的结果)。因此上一个例子的第二行Instruction *pinst = &*i; ,在语义上等价于:Instruction *pinst = i;

也可以将一个类指针转换为相应的 iterator ,这是一个常量时间操作(非常有效)。下面的代码片段演示了使用LLVM iterator 提供的转换构造函数。通过使用这些,您可以显式地获取某个东西的 iterator,而无需通过对某个结构进行迭代来实际获取它:

void printNextInstruction(Instruction* inst) {
BasicBlock::iterator it(inst);
++it; // After this line, it refers to the instruction after *inst
if (it != inst->getParent()->end()) errs() << *it << "\n";
}

不幸的是,这些隐式转换是有代价的;它们阻止这些 iterator 遵守标准 iterator 约定,从而使它们不能与标准算法和容器一起使用。例如,它们阻止编译下面的代码,其中B是一个BasicBlock:

llvm::SmallVector<llvm::Instruction *, 16>(B->begin(), B->end());

因此,这些隐式转换可能会在某一天被删除,并且操作符*将返回指针而不是引用。

查找调用点:一个稍微复杂一点的示例

假设您正在编写一个FunctionPass,并且希望计算整个模块(即跨所有函数)中某个函数(即某个Function*)已经在作用域中的所有位置(被调用的次数)。稍后您将了解到,您可能希望使用 InstVisitor 以一种更直接的方式来实现这一点,但是这个示例将允许我们探索如果没有InstVisitor,您将如何实现这一点。在伪代码中,我们要做的是:

initialize callCounter to zero
for each Function f in the Module
for each BasicBlock b in f
for each Instruction i in b
if (i is a CallInst and calls the given function)
increment callCounter

实际的代码是(记住,因为我们在编写FunctionPass,我们的FunctionPass派生类只需要重载runOnFunction方法):

Function* targetFunc = ...;

class OurFunctionPass : public FunctionPass {
public:
OurFunctionPass(): callCounter(0) { }

virtual runOnFunction(Function& F) {
for (BasicBlock &B : F) {
for (Instruction &I: B) {
if (auto* CallInst = dyn_cast<CallInst>(&I)) {
// We know we've encountered a call instruction, so we
// need to determine if it's a call to the
// function pointed to by m_func or not.
if (CallInst->getCalledFunction() == targetFunc)
++callCounter;
}
}
}
}

private:
unsigned callCounter;
};

以相同的方式处理 calls 和 invokes

您可能已经注意到,前面的示例有些过于简化,因为它没有处理由‘invoke’指令生成的调用站点。在这种情况下,以及在其他情况下,您可能会发现您希望以同样的方式处理 CallInsts 和 InvokeInsts,即使它们最特定的公共基类是 Instruction,其中也包含许多不那么密切相关的东西。对于这些情况,LLVM提供了一个方便的wrapper类 CallSite ,它本质上是一个围绕 Instruction 指针的wrapper,它有一些方法提供 CallInsts 和 InvokeInsts 共有的功能。

该类具有“值语义”:它应该通过值传递,而不是通过引用传递,并且不应该使用 new 或 delete 操作符动态分配或释放该类。它具有高效的可复制性、可分配性和可构造性,其成本相当于一个空指针的成本。如果你看它的定义,它只有一个指针成员。

遍历 def-use 和 use-def 链

通常,我们可能有一个Value类的实例,我们希望确定哪些Users使用这个值。具有特定Value的所有Users的列表称为def-use链。例如,我们有一个 Function* F 指向一个特定的函数 foo。找到所有使用 foo 的指令就像遍历 F 的def-use链一样简单:

Function *F = ...;

for (User *U : F->users()) {
if (Instruction *Inst = dyn_cast<Instruction>(U)) {
errs() << "F is used in instruction:\n";
errs() << *Inst << "\n";
}

或者,通常有一个User类的实例,并且需要知道它使用什么Values。一个User使用的所有Values的列表称为use-def链。类Instruction的实例是常见的User,所以我们可能需要遍历特定Instruction使用的所有values(即特定Instruction的操作数):

Instruction *pi = ...;

for (Use &U : pi->operands()) {
Value* v = U.get();
// ...
}

将对象声明为 const 是实现无变化算法(如分析等)的一个重要工具。为此,上面的iterators有两种固定的风格:Value::const_use_iterator和Value::const_op_iterator。当分别调用 const Values 或 const Users 上的use/op_begin()时,它们会自动出现。在取消引用后,它们返回const Use*s。否则,上述模式将保持不变。

遍历块的前置和后继

使用“llvm/IR/CFG.h”中定义的例程,遍历块的前置和后继是非常容易的。只需使用这样的代码来遍历所有BB的前置:

#include "llvm/IR/CFG.h"
BasicBlock* BB = ...;

for (BasicBlock* Pred : predecessors(BB)) {
// ...
}

类似地,要遍历后继,可以使用successors