在 [上一节](./Pass_01 写一个pass) 的基础上,本节展示一些指令的相关操作
遍历操作
这一节主要是讲如何进行一些简单的转换和遍历的操作
首先回顾LLVM 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::iterator
,j
是 BasicBlock::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);
上面这行创建了一个AllocaInst
Ins,它代表一个整数在当前栈的位置,每一个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个很好用的替换函数: ReplaceInstWithValue
和 ReplaceInstWithInst
.
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