LLVM Programmer‘s Manual(2)

5 Debugging

为一些核心 LLVM 库提供了一些 GDB pretty printers。 要使用它们,请执行以下命令(或将其添加到您的 ~/.gdbinit):

source /path/to/llvm/src/utils/gdb-scripts/prettyprinters.py

启用 print pretty 选项可以更方便,以避免数据结构被打印为一大块文本。

6 Helpful Hints for Common Operations

本节介绍如何对 LLVM 代码执行一些非常简单的转换。 这是为了给出常用习语的例子,展示 LLVM 转换的实际操作。

因为这是一个“how-to”部分,您还应该阅读您将使用的主要类的相关信息。 Core LLVM Class Hierarchy Reference 包含您应该了解的主要类的详细信息和描述。

6.1 Basic Inspection and Traversal Routines 基本检查和遍历例程

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

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

6.1.1 Iterating over the BasicBlock in a Function 在函数中迭代基本块

通常会有你想以某种方式转换的 Function 实例;尤其是,你想操纵它的 BasicBlocks时。 为此,您需要遍历构成Function的所有BasicBlocks。 以下是打印 BasicBlock 的名称及其包含的指令数量的示例:

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";

6.1.2 Iterating over the Instruction in a BasicBlock 迭代基本块中的指令

就像在函数中处理 BasicBlock 一样,迭代构成 BasicBlock 的各个指令也很容易。 这是一个打印出 BasicBlock 中的每条指令的代码片段:

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";

6.1.3 Iterating over the Instruction in a Function 迭代函数中的指令

如果你经常迭代一个函数的 BasicBlocks ,然后迭代BasicBlock’s InstructionS,应该使用 InstIterator 代替。 您需要包含 llvm/IR/InstIterator.h (doxygen),然后在代码中显式实例化 InstIterators。 这是一个小例子,展示了如何将函数中的所有指令转储到标准错误流中:

#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";

很容易,不是吗? 您还可以使用 InstIterators 来填充工作列表的初始内容。 例如,如果您想初始化一个工作列表,以包含Function 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);

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

6.1.4 Turning an iterator into a class pointer (and vice-versa) 将迭代器转换为类指针(反之亦然)

当您只有一个迭代器时,获取类实例的引用(或指针)会很有用。从迭代器中提取引用或指针非常简单。 假设 i 是 一个BasicBlock::iterator 并且 jBasicBlock::const_iterator

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

但是,在 LLVM 框架中使用的迭代器是比较特殊的:它们会在需要时自动转换为 ptr-to-instance 类型。 您可以简单地将迭代器分配给正确的指针类型,而不是取消引用迭代器然后获取结果地址。并且您可以获得取消引用和地址操作作为分配的结果(在幕后,这是一个重载铸造机制的结果)。 因此最后一个例子的第二行:

Instruction *pinst = &*i;

在语义上等价于:

Instruction *pinst = i;

也可以将类指针转换为对应的迭代器,这是一个常数时间的操作(非常有效)。 以下代码片段说明了 LLVM 迭代器提供的转换构造函数的使用。 通过使用这些,您可以显式地获取something的迭代器,而无需通过对某个结构的迭代实际获得它:

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";
}

缺点是,这些隐式转换是有代价的。 它们阻止这些迭代器符合标准迭代器约定,因此无法与标准算法和容器一起使用。 例如,它们会阻止以下代码(其中 BBasicBlock)编译:

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

因此,这些隐式转换可能会被删除,并且 operator* 会更改为返回一个指针,而不是引用。

6.1.5 Finding call sites: a slightly more complex example 查找呼叫站点

假设您正在编写一个 FunctionPass,并且想要计算整个模块(即跨每个 Function)中某个函数(即某个 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 a Call 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 *CB = dyn_cast<CallBase>(&I)) {
            // We know we've encountered some kind of call instruction (call,
            // invoke, or callbr), so we need to determine if it's a call to
            // the function pointed to by m_func or not.
            if (CB->getCalledFunction() == targetFunc)
              ++callCounter;
          }
        }
      }
    }

  private:
    unsigned callCounter;
};

6.1.6 Iterating over def-use & use-def chains 迭代 def-use 和 use-def 链

通常,我们可能有一个 Value 类 (doxygen) 的实例,并且我们想确定哪些UserS使用该 Value。 具有特定Value的所有UserS的列表称为def-use 链(chain)。 例如,假设我们有一个名为 FFunction* 到一个特定的函数 foo。 查找所有use 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类(doxygen)的实例,并且需要知道它使用了哪些Values。 一个User使用的所有Values的列表称为 use-def 链(chain)。 类 Instruction 的实例是常见的 UserS ,因此我们可能希望遍历特定指令使用的所有值(即特定Instruction的操作数):

Instruction *pi = ...;

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

将对象声明为 const 是执行无突变算法(例如分析, 等)的重要工具。 为此,上述迭代器具有恒定的风格,如 Value::const_use_iteratorValue::const_op_iterator。 当分别在 const Value*Sconst User*S 上调用 use/op_begin() 时,它们会自动出现。 取消引用后,它们返回 const Use*S,否则上述模式保持不变。

6.1.6 Iterating over predecessors & successors of blocks 迭代块的前驱和后继

使用“llvm/IR/CFG.h”中定义的例程(routines)迭代块的前驱和后继非常容易。 只需使用这样的代码来迭代 BB 的所有前驱:

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

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

类似地,要迭代后继,请使用successors

6.2 Making Simple Changes

LLVM 基础架构中存在一些值得了解的原始转换操作。 在执行转换时,操作基本块的内容是非常常见的。 本节描述了一些常用的方法,并给出了示例代码。

6.2.1 Creating and inserting new Instructions

  1. Instantiating Instructions
    InstructionS的创建很直接简单:只需调用指令类型的构造函数来实例化并提供必要的参数。 例如,一个 AllocaInst 只需要一个 (const-ptr-to) Type。 因此:
auto *ai = new AllocaInst(Type::Int32Ty);

将创建一个 AllocaInst 实例,该实例表示运行时在当前堆栈帧中分配一个整数。 每个Instruction 子类可能有不同的默认参数,这些参数会改变指令的语义,因此请参阅 doxygen documentation for the subclass of Instruction,以获取您有兴趣实例化的指令子类。

  1. Naming values
    尽可能给指令的值命名非常有用,因为这有助于调试转换(transformations)。 如果您最终查看生成的 LLVM 机器码,肯定希望名称是有逻辑的,且与指令的结果相关联! 通过为Instruction构造函数的Name(默认)参数提供一个值,您可以将逻辑名称与指令在运行时的执行结果相关联。
    例如,假设我正在写一个为堆栈上的整数动态分配空间的转换,并且该整数将被其他代码用作某种索引。 为此,我在某个 Function 的第一个 BasicBlock 的第一个点放置了一个 AllocaInst,并且我打算在同一个 Function 中使用它。 我可能会这样做:
auto *pa = new AllocaInst(Type::Int32Ty, 0, "indexLoc");

其中, indexLoc 现在是指令执行值的逻辑名称,它是指向运行时堆栈上的整数的指针。

  1. Inserting instructions
    一般有三种方法可以将一个Instruction插入到组成一个BasicBlock的现有指令序列中:
  • Insertion into an explicit instruction list 插入显式指令列表

给定一个 BasicBlock* pb、该 BasicBlock 中的一个 Instruction* pi,以及我们希望在 *pi 之前插入一个新创建的指令,我们执行以下操作:

BasicBlock *pb = ...;
Instruction *pi = ...;
auto *newInst = new Instruction(...);

pb->getInstList().insert(pi, newInst); // Inserts newInst before pi in pb

添加到 BasicBlock 的末尾非常常见,以至于 Instruction 类和 Instruction 派生类提供了构造函数,该构造函数用一个指针指向要添加到的 BasicBlock 。 例如这样的代码:

BasicBlock *pb = ...;
auto *newInst = new Instruction(...);

pb->getInstList().push_back(newInst); // Appends newInst to pb

变成

BasicBlock *pb = ...;
auto *newInst = new Instruction(..., pb);

这更简洁,特别是如果您要创建长指令流。

  • Insertion into an implicit instruction list 插入隐式指令列表

已经在 BasicBlocks 中的Instruction 实例与现有指令列表隐式关联:封闭的基本块的指令列表。 因此,我们可以通过以下操作完成与上述代码相同的事情,而无需获得 BasicBlock

Instruction *pi = ...;
auto *newInst = new Instruction(...);

pi->getParent()->getInstList().insert(pi, newInst);

实际上,这一系列步骤非常常见,以至Instruction 类和Instruction 派生类提供了构造函数,这些构造函数采用(作为默认参数)一个指针,指向新创建的指令应该先于的某指令。 也就是说,Instruction构造函数能够将新创建的实例插入到所提供指令的 BasicBlock 中,紧接在该指令之前。
使用带有 insertBefore(默认)参数的 Instruction 构造函数,上面的代码变为:

Instruction* pi = ...;
auto *newInst = new Instruction(..., pi);

这更简洁,尤其是当您创建大量指令并将它们添加到 BasicBlocks 时。

  • Insertion using an instance of IRBuilder 使用 IRBuilder 的实例插入

使用先前的方法插入多个InstructionS 可能非常费力。 IRBuilder 是一个便利类,可用于在 BasicBlock 的末尾或特定Instruction之前添加几条指令。 它还支持常量折叠和重命名命名寄存器(参见 IRBuilder 的模板参数)。

下面的示例演示了 IRBuilder 的一个非常简单的用法,其中在指令 pi 之前插入了三个指令。 前两条指令是调用指令(Call instructions),第三条指令将两个调用的返回值相乘。

Instruction *pi = ...;
IRBuilder<> Builder(pi);
CallInst* callOne = Builder.CreateCall(...);
CallInst* callTwo = Builder.CreateCall(...);
Value* result = Builder.CreateMul(callOne, callTwo);

下面的示例与上面类似,只是创建的 IRBuilderBasicBlock pb 的末尾插入了指令。

BasicBlock *pb = ...;
IRBuilder<> Builder(pb);
CallInst* callOne = Builder.CreateCall(...);
CallInst* callTwo = Builder.CreateCall(...);
Value* result = Builder.CreateMul(callOne, callTwo);

有关 IRBuilder 的实际使用,请参阅 Kaleidoscope 教程。

6.2.2 Deleting Instructions

从构成 BasicBlock 的现有指令序列中删除指令非常简单:只需调用指令的 eraseFromParent() 方法。 例如:

Instruction *I = .. ;
I->eraseFromParent();

这会将指令与其包含的基本块取消链接并将其删除。 如果您只想从包含基本块的指令中取消链接但不删除它,您可以使用 removeFromParent() 方法。

6.2.3 Replacing an Instruction with another Value 用另一个值替换指令

  1. Replacing individual instructions 替换个别指令

头文件“llvm/Transforms/Utils/BasicBlockUtils.h”允许使用两个非常有用的替换函数:ReplaceInstWithValueReplaceInstWithInst

  1. Deleting Instructions 删除指令

ReplaceInstWithValue

此函数用一个值替换给定指令的所有使用,然后删除原始指令。 下面的示例说明了使用指向整数的空指针替换为单个整数分配内存的特定 AllocaInst 的结果。

AllocaInst* instToReplace = ...;
BasicBlock::iterator ii(instToReplace);

ReplaceInstWithValue(instToReplace->getParent()->getInstList(), ii,
                     Constant::getNullValue(PointerType::getUnqual(Type::Int32Ty)));

ReplaceInstWithInst

此功能用另一条指令替换特定指令,将新指令插入到旧指令所在位置的基本块中,并用新指令替换旧指令的任何使用。 以下示例说明了将一个 AllocaInst 替换为另一个。

AllocaInst* instToReplace = ...;
BasicBlock::iterator ii(instToReplace);

ReplaceInstWithInst(instToReplace->getParent()->getInstList(), ii,
                    new AllocaInst(Type::Int32Ty, 0, "ptrToReplacedInt"));
  1. Replacing multiple uses of Users and Values 替换用户和值的多种使用

您可以使用 Value::replaceAllUsesWithUser::replaceUsesOfWith 一次性更改多个使用。 有关详细信息,请分别参阅 Value ClassUser Class的 doxygen 文档。

6.2.4 Deleting GlobalVariables

从模块中删除全局变量就像删除指令一样简单。 首先,必须有一个指向要删除的全局变量的指针。 您可以使用此指针将其从其父模块(模块)中删除。 例如:

GlobalVariable *GV = .. ;

GV->eraseFromParent();
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值