11dad6247STeresa Johnson //===-- MemoryProfileInfo.cpp - memory profile info ------------------------==// 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 #include "llvm/Analysis/MemoryProfileInfo.h" 140da2ba81SDaniil Fukalov #include "llvm/IR/Constants.h" 151dad6247STeresa Johnson #include "llvm/Support/CommandLine.h" 161dad6247STeresa Johnson 171dad6247STeresa Johnson using namespace llvm; 181dad6247STeresa Johnson using namespace llvm::memprof; 191dad6247STeresa Johnson 201dad6247STeresa Johnson #define DEBUG_TYPE "memory-profile-info" 211dad6247STeresa Johnson 22a4bdb275STeresa Johnson // Upper bound on lifetime access density (accesses per byte per lifetime sec) 23a4bdb275STeresa Johnson // for marking an allocation cold. 24a4bdb275STeresa Johnson cl::opt<float> MemProfLifetimeAccessDensityColdThreshold( 25a4bdb275STeresa Johnson "memprof-lifetime-access-density-cold-threshold", cl::init(0.05), 26a4bdb275STeresa Johnson cl::Hidden, 27a4bdb275STeresa Johnson cl::desc("The threshold the lifetime access density (accesses per byte per " 28a4bdb275STeresa Johnson "lifetime sec) must be under to consider an allocation cold")); 291dad6247STeresa Johnson 301dad6247STeresa Johnson // Lower bound on lifetime to mark an allocation cold (in addition to accesses 31a4bdb275STeresa Johnson // per byte per sec above). This is to avoid pessimizing short lived objects. 32a4bdb275STeresa Johnson cl::opt<unsigned> MemProfAveLifetimeColdThreshold( 33a4bdb275STeresa Johnson "memprof-ave-lifetime-cold-threshold", cl::init(200), cl::Hidden, 34a4bdb275STeresa Johnson cl::desc("The average lifetime (s) for an allocation to be considered " 351dad6247STeresa Johnson "cold")); 361dad6247STeresa Johnson 37b8d2f717SKan Wu // Lower bound on average lifetime accesses density (total life time access 38b8d2f717SKan Wu // density / alloc count) for marking an allocation hot. 39b8d2f717SKan Wu cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold( 40b8d2f717SKan Wu "memprof-min-ave-lifetime-access-density-hot-threshold", cl::init(1000), 41b8d2f717SKan Wu cl::Hidden, 42b8d2f717SKan Wu cl::desc("The minimum TotalLifetimeAccessDensity / AllocCount for an " 43b8d2f717SKan Wu "allocation to be considered hot")); 44b8d2f717SKan Wu 45ae8b5608STeresa Johnson cl::opt<bool> 46ae8b5608STeresa Johnson MemProfUseHotHints("memprof-use-hot-hints", cl::init(false), cl::Hidden, 47ae8b5608STeresa Johnson cl::desc("Enable use of hot hints (only supported for " 48ae8b5608STeresa Johnson "unambigously hot allocations)")); 49ae8b5608STeresa Johnson 508c1bd67dSTeresa Johnson cl::opt<bool> MemProfReportHintedSizes( 518c1bd67dSTeresa Johnson "memprof-report-hinted-sizes", cl::init(false), cl::Hidden, 528c1bd67dSTeresa Johnson cl::desc("Report total allocation sizes of hinted allocations")); 538c1bd67dSTeresa Johnson 54*ae6d5dd5STeresa Johnson // This is useful if we have enabled reporting of hinted sizes, and want to get 55*ae6d5dd5STeresa Johnson // information from the indexing step for all contexts (especially for testing), 56*ae6d5dd5STeresa Johnson // or have specified a value less than 100% for -memprof-cloning-cold-threshold. 57*ae6d5dd5STeresa Johnson cl::opt<bool> MemProfKeepAllNotColdContexts( 58*ae6d5dd5STeresa Johnson "memprof-keep-all-not-cold-contexts", cl::init(false), cl::Hidden, 59*ae6d5dd5STeresa Johnson cl::desc("Keep all non-cold contexts (increases cloning overheads)")); 60*ae6d5dd5STeresa Johnson 61a4bdb275STeresa Johnson AllocationType llvm::memprof::getAllocType(uint64_t TotalLifetimeAccessDensity, 62a4bdb275STeresa Johnson uint64_t AllocCount, 63a4bdb275STeresa Johnson uint64_t TotalLifetime) { 64a4bdb275STeresa Johnson // The access densities are multiplied by 100 to hold 2 decimal places of 65a4bdb275STeresa Johnson // precision, so need to divide by 100. 66a4bdb275STeresa Johnson if (((float)TotalLifetimeAccessDensity) / AllocCount / 100 < 67a4bdb275STeresa Johnson MemProfLifetimeAccessDensityColdThreshold 68a4bdb275STeresa Johnson // Lifetime is expected to be in ms, so convert the threshold to ms. 69a4bdb275STeresa Johnson && ((float)TotalLifetime) / AllocCount >= 70a4bdb275STeresa Johnson MemProfAveLifetimeColdThreshold * 1000) 711dad6247STeresa Johnson return AllocationType::Cold; 72b8d2f717SKan Wu 73b8d2f717SKan Wu // The access densities are multiplied by 100 to hold 2 decimal places of 74b8d2f717SKan Wu // precision, so need to divide by 100. 75ae8b5608STeresa Johnson if (MemProfUseHotHints && 76ae8b5608STeresa Johnson ((float)TotalLifetimeAccessDensity) / AllocCount / 100 > 77b8d2f717SKan Wu MemProfMinAveLifetimeAccessDensityHotThreshold) 78b8d2f717SKan Wu return AllocationType::Hot; 79b8d2f717SKan Wu 801dad6247STeresa Johnson return AllocationType::NotCold; 811dad6247STeresa Johnson } 821dad6247STeresa Johnson 831dad6247STeresa Johnson MDNode *llvm::memprof::buildCallstackMetadata(ArrayRef<uint64_t> CallStack, 841dad6247STeresa Johnson LLVMContext &Ctx) { 85890c4becSKazu Hirata SmallVector<Metadata *, 8> StackVals; 86890c4becSKazu Hirata StackVals.reserve(CallStack.size()); 871dad6247STeresa Johnson for (auto Id : CallStack) { 881dad6247STeresa Johnson auto *StackValMD = 891dad6247STeresa Johnson ValueAsMetadata::get(ConstantInt::get(Type::getInt64Ty(Ctx), Id)); 901dad6247STeresa Johnson StackVals.push_back(StackValMD); 911dad6247STeresa Johnson } 921dad6247STeresa Johnson return MDNode::get(Ctx, StackVals); 931dad6247STeresa Johnson } 941dad6247STeresa Johnson 951dad6247STeresa Johnson MDNode *llvm::memprof::getMIBStackNode(const MDNode *MIB) { 968c1bd67dSTeresa Johnson assert(MIB->getNumOperands() >= 2); 971dad6247STeresa Johnson // The stack metadata is the first operand of each memprof MIB metadata. 981dad6247STeresa Johnson return cast<MDNode>(MIB->getOperand(0)); 991dad6247STeresa Johnson } 1001dad6247STeresa Johnson 1011dad6247STeresa Johnson AllocationType llvm::memprof::getMIBAllocType(const MDNode *MIB) { 1028c1bd67dSTeresa Johnson assert(MIB->getNumOperands() >= 2); 1031dad6247STeresa Johnson // The allocation type is currently the second operand of each memprof 1041dad6247STeresa Johnson // MIB metadata. This will need to change as we add additional allocation 1051dad6247STeresa Johnson // types that can be applied based on the allocation profile data. 1061dad6247STeresa Johnson auto *MDS = dyn_cast<MDString>(MIB->getOperand(1)); 1071dad6247STeresa Johnson assert(MDS); 108026a29e8SKazu Hirata if (MDS->getString() == "cold") { 1091dad6247STeresa Johnson return AllocationType::Cold; 110026a29e8SKazu Hirata } else if (MDS->getString() == "hot") { 111b8d2f717SKan Wu return AllocationType::Hot; 112b8d2f717SKan Wu } 1131dad6247STeresa Johnson return AllocationType::NotCold; 1141dad6247STeresa Johnson } 1151dad6247STeresa Johnson 116a28261c7STeresa Johnson std::string llvm::memprof::getAllocTypeAttributeString(AllocationType Type) { 1171dad6247STeresa Johnson switch (Type) { 1181dad6247STeresa Johnson case AllocationType::NotCold: 1191dad6247STeresa Johnson return "notcold"; 1201dad6247STeresa Johnson break; 1211dad6247STeresa Johnson case AllocationType::Cold: 1221dad6247STeresa Johnson return "cold"; 1231dad6247STeresa Johnson break; 124b8d2f717SKan Wu case AllocationType::Hot: 125b8d2f717SKan Wu return "hot"; 126b8d2f717SKan Wu break; 1271dad6247STeresa Johnson default: 1281dad6247STeresa Johnson assert(false && "Unexpected alloc type"); 1291dad6247STeresa Johnson } 1301dad6247STeresa Johnson llvm_unreachable("invalid alloc type"); 1311dad6247STeresa Johnson } 1321dad6247STeresa Johnson 1331dad6247STeresa Johnson static void addAllocTypeAttribute(LLVMContext &Ctx, CallBase *CI, 1341dad6247STeresa Johnson AllocationType AllocType) { 1351dad6247STeresa Johnson auto AllocTypeString = getAllocTypeAttributeString(AllocType); 1361dad6247STeresa Johnson auto A = llvm::Attribute::get(Ctx, "memprof", AllocTypeString); 1371dad6247STeresa Johnson CI->addFnAttr(A); 1381dad6247STeresa Johnson } 1391dad6247STeresa Johnson 1405fd82ca0STeresa Johnson bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) { 141caa99a01SKazu Hirata const unsigned NumAllocTypes = llvm::popcount(AllocTypes); 1421dad6247STeresa Johnson assert(NumAllocTypes != 0); 1431dad6247STeresa Johnson return NumAllocTypes == 1; 1441dad6247STeresa Johnson } 1451dad6247STeresa Johnson 1469513f2fdSTeresa Johnson void CallStackTrie::addCallStack( 1479513f2fdSTeresa Johnson AllocationType AllocType, ArrayRef<uint64_t> StackIds, 1489513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo) { 1491dad6247STeresa Johnson bool First = true; 1501dad6247STeresa Johnson CallStackTrieNode *Curr = nullptr; 1511dad6247STeresa Johnson for (auto StackId : StackIds) { 1521dad6247STeresa Johnson // If this is the first stack frame, add or update alloc node. 1531dad6247STeresa Johnson if (First) { 1541dad6247STeresa Johnson First = false; 1551dad6247STeresa Johnson if (Alloc) { 1561dad6247STeresa Johnson assert(AllocStackId == StackId); 157c725a95eSTeresa Johnson Alloc->addAllocType(AllocType); 1581dad6247STeresa Johnson } else { 1591dad6247STeresa Johnson AllocStackId = StackId; 1609513f2fdSTeresa Johnson Alloc = new CallStackTrieNode(AllocType); 1611dad6247STeresa Johnson } 1621dad6247STeresa Johnson Curr = Alloc; 1631dad6247STeresa Johnson continue; 1641dad6247STeresa Johnson } 1651dad6247STeresa Johnson // Update existing caller node if it exists. 166*ae6d5dd5STeresa Johnson CallStackTrieNode *Prev = nullptr; 1671dad6247STeresa Johnson auto Next = Curr->Callers.find(StackId); 1681dad6247STeresa Johnson if (Next != Curr->Callers.end()) { 169*ae6d5dd5STeresa Johnson Prev = Curr; 1701dad6247STeresa Johnson Curr = Next->second; 171c725a95eSTeresa Johnson Curr->addAllocType(AllocType); 172*ae6d5dd5STeresa Johnson // If this node has an ambiguous alloc type, its callee is not the deepest 173*ae6d5dd5STeresa Johnson // point where we have an ambigous allocation type. 174*ae6d5dd5STeresa Johnson if (!hasSingleAllocType(Curr->AllocTypes)) 175*ae6d5dd5STeresa Johnson Prev->DeepestAmbiguousAllocType = false; 1761dad6247STeresa Johnson continue; 1771dad6247STeresa Johnson } 1781dad6247STeresa Johnson // Otherwise add a new caller node. 1799513f2fdSTeresa Johnson auto *New = new CallStackTrieNode(AllocType); 1801dad6247STeresa Johnson Curr->Callers[StackId] = New; 1811dad6247STeresa Johnson Curr = New; 1821dad6247STeresa Johnson } 1831dad6247STeresa Johnson assert(Curr); 1849513f2fdSTeresa Johnson Curr->ContextSizeInfo.insert(Curr->ContextSizeInfo.end(), 1859513f2fdSTeresa Johnson ContextSizeInfo.begin(), ContextSizeInfo.end()); 1861dad6247STeresa Johnson } 1871dad6247STeresa Johnson 1881dad6247STeresa Johnson void CallStackTrie::addCallStack(MDNode *MIB) { 1891dad6247STeresa Johnson MDNode *StackMD = getMIBStackNode(MIB); 1901dad6247STeresa Johnson assert(StackMD); 1911dad6247STeresa Johnson std::vector<uint64_t> CallStack; 1921dad6247STeresa Johnson CallStack.reserve(StackMD->getNumOperands()); 193e20d210eSKazu Hirata for (const auto &MIBStackIter : StackMD->operands()) { 1941dad6247STeresa Johnson auto *StackId = mdconst::dyn_extract<ConstantInt>(MIBStackIter); 1951dad6247STeresa Johnson assert(StackId); 1961dad6247STeresa Johnson CallStack.push_back(StackId->getZExtValue()); 1971dad6247STeresa Johnson } 1989513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo; 1999513f2fdSTeresa Johnson // Collect the context size information if it exists. 2009513f2fdSTeresa Johnson if (MIB->getNumOperands() > 2) { 2019513f2fdSTeresa Johnson for (unsigned I = 2; I < MIB->getNumOperands(); I++) { 2029513f2fdSTeresa Johnson MDNode *ContextSizePair = dyn_cast<MDNode>(MIB->getOperand(I)); 2039513f2fdSTeresa Johnson assert(ContextSizePair->getNumOperands() == 2); 2049513f2fdSTeresa Johnson uint64_t FullStackId = 2059513f2fdSTeresa Johnson mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(0)) 2069513f2fdSTeresa Johnson ->getZExtValue(); 2079513f2fdSTeresa Johnson uint64_t TotalSize = 2089513f2fdSTeresa Johnson mdconst::dyn_extract<ConstantInt>(ContextSizePair->getOperand(1)) 2099513f2fdSTeresa Johnson ->getZExtValue(); 2109513f2fdSTeresa Johnson ContextSizeInfo.push_back({FullStackId, TotalSize}); 2119513f2fdSTeresa Johnson } 2129513f2fdSTeresa Johnson } 2139513f2fdSTeresa Johnson addCallStack(getMIBAllocType(MIB), CallStack, std::move(ContextSizeInfo)); 2141dad6247STeresa Johnson } 2151dad6247STeresa Johnson 216b2f3ac83SKazu Hirata static MDNode *createMIBNode(LLVMContext &Ctx, ArrayRef<uint64_t> MIBCallStack, 2179513f2fdSTeresa Johnson AllocationType AllocType, 2189513f2fdSTeresa Johnson ArrayRef<ContextTotalSize> ContextSizeInfo) { 219b2f3ac83SKazu Hirata SmallVector<Metadata *> MIBPayload( 2201dad6247STeresa Johnson {buildCallstackMetadata(MIBCallStack, Ctx)}); 2211dad6247STeresa Johnson MIBPayload.push_back( 2221dad6247STeresa Johnson MDString::get(Ctx, getAllocTypeAttributeString(AllocType))); 2239513f2fdSTeresa Johnson if (!ContextSizeInfo.empty()) { 2249513f2fdSTeresa Johnson for (const auto &[FullStackId, TotalSize] : ContextSizeInfo) { 2259513f2fdSTeresa Johnson auto *FullStackIdMD = ValueAsMetadata::get( 2269513f2fdSTeresa Johnson ConstantInt::get(Type::getInt64Ty(Ctx), FullStackId)); 2279513f2fdSTeresa Johnson auto *TotalSizeMD = ValueAsMetadata::get( 2289513f2fdSTeresa Johnson ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize)); 2299513f2fdSTeresa Johnson auto *ContextSizeMD = MDNode::get(Ctx, {FullStackIdMD, TotalSizeMD}); 2309513f2fdSTeresa Johnson MIBPayload.push_back(ContextSizeMD); 2319513f2fdSTeresa Johnson } 2329513f2fdSTeresa Johnson } 2331dad6247STeresa Johnson return MDNode::get(Ctx, MIBPayload); 2341dad6247STeresa Johnson } 2351dad6247STeresa Johnson 2369513f2fdSTeresa Johnson void CallStackTrie::collectContextSizeInfo( 2379513f2fdSTeresa Johnson CallStackTrieNode *Node, std::vector<ContextTotalSize> &ContextSizeInfo) { 2389513f2fdSTeresa Johnson ContextSizeInfo.insert(ContextSizeInfo.end(), Node->ContextSizeInfo.begin(), 2399513f2fdSTeresa Johnson Node->ContextSizeInfo.end()); 2409513f2fdSTeresa Johnson for (auto &Caller : Node->Callers) 2419513f2fdSTeresa Johnson collectContextSizeInfo(Caller.second, ContextSizeInfo); 2429513f2fdSTeresa Johnson } 2439513f2fdSTeresa Johnson 244c725a95eSTeresa Johnson void CallStackTrie::convertHotToNotCold(CallStackTrieNode *Node) { 245c725a95eSTeresa Johnson if (Node->hasAllocType(AllocationType::Hot)) { 246c725a95eSTeresa Johnson Node->removeAllocType(AllocationType::Hot); 247c725a95eSTeresa Johnson Node->addAllocType(AllocationType::NotCold); 248c725a95eSTeresa Johnson } 249c725a95eSTeresa Johnson for (auto &Caller : Node->Callers) 250c725a95eSTeresa Johnson convertHotToNotCold(Caller.second); 251c725a95eSTeresa Johnson } 252c725a95eSTeresa Johnson 2531dad6247STeresa Johnson // Recursive helper to trim contexts and create metadata nodes. 2541dad6247STeresa Johnson // Caller should have pushed Node's loc to MIBCallStack. Doing this in the 2551dad6247STeresa Johnson // caller makes it simpler to handle the many early returns in this method. 2561dad6247STeresa Johnson bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx, 2571dad6247STeresa Johnson std::vector<uint64_t> &MIBCallStack, 2581dad6247STeresa Johnson std::vector<Metadata *> &MIBNodes, 259*ae6d5dd5STeresa Johnson bool CalleeHasAmbiguousCallerContext, 260*ae6d5dd5STeresa Johnson bool &CalleeDeepestAmbiguousAllocType) { 2611dad6247STeresa Johnson // Trim context below the first node in a prefix with a single alloc type. 2621dad6247STeresa Johnson // Add an MIB record for the current call stack prefix. 2631dad6247STeresa Johnson if (hasSingleAllocType(Node->AllocTypes)) { 264*ae6d5dd5STeresa Johnson // Because we only clone cold contexts (we don't clone for exposing NotCold 265*ae6d5dd5STeresa Johnson // contexts as that is the default allocation behavior), we create MIB 266*ae6d5dd5STeresa Johnson // metadata for this context if any of the following are true: 267*ae6d5dd5STeresa Johnson // 1) It is cold. 268*ae6d5dd5STeresa Johnson // 2) The immediate callee is the deepest point where we have an ambiguous 269*ae6d5dd5STeresa Johnson // allocation type (i.e. the other callers that are cold need to know 270*ae6d5dd5STeresa Johnson // that we have a not cold context overlapping to this point so that we 271*ae6d5dd5STeresa Johnson // know how deep to clone). 272*ae6d5dd5STeresa Johnson // 3) MemProfKeepAllNotColdContexts is enabled, which is useful if we are 273*ae6d5dd5STeresa Johnson // reporting hinted sizes, and want to get information from the indexing 274*ae6d5dd5STeresa Johnson // step for all contexts, or have specified a value less than 100% for 275*ae6d5dd5STeresa Johnson // -memprof-cloning-cold-threshold. 276*ae6d5dd5STeresa Johnson if (Node->hasAllocType(AllocationType::Cold) || 277*ae6d5dd5STeresa Johnson CalleeDeepestAmbiguousAllocType || MemProfKeepAllNotColdContexts) { 2789513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo; 2799513f2fdSTeresa Johnson collectContextSizeInfo(Node, ContextSizeInfo); 280*ae6d5dd5STeresa Johnson MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, 281*ae6d5dd5STeresa Johnson (AllocationType)Node->AllocTypes, 282*ae6d5dd5STeresa Johnson ContextSizeInfo)); 283*ae6d5dd5STeresa Johnson // If we just emitted an MIB for a not cold caller, don't need to emit 284*ae6d5dd5STeresa Johnson // another one for the callee to correctly disambiguate its cold callers. 285*ae6d5dd5STeresa Johnson if (!Node->hasAllocType(AllocationType::Cold)) 286*ae6d5dd5STeresa Johnson CalleeDeepestAmbiguousAllocType = false; 287*ae6d5dd5STeresa Johnson } 2881dad6247STeresa Johnson return true; 2891dad6247STeresa Johnson } 2901dad6247STeresa Johnson 2911dad6247STeresa Johnson // We don't have a single allocation for all the contexts sharing this prefix, 2921dad6247STeresa Johnson // so recursively descend into callers in trie. 2931dad6247STeresa Johnson if (!Node->Callers.empty()) { 2941dad6247STeresa Johnson bool NodeHasAmbiguousCallerContext = Node->Callers.size() > 1; 2951dad6247STeresa Johnson bool AddedMIBNodesForAllCallerContexts = true; 2961dad6247STeresa Johnson for (auto &Caller : Node->Callers) { 2971dad6247STeresa Johnson MIBCallStack.push_back(Caller.first); 298*ae6d5dd5STeresa Johnson AddedMIBNodesForAllCallerContexts &= buildMIBNodes( 299*ae6d5dd5STeresa Johnson Caller.second, Ctx, MIBCallStack, MIBNodes, 300*ae6d5dd5STeresa Johnson NodeHasAmbiguousCallerContext, Node->DeepestAmbiguousAllocType); 3011dad6247STeresa Johnson // Remove Caller. 3021dad6247STeresa Johnson MIBCallStack.pop_back(); 3031dad6247STeresa Johnson } 3041dad6247STeresa Johnson if (AddedMIBNodesForAllCallerContexts) 3051dad6247STeresa Johnson return true; 3061dad6247STeresa Johnson // We expect that the callers should be forced to add MIBs to disambiguate 3071dad6247STeresa Johnson // the context in this case (see below). 3081dad6247STeresa Johnson assert(!NodeHasAmbiguousCallerContext); 3091dad6247STeresa Johnson } 3101dad6247STeresa Johnson 3111dad6247STeresa Johnson // If we reached here, then this node does not have a single allocation type, 3121dad6247STeresa Johnson // and we didn't add metadata for a longer call stack prefix including any of 3131dad6247STeresa Johnson // Node's callers. That means we never hit a single allocation type along all 3141dad6247STeresa Johnson // call stacks with this prefix. This can happen due to recursion collapsing 3151dad6247STeresa Johnson // or the stack being deeper than tracked by the profiler runtime, leading to 3161dad6247STeresa Johnson // contexts with different allocation types being merged. In that case, we 3171dad6247STeresa Johnson // trim the context just below the deepest context split, which is this 3181dad6247STeresa Johnson // node if the callee has an ambiguous caller context (multiple callers), 3191dad6247STeresa Johnson // since the recursive calls above returned false. Conservatively give it 3201dad6247STeresa Johnson // non-cold allocation type. 3211dad6247STeresa Johnson if (!CalleeHasAmbiguousCallerContext) 3221dad6247STeresa Johnson return false; 3239513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo; 3249513f2fdSTeresa Johnson collectContextSizeInfo(Node, ContextSizeInfo); 3258c1bd67dSTeresa Johnson MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold, 3269513f2fdSTeresa Johnson ContextSizeInfo)); 3271dad6247STeresa Johnson return true; 3281dad6247STeresa Johnson } 3291dad6247STeresa Johnson 330d7d0e740STeresa Johnson void CallStackTrie::addSingleAllocTypeAttribute(CallBase *CI, AllocationType AT, 331d7d0e740STeresa Johnson StringRef Descriptor) { 332d7d0e740STeresa Johnson addAllocTypeAttribute(CI->getContext(), CI, AT); 3338c1bd67dSTeresa Johnson if (MemProfReportHintedSizes) { 3349513f2fdSTeresa Johnson std::vector<ContextTotalSize> ContextSizeInfo; 3359513f2fdSTeresa Johnson collectContextSizeInfo(Alloc, ContextSizeInfo); 3369513f2fdSTeresa Johnson for (const auto &[FullStackId, TotalSize] : ContextSizeInfo) { 337d7d0e740STeresa Johnson errs() << "MemProf hinting: Total size for full allocation context hash " 338d7d0e740STeresa Johnson << FullStackId << " and " << Descriptor << " alloc type " 339d7d0e740STeresa Johnson << getAllocTypeAttributeString(AT) << ": " << TotalSize << "\n"; 3409513f2fdSTeresa Johnson } 3418c1bd67dSTeresa Johnson } 342d7d0e740STeresa Johnson } 343d7d0e740STeresa Johnson 344d7d0e740STeresa Johnson // Build and attach the minimal necessary MIB metadata. If the alloc has a 345d7d0e740STeresa Johnson // single allocation type, add a function attribute instead. Returns true if 346d7d0e740STeresa Johnson // memprof metadata attached, false if not (attribute added). 347d7d0e740STeresa Johnson bool CallStackTrie::buildAndAttachMIBMetadata(CallBase *CI) { 348d7d0e740STeresa Johnson if (hasSingleAllocType(Alloc->AllocTypes)) { 349d7d0e740STeresa Johnson addSingleAllocTypeAttribute(CI, (AllocationType)Alloc->AllocTypes, 350d7d0e740STeresa Johnson "single"); 3511dad6247STeresa Johnson return false; 3521dad6247STeresa Johnson } 353c725a95eSTeresa Johnson // If there were any hot allocation contexts, the Alloc trie node would have 354c725a95eSTeresa Johnson // the Hot type set. If so, because we don't currently support cloning for hot 355c725a95eSTeresa Johnson // contexts, they should be converted to NotCold. This happens in the cloning 356c725a95eSTeresa Johnson // support anyway, however, doing this now enables more aggressive context 357c725a95eSTeresa Johnson // trimming when building the MIB metadata (and possibly may make the 358c725a95eSTeresa Johnson // allocation have a single NotCold allocation type), greatly reducing 359c725a95eSTeresa Johnson // overheads in bitcode, cloning memory and cloning time. 360c725a95eSTeresa Johnson if (Alloc->hasAllocType(AllocationType::Hot)) { 361c725a95eSTeresa Johnson convertHotToNotCold(Alloc); 362c725a95eSTeresa Johnson // Check whether we now have a single alloc type. 363c725a95eSTeresa Johnson if (hasSingleAllocType(Alloc->AllocTypes)) { 364c725a95eSTeresa Johnson addSingleAllocTypeAttribute(CI, (AllocationType)Alloc->AllocTypes, 365c725a95eSTeresa Johnson "single"); 366c725a95eSTeresa Johnson return false; 367c725a95eSTeresa Johnson } 368c725a95eSTeresa Johnson } 369d7d0e740STeresa Johnson auto &Ctx = CI->getContext(); 3701dad6247STeresa Johnson std::vector<uint64_t> MIBCallStack; 3711dad6247STeresa Johnson MIBCallStack.push_back(AllocStackId); 3721dad6247STeresa Johnson std::vector<Metadata *> MIBNodes; 3731dad6247STeresa Johnson assert(!Alloc->Callers.empty() && "addCallStack has not been called yet"); 374*ae6d5dd5STeresa Johnson // The CalleeHasAmbiguousCallerContext flag is meant to say whether the 375*ae6d5dd5STeresa Johnson // callee of the given node has more than one caller. Here the node being 376*ae6d5dd5STeresa Johnson // passed in is the alloc and it has no callees. So it's false. 377*ae6d5dd5STeresa Johnson // Similarly, the last parameter is meant to say whether the callee of the 378*ae6d5dd5STeresa Johnson // given node is the deepest point where we have ambiguous alloc types, which 379*ae6d5dd5STeresa Johnson // is also false as the alloc has no callees. 380*ae6d5dd5STeresa Johnson bool DeepestAmbiguousAllocType = true; 381*ae6d5dd5STeresa Johnson if (buildMIBNodes(Alloc, Ctx, MIBCallStack, MIBNodes, 382*ae6d5dd5STeresa Johnson /*CalleeHasAmbiguousCallerContext=*/false, 383*ae6d5dd5STeresa Johnson DeepestAmbiguousAllocType)) { 3841dad6247STeresa Johnson assert(MIBCallStack.size() == 1 && 3851dad6247STeresa Johnson "Should only be left with Alloc's location in stack"); 3861dad6247STeresa Johnson CI->setMetadata(LLVMContext::MD_memprof, MDNode::get(Ctx, MIBNodes)); 3871dad6247STeresa Johnson return true; 3881dad6247STeresa Johnson } 3891e7d5871Slifengxiang1025 // If there exists corner case that CallStackTrie has one chain to leaf 3901e7d5871Slifengxiang1025 // and all node in the chain have multi alloc type, conservatively give 3911e7d5871Slifengxiang1025 // it non-cold allocation type. 392d7d0e740STeresa Johnson // FIXME: Avoid this case before memory profile created. Alternatively, select 393d7d0e740STeresa Johnson // hint based on fraction cold. 394d7d0e740STeresa Johnson addSingleAllocTypeAttribute(CI, AllocationType::NotCold, "indistinguishable"); 3951e7d5871Slifengxiang1025 return false; 3961e7d5871Slifengxiang1025 } 3979eacbba2STeresa Johnson 3989eacbba2STeresa Johnson template <> 3999eacbba2STeresa Johnson CallStack<MDNode, MDNode::op_iterator>::CallStackIterator::CallStackIterator( 4009eacbba2STeresa Johnson const MDNode *N, bool End) 4019eacbba2STeresa Johnson : N(N) { 4029eacbba2STeresa Johnson if (!N) 4039eacbba2STeresa Johnson return; 4049eacbba2STeresa Johnson Iter = End ? N->op_end() : N->op_begin(); 4059eacbba2STeresa Johnson } 4069eacbba2STeresa Johnson 4079eacbba2STeresa Johnson template <> 4089eacbba2STeresa Johnson uint64_t 4099eacbba2STeresa Johnson CallStack<MDNode, MDNode::op_iterator>::CallStackIterator::operator*() { 4109eacbba2STeresa Johnson assert(Iter != N->op_end()); 4119eacbba2STeresa Johnson ConstantInt *StackIdCInt = mdconst::dyn_extract<ConstantInt>(*Iter); 4129eacbba2STeresa Johnson assert(StackIdCInt); 4139eacbba2STeresa Johnson return StackIdCInt->getZExtValue(); 4149eacbba2STeresa Johnson } 4156827c4f0STeresa Johnson 4166827c4f0STeresa Johnson template <> uint64_t CallStack<MDNode, MDNode::op_iterator>::back() const { 4176827c4f0STeresa Johnson assert(N); 4186827c4f0STeresa Johnson return mdconst::dyn_extract<ConstantInt>(N->operands().back()) 4196827c4f0STeresa Johnson ->getZExtValue(); 4206827c4f0STeresa Johnson } 4213a423a10STeresa Johnson 4223a423a10STeresa Johnson MDNode *MDNode::getMergedMemProfMetadata(MDNode *A, MDNode *B) { 4233a423a10STeresa Johnson // TODO: Support more sophisticated merging, such as selecting the one with 4243a423a10STeresa Johnson // more bytes allocated, or implement support for carrying multiple allocation 4253a423a10STeresa Johnson // leaf contexts. For now, keep the first one. 4263a423a10STeresa Johnson if (A) 4273a423a10STeresa Johnson return A; 4283a423a10STeresa Johnson return B; 4293a423a10STeresa Johnson } 4303a423a10STeresa Johnson 4313a423a10STeresa Johnson MDNode *MDNode::getMergedCallsiteMetadata(MDNode *A, MDNode *B) { 4323a423a10STeresa Johnson // TODO: Support more sophisticated merging, which will require support for 4333a423a10STeresa Johnson // carrying multiple contexts. For now, keep the first one. 4343a423a10STeresa Johnson if (A) 4353a423a10STeresa Johnson return A; 4363a423a10STeresa Johnson return B; 4373a423a10STeresa Johnson } 438