LLVM中对循环的识别是十分的简单。他是一种类似于Tarjan的可规约流图识别(《Testing flow graph reducibility》),不同的是LLVM是基于dom-tree的遍历,这有点像区域分析。因为这个原因,LoopInfo这个模块是没法找到不可规约结构的,因为不可规约循环的循环头不支配循环体。其实这点也可以理解,不可规约循环本来就少见,LLVM也不是什么高性能计算编译器。
循环的识别在LoopInfo.cpp中,其中也包含了一些辅助函数。
循环分析的入口:
LoopInfo LoopAnalysis::run(Function & F, FunctionAnalysisManager & AM) {
LoopInfo LI;
LI.analyze(AM.getResult<DominatorTreeAnalysis>(F));
return LI;
}
分析算法会按dom-tree的后序序列进行遍历,这样可以先找到最内层的循环(后序,逆后序)。每遍历一个node就会看其对应的block是否支配他的某个前继block,如果是,说明这个block是loop header而它和这个前继block之间的边就是back edge。一旦找到这样一对block后,就可以从这个前继block开始沿着reverse cfg来找到循环体中的所有结点。
/// Analyze LoopInfo discovers loops during a postorder DominatorTree traversal
/// interleaved with backward CFG traversals within each subloop
/// (discoverAndMapSubloop). The backward traversal skips inner subloops, so
/// this part of the algorithm is linear in the number of CFG edges. Subloop and
/// Block vectors are then populated during a single forward CFG traversal
/// (PopulateLoopDFS).
///
/// During the two CFG traversals each block is seen three times:
/// 1) Discovered and mapped by a reverse CFG traversal.
/// 2) Visited during a forward DFS CFG traversal.
/// 3) Reverse-inserted in the loop in postorder following forward DFS.
///
/// The Block vectors are inclusive, so step 3 requires loop-depth number of
/// insertions per block.
template <class BlockT, class LoopT>
void LoopInfoBase<BlockT, LoopT>::analyze(const DomTreeBase<BlockT> & DomTree) {
// Postorder traversal of the dominator tree.
const DomTreeNodeBase<BlockT>* DomRoot = DomTree.getRootNode();
// 后序遍历domtree
for (auto DomNode : post_order(DomRoot)) {
BlockT* Header = DomNode->getBlock();
SmallVector<BlockT*, 4> Backedges;
// 如果一个结点支配其前继,则将这个边识别为backedge
for (const auto Backedge : children<Inverse<BlockT*>>(Header)) {
if (DomTree.dominates(Header, Backedge) &&
DomTree.isReachableFromEntry(Backedge)) {
Backedges.push_back(Backedge);
}
}
// 沿着reverse cfg的找到所有循环体
if (!Backedges.empty()) {
// 初始化一个loop,以backedge的端点为loop header
LoopT* L = AllocateLoop(Header);
// 构造循环体
discoverAndMapSubloop(L, ArrayRef<BlockT*>(Backedges), this, DomTree);
}
}
// 完善循环嵌套关系
PopulateLoopsDFS<BlockT, LoopT> DFS(this);
DFS.traverse(DomRoot->getBlock());
}
在discoverAndMapSubloop中,从所有backedge的source开始,沿着reverse cfg找到所有的循环体block。注意因为analyze中是按支配树的后序进行遍历的,因此任何存在的sub-loop都在当前循环头的支配域中,并且根据后序遍历的性质他们肯定已经被遍历完毕,配合循环性质:两个循环要么包含要么不相交,绝不可能部分重合,可以证明这个算法的正确性。
在遍历的过程中会分为两种情况,一种是候选block目前不在任何循环中,则直接将该block放到当前循环中(函数的第一个参数)并将其所有前继成为候选block,另一种是候选block已经是某个循环的一部分,则这时候将这个循环的outermost循环成为当前循环的父循环,并将其循环头的所有前继成为候选block。
/// Discover a subloop with the specified backedges such that: All blocks within
/// this loop are mapped to this loop or a subloop. And all subloops within this
/// loop have their parent loop set to this loop or a subloop.
template <class BlockT, class LoopT>
static void discoverAndMapSubloop(LoopT * L, ArrayRef<BlockT*> Backedges,
LoopInfoBase<BlockT, LoopT> * LI,
const DomTreeBase<BlockT> & DomTree) {
typedef GraphTraits<Inverse<BlockT*>> InvBlockTraits;
unsigned NumBlocks = 0;
unsigned NumSubloops = 0;
// Perform a backward CFG traversal using a worklist.
std::vector<BlockT*> ReverseCFGWorklist(Backedges.begin(), Backedges.end());
while (!ReverseCFGWorklist.empty()) {
BlockT* PredBB = ReverseCFGWorklist.back();
ReverseCFGWorklist.pop_back();
// 获取当前节点所在的循环
// 如果没有,则将其前继加入到worklist中
// 如果有,则将其outermost循环头节点的前继加入到worklist中
LoopT* Subloop = LI->getLoopFor(PredBB);
if (!Subloop) {
if (!DomTree.isReachableFromEntry(PredBB))
continue;
// 将这个结点加入到当前的循环中
LI->changeLoopFor(PredBB, L);
++NumBlocks;
if (PredBB == L->getHeader())
continue;
// 将所有前继加入到worklist中
ReverseCFGWorklist.insert(ReverseCFGWorklist.end(),
InvBlockTraits::child_begin(PredBB),
InvBlockTraits::child_end(PredBB));
}
else {
// 获取最外层的循环
while (LoopT * Parent = Subloop->getParentLoop())
Subloop = Parent;
// If it is already discovered to be a subloop of this loop, continue.
if (Subloop == L)
continue;
// 设置循环嵌套关系
Subloop->setParentLoop(L);
++NumSubloops;
NumBlocks += Subloop->getBlocksVector().capacity();
PredBB = Subloop->getHeader();
// 将子循环头节点的前继加入到worklist中。
for (const auto Pred : children<Inverse<BlockT*>>(PredBB)) {
if (LI->getLoopFor(Pred) != Subloop)
ReverseCFGWorklist.push_back(Pred);
}
}
}
L->getSubLoopsVector().reserve(NumSubloops);
L->reserveBlocks(NumBlocks);
}
最后一步是收尾工作,完善循环嵌套关系,这段逻辑很简单:
/// Add a single Block to its ancestor loops in PostOrder. If the block is a
/// subloop header, add the subloop to its parent in PostOrder, then reverse the
/// Block and Subloop vectors of the now complete subloop to achieve RPO.
template <class BlockT, class LoopT>
void PopulateLoopsDFS<BlockT, LoopT>::insertIntoLoop(BlockT * Block) {
LoopT* Subloop = LI->getLoopFor(Block);
// 如果当前结点是循环头节点,则设置循环嵌套关系
if (Subloop && Block == Subloop->getHeader()) {
// We reach this point once per subloop after processing all the blocks in
// the subloop.
if (Subloop->getParentLoop())
Subloop->getParentLoop()->getSubLoopsVector().push_back(Subloop);
else
LI->addTopLevelLoop(Subloop);
// For convenience, Blocks and Subloops are inserted in postorder. Reverse
// the lists, except for the loop header, which is always at the beginning.
Subloop->reverseBlock(1);
std::reverse(Subloop->getSubLoopsVector().begin(),
Subloop->getSubLoopsVector().end());
Subloop = Subloop->getParentLoop();
}
// 在每个祖先循环中加入当前basicblock
for (; Subloop; Subloop = Subloop->getParentLoop())
Subloop->addBlockEntry(Block);
}
实际上,LoopInfo类记录了一个函数中的所有的循环,其中TopLevelLoops成员记录了所有的outermost循环。而每个循环对象中都包含了下一级循环以及循环体中包含的所有block,注意所有子循环的block也包含在内。
循环的识别是主要工作,除了这个外,这个模块还提供了许多辅助性函数,通过阅读这些函数代码,也可以让我们更好的了解循环的性质。
1,ExitingBlock:循环内即将退出循环的block,ExitBlock:循环推出后第一个到达的block。
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::getExitingBlocks(
SmallVectorImpl<BlockT*>& ExitingBlocks) const {
assert(!isInvalid() && "Loop not in a valid state!");
for (const auto BB : blocks())
// 如果有后继不再循环内,则它就是exit block
for (const auto& Succ : children<BlockT*>(BB))
if (!contains(Succ)) {
// Not in current loop? It must be an exit block.
ExitingBlocks.push_back(BB);
break;
}
}
template <class BlockT, class LoopT>
void LoopBase<BlockT, LoopT>::getExitBlocks(
SmallVectorImpl<BlockT*> & ExitBlocks) const {
assert(!isInvalid() && "Loop not in a valid state!");
for (const auto BB : blocks())
for (const auto& Succ : children<BlockT*>(BB))
if (!contains(Succ))
// Not in current loop? It must be an exit block.
ExitBlocks.push_back(Succ);
}
2,PreHeader:循环头的唯一循环外前继并且该前继只有循环头一个后继。后面各种代码提升需要这个特殊block。
// 判断循环头的唯一前继是否以循环头为唯一后继,同时判断该前继是否以循环头为唯一后继。还需要确定能否将代码提升到这个前继中。
template <class BlockT, class LoopT>
BlockT * LoopBase<BlockT, LoopT>::getLoopPreheader() const {
// 获取唯一循环外前继
BlockT* Out = getLoopPredecessor();
if (!Out)
return nullptr;
// 可以进行代码提升
if (!Out->isLegalToHoistInto())
return nullptr;
// 判断循环头是否也是该前继的唯一后继
typedef GraphTraits<BlockT*> BlockTraits;
typename BlockTraits::ChildIteratorType SI = BlockTraits::child_begin(Out);
++SI;
if (SI != BlockTraits::child_end(Out))
return nullptr; // Multiple exits from the block, must not be a preheader.
// The predecessor has exactly one successor, so it is a preheader.
return Out;
}
// 获取循环头的唯一前继
template <class BlockT, class LoopT>
BlockT* LoopBase<BlockT, LoopT>::getLoopPredecessor() const {
BlockT* Out = nullptr;
BlockT* Header = getHeader();
// 遍历头节点的所有前继,并记录其唯一的preheader
for (const auto Pred : children<Inverse<BlockT*>>(Header)) {
if (!contains(Pred)) { // If the block is not in the loop...
if (Out && Out != Pred)
return nullptr; // Multiple predecessors outside the loop
Out = Pred;
}
}
return Out;
}
3,Latch:循环内唯一跳转到循环头的结点,可能有退出循环语句或者continue语句。
// latch是一条分支进入循环头的结点。
template <class BlockT, class LoopT>
BlockT* LoopBase<BlockT, LoopT>::getLoopLatch() const {
BlockT* Header = getHeader();
BlockT* Latch = nullptr;
for (const auto Pred : children<Inverse<BlockT*>>(Header)) {
if (contains(Pred)) {
if (Latch)
return nullptr;
Latch = Pred;
}
}
return Latch;
}
4,DedicatedExits:和PreHeader类似但是相反,即所有的ExitBlocks的前继只能在循环内。
template <class BlockT, class LoopT>
bool LoopBase<BlockT, LoopT>::hasDedicatedExits() const {
// Each predecessor of each exit block of a normal loop is contained
// within the loop.
SmallVector<BlockT*, 4> ExitBlocks;
getExitBlocks(ExitBlocks);
// 所有ExitBlocks只能有位于循环内的前继
for (BlockT* EB : ExitBlocks)
for (BlockT* Predecessor : children<Inverse<BlockT*>>(EB))
if (!contains(Predecessor))
return false;
// All the requirements are met.
return true;
}
循环识别技术现在已经比较成熟了,有很多更方便和成熟的技术,甚至还有单遍DFS就能识别循环结构的算法(《A New Algorithm for Identifying Loops in Decompilation》)。这些往往都能识别出完整的循环,包括可规约结构和不可规约结构,但是对不可规约的结构的优化有很大限制或者需要对流图进行一些变换(《Controlled node splitting》)。而不可规约结构在编译过程中往往不常见(反编译中或许会比较常见),所以LLVM中这种只是识别了可规约循环。