[clang][ExtractAPI] Refactor ExtractAPIVisitor to make it more extensible
[llvm-project.git] / llvm / unittests / Analysis / MemoryProfileInfoTest.cpp
blobff07666ef8325d5cff53d980a300258c2aa813bf
1 //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===//
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 //===----------------------------------------------------------------------===//
9 #include "llvm/Analysis/MemoryProfileInfo.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/Instructions.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/IR/ModuleSummaryIndex.h"
15 #include "llvm/Support/CommandLine.h"
16 #include "llvm/Support/SourceMgr.h"
17 #include "gtest/gtest.h"
18 #include <cstring>
20 using namespace llvm;
21 using namespace llvm::memprof;
23 extern cl::opt<float> MemProfAccessesPerByteColdThreshold;
24 extern cl::opt<unsigned> MemProfMinLifetimeColdThreshold;
26 namespace {
28 class MemoryProfileInfoTest : public testing::Test {
29 protected:
30 std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
31 SMDiagnostic Err;
32 std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
33 if (!Mod)
34 Err.print("MemoryProfileInfoTest", errs());
35 return Mod;
38 std::unique_ptr<ModuleSummaryIndex> makeLLVMIndex(const char *Summary) {
39 SMDiagnostic Err;
40 std::unique_ptr<ModuleSummaryIndex> Index =
41 parseSummaryIndexAssemblyString(Summary, Err);
42 if (!Index)
43 Err.print("MemoryProfileInfoTest", errs());
44 return Index;
47 // This looks for a call that has the given value name, which
48 // is the name of the value being assigned the call return value.
49 CallBase *findCall(Function &F, const char *Name = nullptr) {
50 for (auto &BB : F)
51 for (auto &I : BB)
52 if (auto *CB = dyn_cast<CallBase>(&I))
53 if (!Name || CB->getName() == Name)
54 return CB;
55 return nullptr;
59 // Test getAllocType helper.
60 // Basic checks on the allocation type for values just above and below
61 // the thresholds.
62 TEST_F(MemoryProfileInfoTest, GetAllocType) {
63 // Long lived with more accesses per byte than threshold is not cold.
64 EXPECT_EQ(
65 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,
66 /*MinSize=*/1,
67 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),
68 AllocationType::NotCold);
69 // Long lived with less accesses per byte than threshold is cold.
70 EXPECT_EQ(
71 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,
72 /*MinSize=*/1,
73 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),
74 AllocationType::Cold);
75 // Short lived with more accesses per byte than threshold is not cold.
76 EXPECT_EQ(
77 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,
78 /*MinSize=*/1,
79 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),
80 AllocationType::NotCold);
81 // Short lived with less accesses per byte than threshold is not cold.
82 EXPECT_EQ(
83 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,
84 /*MinSize=*/1,
85 /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),
86 AllocationType::NotCold);
89 // Test the hasSingleAllocType helper.
90 TEST_F(MemoryProfileInfoTest, SingleAllocType) {
91 uint8_t NotCold = (uint8_t)AllocationType::NotCold;
92 uint8_t Cold = (uint8_t)AllocationType::Cold;
93 EXPECT_TRUE(hasSingleAllocType(NotCold));
94 EXPECT_TRUE(hasSingleAllocType(Cold));
95 EXPECT_FALSE(hasSingleAllocType(NotCold | Cold));
98 // Test buildCallstackMetadata helper.
99 TEST_F(MemoryProfileInfoTest, BuildCallStackMD) {
100 LLVMContext C;
101 MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C);
102 ASSERT_EQ(CallStack->getNumOperands(), 3u);
103 unsigned ExpectedId = 1;
104 for (auto &Op : CallStack->operands()) {
105 auto *StackId = mdconst::dyn_extract<ConstantInt>(Op);
106 EXPECT_EQ(StackId->getZExtValue(), ExpectedId++);
110 // Test CallStackTrie::addCallStack interface taking allocation type and list of
111 // call stack ids.
112 // Check that allocations with a single allocation type along all call stacks
113 // get an attribute instead of memprof metadata.
114 TEST_F(MemoryProfileInfoTest, Attribute) {
115 LLVMContext C;
116 std::unique_ptr<Module> M = makeLLVMModule(C,
117 R"IR(
118 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
119 target triple = "x86_64-pc-linux-gnu"
120 define i32* @test() {
121 entry:
122 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
123 %0 = bitcast i8* %call1 to i32*
124 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
125 %1 = bitcast i8* %call2 to i32*
126 ret i32* %1
128 declare dso_local noalias noundef i8* @malloc(i64 noundef)
129 )IR");
131 Function *Func = M->getFunction("test");
133 // First call has all cold contexts.
134 CallStackTrie Trie1;
135 Trie1.addCallStack(AllocationType::Cold, {1, 2});
136 Trie1.addCallStack(AllocationType::Cold, {1, 3, 4});
137 CallBase *Call1 = findCall(*Func, "call1");
138 Trie1.buildAndAttachMIBMetadata(Call1);
140 EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof));
141 EXPECT_TRUE(Call1->hasFnAttr("memprof"));
142 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
144 // Second call has all non-cold contexts.
145 CallStackTrie Trie2;
146 Trie2.addCallStack(AllocationType::NotCold, {5, 6});
147 Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8});
148 CallBase *Call2 = findCall(*Func, "call2");
149 Trie2.buildAndAttachMIBMetadata(Call2);
151 EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof));
152 EXPECT_TRUE(Call2->hasFnAttr("memprof"));
153 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
156 // Test CallStackTrie::addCallStack interface taking allocation type and list of
157 // call stack ids.
158 // Test that an allocation call reached by both cold and non cold call stacks
159 // gets memprof metadata representing the different allocation type contexts.
160 TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) {
161 LLVMContext C;
162 std::unique_ptr<Module> M = makeLLVMModule(C,
163 R"IR(
164 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
165 target triple = "x86_64-pc-linux-gnu"
166 define i32* @test() {
167 entry:
168 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
169 %0 = bitcast i8* %call to i32*
170 ret i32* %0
172 declare dso_local noalias noundef i8* @malloc(i64 noundef)
173 )IR");
175 Function *Func = M->getFunction("test");
177 CallStackTrie Trie;
178 Trie.addCallStack(AllocationType::Cold, {1, 2});
179 Trie.addCallStack(AllocationType::NotCold, {1, 3});
181 CallBase *Call = findCall(*Func, "call");
182 Trie.buildAndAttachMIBMetadata(Call);
184 EXPECT_FALSE(Call->hasFnAttr("memprof"));
185 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
186 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
187 ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
188 for (auto &MIBOp : MemProfMD->operands()) {
189 MDNode *MIB = dyn_cast<MDNode>(MIBOp);
190 MDNode *StackMD = getMIBStackNode(MIB);
191 ASSERT_NE(StackMD, nullptr);
192 ASSERT_EQ(StackMD->getNumOperands(), 2u);
193 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
194 ASSERT_EQ(StackId->getZExtValue(), 1u);
195 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
196 if (StackId->getZExtValue() == 2u)
197 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
198 else {
199 ASSERT_EQ(StackId->getZExtValue(), 3u);
200 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
205 // Test CallStackTrie::addCallStack interface taking allocation type and list of
206 // call stack ids.
207 // Test that an allocation call reached by multiple call stacks has memprof
208 // metadata with the contexts trimmed to the minimum context required to
209 // identify the allocation type.
210 TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) {
211 LLVMContext C;
212 std::unique_ptr<Module> M = makeLLVMModule(C,
213 R"IR(
214 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
215 target triple = "x86_64-pc-linux-gnu"
216 define i32* @test() {
217 entry:
218 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
219 %0 = bitcast i8* %call to i32*
220 ret i32* %0
222 declare dso_local noalias noundef i8* @malloc(i64 noundef)
223 )IR");
225 Function *Func = M->getFunction("test");
227 CallStackTrie Trie;
228 // We should be able to trim the following two and combine into a single MIB
229 // with the cold context {1, 2}.
230 Trie.addCallStack(AllocationType::Cold, {1, 2, 3});
231 Trie.addCallStack(AllocationType::Cold, {1, 2, 4});
232 // We should be able to trim the following two and combine into a single MIB
233 // with the non-cold context {1, 5}.
234 Trie.addCallStack(AllocationType::NotCold, {1, 5, 6});
235 Trie.addCallStack(AllocationType::NotCold, {1, 5, 7});
237 CallBase *Call = findCall(*Func, "call");
238 Trie.buildAndAttachMIBMetadata(Call);
240 EXPECT_FALSE(Call->hasFnAttr("memprof"));
241 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
242 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
243 ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
244 for (auto &MIBOp : MemProfMD->operands()) {
245 MDNode *MIB = dyn_cast<MDNode>(MIBOp);
246 MDNode *StackMD = getMIBStackNode(MIB);
247 ASSERT_NE(StackMD, nullptr);
248 ASSERT_EQ(StackMD->getNumOperands(), 2u);
249 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
250 EXPECT_EQ(StackId->getZExtValue(), 1u);
251 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
252 if (StackId->getZExtValue() == 2u)
253 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
254 else {
255 ASSERT_EQ(StackId->getZExtValue(), 5u);
256 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
261 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
262 // Check that allocations annotated with memprof metadata with a single
263 // allocation type get simplified to an attribute.
264 TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) {
265 LLVMContext C;
266 std::unique_ptr<Module> M = makeLLVMModule(C,
267 R"IR(
268 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
269 target triple = "x86_64-pc-linux-gnu"
270 define i32* @test() {
271 entry:
272 %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
273 %0 = bitcast i8* %call1 to i32*
274 %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3
275 %1 = bitcast i8* %call2 to i32*
276 ret i32* %1
278 declare dso_local noalias noundef i8* @malloc(i64 noundef)
279 !0 = !{!1}
280 !1 = !{!2, !"cold"}
281 !2 = !{i64 1, i64 2, i64 3}
282 !3 = !{!4}
283 !4 = !{!5, !"notcold"}
284 !5 = !{i64 4, i64 5, i64 6, i64 7}
285 )IR");
287 Function *Func = M->getFunction("test");
289 // First call has all cold contexts.
290 CallStackTrie Trie1;
291 CallBase *Call1 = findCall(*Func, "call1");
292 MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof);
293 ASSERT_EQ(MemProfMD1->getNumOperands(), 1u);
294 MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0));
295 Trie1.addCallStack(MIB1);
296 Trie1.buildAndAttachMIBMetadata(Call1);
298 EXPECT_TRUE(Call1->hasFnAttr("memprof"));
299 EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
301 // Second call has all non-cold contexts.
302 CallStackTrie Trie2;
303 CallBase *Call2 = findCall(*Func, "call2");
304 MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof);
305 ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
306 MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0));
307 Trie2.addCallStack(MIB2);
308 Trie2.buildAndAttachMIBMetadata(Call2);
310 EXPECT_TRUE(Call2->hasFnAttr("memprof"));
311 EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
314 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
315 // Test that allocations annotated with memprof metadata with multiple call
316 // stacks gets new memprof metadata with the contexts trimmed to the minimum
317 // context required to identify the allocation type.
318 TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) {
319 LLVMContext C;
320 std::unique_ptr<Module> M = makeLLVMModule(C,
321 R"IR(
322 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
323 target triple = "x86_64-pc-linux-gnu"
324 define i32* @test() {
325 entry:
326 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
327 %0 = bitcast i8* %call to i32*
328 ret i32* %0
330 declare dso_local noalias noundef i8* @malloc(i64 noundef)
331 !0 = !{!1, !3, !5, !7}
332 !1 = !{!2, !"cold"}
333 !2 = !{i64 1, i64 2, i64 3}
334 !3 = !{!4, !"cold"}
335 !4 = !{i64 1, i64 2, i64 4}
336 !5 = !{!6, !"notcold"}
337 !6 = !{i64 1, i64 5, i64 6}
338 !7 = !{!8, !"notcold"}
339 !8 = !{i64 1, i64 5, i64 7}
340 )IR");
342 Function *Func = M->getFunction("test");
344 CallStackTrie Trie;
345 ASSERT_TRUE(Trie.empty());
346 CallBase *Call = findCall(*Func, "call");
347 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
348 for (auto &MIBOp : MemProfMD->operands()) {
349 MDNode *MIB = dyn_cast<MDNode>(MIBOp);
350 Trie.addCallStack(MIB);
352 ASSERT_FALSE(Trie.empty());
353 Trie.buildAndAttachMIBMetadata(Call);
355 // We should be able to trim the first two and combine into a single MIB
356 // with the cold context {1, 2}.
357 // We should be able to trim the second two and combine into a single MIB
358 // with the non-cold context {1, 5}.
360 EXPECT_FALSE(Call->hasFnAttr("memprof"));
361 EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
362 MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
363 ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
364 for (auto &MIBOp : MemProfMD->operands()) {
365 MDNode *MIB = dyn_cast<MDNode>(MIBOp);
366 MDNode *StackMD = getMIBStackNode(MIB);
367 ASSERT_NE(StackMD, nullptr);
368 ASSERT_EQ(StackMD->getNumOperands(), 2u);
369 auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
370 EXPECT_EQ(StackId->getZExtValue(), 1u);
371 StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
372 if (StackId->getZExtValue() == 2u)
373 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
374 else {
375 ASSERT_EQ(StackId->getZExtValue(), 5u);
376 EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
381 TEST_F(MemoryProfileInfoTest, CallStackTestIR) {
382 LLVMContext C;
383 std::unique_ptr<Module> M = makeLLVMModule(C,
384 R"IR(
385 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
386 target triple = "x86_64-pc-linux-gnu"
387 define ptr @test() {
388 entry:
389 %call = call noalias noundef nonnull dereferenceable(10) ptr @_Znam(i64 noundef 10), !memprof !1, !callsite !6
390 ret ptr %call
392 declare noundef nonnull ptr @_Znam(i64 noundef)
393 !1 = !{!2, !4}
394 !2 = !{!3, !"notcold"}
395 !3 = !{i64 1, i64 2, i64 3, i64 4}
396 !4 = !{!5, !"cold"}
397 !5 = !{i64 1, i64 2, i64 3, i64 5}
398 !6 = !{i64 1}
399 )IR");
401 Function *Func = M->getFunction("test");
402 CallBase *Call = findCall(*Func, "call");
404 CallStack<MDNode, MDNode::op_iterator> InstCallsite(
405 Call->getMetadata(LLVMContext::MD_callsite));
407 MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
408 bool First = true;
409 for (auto &MIBOp : MemProfMD->operands()) {
410 auto *MIBMD = cast<const MDNode>(MIBOp);
411 MDNode *StackNode = getMIBStackNode(MIBMD);
412 CallStack<MDNode, MDNode::op_iterator> StackContext(StackNode);
413 uint64_t ExpectedBack = First ? 4 : 5;
414 EXPECT_EQ(StackContext.back(), ExpectedBack);
415 std::vector<uint64_t> StackIds;
416 for (auto ContextIter = StackContext.beginAfterSharedPrefix(InstCallsite);
417 ContextIter != StackContext.end(); ++ContextIter)
418 StackIds.push_back(*ContextIter);
419 if (First) {
420 std::vector<uint64_t> Expected = {2, 3, 4};
421 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
422 } else {
423 std::vector<uint64_t> Expected = {2, 3, 5};
424 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
426 First = false;
430 TEST_F(MemoryProfileInfoTest, CallStackTestSummary) {
431 std::unique_ptr<ModuleSummaryIndex> Index = makeLLVMIndex(R"Summary(
432 ^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
433 ^1 = gv: (guid: 23, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 2, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 0, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), allocs: ((versions: (none), memProf: ((type: notcold, stackIds: (1, 2, 3, 4)), (type: cold, stackIds: (1, 2, 3, 5))))))))
434 ^2 = gv: (guid: 25, summaries: (function: (module: ^0, flags: (linkage: external, visibility: default, notEligibleToImport: 0, live: 0, dsoLocal: 1, canAutoHide: 0), insts: 22, funcFlags: (readNone: 0, readOnly: 0, noRecurse: 1, returnDoesNotAlias: 0, noInline: 1, alwaysInline: 0, noUnwind: 0, mayThrow: 0, hasUnknownCall: 0, mustBeUnreachable: 0), calls: ((callee: ^1)), callsites: ((callee: ^1, clones: (0), stackIds: (3, 4)), (callee: ^1, clones: (0), stackIds: (3, 5))))))
435 )Summary");
437 ASSERT_NE(Index, nullptr);
438 auto *CallsiteSummary =
439 cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/25));
440 bool First = true;
441 for (auto &CI : CallsiteSummary->callsites()) {
442 CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator> InstCallsite(
443 &CI);
444 std::vector<uint64_t> StackIds;
445 for (auto StackIdIndex : InstCallsite)
446 StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
447 if (First) {
448 std::vector<uint64_t> Expected = {3, 4};
449 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
450 } else {
451 std::vector<uint64_t> Expected = {3, 5};
452 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
454 First = false;
457 auto *AllocSummary =
458 cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/23));
459 for (auto &AI : AllocSummary->allocs()) {
460 bool First = true;
461 for (auto &MIB : AI.MIBs) {
462 CallStack<MIBInfo, SmallVector<unsigned>::const_iterator> StackContext(
463 &MIB);
464 uint64_t ExpectedBack = First ? 4 : 5;
465 EXPECT_EQ(Index->getStackIdAtIndex(StackContext.back()), ExpectedBack);
466 std::vector<uint64_t> StackIds;
467 for (auto StackIdIndex : StackContext)
468 StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
469 if (First) {
470 std::vector<uint64_t> Expected = {1, 2, 3, 4};
471 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
472 } else {
473 std::vector<uint64_t> Expected = {1, 2, 3, 5};
474 EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
476 First = false;
480 } // end anonymous namespace