forked from OSchip/llvm-project
[BOLT] Add pass to normalize CFG
Summary: Some optimizations may remove all instructions in a basic block. The pass will cleanup the CFG afterwards by removing empty basic blocks and merging duplicate CFG edges. The normalized CFG is printed under '-print-normalized' option. (cherry picked from FBD32774360)
This commit is contained in:
parent
fd71cc5163
commit
cbf530bf41
|
@ -89,6 +89,24 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
/// The pass normalizes CFG by performing the following transformations:
|
||||
/// * removes empty basic blocks
|
||||
/// * merges duplicate edges and updates jump instructions
|
||||
class NormalizeCFG : public BinaryFunctionPass {
|
||||
std::atomic<uint64_t> NumBlocksRemoved{0};
|
||||
std::atomic<uint64_t> NumDuplicateEdgesMerged{0};
|
||||
|
||||
void runOnFunction(BinaryFunction &BF);
|
||||
|
||||
public:
|
||||
NormalizeCFG(const cl::opt<bool> &PrintPass)
|
||||
: BinaryFunctionPass(PrintPass) {}
|
||||
|
||||
const char *getName() const override { return "normalize CFG"; }
|
||||
|
||||
void runOnFunctions(BinaryContext &) override;
|
||||
};
|
||||
|
||||
/// Detect and eliminate unreachable basic blocks. We could have those
|
||||
/// filled with nops and they are used for alignment.
|
||||
class EliminateUnreachableBlocks : public BinaryFunctionPass {
|
||||
|
|
|
@ -272,6 +272,90 @@ bool BinaryFunctionPass::shouldPrint(const BinaryFunction &BF) const {
|
|||
return BF.isSimple() && !BF.isIgnored();
|
||||
}
|
||||
|
||||
void NormalizeCFG::runOnFunction(BinaryFunction &BF) {
|
||||
uint64_t NumRemoved = 0;
|
||||
uint64_t NumDuplicateEdges = 0;
|
||||
uint64_t NeedsFixBranches = 0;
|
||||
for (BinaryBasicBlock &BB : BF) {
|
||||
if (!BB.empty())
|
||||
continue;
|
||||
|
||||
if (BB.isEntryPoint() || BB.isLandingPad())
|
||||
continue;
|
||||
|
||||
// Handle a dangling empty block.
|
||||
if (BB.succ_size() == 0) {
|
||||
// If an empty dangling basic block has a predecessor, it could be a
|
||||
// result of codegen for __builtin_unreachable. In such case, do not
|
||||
// remove the block.
|
||||
if (BB.pred_size() == 0) {
|
||||
BB.markValid(false);
|
||||
++NumRemoved;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// The block should have just one successor.
|
||||
BinaryBasicBlock *Successor = BB.getSuccessor();
|
||||
assert(Successor && "invalid CFG encountered");
|
||||
|
||||
// Redirect all predecessors to the successor block.
|
||||
while (!BB.pred_empty()) {
|
||||
BinaryBasicBlock *Predecessor = *BB.pred_begin();
|
||||
if (Predecessor->hasJumpTable())
|
||||
break;
|
||||
|
||||
if (Predecessor == Successor)
|
||||
break;
|
||||
|
||||
BinaryBasicBlock::BinaryBranchInfo &BI = Predecessor->getBranchInfo(BB);
|
||||
Predecessor->replaceSuccessor(&BB, Successor, BI.Count,
|
||||
BI.MispredictedCount);
|
||||
// We need to fix branches even if we failed to replace all successors
|
||||
// and remove the block.
|
||||
NeedsFixBranches = true;
|
||||
}
|
||||
|
||||
if (BB.pred_empty()) {
|
||||
BB.removeAllSuccessors();
|
||||
BB.markValid(false);
|
||||
++NumRemoved;
|
||||
}
|
||||
}
|
||||
|
||||
if (NumRemoved)
|
||||
BF.eraseInvalidBBs();
|
||||
|
||||
// Check for duplicate successors. Do it after the empty block elimination as
|
||||
// we can get more duplicate successors.
|
||||
for (BinaryBasicBlock &BB : BF)
|
||||
if (!BB.hasJumpTable() && BB.succ_size() == 2 &&
|
||||
BB.getConditionalSuccessor(false) == BB.getConditionalSuccessor(true))
|
||||
++NumDuplicateEdges;
|
||||
|
||||
// fixBranches() will get rid of duplicate edges and update jump instructions.
|
||||
if (NumDuplicateEdges || NeedsFixBranches)
|
||||
BF.fixBranches();
|
||||
|
||||
NumDuplicateEdgesMerged += NumDuplicateEdges;
|
||||
NumBlocksRemoved += NumRemoved;
|
||||
}
|
||||
|
||||
void NormalizeCFG::runOnFunctions(BinaryContext &BC) {
|
||||
ParallelUtilities::runOnEachFunction(
|
||||
BC, ParallelUtilities::SchedulingPolicy::SP_BB_LINEAR,
|
||||
[&](BinaryFunction &BF) { runOnFunction(BF); },
|
||||
[&](const BinaryFunction &BF) { return !shouldOptimize(BF); },
|
||||
"NormalizeCFG");
|
||||
if (NumBlocksRemoved)
|
||||
outs() << "BOLT-INFO: removed " << NumBlocksRemoved << " empty block"
|
||||
<< (NumBlocksRemoved == 1 ? "" : "s") << '\n';
|
||||
if (NumDuplicateEdgesMerged)
|
||||
outs() << "BOLT-INFO: merged " << NumDuplicateEdgesMerged
|
||||
<< " duplicate CFG edge" << (NumDuplicateEdgesMerged == 1 ? "" : "s")
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
void EliminateUnreachableBlocks::runOnFunction(BinaryFunction& Function) {
|
||||
if (Function.layout_size() > 0) {
|
||||
unsigned Count;
|
||||
|
|
|
@ -147,6 +147,13 @@ PrintICP("print-icp",
|
|||
cl::Hidden,
|
||||
cl::cat(BoltOptCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
PrintNormalized("print-normalized",
|
||||
cl::desc("print functions after CFG is normalized"),
|
||||
cl::ZeroOrMore,
|
||||
cl::Hidden,
|
||||
cl::cat(BoltCategory));
|
||||
|
||||
static cl::opt<bool>
|
||||
PrintRegReAssign("print-regreassign",
|
||||
cl::desc("print functions after regreassign pass"),
|
||||
|
@ -409,6 +416,8 @@ void BinaryFunctionPassManager::runAllPasses(BinaryContext &BC) {
|
|||
|
||||
Manager.registerPass(std::make_unique<ValidateInternalCalls>(NeverPrint));
|
||||
|
||||
Manager.registerPass(std::make_unique<NormalizeCFG>(PrintNormalized));
|
||||
|
||||
Manager.registerPass(std::make_unique<StripRepRet>(NeverPrint),
|
||||
opts::StripRepRet);
|
||||
|
||||
|
|
|
@ -9,21 +9,26 @@
|
|||
# RUN: llvm-strip --strip-unneeded %t.o
|
||||
# RUN: %clang %cflags %t.o -o %t.exe -Wl,-q -nostdlib
|
||||
# RUN: llvm-bolt %t.exe -o %t.out -data %t.fdata -lite=0 -dyno-stats \
|
||||
# RUN: -print-sctc 2>&1 | FileCheck %s
|
||||
# RUN: -print-sctc -print-only=_start 2>&1 | FileCheck %s
|
||||
# CHECK-NOT: Assertion `BranchInfo.size() == 2 && "could only be called for blocks with 2 successors"' failed.
|
||||
# Two tail calls in the same basic block after SCTC:
|
||||
# CHECK: {{.*}}: jae {{.*}} # TAILCALL # CTCTakenCount: {{.*}}
|
||||
# CHECK: {{.*}}: ja {{.*}} # TAILCALL # CTCTakenCount: {{.*}}
|
||||
# CHECK-NEXT: {{.*}}: jmp {{.*}} # TAILCALL
|
||||
|
||||
.globl _start
|
||||
_start:
|
||||
ja a
|
||||
b: jb c
|
||||
# FDATA: 1 _start #b# 1 _start #c# 2 4
|
||||
jmp e
|
||||
a: nop
|
||||
c: jmp e
|
||||
je x
|
||||
a: ja b
|
||||
jmp c
|
||||
x: ret
|
||||
# FDATA: 1 _start #a# 1 _start #b# 2 4
|
||||
b: jmp e
|
||||
c: jmp f
|
||||
|
||||
.globl e
|
||||
e:
|
||||
nop
|
||||
|
||||
.globl f
|
||||
f:
|
||||
nop
|
||||
|
|
Loading…
Reference in New Issue