[BOLT] Fix double jump peephole, remove useless conditional branches.

Summary:
I split some of this out from the jumptable diff since it fixes the
double jump peephole.

I've changed the pass manager so that UCE and peepholes are not called
after SCTC.  I've incorporated a call to the double jump fixer to SCTC
since it is needed to fix things up afterwards.

While working on fixing the double jump peephole I discovered a few
useless conditional branches that could be removed as well.  I highly
doubt that removing them will improve perf at all but it does seem
odd to leave in useless conditional branches.

There are also some minor logging improvements.

(cherry picked from FBD4751875)
This commit is contained in:
Bill Nell 2017-03-20 22:44:25 -07:00 committed by Maksim Panchenko
parent f7d32f7e7d
commit 6c5c65e3a3
7 changed files with 282 additions and 134 deletions

View File

@ -55,6 +55,8 @@ BinaryBasicBlock::reverse_iterator BinaryBasicBlock::getLastNonPseudo() {
}
bool BinaryBasicBlock::validateSuccessorInvariants() {
auto *Func = getFunction();
auto &BC = Func->getBinaryContext();
const MCSymbol *TBB = nullptr;
const MCSymbol *FBB = nullptr;
MCInst *CondBranch = nullptr;
@ -67,7 +69,9 @@ bool BinaryBasicBlock::validateSuccessorInvariants() {
case 0:
return !CondBranch && !UncondBranch;
case 1:
return !CondBranch;
return !CondBranch ||
(CondBranch &&
!Func->getBasicBlockForLabel(BC.MIA->getTargetSymbol(*CondBranch)));
case 2:
return
(!CondBranch ||
@ -185,6 +189,7 @@ void BinaryBasicBlock::replaceSuccessor(BinaryBasicBlock *Succ,
BinaryBasicBlock *NewSucc,
uint64_t Count,
uint64_t MispredictedCount) {
Succ->removePredecessor(this);
auto I = succ_begin();
auto BI = BranchInfo.begin();
for (; I != succ_end(); ++I) {
@ -197,6 +202,7 @@ void BinaryBasicBlock::replaceSuccessor(BinaryBasicBlock *Succ,
*I = NewSucc;
*BI = BinaryBranchInfo{Count, MispredictedCount};
NewSucc->addPredecessor(this);
}
void BinaryBasicBlock::removeSuccessor(BinaryBasicBlock *Succ) {
@ -225,6 +231,28 @@ void BinaryBasicBlock::removePredecessor(BinaryBasicBlock *Pred) {
Predecessors.erase(I);
}
void BinaryBasicBlock::removeDuplicateConditionalSuccessor(MCInst *CondBranch) {
assert(succ_size() == 2);
auto *Succ = Successors[0];
assert(Succ == Successors[1]);
const auto CondBI = BranchInfo[0];
const auto UncondBI = BranchInfo[1];
eraseInstruction(CondBranch);
Successors.clear();
BranchInfo.clear();
Successors.push_back(Succ);
BranchInfo.push_back({CondBI.Count + UncondBI.Count,
CondBI.MispredictedCount + UncondBI.MispredictedCount});
assert(isSuccessor(Succ));
assert(Succ->isPredecessor(this));
}
void BinaryBasicBlock::addLandingPad(BinaryBasicBlock *LPBlock) {
if (std::find(LandingPads.begin(), LandingPads.end(), LPBlock) == LandingPads.end()) {
LandingPads.push_back(LPBlock);

View File

@ -522,6 +522,17 @@ public:
}
}
/// Remove useless duplicate successors. When the conditional
/// successor is the same as the unconditional successor, we can
/// remove the conditional successor and branch instruction.
void removeDuplicateConditionalSuccessor(MCInst *CondBranch);
/// Test if BB is a predecessor of this block.
bool isPredecessor(const BinaryBasicBlock *BB) const {
auto Itr = std::find(Predecessors.begin(), Predecessors.end(), BB);
return Itr != Predecessors.end();
}
/// Test if BB is a successor of this block.
bool isSuccessor(const BinaryBasicBlock *BB) const {
auto Itr = std::find(Successors.begin(), Successors.end(), BB);

View File

@ -322,10 +322,22 @@ std::pair<unsigned, uint64_t> BinaryFunction::eraseInvalidBBs() {
}
bool BinaryFunction::isForwardCall(const MCSymbol *CalleeSymbol) const {
// TODO: Once we start reordering functions this has to change. #15031238
// This function should work properly before and after function reordering.
// In order to accomplish this, we use the function index (if it is valid).
// If the function indices are not valid, we fall back to the original
// addresses. This should be ok because the functions without valid indices
// should have been ordered with a stable sort.
const auto *CalleeBF = BC.getFunctionForSymbol(CalleeSymbol);
if (CalleeBF) {
return CalleeBF->getAddress() > getAddress();
if (hasValidIndex() && CalleeBF->hasValidIndex()) {
return getIndex() < CalleeBF->getIndex();
} else if (hasValidIndex() && !CalleeBF->hasValidIndex()) {
return true;
} else if (!hasValidIndex() && CalleeBF->hasValidIndex()) {
return false;
} else {
return getAddress() < CalleeBF->getAddress();
}
} else {
// Absolute symbol.
auto const CalleeSI = BC.GlobalSymbols.find(CalleeSymbol->getName());
@ -2888,6 +2900,9 @@ void BinaryFunction::fixBranches() {
} else {
MIA->replaceBranchTarget(*CondBranch, TSuccessor->getLabel(), Ctx);
}
if (TSuccessor == FSuccessor) {
BB->removeDuplicateConditionalSuccessor(CondBranch);
}
if (!NextBB || (NextBB != TSuccessor && NextBB != FSuccessor)) {
BB->addBranchInstruction(FSuccessor);
}

View File

@ -337,11 +337,6 @@ private:
return BB->getIndex();
}
BinaryBasicBlock *getBasicBlockForLabel(const MCSymbol *Label) const {
auto I = LabelToBB.find(Label);
return I == LabelToBB.end() ? nullptr : I->second;
}
/// Return basic block that originally contained offset \p Offset
/// from the function start.
BinaryBasicBlock *getBasicBlockContainingOffset(uint64_t Offset);
@ -913,6 +908,16 @@ public:
/// fixBranches().
DynoStats getDynoStats() const;
BinaryBasicBlock *getBasicBlockForLabel(const MCSymbol *Label) {
auto I = LabelToBB.find(Label);
return I == LabelToBB.end() ? nullptr : I->second;
}
const BinaryBasicBlock *getBasicBlockForLabel(const MCSymbol *Label) const {
auto I = LabelToBB.find(Label);
return I == LabelToBB.end() ? nullptr : I->second;
}
/// Returns the basic block after the given basic block in the layout or
/// nullptr the last basic block is given.
const BinaryBasicBlock *getBasicBlockAfter(const BinaryBasicBlock *BB) const {

View File

@ -13,6 +13,7 @@
#include "Passes/FrameOptimizer.h"
#include "Passes/Inliner.h"
#include "llvm/Support/Timer.h"
#include "llvm/Support/raw_ostream.h"
#include <numeric>
using namespace llvm;
@ -21,10 +22,17 @@ namespace opts {
extern cl::OptionCategory BoltOptCategory;
extern cl::opt<unsigned> Verbosity;
extern cl::opt<bool> PrintAll;
extern cl::opt<bool> DumpDotAll;
extern cl::opt<bool> DynoStatsAll;
static cl::opt<bool>
ICF("icf",
cl::desc("fold functions with identical code"),
cl::ZeroOrMore,
cl::cat(BoltOptCategory));
static cl::opt<bool>
EliminateUnreachable("eliminate-unreachable",
cl::desc("eliminate unreachable code"),
@ -223,6 +231,10 @@ void BinaryFunctionPassManager::runPasses() {
auto &Pass = OptPassPair.second;
if (opts::Verbosity > 0) {
outs() << "BOLT-INFO: Starting pass: " << Pass->getName() << "\n";
}
NamedRegionTimer T(Pass->getName(), TimerGroupName, TimeOpts);
callWithDynoStats(
@ -247,6 +259,10 @@ void BinaryFunctionPassManager::runPasses() {
exit(1);
}
if (opts::Verbosity > 0) {
outs() << "BOLT-INFO: Finished pass: " << Pass->getName() << "\n";
}
if (!opts::PrintAll && !opts::DumpDotAll && !Pass->printPass())
continue;
@ -282,7 +298,8 @@ void BinaryFunctionPassManager::runAllPasses(
Manager.registerPass(llvm::make_unique<StripRepRet>(NeverPrint),
opts::StripRepRet);
Manager.registerPass(llvm::make_unique<IdenticalCodeFolding>(PrintICF));
Manager.registerPass(llvm::make_unique<IdenticalCodeFolding>(PrintICF),
opts::ICF);
Manager.registerPass(llvm::make_unique<IndirectCallPromotion>(PrintICP),
opts::IndirectCallPromotion);
@ -301,7 +318,8 @@ void BinaryFunctionPassManager::runAllPasses(
llvm::make_unique<SimplifyRODataLoads>(PrintSimplifyROLoads),
opts::SimplifyRODataLoads);
Manager.registerPass(llvm::make_unique<IdenticalCodeFolding>(PrintICF));
Manager.registerPass(llvm::make_unique<IdenticalCodeFolding>(PrintICF),
opts::ICF);
Manager.registerPass(llvm::make_unique<ReorderBasicBlocks>(PrintReordered));
@ -320,27 +338,30 @@ void BinaryFunctionPassManager::runAllPasses(
Manager.registerPass(llvm::make_unique<FrameOptimizerPass>(PrintFOP),
OptimizeFrameAccesses);
// This pass should come close to last since it uses the estimated hot
// size of a function to determine the order. It should definitely
// also happen after any changes to the call graph are made, e.g. inlining.
Manager.registerPass(
llvm::make_unique<ReorderFunctions>(PrintReorderedFunctions));
// This pass introduces conditional jumps into external functions.
// Between extending CFG to support this and isolating this pass we chose
// the latter. Thus this pass will do unreachable code elimination
// if necessary and wouldn't rely on UCE for this.
// More generally this pass should be the last optimization pass.
// the latter. Thus this pass will do double jump removal and unreachable
// code elimination if necessary and won't rely on peepholes/UCE for these
// optimizations.
// More generally this pass should be the last optimization pass that
// modifies branches/control flow. This pass is run after function
// reordering so that it can tell whether calls are forward/backward
// accurately.
Manager.registerPass(
llvm::make_unique<SimplifyConditionalTailCalls>(PrintSCTC),
opts::SimplifyConditionalTailCalls);
Manager.registerPass(llvm::make_unique<Peepholes>(PrintPeepholes),
opts::Peepholes);
Manager.registerPass(
llvm::make_unique<EliminateUnreachableBlocks>(PrintUCE),
opts::EliminateUnreachable);
Manager.registerPass(
llvm::make_unique<ReorderFunctions>(PrintReorderedFunctions));
// This pass should always run last.*
Manager.registerPass(llvm::make_unique<FinalizeFunctions>(PrintFinalized));
// *except for this pass. TODO: figure out why moving this before function
// reordering breaks things badly.
Manager.registerPass(
llvm::make_unique<InstructionLowering>(PrintAfterLowering));

View File

@ -77,12 +77,6 @@ FunctionOrderFile("function-order",
"reordering"),
cl::cat(BoltOptCategory));
static cl::opt<bool>
ICF("icf",
cl::desc("fold functions with identical code"),
cl::ZeroOrMore,
cl::cat(BoltOptCategory));
static cl::opt<bool>
ICFUseDFS("icf-dfs",
cl::desc("use DFS ordering when using -icf option"),
@ -371,7 +365,7 @@ void EliminateUnreachableBlocks::runOnFunction(BinaryFunction& Function) {
std::tie(Count, Bytes) = Function.eraseInvalidBBs();
DeletedBlocks += Count;
DeletedBytes += Bytes;
if (Count) {
if (Count && opts::Verbosity > 0) {
Modified.insert(&Function);
outs() << "BOLT-INFO: Removed " << Count
<< " dead basic block(s) accounting for " << Bytes
@ -404,21 +398,22 @@ void ReorderBasicBlocks::runOnFunctions(
BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs,
std::set<uint64_t> &LargeFunctions) {
if (opts::ReorderBlocks == BinaryFunction::LT_NONE)
return;
for (auto &It : BFs) {
auto &Function = It.second;
if (!shouldOptimize(Function))
continue;
if (opts::ReorderBlocks != BinaryFunction::LT_NONE) {
bool ShouldSplit =
(opts::SplitFunctions == BinaryFunction::ST_ALL) ||
(opts::SplitFunctions == BinaryFunction::ST_EH &&
Function.hasEHRanges()) ||
(LargeFunctions.find(It.first) != LargeFunctions.end());
Function.modifyLayout(opts::ReorderBlocks, opts::MinBranchClusters,
ShouldSplit);
}
const bool ShouldSplit =
(opts::SplitFunctions == BinaryFunction::ST_ALL) ||
(opts::SplitFunctions == BinaryFunction::ST_EH &&
Function.hasEHRanges()) ||
(LargeFunctions.find(It.first) != LargeFunctions.end());
Function.modifyLayout(opts::ReorderBlocks, opts::MinBranchClusters,
ShouldSplit);
}
}
@ -441,13 +436,14 @@ void FinalizeFunctions::runOnFunctions(
) {
for (auto &It : BFs) {
auto &Function = It.second;
const auto ShouldOptimize = shouldOptimize(Function);
// Always fix functions in relocation mode.
if (!opts::Relocs && !shouldOptimize(Function))
if (!opts::Relocs && !ShouldOptimize)
continue;
// Fix the CFI state.
if (shouldOptimize(Function) && !Function.fixCFIState()) {
if (ShouldOptimize && !Function.fixCFIState()) {
if (opts::Relocs) {
errs() << "BOLT-ERROR: unable to fix CFI state for function "
<< Function << ". Exiting.\n";
@ -464,6 +460,111 @@ void FinalizeFunctions::runOnFunctions(
}
}
namespace {
// This peephole fixes jump instructions that jump to another basic
// block with a single jump instruction, e.g.
//
// B0: ...
// jmp B1 (or jcc B1)
//
// B1: jmp B2
//
// ->
//
// B0: ...
// jmp B2 (or jcc B2)
//
uint64_t fixDoubleJumps(BinaryContext &BC, BinaryFunction &Function) {
uint64_t NumDoubleJumps = 0;
for (auto &BB : Function) {
auto checkAndPatch = [&](BinaryBasicBlock *Pred,
BinaryBasicBlock *Succ,
const MCSymbol *SuccSym) {
// Ignore infinite loop jumps or fallthrough tail jumps.
if (Pred == Succ || Succ == &BB)
return;
if (Succ) {
const MCSymbol *TBB = nullptr;
const MCSymbol *FBB = nullptr;
MCInst *CondBranch = nullptr;
MCInst *UncondBranch = nullptr;
auto Res = Pred->analyzeBranch(TBB, FBB, CondBranch, UncondBranch);
if(!Res) {
DEBUG(dbgs() << "analyzeBranch failed in peepholes in block:\n";
Pred->dump());
return;
}
Pred->replaceSuccessor(&BB, Succ);
// We must patch up any existing branch instructions to match up
// with the new successor.
auto *Ctx = BC.Ctx.get();
if (CondBranch &&
BC.MIA->getTargetSymbol(*CondBranch) == BB.getLabel()) {
BC.MIA->replaceBranchTarget(*CondBranch, Succ->getLabel(), Ctx);
} else if (UncondBranch &&
BC.MIA->getTargetSymbol(*UncondBranch) == BB.getLabel()) {
BC.MIA->replaceBranchTarget(*UncondBranch, Succ->getLabel(), Ctx);
}
} else {
// Succ will be null in the tail call case. In this case we
// need to explicitly add a tail call instruction.
auto *Branch = Pred->getLastNonPseudoInstr();
if (Branch && BC.MIA->isUnconditionalBranch(*Branch)) {
assert(BC.MIA->getTargetSymbol(*Branch) == BB.getLabel());
Pred->removeSuccessor(&BB);
Pred->eraseInstruction(Branch);
Pred->addTailCallInstruction(SuccSym);
} else {
return;
}
}
++NumDoubleJumps;
DEBUG(dbgs() << "Removed double jump in " << Function << " from "
<< Pred->getName() << " -> " << BB.getName() << " to "
<< Pred->getName() << " -> " << SuccSym->getName()
<< (!Succ ? " (tail)\n" : "\n"));
};
if (BB.getNumNonPseudos() != 1 || BB.isLandingPad())
continue;
auto *Inst = BB.getFirstNonPseudoInstr();
const bool IsTailCall = BC.MIA->isTailCall(*Inst);
if (!BC.MIA->isUnconditionalBranch(*Inst) && !IsTailCall)
continue;
const auto *SuccSym = BC.MIA->getTargetSymbol(*Inst);
auto *Succ = BB.getSuccessor();
if ((!Succ || &BB == Succ) && !IsTailCall)
continue;
std::vector<BinaryBasicBlock *> Preds{BB.pred_begin(), BB.pred_end()};
for (auto *Pred : Preds) {
if (Pred->isLandingPad())
continue;
if (Pred->getSuccessor() == &BB ||
(Pred->getConditionalSuccessor(true) == &BB && !IsTailCall) ||
Pred->getConditionalSuccessor(false) == &BB) {
checkAndPatch(Pred, Succ, SuccSym);
assert(Function.validateCFG());
}
}
}
return NumDoubleJumps;
}
}
bool
SimplifyConditionalTailCalls::shouldRewriteBranch(const BinaryBasicBlock *PredBB,
const MCInst &CondBranch,
@ -597,8 +698,11 @@ uint64_t SimplifyConditionalTailCalls::fixTailCalls(BinaryContext &BC,
}
if (NumLocalCTCs > 0) {
NumDoubleJumps += fixDoubleJumps(BC, BF);
// Clean-up unreachable tail-call blocks.
BF.eraseInvalidBBs();
const auto Stats = BF.eraseInvalidBBs();
DeletedBlocks += Stats.first;
DeletedBytes += Stats.second;
}
DEBUG(dbgs() << "BOLT: created " << NumLocalCTCs
@ -631,7 +735,10 @@ void SimplifyConditionalTailCalls::runOnFunctions(
outs() << "BOLT-INFO: SCTC: patched " << NumTailCallsPatched
<< " tail calls (" << NumOrigForwardBranches << " forward)"
<< " tail calls (" << NumOrigBackwardBranches << " backward)"
<< " from a total of " << NumCandidateTailCalls << "\n";
<< " from a total of " << NumCandidateTailCalls
<< " while removing " << NumDoubleJumps << " double jumps"
<< " and removing " << DeletedBlocks << " basic blocks"
<< " totalling " << DeletedBytes << " bytes of code.\n";
}
void Peepholes::shortenInstructions(BinaryContext &BC,
@ -643,85 +750,6 @@ void Peepholes::shortenInstructions(BinaryContext &BC,
}
}
void debugDump(BinaryFunction *BF) {
BF->dump();
}
// This peephole fixes jump instructions that jump to another basic
// block with a single jump instruction, e.g.
//
// B0: ...
// jmp B1 (or jcc B1)
//
// B1: jmp B2
//
// ->
//
// B0: ...
// jmp B2 (or jcc B2)
//
void Peepholes::fixDoubleJumps(BinaryContext &BC,
BinaryFunction &Function) {
for (auto &BB : Function) {
auto checkAndPatch = [&](BinaryBasicBlock *Pred,
BinaryBasicBlock *Succ,
const MCSymbol *SuccSym) {
// Ignore infinite loop jumps or fallthrough tail jumps.
if (Pred == Succ || Succ == &BB)
return;
if (Succ) {
Pred->replaceSuccessor(&BB, Succ);
} else {
// Succ will be null in the tail call case. In this case we
// need to explicitly add a tail call instruction.
auto *Branch = Pred->getLastNonPseudoInstr();
if (Branch && BC.MIA->isUnconditionalBranch(*Branch)) {
Pred->removeSuccessor(&BB);
Pred->eraseInstruction(Branch);
Pred->addTailCallInstruction(SuccSym);
} else {
return;
}
}
++NumDoubleJumps;
DEBUG(dbgs() << "Removed double jump in " << Function << " from "
<< Pred->getName() << " -> " << BB.getName() << " to "
<< Pred->getName() << " -> " << SuccSym->getName()
<< (!Succ ? " (tail)\n" : "\n"));
};
if (BB.getNumNonPseudos() != 1 || BB.isLandingPad())
continue;
auto *Inst = BB.getFirstNonPseudoInstr();
const bool IsTailCall = BC.MIA->isTailCall(*Inst);
if (!BC.MIA->isUnconditionalBranch(*Inst) && !IsTailCall)
continue;
const auto *SuccSym = BC.MIA->getTargetSymbol(*Inst);
auto *Succ = BB.getSuccessor();
if ((!Succ || &BB == Succ) && !IsTailCall)
continue;
std::vector<BinaryBasicBlock *> Preds{BB.pred_begin(), BB.pred_end()};
for (auto *Pred : Preds) {
if (Pred->isLandingPad())
continue;
if (Pred->getSuccessor() == &BB ||
(Pred->getConditionalSuccessor(true) == &BB && !IsTailCall) ||
Pred->getConditionalSuccessor(false) == &BB) {
checkAndPatch(Pred, Succ, SuccSym);
}
}
}
}
void Peepholes::addTailcallTraps(BinaryContext &BC,
BinaryFunction &Function) {
for (auto &BB : Function) {
@ -736,6 +764,37 @@ void Peepholes::addTailcallTraps(BinaryContext &BC,
}
}
void Peepholes::removeUselessCondBranches(BinaryContext &BC,
BinaryFunction &Function) {
for (auto &BB : Function) {
if (BB.succ_size() != 2)
continue;
auto *CondBB = BB.getConditionalSuccessor(true);
auto *UncondBB = BB.getConditionalSuccessor(false);
if (CondBB == UncondBB) {
const MCSymbol *TBB = nullptr;
const MCSymbol *FBB = nullptr;
MCInst *CondBranch = nullptr;
MCInst *UncondBranch = nullptr;
auto Result = BB.analyzeBranch(TBB, FBB, CondBranch, UncondBranch);
// analyzeBranch can fail due to unusual branch instructions, e.g. jrcxz
if (!Result) {
DEBUG(dbgs() << "analyzeBranch failed in peepholes in block:\n";
BB.dump());
continue;
}
if (CondBranch) {
BB.removeDuplicateConditionalSuccessor(CondBranch);
++NumUselessCondBranches;
}
}
}
}
void Peepholes::runOnFunctions(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs,
std::set<uint64_t> &LargeFunctions) {
@ -743,12 +802,17 @@ void Peepholes::runOnFunctions(BinaryContext &BC,
auto &Function = It.second;
if (shouldOptimize(Function)) {
shortenInstructions(BC, Function);
fixDoubleJumps(BC, Function);
NumDoubleJumps += fixDoubleJumps(BC, Function);
addTailcallTraps(BC, Function);
removeUselessCondBranches(BC, Function);
}
}
outs() << "BOLT-INFO: Peephole: " << NumDoubleJumps << " double jumps patched.\n";
outs() << "BOLT-INFO: Peephole: " << TailCallTraps << " tail call traps inserted.\n";
outs() << "BOLT-INFO: Peephole: " << NumDoubleJumps
<< " double jumps patched.\n"
<< "BOLT-INFO: Peephole: " << TailCallTraps
<< " tail call traps inserted.\n"
<< "BOLT-INFO: Peephole: " << NumUselessCondBranches
<< " useless conditional branches removed.\n";
}
bool SimplifyRODataLoads::simplifyRODataLoads(
@ -854,9 +918,6 @@ void SimplifyRODataLoads::runOnFunctions(
void IdenticalCodeFolding::runOnFunctions(BinaryContext &BC,
std::map<uint64_t, BinaryFunction> &BFs,
std::set<uint64_t> &) {
if (!opts::ICF)
return;
const auto OriginalFunctionCount = BFs.size();
uint64_t NumFunctionsFolded = 0;
uint64_t NumJTFunctionsFolded = 0;
@ -1820,7 +1881,9 @@ void ReorderFunctions::reorder(std::vector<Cluster> &&Clusters,
}
}
if (opts::Verbosity > 0 || (DebugFlag && isCurrentDebugType("hfsort"))) {
if (opts::ReorderFunctions != BinaryFunction::RT_NONE &&
(opts::Verbosity > 0 ||
(DebugFlag && isCurrentDebugType("hfsort")))) {
uint64_t TotalSize = 0;
uint64_t CurPage = 0;
uint64_t Hotfuncs = 0;

View File

@ -198,6 +198,9 @@ class SimplifyConditionalTailCalls : public BinaryFunctionPass {
uint64_t NumTailCallsPatched{0};
uint64_t NumOrigForwardBranches{0};
uint64_t NumOrigBackwardBranches{0};
uint64_t NumDoubleJumps{0};
uint64_t DeletedBlocks{0};
uint64_t DeletedBytes{0};
std::unordered_set<const BinaryFunction *> Modified;
bool shouldRewriteBranch(const BinaryBasicBlock *PredBB,
@ -225,20 +228,22 @@ class SimplifyConditionalTailCalls : public BinaryFunctionPass {
class Peepholes : public BinaryFunctionPass {
uint64_t NumDoubleJumps{0};
uint64_t TailCallTraps{0};
uint64_t NumUselessCondBranches{0};
/// Attempt to use the minimum operand width for arithmetic, branch and
/// move instructions.
void shortenInstructions(BinaryContext &BC, BinaryFunction &Function);
/// Replace double jumps with a jump directly to the target, i.e.
/// jmp/jcc L1; L1: jmp L2 -> jmp/jcc L2.
void fixDoubleJumps(BinaryContext &BC, BinaryFunction &Function);
/// Add trap instructions immediately after indirect tail calls to prevent
/// the processor from decoding instructions immediate following the
/// tailcall.
void addTailcallTraps(BinaryContext &BC, BinaryFunction &Function);
public:
/// Remove useless duplicate successors. When the conditional
/// successor is the same as the unconditional successor, we can
/// remove the conditional successor and branch instruction.
void removeUselessCondBranches(BinaryContext &BC, BinaryFunction &Function);
public:
explicit Peepholes(const cl::opt<bool> &PrintPass)
: BinaryFunctionPass(PrintPass) { }