xref: /llvm-project/llvm/unittests/Analysis/MemoryProfileInfoTest.cpp (revision 5d938eb6f79b16f55266dd23d5df831f552ea082)
1 //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "llvm/Analysis/MemoryProfileInfo.h"
10 #include "llvm/AsmParser/Parser.h"
11 #include "llvm/IR/Instructions.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/Module.h"
14 #include "llvm/Support/CommandLine.h"
15 #include "llvm/Support/SourceMgr.h"
16 #include "gtest/gtest.h"
17 #include <cstring>
18 
19 using namespace llvm;
20 using namespace llvm::memprof;
21 
22 extern cl::opt<float> MemProfAccessesPerByteColdThreshold;
23 extern cl::opt<unsigned> MemProfMinLifetimeColdThreshold;
24 
25 namespace {
26 
27 class MemoryProfileInfoTest : public testing::Test {
28 protected:
29   std::unique_ptr<Module> makeLLVMModule(LLVMContext &C, const char *IR) {
30     SMDiagnostic Err;
31     std::unique_ptr<Module> Mod = parseAssemblyString(IR, Err, C);
32     if (!Mod)
33       Err.print("MemoryProfileInfoTest", errs());
34     return Mod;
35   }
36 
37   // This looks for a call that has the given value name, which
38   // is the name of the value being assigned the call return value.
39   CallBase *findCall(Function &F, const char *Name = nullptr) {
40     for (auto &BB : F)
41       for (auto &I : BB)
42         if (auto *CB = dyn_cast<CallBase>(&I))
43           if (!Name || CB->getName() == Name)
44             return CB;
45     return nullptr;
46   }
47 };
48 
49 // Test getAllocType helper.
50 // Basic checks on the allocation type for values just above and below
51 // the thresholds.
52 TEST_F(MemoryProfileInfoTest, GetAllocType) {
53   // Long lived with more accesses per byte than threshold is not cold.
54   EXPECT_EQ(
55       getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,
56                    /*MinSize=*/1,
57                    /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),
58       AllocationType::NotCold);
59   // Long lived with less accesses per byte than threshold is cold.
60   EXPECT_EQ(
61       getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,
62                    /*MinSize=*/1,
63                    /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 + 1),
64       AllocationType::Cold);
65   // Short lived with more accesses per byte than threshold is not cold.
66   EXPECT_EQ(
67       getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold + 1,
68                    /*MinSize=*/1,
69                    /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),
70       AllocationType::NotCold);
71   // Short lived with less accesses per byte than threshold is not cold.
72   EXPECT_EQ(
73       getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold - 1,
74                    /*MinSize=*/1,
75                    /*MinLifetime=*/MemProfMinLifetimeColdThreshold * 1000 - 1),
76       AllocationType::NotCold);
77 }
78 
79 // Test buildCallstackMetadata helper.
80 TEST_F(MemoryProfileInfoTest, BuildCallStackMD) {
81   LLVMContext C;
82   MDNode *CallStack = buildCallstackMetadata({1, 2, 3}, C);
83   ASSERT_EQ(CallStack->getNumOperands(), 3u);
84   unsigned ExpectedId = 1;
85   for (auto &Op : CallStack->operands()) {
86     auto *StackId = mdconst::dyn_extract<ConstantInt>(Op);
87     EXPECT_EQ(StackId->getZExtValue(), ExpectedId++);
88   }
89 }
90 
91 // Test CallStackTrie::addCallStack interface taking allocation type and list of
92 // call stack ids.
93 // Check that allocations with a single allocation type along all call stacks
94 // get an attribute instead of memprof metadata.
95 TEST_F(MemoryProfileInfoTest, Attribute) {
96   LLVMContext C;
97   std::unique_ptr<Module> M = makeLLVMModule(C,
98                                              R"IR(
99 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
100 target triple = "x86_64-pc-linux-gnu"
101 define i32* @test() {
102 entry:
103   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
104   %0 = bitcast i8* %call1 to i32*
105   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
106   %1 = bitcast i8* %call2 to i32*
107   ret i32* %1
108 }
109 declare dso_local noalias noundef i8* @malloc(i64 noundef)
110 )IR");
111 
112   Function *Func = M->getFunction("test");
113 
114   // First call has all cold contexts.
115   CallStackTrie Trie1;
116   Trie1.addCallStack(AllocationType::Cold, {1, 2});
117   Trie1.addCallStack(AllocationType::Cold, {1, 3, 4});
118   CallBase *Call1 = findCall(*Func, "call1");
119   Trie1.buildAndAttachMIBMetadata(Call1);
120 
121   EXPECT_FALSE(Call1->hasMetadata(LLVMContext::MD_memprof));
122   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
123   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
124 
125   // Second call has all non-cold contexts.
126   CallStackTrie Trie2;
127   Trie2.addCallStack(AllocationType::NotCold, {5, 6});
128   Trie2.addCallStack(AllocationType::NotCold, {5, 7, 8});
129   CallBase *Call2 = findCall(*Func, "call2");
130   Trie2.buildAndAttachMIBMetadata(Call2);
131 
132   EXPECT_FALSE(Call2->hasMetadata(LLVMContext::MD_memprof));
133   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
134   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
135 }
136 
137 // Test CallStackTrie::addCallStack interface taking allocation type and list of
138 // call stack ids.
139 // Test that an allocation call reached by both cold and non cold call stacks
140 // gets memprof metadata representing the different allocation type contexts.
141 TEST_F(MemoryProfileInfoTest, ColdAndNotColdMIB) {
142   LLVMContext C;
143   std::unique_ptr<Module> M = makeLLVMModule(C,
144                                              R"IR(
145 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
146 target triple = "x86_64-pc-linux-gnu"
147 define i32* @test() {
148 entry:
149   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
150   %0 = bitcast i8* %call to i32*
151   ret i32* %0
152 }
153 declare dso_local noalias noundef i8* @malloc(i64 noundef)
154 )IR");
155 
156   Function *Func = M->getFunction("test");
157 
158   CallStackTrie Trie;
159   Trie.addCallStack(AllocationType::Cold, {1, 2});
160   Trie.addCallStack(AllocationType::NotCold, {1, 3});
161 
162   CallBase *Call = findCall(*Func, "call");
163   Trie.buildAndAttachMIBMetadata(Call);
164 
165   EXPECT_FALSE(Call->hasFnAttr("memprof"));
166   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
167   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
168   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
169   for (auto &MIBOp : MemProfMD->operands()) {
170     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
171     MDNode *StackMD = getMIBStackNode(MIB);
172     ASSERT_NE(StackMD, nullptr);
173     ASSERT_EQ(StackMD->getNumOperands(), 2u);
174     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
175     ASSERT_EQ(StackId->getZExtValue(), 1u);
176     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
177     if (StackId->getZExtValue() == 2u)
178       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
179     else {
180       ASSERT_EQ(StackId->getZExtValue(), 3u);
181       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
182     }
183   }
184 }
185 
186 // Test CallStackTrie::addCallStack interface taking allocation type and list of
187 // call stack ids.
188 // Test that an allocation call reached by multiple call stacks has memprof
189 // metadata with the contexts trimmed to the minimum context required to
190 // identify the allocation type.
191 TEST_F(MemoryProfileInfoTest, TrimmedMIBContext) {
192   LLVMContext C;
193   std::unique_ptr<Module> M = makeLLVMModule(C,
194                                              R"IR(
195 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
196 target triple = "x86_64-pc-linux-gnu"
197 define i32* @test() {
198 entry:
199   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
200   %0 = bitcast i8* %call to i32*
201   ret i32* %0
202 }
203 declare dso_local noalias noundef i8* @malloc(i64 noundef)
204 )IR");
205 
206   Function *Func = M->getFunction("test");
207 
208   CallStackTrie Trie;
209   // We should be able to trim the following two and combine into a single MIB
210   // with the cold context {1, 2}.
211   Trie.addCallStack(AllocationType::Cold, {1, 2, 3});
212   Trie.addCallStack(AllocationType::Cold, {1, 2, 4});
213   // We should be able to trim the following two and combine into a single MIB
214   // with the non-cold context {1, 5}.
215   Trie.addCallStack(AllocationType::NotCold, {1, 5, 6});
216   Trie.addCallStack(AllocationType::NotCold, {1, 5, 7});
217 
218   CallBase *Call = findCall(*Func, "call");
219   Trie.buildAndAttachMIBMetadata(Call);
220 
221   EXPECT_FALSE(Call->hasFnAttr("memprof"));
222   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
223   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
224   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
225   for (auto &MIBOp : MemProfMD->operands()) {
226     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
227     MDNode *StackMD = getMIBStackNode(MIB);
228     ASSERT_NE(StackMD, nullptr);
229     ASSERT_EQ(StackMD->getNumOperands(), 2u);
230     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
231     EXPECT_EQ(StackId->getZExtValue(), 1u);
232     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
233     if (StackId->getZExtValue() == 2u)
234       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
235     else {
236       ASSERT_EQ(StackId->getZExtValue(), 5u);
237       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
238     }
239   }
240 }
241 
242 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
243 // Check that allocations annotated with memprof metadata with a single
244 // allocation type get simplified to an attribute.
245 TEST_F(MemoryProfileInfoTest, SimplifyMIBToAttribute) {
246   LLVMContext C;
247   std::unique_ptr<Module> M = makeLLVMModule(C,
248                                              R"IR(
249 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
250 target triple = "x86_64-pc-linux-gnu"
251 define i32* @test() {
252 entry:
253   %call1 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
254   %0 = bitcast i8* %call1 to i32*
255   %call2 = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !3
256   %1 = bitcast i8* %call2 to i32*
257   ret i32* %1
258 }
259 declare dso_local noalias noundef i8* @malloc(i64 noundef)
260 !0 = !{!1}
261 !1 = !{!2, !"cold"}
262 !2 = !{i64 1, i64 2, i64 3}
263 !3 = !{!4}
264 !4 = !{!5, !"notcold"}
265 !5 = !{i64 4, i64 5, i64 6, i64 7}
266 )IR");
267 
268   Function *Func = M->getFunction("test");
269 
270   // First call has all cold contexts.
271   CallStackTrie Trie1;
272   CallBase *Call1 = findCall(*Func, "call1");
273   MDNode *MemProfMD1 = Call1->getMetadata(LLVMContext::MD_memprof);
274   ASSERT_EQ(MemProfMD1->getNumOperands(), 1u);
275   MDNode *MIB1 = dyn_cast<MDNode>(MemProfMD1->getOperand(0));
276   Trie1.addCallStack(MIB1);
277   Trie1.buildAndAttachMIBMetadata(Call1);
278 
279   EXPECT_TRUE(Call1->hasFnAttr("memprof"));
280   EXPECT_EQ(Call1->getFnAttr("memprof").getValueAsString(), "cold");
281 
282   // Second call has all non-cold contexts.
283   CallStackTrie Trie2;
284   CallBase *Call2 = findCall(*Func, "call2");
285   MDNode *MemProfMD2 = Call2->getMetadata(LLVMContext::MD_memprof);
286   ASSERT_EQ(MemProfMD2->getNumOperands(), 1u);
287   MDNode *MIB2 = dyn_cast<MDNode>(MemProfMD2->getOperand(0));
288   Trie2.addCallStack(MIB2);
289   Trie2.buildAndAttachMIBMetadata(Call2);
290 
291   EXPECT_TRUE(Call2->hasFnAttr("memprof"));
292   EXPECT_EQ(Call2->getFnAttr("memprof").getValueAsString(), "notcold");
293 }
294 
295 // Test CallStackTrie::addCallStack interface taking memprof MIB metadata.
296 // Test that allocations annotated with memprof metadata with multiple call
297 // stacks gets new memprof metadata with the contexts trimmed to the minimum
298 // context required to identify the allocation type.
299 TEST_F(MemoryProfileInfoTest, ReTrimMIBContext) {
300   LLVMContext C;
301   std::unique_ptr<Module> M = makeLLVMModule(C,
302                                              R"IR(
303 target datalayout = "e-m:e-i64:64-f80:128-n8:16:32:64-S128"
304 target triple = "x86_64-pc-linux-gnu"
305 define i32* @test() {
306 entry:
307   %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
308   %0 = bitcast i8* %call to i32*
309   ret i32* %0
310 }
311 declare dso_local noalias noundef i8* @malloc(i64 noundef)
312 !0 = !{!1, !3, !5, !7}
313 !1 = !{!2, !"cold"}
314 !2 = !{i64 1, i64 2, i64 3}
315 !3 = !{!4, !"cold"}
316 !4 = !{i64 1, i64 2, i64 4}
317 !5 = !{!6, !"notcold"}
318 !6 = !{i64 1, i64 5, i64 6}
319 !7 = !{!8, !"notcold"}
320 !8 = !{i64 1, i64 5, i64 7}
321 )IR");
322 
323   Function *Func = M->getFunction("test");
324 
325   CallStackTrie Trie;
326   ASSERT_TRUE(Trie.empty());
327   CallBase *Call = findCall(*Func, "call");
328   MDNode *MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
329   for (auto &MIBOp : MemProfMD->operands()) {
330     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
331     Trie.addCallStack(MIB);
332   }
333   ASSERT_FALSE(Trie.empty());
334   Trie.buildAndAttachMIBMetadata(Call);
335 
336   // We should be able to trim the first two and combine into a single MIB
337   // with the cold context {1, 2}.
338   // We should be able to trim the second two and combine into a single MIB
339   // with the non-cold context {1, 5}.
340 
341   EXPECT_FALSE(Call->hasFnAttr("memprof"));
342   EXPECT_TRUE(Call->hasMetadata(LLVMContext::MD_memprof));
343   MemProfMD = Call->getMetadata(LLVMContext::MD_memprof);
344   ASSERT_EQ(MemProfMD->getNumOperands(), 2u);
345   for (auto &MIBOp : MemProfMD->operands()) {
346     MDNode *MIB = dyn_cast<MDNode>(MIBOp);
347     MDNode *StackMD = getMIBStackNode(MIB);
348     ASSERT_NE(StackMD, nullptr);
349     ASSERT_EQ(StackMD->getNumOperands(), 2u);
350     auto *StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(0));
351     EXPECT_EQ(StackId->getZExtValue(), 1u);
352     StackId = mdconst::dyn_extract<ConstantInt>(StackMD->getOperand(1));
353     if (StackId->getZExtValue() == 2u)
354       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::Cold);
355     else {
356       ASSERT_EQ(StackId->getZExtValue(), 5u);
357       EXPECT_EQ(getMIBAllocType(MIB), AllocationType::NotCold);
358     }
359   }
360 }
361 
362 } // end anonymous namespace
363