xref: /llvm-project/llvm/unittests/Analysis/MemoryProfileInfoTest.cpp (revision ae6d5dd58bcae9ced3b3c5b058876d3564017337)
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 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Analysis/MemoryProfileInfo.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/Constants.h"
12 #include "llvm/IR/Instructions.h"
13 #include "llvm/IR/LLVMContext.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/IR/ModuleSummaryIndex.h"
16 #include "llvm/Support/CommandLine.h"
17 #include "llvm/Support/SourceMgr.h"
18 #include "gmock/gmock.h"
19 #include "gtest/gtest.h"
20 #include <cstring>
21 #include <sys/types.h>
22 
23 using namespace llvm;
24 using namespace llvm::memprof;
25 
26 extern cl::opt<float> MemProfLifetimeAccessDensityColdThreshold;
27 extern cl::opt<unsigned> MemProfAveLifetimeColdThreshold;
28 extern cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold;
29 extern cl::opt<bool> MemProfUseHotHints;
30 
31 namespace {
32 
33 class MemoryProfileInfoTest : public testing::Test {
34 protected:
35   std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
36     SMDiagnostic Err;
37     std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
38     if (!Mod)
39       Err.print("MemoryProfileInfoTest", errs());
40     return Mod;
41   }
42 
43   std::unique_ptr<ModuleSummaryIndex> makeLLVMIndex(const char *Summary) {
44     SMDiagnostic Err;
45     std::unique_ptr<ModuleSummaryIndex> Index =
46         parseSummaryIndexAssemblyString(Summary, Err);
47     if (!Index)
48       Err.print("MemoryProfileInfoTest", errs());
49     return Index;
50   }
51 
52   // This looks for a call that has the given value name, which
53   // is the name of the value being assigned the call return value.
54   CallBase *findCall(Function &F, const char *Name = nullptr) {
55     for (auto &BB : F)
56       for (auto &I : BB)
57         if (auto *CB = dyn_cast<CallBase>(&I))
58           if (!Name || CB->getName() == Name)
59             return CB;
60     return nullptr;
61   }
62 };
63 
64 // Test getAllocType helper.
65 // Basic checks on the allocation type for values just above and below
66 // the thresholds.
67 TEST_F(MemoryProfileInfoTest, GetAllocType) {
68   const uint64_t AllocCount = 2;
69   // To be cold we require that
70   // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 <
71   //    MemProfLifetimeAccessDensityColdThreshold
72   // so compute the ColdTotalLifetimeAccessDensityThreshold at the threshold.
73   const uint64_t ColdTotalLifetimeAccessDensityThreshold =
74       (uint64_t)(MemProfLifetimeAccessDensityColdThreshold * AllocCount * 100);
75   // To be cold we require that
76   // ((float)TotalLifetime) / AllocCount >=
77   //    MemProfAveLifetimeColdThreshold * 1000
78   // so compute the TotalLifetime right at the threshold.
79   const uint64_t ColdTotalLifetimeThreshold =
80       MemProfAveLifetimeColdThreshold * AllocCount * 1000;
81   // To be hot we require that
82   // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 >
83   //    MemProfMinAveLifetimeAccessDensityHotThreshold
84   // so compute the HotTotalLifetimeAccessDensityThreshold  at the threshold.
85   const uint64_t HotTotalLifetimeAccessDensityThreshold =
86       (uint64_t)(MemProfMinAveLifetimeAccessDensityHotThreshold * AllocCount *
87                  100);
88 
89   // Make sure the option for detecting hot allocations is set.
90   MemProfUseHotHints = true;
91   // Test Hot
92   // More accesses per byte per sec than hot threshold is hot.
93   EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount,
94                          ColdTotalLifetimeThreshold + 1),
95             AllocationType::Hot);
96   // Undo the manual set of the option above.
97   cl::ResetAllOptionOccurrences();
98 
99   // Without MemProfUseHotHints (default) we should treat simply as NotCold.
100   EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount,
101                          ColdTotalLifetimeThreshold + 1),
102             AllocationType::NotCold);
103 
104   // Test Cold
105   // Long lived with less accesses per byte per sec than cold threshold is cold.
106   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount,
107                          ColdTotalLifetimeThreshold + 1),
108             AllocationType::Cold);
109 
110   // Test NotCold
111   // Long lived with more accesses per byte per sec than cold threshold is not cold.
112   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount,
113                          ColdTotalLifetimeThreshold + 1),
114             AllocationType::NotCold);
115   // Short lived with more accesses per byte per sec than cold threshold is not cold.
116   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount,
117                          ColdTotalLifetimeThreshold - 1),
118             AllocationType::NotCold);
119   // Short lived with less accesses per byte per sec than cold threshold is not cold.
120   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount,
121                          ColdTotalLifetimeThreshold - 1),
122             AllocationType::NotCold);
123 }
124 
125 // Test the hasSingleAllocType helper.
126 TEST_F(MemoryProfileInfoTest, SingleAllocType) {
127   uint8_t NotCold = (uint8_t)AllocationType::NotCold;
128   uint8_t Cold = (uint8_t)AllocationType::Cold;
129   uint8_t Hot = (uint8_t)AllocationType::Hot;
130   EXPECT_TRUE(hasSingleAllocType(NotCold));
131   EXPECT_TRUE(hasSingleAllocType(Cold));
132   EXPECT_TRUE(hasSingleAllocType(Hot));
133   EXPECT_FALSE(hasSingleAllocType(NotCold | Cold));
134   EXPECT_FALSE(hasSingleAllocType(NotCold | Hot));
135   EXPECT_FALSE(hasSingleAllocType(Cold | Hot));
136   EXPECT_FALSE(hasSingleAllocType(NotCold | Cold | Hot));
137 }
138 
139 // Test buildCallstackMetadata helper.
140 TEST_F(MemoryProfileInfoTest, BuildCallStackMD) {
141   LLVMContext C;
142   MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C);
143   ASSERT_EQ(CallStack->getNumOperands(), 3u);
144   unsigned ExpectedId = 1;
145   for (auto &Op : CallStack->operands()) {
146     auto *StackId = mdconst::dyn_extract<ConstantInt>(Op);
147     EXPECT_EQ(StackId->getZExtValue(), ExpectedId++);
148   }
149 }
150 
151 // Test CallStackTrie::addCallStack interface taking allocation type and list of
152 // call stack ids.
153 // Check that allocations with a single allocation type along all call stacks
154 // get an attribute instead of memprof metadata.
155 TEST_F(MemoryProfileInfoTest, Attribute) {
156   LLVMContext C;
157   std::unique_ptr<Module> M = makeLLVMModule(C,
158                                              R"IR(
159 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
160 target triple = "x86_64-pc-linux-gnu"
161 define i32* @test() {
162 entry:
163   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
164   %0 = bitcast i8* %call1 to i32*
165   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
166   %1 = bitcast i8* %call2 to i32*
167   %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
168   %2 = bitcast i8* %call3 to i32*
169   %call4 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
170   %3 = bitcast i8* %call4 to i32*
171   ret i32* %1
172 }
173 declare dso_local noalias noundef i8* @malloc(i64 noundef)
174 )IR");
175 
176   Function *Func = M->getFunction("test");
177 
178   // First call has all cold contexts.
179   CallStackTrie Trie1;
180   Trie1.addCallStack(AllocationType::Cold, {1, 2});
181   Trie1.addCallStack(AllocationType::Cold, {1, 3, 4});
182   CallBase *Call1 = findCall(*Func, "call1");
183   Trie1.buildAndAttachMIBMetadata(Call1);
184 
185   EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof));
186   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
187   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
188 
189   // Second call has all non-cold contexts.
190   CallStackTrie Trie2;
191   Trie2.addCallStack(AllocationType::NotCold, {5, 6});
192   Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8});
193   CallBase *Call2 = findCall(*Func, "call2");
194   Trie2.buildAndAttachMIBMetadata(Call2);
195 
196   EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof));
197   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
198   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
199 
200   // Third call has all hot contexts.
201   CallStackTrie Trie3;
202   Trie3.addCallStack(AllocationType::Hot, {9, 10});
203   Trie3.addCallStack(AllocationType::Hot, {9, 11, 12});
204   CallBase *Call3 = findCall(*Func, "call3");
205   Trie3.buildAndAttachMIBMetadata(Call3);
206 
207   EXPECT_FALSE(Call3->hasMetadata(LLVMContext::MD_memprof));
208   EXPECT_TRUE(Call3->hasFnAttr("memprof"));
209   EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot");
210 
211   // Fourth call has hot and non-cold contexts. These should be treated as
212   // notcold and given a notcold attribute.
213   CallStackTrie Trie4;
214   Trie4.addCallStack(AllocationType::Hot, {5, 6});
215   Trie4.addCallStack(AllocationType::NotCold, {5, 7, 8});
216   CallBase *Call4 = findCall(*Func, "call4");
217   Trie4.buildAndAttachMIBMetadata(Call4);
218 
219   EXPECT_FALSE(Call4->hasMetadata(LLVMContext::MD_memprof));
220   EXPECT_TRUE(Call4->hasFnAttr("memprof"));
221   EXPECT_EQ(Call4->getFnAttr("memprof").getValueAsString(), "notcold");
222 }
223 
224 // TODO: Use this matcher in existing tests.
225 // ExpectedVals should be a vector of expected MIBs and their allocation type
226 // and stack id contents in order, of type:
227 //  std::vector<std::pair<AllocationType, std::vector<unsigned>>>
228 MATCHER_P(MemprofMetadataEquals, ExpectedVals, "Matching !memprof contents") {
229   auto PrintAndFail = [&]() {
230     std::string Buffer;
231     llvm::raw_string_ostream OS(Buffer);
232     OS << "Expected:\n";
233     for (auto &[ExpectedAllocType, ExpectedStackIds] : ExpectedVals) {
234       OS << "\t" << getAllocTypeAttributeString(ExpectedAllocType) << " { ";
235       for (auto Id : ExpectedStackIds)
236         OS << Id << " ";
237       OS << "}\n";
238     }
239     OS << "Got:\n";
240     arg->printTree(OS);
241     *result_listener << "!memprof metadata differs!\n" << Buffer;
242     return false;
243   };
244 
245   if (ExpectedVals.size() != arg->getNumOperands())
246     return PrintAndFail();
247 
248   for (size_t I = 0; I < ExpectedVals.size(); I++) {
249     const auto &[ExpectedAllocType, ExpectedStackIds] = ExpectedVals[I];
250     MDNode *MIB = dyn_cast<MDNode>(arg->getOperand(I));
251     if (getMIBAllocType(MIB) != ExpectedAllocType)
252       return PrintAndFail();
253     MDNode *StackMD = getMIBStackNode(MIB);
254     EXPECT_NE(StackMD, nullptr);
255     if (StackMD->getNumOperands() != ExpectedStackIds.size())
256       return PrintAndFail();
257     for (size_t J = 0; J < ExpectedStackIds.size(); J++) {
258       auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(J));
259       if (StackId->getZExtValue() != ExpectedStackIds[J])
260         return PrintAndFail();
261     }
262   }
263   return true;
264 }
265 
266 // Test CallStackTrie::addCallStack interface taking allocation type and list of
267 // call stack ids.
268 // Test that an allocation call reached by both cold and non cold call stacks
269 // gets memprof metadata representing the different allocation type contexts.
270 TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) {
271   LLVMContext C;
272   std::unique_ptr<Module> M = makeLLVMModule(C,
273                                              R"IR(
274 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
275 target triple = "x86_64-pc-linux-gnu"
276 define i32* @test() {
277 entry:
278   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
279   %0 = bitcast i8* %call to i32*
280   ret i32* %0
281 }
282 declare dso_local noalias noundef i8* @malloc(i64 noundef)
283 )IR");
284 
285   Function *Func = M->getFunction("test");
286 
287   CallStackTrie Trie;
288   Trie.addCallStack(AllocationType::Cold, {1, 2});
289   Trie.addCallStack(AllocationType::NotCold, {1, 3});
290 
291   CallBase *Call = findCall(*Func, "call");
292   Trie.buildAndAttachMIBMetadata(Call);
293 
294   EXPECT_FALSE(Call->hasFnAttr("memprof"));
295   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
296   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
297   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
298   for (auto &MIBOp : MemProfMD->operands()) {
299     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
300     MDNode *StackMD = getMIBStackNode(MIB);
301     ASSERT_NE(StackMD, nullptr);
302     ASSERT_EQ(StackMD->getNumOperands(), 2u);
303     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
304     ASSERT_EQ(StackId->getZExtValue(), 1u);
305     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
306     if (StackId->getZExtValue() == 2u)
307       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
308     else {
309       ASSERT_EQ(StackId->getZExtValue(), 3u);
310       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
311     }
312   }
313 }
314 
315 // Test CallStackTrie::addCallStack interface taking allocation type and list of
316 // call stack ids.
317 // Test that an allocation call reached by both cold and hot call stacks
318 // gets memprof metadata representing the different allocation type contexts.
319 TEST_F(MemoryProfileInfoTest, ColdAndHotMIB) {
320   LLVMContext C;
321   std::unique_ptr<Module> M = makeLLVMModule(C,
322                                              R"IR(
323 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
324 target triple = "x86_64-pc-linux-gnu"
325 define i32* @test() {
326 entry:
327   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
328   %0 = bitcast i8* %call to i32*
329   ret i32* %0
330 }
331 declare dso_local noalias noundef i8* @malloc(i64 noundef)
332 )IR");
333 
334   Function *Func = M->getFunction("test");
335 
336   CallStackTrie Trie;
337   Trie.addCallStack(AllocationType::Cold, {1, 2});
338   Trie.addCallStack(AllocationType::Hot, {1, 3});
339 
340   CallBase *Call = findCall(*Func, "call");
341   Trie.buildAndAttachMIBMetadata(Call);
342 
343   EXPECT_FALSE(Call->hasFnAttr("memprof"));
344   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
345   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
346   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
347   for (auto &MIBOp : MemProfMD->operands()) {
348     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
349     MDNode *StackMD = getMIBStackNode(MIB);
350     ASSERT_NE(StackMD, nullptr);
351     ASSERT_EQ(StackMD->getNumOperands(), 2u);
352     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
353     ASSERT_EQ(StackId->getZExtValue(), 1u);
354     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
355     if (StackId->getZExtValue() == 2u)
356       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
357     else {
358       ASSERT_EQ(StackId->getZExtValue(), 3u);
359       // Hot contexts are converted to NotCold when building the metadata.
360       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
361     }
362   }
363 }
364 
365 // Test CallStackTrie::addCallStack interface taking allocation type and list of
366 // call stack ids.
367 // Test that an allocation call reached by both cold, non cold and hot call
368 // stacks gets memprof metadata representing the different allocation type
369 // contexts.
370 TEST_F(MemoryProfileInfoTest, ColdAndNotColdAndHotMIB) {
371   LLVMContext C;
372   std::unique_ptr<Module> M = makeLLVMModule(C,
373                                              R"IR(
374 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
375 target triple = "x86_64-pc-linux-gnu"
376 define i32* @test() {
377 entry:
378   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
379   %0 = bitcast i8* %call to i32*
380   ret i32* %0
381 }
382 declare dso_local noalias noundef i8* @malloc(i64 noundef)
383 )IR");
384 
385   Function *Func = M->getFunction("test");
386 
387   CallStackTrie Trie;
388   Trie.addCallStack(AllocationType::Cold, {1, 2});
389   Trie.addCallStack(AllocationType::NotCold, {1, 3});
390   // This will be pruned as it is unnecessary to determine how to clone the
391   // cold allocation.
392   Trie.addCallStack(AllocationType::Hot, {1, 4});
393 
394   CallBase *Call = findCall(*Func, "call");
395   Trie.buildAndAttachMIBMetadata(Call);
396 
397   EXPECT_FALSE(Call->hasFnAttr("memprof"));
398   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
399   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
400   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
401   for (auto &MIBOp : MemProfMD->operands()) {
402     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
403     MDNode *StackMD = getMIBStackNode(MIB);
404     ASSERT_NE(StackMD, nullptr);
405     ASSERT_EQ(StackMD->getNumOperands(), 2u);
406     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
407     ASSERT_EQ(StackId->getZExtValue(), 1u);
408     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
409     if (StackId->getZExtValue() == 2u) {
410       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
411     } else if (StackId->getZExtValue() == 3u) {
412       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
413     }
414   }
415 }
416 
417 // Test CallStackTrie::addCallStack interface taking allocation type and list of
418 // call stack ids.
419 // Test that an allocation call reached by multiple call stacks has memprof
420 // metadata with the contexts trimmed to the minimum context required to
421 // identify the allocation type.
422 TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) {
423   LLVMContext C;
424   std::unique_ptr<Module> M = makeLLVMModule(C,
425                                              R"IR(
426 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
427 target triple = "x86_64-pc-linux-gnu"
428 define i32* @test() {
429 entry:
430   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
431   %0 = bitcast i8* %call to i32*
432   ret i32* %0
433 }
434 declare dso_local noalias noundef i8* @malloc(i64 noundef)
435 )IR");
436 
437   Function *Func = M->getFunction("test");
438 
439   CallStackTrie Trie;
440   // We should be able to trim the following two and combine into a single MIB
441   // with the cold context {1, 2}.
442   Trie.addCallStack(AllocationType::Cold, {1, 2, 3});
443   Trie.addCallStack(AllocationType::Cold, {1, 2, 4});
444   // We should be able to trim the following two and combine into a single MIB
445   // with the non-cold context {1, 5}.
446   Trie.addCallStack(AllocationType::NotCold, {1, 5, 6});
447   Trie.addCallStack(AllocationType::NotCold, {1, 5, 7});
448   // These will be pruned as they are unnecessary to determine how to clone the
449   // cold allocation.
450   Trie.addCallStack(AllocationType::Hot, {1, 8, 9});
451   Trie.addCallStack(AllocationType::Hot, {1, 8, 10});
452 
453   CallBase *Call = findCall(*Func, "call");
454   Trie.buildAndAttachMIBMetadata(Call);
455 
456   EXPECT_FALSE(Call->hasFnAttr("memprof"));
457   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
458   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
459   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
460   for (auto &MIBOp : MemProfMD->operands()) {
461     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
462     MDNode *StackMD = getMIBStackNode(MIB);
463     ASSERT_NE(StackMD, nullptr);
464     ASSERT_EQ(StackMD->getNumOperands(), 2u);
465     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
466     EXPECT_EQ(StackId->getZExtValue(), 1u);
467     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
468     if (StackId->getZExtValue() == 2u)
469       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
470     else if (StackId->getZExtValue() == 5u)
471       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
472   }
473 }
474 
475 // Test to ensure that we prune NotCold contexts that are unneeded for
476 // determining where Cold contexts need to be cloned to enable correct hinting.
477 TEST_F(MemoryProfileInfoTest, PruneUnneededNotColdContexts) {
478   LLVMContext C;
479   std::unique_ptr<Module> M = makeLLVMModule(C,
480                                              R"IR(
481 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
482 target triple = "x86_64-pc-linux-gnu"
483 define i32* @test() {
484 entry:
485   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
486   %0 = bitcast i8* %call to i32*
487   ret i32* %0
488 }
489 declare dso_local noalias noundef i8* @malloc(i64 noundef)
490 )IR");
491 
492   Function *Func = M->getFunction("test");
493 
494   CallStackTrie Trie;
495 
496   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 4});
497   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 5, 6, 7});
498   // This NotCold context is needed to know where the above two Cold contexts
499   // must be cloned from:
500   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 5, 6, 13});
501 
502   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 8, 9, 10});
503   // This NotCold context is needed to know where the above Cold context must be
504   // cloned from:
505   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 8, 9, 14});
506   // This NotCold context is not needed since the above is sufficient (we pick
507   // the first in sorted order).
508   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 8, 9, 15});
509 
510   // None of these NotCold contexts are needed as the Cold contexts they
511   // overlap with are covered by longer overlapping NotCold contexts.
512   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 12});
513   Trie.addCallStack(AllocationType::NotCold, {1, 2, 11});
514   Trie.addCallStack(AllocationType::NotCold, {1, 16});
515 
516   std::vector<std::pair<AllocationType, std::vector<unsigned>>> ExpectedVals = {
517       {AllocationType::Cold, {1, 2, 3, 4}},
518       {AllocationType::Cold, {1, 2, 3, 5, 6, 7}},
519       {AllocationType::NotCold, {1, 2, 3, 5, 6, 13}},
520       {AllocationType::Cold, {1, 2, 3, 8, 9, 10}},
521       {AllocationType::NotCold, {1, 2, 3, 8, 9, 14}}};
522 
523   CallBase *Call = findCall(*Func, "call");
524   ASSERT_NE(Call, nullptr);
525   Trie.buildAndAttachMIBMetadata(Call);
526 
527   EXPECT_FALSE(Call->hasFnAttr("memprof"));
528   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
529   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
530   EXPECT_THAT(MemProfMD, MemprofMetadataEquals(ExpectedVals));
531 }
532 
533 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
534 // Check that allocations annotated with memprof metadata with a single
535 // allocation type get simplified to an attribute.
536 TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) {
537   LLVMContext C;
538   std::unique_ptr<Module> M = makeLLVMModule(C,
539                                              R"IR(
540 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
541 target triple = "x86_64-pc-linux-gnu"
542 define i32* @test() {
543 entry:
544   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
545   %0 = bitcast i8* %call1 to i32*
546   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3
547   %1 = bitcast i8* %call2 to i32*
548   %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !6
549   %2 = bitcast i8* %call3 to i32*
550   ret i32* %1
551 }
552 declare dso_local noalias noundef i8* @malloc(i64 noundef)
553 !0 = !{!1}
554 !1 = !{!2, !"cold"}
555 !2 = !{i64 1, i64 2, i64 3}
556 !3 = !{!4}
557 !4 = !{!5, !"notcold"}
558 !5 = !{i64 4, i64 5, i64 6, i64 7}
559 !6 = !{!7}
560 !7 = !{!8, !"hot"}
561 !8 = !{i64 8, i64 9, i64 10}
562 )IR");
563 
564   Function *Func = M->getFunction("test");
565 
566   // First call has all cold contexts.
567   CallStackTrie Trie1;
568   CallBase *Call1 = findCall(*Func, "call1");
569   MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof);
570   ASSERT_EQ(MemProfMD1->getNumOperands(), 1u);
571   MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0));
572   Trie1.addCallStack(MIB1);
573   Trie1.buildAndAttachMIBMetadata(Call1);
574 
575   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
576   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
577 
578   // Second call has all non-cold contexts.
579   CallStackTrie Trie2;
580   CallBase *Call2 = findCall(*Func, "call2");
581   MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof);
582   ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
583   MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0));
584   Trie2.addCallStack(MIB2);
585   Trie2.buildAndAttachMIBMetadata(Call2);
586 
587   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
588   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
589 
590   // Third call has all hot contexts.
591   CallStackTrie Trie3;
592   CallBase *Call3 = findCall(*Func, "call3");
593   MDNode *MemProfMD3 = Call3->getMetadata(LLVMContext::MD_memprof);
594   ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
595   MDNode *MIB3 = dyn_cast<MDNode>(MemProfMD3->getOperand(0));
596   Trie3.addCallStack(MIB3);
597   Trie3.buildAndAttachMIBMetadata(Call3);
598 
599   EXPECT_TRUE(Call3->hasFnAttr("memprof"));
600   EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot");
601 }
602 
603 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
604 // Test that allocations annotated with memprof metadata with multiple call
605 // stacks gets new memprof metadata with the contexts trimmed to the minimum
606 // context required to identify the allocation type.
607 TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) {
608   LLVMContext C;
609   std::unique_ptr<Module> M = makeLLVMModule(C,
610                                              R"IR(
611 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
612 target triple = "x86_64-pc-linux-gnu"
613 define i32* @test() {
614 entry:
615   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
616   %0 = bitcast i8* %call to i32*
617   ret i32* %0
618 }
619 declare dso_local noalias noundef i8* @malloc(i64 noundef)
620 !0 = !{!1, !3, !5, !7, !9, !11}
621 !1 = !{!2, !"cold"}
622 !2 = !{i64 1, i64 2, i64 3}
623 !3 = !{!4, !"cold"}
624 !4 = !{i64 1, i64 2, i64 4}
625 !5 = !{!6, !"notcold"}
626 !6 = !{i64 1, i64 5, i64 6}
627 !7 = !{!8, !"notcold"}
628 !8 = !{i64 1, i64 5, i64 7}
629 !9 = !{!10, !"hot"}
630 !10 = !{i64 1, i64 8, i64 9}
631 !11 = !{!12, !"hot"}
632 !12 = !{i64 1, i64 8, i64 10}
633 )IR");
634 
635   Function *Func = M->getFunction("test");
636 
637   CallStackTrie Trie;
638   ASSERT_TRUE(Trie.empty());
639   CallBase *Call = findCall(*Func, "call");
640   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
641   for (auto &MIBOp : MemProfMD->operands()) {
642     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
643     Trie.addCallStack(MIB);
644   }
645   ASSERT_FALSE(Trie.empty());
646   Trie.buildAndAttachMIBMetadata(Call);
647 
648   // We should be able to trim the first two and combine into a single MIB
649   // with the cold context {1, 2}.
650   // We should be able to trim the second two and combine into a single MIB
651   // with the non-cold context {1, 5}.
652   // The hot allocations will be converted to NotCold and pruned as they
653   // are unnecessary to determine how to clone the cold allocation.
654 
655   EXPECT_FALSE(Call->hasFnAttr("memprof"));
656   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
657   MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
658   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
659   for (auto &MIBOp : MemProfMD->operands()) {
660     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
661     MDNode *StackMD = getMIBStackNode(MIB);
662     ASSERT_NE(StackMD, nullptr);
663     ASSERT_EQ(StackMD->getNumOperands(), 2u);
664     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
665     EXPECT_EQ(StackId->getZExtValue(), 1u);
666     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
667     if (StackId->getZExtValue() == 2u)
668       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
669     else if (StackId->getZExtValue() == 5u)
670       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
671   }
672 }
673 
674 TEST_F(MemoryProfileInfoTest, CallStackTestIR) {
675   LLVMContext C;
676   std::unique_ptr<Module> M = makeLLVMModule(C,
677                                              R"IR(
678 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
679 target triple = "x86_64-pc-linux-gnu"
680 define ptr @test() {
681 entry:
682   %call = call noalias noundef nonnull dereferenceable(10) ptr @_Znam(i64 noundef 10), !memprof !1, !callsite !6
683   ret ptr %call
684 }
685 declare noundef nonnull ptr @_Znam(i64 noundef)
686 !1 = !{!2, !4, !7}
687 !2 = !{!3, !"notcold"}
688 !3 = !{i64 1, i64 2, i64 3, i64 4}
689 !4 = !{!5, !"cold"}
690 !5 = !{i64 1, i64 2, i64 3, i64 5}
691 !6 = !{i64 1}
692 !7 = !{!8, !"hot"}
693 !8 = !{i64 1, i64 2, i64 3, i64 6}
694 )IR");
695 
696   Function *Func = M->getFunction("test");
697   CallBase *Call = findCall(*Func, "call");
698 
699   CallStack<MDNode, MDNode::op_iterator> InstCallsite(
700       Call->getMetadata(LLVMContext::MD_callsite));
701 
702   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
703   unsigned Idx = 0;
704   for (auto &MIBOp : MemProfMD->operands()) {
705     auto *MIBMD = cast<const MDNode>(MIBOp);
706     MDNode *StackNode = getMIBStackNode(MIBMD);
707     CallStack<MDNode, MDNode::op_iterator> StackContext(StackNode);
708     EXPECT_EQ(StackContext.back(), 4 + Idx);
709     std::vector<uint64_t> StackIds;
710     for (auto ContextIter = StackContext.beginAfterSharedPrefix(InstCallsite);
711          ContextIter != StackContext.end(); ++ContextIter)
712       StackIds.push_back(*ContextIter);
713     if (Idx == 0) {
714       std::vector<uint64_t> Expected = {2, 3, 4};
715       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
716     } else if (Idx == 1) {
717       std::vector<uint64_t> Expected = {2, 3, 5};
718       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
719     } else {
720       std::vector<uint64_t> Expected = {2, 3, 6};
721       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
722     }
723     Idx++;
724   }
725 }
726 
727 TEST_F(MemoryProfileInfoTest, CallStackTestSummary) {
728   std::unique_ptr<ModuleSummaryIndex> Index = makeLLVMIndex(R"Summary(
729 ^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
730 ^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)), (type: hot, stackIds: (1, 2, 3, 6))))))))
731 ^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)), (callee: ^1, clones: (0), stackIds: (3, 6))))))
732 )Summary");
733 
734   ASSERT_NE(Index, nullptr);
735   auto *CallsiteSummary =
736       cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/25));
737   unsigned Idx = 0;
738   for (auto &CI : CallsiteSummary->callsites()) {
739     CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator> InstCallsite(
740         &CI);
741     std::vector<uint64_t> StackIds;
742     for (auto StackIdIndex : InstCallsite)
743       StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
744     if (Idx == 0) {
745       std::vector<uint64_t> Expected = {3, 4};
746       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
747     } else if (Idx == 1) {
748       std::vector<uint64_t> Expected = {3, 5};
749       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
750     } else {
751       std::vector<uint64_t> Expected = {3, 6};
752       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
753     }
754     Idx++;
755   }
756 
757   auto *AllocSummary =
758       cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/23));
759   for (auto &AI : AllocSummary->allocs()) {
760     unsigned Idx = 0;
761     for (auto &MIB : AI.MIBs) {
762       CallStack<MIBInfo, SmallVector<unsigned>::const_iterator> StackContext(
763           &MIB);
764       EXPECT_EQ(Index->getStackIdAtIndex(StackContext.back()), 4 + Idx);
765       std::vector<uint64_t> StackIds;
766       for (auto StackIdIndex : StackContext)
767         StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
768       if (Idx == 0) {
769         std::vector<uint64_t> Expected = {1, 2, 3, 4};
770         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
771       } else if (Idx == 1) {
772         std::vector<uint64_t> Expected = {1, 2, 3, 5};
773         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
774       } else {
775         std::vector<uint64_t> Expected = {1, 2, 3, 6};
776         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
777       }
778       Idx++;
779     }
780   }
781 }
782 } // end anonymous namespace
783