1 //===- MemoryProfileInfoTest.cpp - Memory Profile Info Unit Tests-===//
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
7 //===----------------------------------------------------------------------===//
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"
20 using namespace llvm::memprof
;
22 extern cl::opt
<float> MemProfAccessesPerByteColdThreshold
;
23 extern cl::opt
<unsigned> MemProfMinLifetimeColdThreshold
;
27 class MemoryProfileInfoTest
: public testing::Test
{
29 std::unique_ptr
<Module
> makeLLVMModule(LLVMContext
&C
, const char *IR
) {
31 std::unique_ptr
<Module
> Mod
= parseAssemblyString(IR
, Err
, C
);
33 Err
.print("MemoryProfileInfoTest", errs());
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) {
42 if (auto *CB
= dyn_cast
<CallBase
>(&I
))
43 if (!Name
|| CB
->getName() == Name
)
49 // Test getAllocType helper.
50 // Basic checks on the allocation type for values just above and below
52 TEST_F(MemoryProfileInfoTest
, GetAllocType
) {
53 // Long lived with more accesses per byte than threshold is not cold.
55 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
+ 1,
57 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 + 1),
58 AllocationType::NotCold
);
59 // Long lived with less accesses per byte than threshold is cold.
61 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
- 1,
63 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 + 1),
64 AllocationType::Cold
);
65 // Short lived with more accesses per byte than threshold is not cold.
67 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
+ 1,
69 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 - 1),
70 AllocationType::NotCold
);
71 // Short lived with less accesses per byte than threshold is not cold.
73 getAllocType(/*MaxAccessCount=*/MemProfAccessesPerByteColdThreshold
- 1,
75 /*MinLifetime=*/MemProfMinLifetimeColdThreshold
* 1000 - 1),
76 AllocationType::NotCold
);
79 // Test buildCallstackMetadata helper.
80 TEST_F(MemoryProfileInfoTest
, BuildCallStackMD
) {
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
++);
91 // Test CallStackTrie::addCallStack interface taking allocation type and list of
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
) {
97 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
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() {
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*
109 declare dso_local noalias noundef i8* @malloc(i64 noundef)
112 Function
*Func
= M
->getFunction("test");
114 // First call has all cold contexts.
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
);
121 EXPECT_FALSE(Call1
->hasMetadata(LLVMContext::MD_memprof
));
122 EXPECT_TRUE(Call1
->hasFnAttr("memprof"));
123 EXPECT_EQ(Call1
->getFnAttr("memprof").getValueAsString(), "cold");
125 // Second call has all non-cold contexts.
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
);
132 EXPECT_FALSE(Call2
->hasMetadata(LLVMContext::MD_memprof
));
133 EXPECT_TRUE(Call2
->hasFnAttr("memprof"));
134 EXPECT_EQ(Call2
->getFnAttr("memprof").getValueAsString(), "notcold");
137 // Test CallStackTrie::addCallStack interface taking allocation type and list of
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
) {
143 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
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() {
149 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
150 %0 = bitcast i8* %call to i32*
153 declare dso_local noalias noundef i8* @malloc(i64 noundef)
156 Function
*Func
= M
->getFunction("test");
159 Trie
.addCallStack(AllocationType::Cold
, {1, 2});
160 Trie
.addCallStack(AllocationType::NotCold
, {1, 3});
162 CallBase
*Call
= findCall(*Func
, "call");
163 Trie
.buildAndAttachMIBMetadata(Call
);
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
);
180 ASSERT_EQ(StackId
->getZExtValue(), 3u);
181 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
186 // Test CallStackTrie::addCallStack interface taking allocation type and list of
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
) {
193 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
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() {
199 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40)
200 %0 = bitcast i8* %call to i32*
203 declare dso_local noalias noundef i8* @malloc(i64 noundef)
206 Function
*Func
= M
->getFunction("test");
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});
218 CallBase
*Call
= findCall(*Func
, "call");
219 Trie
.buildAndAttachMIBMetadata(Call
);
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
);
236 ASSERT_EQ(StackId
->getZExtValue(), 5u);
237 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
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
) {
247 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
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() {
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*
259 declare dso_local noalias noundef i8* @malloc(i64 noundef)
262 !2 = !{i64 1, i64 2, i64 3}
264 !4 = !{!5, !"notcold
"}
265 !5 = !{i64 4, i64 5, i64 6, i64 7}
268 Function
*Func
= M
->getFunction("test");
270 // First call has all cold contexts.
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
);
279 EXPECT_TRUE(Call1
->hasFnAttr("memprof"));
280 EXPECT_EQ(Call1
->getFnAttr("memprof").getValueAsString(), "cold");
282 // Second call has all non-cold contexts.
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
);
291 EXPECT_TRUE(Call2
->hasFnAttr("memprof"));
292 EXPECT_EQ(Call2
->getFnAttr("memprof").getValueAsString(), "notcold");
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
) {
301 std::unique_ptr
<Module
> M
= makeLLVMModule(C
,
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() {
307 %call = call noalias dereferenceable_or_null(40) i8* @malloc(i64 noundef 40), !memprof !0
308 %0 = bitcast i8* %call to i32*
311 declare dso_local noalias noundef i8* @malloc(i64 noundef)
312 !0 = !{!1, !3, !5, !7}
314 !2 = !{i64 1, i64 2, i64 3}
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}
323 Function
*Func
= M
->getFunction("test");
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
);
333 ASSERT_FALSE(Trie
.empty());
334 Trie
.buildAndAttachMIBMetadata(Call
);
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}.
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
);
356 ASSERT_EQ(StackId
->getZExtValue(), 5u);
357 EXPECT_EQ(getMIBAllocType(MIB
), AllocationType::NotCold
);
362 } // end anonymous namespace