SplitBasicBlocks是一个llvm体系下的标准Pass风格的代码处理组件,继承自FunctionPass。 根据llvm的官方文档描述,这种Pass只会作用于Function这种粒度,也就是llvm在编译流程里面,只有对单个Function应用Pass逻辑的时候, 才会调用继承自FunctionPass的各种Pass(包括llvm自带的Pass和普通开发人员自定义的Pass), 来看下官方解释:
All LLVM passes are subclasses of the Pass class, which implement functionality by overriding virtual methods inherited from Pass. Depending on how your pass works, you should inherit from the ModulePass , CallGraphSCCPass, FunctionPass , or LoopPass, or RegionPass, or BasicBlockPass classes, which gives the system more information about what your pass does, and how it can be combined with other passes. One of the main features of the LLVM Pass Framework is that it schedules passes to run in an efficient way based on the constraints that your pass meets (which are indicated by which class they derive from).
我粗略的翻译一下:所有的LLVM Pass都是Pass这个类的子类,这些Pass继承自Pass类,然后通过实现Pass类的虚函数来实现自身的功能。如果你要自己实现一个Pass, 那么你要从哪个类继承呢?目前LLVM系统提供了 ModulePass、CallGraphSCCPass、FunctionPass、LoopPass和BasicBlocks这个基类可供使用,根据你想要实现的功能来选择从哪个类来继承。一旦你写好了自己的Pass, LLVM Pass Framework会以一种高效的方式在LLVM编译过程中在合适的时机调用你的Pass(具体的调用实际就完全取决于你继承的基类)
上面说了SplitBasicBlocks是一个什么东西,那它的实际作用是什么呢?为什么要有SplitBasicBlocks这么一个Pass呢?
这里我大致简述一下代码流程平坦化:
了解了什么是流程平坦化之后,那我们来思考,按照图中左半部分标示出来的A B C三个代码块来看,如果我们把ABC三个代码快每个都再进行细粒度的切割,变成A1,A2...Ax, B1,B2,Bx, C1,C2,Cx这样,那再按照流程平坦化的逻辑去进行一次代码重组,重组之后的画面,大家可以脑补一下,重组之后的代码会充斥着switch case,而且case的顺序还是随机的,结果是大大的降低了整个代码逻辑的可读性。
29 namespace { 30 struct SplitBasicBlock : public FunctionPass { 31 static char ID; // Pass identification, replacement for typeid 32 bool flag; 33 34 SplitBasicBlock() : FunctionPass(ID) {} 35 SplitBasicBlock(bool flag) : FunctionPass(ID) { 36 37 this->flag = flag; 38 } 39 40 bool runOnFunction(Function &F); 41 void split(Function *f); 42 43 bool containsPHI(BasicBlock *b); 44 void shuffle(std::vector<int> &vec); 45 }; 46 }
55 bool SplitBasicBlock::runOnFunction(Function &F) { 56 // Check if the number of applications is correct 57 if (!((SplitNum > 1) && (SplitNum <= 10))) { 58 errs()<<"Split application basic block percentage\ 59 -split_num=x must be 1 < x <= 10"; 60 return false; 61 } 62 63 Function *tmp = &F; 64 65 // Do we obfuscate 66 if (toObfuscate(flag, tmp, "split")) { 67 split(tmp); 68 ++Split; 69 } 70 71 return false; 72 }
74 void SplitBasicBlock::split(Function *f) { 75 std::vector<BasicBlock *> origBB; 76 int splitN = SplitNum; 77 78 // Save all basic blocks 79 for (Function::iterator I = f->begin(), IE = f->end(); I != IE; ++I) { 80 origBB.push_back(&*I); 81 } 82 83 for (std::vector<BasicBlock *>::iterator I = origBB.begin(), 84 IE = origBB.end(); 85 I != IE; ++I) { 86 BasicBlock *curr = *I; 87 88 // 这里省略了一些细枝末节... 98 // 这里开始,生成切割点 99 // Generate splits point 100 std::vector<int> test; 101 for (unsigned i = 1; i < curr->size(); ++i) { 102 test.push_back(i); 103 } 104 105 // Shuffle 106 if (test.size() != 1) { 107 shuffle(test); 108 std::sort(test.begin(), test.begin() + splitN); 109 } 110 //切割点生成完毕,下面开始切割 111 // Split 112 BasicBlock::iterator it = curr->begin(); 113 BasicBlock *toSplit = curr; 114 int last = 0; 115 for (int i = 0; i < splitN; ++i) { 116 for (int j = 0; j < test[i] - last; ++j) { 117 ++it; 118 } 119 last = test[i]; 120 if(toSplit->size() < 2) 121 continue; 122 toSplit = toSplit->splitBasicBlock(it, toSplit->getName() + ".split"); 123 } 124 125 ++Split; 126 } 127 }
363 /// This splits a basic block into two at the specified 364 /// instruction. Note that all instructions BEFORE the specified iterator stay 365 /// as part of the original basic block, an unconditional branch is added to 366 /// the new BB, and the rest of the instructions in the BB are moved to the new 367 /// BB, including the old terminator. This invalidates the iterator. 368 /// 369 /// Note that this only works on well formed basic blocks (must have a 370 /// terminator), and 'I' must not be the end of instruction list (which would 371 /// cause a degenerate basic block to be formed, having a terminator inside of 372 /// the basic block). 373 /// 374 BasicBlock *BasicBlock::splitBasicBlock(iterator I, const Twine &BBName) { 375 assert(getTerminator() && "Can't use splitBasicBlock on degenerate BB!"); 376 assert(I != InstList.end() && 377 "Trying to get me to create degenerate basic block!"); 378 379 BasicBlock *New = BasicBlock::Create(getContext(), BBName, getParent(), 380 this->getNextNode()); 381 382 // Save DebugLoc of split point before invalidating iterator. 383 DebugLoc Loc = I->getDebugLoc(); 384 // Move all of the specified instructions from the original basic block into 385 // the new basic block. 386 New->getInstList().splice(New->end(), this->getInstList(), I, end()); 387 388 // Add a branch instruction to the newly formed basic block. 389 BranchInst *BI = BranchInst::Create(New, this); 390 BI->setDebugLoc(Loc); 391 392 // Now we must loop through all of the successors of the New block (which 393 // _were_ the successors of the 'this' block), and update any PHI nodes in 394 // successors. If there were PHI nodes in the successors, then they need to 395 // know that incoming branches will be from New, not from Old. 396 // 397 for (succ_iterator I = succ_begin(New), E = succ_end(New); I != E; ++I) { 398 // Loop over any phi nodes in the basic block, updating the BB field of 399 // incoming values... 400 BasicBlock *Successor = *I; 401 PHINode *PN; 402 for (BasicBlock::iterator II = Successor->begin(); 403 (PN = dyn_cast<PHINode>(II)); ++II) { 404 int IDX = PN->getBasicBlockIndex(this); 405 while (IDX != -1) { 406 PN->setIncomingBlock((unsigned)IDX, New); 407 IDX = PN->getBasicBlockIndex(this); 408 } 409 } 410 } 411 return New; 412 }
博客地址:https://www.cnblogs.com/ilocker/p/4897325.html
什么是 PHI node?
所有 LLVM 指令都使用 SSA (Static Single Assignment,静态一次性赋值) 方式表示。意思是所有变量都只能被赋值一次,这样做主要是便于后期的代码优化。1 a = 1; 2 if (v < 10) 3 a = 2; 4 b = a;假设 v 的值小于 10,变量 a 就要被赋值为 2,但 a 已经被赋值了一次,由于 SSA 性质的约束,只能赋值另外一个“a”。最后在给 b 赋值时,通过添加一个 PHI node,由其来决定选择哪个版本的 a 来给 b 赋值。
1 a1 = 1; 2 if (v < 10) 3 a2 = 2; 4 b = PHI(a1, a2);PHI node 根据控制流是从哪一个 block (“a1 = 1” or “a2 = 2”) 到达,来决定使用 a1 还是 a2 来给 b 赋值。
这些 PHI node 必须在 IR 中显示创建,llvm 指令集中有对应的 phi 指令。
[公告]安全服务和外包项目请将项目需求发到看雪企服平台:https://qifu.kanxue.com
最后于 16小时前 被freakish编辑 ,原因: