| //===--- 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/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/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<bool> |
| 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<StringRef, Node *>; |
| using NodeList = SmallVector<Node *, 8>; |
| |
| /// 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<LocationKey, 16> 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<Node> 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<DILocation *, 8> 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<Summary, 64> 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; |
| } |