xref: /llvm-project/llvm/unittests/Analysis/MemoryProfileInfoTest.cpp (revision ae6d5dd58bcae9ced3b3c5b058876d3564017337)
11dad6247STeresa Johnson //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===//
21dad6247STeresa Johnson //
31dad6247STeresa Johnson // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
41dad6247STeresa Johnson // See https://llvm.org/LICENSE.txt for license information.
51dad6247STeresa Johnson // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
61dad6247STeresa Johnson //
71dad6247STeresa Johnson //===----------------------------------------------------------------------===//
81dad6247STeresa Johnson 
91dad6247STeresa Johnson #include "llvm/Analysis/MemoryProfileInfo.h"
101dad6247STeresa Johnson #include "llvm/AsmParser/Parser.h"
110da2ba81SDaniil Fukalov #include "llvm/IR/Constants.h"
121dad6247STeresa Johnson #include "llvm/IR/Instructions.h"
131dad6247STeresa Johnson #include "llvm/IR/LLVMContext.h"
141dad6247STeresa Johnson #include "llvm/IR/Module.h"
159eacbba2STeresa Johnson #include "llvm/IR/ModuleSummaryIndex.h"
161dad6247STeresa Johnson #include "llvm/Support/CommandLine.h"
171dad6247STeresa Johnson #include "llvm/Support/SourceMgr.h"
18*ae6d5dd5STeresa Johnson #include "gmock/gmock.h"
191dad6247STeresa Johnson #include "gtest/gtest.h"
201dad6247STeresa Johnson #include <cstring>
21b8d2f717SKan Wu #include <sys/types.h>
221dad6247STeresa Johnson 
231dad6247STeresa Johnson using namespace llvm;
241dad6247STeresa Johnson using namespace llvm::memprof;
251dad6247STeresa Johnson 
26a4bdb275STeresa Johnson extern cl::opt<float> MemProfLifetimeAccessDensityColdThreshold;
27a4bdb275STeresa Johnson extern cl::opt<unsigned> MemProfAveLifetimeColdThreshold;
28b8d2f717SKan Wu extern cl::opt<unsigned> MemProfMinAveLifetimeAccessDensityHotThreshold;
29ae8b5608STeresa Johnson extern cl::opt<bool> MemProfUseHotHints;
301dad6247STeresa Johnson 
311dad6247STeresa Johnson namespace {
321dad6247STeresa Johnson 
331dad6247STeresa Johnson class MemoryProfileInfoTest : public testing::Test {
341dad6247STeresa Johnson protected:
351dad6247STeresa Johnson   std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
361dad6247STeresa Johnson     SMDiagnostic Err;
371dad6247STeresa Johnson     std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
381dad6247STeresa Johnson     if (!Mod)
391dad6247STeresa Johnson       Err.print("MemoryProfileInfoTest", errs());
401dad6247STeresa Johnson     return Mod;
411dad6247STeresa Johnson   }
421dad6247STeresa Johnson 
439eacbba2STeresa Johnson   std::unique_ptr<ModuleSummaryIndex> makeLLVMIndex(const char *Summary) {
449eacbba2STeresa Johnson     SMDiagnostic Err;
459eacbba2STeresa Johnson     std::unique_ptr<ModuleSummaryIndex> Index =
469eacbba2STeresa Johnson         parseSummaryIndexAssemblyString(Summary, Err);
479eacbba2STeresa Johnson     if (!Index)
489eacbba2STeresa Johnson       Err.print("MemoryProfileInfoTest", errs());
499eacbba2STeresa Johnson     return Index;
509eacbba2STeresa Johnson   }
519eacbba2STeresa Johnson 
521dad6247STeresa Johnson   // This looks for a call that has the given value name, which
531dad6247STeresa Johnson   // is the name of the value being assigned the call return value.
541dad6247STeresa Johnson   CallBase *findCall(Function &F, const char *Name = nullptr) {
551dad6247STeresa Johnson     for (auto &BB : F)
561dad6247STeresa Johnson       for (auto &I : BB)
571dad6247STeresa Johnson         if (auto *CB = dyn_cast<CallBase>(&I))
581dad6247STeresa Johnson           if (!Name || CB->getName() == Name)
591dad6247STeresa Johnson             return CB;
601dad6247STeresa Johnson     return nullptr;
611dad6247STeresa Johnson   }
621dad6247STeresa Johnson };
631dad6247STeresa Johnson 
641dad6247STeresa Johnson // Test getAllocType helper.
651dad6247STeresa Johnson // Basic checks on the allocation type for values just above and below
661dad6247STeresa Johnson // the thresholds.
671dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, GetAllocType) {
68a4bdb275STeresa Johnson   const uint64_t AllocCount = 2;
69a4bdb275STeresa Johnson   // To be cold we require that
70a4bdb275STeresa Johnson   // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 <
71a4bdb275STeresa Johnson   //    MemProfLifetimeAccessDensityColdThreshold
72b8d2f717SKan Wu   // so compute the ColdTotalLifetimeAccessDensityThreshold at the threshold.
73b8d2f717SKan Wu   const uint64_t ColdTotalLifetimeAccessDensityThreshold =
74a4bdb275STeresa Johnson       (uint64_t)(MemProfLifetimeAccessDensityColdThreshold * AllocCount * 100);
75a4bdb275STeresa Johnson   // To be cold we require that
76a4bdb275STeresa Johnson   // ((float)TotalLifetime) / AllocCount >=
77a4bdb275STeresa Johnson   //    MemProfAveLifetimeColdThreshold * 1000
78a4bdb275STeresa Johnson   // so compute the TotalLifetime right at the threshold.
79b8d2f717SKan Wu   const uint64_t ColdTotalLifetimeThreshold =
80a4bdb275STeresa Johnson       MemProfAveLifetimeColdThreshold * AllocCount * 1000;
81b8d2f717SKan Wu   // To be hot we require that
82b8d2f717SKan Wu   // ((float)TotalLifetimeAccessDensity) / AllocCount / 100 >
83b8d2f717SKan Wu   //    MemProfMinAveLifetimeAccessDensityHotThreshold
84b8d2f717SKan Wu   // so compute the HotTotalLifetimeAccessDensityThreshold  at the threshold.
85b8d2f717SKan Wu   const uint64_t HotTotalLifetimeAccessDensityThreshold =
86ae8b5608STeresa Johnson       (uint64_t)(MemProfMinAveLifetimeAccessDensityHotThreshold * AllocCount *
87ae8b5608STeresa Johnson                  100);
88a4bdb275STeresa Johnson 
89ae8b5608STeresa Johnson   // Make sure the option for detecting hot allocations is set.
90ae8b5608STeresa Johnson   MemProfUseHotHints = true;
91b8d2f717SKan Wu   // Test Hot
92b8d2f717SKan Wu   // More accesses per byte per sec than hot threshold is hot.
93b8d2f717SKan Wu   EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount,
94b8d2f717SKan Wu                          ColdTotalLifetimeThreshold + 1),
95b8d2f717SKan Wu             AllocationType::Hot);
96ae8b5608STeresa Johnson   // Undo the manual set of the option above.
97ae8b5608STeresa Johnson   cl::ResetAllOptionOccurrences();
98ae8b5608STeresa Johnson 
99ae8b5608STeresa Johnson   // Without MemProfUseHotHints (default) we should treat simply as NotCold.
100ae8b5608STeresa Johnson   EXPECT_EQ(getAllocType(HotTotalLifetimeAccessDensityThreshold + 1, AllocCount,
101ae8b5608STeresa Johnson                          ColdTotalLifetimeThreshold + 1),
102ae8b5608STeresa Johnson             AllocationType::NotCold);
103b8d2f717SKan Wu 
104b8d2f717SKan Wu   // Test Cold
105b8d2f717SKan Wu   // Long lived with less accesses per byte per sec than cold threshold is cold.
106b8d2f717SKan Wu   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount,
107b8d2f717SKan Wu                          ColdTotalLifetimeThreshold + 1),
1081dad6247STeresa Johnson             AllocationType::Cold);
109b8d2f717SKan Wu 
110b8d2f717SKan Wu   // Test NotCold
111b8d2f717SKan Wu   // Long lived with more accesses per byte per sec than cold threshold is not cold.
112b8d2f717SKan Wu   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount,
113b8d2f717SKan Wu                          ColdTotalLifetimeThreshold + 1),
1141dad6247STeresa Johnson             AllocationType::NotCold);
115b8d2f717SKan Wu   // Short lived with more accesses per byte per sec than cold threshold is not cold.
116b8d2f717SKan Wu   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold + 1, AllocCount,
117b8d2f717SKan Wu                          ColdTotalLifetimeThreshold - 1),
118b8d2f717SKan Wu             AllocationType::NotCold);
119b8d2f717SKan Wu   // Short lived with less accesses per byte per sec than cold threshold is not cold.
120b8d2f717SKan Wu   EXPECT_EQ(getAllocType(ColdTotalLifetimeAccessDensityThreshold - 1, AllocCount,
121b8d2f717SKan Wu                          ColdTotalLifetimeThreshold - 1),
1221dad6247STeresa Johnson             AllocationType::NotCold);
1231dad6247STeresa Johnson }
1241dad6247STeresa Johnson 
1255fd82ca0STeresa Johnson // Test the hasSingleAllocType helper.
1265fd82ca0STeresa Johnson TEST_F(MemoryProfileInfoTest, SingleAllocType) {
1275fd82ca0STeresa Johnson   uint8_t NotCold = (uint8_t)AllocationType::NotCold;
1285fd82ca0STeresa Johnson   uint8_t Cold = (uint8_t)AllocationType::Cold;
129b8d2f717SKan Wu   uint8_t Hot = (uint8_t)AllocationType::Hot;
1305fd82ca0STeresa Johnson   EXPECT_TRUE(hasSingleAllocType(NotCold));
1315fd82ca0STeresa Johnson   EXPECT_TRUE(hasSingleAllocType(Cold));
132b8d2f717SKan Wu   EXPECT_TRUE(hasSingleAllocType(Hot));
1335fd82ca0STeresa Johnson   EXPECT_FALSE(hasSingleAllocType(NotCold | Cold));
134b8d2f717SKan Wu   EXPECT_FALSE(hasSingleAllocType(NotCold | Hot));
135b8d2f717SKan Wu   EXPECT_FALSE(hasSingleAllocType(Cold | Hot));
136b8d2f717SKan Wu   EXPECT_FALSE(hasSingleAllocType(NotCold | Cold | Hot));
1375fd82ca0STeresa Johnson }
1385fd82ca0STeresa Johnson 
1391dad6247STeresa Johnson // Test buildCallstackMetadata helper.
1401dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, BuildCallStackMD) {
1411dad6247STeresa Johnson   LLVMContext C;
1421dad6247STeresa Johnson   MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C);
1431dad6247STeresa Johnson   ASSERT_EQ(CallStack->getNumOperands(), 3u);
1441dad6247STeresa Johnson   unsigned ExpectedId = 1;
1451dad6247STeresa Johnson   for (auto &Op : CallStack->operands()) {
1461dad6247STeresa Johnson     auto *StackId = mdconst::dyn_extract<ConstantInt>(Op);
1471dad6247STeresa Johnson     EXPECT_EQ(StackId->getZExtValue(), ExpectedId++);
1481dad6247STeresa Johnson   }
1491dad6247STeresa Johnson }
1501dad6247STeresa Johnson 
1511dad6247STeresa Johnson // Test CallStackTrie::addCallStack interface taking allocation type and list of
1521dad6247STeresa Johnson // call stack ids.
1531dad6247STeresa Johnson // Check that allocations with a single allocation type along all call stacks
1541dad6247STeresa Johnson // get an attribute instead of memprof metadata.
1551dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, Attribute) {
1561dad6247STeresa Johnson   LLVMContext C;
1571dad6247STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
1581dad6247STeresa Johnson                                              R"IR(
1591dad6247STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
1601dad6247STeresa Johnson target triple = "x86_64-pc-linux-gnu"
1611dad6247STeresa Johnson define i32* @test() {
1621dad6247STeresa Johnson entry:
1631dad6247STeresa Johnson   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
1641dad6247STeresa Johnson   %0 = bitcast i8* %call1 to i32*
1651dad6247STeresa Johnson   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
1661dad6247STeresa Johnson   %1 = bitcast i8* %call2 to i32*
167b8d2f717SKan Wu   %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
168b8d2f717SKan Wu   %2 = bitcast i8* %call3 to i32*
169c725a95eSTeresa Johnson   %call4 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
170c725a95eSTeresa Johnson   %3 = bitcast i8* %call4 to i32*
1711dad6247STeresa Johnson   ret i32* %1
1721dad6247STeresa Johnson }
1731dad6247STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
1741dad6247STeresa Johnson )IR");
1751dad6247STeresa Johnson 
1761dad6247STeresa Johnson   Function *Func = M->getFunction("test");
1771dad6247STeresa Johnson 
1781dad6247STeresa Johnson   // First call has all cold contexts.
1791dad6247STeresa Johnson   CallStackTrie Trie1;
1801dad6247STeresa Johnson   Trie1.addCallStack(AllocationType::Cold, {1, 2});
1811dad6247STeresa Johnson   Trie1.addCallStack(AllocationType::Cold, {1, 3, 4});
1821dad6247STeresa Johnson   CallBase *Call1 = findCall(*Func, "call1");
1831dad6247STeresa Johnson   Trie1.buildAndAttachMIBMetadata(Call1);
1841dad6247STeresa Johnson 
1851dad6247STeresa Johnson   EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof));
1861dad6247STeresa Johnson   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
1871dad6247STeresa Johnson   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
1881dad6247STeresa Johnson 
1891dad6247STeresa Johnson   // Second call has all non-cold contexts.
1901dad6247STeresa Johnson   CallStackTrie Trie2;
1911dad6247STeresa Johnson   Trie2.addCallStack(AllocationType::NotCold, {5, 6});
1921dad6247STeresa Johnson   Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8});
1931dad6247STeresa Johnson   CallBase *Call2 = findCall(*Func, "call2");
1941dad6247STeresa Johnson   Trie2.buildAndAttachMIBMetadata(Call2);
1951dad6247STeresa Johnson 
1961dad6247STeresa Johnson   EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof));
1971dad6247STeresa Johnson   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
1981dad6247STeresa Johnson   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
199b8d2f717SKan Wu 
200b8d2f717SKan Wu   // Third call has all hot contexts.
201b8d2f717SKan Wu   CallStackTrie Trie3;
202b8d2f717SKan Wu   Trie3.addCallStack(AllocationType::Hot, {9, 10});
203b8d2f717SKan Wu   Trie3.addCallStack(AllocationType::Hot, {9, 11, 12});
204b8d2f717SKan Wu   CallBase *Call3 = findCall(*Func, "call3");
205b8d2f717SKan Wu   Trie3.buildAndAttachMIBMetadata(Call3);
206b8d2f717SKan Wu 
207b8d2f717SKan Wu   EXPECT_FALSE(Call3->hasMetadata(LLVMContext::MD_memprof));
208b8d2f717SKan Wu   EXPECT_TRUE(Call3->hasFnAttr("memprof"));
209b8d2f717SKan Wu   EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot");
210c725a95eSTeresa Johnson 
211c725a95eSTeresa Johnson   // Fourth call has hot and non-cold contexts. These should be treated as
212c725a95eSTeresa Johnson   // notcold and given a notcold attribute.
213c725a95eSTeresa Johnson   CallStackTrie Trie4;
214c725a95eSTeresa Johnson   Trie4.addCallStack(AllocationType::Hot, {5, 6});
215c725a95eSTeresa Johnson   Trie4.addCallStack(AllocationType::NotCold, {5, 7, 8});
216c725a95eSTeresa Johnson   CallBase *Call4 = findCall(*Func, "call4");
217c725a95eSTeresa Johnson   Trie4.buildAndAttachMIBMetadata(Call4);
218c725a95eSTeresa Johnson 
219c725a95eSTeresa Johnson   EXPECT_FALSE(Call4->hasMetadata(LLVMContext::MD_memprof));
220c725a95eSTeresa Johnson   EXPECT_TRUE(Call4->hasFnAttr("memprof"));
221c725a95eSTeresa Johnson   EXPECT_EQ(Call4->getFnAttr("memprof").getValueAsString(), "notcold");
2221dad6247STeresa Johnson }
2231dad6247STeresa Johnson 
224*ae6d5dd5STeresa Johnson // TODO: Use this matcher in existing tests.
225*ae6d5dd5STeresa Johnson // ExpectedVals should be a vector of expected MIBs and their allocation type
226*ae6d5dd5STeresa Johnson // and stack id contents in order, of type:
227*ae6d5dd5STeresa Johnson //  std::vector<std::pair<AllocationType, std::vector<unsigned>>>
228*ae6d5dd5STeresa Johnson MATCHER_P(MemprofMetadataEquals, ExpectedVals, "Matching !memprof contents") {
229*ae6d5dd5STeresa Johnson   auto PrintAndFail = [&]() {
230*ae6d5dd5STeresa Johnson     std::string Buffer;
231*ae6d5dd5STeresa Johnson     llvm::raw_string_ostream OS(Buffer);
232*ae6d5dd5STeresa Johnson     OS << "Expected:\n";
233*ae6d5dd5STeresa Johnson     for (auto &[ExpectedAllocType, ExpectedStackIds] : ExpectedVals) {
234*ae6d5dd5STeresa Johnson       OS << "\t" << getAllocTypeAttributeString(ExpectedAllocType) << " { ";
235*ae6d5dd5STeresa Johnson       for (auto Id : ExpectedStackIds)
236*ae6d5dd5STeresa Johnson         OS << Id << " ";
237*ae6d5dd5STeresa Johnson       OS << "}\n";
238*ae6d5dd5STeresa Johnson     }
239*ae6d5dd5STeresa Johnson     OS << "Got:\n";
240*ae6d5dd5STeresa Johnson     arg->printTree(OS);
241*ae6d5dd5STeresa Johnson     *result_listener << "!memprof metadata differs!\n" << Buffer;
242*ae6d5dd5STeresa Johnson     return false;
243*ae6d5dd5STeresa Johnson   };
244*ae6d5dd5STeresa Johnson 
245*ae6d5dd5STeresa Johnson   if (ExpectedVals.size() != arg->getNumOperands())
246*ae6d5dd5STeresa Johnson     return PrintAndFail();
247*ae6d5dd5STeresa Johnson 
248*ae6d5dd5STeresa Johnson   for (size_t I = 0; I < ExpectedVals.size(); I++) {
249*ae6d5dd5STeresa Johnson     const auto &[ExpectedAllocType, ExpectedStackIds] = ExpectedVals[I];
250*ae6d5dd5STeresa Johnson     MDNode *MIB = dyn_cast<MDNode>(arg->getOperand(I));
251*ae6d5dd5STeresa Johnson     if (getMIBAllocType(MIB) != ExpectedAllocType)
252*ae6d5dd5STeresa Johnson       return PrintAndFail();
253*ae6d5dd5STeresa Johnson     MDNode *StackMD = getMIBStackNode(MIB);
254*ae6d5dd5STeresa Johnson     EXPECT_NE(StackMD, nullptr);
255*ae6d5dd5STeresa Johnson     if (StackMD->getNumOperands() != ExpectedStackIds.size())
256*ae6d5dd5STeresa Johnson       return PrintAndFail();
257*ae6d5dd5STeresa Johnson     for (size_t J = 0; J < ExpectedStackIds.size(); J++) {
258*ae6d5dd5STeresa Johnson       auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(J));
259*ae6d5dd5STeresa Johnson       if (StackId->getZExtValue() != ExpectedStackIds[J])
260*ae6d5dd5STeresa Johnson         return PrintAndFail();
261*ae6d5dd5STeresa Johnson     }
262*ae6d5dd5STeresa Johnson   }
263*ae6d5dd5STeresa Johnson   return true;
264*ae6d5dd5STeresa Johnson }
265*ae6d5dd5STeresa Johnson 
2661dad6247STeresa Johnson // Test CallStackTrie::addCallStack interface taking allocation type and list of
2671dad6247STeresa Johnson // call stack ids.
2681dad6247STeresa Johnson // Test that an allocation call reached by both cold and non cold call stacks
2691dad6247STeresa Johnson // gets memprof metadata representing the different allocation type contexts.
2701dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) {
2711dad6247STeresa Johnson   LLVMContext C;
2721dad6247STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
2731dad6247STeresa Johnson                                              R"IR(
2741dad6247STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
2751dad6247STeresa Johnson target triple = "x86_64-pc-linux-gnu"
2761dad6247STeresa Johnson define i32* @test() {
2771dad6247STeresa Johnson entry:
2781dad6247STeresa Johnson   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
2791dad6247STeresa Johnson   %0 = bitcast i8* %call to i32*
2801dad6247STeresa Johnson   ret i32* %0
2811dad6247STeresa Johnson }
2821dad6247STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
2831dad6247STeresa Johnson )IR");
2841dad6247STeresa Johnson 
2851dad6247STeresa Johnson   Function *Func = M->getFunction("test");
2861dad6247STeresa Johnson 
2871dad6247STeresa Johnson   CallStackTrie Trie;
2881dad6247STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2});
2891dad6247STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 3});
2901dad6247STeresa Johnson 
2911dad6247STeresa Johnson   CallBase *Call = findCall(*Func, "call");
2921dad6247STeresa Johnson   Trie.buildAndAttachMIBMetadata(Call);
2931dad6247STeresa Johnson 
2941dad6247STeresa Johnson   EXPECT_FALSE(Call->hasFnAttr("memprof"));
2951dad6247STeresa Johnson   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
2961dad6247STeresa Johnson   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
2971dad6247STeresa Johnson   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
2981dad6247STeresa Johnson   for (auto &MIBOp : MemProfMD->operands()) {
2991dad6247STeresa Johnson     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
3001dad6247STeresa Johnson     MDNode *StackMD = getMIBStackNode(MIB);
3011dad6247STeresa Johnson     ASSERT_NE(StackMD, nullptr);
3021dad6247STeresa Johnson     ASSERT_EQ(StackMD->getNumOperands(), 2u);
3031dad6247STeresa Johnson     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
3041dad6247STeresa Johnson     ASSERT_EQ(StackId->getZExtValue(), 1u);
3051dad6247STeresa Johnson     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
3061dad6247STeresa Johnson     if (StackId->getZExtValue() == 2u)
3071dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
3081dad6247STeresa Johnson     else {
3091dad6247STeresa Johnson       ASSERT_EQ(StackId->getZExtValue(), 3u);
3101dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
3111dad6247STeresa Johnson     }
3121dad6247STeresa Johnson   }
3131dad6247STeresa Johnson }
3141dad6247STeresa Johnson 
3151dad6247STeresa Johnson // Test CallStackTrie::addCallStack interface taking allocation type and list of
3161dad6247STeresa Johnson // call stack ids.
317b8d2f717SKan Wu // Test that an allocation call reached by both cold and hot call stacks
318b8d2f717SKan Wu // gets memprof metadata representing the different allocation type contexts.
319b8d2f717SKan Wu TEST_F(MemoryProfileInfoTest, ColdAndHotMIB) {
320b8d2f717SKan Wu   LLVMContext C;
321b8d2f717SKan Wu   std::unique_ptr<Module> M = makeLLVMModule(C,
322b8d2f717SKan Wu                                              R"IR(
323b8d2f717SKan Wu target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
324b8d2f717SKan Wu target triple = "x86_64-pc-linux-gnu"
325b8d2f717SKan Wu define i32* @test() {
326b8d2f717SKan Wu entry:
327b8d2f717SKan Wu   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
328b8d2f717SKan Wu   %0 = bitcast i8* %call to i32*
329b8d2f717SKan Wu   ret i32* %0
330b8d2f717SKan Wu }
331b8d2f717SKan Wu declare dso_local noalias noundef i8* @malloc(i64 noundef)
332b8d2f717SKan Wu )IR");
333b8d2f717SKan Wu 
334b8d2f717SKan Wu   Function *Func = M->getFunction("test");
335b8d2f717SKan Wu 
336b8d2f717SKan Wu   CallStackTrie Trie;
337b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Cold, {1, 2});
338b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Hot, {1, 3});
339b8d2f717SKan Wu 
340b8d2f717SKan Wu   CallBase *Call = findCall(*Func, "call");
341b8d2f717SKan Wu   Trie.buildAndAttachMIBMetadata(Call);
342b8d2f717SKan Wu 
343b8d2f717SKan Wu   EXPECT_FALSE(Call->hasFnAttr("memprof"));
344b8d2f717SKan Wu   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
345b8d2f717SKan Wu   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
346b8d2f717SKan Wu   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
347b8d2f717SKan Wu   for (auto &MIBOp : MemProfMD->operands()) {
348b8d2f717SKan Wu     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
349b8d2f717SKan Wu     MDNode *StackMD = getMIBStackNode(MIB);
350b8d2f717SKan Wu     ASSERT_NE(StackMD, nullptr);
351b8d2f717SKan Wu     ASSERT_EQ(StackMD->getNumOperands(), 2u);
352b8d2f717SKan Wu     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
353b8d2f717SKan Wu     ASSERT_EQ(StackId->getZExtValue(), 1u);
354b8d2f717SKan Wu     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
355b8d2f717SKan Wu     if (StackId->getZExtValue() == 2u)
356b8d2f717SKan Wu       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
357b8d2f717SKan Wu     else {
358b8d2f717SKan Wu       ASSERT_EQ(StackId->getZExtValue(), 3u);
359c725a95eSTeresa Johnson       // Hot contexts are converted to NotCold when building the metadata.
360b8d2f717SKan Wu       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
361b8d2f717SKan Wu     }
362b8d2f717SKan Wu   }
363b8d2f717SKan Wu }
364b8d2f717SKan Wu 
365b8d2f717SKan Wu // Test CallStackTrie::addCallStack interface taking allocation type and list of
366b8d2f717SKan Wu // call stack ids.
367b8d2f717SKan Wu // Test that an allocation call reached by both cold, non cold and hot call
368b8d2f717SKan Wu // stacks gets memprof metadata representing the different allocation type
369b8d2f717SKan Wu // contexts.
370b8d2f717SKan Wu TEST_F(MemoryProfileInfoTest, ColdAndNotColdAndHotMIB) {
371b8d2f717SKan Wu   LLVMContext C;
372b8d2f717SKan Wu   std::unique_ptr<Module> M = makeLLVMModule(C,
373b8d2f717SKan Wu                                              R"IR(
374b8d2f717SKan Wu target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
375b8d2f717SKan Wu target triple = "x86_64-pc-linux-gnu"
376b8d2f717SKan Wu define i32* @test() {
377b8d2f717SKan Wu entry:
378b8d2f717SKan Wu   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
379b8d2f717SKan Wu   %0 = bitcast i8* %call to i32*
380b8d2f717SKan Wu   ret i32* %0
381b8d2f717SKan Wu }
382b8d2f717SKan Wu declare dso_local noalias noundef i8* @malloc(i64 noundef)
383b8d2f717SKan Wu )IR");
384b8d2f717SKan Wu 
385b8d2f717SKan Wu   Function *Func = M->getFunction("test");
386b8d2f717SKan Wu 
387b8d2f717SKan Wu   CallStackTrie Trie;
388b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Cold, {1, 2});
389b8d2f717SKan Wu   Trie.addCallStack(AllocationType::NotCold, {1, 3});
390*ae6d5dd5STeresa Johnson   // This will be pruned as it is unnecessary to determine how to clone the
391*ae6d5dd5STeresa Johnson   // cold allocation.
392b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Hot, {1, 4});
393b8d2f717SKan Wu 
394b8d2f717SKan Wu   CallBase *Call = findCall(*Func, "call");
395b8d2f717SKan Wu   Trie.buildAndAttachMIBMetadata(Call);
396b8d2f717SKan Wu 
397b8d2f717SKan Wu   EXPECT_FALSE(Call->hasFnAttr("memprof"));
398b8d2f717SKan Wu   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
399b8d2f717SKan Wu   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
400*ae6d5dd5STeresa Johnson   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
401b8d2f717SKan Wu   for (auto &MIBOp : MemProfMD->operands()) {
402b8d2f717SKan Wu     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
403b8d2f717SKan Wu     MDNode *StackMD = getMIBStackNode(MIB);
404b8d2f717SKan Wu     ASSERT_NE(StackMD, nullptr);
405b8d2f717SKan Wu     ASSERT_EQ(StackMD->getNumOperands(), 2u);
406b8d2f717SKan Wu     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
407b8d2f717SKan Wu     ASSERT_EQ(StackId->getZExtValue(), 1u);
408b8d2f717SKan Wu     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
409b8d2f717SKan Wu     if (StackId->getZExtValue() == 2u) {
410b8d2f717SKan Wu       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
411b8d2f717SKan Wu     } else if (StackId->getZExtValue() == 3u) {
412b8d2f717SKan Wu       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
413b8d2f717SKan Wu     }
414b8d2f717SKan Wu   }
415b8d2f717SKan Wu }
416b8d2f717SKan Wu 
417b8d2f717SKan Wu // Test CallStackTrie::addCallStack interface taking allocation type and list of
418b8d2f717SKan Wu // call stack ids.
4191dad6247STeresa Johnson // Test that an allocation call reached by multiple call stacks has memprof
4201dad6247STeresa Johnson // metadata with the contexts trimmed to the minimum context required to
4211dad6247STeresa Johnson // identify the allocation type.
4221dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) {
4231dad6247STeresa Johnson   LLVMContext C;
4241dad6247STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
4251dad6247STeresa Johnson                                              R"IR(
4261dad6247STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
4271dad6247STeresa Johnson target triple = "x86_64-pc-linux-gnu"
4281dad6247STeresa Johnson define i32* @test() {
4291dad6247STeresa Johnson entry:
4301dad6247STeresa Johnson   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
4311dad6247STeresa Johnson   %0 = bitcast i8* %call to i32*
4321dad6247STeresa Johnson   ret i32* %0
4331dad6247STeresa Johnson }
4341dad6247STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
4351dad6247STeresa Johnson )IR");
4361dad6247STeresa Johnson 
4371dad6247STeresa Johnson   Function *Func = M->getFunction("test");
4381dad6247STeresa Johnson 
4391dad6247STeresa Johnson   CallStackTrie Trie;
4401dad6247STeresa Johnson   // We should be able to trim the following two and combine into a single MIB
4411dad6247STeresa Johnson   // with the cold context {1, 2}.
4421dad6247STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2, 3});
4431dad6247STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2, 4});
4441dad6247STeresa Johnson   // We should be able to trim the following two and combine into a single MIB
4451dad6247STeresa Johnson   // with the non-cold context {1, 5}.
4461dad6247STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 5, 6});
4471dad6247STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 5, 7});
448*ae6d5dd5STeresa Johnson   // These will be pruned as they are unnecessary to determine how to clone the
449*ae6d5dd5STeresa Johnson   // cold allocation.
450b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Hot, {1, 8, 9});
451b8d2f717SKan Wu   Trie.addCallStack(AllocationType::Hot, {1, 8, 10});
4521dad6247STeresa Johnson 
4531dad6247STeresa Johnson   CallBase *Call = findCall(*Func, "call");
4541dad6247STeresa Johnson   Trie.buildAndAttachMIBMetadata(Call);
4551dad6247STeresa Johnson 
4561dad6247STeresa Johnson   EXPECT_FALSE(Call->hasFnAttr("memprof"));
4571dad6247STeresa Johnson   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
4581dad6247STeresa Johnson   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
459*ae6d5dd5STeresa Johnson   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
4601dad6247STeresa Johnson   for (auto &MIBOp : MemProfMD->operands()) {
4611dad6247STeresa Johnson     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
4621dad6247STeresa Johnson     MDNode *StackMD = getMIBStackNode(MIB);
4631dad6247STeresa Johnson     ASSERT_NE(StackMD, nullptr);
4641dad6247STeresa Johnson     ASSERT_EQ(StackMD->getNumOperands(), 2u);
4651dad6247STeresa Johnson     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
4661dad6247STeresa Johnson     EXPECT_EQ(StackId->getZExtValue(), 1u);
4671dad6247STeresa Johnson     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
4681dad6247STeresa Johnson     if (StackId->getZExtValue() == 2u)
4691dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
470b8d2f717SKan Wu     else if (StackId->getZExtValue() == 5u)
4711dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
4721dad6247STeresa Johnson   }
4731dad6247STeresa Johnson }
474*ae6d5dd5STeresa Johnson 
475*ae6d5dd5STeresa Johnson // Test to ensure that we prune NotCold contexts that are unneeded for
476*ae6d5dd5STeresa Johnson // determining where Cold contexts need to be cloned to enable correct hinting.
477*ae6d5dd5STeresa Johnson TEST_F(MemoryProfileInfoTest, PruneUnneededNotColdContexts) {
478*ae6d5dd5STeresa Johnson   LLVMContext C;
479*ae6d5dd5STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
480*ae6d5dd5STeresa Johnson                                              R"IR(
481*ae6d5dd5STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
482*ae6d5dd5STeresa Johnson target triple = "x86_64-pc-linux-gnu"
483*ae6d5dd5STeresa Johnson define i32* @test() {
484*ae6d5dd5STeresa Johnson entry:
485*ae6d5dd5STeresa Johnson   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
486*ae6d5dd5STeresa Johnson   %0 = bitcast i8* %call to i32*
487*ae6d5dd5STeresa Johnson   ret i32* %0
488*ae6d5dd5STeresa Johnson }
489*ae6d5dd5STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
490*ae6d5dd5STeresa Johnson )IR");
491*ae6d5dd5STeresa Johnson 
492*ae6d5dd5STeresa Johnson   Function *Func = M->getFunction("test");
493*ae6d5dd5STeresa Johnson 
494*ae6d5dd5STeresa Johnson   CallStackTrie Trie;
495*ae6d5dd5STeresa Johnson 
496*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 4});
497*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 5, 6, 7});
498*ae6d5dd5STeresa Johnson   // This NotCold context is needed to know where the above two Cold contexts
499*ae6d5dd5STeresa Johnson   // must be cloned from:
500*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 5, 6, 13});
501*ae6d5dd5STeresa Johnson 
502*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::Cold, {1, 2, 3, 8, 9, 10});
503*ae6d5dd5STeresa Johnson   // This NotCold context is needed to know where the above Cold context must be
504*ae6d5dd5STeresa Johnson   // cloned from:
505*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 8, 9, 14});
506*ae6d5dd5STeresa Johnson   // This NotCold context is not needed since the above is sufficient (we pick
507*ae6d5dd5STeresa Johnson   // the first in sorted order).
508*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 8, 9, 15});
509*ae6d5dd5STeresa Johnson 
510*ae6d5dd5STeresa Johnson   // None of these NotCold contexts are needed as the Cold contexts they
511*ae6d5dd5STeresa Johnson   // overlap with are covered by longer overlapping NotCold contexts.
512*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 2, 3, 12});
513*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 2, 11});
514*ae6d5dd5STeresa Johnson   Trie.addCallStack(AllocationType::NotCold, {1, 16});
515*ae6d5dd5STeresa Johnson 
516*ae6d5dd5STeresa Johnson   std::vector<std::pair<AllocationType, std::vector<unsigned>>> ExpectedVals = {
517*ae6d5dd5STeresa Johnson       {AllocationType::Cold, {1, 2, 3, 4}},
518*ae6d5dd5STeresa Johnson       {AllocationType::Cold, {1, 2, 3, 5, 6, 7}},
519*ae6d5dd5STeresa Johnson       {AllocationType::NotCold, {1, 2, 3, 5, 6, 13}},
520*ae6d5dd5STeresa Johnson       {AllocationType::Cold, {1, 2, 3, 8, 9, 10}},
521*ae6d5dd5STeresa Johnson       {AllocationType::NotCold, {1, 2, 3, 8, 9, 14}}};
522*ae6d5dd5STeresa Johnson 
523*ae6d5dd5STeresa Johnson   CallBase *Call = findCall(*Func, "call");
524*ae6d5dd5STeresa Johnson   ASSERT_NE(Call, nullptr);
525*ae6d5dd5STeresa Johnson   Trie.buildAndAttachMIBMetadata(Call);
526*ae6d5dd5STeresa Johnson 
527*ae6d5dd5STeresa Johnson   EXPECT_FALSE(Call->hasFnAttr("memprof"));
528*ae6d5dd5STeresa Johnson   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
529*ae6d5dd5STeresa Johnson   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
530*ae6d5dd5STeresa Johnson   EXPECT_THAT(MemProfMD, MemprofMetadataEquals(ExpectedVals));
5311dad6247STeresa Johnson }
5321dad6247STeresa Johnson 
5331dad6247STeresa Johnson // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
5341dad6247STeresa Johnson // Check that allocations annotated with memprof metadata with a single
5351dad6247STeresa Johnson // allocation type get simplified to an attribute.
5361dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) {
5371dad6247STeresa Johnson   LLVMContext C;
5381dad6247STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
5391dad6247STeresa Johnson                                              R"IR(
5401dad6247STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
5411dad6247STeresa Johnson target triple = "x86_64-pc-linux-gnu"
5421dad6247STeresa Johnson define i32* @test() {
5431dad6247STeresa Johnson entry:
5441dad6247STeresa Johnson   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
5451dad6247STeresa Johnson   %0 = bitcast i8* %call1 to i32*
5461dad6247STeresa Johnson   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3
5471dad6247STeresa Johnson   %1 = bitcast i8* %call2 to i32*
548b8d2f717SKan Wu   %call3 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !6
549b8d2f717SKan Wu   %2 = bitcast i8* %call3 to i32*
5501dad6247STeresa Johnson   ret i32* %1
5511dad6247STeresa Johnson }
5521dad6247STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
5531dad6247STeresa Johnson !0 = !{!1}
5541dad6247STeresa Johnson !1 = !{!2, !"cold"}
5551dad6247STeresa Johnson !2 = !{i64 1, i64 2, i64 3}
5561dad6247STeresa Johnson !3 = !{!4}
5571dad6247STeresa Johnson !4 = !{!5, !"notcold"}
5581dad6247STeresa Johnson !5 = !{i64 4, i64 5, i64 6, i64 7}
559b8d2f717SKan Wu !6 = !{!7}
560b8d2f717SKan Wu !7 = !{!8, !"hot"}
561b8d2f717SKan Wu !8 = !{i64 8, i64 9, i64 10}
5621dad6247STeresa Johnson )IR");
5631dad6247STeresa Johnson 
5641dad6247STeresa Johnson   Function *Func = M->getFunction("test");
5651dad6247STeresa Johnson 
5661dad6247STeresa Johnson   // First call has all cold contexts.
5671dad6247STeresa Johnson   CallStackTrie Trie1;
5681dad6247STeresa Johnson   CallBase *Call1 = findCall(*Func, "call1");
5691dad6247STeresa Johnson   MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof);
5701dad6247STeresa Johnson   ASSERT_EQ(MemProfMD1->getNumOperands(), 1u);
5711dad6247STeresa Johnson   MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0));
5721dad6247STeresa Johnson   Trie1.addCallStack(MIB1);
5731dad6247STeresa Johnson   Trie1.buildAndAttachMIBMetadata(Call1);
5741dad6247STeresa Johnson 
5751dad6247STeresa Johnson   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
5761dad6247STeresa Johnson   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
5771dad6247STeresa Johnson 
5781dad6247STeresa Johnson   // Second call has all non-cold contexts.
5791dad6247STeresa Johnson   CallStackTrie Trie2;
5801dad6247STeresa Johnson   CallBase *Call2 = findCall(*Func, "call2");
5811dad6247STeresa Johnson   MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof);
5821dad6247STeresa Johnson   ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
5831dad6247STeresa Johnson   MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0));
5841dad6247STeresa Johnson   Trie2.addCallStack(MIB2);
5851dad6247STeresa Johnson   Trie2.buildAndAttachMIBMetadata(Call2);
5861dad6247STeresa Johnson 
5871dad6247STeresa Johnson   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
5881dad6247STeresa Johnson   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
589b8d2f717SKan Wu 
590b8d2f717SKan Wu   // Third call has all hot contexts.
591b8d2f717SKan Wu   CallStackTrie Trie3;
592b8d2f717SKan Wu   CallBase *Call3 = findCall(*Func, "call3");
593b8d2f717SKan Wu   MDNode *MemProfMD3 = Call3->getMetadata(LLVMContext::MD_memprof);
594b8d2f717SKan Wu   ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
595b8d2f717SKan Wu   MDNode *MIB3 = dyn_cast<MDNode>(MemProfMD3->getOperand(0));
596b8d2f717SKan Wu   Trie3.addCallStack(MIB3);
597b8d2f717SKan Wu   Trie3.buildAndAttachMIBMetadata(Call3);
598b8d2f717SKan Wu 
599b8d2f717SKan Wu   EXPECT_TRUE(Call3->hasFnAttr("memprof"));
600b8d2f717SKan Wu   EXPECT_EQ(Call3->getFnAttr("memprof").getValueAsString(), "hot");
6011dad6247STeresa Johnson }
6021dad6247STeresa Johnson 
6031dad6247STeresa Johnson // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
6041dad6247STeresa Johnson // Test that allocations annotated with memprof metadata with multiple call
6051dad6247STeresa Johnson // stacks gets new memprof metadata with the contexts trimmed to the minimum
6061dad6247STeresa Johnson // context required to identify the allocation type.
6071dad6247STeresa Johnson TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) {
6081dad6247STeresa Johnson   LLVMContext C;
6091dad6247STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
6101dad6247STeresa Johnson                                              R"IR(
6111dad6247STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
6121dad6247STeresa Johnson target triple = "x86_64-pc-linux-gnu"
6131dad6247STeresa Johnson define i32* @test() {
6141dad6247STeresa Johnson entry:
6151dad6247STeresa Johnson   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
6161dad6247STeresa Johnson   %0 = bitcast i8* %call to i32*
6171dad6247STeresa Johnson   ret i32* %0
6181dad6247STeresa Johnson }
6191dad6247STeresa Johnson declare dso_local noalias noundef i8* @malloc(i64 noundef)
620b8d2f717SKan Wu !0 = !{!1, !3, !5, !7, !9, !11}
6211dad6247STeresa Johnson !1 = !{!2, !"cold"}
6221dad6247STeresa Johnson !2 = !{i64 1, i64 2, i64 3}
6231dad6247STeresa Johnson !3 = !{!4, !"cold"}
6241dad6247STeresa Johnson !4 = !{i64 1, i64 2, i64 4}
6251dad6247STeresa Johnson !5 = !{!6, !"notcold"}
6261dad6247STeresa Johnson !6 = !{i64 1, i64 5, i64 6}
6271dad6247STeresa Johnson !7 = !{!8, !"notcold"}
6281dad6247STeresa Johnson !8 = !{i64 1, i64 5, i64 7}
629b8d2f717SKan Wu !9 = !{!10, !"hot"}
630b8d2f717SKan Wu !10 = !{i64 1, i64 8, i64 9}
631b8d2f717SKan Wu !11 = !{!12, !"hot"}
632b8d2f717SKan Wu !12 = !{i64 1, i64 8, i64 10}
6331dad6247STeresa Johnson )IR");
6341dad6247STeresa Johnson 
6351dad6247STeresa Johnson   Function *Func = M->getFunction("test");
6361dad6247STeresa Johnson 
6371dad6247STeresa Johnson   CallStackTrie Trie;
6381dad6247STeresa Johnson   ASSERT_TRUE(Trie.empty());
6391dad6247STeresa Johnson   CallBase *Call = findCall(*Func, "call");
6401dad6247STeresa Johnson   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
6411dad6247STeresa Johnson   for (auto &MIBOp : MemProfMD->operands()) {
6421dad6247STeresa Johnson     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
6431dad6247STeresa Johnson     Trie.addCallStack(MIB);
6441dad6247STeresa Johnson   }
6451dad6247STeresa Johnson   ASSERT_FALSE(Trie.empty());
6461dad6247STeresa Johnson   Trie.buildAndAttachMIBMetadata(Call);
6471dad6247STeresa Johnson 
6481dad6247STeresa Johnson   // We should be able to trim the first two and combine into a single MIB
6491dad6247STeresa Johnson   // with the cold context {1, 2}.
6501dad6247STeresa Johnson   // We should be able to trim the second two and combine into a single MIB
6511dad6247STeresa Johnson   // with the non-cold context {1, 5}.
652*ae6d5dd5STeresa Johnson   // The hot allocations will be converted to NotCold and pruned as they
653*ae6d5dd5STeresa Johnson   // are unnecessary to determine how to clone the cold allocation.
6541dad6247STeresa Johnson 
6551dad6247STeresa Johnson   EXPECT_FALSE(Call->hasFnAttr("memprof"));
6561dad6247STeresa Johnson   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
6571dad6247STeresa Johnson   MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
658*ae6d5dd5STeresa Johnson   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
6591dad6247STeresa Johnson   for (auto &MIBOp : MemProfMD->operands()) {
6601dad6247STeresa Johnson     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
6611dad6247STeresa Johnson     MDNode *StackMD = getMIBStackNode(MIB);
6621dad6247STeresa Johnson     ASSERT_NE(StackMD, nullptr);
6631dad6247STeresa Johnson     ASSERT_EQ(StackMD->getNumOperands(), 2u);
6641dad6247STeresa Johnson     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
6651dad6247STeresa Johnson     EXPECT_EQ(StackId->getZExtValue(), 1u);
6661dad6247STeresa Johnson     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
6671dad6247STeresa Johnson     if (StackId->getZExtValue() == 2u)
6681dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
669b8d2f717SKan Wu     else if (StackId->getZExtValue() == 5u)
6701dad6247STeresa Johnson       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
6711dad6247STeresa Johnson   }
6721dad6247STeresa Johnson }
6731dad6247STeresa Johnson 
6749eacbba2STeresa Johnson TEST_F(MemoryProfileInfoTest, CallStackTestIR) {
6759eacbba2STeresa Johnson   LLVMContext C;
6769eacbba2STeresa Johnson   std::unique_ptr<Module> M = makeLLVMModule(C,
6779eacbba2STeresa Johnson                                              R"IR(
6789eacbba2STeresa Johnson target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
6799eacbba2STeresa Johnson target triple = "x86_64-pc-linux-gnu"
6809eacbba2STeresa Johnson define ptr @test() {
6819eacbba2STeresa Johnson entry:
6829eacbba2STeresa Johnson   %call = call noalias noundef nonnull dereferenceable(10) ptr @_Znam(i64 noundef 10), !memprof !1, !callsite !6
6839eacbba2STeresa Johnson   ret ptr %call
6849eacbba2STeresa Johnson }
6859eacbba2STeresa Johnson declare noundef nonnull ptr @_Znam(i64 noundef)
686b8d2f717SKan Wu !1 = !{!2, !4, !7}
6879eacbba2STeresa Johnson !2 = !{!3, !"notcold"}
6889eacbba2STeresa Johnson !3 = !{i64 1, i64 2, i64 3, i64 4}
6899eacbba2STeresa Johnson !4 = !{!5, !"cold"}
6909eacbba2STeresa Johnson !5 = !{i64 1, i64 2, i64 3, i64 5}
6919eacbba2STeresa Johnson !6 = !{i64 1}
692b8d2f717SKan Wu !7 = !{!8, !"hot"}
693b8d2f717SKan Wu !8 = !{i64 1, i64 2, i64 3, i64 6}
6949eacbba2STeresa Johnson )IR");
6959eacbba2STeresa Johnson 
6969eacbba2STeresa Johnson   Function *Func = M->getFunction("test");
6979eacbba2STeresa Johnson   CallBase *Call = findCall(*Func, "call");
6989eacbba2STeresa Johnson 
6999eacbba2STeresa Johnson   CallStack<MDNode, MDNode::op_iterator> InstCallsite(
7009eacbba2STeresa Johnson       Call->getMetadata(LLVMContext::MD_callsite));
7019eacbba2STeresa Johnson 
7029eacbba2STeresa Johnson   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
70357d71cbaSTeresa Johnson   unsigned Idx = 0;
7049eacbba2STeresa Johnson   for (auto &MIBOp : MemProfMD->operands()) {
7059eacbba2STeresa Johnson     auto *MIBMD = cast<const MDNode>(MIBOp);
7069eacbba2STeresa Johnson     MDNode *StackNode = getMIBStackNode(MIBMD);
7079eacbba2STeresa Johnson     CallStack<MDNode, MDNode::op_iterator> StackContext(StackNode);
708b8d2f717SKan Wu     EXPECT_EQ(StackContext.back(), 4 + Idx);
7099eacbba2STeresa Johnson     std::vector<uint64_t> StackIds;
7109eacbba2STeresa Johnson     for (auto ContextIter = StackContext.beginAfterSharedPrefix(InstCallsite);
7119eacbba2STeresa Johnson          ContextIter != StackContext.end(); ++ContextIter)
7129eacbba2STeresa Johnson       StackIds.push_back(*ContextIter);
713b8d2f717SKan Wu     if (Idx == 0) {
7149eacbba2STeresa Johnson       std::vector<uint64_t> Expected = {2, 3, 4};
71538818b60Sserge-sans-paille       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
716b8d2f717SKan Wu     } else if (Idx == 1) {
7179eacbba2STeresa Johnson       std::vector<uint64_t> Expected = {2, 3, 5};
71838818b60Sserge-sans-paille       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
719b8d2f717SKan Wu     } else {
720b8d2f717SKan Wu       std::vector<uint64_t> Expected = {2, 3, 6};
721b8d2f717SKan Wu       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
7229eacbba2STeresa Johnson     }
723b8d2f717SKan Wu     Idx++;
7249eacbba2STeresa Johnson   }
7259eacbba2STeresa Johnson }
7269eacbba2STeresa Johnson 
7279eacbba2STeresa Johnson TEST_F(MemoryProfileInfoTest, CallStackTestSummary) {
7289eacbba2STeresa Johnson   std::unique_ptr<ModuleSummaryIndex> Index = makeLLVMIndex(R"Summary(
7299eacbba2STeresa Johnson ^0 = module: (path: "test.o", hash: (0, 0, 0, 0, 0))
730b8d2f717SKan Wu ^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))))))))
731b8d2f717SKan Wu ^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))))))
7329eacbba2STeresa Johnson )Summary");
7339eacbba2STeresa Johnson 
7349eacbba2STeresa Johnson   ASSERT_NE(Index, nullptr);
7359eacbba2STeresa Johnson   auto *CallsiteSummary =
7369eacbba2STeresa Johnson       cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/25));
73757d71cbaSTeresa Johnson   unsigned Idx = 0;
7389eacbba2STeresa Johnson   for (auto &CI : CallsiteSummary->callsites()) {
7399eacbba2STeresa Johnson     CallStack<CallsiteInfo, SmallVector<unsigned>::const_iterator> InstCallsite(
7409eacbba2STeresa Johnson         &CI);
7419eacbba2STeresa Johnson     std::vector<uint64_t> StackIds;
7429eacbba2STeresa Johnson     for (auto StackIdIndex : InstCallsite)
7439eacbba2STeresa Johnson       StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
744b8d2f717SKan Wu     if (Idx == 0) {
7459eacbba2STeresa Johnson       std::vector<uint64_t> Expected = {3, 4};
74638818b60Sserge-sans-paille       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
747b8d2f717SKan Wu     } else if (Idx == 1) {
7489eacbba2STeresa Johnson       std::vector<uint64_t> Expected = {3, 5};
74938818b60Sserge-sans-paille       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
750b8d2f717SKan Wu     } else {
751b8d2f717SKan Wu       std::vector<uint64_t> Expected = {3, 6};
752b8d2f717SKan Wu       EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
7539eacbba2STeresa Johnson     }
754b8d2f717SKan Wu     Idx++;
7559eacbba2STeresa Johnson   }
7569eacbba2STeresa Johnson 
7579eacbba2STeresa Johnson   auto *AllocSummary =
7589eacbba2STeresa Johnson       cast<FunctionSummary>(Index->getGlobalValueSummary(/*guid=*/23));
7599eacbba2STeresa Johnson   for (auto &AI : AllocSummary->allocs()) {
76057d71cbaSTeresa Johnson     unsigned Idx = 0;
7619eacbba2STeresa Johnson     for (auto &MIB : AI.MIBs) {
7629eacbba2STeresa Johnson       CallStack<MIBInfo, SmallVector<unsigned>::const_iterator> StackContext(
7639eacbba2STeresa Johnson           &MIB);
764b8d2f717SKan Wu       EXPECT_EQ(Index->getStackIdAtIndex(StackContext.back()), 4 + Idx);
7659eacbba2STeresa Johnson       std::vector<uint64_t> StackIds;
7669eacbba2STeresa Johnson       for (auto StackIdIndex : StackContext)
7679eacbba2STeresa Johnson         StackIds.push_back(Index->getStackIdAtIndex(StackIdIndex));
768b8d2f717SKan Wu       if (Idx == 0) {
7699eacbba2STeresa Johnson         std::vector<uint64_t> Expected = {1, 2, 3, 4};
77038818b60Sserge-sans-paille         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
771b8d2f717SKan Wu       } else if (Idx == 1) {
7729eacbba2STeresa Johnson         std::vector<uint64_t> Expected = {1, 2, 3, 5};
77338818b60Sserge-sans-paille         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
774b8d2f717SKan Wu       } else {
775b8d2f717SKan Wu         std::vector<uint64_t> Expected = {1, 2, 3, 6};
776b8d2f717SKan Wu         EXPECT_EQ(ArrayRef(StackIds), ArrayRef(Expected));
7779eacbba2STeresa Johnson       }
778b8d2f717SKan Wu       Idx++;
7799eacbba2STeresa Johnson     }
7809eacbba2STeresa Johnson   }
7819eacbba2STeresa Johnson }
7821dad6247STeresa Johnson } // end anonymous namespace
783