diff --git a/bolt/BinaryFunction.cpp b/bolt/BinaryFunction.cpp index 7ef79460fef8..3fb1bb149844 100644 --- a/bolt/BinaryFunction.cpp +++ b/bolt/BinaryFunction.cpp @@ -63,10 +63,11 @@ unsigned BinaryFunction::eraseDeadBBs( return Count; } -void BinaryFunction::print(raw_ostream &OS, bool PrintInstructions) const { +void BinaryFunction::print(raw_ostream &OS, std::string Annotation, + bool PrintInstructions) const { StringRef SectionName; Section.getName(SectionName); - OS << "Binary Function \"" << getName() << "\" {" + OS << "Binary Function \"" << getName() << "\" " << Annotation << " {" << "\n State : " << CurrentState << "\n Address : 0x" << Twine::utohexstr(Address) << "\n Size : 0x" << Twine::utohexstr(Size) @@ -75,10 +76,20 @@ void BinaryFunction::print(raw_ostream &OS, bool PrintInstructions) const { << "\n Section : " << SectionName << "\n Orc Section : " << getCodeSectionName() << "\n IsSimple : " << IsSimple - << "\n BB count : " << BasicBlocks.size() - << "\n Image : 0x" << Twine::utohexstr(ImageAddress); + << "\n BB Count : " << BasicBlocksLayout.size(); + if (BasicBlocksLayout.size()) { + OS << "\n BB Layout : "; + auto Sep = ""; + for (auto BB : BasicBlocksLayout) { + OS << Sep << BB->getName(); + Sep = ", "; + } + } + if (ImageAddress) + OS << "\n Image : 0x" << Twine::utohexstr(ImageAddress); if (ExecutionCount != COUNT_NO_PROFILE) OS << "\n Exec Count : " << ExecutionCount; + OS << "\n}\n"; if (!PrintInstructions || !BC.InstPrinter) @@ -321,9 +332,6 @@ bool BinaryFunction::disassemble(ArrayRef FunctionData) { // Update state. updateState(State::Disassembled); - // Print the function in the new state. - DEBUG(print(dbgs(), /* PrintInstructions = */ true)); - return true; } @@ -415,7 +423,7 @@ bool BinaryFunction::buildCFG() { } // Intermediate dump. - DEBUG(print(dbgs(), /* PrintInstructions = */ true)); + DEBUG(print(dbgs(), "after creating basic blocks")); // TODO: handle properly calls to no-return functions, // e.g. exit(3), etc. Otherwise we'll see a false fall-through @@ -486,9 +494,6 @@ bool BinaryFunction::buildCFG() { // Update the state. CurrentState = State::CFG; - // Print the function in the new state. - DEBUG(print(dbgs(), /* PrintInstructions = */ true)); - return true; } @@ -563,7 +568,7 @@ void BinaryFunction::inferFallThroughCounts() { return; } -void BinaryFunction::optimizeLayout(bool DumpLayout) { +void BinaryFunction::optimizeLayout() { // Bail if no profiling information or if empty if (getExecutionCount() == BinaryFunction::COUNT_NO_PROFILE || BasicBlocksLayout.empty()) { @@ -572,11 +577,9 @@ void BinaryFunction::optimizeLayout(bool DumpLayout) { // Work on optimal solution if problem is small enough if (BasicBlocksLayout.size() <= FUNC_SIZE_THRESHOLD) - return solveOptimalLayout(DumpLayout); + return solveOptimalLayout(); - if (DumpLayout) { - dbgs() << "running block layout heuristics on " << getName() << "\n"; - } + DEBUG(dbgs() << "running block layout heuristics on " << getName() << "\n"); // Greedy heuristic implementation for the TSP, applied to BB layout. Try to // maximize weight during a path traversing all BBs. In this way, we will @@ -711,33 +714,14 @@ void BinaryFunction::optimizeLayout(bool DumpLayout) { Unvisited.end()); fixBranches(); - - if (DumpLayout) { - dbgs() << "original BB order is: "; - auto Sep = ""; - for (auto &BB : BasicBlocks) { - dbgs() << Sep << BB.getName(); - Sep = ","; - } - dbgs() << "\nnew order is: "; - Sep = ""; - for (auto BB : BasicBlocksLayout) { - dbgs() << Sep << BB->getName(); - Sep = ","; - } - dbgs() << "\n"; - print(dbgs(), /* PrintInstructions = */ true); - } } -void BinaryFunction::solveOptimalLayout(bool DumpLayout) { +void BinaryFunction::solveOptimalLayout() { std::vector> Weight; std::map BBToIndex; std::vector IndexToBB; - if (DumpLayout) { - dbgs() << "finding optimal block layout for " << getName() << "\n"; - } + DEBUG(dbgs() << "finding optimal block layout for " << getName() << "\n"); unsigned N = BasicBlocksLayout.size(); // Populating weight map and index map @@ -833,23 +817,6 @@ void BinaryFunction::solveOptimalLayout(bool DumpLayout) { } fixBranches(); - - if (DumpLayout) { - dbgs() << "original BB order is: "; - auto Sep = ""; - for (auto &BB : BasicBlocks) { - dbgs() << Sep << BB.getName(); - Sep = ","; - } - dbgs() << "\nnew order is: "; - Sep = ""; - for (auto BB : BasicBlocksLayout) { - dbgs() << Sep << BB->getName(); - Sep = ","; - } - dbgs() << "\n"; - print(dbgs(), /* PrintInstructions = */ true); - } } const BinaryBasicBlock * diff --git a/bolt/BinaryFunction.h b/bolt/BinaryFunction.h index 829de7b8c1a6..3aa970d9aa36 100644 --- a/bolt/BinaryFunction.h +++ b/bolt/BinaryFunction.h @@ -202,7 +202,7 @@ public: /// Perform optimal code layout based on edge frequencies making necessary /// adjustments to instructions at the end of basic blocks. - void optimizeLayout(bool DumpLayout); + void optimizeLayout(); /// Dynamic programming implementation for the TSP, applied to BB layout. Find /// the optimal way to maximize weight during a path traversing all BBs. In @@ -210,7 +210,7 @@ public: /// /// Uses exponential amount of memory on the number of basic blocks and should /// only be used for small functions. - void solveOptimalLayout(bool DumpLayout); + void solveOptimalLayout(); /// View CFG in graphviz program void viewGraph(); @@ -310,12 +310,13 @@ public: /// Dump function information to debug output. If \p PrintInstructions /// is true - include instruction disassembly. - void dump(bool PrintInstructions = false) const { - print(dbgs(), PrintInstructions); + void dump(std::string Annotation = "", bool PrintInstructions = true) const { + print(dbgs(), Annotation, PrintInstructions); } /// Print function information to the \p OS stream. - void print(raw_ostream &OS, bool PrintInstructions = false) const; + void print(raw_ostream &OS, std::string Annotation = "", + bool PrintInstructions = true) const; void addInstruction(uint64_t Offset, MCInst &&Instruction) { Instructions.emplace(Offset, std::forward(Instruction)); diff --git a/bolt/llvm-flo.cpp b/bolt/llvm-flo.cpp index 20502e3e2865..7e920040eac5 100644 --- a/bolt/llvm-flo.cpp +++ b/bolt/llvm-flo.cpp @@ -100,16 +100,60 @@ ReorderBlocks("reorder-blocks", cl::Optional); static cl::opt -DumpData("dump-data", cl::desc("dump parsed flo data (debugging)"), +DumpData("dump-data", cl::desc("dump parsed flo data and exit (debugging)"), cl::Hidden); static cl::opt -DumpFunctions("dump-functions", cl::desc("dump parsed functions (debugging)"), - cl::Hidden); +PrintAll("print-all", cl::desc("print functions after each stage"), + cl::Hidden); static cl::opt -DumpLayout("dump-layout", cl::desc("dump parsed flo data (debugging)"), - cl::Hidden); +PrintCFG("print-cfg", cl::desc("print functions after CFG construction"), + cl::Hidden); + +static cl::opt +PrintUCE("print-uce", + cl::desc("print functions after unreachable code elimination"), + cl::Hidden); + +static cl::opt +PrintDisasm("print-disasm", cl::desc("print function after disassembly"), + cl::Hidden); + +static cl::opt +PrintReordered("print-reordered", + cl::desc("print functions after layout optimization"), + cl::Hidden); + + +// Check against lists of functions from options if we should +// optimize the function with a given name. +bool shouldProcess(StringRef FunctionName) { + bool IsValid = true; + if (!FunctionNames.empty()) { + IsValid = false; + for (auto &Name : FunctionNames) { + if (FunctionName == Name) { + IsValid = true; + break; + } + } + } + if (!IsValid) + return false; + + if (!SkipFunctionNames.empty()) { + for (auto &Name : SkipFunctionNames) { + if (FunctionName == Name) { + IsValid = false; + break; + } + } + } + + return IsValid; +} + } // namespace opts static StringRef ToolName; @@ -406,6 +450,12 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) { for (auto &BFI : BinaryFunctions) { BinaryFunction &Function = BFI.second; + if (!opts::shouldProcess(Function.getName())) { + DEBUG(dbgs() << "FLO: skipping processing function " << Function.getName() + << " per user request.\n"); + continue; + } + SectionRef Section = Function.getSection(); assert(Section.containsSymbol(Function.getSymbol()) && "symbol not in section"); @@ -453,15 +503,16 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) { if (!Function.disassemble(FunctionData)) continue; + if (opts::PrintAll || opts::PrintDisasm) + Function.print(errs(), "after disassembly"); + if (!Function.buildCFG()) continue; - if (opts::DumpFunctions) - Function.print(errs(), true); - } // Iterate over all functions + if (opts::PrintAll || opts::PrintCFG) + Function.print(errs(), "after building cfg"); - if (opts::DumpFunctions) - return; + } // Iterate over all functions // Run optimization passes. // @@ -469,6 +520,10 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) { bool NagUser = true; for (auto &BFI : BinaryFunctions) { auto &Function = BFI.second; + + if (!opts::shouldProcess(Function.getName())) + continue; + // Detect and eliminate unreachable basic blocks. We could have those // filled with nops and they are used for alignment. // @@ -500,18 +555,22 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) { } } - if (unsigned Count = Function.eraseDeadBBs(Reachable)) { - outs() << "FLO: Removed " << Count - << " dead basic block(s) in function " << Function.getName() - << "\n"; + auto Count = Function.eraseDeadBBs(Reachable); + if (Count) { + DEBUG(dbgs() << "FLO: Removed " << Count + << " dead basic block(s) in function " + << Function.getName() << '\n'); } - DEBUG(dbgs() << "*** After unreachable block elimination ***\n"); - DEBUG(Function.print(dbgs(), /* PrintInstructions = */ true)); + if (opts::PrintAll || opts::PrintUCE) + Function.print(errs(), "after unreachable code elimination"); } if (opts::ReorderBlocks) { - BFI.second.optimizeLayout(opts::DumpLayout); + Function.optimizeLayout(); + + if (opts::PrintAll || opts::PrintReordered) + Function.print(errs(), "after reordering blocks"); } } @@ -560,30 +619,7 @@ static void OptimizeFile(ELFObjectFileBase *File, const DataReader &DR) { if (!Function.isSimple()) continue; - // Check against lists of functions from options if we should - // optimize the function. - bool IsValid = true; - if (!opts::FunctionNames.empty()) { - IsValid = false; - for (auto &Name : opts::FunctionNames) { - if (Function.getName() == Name) { - IsValid = true; - break; - } - } - } - if (!IsValid) - continue; - - if (!opts::SkipFunctionNames.empty()) { - for (auto &Name : opts::SkipFunctionNames) { - if (Function.getName() == Name) { - IsValid = false; - break; - } - } - } - if (!IsValid) + if (!opts::shouldProcess(Function.getName())) continue; DEBUG(dbgs() << "FLO: generating code for function \""