11dad6247STeresa Johnson //===- llvm/Analysis/MemoryProfileInfo.h - memory profile info ---*- C++ -*-==// 21dad6247STeresa Johnson // 31dad6247STeresa Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 41dad6247STeresa Johnson // See https://llvm.org/LICENSE.txt for license information. 51dad6247STeresa Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 61dad6247STeresa Johnson // 71dad6247STeresa Johnson //===----------------------------------------------------------------------===// 81dad6247STeresa Johnson // 91dad6247STeresa Johnson // This file contains utilities to analyze memory profile information. 101dad6247STeresa Johnson // 111dad6247STeresa Johnson //===----------------------------------------------------------------------===// 121dad6247STeresa Johnson 131dad6247STeresa Johnson #ifndef LLVM_ANALYSIS_MEMORYPROFILEINFO_H 141dad6247STeresa Johnson #define LLVM_ANALYSIS_MEMORYPROFILEINFO_H 151dad6247STeresa Johnson 161dad6247STeresa Johnson #include "llvm/IR/Metadata.h" 179eacbba2STeresa Johnson #include "llvm/IR/ModuleSummaryIndex.h" 181dad6247STeresa Johnson #include <map> 191dad6247STeresa Johnson 201dad6247STeresa Johnson namespace llvm { 211dad6247STeresa Johnson namespace memprof { 221dad6247STeresa Johnson 231dad6247STeresa Johnson /// Return the allocation type for a given set of memory profile values. 24a4bdb275STeresa Johnson AllocationType getAllocType(uint64_t TotalLifetimeAccessDensity, 25a4bdb275STeresa Johnson uint64_t AllocCount, uint64_t TotalLifetime); 261dad6247STeresa Johnson 271dad6247STeresa Johnson /// Build callstack metadata from the provided list of call stack ids. Returns 281dad6247STeresa Johnson /// the resulting metadata node. 291dad6247STeresa Johnson MDNode *buildCallstackMetadata(ArrayRef<uint64_t> CallStack, LLVMContext &Ctx); 301dad6247STeresa Johnson 319513f2fdSTeresa Johnson /// Build metadata from the provided list of full stack id and profiled size, to 329513f2fdSTeresa Johnson /// use when reporting of hinted sizes is enabled. 339513f2fdSTeresa Johnson MDNode *buildContextSizeMetadata(ArrayRef<ContextTotalSize> ContextSizeInfo, 349513f2fdSTeresa Johnson LLVMContext &Ctx); 359513f2fdSTeresa Johnson 361dad6247STeresa Johnson /// Returns the stack node from an MIB metadata node. 371dad6247STeresa Johnson MDNode *getMIBStackNode(const MDNode *MIB); 381dad6247STeresa Johnson 391dad6247STeresa Johnson /// Returns the allocation type from an MIB metadata node. 401dad6247STeresa Johnson AllocationType getMIBAllocType(const MDNode *MIB); 411dad6247STeresa Johnson 42a28261c7STeresa Johnson /// Returns the string to use in attributes with the given type. 43a28261c7STeresa Johnson std::string getAllocTypeAttributeString(AllocationType Type); 44a28261c7STeresa Johnson 455fd82ca0STeresa Johnson /// True if the AllocTypes bitmask contains just a single type. 465fd82ca0STeresa Johnson bool hasSingleAllocType(uint8_t AllocTypes); 475fd82ca0STeresa Johnson 481dad6247STeresa Johnson /// Class to build a trie of call stack contexts for a particular profiled 491dad6247STeresa Johnson /// allocation call, along with their associated allocation types. 501dad6247STeresa Johnson /// The allocation will be at the root of the trie, which is then used to 511dad6247STeresa Johnson /// compute the minimum lists of context ids needed to associate a call context 521dad6247STeresa Johnson /// with a single allocation type. 531dad6247STeresa Johnson class CallStackTrie { 541dad6247STeresa Johnson private: 551dad6247STeresa Johnson struct CallStackTrieNode { 561dad6247STeresa Johnson // Allocation types for call context sharing the context prefix at this 571dad6247STeresa Johnson // node. 581dad6247STeresa Johnson uint8_t AllocTypes; 59*ae6d5dd5STeresa Johnson // Updated as we add allocations to note if this is the deepest point in the 60*ae6d5dd5STeresa Johnson // trie that has an ambiguous allocation type (both Cold and NotCold). It is 61*ae6d5dd5STeresa Johnson // used to prune unneeded NotCold contexts, taking advantage of the fact 62*ae6d5dd5STeresa Johnson // that we later will only clone Cold contexts, as NotCold is the allocation 63*ae6d5dd5STeresa Johnson // default. We only need to keep as metadata the NotCold contexts that 64*ae6d5dd5STeresa Johnson // overlap the longest with Cold allocations, so that we know how deeply we 65*ae6d5dd5STeresa Johnson // need to clone. For example, assume we add the following contexts to the 66*ae6d5dd5STeresa Johnson // trie: 67*ae6d5dd5STeresa Johnson // 1 3 (notcold) 68*ae6d5dd5STeresa Johnson // 1 2 4 (cold) 69*ae6d5dd5STeresa Johnson // 1 2 5 (notcold) 70*ae6d5dd5STeresa Johnson // 1 2 6 (notcold) 71*ae6d5dd5STeresa Johnson // the trie looks like: 72*ae6d5dd5STeresa Johnson // 1 73*ae6d5dd5STeresa Johnson // / \ 74*ae6d5dd5STeresa Johnson // 2 3 75*ae6d5dd5STeresa Johnson // /|\ 76*ae6d5dd5STeresa Johnson // 4 5 6 77*ae6d5dd5STeresa Johnson // 78*ae6d5dd5STeresa Johnson // It is sufficient to prune all but one not cold contexts (either 1,2,5 or 79*ae6d5dd5STeresa Johnson // 1,2,6, we arbitrarily keep the first one we encounter which will be 80*ae6d5dd5STeresa Johnson // 1,2,5). We'll initially have DeepestAmbiguousAllocType set false for trie 81*ae6d5dd5STeresa Johnson // node 1 after the trie is built, and true for node 2. This indicates that 82*ae6d5dd5STeresa Johnson // the not cold context ending in 3 is not needed (its immediate callee has 83*ae6d5dd5STeresa Johnson // this value set false). The first notcold context we encounter when 84*ae6d5dd5STeresa Johnson // iterating the callers of node 2 will be the context ending in 5 (since 85*ae6d5dd5STeresa Johnson // std::map iteration is in sorted order of key), which will see that this 86*ae6d5dd5STeresa Johnson // field is true for its callee, so we will keep it. But at that point we 87*ae6d5dd5STeresa Johnson // set the callee's flag to false which prevents adding the not cold context 88*ae6d5dd5STeresa Johnson // ending in 6 unnecessarily. 89*ae6d5dd5STeresa Johnson bool DeepestAmbiguousAllocType = true; 909513f2fdSTeresa Johnson // If the user has requested reporting of hinted sizes, keep track of the 919513f2fdSTeresa Johnson // associated full stack id and profiled sizes. Can have more than one 929513f2fdSTeresa Johnson // after trimming (e.g. when building from metadata). This is only placed on 939513f2fdSTeresa Johnson // the last (root-most) trie node for each allocation context. 949513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo; 951dad6247STeresa Johnson // Map of caller stack id to the corresponding child Trie node. 961dad6247STeresa Johnson std::map<uint64_t, CallStackTrieNode *> Callers; 979513f2fdSTeresa Johnson CallStackTrieNode(AllocationType Type) 989513f2fdSTeresa Johnson : AllocTypes(static_cast<uint8_t>(Type)) {} 99c725a95eSTeresa Johnson void addAllocType(AllocationType AllocType) { 100c725a95eSTeresa Johnson AllocTypes |= static_cast<uint8_t>(AllocType); 101c725a95eSTeresa Johnson } 102c725a95eSTeresa Johnson void removeAllocType(AllocationType AllocType) { 103c725a95eSTeresa Johnson AllocTypes &= ~static_cast<uint8_t>(AllocType); 104c725a95eSTeresa Johnson } 105c725a95eSTeresa Johnson bool hasAllocType(AllocationType AllocType) const { 106c725a95eSTeresa Johnson return AllocTypes & static_cast<uint8_t>(AllocType); 107c725a95eSTeresa Johnson } 1081dad6247STeresa Johnson }; 1091dad6247STeresa Johnson 1101dad6247STeresa Johnson // The node for the allocation at the root. 111ad74adbdSKazu Hirata CallStackTrieNode *Alloc = nullptr; 1121dad6247STeresa Johnson // The allocation's leaf stack id. 113ad74adbdSKazu Hirata uint64_t AllocStackId = 0; 1141dad6247STeresa Johnson 1151dad6247STeresa Johnson void deleteTrieNode(CallStackTrieNode *Node) { 1161dad6247STeresa Johnson if (!Node) 1171dad6247STeresa Johnson return; 1181dad6247STeresa Johnson for (auto C : Node->Callers) 1191dad6247STeresa Johnson deleteTrieNode(C.second); 1201dad6247STeresa Johnson delete Node; 1211dad6247STeresa Johnson } 1221dad6247STeresa Johnson 1239513f2fdSTeresa Johnson // Recursively build up a complete list of context size information from the 1249513f2fdSTeresa Johnson // trie nodes reached form the given Node, for hint size reporting. 1259513f2fdSTeresa Johnson void collectContextSizeInfo(CallStackTrieNode *Node, 1269513f2fdSTeresa Johnson std::vector<ContextTotalSize> &ContextSizeInfo); 1279513f2fdSTeresa Johnson 128c725a95eSTeresa Johnson // Recursively convert hot allocation types to notcold, since we don't 129c725a95eSTeresa Johnson // actually do any cloning for hot contexts, to facilitate more aggressive 130c725a95eSTeresa Johnson // pruning of contexts. 131c725a95eSTeresa Johnson void convertHotToNotCold(CallStackTrieNode *Node); 132c725a95eSTeresa Johnson 1331dad6247STeresa Johnson // Recursive helper to trim contexts and create metadata nodes. 1341dad6247STeresa Johnson bool buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx, 1351dad6247STeresa Johnson std::vector<uint64_t> &MIBCallStack, 1361dad6247STeresa Johnson std::vector<Metadata *> &MIBNodes, 137*ae6d5dd5STeresa Johnson bool CalleeHasAmbiguousCallerContext, 138*ae6d5dd5STeresa Johnson bool &CalleeDeepestAmbiguousAllocType); 1391dad6247STeresa Johnson 1401dad6247STeresa Johnson public: 141ad74adbdSKazu Hirata CallStackTrie() = default; 1421dad6247STeresa Johnson ~CallStackTrie() { deleteTrieNode(Alloc); } 1431dad6247STeresa Johnson 1441dad6247STeresa Johnson bool empty() const { return Alloc == nullptr; } 1451dad6247STeresa Johnson 1461dad6247STeresa Johnson /// Add a call stack context with the given allocation type to the Trie. 1471dad6247STeresa Johnson /// The context is represented by the list of stack ids (computed during 1481dad6247STeresa Johnson /// matching via a debug location hash), expected to be in order from the 1491dad6247STeresa Johnson /// allocation call down to the bottom of the call stack (i.e. callee to 1501dad6247STeresa Johnson /// caller order). 1518c1bd67dSTeresa Johnson void addCallStack(AllocationType AllocType, ArrayRef<uint64_t> StackIds, 1529513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo = {}); 1531dad6247STeresa Johnson 1541dad6247STeresa Johnson /// Add the call stack context along with its allocation type from the MIB 1551dad6247STeresa Johnson /// metadata to the Trie. 1561dad6247STeresa Johnson void addCallStack(MDNode *MIB); 1571dad6247STeresa Johnson 1581dad6247STeresa Johnson /// Build and attach the minimal necessary MIB metadata. If the alloc has a 1591dad6247STeresa Johnson /// single allocation type, add a function attribute instead. The reason for 1601dad6247STeresa Johnson /// adding an attribute in this case is that it matches how the behavior for 1611dad6247STeresa Johnson /// allocation calls will be communicated to lib call simplification after 1621dad6247STeresa Johnson /// cloning or another optimization to distinguish the allocation types, 1631dad6247STeresa Johnson /// which is lower overhead and more direct than maintaining this metadata. 1641dad6247STeresa Johnson /// Returns true if memprof metadata attached, false if not (attribute added). 1651dad6247STeresa Johnson bool buildAndAttachMIBMetadata(CallBase *CI); 166d7d0e740STeresa Johnson 167d7d0e740STeresa Johnson /// Add an attribute for the given allocation type to the call instruction. 168d7d0e740STeresa Johnson /// If hinted by reporting is enabled, a message is emitted with the given 169d7d0e740STeresa Johnson /// descriptor used to identify the category of single allocation type. 170d7d0e740STeresa Johnson void addSingleAllocTypeAttribute(CallBase *CI, AllocationType AT, 171d7d0e740STeresa Johnson StringRef Descriptor); 1721dad6247STeresa Johnson }; 1731dad6247STeresa Johnson 1749eacbba2STeresa Johnson /// Helper class to iterate through stack ids in both metadata (memprof MIB and 1759eacbba2STeresa Johnson /// callsite) and the corresponding ThinLTO summary data structures 1769eacbba2STeresa Johnson /// (CallsiteInfo and MIBInfo). This simplifies implementation of client code 1779eacbba2STeresa Johnson /// which doesn't need to worry about whether we are operating with IR (Regular 1789eacbba2STeresa Johnson /// LTO), or summary (ThinLTO). 1799eacbba2STeresa Johnson template <class NodeT, class IteratorT> class CallStack { 1809eacbba2STeresa Johnson public: 1819eacbba2STeresa Johnson CallStack(const NodeT *N = nullptr) : N(N) {} 1829eacbba2STeresa Johnson 1839eacbba2STeresa Johnson // Implement minimum required methods for range-based for loop. 1849eacbba2STeresa Johnson // The default implementation assumes we are operating on ThinLTO data 1859eacbba2STeresa Johnson // structures, which have a vector of StackIdIndices. There are specialized 1869eacbba2STeresa Johnson // versions provided to iterate through metadata. 1879eacbba2STeresa Johnson struct CallStackIterator { 1889eacbba2STeresa Johnson const NodeT *N = nullptr; 1899eacbba2STeresa Johnson IteratorT Iter; 1909eacbba2STeresa Johnson CallStackIterator(const NodeT *N, bool End); 1919eacbba2STeresa Johnson uint64_t operator*(); 1929eacbba2STeresa Johnson bool operator==(const CallStackIterator &rhs) { return Iter == rhs.Iter; } 1939eacbba2STeresa Johnson bool operator!=(const CallStackIterator &rhs) { return !(*this == rhs); } 1949eacbba2STeresa Johnson void operator++() { ++Iter; } 1959eacbba2STeresa Johnson }; 1969eacbba2STeresa Johnson 1979eacbba2STeresa Johnson bool empty() const { return N == nullptr; } 1989eacbba2STeresa Johnson 1999eacbba2STeresa Johnson CallStackIterator begin() const; 2009eacbba2STeresa Johnson CallStackIterator end() const { return CallStackIterator(N, /*End*/ true); } 2019eacbba2STeresa Johnson CallStackIterator beginAfterSharedPrefix(CallStack &Other); 2026827c4f0STeresa Johnson uint64_t back() const; 2039eacbba2STeresa Johnson 2049eacbba2STeresa Johnson private: 2059eacbba2STeresa Johnson const NodeT *N = nullptr; 2069eacbba2STeresa Johnson }; 2079eacbba2STeresa Johnson 2089eacbba2STeresa Johnson template <class NodeT, class IteratorT> 2099eacbba2STeresa Johnson CallStack<NodeT, IteratorT>::CallStackIterator::CallStackIterator( 2109eacbba2STeresa Johnson const NodeT *N, bool End) 2119eacbba2STeresa Johnson : N(N) { 2126827c4f0STeresa Johnson if (!N) { 2136827c4f0STeresa Johnson Iter = nullptr; 2149eacbba2STeresa Johnson return; 2156827c4f0STeresa Johnson } 2169eacbba2STeresa Johnson Iter = End ? N->StackIdIndices.end() : N->StackIdIndices.begin(); 2179eacbba2STeresa Johnson } 2189eacbba2STeresa Johnson 2199eacbba2STeresa Johnson template <class NodeT, class IteratorT> 2209eacbba2STeresa Johnson uint64_t CallStack<NodeT, IteratorT>::CallStackIterator::operator*() { 2219eacbba2STeresa Johnson assert(Iter != N->StackIdIndices.end()); 2229eacbba2STeresa Johnson return *Iter; 2239eacbba2STeresa Johnson } 2249eacbba2STeresa Johnson 2259eacbba2STeresa Johnson template <class NodeT, class IteratorT> 2266827c4f0STeresa Johnson uint64_t CallStack<NodeT, IteratorT>::back() const { 2276827c4f0STeresa Johnson assert(N); 2286827c4f0STeresa Johnson return N->StackIdIndices.back(); 2296827c4f0STeresa Johnson } 2306827c4f0STeresa Johnson 2316827c4f0STeresa Johnson template <class NodeT, class IteratorT> 2329eacbba2STeresa Johnson typename CallStack<NodeT, IteratorT>::CallStackIterator 2339eacbba2STeresa Johnson CallStack<NodeT, IteratorT>::begin() const { 2349eacbba2STeresa Johnson return CallStackIterator(N, /*End*/ false); 2359eacbba2STeresa Johnson } 2369eacbba2STeresa Johnson 2379eacbba2STeresa Johnson template <class NodeT, class IteratorT> 2389eacbba2STeresa Johnson typename CallStack<NodeT, IteratorT>::CallStackIterator 2399eacbba2STeresa Johnson CallStack<NodeT, IteratorT>::beginAfterSharedPrefix(CallStack &Other) { 2409eacbba2STeresa Johnson CallStackIterator Cur = begin(); 2419eacbba2STeresa Johnson for (CallStackIterator OtherCur = Other.begin(); 2429eacbba2STeresa Johnson Cur != end() && OtherCur != Other.end(); ++Cur, ++OtherCur) 2439eacbba2STeresa Johnson assert(*Cur == *OtherCur); 2449eacbba2STeresa Johnson return Cur; 2459eacbba2STeresa Johnson } 2469eacbba2STeresa Johnson 2479eacbba2STeresa Johnson /// Specializations for iterating through IR metadata stack contexts. 2489eacbba2STeresa Johnson template <> 2499eacbba2STeresa Johnson CallStack<MDNode, MDNode::op_iterator>::CallStackIterator::CallStackIterator( 2509eacbba2STeresa Johnson const MDNode *N, bool End); 2519eacbba2STeresa Johnson template <> 2529eacbba2STeresa Johnson uint64_t CallStack<MDNode, MDNode::op_iterator>::CallStackIterator::operator*(); 2536827c4f0STeresa Johnson template <> uint64_t CallStack<MDNode, MDNode::op_iterator>::back() const; 2549eacbba2STeresa Johnson 2551dad6247STeresa Johnson } // end namespace memprof 2561dad6247STeresa Johnson } // end namespace llvm 2571dad6247STeresa Johnson 2581dad6247STeresa Johnson #endif 259