[InstCombine] Signed saturation patterns
[llvm-complete.git] / unittests / ProfileData / SampleProfTest.cpp
blob816f403f570064332915429bb6e4f09f312117fa
1 //===- unittest/ProfileData/SampleProfTest.cpp ------------------*- C++ -*-===//
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 //===----------------------------------------------------------------------===//
9 #include "llvm/ProfileData/SampleProf.h"
10 #include "llvm/ADT/StringMap.h"
11 #include "llvm/ADT/StringRef.h"
12 #include "llvm/IR/LLVMContext.h"
13 #include "llvm/IR/Metadata.h"
14 #include "llvm/IR/Module.h"
15 #include "llvm/ProfileData/SampleProfReader.h"
16 #include "llvm/ProfileData/SampleProfWriter.h"
17 #include "llvm/Support/Casting.h"
18 #include "llvm/Support/ErrorOr.h"
19 #include "llvm/Support/MemoryBuffer.h"
20 #include "llvm/Support/raw_ostream.h"
21 #include "gtest/gtest.h"
22 #include <string>
23 #include <vector>
25 using namespace llvm;
26 using namespace sampleprof;
28 static ::testing::AssertionResult NoError(std::error_code EC) {
29 if (!EC)
30 return ::testing::AssertionSuccess();
31 return ::testing::AssertionFailure() << "error " << EC.value() << ": "
32 << EC.message();
35 namespace {
37 struct SampleProfTest : ::testing::Test {
38 LLVMContext Context;
39 std::unique_ptr<SampleProfileWriter> Writer;
40 std::unique_ptr<SampleProfileReader> Reader;
42 SampleProfTest() : Writer(), Reader() {}
44 void createWriter(SampleProfileFormat Format, StringRef Profile) {
45 std::error_code EC;
46 std::unique_ptr<raw_ostream> OS(
47 new raw_fd_ostream(Profile, EC, sys::fs::OF_None));
48 auto WriterOrErr = SampleProfileWriter::create(OS, Format);
49 ASSERT_TRUE(NoError(WriterOrErr.getError()));
50 Writer = std::move(WriterOrErr.get());
53 void readProfile(const Module &M, StringRef Profile,
54 StringRef RemapFile = "") {
55 auto ReaderOrErr = SampleProfileReader::create(Profile, Context, RemapFile);
56 ASSERT_TRUE(NoError(ReaderOrErr.getError()));
57 Reader = std::move(ReaderOrErr.get());
58 Reader->collectFuncsFrom(M);
61 void createRemapFile(SmallVectorImpl<char> &RemapPath, StringRef &RemapFile) {
62 std::error_code EC =
63 llvm::sys::fs::createTemporaryFile("remapfile", "", RemapPath);
64 ASSERT_TRUE(NoError(EC));
65 RemapFile = StringRef(RemapPath.data(), RemapPath.size());
67 std::unique_ptr<raw_fd_ostream> OS(
68 new raw_fd_ostream(RemapFile, EC, sys::fs::OF_None));
69 *OS << R"(
70 # Types 'int' and 'long' are equivalent
71 type i l
72 # Function names 'foo' and 'faux' are equivalent
73 name 3foo 4faux
74 )";
75 OS->close();
78 void testRoundTrip(SampleProfileFormat Format, bool Remap) {
79 SmallVector<char, 128> ProfilePath;
80 ASSERT_TRUE(NoError(llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath)));
81 StringRef Profile(ProfilePath.data(), ProfilePath.size());
82 createWriter(Format, Profile);
84 StringRef FooName("_Z3fooi");
85 FunctionSamples FooSamples;
86 FooSamples.setName(FooName);
87 FooSamples.addTotalSamples(7711);
88 FooSamples.addHeadSamples(610);
89 FooSamples.addBodySamples(1, 0, 610);
90 FooSamples.addBodySamples(2, 0, 600);
91 FooSamples.addBodySamples(4, 0, 60000);
92 FooSamples.addBodySamples(8, 0, 60351);
93 FooSamples.addBodySamples(10, 0, 605);
95 StringRef BarName("_Z3bari");
96 FunctionSamples BarSamples;
97 BarSamples.setName(BarName);
98 BarSamples.addTotalSamples(20301);
99 BarSamples.addHeadSamples(1437);
100 BarSamples.addBodySamples(1, 0, 1437);
101 // Test how reader/writer handles unmangled names.
102 StringRef MconstructName("_M_construct<char *>");
103 StringRef StringviewName("string_view<std::allocator<char> >");
104 BarSamples.addCalledTargetSamples(1, 0, MconstructName, 1000);
105 BarSamples.addCalledTargetSamples(1, 0, StringviewName, 437);
107 StringRef BazName("_Z3bazi");
108 FunctionSamples BazSamples;
109 BazSamples.setName(BazName);
110 BazSamples.addTotalSamples(12557);
111 BazSamples.addHeadSamples(1257);
112 BazSamples.addBodySamples(1, 0, 12557);
114 StringRef BooName("_Z3booi");
115 FunctionSamples BooSamples;
116 BooSamples.setName(BooName);
117 BooSamples.addTotalSamples(1232);
118 BooSamples.addHeadSamples(1);
119 BooSamples.addBodySamples(1, 0, 1232);
121 StringMap<FunctionSamples> Profiles;
122 Profiles[FooName] = std::move(FooSamples);
123 Profiles[BarName] = std::move(BarSamples);
124 Profiles[BazName] = std::move(BazSamples);
125 Profiles[BooName] = std::move(BooSamples);
127 Module M("my_module", Context);
128 FunctionType *fn_type =
129 FunctionType::get(Type::getVoidTy(Context), {}, false);
131 SmallVector<char, 128> RemapPath;
132 StringRef RemapFile;
133 if (Remap) {
134 createRemapFile(RemapPath, RemapFile);
135 FooName = "_Z4fauxi";
136 BarName = "_Z3barl";
139 M.getOrInsertFunction(FooName, fn_type);
140 M.getOrInsertFunction(BarName, fn_type);
141 M.getOrInsertFunction(BooName, fn_type);
143 ProfileSymbolList List;
144 if (Format == SampleProfileFormat::SPF_Ext_Binary) {
145 List.add("zoo", true);
146 List.add("moo", true);
148 Writer->setProfileSymbolList(&List);
150 std::error_code EC;
151 EC = Writer->write(Profiles);
152 ASSERT_TRUE(NoError(EC));
154 Writer->getOutputStream().flush();
156 readProfile(M, Profile, RemapFile);
157 EC = Reader->read();
158 ASSERT_TRUE(NoError(EC));
160 if (Format == SampleProfileFormat::SPF_Ext_Binary) {
161 std::unique_ptr<ProfileSymbolList> ReaderList =
162 Reader->getProfileSymbolList();
163 ReaderList->contains("zoo");
164 ReaderList->contains("moo");
167 FunctionSamples *ReadFooSamples = Reader->getSamplesFor(FooName);
168 ASSERT_TRUE(ReadFooSamples != nullptr);
169 if (Format != SampleProfileFormat::SPF_Compact_Binary) {
170 ASSERT_EQ("_Z3fooi", ReadFooSamples->getName());
172 ASSERT_EQ(7711u, ReadFooSamples->getTotalSamples());
173 ASSERT_EQ(610u, ReadFooSamples->getHeadSamples());
175 FunctionSamples *ReadBarSamples = Reader->getSamplesFor(BarName);
176 ASSERT_TRUE(ReadBarSamples != nullptr);
177 if (Format != SampleProfileFormat::SPF_Compact_Binary) {
178 ASSERT_EQ("_Z3bari", ReadBarSamples->getName());
180 ASSERT_EQ(20301u, ReadBarSamples->getTotalSamples());
181 ASSERT_EQ(1437u, ReadBarSamples->getHeadSamples());
182 ErrorOr<SampleRecord::CallTargetMap> CTMap =
183 ReadBarSamples->findCallTargetMapAt(1, 0);
184 ASSERT_FALSE(CTMap.getError());
186 // Because _Z3bazi is not defined in module M, expect _Z3bazi's profile
187 // is not loaded when the profile is ExtBinary or Compact format because
188 // these formats support loading function profiles on demand.
189 FunctionSamples *ReadBazSamples = Reader->getSamplesFor(BazName);
190 if (Format == SampleProfileFormat::SPF_Ext_Binary ||
191 Format == SampleProfileFormat::SPF_Compact_Binary) {
192 ASSERT_TRUE(ReadBazSamples == nullptr);
193 ASSERT_EQ(3u, Reader->getProfiles().size());
194 } else {
195 ASSERT_TRUE(ReadBazSamples != nullptr);
196 ASSERT_EQ(12557u, ReadBazSamples->getTotalSamples());
197 ASSERT_EQ(4u, Reader->getProfiles().size());
200 FunctionSamples *ReadBooSamples = Reader->getSamplesFor(BooName);
201 ASSERT_TRUE(ReadBooSamples != nullptr);
202 ASSERT_EQ(1232u, ReadBooSamples->getTotalSamples());
204 std::string MconstructGUID;
205 StringRef MconstructRep =
206 getRepInFormat(MconstructName, Format, MconstructGUID);
207 std::string StringviewGUID;
208 StringRef StringviewRep =
209 getRepInFormat(StringviewName, Format, StringviewGUID);
210 ASSERT_EQ(1000u, CTMap.get()[MconstructRep]);
211 ASSERT_EQ(437u, CTMap.get()[StringviewRep]);
213 auto VerifySummary = [](ProfileSummary &Summary) mutable {
214 ASSERT_EQ(ProfileSummary::PSK_Sample, Summary.getKind());
215 ASSERT_EQ(137392u, Summary.getTotalCount());
216 ASSERT_EQ(8u, Summary.getNumCounts());
217 ASSERT_EQ(4u, Summary.getNumFunctions());
218 ASSERT_EQ(1437u, Summary.getMaxFunctionCount());
219 ASSERT_EQ(60351u, Summary.getMaxCount());
221 uint32_t Cutoff = 800000;
222 auto Predicate = [&Cutoff](const ProfileSummaryEntry &PE) {
223 return PE.Cutoff == Cutoff;
225 std::vector<ProfileSummaryEntry> &Details = Summary.getDetailedSummary();
226 auto EightyPerc = find_if(Details, Predicate);
227 Cutoff = 900000;
228 auto NinetyPerc = find_if(Details, Predicate);
229 Cutoff = 950000;
230 auto NinetyFivePerc = find_if(Details, Predicate);
231 Cutoff = 990000;
232 auto NinetyNinePerc = find_if(Details, Predicate);
233 ASSERT_EQ(60000u, EightyPerc->MinCount);
234 ASSERT_EQ(12557u, NinetyPerc->MinCount);
235 ASSERT_EQ(12557u, NinetyFivePerc->MinCount);
236 ASSERT_EQ(610u, NinetyNinePerc->MinCount);
239 ProfileSummary &Summary = Reader->getSummary();
240 VerifySummary(Summary);
242 // Test that conversion of summary to and from Metadata works.
243 Metadata *MD = Summary.getMD(Context);
244 ASSERT_TRUE(MD);
245 ProfileSummary *PS = ProfileSummary::getFromMD(MD);
246 ASSERT_TRUE(PS);
247 VerifySummary(*PS);
248 delete PS;
250 // Test that summary can be attached to and read back from module.
251 M.setProfileSummary(MD, ProfileSummary::PSK_Sample);
252 MD = M.getProfileSummary(/* IsCS */ false);
253 ASSERT_TRUE(MD);
254 PS = ProfileSummary::getFromMD(MD);
255 ASSERT_TRUE(PS);
256 VerifySummary(*PS);
257 delete PS;
260 void addFunctionSamples(StringMap<FunctionSamples> *Smap, const char *Fname,
261 uint64_t TotalSamples, uint64_t HeadSamples) {
262 StringRef Name(Fname);
263 FunctionSamples FcnSamples;
264 FcnSamples.setName(Name);
265 FcnSamples.addTotalSamples(TotalSamples);
266 FcnSamples.addHeadSamples(HeadSamples);
267 FcnSamples.addBodySamples(1, 0, HeadSamples);
268 (*Smap)[Name] = FcnSamples;
271 StringMap<FunctionSamples> setupFcnSamplesForElisionTest(StringRef Policy) {
272 StringMap<FunctionSamples> Smap;
273 addFunctionSamples(&Smap, "foo", uint64_t(20301), uint64_t(1437));
274 if (Policy == "" || Policy == "all")
275 return Smap;
276 addFunctionSamples(&Smap, "foo.bar", uint64_t(20303), uint64_t(1439));
277 if (Policy == "selected")
278 return Smap;
279 addFunctionSamples(&Smap, "foo.llvm.2465", uint64_t(20305), uint64_t(1441));
280 return Smap;
283 void createFunctionWithSampleProfileElisionPolicy(Module *M,
284 const char *Fname,
285 StringRef Policy) {
286 FunctionType *FnType =
287 FunctionType::get(Type::getVoidTy(Context), {}, false);
288 auto Inserted = M->getOrInsertFunction(Fname, FnType);
289 auto Fcn = cast<Function>(Inserted.getCallee());
290 if (Policy != "")
291 Fcn->addFnAttr("sample-profile-suffix-elision-policy", Policy);
294 void setupModuleForElisionTest(Module *M, StringRef Policy) {
295 createFunctionWithSampleProfileElisionPolicy(M, "foo", Policy);
296 createFunctionWithSampleProfileElisionPolicy(M, "foo.bar", Policy);
297 createFunctionWithSampleProfileElisionPolicy(M, "foo.llvm.2465", Policy);
300 void testSuffixElisionPolicy(SampleProfileFormat Format, StringRef Policy,
301 const StringMap<uint64_t> &Expected) {
302 SmallVector<char, 128> ProfilePath;
303 std::error_code EC;
304 EC = llvm::sys::fs::createTemporaryFile("profile", "", ProfilePath);
305 ASSERT_TRUE(NoError(EC));
306 StringRef ProfileFile(ProfilePath.data(), ProfilePath.size());
308 Module M("my_module", Context);
309 setupModuleForElisionTest(&M, Policy);
310 StringMap<FunctionSamples> ProfMap = setupFcnSamplesForElisionTest(Policy);
312 // write profile
313 createWriter(Format, ProfileFile);
314 EC = Writer->write(ProfMap);
315 ASSERT_TRUE(NoError(EC));
316 Writer->getOutputStream().flush();
318 // read profile
319 readProfile(M, ProfileFile);
320 EC = Reader->read();
321 ASSERT_TRUE(NoError(EC));
323 for (auto I = Expected.begin(); I != Expected.end(); ++I) {
324 uint64_t Esamples = uint64_t(-1);
325 FunctionSamples *Samples = Reader->getSamplesFor(I->getKey());
326 if (Samples != nullptr)
327 Esamples = Samples->getTotalSamples();
328 ASSERT_EQ(I->getValue(), Esamples);
333 TEST_F(SampleProfTest, roundtrip_text_profile) {
334 testRoundTrip(SampleProfileFormat::SPF_Text, false);
337 TEST_F(SampleProfTest, roundtrip_raw_binary_profile) {
338 testRoundTrip(SampleProfileFormat::SPF_Binary, false);
341 TEST_F(SampleProfTest, roundtrip_compact_binary_profile) {
342 testRoundTrip(SampleProfileFormat::SPF_Compact_Binary, false);
345 TEST_F(SampleProfTest, roundtrip_ext_binary_profile) {
346 testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, false);
349 TEST_F(SampleProfTest, remap_text_profile) {
350 testRoundTrip(SampleProfileFormat::SPF_Text, true);
353 TEST_F(SampleProfTest, remap_raw_binary_profile) {
354 testRoundTrip(SampleProfileFormat::SPF_Binary, true);
357 TEST_F(SampleProfTest, remap_ext_binary_profile) {
358 testRoundTrip(SampleProfileFormat::SPF_Ext_Binary, true);
361 TEST_F(SampleProfTest, sample_overflow_saturation) {
362 const uint64_t Max = std::numeric_limits<uint64_t>::max();
363 sampleprof_error Result;
365 FunctionSamples FooSamples;
366 Result = FooSamples.addTotalSamples(1);
367 ASSERT_EQ(Result, sampleprof_error::success);
369 Result = FooSamples.addHeadSamples(1);
370 ASSERT_EQ(Result, sampleprof_error::success);
372 Result = FooSamples.addBodySamples(10, 0, 1);
373 ASSERT_EQ(Result, sampleprof_error::success);
375 Result = FooSamples.addTotalSamples(Max);
376 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
377 ASSERT_EQ(FooSamples.getTotalSamples(), Max);
379 Result = FooSamples.addHeadSamples(Max);
380 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
381 ASSERT_EQ(FooSamples.getHeadSamples(), Max);
383 Result = FooSamples.addBodySamples(10, 0, Max);
384 ASSERT_EQ(Result, sampleprof_error::counter_overflow);
385 ErrorOr<uint64_t> BodySamples = FooSamples.findSamplesAt(10, 0);
386 ASSERT_FALSE(BodySamples.getError());
387 ASSERT_EQ(BodySamples.get(), Max);
390 TEST_F(SampleProfTest, default_suffix_elision_text) {
391 // Default suffix elision policy: strip everything after first dot.
392 // This implies that all suffix variants will map to "foo", so
393 // we don't expect to see any entries for them in the sample
394 // profile.
395 StringMap<uint64_t> Expected;
396 Expected["foo"] = uint64_t(20301);
397 Expected["foo.bar"] = uint64_t(-1);
398 Expected["foo.llvm.2465"] = uint64_t(-1);
399 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "", Expected);
402 TEST_F(SampleProfTest, default_suffix_elision_compact_binary) {
403 // Default suffix elision policy: strip everything after first dot.
404 // This implies that all suffix variants will map to "foo", so
405 // we don't expect to see any entries for them in the sample
406 // profile.
407 StringMap<uint64_t> Expected;
408 Expected["foo"] = uint64_t(20301);
409 Expected["foo.bar"] = uint64_t(-1);
410 Expected["foo.llvm.2465"] = uint64_t(-1);
411 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "",
412 Expected);
415 TEST_F(SampleProfTest, selected_suffix_elision_text) {
416 // Profile is created and searched using the "selected"
417 // suffix elision policy: we only strip a .XXX suffix if
418 // it matches a pattern known to be generated by the compiler
419 // (e.g. ".llvm.<digits>").
420 StringMap<uint64_t> Expected;
421 Expected["foo"] = uint64_t(20301);
422 Expected["foo.bar"] = uint64_t(20303);
423 Expected["foo.llvm.2465"] = uint64_t(-1);
424 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "selected", Expected);
427 TEST_F(SampleProfTest, selected_suffix_elision_compact_binary) {
428 // Profile is created and searched using the "selected"
429 // suffix elision policy: we only strip a .XXX suffix if
430 // it matches a pattern known to be generated by the compiler
431 // (e.g. ".llvm.<digits>").
432 StringMap<uint64_t> Expected;
433 Expected["foo"] = uint64_t(20301);
434 Expected["foo.bar"] = uint64_t(20303);
435 Expected["foo.llvm.2465"] = uint64_t(-1);
436 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "selected",
437 Expected);
440 TEST_F(SampleProfTest, none_suffix_elision_text) {
441 // Profile is created and searched using the "none"
442 // suffix elision policy: no stripping of suffixes at all.
443 // Here we expect to see all variants in the profile.
444 StringMap<uint64_t> Expected;
445 Expected["foo"] = uint64_t(20301);
446 Expected["foo.bar"] = uint64_t(20303);
447 Expected["foo.llvm.2465"] = uint64_t(20305);
448 testSuffixElisionPolicy(SampleProfileFormat::SPF_Text, "none", Expected);
451 TEST_F(SampleProfTest, none_suffix_elision_compact_binary) {
452 // Profile is created and searched using the "none"
453 // suffix elision policy: no stripping of suffixes at all.
454 // Here we expect to see all variants in the profile.
455 StringMap<uint64_t> Expected;
456 Expected["foo"] = uint64_t(20301);
457 Expected["foo.bar"] = uint64_t(20303);
458 Expected["foo.llvm.2465"] = uint64_t(20305);
459 testSuffixElisionPolicy(SampleProfileFormat::SPF_Compact_Binary, "none",
460 Expected);
463 } // end anonymous namespace