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