BOLT: Use profiling info to control branch simplification optimization.

Summary:
An optimization to simplify conditional tail calls by removing unnecessary branches.  It adds the following two command line options:

  -simplify-conditional-tail-calls  - simplify conditional tail calls by removing unnecessary jumps
  -sctc-mode                        - mode for simplify conditional tail calls
    =always                         -   always perform sctc
    =preserve                       -   only perform sctc when branch direction is preserved
    =heuristic                      -   use branch prediction data to control sctc

This optimization considers both of the following cases:

  foo: ...
       jcc L1   original
       ...
  L1:  jmp bar  # TAILJMP

->

  foo: ...
       jcc bar  iff jcc L1 is expected
       ...

  L1 is unreachable

OR

  foo: ...
       jcc  L2
  L1:  jmp  dest  # TAILJMP
  L2:  ...

->

  foo: jncc dest  # TAILJMP
  L2:  ...

  L1 is unreachable

For this particular case, the first basic block ends with a conditional branch and has two successors, one fall-through and one for when the condition is true.  The target of the conditional is a basic block with a single unconditional branch (i.e. tail call) to another function.  We don't care about the contents of the fall-through block.

(cherry picked from FBD3719617)
This commit is contained in:
Bill Nell
2016-09-22 18:08:20 -07:00
committed by Maksim Panchenko
parent 06caefdb1d
commit 3a3dfc3dc2
7 changed files with 236 additions and 42 deletions

View File

@@ -258,6 +258,19 @@ std::pair<unsigned, uint64_t> BinaryFunction::eraseInvalidBBs() {
return std::make_pair(Count, Bytes);
}
bool BinaryFunction::isForwardCall(const MCSymbol *CalleeSymbol) const {
// TODO: Once we start reordering functions this has to change. #15031238
const auto *CalleeBF = BC.getFunctionForSymbol(CalleeSymbol);
if (CalleeBF) {
return CalleeBF->getAddress() > getAddress();
} else {
// Absolute symbol.
auto const CalleeSI = BC.GlobalSymbols.find(CalleeSymbol->getName());
assert(CalleeSI != BC.GlobalSymbols.end() && "unregistered symbol found");
return CalleeSI->second > getAddress();
}
}
void BinaryFunction::dump(std::string Annotation,
bool PrintInstructions) const {
print(dbgs(), Annotation, PrintInstructions);
@@ -3338,17 +3351,33 @@ DynoStats BinaryFunction::getDynoStats() const {
}
// Conditional branch that could be followed by an unconditional branch.
uint64_t TakenCount = BB->getBranchInfo(true).Count;
uint64_t TakenCount;
uint64_t NonTakenCount;
bool IsForwardBranch;
if (BB->succ_size() == 2) {
TakenCount = BB->getBranchInfo(true).Count;
NonTakenCount = BB->getBranchInfo(false).Count;
IsForwardBranch = isForwardBranch(BB, BB->getConditionalSuccessor(true));
} else {
// SCTC breaks the CFG invariant so we have to make some affordances
// here if we want dyno stats after running it.
TakenCount = BB->branch_info_begin()->Count;
if (TakenCount != COUNT_NO_PROFILE)
NonTakenCount = BBExecutionCount - TakenCount;
else
NonTakenCount = 0;
IsForwardBranch = isForwardBranch(BB, BB->getFallthrough());
}
if (TakenCount == COUNT_NO_PROFILE)
TakenCount = 0;
uint64_t NonTakenCount = BB->getBranchInfo(false).Count;
if (NonTakenCount == COUNT_NO_PROFILE)
NonTakenCount = 0;
assert(TakenCount + NonTakenCount == BBExecutionCount &&
"internal calculation error");
if (isForwardBranch(BB, BB->getConditionalSuccessor(true))) {
if (IsForwardBranch) {
Stats[DynoStats::FORWARD_COND_BRANCHES] += BBExecutionCount;
Stats[DynoStats::FORWARD_COND_BRANCHES_TAKEN] += TakenCount;
} else {