forked from OSchip/llvm-project
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:
parent
7f44331773
commit
d4d773458c
|
@ -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 *
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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 \""
|
||||
|
|
Loading…
Reference in New Issue