LLVM 20.0.0git
MemoryProfileInfo.cpp
Go to the documentation of this file.
1//===-- MemoryProfileInfo.cpp - memory profile info ------------------------==//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file contains utilities to analyze memory profile information.
10//
11//===----------------------------------------------------------------------===//
12
14#include "llvm/IR/Constants.h"
16
17using namespace llvm;
18using namespace llvm::memprof;
19
20#define DEBUG_TYPE "memory-profile-info"
21
22// Upper bound on lifetime access density (accesses per byte per lifetime sec)
23// for marking an allocation cold.
25 "memprof-lifetime-access-density-cold-threshold", cl::init(0.05),
27 cl::desc("The threshold the lifetime access density (accesses per byte per "
28 "lifetime sec) must be under to consider an allocation cold"));
29
30// Lower bound on lifetime to mark an allocation cold (in addition to accesses
31// per byte per sec above). This is to avoid pessimizing short lived objects.
33 "memprof-ave-lifetime-cold-threshold", cl::init(200), cl::Hidden,
34 cl::desc("The average lifetime (s) for an allocation to be considered "
35 "cold"));
36
37// Lower bound on average lifetime accesses density (total life time access
38// density / alloc count) for marking an allocation hot.
40 "memprof-min-ave-lifetime-access-density-hot-threshold", cl::init(1000),
42 cl::desc("The minimum TotalLifetimeAccessDensity / AllocCount for an "
43 "allocation to be considered hot"));
44
46 "memprof-report-hinted-sizes", cl::init(false), cl::Hidden,
47 cl::desc("Report total allocation sizes of hinted allocations"));
48
50 uint64_t AllocCount,
51 uint64_t TotalLifetime) {
52 // The access densities are multiplied by 100 to hold 2 decimal places of
53 // precision, so need to divide by 100.
54 if (((float)TotalLifetimeAccessDensity) / AllocCount / 100 <
56 // Lifetime is expected to be in ms, so convert the threshold to ms.
57 && ((float)TotalLifetime) / AllocCount >=
59 return AllocationType::Cold;
60
61 // The access densities are multiplied by 100 to hold 2 decimal places of
62 // precision, so need to divide by 100.
63 if (((float)TotalLifetimeAccessDensity) / AllocCount / 100 >
65 return AllocationType::Hot;
66
67 return AllocationType::NotCold;
68}
69
71 LLVMContext &Ctx) {
72 std::vector<Metadata *> StackVals;
73 for (auto Id : CallStack) {
74 auto *StackValMD =
75 ValueAsMetadata::get(ConstantInt::get(Type::getInt64Ty(Ctx), Id));
76 StackVals.push_back(StackValMD);
77 }
78 return MDNode::get(Ctx, StackVals);
79}
80
82 assert(MIB->getNumOperands() >= 2);
83 // The stack metadata is the first operand of each memprof MIB metadata.
84 return cast<MDNode>(MIB->getOperand(0));
85}
86
88 assert(MIB->getNumOperands() >= 2);
89 // The allocation type is currently the second operand of each memprof
90 // MIB metadata. This will need to change as we add additional allocation
91 // types that can be applied based on the allocation profile data.
92 auto *MDS = dyn_cast<MDString>(MIB->getOperand(1));
93 assert(MDS);
94 if (MDS->getString() == "cold") {
95 return AllocationType::Cold;
96 } else if (MDS->getString() == "hot") {
97 return AllocationType::Hot;
98 }
99 return AllocationType::NotCold;
100}
101
103 if (MIB->getNumOperands() < 3)
104 return 0;
105 return mdconst::dyn_extract<ConstantInt>(MIB->getOperand(2))->getZExtValue();
106}
107
109 switch (Type) {
110 case AllocationType::NotCold:
111 return "notcold";
112 break;
113 case AllocationType::Cold:
114 return "cold";
115 break;
116 case AllocationType::Hot:
117 return "hot";
118 break;
119 default:
120 assert(false && "Unexpected alloc type");
121 }
122 llvm_unreachable("invalid alloc type");
123}
124
127 auto AllocTypeString = getAllocTypeAttributeString(AllocType);
128 auto A = llvm::Attribute::get(Ctx, "memprof", AllocTypeString);
129 CI->addFnAttr(A);
130}
131
132bool llvm::memprof::hasSingleAllocType(uint8_t AllocTypes) {
133 const unsigned NumAllocTypes = llvm::popcount(AllocTypes);
134 assert(NumAllocTypes != 0);
135 return NumAllocTypes == 1;
136}
137
139 ArrayRef<uint64_t> StackIds,
140 uint64_t TotalSize) {
141 bool First = true;
142 CallStackTrieNode *Curr = nullptr;
143 for (auto StackId : StackIds) {
144 // If this is the first stack frame, add or update alloc node.
145 if (First) {
146 First = false;
147 if (Alloc) {
148 assert(AllocStackId == StackId);
149 Alloc->AllocTypes |= static_cast<uint8_t>(AllocType);
150 Alloc->TotalSize += TotalSize;
151 } else {
152 AllocStackId = StackId;
153 Alloc = new CallStackTrieNode(AllocType, TotalSize);
154 }
155 Curr = Alloc;
156 continue;
157 }
158 // Update existing caller node if it exists.
159 auto Next = Curr->Callers.find(StackId);
160 if (Next != Curr->Callers.end()) {
161 Curr = Next->second;
162 Curr->AllocTypes |= static_cast<uint8_t>(AllocType);
163 Curr->TotalSize += TotalSize;
164 continue;
165 }
166 // Otherwise add a new caller node.
167 auto *New = new CallStackTrieNode(AllocType, TotalSize);
168 Curr->Callers[StackId] = New;
169 Curr = New;
170 }
171 assert(Curr);
172}
173
175 MDNode *StackMD = getMIBStackNode(MIB);
176 assert(StackMD);
177 std::vector<uint64_t> CallStack;
178 CallStack.reserve(StackMD->getNumOperands());
179 for (const auto &MIBStackIter : StackMD->operands()) {
180 auto *StackId = mdconst::dyn_extract<ConstantInt>(MIBStackIter);
181 assert(StackId);
182 CallStack.push_back(StackId->getZExtValue());
183 }
185}
186
188 std::vector<uint64_t> &MIBCallStack,
189 AllocationType AllocType, uint64_t TotalSize) {
190 std::vector<Metadata *> MIBPayload(
191 {buildCallstackMetadata(MIBCallStack, Ctx)});
192 MIBPayload.push_back(
194 if (TotalSize)
195 MIBPayload.push_back(ValueAsMetadata::get(
196 ConstantInt::get(Type::getInt64Ty(Ctx), TotalSize)));
197 return MDNode::get(Ctx, MIBPayload);
198}
199
200// Recursive helper to trim contexts and create metadata nodes.
201// Caller should have pushed Node's loc to MIBCallStack. Doing this in the
202// caller makes it simpler to handle the many early returns in this method.
203bool CallStackTrie::buildMIBNodes(CallStackTrieNode *Node, LLVMContext &Ctx,
204 std::vector<uint64_t> &MIBCallStack,
205 std::vector<Metadata *> &MIBNodes,
206 bool CalleeHasAmbiguousCallerContext) {
207 // Trim context below the first node in a prefix with a single alloc type.
208 // Add an MIB record for the current call stack prefix.
209 if (hasSingleAllocType(Node->AllocTypes)) {
210 MIBNodes.push_back(createMIBNode(
211 Ctx, MIBCallStack, (AllocationType)Node->AllocTypes, Node->TotalSize));
212 return true;
213 }
214
215 // We don't have a single allocation for all the contexts sharing this prefix,
216 // so recursively descend into callers in trie.
217 if (!Node->Callers.empty()) {
218 bool NodeHasAmbiguousCallerContext = Node->Callers.size() > 1;
219 bool AddedMIBNodesForAllCallerContexts = true;
220 for (auto &Caller : Node->Callers) {
221 MIBCallStack.push_back(Caller.first);
222 AddedMIBNodesForAllCallerContexts &=
223 buildMIBNodes(Caller.second, Ctx, MIBCallStack, MIBNodes,
224 NodeHasAmbiguousCallerContext);
225 // Remove Caller.
226 MIBCallStack.pop_back();
227 }
228 if (AddedMIBNodesForAllCallerContexts)
229 return true;
230 // We expect that the callers should be forced to add MIBs to disambiguate
231 // the context in this case (see below).
232 assert(!NodeHasAmbiguousCallerContext);
233 }
234
235 // If we reached here, then this node does not have a single allocation type,
236 // and we didn't add metadata for a longer call stack prefix including any of
237 // Node's callers. That means we never hit a single allocation type along all
238 // call stacks with this prefix. This can happen due to recursion collapsing
239 // or the stack being deeper than tracked by the profiler runtime, leading to
240 // contexts with different allocation types being merged. In that case, we
241 // trim the context just below the deepest context split, which is this
242 // node if the callee has an ambiguous caller context (multiple callers),
243 // since the recursive calls above returned false. Conservatively give it
244 // non-cold allocation type.
245 if (!CalleeHasAmbiguousCallerContext)
246 return false;
247 MIBNodes.push_back(createMIBNode(Ctx, MIBCallStack, AllocationType::NotCold,
248 Node->TotalSize));
249 return true;
250}
251
252// Build and attach the minimal necessary MIB metadata. If the alloc has a
253// single allocation type, add a function attribute instead. Returns true if
254// memprof metadata attached, false if not (attribute added).
256 auto &Ctx = CI->getContext();
257 if (hasSingleAllocType(Alloc->AllocTypes)) {
258 addAllocTypeAttribute(Ctx, CI, (AllocationType)Alloc->AllocTypes);
260 assert(Alloc->TotalSize);
261 errs() << "Total size for allocation with location hash " << AllocStackId
262 << " and single alloc type "
263 << getAllocTypeAttributeString((AllocationType)Alloc->AllocTypes)
264 << ": " << Alloc->TotalSize << "\n";
265 }
266 return false;
267 }
268 std::vector<uint64_t> MIBCallStack;
269 MIBCallStack.push_back(AllocStackId);
270 std::vector<Metadata *> MIBNodes;
271 assert(!Alloc->Callers.empty() && "addCallStack has not been called yet");
272 // The last parameter is meant to say whether the callee of the given node
273 // has more than one caller. Here the node being passed in is the alloc
274 // and it has no callees. So it's false.
275 if (buildMIBNodes(Alloc, Ctx, MIBCallStack, MIBNodes, false)) {
276 assert(MIBCallStack.size() == 1 &&
277 "Should only be left with Alloc's location in stack");
278 CI->setMetadata(LLVMContext::MD_memprof, MDNode::get(Ctx, MIBNodes));
279 return true;
280 }
281 // If there exists corner case that CallStackTrie has one chain to leaf
282 // and all node in the chain have multi alloc type, conservatively give
283 // it non-cold allocation type.
284 // FIXME: Avoid this case before memory profile created.
286 return false;
287}
288
289template <>
291 const MDNode *N, bool End)
292 : N(N) {
293 if (!N)
294 return;
295 Iter = End ? N->op_end() : N->op_begin();
296}
297
298template <>
301 assert(Iter != N->op_end());
302 ConstantInt *StackIdCInt = mdconst::dyn_extract<ConstantInt>(*Iter);
303 assert(StackIdCInt);
304 return StackIdCInt->getZExtValue();
305}
306
308 assert(N);
309 return mdconst::dyn_extract<ConstantInt>(N->operands().back())
310 ->getZExtValue();
311}
static GCRegistry::Add< ErlangGC > A("erlang", "erlang-compatible garbage collector")
This file contains the declarations for the subclasses of Constant, which represent the different fla...
bool End
Definition: ELF_riscv.cpp:480
AllocType
cl::opt< float > MemProfLifetimeAccessDensityColdThreshold("memprof-lifetime-access-density-cold-threshold", cl::init(0.05), cl::Hidden, cl::desc("The threshold the lifetime access density (accesses per byte per " "lifetime sec) must be under to consider an allocation cold"))
cl::opt< unsigned > MemProfMinAveLifetimeAccessDensityHotThreshold("memprof-min-ave-lifetime-access-density-hot-threshold", cl::init(1000), cl::Hidden, cl::desc("The minimum TotalLifetimeAccessDensity / AllocCount for an " "allocation to be considered hot"))
cl::opt< bool > MemProfReportHintedSizes("memprof-report-hinted-sizes", cl::init(false), cl::Hidden, cl::desc("Report total allocation sizes of hinted allocations"))
cl::opt< unsigned > MemProfAveLifetimeColdThreshold("memprof-ave-lifetime-cold-threshold", cl::init(200), cl::Hidden, cl::desc("The average lifetime (s) for an allocation to be considered " "cold"))
static MDNode * createMIBNode(LLVMContext &Ctx, std::vector< uint64_t > &MIBCallStack, AllocationType AllocType, uint64_t TotalSize)
static void addAllocTypeAttribute(LLVMContext &Ctx, CallBase *CI, AllocationType AllocType)
assert(ImpDefSCC.getReg()==AMDGPU::SCC &&ImpDefSCC.isDef())
ArrayRef - Represent a constant reference to an array (0 or more elements consecutively in memory),...
Definition: ArrayRef.h:41
static Attribute get(LLVMContext &Context, AttrKind Kind, uint64_t Val=0)
Return a uniquified Attribute object.
Definition: Attributes.cpp:94
Base class for all callable instructions (InvokeInst and CallInst) Holds everything related to callin...
Definition: InstrTypes.h:1236
void addFnAttr(Attribute::AttrKind Kind)
Adds the attribute to the function.
Definition: InstrTypes.h:1574
This is the shared class of boolean and integer constants.
Definition: Constants.h:81
uint64_t getZExtValue() const
Return the constant as a 64-bit unsigned integer value after it has been zero extended as appropriate...
Definition: Constants.h:155
void setMetadata(unsigned KindID, MDNode *Node)
Set the metadata of the specified kind to the specified node.
Definition: Metadata.cpp:1642
This is an important class for using LLVM in a threaded context.
Definition: LLVMContext.h:67
Metadata node.
Definition: Metadata.h:1069
const MDOperand & getOperand(unsigned I) const
Definition: Metadata.h:1430
ArrayRef< MDOperand > operands() const
Definition: Metadata.h:1428
static MDTuple * get(LLVMContext &Context, ArrayRef< Metadata * > MDs)
Definition: Metadata.h:1542
unsigned getNumOperands() const
Return number of MDNode operands.
Definition: Metadata.h:1436
static MDString * get(LLVMContext &Context, StringRef Str)
Definition: Metadata.cpp:606
The instances of the Type class are immutable: once they are created, they are never changed.
Definition: Type.h:45
static IntegerType * getInt64Ty(LLVMContext &C)
static ValueAsMetadata * get(Value *V)
Definition: Metadata.cpp:501
LLVMContext & getContext() const
All values hold a context through their type.
Definition: Value.cpp:1075
void addCallStack(AllocationType AllocType, ArrayRef< uint64_t > StackIds, uint64_t TotalSize=0)
Add a call stack context with the given allocation type to the Trie.
bool buildAndAttachMIBMetadata(CallBase *CI)
Build and attach the minimal necessary MIB metadata.
Helper class to iterate through stack ids in both metadata (memprof MIB and callsite) and the corresp...
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
initializer< Ty > init(const Ty &Val)
Definition: CommandLine.h:443
MDNode * buildCallstackMetadata(ArrayRef< uint64_t > CallStack, LLVMContext &Ctx)
Build callstack metadata from the provided list of call stack ids.
AllocationType getAllocType(uint64_t TotalLifetimeAccessDensity, uint64_t AllocCount, uint64_t TotalLifetime)
Return the allocation type for a given set of memory profile values.
AllocationType getMIBAllocType(const MDNode *MIB)
Returns the allocation type from an MIB metadata node.
uint64_t getMIBTotalSize(const MDNode *MIB)
Returns the total size from an MIB metadata node, or 0 if it was not recorded.
bool hasSingleAllocType(uint8_t AllocTypes)
True if the AllocTypes bitmask contains just a single type.
std::string getAllocTypeAttributeString(AllocationType Type)
Returns the string to use in attributes with the given type.
MDNode * getMIBStackNode(const MDNode *MIB)
Returns the stack node from an MIB metadata node.
This is an optimization pass for GlobalISel generic memory operations.
Definition: AddressRanges.h:18
int popcount(T Value) noexcept
Count the number of set bits in a value.
Definition: bit.h:385
raw_fd_ostream & errs()
This returns a reference to a raw_ostream for standard error.
@ First
Helpers to iterate all locations in the MemoryEffectsBase class.
#define N