Pass_02 指令的相关操作

在 [上一节](./Pass_01 写一个pass) 的基础上,本节展示一些指令的相关操作

遍历操作

这一节主要是讲如何进行一些简单的转换和遍历的操作

首先回顾LLVM IR中的组件结构:

IR中的组件结构

对于一个可迭代的序列,xxxbegin() 返回一个指向序列开始的迭代器,xxxend() 返回一个指向序列结尾的迭代器,两者之间有一些xxxiterator()

在各个组件结构中,有一些常见的遍历方法:

遍历Function中的BasicBlock

Function &Func = ...
for (BasicBlock &BB : Func)
    // 遍历Function中的所有BasicBlock,并输出每个BB中有几个Instruction
      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";

然而这其实不是输出BB中的Ins的最好方法,因为ostream对几乎所有内容都是重载的。

可以在基本块本身调用errs() << BB << "\n";

遍历Function中的Instruction

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

或者用一个worklist把所有的Ins都保存下来:

std::set<Instruction*> worklist;

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

把迭代器转换成类指针

为什么要这么转换呢?因为有时,手边只有一个迭代器,这时候获取实例的指针就很有用。

假设i 是一个 BasicBlock::iteratorjBasicBlock::const_iterator:

Instruction& inst = *i;
// 下面两行等价
Instruction* pinst = &*i; 
Instruction *pinst = i;
const Instruction& inst = *j;

也可以把类指针转换成迭代器,这个操作很高效。

获取某个对象的迭代器:

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

一个稍微复杂的例子

假如你正在写一个FunctionPass,想要数一数整个模块中的某个函数在域中的出现了多少次(不用InstVisitor)。

由于正在写FunctionPass所以你要重写runOnFunction函数

Function* targetFunc = ...;

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

    virtual runOnFunction(Function& F) {
        for (BasicBlock &B : F) {
            for (Instruction &I: B) {
                // dyn_cast<CallBase>(&I)来检查指令I是否符合CallBase的模式匹配
                if (auto *CB = dyn_cast<CallBase>(&I)) {
                    // 检查是否是目标函数
                    if (CB->getCalledFunction() == targetFunc)
                        ++callCounter;
                }
            }
        }
    }

    private:
    unsigned callCounter;
};

在def-use 和 use-def 链中遍历

我们经常拿到一个Value类的实例,想要决定哪个User正在用这个Value,这个Value的所有的users叫做def-use链,找到所有的用Function *F的指令:

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类的实例时,想要知道它用了哪些Value,所有的这个User用的Value叫做use-def链.

Instruction类的实例是最普通的User,所以我们想要遍历某个Ins用的所有的values

Instruction *pi = ...;

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

遍历某一个BB的前面的BB和后面的BB

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

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

同理successors

创建指令

创建和插入一些简单的Instructions

auto *ai = new AllocaInst(Type::Int32Ty);

上面这行创建了一个AllocaInstIns,它代表一个整数在当前栈的位置,每一个Ins的子类都有不同的默认参数,这些参数会改变Ins的语意,可以看这个链接: doxygen documentation for the subclass of Instruction

给指令的值命名

如果你查看机器码,你肯定想把指令的结果和逻辑名关联起来。可以通过给指令的构造函数的Name参数提供一个值。

例如,我正在写一个transform,动态的给堆栈上的一个整数分配空间,这个证书将被其他的一些代码用作某个索引:

auto *pa = new AllocaInst(Type::Int32Ty, 0, "indexLoc");

插入指令

有3种方法可以将指令插入到构成BB的现有指令序列中:

  • 插入到显式指令列表中

    想把NewInst插入到pi的前面:

    BasicBlock *pb = ...;
    Instruction *pi = ...;
    auto *newInst = new Instruction(...);
    
    pb->getInstList().insert(pi, newInst); 
    

    插到BB的结尾:

    BasicBlock *pb = ...;
    auto *newInst = new Instruction(...);
    
    pb->getInstList().push_back(newInst); 
    

    或者更简单的:

    BasicBlock *pb = ...;
    auto *newInst = new Instruction(..., pb);
    
  • 插到隐式指令列表里

    BB中的指令实例隐式的与一个指令列表相关联,因此,我们可以在没有BB的情况下完成上面代码相同的事情:

    Instruction *pi = ...;
    auto *newInst = new Instruction(...);
    
    pi->getParent()->getInstList().insert(pi, newInst);
    

    可以直接用构造函数,把新指令插到某一个指令的前面:

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

​ 当你要构造很多Ins并把它们加到BB中,可以用上面这个方法。

  • IRBuilder的实例插

    IRBuilder可以很方便的在某个BB的结尾或是某个指令的前面插入多条指令。

    下面这个例子展示了IRBuilder的一个简单用法,在pi之前插入3条指令。

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

    在BB的后面插入指令:

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

删除指令

删除指令

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

如果你只想把这个指令和BB取消链接而不删它,可以用removeFromParent()方法

删除全局变量

GlobalVariable *GV = .. ;

GV->eraseFromParent();

替换指令

#include "llvm/Transforms/Utils/BasicBlockUtils.h"

然后就能用2个很好用的替换函数: ReplaceInstWithValueReplaceInstWithInst.

ReplaceInstWithValue

把某一个指令的所有use都用一个value替换,然后删除原指令:

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

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

ReplaceInstWithInst

用新指令替换旧指令,同时替换所有的use

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

ReplaceInstWithInst(instToReplace->getParent()->getInstList(), ii,
                    new AllocaInst(Type::Int32Ty, 0, "ptrToReplacedInt"));

参考链接:

helpful-hints-for-common-operations


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 1149440709@qq.com

×

喜欢就点赞,疼爱就打赏