Pass简介——SimplifyCFG
SimplifyCFGPass 是 LLVM编译器框架中的一个 控制流图优化(Control Flow Graph, CFG) 相关的 优化 Pass。它的主要作用是 简化控制流图(CFG),减少不必要的基本块(Basic Blocks)和跳转,从而提高指令级并行性、减少分支预测失败、提高编译器生成的代码质量。
其入口为 SimplifyCFGPass. cpp,其主要逻辑位于 SimplifyCFG. cpp。
根据 SimplifyCFG. cpp 的说明,可以知道这个文件主要用途是:
- 移除没有前驱的基本块。
- 如果基本块只有一个前驱,且前驱只有一个后继,则将该基本块合并到前驱中。
- 对于只有一个前驱的基本块,消除 PHI 节点。
- 消除仅包含无条件跳转的基本块。
- 将对 nounwind 函数的 invoke 指令转换为 call 指令。
- 将类似 “if (x) if (y)” 的结构转换为 “if (x&y)”。
代表性函数
MergeBlockIntoPredecessor
这个函数定义于 BasicBlockUtils. cpp,但是在该文件起辅助作用,因为满足时可以使用该函数合并基本块。我们可以看到次级入口函数 simplifyCFG 中有这个函数。当前的
- 前驱只有一个:只有一个前驱的基本块才会考虑合并,避免了像循环中的基本块(有多个前驱)被误合并的情况。
- 前驱只有一个后继:基本块的前驱只有一个后继时,才进行合并。这样做是为了避免循环或者分支结构的破坏,确保控制流图的结构不被错误修改。
- 没有 PHI 节点:==如果基本块中存在 PHI 节点,则不允许合并。==这是因为 PHI 节点涉及到不同控制流路径的合并,合并后可能会导致不明确的值或冲突。
- 删除无用的基本块:如果基本块没有前驱(或者只有自己作为前驱),就会被删除,避免冗余的控制流。
显然这个要求是比较粗略的,但是也可能为了防止逻辑过于复杂(更激进的策略)带来的复杂度开销
bool SimplifyCFGOpt::simplifyOnce(BasicBlock *BB) {
bool Changed = false;
assert(BB && BB->getParent() && "Block not embedded in function!");
assert(BB->getTerminator() && "Degenerate basic block encountered!");
// Remove basic blocks that have no predecessors (except the entry block)...
// or that just have themself as a predecessor. These are unreachable.
if ((pred_empty(BB) && BB != &BB->getParent()->getEntryBlock()) ||
BB->getSinglePredecessor() == BB) {
LLVM_DEBUG(dbgs() << "Removing BB: \n" << *BB);
DeleteDeadBlock(BB, DTU);
return true;
}
// Check to see if we can constant propagate this terminator instruction
// away...
Changed |= ConstantFoldTerminator(BB, /*DeleteDeadConditions=*/true,
/*TLI=*/nullptr, DTU);
// Check for and eliminate duplicate PHI nodes in this block.
Changed |= EliminateDuplicatePHINodes(BB);
// Check for and remove branches that will always cause undefined behavior.
if (removeUndefIntroducingPredecessor(BB, DTU, Options.AC))
return requestResimplify();
// Merge basic blocks into their predecessor if there is only one distinct
// pred, and if there is only one distinct successor of the predecessor, and
// if there are no PHI nodes.
if (MergeBlockIntoPredecessor(BB, DTU))
return true;
if (SinkCommon && Options.SinkCommonInsts)
if (sinkCommonCodeFromPredecessors(BB, DTU) ||
mergeCompatibleInvokes(BB, DTU)) {
// sinkCommonCodeFromPredecessors() does not automatically CSE PHI's,
// so we may now how duplicate PHI's.
// Let's rerun EliminateDuplicatePHINodes() first,
// before foldTwoEntryPHINode() potentially converts them into select's,
// after which we'd need a whole EarlyCSE pass run to cleanup them.
return true;
}
IRBuilder<> Builder(BB);
if (Options.SpeculateBlocks &&
!BB->getParent()->hasFnAttribute(Attribute::OptForFuzzing)) {
// If there is a trivial two-entry PHI node in this basic block, and we can
// eliminate it, do so now.
if (auto *PN = dyn_cast<PHINode>(BB->begin()))
if (PN->getNumIncomingValues() == 2)
if (foldTwoEntryPHINode(PN, TTI, DTU, Options.AC, DL,
Options.SpeculateUnpredictables))
return true;
}
Instruction *Terminator = BB->getTerminator();
Builder.SetInsertPoint(Terminator);
switch (Terminator->getOpcode()) {
case Instruction::Br:
Changed |= simplifyBranch(cast<BranchInst>(Terminator), Builder);
break;
case Instruction::Resume:
Changed |= simplifyResume(cast<ResumeInst>(Terminator), Builder);
break;
case Instruction::CleanupRet:
Changed |= simplifyCleanupReturn(cast<CleanupReturnInst>(Terminator));
break;
case Instruction::Switch:
Changed |= simplifySwitch(cast<SwitchInst>(Terminator), Builder);
break;
case Instruction::Unreachable:
Changed |= simplifyUnreachable(cast<UnreachableInst>(Terminator));
break;
case Instruction::IndirectBr:
Changed |= simplifyIndirectBr(cast<IndirectBrInst>(Terminator));
break;
}
return Changed;
}
另一处 MergeBlockIntoPredecessor 位于函数 foldCondBranchOnValueKnownInPredecessorImpl
之中,其主要是在处理 条件分支时 通过常量已知值简化控制流,然后可以合并基本块。
hoistCommonCodeFromSuccessors
,sinkCommonCodeFromPredecessors
两个函数顾名思义是从后继块和前驱块中提取公共指令,并将它们提升到更合适的位置,以减少冗余(可能的重复部分)。
常用超参数
PHINodeFoldingThreshold
:PHI 节点折叠 的 启用门槛。它的默认值是 2。tTwoEntryPHINodeFoldingThreshold
: 折叠具有两个入口的 PHI 节点(即只有两个输入值的 PHI 节点)时 可以接受的最大总指令成本,默认值是 4。
上述两个都是启发式调参因子,所以没有直接意义。
评论