diff --git a/bolt/BinaryBasicBlock.cpp b/bolt/BinaryBasicBlock.cpp index d845ba4c7f2a..c3c9e0a4f193 100644 --- a/bolt/BinaryBasicBlock.cpp +++ b/bolt/BinaryBasicBlock.cpp @@ -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); diff --git a/bolt/BinaryBasicBlock.h b/bolt/BinaryBasicBlock.h index 5caf1e8f1ed5..8e6a75ba1424 100644 --- a/bolt/BinaryBasicBlock.h +++ b/bolt/BinaryBasicBlock.h @@ -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); diff --git a/bolt/BinaryFunction.cpp b/bolt/BinaryFunction.cpp index e937e3a6f1cc..73a66e0c3dd9 100644 --- a/bolt/BinaryFunction.cpp +++ b/bolt/BinaryFunction.cpp @@ -322,10 +322,22 @@ std::pair 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); } diff --git a/bolt/BinaryFunction.h b/bolt/BinaryFunction.h index ac90f7f6313f..2f903b760de4 100644 --- a/bolt/BinaryFunction.h +++ b/bolt/BinaryFunction.h @@ -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 { diff --git a/bolt/BinaryPassManager.cpp b/bolt/BinaryPassManager.cpp index b3f1d306ee4a..ae2029be0984 100644 --- a/bolt/BinaryPassManager.cpp +++ b/bolt/BinaryPassManager.cpp @@ -13,6 +13,7 @@ #include "Passes/FrameOptimizer.h" #include "Passes/Inliner.h" #include "llvm/Support/Timer.h" +#include "llvm/Support/raw_ostream.h" #include using namespace llvm; @@ -21,10 +22,17 @@ namespace opts { extern cl::OptionCategory BoltOptCategory; +extern cl::opt Verbosity; extern cl::opt PrintAll; extern cl::opt DumpDotAll; extern cl::opt DynoStatsAll; +static cl::opt +ICF("icf", + cl::desc("fold functions with identical code"), + cl::ZeroOrMore, + cl::cat(BoltOptCategory)); + static cl::opt 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(NeverPrint), opts::StripRepRet); - Manager.registerPass(llvm::make_unique(PrintICF)); + Manager.registerPass(llvm::make_unique(PrintICF), + opts::ICF); Manager.registerPass(llvm::make_unique(PrintICP), opts::IndirectCallPromotion); @@ -301,7 +318,8 @@ void BinaryFunctionPassManager::runAllPasses( llvm::make_unique(PrintSimplifyROLoads), opts::SimplifyRODataLoads); - Manager.registerPass(llvm::make_unique(PrintICF)); + Manager.registerPass(llvm::make_unique(PrintICF), + opts::ICF); Manager.registerPass(llvm::make_unique(PrintReordered)); @@ -320,27 +338,30 @@ void BinaryFunctionPassManager::runAllPasses( Manager.registerPass(llvm::make_unique(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(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(PrintSCTC), opts::SimplifyConditionalTailCalls); - Manager.registerPass(llvm::make_unique(PrintPeepholes), - opts::Peepholes); - - Manager.registerPass( - llvm::make_unique(PrintUCE), - opts::EliminateUnreachable); - - Manager.registerPass( - llvm::make_unique(PrintReorderedFunctions)); - + // This pass should always run last.* Manager.registerPass(llvm::make_unique(PrintFinalized)); + // *except for this pass. TODO: figure out why moving this before function + // reordering breaks things badly. Manager.registerPass( llvm::make_unique(PrintAfterLowering)); diff --git a/bolt/Passes/BinaryPasses.cpp b/bolt/Passes/BinaryPasses.cpp index 17bd0b475986..76028dd037b0 100644 --- a/bolt/Passes/BinaryPasses.cpp +++ b/bolt/Passes/BinaryPasses.cpp @@ -77,12 +77,6 @@ FunctionOrderFile("function-order", "reordering"), cl::cat(BoltOptCategory)); -static cl::opt -ICF("icf", - cl::desc("fold functions with identical code"), - cl::ZeroOrMore, - cl::cat(BoltOptCategory)); - static cl::opt 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 &BFs, std::set &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 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 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 &BFs, std::set &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 &BFs, std::set &) { - 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 &&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; diff --git a/bolt/Passes/BinaryPasses.h b/bolt/Passes/BinaryPasses.h index 37866f9e36b1..fe420594828e 100644 --- a/bolt/Passes/BinaryPasses.h +++ b/bolt/Passes/BinaryPasses.h @@ -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 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 &PrintPass) : BinaryFunctionPass(PrintPass) { }