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