More control over function printing.

Summary:
Can use '-print-*' option to print function at specific stage.
Use '-print-all' to print at every stage.

(cherry picked from FBD2578196)
This commit is contained in:
Maksim Panchenko 2015-10-23 15:52:59 -07:00
parent 7f44331773
commit d4d773458c
3 changed files with 104 additions and 100 deletions

View File

@ -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<uint8_t> 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<std::vector<uint64_t>> Weight;
std::map<BinaryBasicBlock *, int> BBToIndex;
std::vector<BinaryBasicBlock *> 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 *

View File

@ -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<MCInst>(Instruction));

View File

@ -100,16 +100,60 @@ ReorderBlocks("reorder-blocks",
cl::Optional);
static cl::opt<bool>
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<bool>
DumpFunctions("dump-functions", cl::desc("dump parsed functions (debugging)"),
PrintAll("print-all", cl::desc("print functions after each stage"),
cl::Hidden);
static cl::opt<bool>
DumpLayout("dump-layout", cl::desc("dump parsed flo data (debugging)"),
PrintCFG("print-cfg", cl::desc("print functions after CFG construction"),
cl::Hidden);
static cl::opt<bool>
PrintUCE("print-uce",
cl::desc("print functions after unreachable code elimination"),
cl::Hidden);
static cl::opt<bool>
PrintDisasm("print-disasm", cl::desc("print function after disassembly"),
cl::Hidden);
static cl::opt<bool>
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 \""