//===--- LLVMInlineTree.cpp - Prints the inline tree ----------------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See https://swift.org/LICENSE.txt for license information // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // This pass prints the tree of inlined instructions. It also prints a sorted // table containing the contribution to the code size increase for all inlined // functions. // The output is only an estimation because all printed numbers are LLVM // instruction counts rather than real code size bytes. // //===----------------------------------------------------------------------===// #define DEBUG_TYPE "llvm-inlinetree" #include "swift/Basic/Assertions.h" #include "swift/Basic/Range.h" #include "swift/Demangling/Demangle.h" #include "swift/LLVMPasses/Passes.h" #include "llvm/ADT/SmallSet.h" #include "llvm/IR/DebugInfoMetadata.h" #include "llvm/IR/Function.h" #include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Pass.h" #include "llvm/Support/Allocator.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Format.h" #include "llvm/Support/raw_ostream.h" using namespace llvm; using namespace swift; //===----------------------------------------------------------------------===// // LLVMInlineTree Pass //===----------------------------------------------------------------------===// char InlineTreePrinter::ID = 0; INITIALIZE_PASS_BEGIN(InlineTreePrinter, "inline-tree-printer", "Inline tree printer pass", false, false) INITIALIZE_PASS_END(InlineTreePrinter, "inline-tree-printer", "Inline tree printer pass", false, false) llvm::cl::opt InlineTreeNoDemangle("inline-tree-no-demangle", llvm::cl::init(false), llvm::cl::desc("Don't demangle symbols in inline tree output")); ModulePass *swift::createInlineTreePrinterPass() { initializeInlineTreePrinterPass(*PassRegistry::getPassRegistry()); return new InlineTreePrinter(); } void InlineTreePrinter::getAnalysisUsage(AnalysisUsage &AU) const { AU.setPreservesAll(); } namespace { class InlineTree { struct Node; using NodeMap = DenseMap; using NodeList = SmallVector; /// Defines a unique inline location. /// Used to distinguish between different instances of an inlined function. struct LocationKey { unsigned line; unsigned column; void *file; LocationKey(DILocation *DL) : line(DL->getLine()), column(DL->getColumn()), file(DL->getFile()) { } bool operator==(const LocationKey &RHS) const { return line == RHS.line && column == RHS.column && file == RHS.file; } bool operator<(const LocationKey &RHS) const { if (line != RHS.line) return line < RHS.line; if (column != RHS.column) return column < RHS.column; return file < RHS.file; } }; /// Represents a function or inlined function. There may be multiple nodes /// for a function in case the function is inlined into different callers. struct Node { StringRef FunctionName; /// Number of inlined instructions of this function in its caller. int numSelfInsts = 0; /// numSelfInsts + the callee-instructions inlined into this function. int numTotalInsts = 0; /// Callees which are inlined into this function. NodeMap UnsortedChildren; /// Contains all nodes of UnsortedChildren, but sorted by numTotalInsts. NodeList SortedChildren; /// Unique inline locations. The size is the number of times this function /// is inlined into its caller. SmallSet UniqueLocations; /// If true, this is a "real" function not an inlined one. bool isTopLevel = false; const NodeList &getChildren() { if (SortedChildren.empty() && !UnsortedChildren.empty()) sortNodes(UnsortedChildren, SortedChildren); return SortedChildren; } Node(StringRef FunctionName) : FunctionName(FunctionName) {} }; /// The summary for a function. It contains the total overhead of inlining /// the function. It is the summarized numbers of all nodes referring to the /// function minus the size of the original function (which is not inlined). struct Summary { StringRef FunctionName; unsigned totalInstOverhead = 0; unsigned selfInstOverhead = 0; unsigned instanceOverhead = 0; Summary(StringRef FunctionName) : FunctionName(FunctionName) { } }; SpecificBumpPtrAllocator NodeAllocator; /// Top level functions in the module (= not inlined functions). NodeList FunctionRootNodes; /// Mapping for the top level functions. NodeMap Functions2Nodes; /// All nodes: top level functions + inlined functions. NodeList allNodes; /// The total number of LLVM instructions in the module. unsigned totalNumberOfInstructions = 0; Node *getNode(StringRef FunctionName, NodeMap &Nodes) { Node *&Nd = Nodes[FunctionName]; if (!Nd) { Nd = new (NodeAllocator.Allocate()) Node(FunctionName); allNodes.push_back(Nd); } return Nd; } /// Create a sorted list of nodes according to numTotalInsts. static void sortNodes(const NodeMap &Map, NodeList &Result); /// Build the inline-tree for a function. void buildTree(Function *F); /// Returns a printable percent string of the \p numInsts compared to /// totalNumberOfInstructions. std::string getPercent(int numInsts) const; /// Recursively print the node tree starting at \p Nd. void printNode(Node *Nd, int indent, raw_ostream &os); public: /// Build the inline tree. void build(Module *M); /// Print the inline tree. void print(raw_ostream &os); }; /// Print the function \p Name as simplified demangled name or optionally /// as not demangled name. static void printSymbol(StringRef Name, raw_ostream &os) { if (InlineTreeNoDemangle) { os << Name; } else { os << demangleSymbolAsString(Name, Demangle::DemangleOptions::SimplifiedUIDemangleOptions()); } } void InlineTree::sortNodes(const NodeMap &Map, NodeList &Result) { for (auto Iter : Map) { Node *Nd = Iter.second; Result.push_back(Nd); } std::sort(Result.begin(), Result.end(), [](Node *Nd1, Node *Nd2) -> bool { return Nd1->numTotalInsts > Nd2->numTotalInsts; }); } void InlineTree::buildTree(Function *F) { Node *rootNode = getNode(F->getName(), Functions2Nodes); rootNode->isTopLevel = true; LLVM_DEBUG(dbgs() << "\nFunction " << F->getName() << '\n'); for (BasicBlock &BB : *F) { for (Instruction &I : BB) { LLVM_DEBUG(dbgs() << I << '\n'); ++totalNumberOfInstructions; SmallVector InlineChain; // Scan the chain of inlined scopes. DILocation *DL = I.getDebugLoc().get(); while (DL) { InlineChain.push_back(DL); DL = DL->getInlinedAt(); } Node *Nd = nullptr; DILocation *PrevDL = nullptr; for (DILocation *DL : llvm::reverse(InlineChain)) { DILocalScope *Sc = DL->getScope(); DISubprogram *SP = Sc->getSubprogram(); assert(SP); LLVM_DEBUG(dbgs() << " f=" << SP->getLinkageName()); if (Nd) { Nd = getNode(SP->getLinkageName(), Nd->UnsortedChildren); Nd->UniqueLocations.insert(LocationKey(PrevDL)); LLVM_DEBUG(dbgs() << ", loc="; PrevDL->print(dbgs()); dbgs() << '\n'); } else { Nd = rootNode; LLVM_DEBUG(dbgs() << ", root\n"); } ++Nd->numTotalInsts; PrevDL = DL; } if (!Nd) { Nd = rootNode; ++Nd->numTotalInsts; } ++Nd->numSelfInsts; } } } std::string InlineTree::getPercent(int numInsts) const { assert(totalNumberOfInstructions > 0); std::string str; raw_string_ostream os(str); double percent = (double)numInsts * 100. / totalNumberOfInstructions; os << format("%0.2f", percent) << '%'; return os.str(); } void InlineTree::printNode(Node *Nd, int indent, raw_ostream &os) { os << std::string(indent * 4, ' ') << '[' << Nd->UniqueLocations.size() << "x," << getPercent(Nd->numTotalInsts) << '=' << Nd->numTotalInsts << ",self=" << Nd->numSelfInsts << ']' << ' '; printSymbol(Nd->FunctionName, os); os << '\n'; for (Node *Child : Nd->getChildren()) { printNode(Child, indent + 1, os); } } void InlineTree::build(Module *M) { // Build the trees for all top-level functions. for (Function &F : *M) { if (F.empty()) continue; buildTree(&F); } sortNodes(Functions2Nodes, FunctionRootNodes); // Sort all nodes by FunctionName -> isTopLevel -> numTotalInsts std::sort(allNodes.begin(), allNodes.end(), [](Node *Nd1, Node *Nd2) -> bool { if (Nd1->FunctionName != Nd2->FunctionName) return Nd1->FunctionName < Nd2->FunctionName; if (Nd1->isTopLevel != Nd2->isTopLevel) return (int)Nd1->isTopLevel > (int)Nd2->isTopLevel; return Nd1->numTotalInsts > Nd2->numTotalInsts; }); } void InlineTree::print(raw_ostream &os) { // Calculate the summary information. os << "Inlining overhead (total = " << totalNumberOfInstructions << "):\n"; SmallVector Summaries; Summary S = Summary(StringRef()); // allNodes is sorted by function name. for (Node *Nd : allNodes) { if (Nd->FunctionName != S.FunctionName) { // Record the summary for the current function and continue with the // new function. if (S.instanceOverhead) Summaries.push_back(S); S = Summary(Nd->FunctionName); // The top-level node for the function appears first in the list. In this // case the size of UniqueLocations is 1. Only if the function is only // inlined and not present as top-level function the instanceOverhead may // be > 1. S.instanceOverhead = Nd->UniqueLocations.size(); if (S.instanceOverhead > 0) { // Remove the size of the first instance, because a single instance // of a function is always needed and does not contribute to the // _overhead_. S.totalInstOverhead = Nd->numTotalInsts - Nd->numTotalInsts / S.instanceOverhead; S.selfInstOverhead = Nd->numSelfInsts - Nd->numSelfInsts / S.instanceOverhead; --S.instanceOverhead; } } else { S.totalInstOverhead += Nd->numTotalInsts; S.selfInstOverhead += Nd->numSelfInsts; S.instanceOverhead += Nd->UniqueLocations.size(); } } if (S.instanceOverhead) Summaries.push_back(S); // Sort the summary table. std::sort(Summaries.begin(), Summaries.end(), [](const Summary &S1, const Summary &S2) -> bool { if (S1.totalInstOverhead != S2.totalInstOverhead) return S1.totalInstOverhead > S2.totalInstOverhead; if (S1.selfInstOverhead != S2.selfInstOverhead) return S1.selfInstOverhead > S2.selfInstOverhead; return S1.instanceOverhead > S2.instanceOverhead; }); // Print the summary table. unsigned totalOverhead = 0; for (const Summary &S : Summaries) { os << '[' << S.instanceOverhead << "x," << getPercent(S.totalInstOverhead) << '=' << S.totalInstOverhead << ",self=" << S.selfInstOverhead << "] "; printSymbol(S.FunctionName, os); os << '\n'; totalOverhead += S.selfInstOverhead; } os << "\nTotal inlining overhead; " << getPercent(totalOverhead) << '=' << totalOverhead << '\n'; // Print the inline tree. os << "\nInlining tree:\n"; for (Node *Nd : FunctionRootNodes) { printNode(Nd, 0, os); os << '\n'; } } } // end anonymous namespace /// The main entry point. bool InlineTreePrinter::runOnModule(Module &M) { InlineTree Tree; Tree.build(&M); Tree.print(outs()); return false; } llvm::PreservedAnalyses swift::InlineTreePrinterPass::run(llvm::Module &M, llvm::ModuleAnalysisManager &AM) { InlineTree Tree; Tree.build(&M); Tree.print(outs()); return llvm::PreservedAnalyses::all(); }