12f09f445SMaksim Panchenko //===- bolt/Profile/DataReader.cpp - Perf data reader ---------------------===//
2a34c753fSRafael Auler //
3a34c753fSRafael Auler // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4a34c753fSRafael Auler // See https://llvm.org/LICENSE.txt for license information.
5a34c753fSRafael Auler // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6a34c753fSRafael Auler //
7a34c753fSRafael Auler //===----------------------------------------------------------------------===//
8a34c753fSRafael Auler //
9a34c753fSRafael Auler // This family of functions reads profile data written by the perf2bolt
10a34c753fSRafael Auler // utility and stores it in memory for llvm-bolt consumption.
11a34c753fSRafael Auler //
12a34c753fSRafael Auler //===----------------------------------------------------------------------===//
13a34c753fSRafael Auler
14a34c753fSRafael Auler #include "bolt/Profile/DataReader.h"
15a34c753fSRafael Auler #include "bolt/Core/BinaryFunction.h"
16a34c753fSRafael Auler #include "bolt/Passes/MCF.h"
17a34c753fSRafael Auler #include "bolt/Utils/Utils.h"
18a34c753fSRafael Auler #include "llvm/Support/CommandLine.h"
19a34c753fSRafael Auler #include "llvm/Support/Debug.h"
20290e4823Sserge-sans-paille #include "llvm/Support/Errc.h"
21a34c753fSRafael Auler
22a34c753fSRafael Auler #undef DEBUG_TYPE
23a34c753fSRafael Auler #define DEBUG_TYPE "bolt-prof"
24a34c753fSRafael Auler
25a34c753fSRafael Auler using namespace llvm;
26a34c753fSRafael Auler
27a34c753fSRafael Auler namespace opts {
28a34c753fSRafael Auler
29a34c753fSRafael Auler extern cl::OptionCategory BoltCategory;
30a34c753fSRafael Auler extern llvm::cl::opt<unsigned> Verbosity;
31a34c753fSRafael Auler
32a34c753fSRafael Auler static cl::opt<bool>
33a34c753fSRafael Auler DumpData("dump-data",
34a34c753fSRafael Auler cl::desc("dump parsed bolt data for debugging"),
35a34c753fSRafael Auler cl::Hidden,
36a34c753fSRafael Auler cl::cat(BoltCategory));
37a34c753fSRafael Auler
38a34c753fSRafael Auler } // namespace opts
39a34c753fSRafael Auler
40a34c753fSRafael Auler namespace llvm {
41a34c753fSRafael Auler namespace bolt {
42a34c753fSRafael Auler
43a34c753fSRafael Auler namespace {
44a34c753fSRafael Auler
45a34c753fSRafael Auler /// Return true if the function name can change across compilations.
hasVolatileName(const BinaryFunction & BF)46a34c753fSRafael Auler bool hasVolatileName(const BinaryFunction &BF) {
47253b8f0aSAmir Ayupov for (const StringRef &Name : BF.getNames())
48a34c753fSRafael Auler if (getLTOCommonName(Name))
49a34c753fSRafael Auler return true;
50def464aaSAmir Ayupov
51a34c753fSRafael Auler return false;
52a34c753fSRafael Auler }
53a34c753fSRafael Auler
54a34c753fSRafael Auler /// Return standard escaped name of the function possibly renamed by BOLT.
normalizeName(StringRef NameRef)55a34c753fSRafael Auler std::string normalizeName(StringRef NameRef) {
56a34c753fSRafael Auler // Strip "PG." prefix used for globalized locals.
57ad8fd5b1SKazu Hirata NameRef = NameRef.starts_with("PG.") ? NameRef.substr(2) : NameRef;
58a34c753fSRafael Auler return getEscapedName(NameRef);
59a34c753fSRafael Auler }
60a34c753fSRafael Auler
61a34c753fSRafael Auler } // anonymous namespace
62a34c753fSRafael Auler
operator <<(raw_ostream & OS,const Location & Loc)63a34c753fSRafael Auler raw_ostream &operator<<(raw_ostream &OS, const Location &Loc) {
64a34c753fSRafael Auler if (Loc.IsSymbol) {
65a34c753fSRafael Auler OS << Loc.Name;
66a34c753fSRafael Auler if (Loc.Offset)
67a34c753fSRafael Auler OS << "+" << Twine::utohexstr(Loc.Offset);
68a34c753fSRafael Auler } else {
69a34c753fSRafael Auler OS << Twine::utohexstr(Loc.Offset);
70a34c753fSRafael Auler }
71a34c753fSRafael Auler return OS;
72a34c753fSRafael Auler }
73a34c753fSRafael Auler
appendFrom(const FuncBranchData & FBD,uint64_t Offset)74a34c753fSRafael Auler void FuncBranchData::appendFrom(const FuncBranchData &FBD, uint64_t Offset) {
75a34c753fSRafael Auler Data.insert(Data.end(), FBD.Data.begin(), FBD.Data.end());
76a34c753fSRafael Auler for (auto I = Data.begin(), E = Data.end(); I != E; ++I) {
77a34c753fSRafael Auler if (I->From.Name == FBD.Name) {
78a34c753fSRafael Auler I->From.Name = this->Name;
79a34c753fSRafael Auler I->From.Offset += Offset;
80a34c753fSRafael Auler }
81a34c753fSRafael Auler if (I->To.Name == FBD.Name) {
82a34c753fSRafael Auler I->To.Name = this->Name;
83a34c753fSRafael Auler I->To.Offset += Offset;
84a34c753fSRafael Auler }
85a34c753fSRafael Auler }
86d2c87699SAmir Ayupov llvm::stable_sort(Data);
87a34c753fSRafael Auler ExecutionCount += FBD.ExecutionCount;
88a34c753fSRafael Auler for (auto I = FBD.EntryData.begin(), E = FBD.EntryData.end(); I != E; ++I) {
89a34c753fSRafael Auler assert(I->To.Name == FBD.Name);
90a34c753fSRafael Auler auto NewElmt = EntryData.insert(EntryData.end(), *I);
91a34c753fSRafael Auler NewElmt->To.Name = this->Name;
92a34c753fSRafael Auler NewElmt->To.Offset += Offset;
93a34c753fSRafael Auler }
94a34c753fSRafael Auler }
95a34c753fSRafael Auler
getNumExecutedBranches() const96a34c753fSRafael Auler uint64_t FuncBranchData::getNumExecutedBranches() const {
97a34c753fSRafael Auler uint64_t ExecutedBranches = 0;
98a34c753fSRafael Auler for (const BranchInfo &BI : Data) {
99a34c753fSRafael Auler int64_t BranchCount = BI.Branches;
100a34c753fSRafael Auler assert(BranchCount >= 0 && "branch execution count should not be negative");
101a34c753fSRafael Auler ExecutedBranches += BranchCount;
102a34c753fSRafael Auler }
103a34c753fSRafael Auler return ExecutedBranches;
104a34c753fSRafael Auler }
105a34c753fSRafael Auler
mergeWith(const SampleInfo & SI)10640c2e0faSMaksim Panchenko void SampleInfo::mergeWith(const SampleInfo &SI) { Hits += SI.Hits; }
107a34c753fSRafael Auler
print(raw_ostream & OS) const108a34c753fSRafael Auler void SampleInfo::print(raw_ostream &OS) const {
109a34c753fSRafael Auler OS << Loc.IsSymbol << " " << Loc.Name << " " << Twine::utohexstr(Loc.Offset)
110a34c753fSRafael Auler << " " << Hits << "\n";
111a34c753fSRafael Auler }
112a34c753fSRafael Auler
getSamples(uint64_t Start,uint64_t End) const11340c2e0faSMaksim Panchenko uint64_t FuncSampleData::getSamples(uint64_t Start, uint64_t End) const {
114d2c87699SAmir Ayupov assert(llvm::is_sorted(Data));
115a34c753fSRafael Auler struct Compare {
116a34c753fSRafael Auler bool operator()(const SampleInfo &SI, const uint64_t Val) const {
117a34c753fSRafael Auler return SI.Loc.Offset < Val;
118a34c753fSRafael Auler }
119a34c753fSRafael Auler bool operator()(const uint64_t Val, const SampleInfo &SI) const {
120a34c753fSRafael Auler return Val < SI.Loc.Offset;
121a34c753fSRafael Auler }
122a34c753fSRafael Auler };
123a34c753fSRafael Auler uint64_t Result = 0;
124d2c87699SAmir Ayupov for (auto I = llvm::lower_bound(Data, Start, Compare()),
125d2c87699SAmir Ayupov E = llvm::lower_bound(Data, End, Compare());
126def464aaSAmir Ayupov I != E; ++I)
127a34c753fSRafael Auler Result += I->Hits;
128a34c753fSRafael Auler return Result;
129a34c753fSRafael Auler }
130a34c753fSRafael Auler
bumpCount(uint64_t Offset,uint64_t Count)131a34c753fSRafael Auler void FuncSampleData::bumpCount(uint64_t Offset, uint64_t Count) {
132a34c753fSRafael Auler auto Iter = Index.find(Offset);
133a34c753fSRafael Auler if (Iter == Index.end()) {
134a34c753fSRafael Auler Data.emplace_back(Location(true, Name, Offset), Count);
135a34c753fSRafael Auler Index[Offset] = Data.size() - 1;
136a34c753fSRafael Auler return;
137a34c753fSRafael Auler }
138a34c753fSRafael Auler SampleInfo &SI = Data[Iter->second];
139a34c753fSRafael Auler SI.Hits += Count;
140a34c753fSRafael Auler }
141a34c753fSRafael Auler
bumpBranchCount(uint64_t OffsetFrom,uint64_t OffsetTo,uint64_t Count,uint64_t Mispreds)142a34c753fSRafael Auler void FuncBranchData::bumpBranchCount(uint64_t OffsetFrom, uint64_t OffsetTo,
143a34c753fSRafael Auler uint64_t Count, uint64_t Mispreds) {
144a34c753fSRafael Auler auto Iter = IntraIndex[OffsetFrom].find(OffsetTo);
145a34c753fSRafael Auler if (Iter == IntraIndex[OffsetFrom].end()) {
146a34c753fSRafael Auler Data.emplace_back(Location(true, Name, OffsetFrom),
147a34c753fSRafael Auler Location(true, Name, OffsetTo), Mispreds, Count);
148a34c753fSRafael Auler IntraIndex[OffsetFrom][OffsetTo] = Data.size() - 1;
149a34c753fSRafael Auler return;
150a34c753fSRafael Auler }
151a34c753fSRafael Auler BranchInfo &BI = Data[Iter->second];
152a34c753fSRafael Auler BI.Branches += Count;
153a34c753fSRafael Auler BI.Mispreds += Mispreds;
154a34c753fSRafael Auler }
155a34c753fSRafael Auler
bumpCallCount(uint64_t OffsetFrom,const Location & To,uint64_t Count,uint64_t Mispreds)156a34c753fSRafael Auler void FuncBranchData::bumpCallCount(uint64_t OffsetFrom, const Location &To,
157a34c753fSRafael Auler uint64_t Count, uint64_t Mispreds) {
158a34c753fSRafael Auler auto Iter = InterIndex[OffsetFrom].find(To);
159a34c753fSRafael Auler if (Iter == InterIndex[OffsetFrom].end()) {
160a34c753fSRafael Auler Data.emplace_back(Location(true, Name, OffsetFrom), To, Mispreds, Count);
161a34c753fSRafael Auler InterIndex[OffsetFrom][To] = Data.size() - 1;
162a34c753fSRafael Auler return;
163a34c753fSRafael Auler }
164a34c753fSRafael Auler BranchInfo &BI = Data[Iter->second];
165a34c753fSRafael Auler BI.Branches += Count;
166a34c753fSRafael Auler BI.Mispreds += Mispreds;
167a34c753fSRafael Auler }
168a34c753fSRafael Auler
bumpEntryCount(const Location & From,uint64_t OffsetTo,uint64_t Count,uint64_t Mispreds)169a34c753fSRafael Auler void FuncBranchData::bumpEntryCount(const Location &From, uint64_t OffsetTo,
170a34c753fSRafael Auler uint64_t Count, uint64_t Mispreds) {
171a34c753fSRafael Auler auto Iter = EntryIndex[OffsetTo].find(From);
172a34c753fSRafael Auler if (Iter == EntryIndex[OffsetTo].end()) {
173a34c753fSRafael Auler EntryData.emplace_back(From, Location(true, Name, OffsetTo), Mispreds,
174a34c753fSRafael Auler Count);
175a34c753fSRafael Auler EntryIndex[OffsetTo][From] = EntryData.size() - 1;
176a34c753fSRafael Auler return;
177a34c753fSRafael Auler }
178a34c753fSRafael Auler BranchInfo &BI = EntryData[Iter->second];
179a34c753fSRafael Auler BI.Branches += Count;
180a34c753fSRafael Auler BI.Mispreds += Mispreds;
181a34c753fSRafael Auler }
182a34c753fSRafael Auler
mergeWith(const BranchInfo & BI)183a34c753fSRafael Auler void BranchInfo::mergeWith(const BranchInfo &BI) {
184a34c753fSRafael Auler Branches += BI.Branches;
185a34c753fSRafael Auler Mispreds += BI.Mispreds;
186a34c753fSRafael Auler }
187a34c753fSRafael Auler
print(raw_ostream & OS) const188a34c753fSRafael Auler void BranchInfo::print(raw_ostream &OS) const {
189a34c753fSRafael Auler OS << From.IsSymbol << " " << From.Name << " "
19040c2e0faSMaksim Panchenko << Twine::utohexstr(From.Offset) << " " << To.IsSymbol << " " << To.Name
19140c2e0faSMaksim Panchenko << " " << Twine::utohexstr(To.Offset) << " " << Mispreds << " " << Branches
19240c2e0faSMaksim Panchenko << '\n';
193a34c753fSRafael Auler }
194a34c753fSRafael Auler
getBranch(uint64_t From,uint64_t To) const195a34c753fSRafael Auler ErrorOr<const BranchInfo &> FuncBranchData::getBranch(uint64_t From,
196a34c753fSRafael Auler uint64_t To) const {
197def464aaSAmir Ayupov for (const BranchInfo &I : Data)
19840c2e0faSMaksim Panchenko if (I.From.Offset == From && I.To.Offset == To && I.From.Name == I.To.Name)
199a34c753fSRafael Auler return I;
200def464aaSAmir Ayupov
201a34c753fSRafael Auler return make_error_code(llvm::errc::invalid_argument);
202a34c753fSRafael Auler }
203a34c753fSRafael Auler
204a34c753fSRafael Auler ErrorOr<const BranchInfo &>
getDirectCallBranch(uint64_t From) const205a34c753fSRafael Auler FuncBranchData::getDirectCallBranch(uint64_t From) const {
206a34c753fSRafael Auler // Commented out because it can be expensive.
207a34c753fSRafael Auler // assert(std::is_sorted(Data.begin(), Data.end()));
208a34c753fSRafael Auler struct Compare {
209a34c753fSRafael Auler bool operator()(const BranchInfo &BI, const uint64_t Val) const {
210a34c753fSRafael Auler return BI.From.Offset < Val;
211a34c753fSRafael Auler }
212a34c753fSRafael Auler bool operator()(const uint64_t Val, const BranchInfo &BI) const {
213a34c753fSRafael Auler return Val < BI.From.Offset;
214a34c753fSRafael Auler }
215a34c753fSRafael Auler };
216a34c753fSRafael Auler auto Range = std::equal_range(Data.begin(), Data.end(), From, Compare());
21717f3cbe3SAmir Ayupov for (const auto &RI : llvm::make_range(Range))
21817f3cbe3SAmir Ayupov if (RI.From.Name != RI.To.Name)
21917f3cbe3SAmir Ayupov return RI;
220def464aaSAmir Ayupov
221a34c753fSRafael Auler return make_error_code(llvm::errc::invalid_argument);
222a34c753fSRafael Auler }
223a34c753fSRafael Auler
print(raw_ostream & OS) const224a34c753fSRafael Auler void MemInfo::print(raw_ostream &OS) const {
225a34c753fSRafael Auler OS << (Offset.IsSymbol + 3) << " " << Offset.Name << " "
22640c2e0faSMaksim Panchenko << Twine::utohexstr(Offset.Offset) << " " << (Addr.IsSymbol + 3) << " "
22740c2e0faSMaksim Panchenko << Addr.Name << " " << Twine::utohexstr(Addr.Offset) << " " << Count
22840c2e0faSMaksim Panchenko << "\n";
229a34c753fSRafael Auler }
230a34c753fSRafael Auler
prettyPrint(raw_ostream & OS) const231a34c753fSRafael Auler void MemInfo::prettyPrint(raw_ostream &OS) const {
232a34c753fSRafael Auler OS << "(PC: " << Offset << ", M: " << Addr << ", C: " << Count << ")";
233a34c753fSRafael Auler }
234a34c753fSRafael Auler
update(const Location & Offset,const Location & Addr)235a34c753fSRafael Auler void FuncMemData::update(const Location &Offset, const Location &Addr) {
236a34c753fSRafael Auler auto Iter = EventIndex[Offset.Offset].find(Addr);
237a34c753fSRafael Auler if (Iter == EventIndex[Offset.Offset].end()) {
238a34c753fSRafael Auler Data.emplace_back(MemInfo(Offset, Addr, 1));
239a34c753fSRafael Auler EventIndex[Offset.Offset][Addr] = Data.size() - 1;
240a34c753fSRafael Auler return;
241a34c753fSRafael Auler }
242a34c753fSRafael Auler ++Data[Iter->second].Count;
243a34c753fSRafael Auler }
244a34c753fSRafael Auler
preprocessProfile(BinaryContext & BC)245a34c753fSRafael Auler Error DataReader::preprocessProfile(BinaryContext &BC) {
246def464aaSAmir Ayupov if (std::error_code EC = parseInput())
247a34c753fSRafael Auler return errorCodeToError(EC);
248a34c753fSRafael Auler
249def464aaSAmir Ayupov if (opts::DumpData)
250a34c753fSRafael Auler dump();
251a34c753fSRafael Auler
252def464aaSAmir Ayupov if (collectedInBoltedBinary())
253a34c753fSRafael Auler outs() << "BOLT-INFO: profile collection done on a binary already "
254a34c753fSRafael Auler "processed by BOLT\n";
255a34c753fSRafael Auler
256a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
257a34c753fSRafael Auler BinaryFunction &Function = BFI.second;
258a34c753fSRafael Auler if (FuncMemData *MemData = getMemDataForNames(Function.getNames())) {
259a34c753fSRafael Auler setMemData(Function, MemData);
260a34c753fSRafael Auler MemData->Used = true;
261a34c753fSRafael Auler }
262a34c753fSRafael Auler if (FuncBranchData *FuncData = getBranchDataForNames(Function.getNames())) {
263a34c753fSRafael Auler setBranchData(Function, FuncData);
264a34c753fSRafael Auler Function.ExecutionCount = FuncData->ExecutionCount;
265a34c753fSRafael Auler FuncData->Used = true;
266a34c753fSRafael Auler }
267a34c753fSRafael Auler }
268a34c753fSRafael Auler
269a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
270a34c753fSRafael Auler BinaryFunction &Function = BFI.second;
271a34c753fSRafael Auler matchProfileMemData(Function);
272a34c753fSRafael Auler }
273a34c753fSRafael Auler
274a34c753fSRafael Auler return Error::success();
275a34c753fSRafael Auler }
276a34c753fSRafael Auler
readProfilePreCFG(BinaryContext & BC)277a34c753fSRafael Auler Error DataReader::readProfilePreCFG(BinaryContext &BC) {
278a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
279a34c753fSRafael Auler BinaryFunction &Function = BFI.second;
280a34c753fSRafael Auler FuncMemData *MemoryData = getMemData(Function);
281a34c753fSRafael Auler if (!MemoryData)
282a34c753fSRafael Auler continue;
283a34c753fSRafael Auler
284a34c753fSRafael Auler for (MemInfo &MI : MemoryData->Data) {
285a34c753fSRafael Auler const uint64_t Offset = MI.Offset.Offset;
286a34c753fSRafael Auler auto II = Function.Instructions.find(Offset);
287a34c753fSRafael Auler if (II == Function.Instructions.end()) {
288a34c753fSRafael Auler // Ignore bad instruction address.
289a34c753fSRafael Auler continue;
290a34c753fSRafael Auler }
291a34c753fSRafael Auler
292a34c753fSRafael Auler auto &MemAccessProfile =
293a34c753fSRafael Auler BC.MIB->getOrCreateAnnotationAs<MemoryAccessProfile>(
294a34c753fSRafael Auler II->second, "MemoryAccessProfile");
295a34c753fSRafael Auler BinaryData *BD = nullptr;
296def464aaSAmir Ayupov if (MI.Addr.IsSymbol)
297a34c753fSRafael Auler BD = BC.getBinaryDataByName(MI.Addr.Name);
29840c2e0faSMaksim Panchenko MemAccessProfile.AddressAccessInfo.push_back(
29940c2e0faSMaksim Panchenko {BD, MI.Addr.Offset, MI.Count});
300a34c753fSRafael Auler auto NextII = std::next(II);
301def464aaSAmir Ayupov if (NextII == Function.Instructions.end())
302a34c753fSRafael Auler MemAccessProfile.NextInstrOffset = Function.getSize();
303def464aaSAmir Ayupov else
304a34c753fSRafael Auler MemAccessProfile.NextInstrOffset = II->first;
305a34c753fSRafael Auler }
306a34c753fSRafael Auler Function.HasMemoryProfile = true;
307a34c753fSRafael Auler }
308a34c753fSRafael Auler
309a34c753fSRafael Auler return Error::success();
310a34c753fSRafael Auler }
311a34c753fSRafael Auler
readProfile(BinaryContext & BC)312a34c753fSRafael Auler Error DataReader::readProfile(BinaryContext &BC) {
313a34c753fSRafael Auler for (auto &BFI : BC.getBinaryFunctions()) {
314a34c753fSRafael Auler BinaryFunction &Function = BFI.second;
315a34c753fSRafael Auler readProfile(Function);
316a34c753fSRafael Auler }
317a34c753fSRafael Auler
318a34c753fSRafael Auler uint64_t NumUnused = 0;
31973b89e3fSMaksim Panchenko for (const auto &KV : NamesToBranches) {
32073b89e3fSMaksim Panchenko const FuncBranchData &FBD = KV.second;
32173b89e3fSMaksim Panchenko if (!FBD.Used)
322a34c753fSRafael Auler ++NumUnused;
32373b89e3fSMaksim Panchenko }
324a34c753fSRafael Auler BC.setNumUnusedProfiledObjects(NumUnused);
325a34c753fSRafael Auler
326a34c753fSRafael Auler return Error::success();
327a34c753fSRafael Auler }
328a34c753fSRafael Auler
parseInput()329a34c753fSRafael Auler std::error_code DataReader::parseInput() {
330a34c753fSRafael Auler ErrorOr<std::unique_ptr<MemoryBuffer>> MB =
331a34c753fSRafael Auler MemoryBuffer::getFileOrSTDIN(Filename);
332a34c753fSRafael Auler if (std::error_code EC = MB.getError()) {
333a34c753fSRafael Auler Diag << "cannot open " << Filename << ": " << EC.message() << "\n";
334a34c753fSRafael Auler return EC;
335a34c753fSRafael Auler }
336a34c753fSRafael Auler FileBuf = std::move(MB.get());
337a34c753fSRafael Auler ParsingBuf = FileBuf->getBuffer();
338def464aaSAmir Ayupov if (std::error_code EC = parse())
339a34c753fSRafael Auler return EC;
340def464aaSAmir Ayupov if (!ParsingBuf.empty())
341a34c753fSRafael Auler Diag << "WARNING: invalid profile data detected at line " << Line
342a34c753fSRafael Auler << ". Possibly corrupted profile.\n";
343a34c753fSRafael Auler
344a34c753fSRafael Auler buildLTONameMaps();
345a34c753fSRafael Auler
346a34c753fSRafael Auler return std::error_code();
347a34c753fSRafael Auler }
348a34c753fSRafael Auler
readProfile(BinaryFunction & BF)349a34c753fSRafael Auler void DataReader::readProfile(BinaryFunction &BF) {
350a34c753fSRafael Auler if (BF.empty())
351a34c753fSRafael Auler return;
352a34c753fSRafael Auler
353a34c753fSRafael Auler if (!hasLBR()) {
354a34c753fSRafael Auler BF.ProfileFlags = BinaryFunction::PF_SAMPLE;
355a34c753fSRafael Auler readSampleData(BF);
356a34c753fSRafael Auler return;
357a34c753fSRafael Auler }
358a34c753fSRafael Auler
359a34c753fSRafael Auler BF.ProfileFlags = BinaryFunction::PF_LBR;
360a34c753fSRafael Auler
361a34c753fSRafael Auler // Possibly assign/re-assign branch profile data.
362a34c753fSRafael Auler matchProfileData(BF);
363a34c753fSRafael Auler
364a34c753fSRafael Auler FuncBranchData *FBD = getBranchData(BF);
365a34c753fSRafael Auler if (!FBD)
366a34c753fSRafael Auler return;
367a34c753fSRafael Auler
368a34c753fSRafael Auler // Assign basic block counts to function entry points. These only include
369a34c753fSRafael Auler // counts for outside entries.
370a34c753fSRafael Auler //
371a34c753fSRafael Auler // There is a slight skew introduced here as branches originated from RETs
372a34c753fSRafael Auler // may be accounted for in the execution count of an entry block if the last
373a34c753fSRafael Auler // instruction in a predecessor fall-through block is a call. This situation
374a34c753fSRafael Auler // should rarely happen because there are few multiple-entry functions.
375a34c753fSRafael Auler for (const BranchInfo &BI : FBD->EntryData) {
376a34c753fSRafael Auler BinaryBasicBlock *BB = BF.getBasicBlockAtOffset(BI.To.Offset);
377a34c753fSRafael Auler if (BB && (BB->isEntryPoint() || BB->isLandingPad())) {
378a34c753fSRafael Auler uint64_t Count = BB->getExecutionCount();
379a34c753fSRafael Auler if (Count == BinaryBasicBlock::COUNT_NO_PROFILE)
380a34c753fSRafael Auler Count = 0;
381a34c753fSRafael Auler BB->setExecutionCount(Count + BI.Branches);
382a34c753fSRafael Auler }
383a34c753fSRafael Auler }
384a34c753fSRafael Auler
385a34c753fSRafael Auler for (const BranchInfo &BI : FBD->Data) {
386def464aaSAmir Ayupov if (BI.From.Name != BI.To.Name)
387a34c753fSRafael Auler continue;
388a34c753fSRafael Auler
38940c2e0faSMaksim Panchenko if (!recordBranch(BF, BI.From.Offset, BI.To.Offset, BI.Branches,
39040c2e0faSMaksim Panchenko BI.Mispreds)) {
391a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "bad branch : " << BI.From.Offset << " -> "
392a34c753fSRafael Auler << BI.To.Offset << '\n');
393a34c753fSRafael Auler }
394a34c753fSRafael Auler }
395a34c753fSRafael Auler
396a34c753fSRafael Auler // Convert branch data into annotations.
397a34c753fSRafael Auler convertBranchData(BF);
398a34c753fSRafael Auler }
399a34c753fSRafael Auler
matchProfileData(BinaryFunction & BF)400a34c753fSRafael Auler void DataReader::matchProfileData(BinaryFunction &BF) {
401a34c753fSRafael Auler // This functionality is available for LBR-mode only
402a34c753fSRafael Auler // TODO: Implement evaluateProfileData() for samples, checking whether
403a34c753fSRafael Auler // sample addresses match instruction addresses in the function
404a34c753fSRafael Auler if (!hasLBR())
405a34c753fSRafael Auler return;
406a34c753fSRafael Auler
407a34c753fSRafael Auler FuncBranchData *FBD = getBranchData(BF);
408a34c753fSRafael Auler if (FBD) {
409a34c753fSRafael Auler BF.ProfileMatchRatio = evaluateProfileData(BF, *FBD);
410a34c753fSRafael Auler BF.RawBranchCount = FBD->getNumExecutedBranches();
411a34c753fSRafael Auler if (BF.ProfileMatchRatio == 1.0f) {
412a34c753fSRafael Auler if (fetchProfileForOtherEntryPoints(BF)) {
413a34c753fSRafael Auler BF.ProfileMatchRatio = evaluateProfileData(BF, *FBD);
414a34c753fSRafael Auler BF.ExecutionCount = FBD->ExecutionCount;
415a34c753fSRafael Auler BF.RawBranchCount = FBD->getNumExecutedBranches();
416a34c753fSRafael Auler }
417a34c753fSRafael Auler return;
418a34c753fSRafael Auler }
419a34c753fSRafael Auler }
420a34c753fSRafael Auler
421a34c753fSRafael Auler // Check if the function name can fluctuate between several compilations
422a34c753fSRafael Auler // possibly triggered by minor unrelated code changes in the source code
423a34c753fSRafael Auler // of the input binary.
424a34c753fSRafael Auler if (!hasVolatileName(BF))
425a34c753fSRafael Auler return;
426a34c753fSRafael Auler
427a34c753fSRafael Auler // Check for a profile that matches with 100% confidence.
428a34c753fSRafael Auler const std::vector<FuncBranchData *> AllBranchData =
429a34c753fSRafael Auler getBranchDataForNamesRegex(BF.getNames());
430a34c753fSRafael Auler for (FuncBranchData *NewBranchData : AllBranchData) {
431a34c753fSRafael Auler // Prevent functions from sharing the same profile.
432a34c753fSRafael Auler if (NewBranchData->Used)
433a34c753fSRafael Auler continue;
434a34c753fSRafael Auler
435a34c753fSRafael Auler if (evaluateProfileData(BF, *NewBranchData) != 1.0f)
436a34c753fSRafael Auler continue;
437a34c753fSRafael Auler
438a34c753fSRafael Auler if (FBD)
439a34c753fSRafael Auler FBD->Used = false;
440a34c753fSRafael Auler
441a34c753fSRafael Auler // Update function profile data with the new set.
442a34c753fSRafael Auler setBranchData(BF, NewBranchData);
443a34c753fSRafael Auler NewBranchData->Used = true;
444a34c753fSRafael Auler BF.ExecutionCount = NewBranchData->ExecutionCount;
445a34c753fSRafael Auler BF.ProfileMatchRatio = 1.0f;
446a34c753fSRafael Auler break;
447a34c753fSRafael Auler }
448a34c753fSRafael Auler }
449a34c753fSRafael Auler
matchProfileMemData(BinaryFunction & BF)450a34c753fSRafael Auler void DataReader::matchProfileMemData(BinaryFunction &BF) {
451a34c753fSRafael Auler const std::vector<FuncMemData *> AllMemData =
452a34c753fSRafael Auler getMemDataForNamesRegex(BF.getNames());
453a34c753fSRafael Auler for (FuncMemData *NewMemData : AllMemData) {
454a34c753fSRafael Auler // Prevent functions from sharing the same profile.
455a34c753fSRafael Auler if (NewMemData->Used)
456a34c753fSRafael Auler continue;
457a34c753fSRafael Auler
458a34c753fSRafael Auler if (FuncMemData *MD = getMemData(BF))
459a34c753fSRafael Auler MD->Used = false;
460a34c753fSRafael Auler
461a34c753fSRafael Auler // Update function profile data with the new set.
462a34c753fSRafael Auler setMemData(BF, NewMemData);
463a34c753fSRafael Auler NewMemData->Used = true;
464a34c753fSRafael Auler break;
465a34c753fSRafael Auler }
466a34c753fSRafael Auler }
467a34c753fSRafael Auler
fetchProfileForOtherEntryPoints(BinaryFunction & BF)468a34c753fSRafael Auler bool DataReader::fetchProfileForOtherEntryPoints(BinaryFunction &BF) {
469a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext();
470a34c753fSRafael Auler
471a34c753fSRafael Auler FuncBranchData *FBD = getBranchData(BF);
472a34c753fSRafael Auler if (!FBD)
473a34c753fSRafael Auler return false;
474a34c753fSRafael Auler
475a34c753fSRafael Auler // Check if we are missing profiling data for secondary entry points
476a34c753fSRafael Auler bool First = true;
477a34c753fSRafael Auler bool Updated = false;
478a34c753fSRafael Auler for (BinaryBasicBlock *BB : BF.BasicBlocks) {
479a34c753fSRafael Auler if (First) {
480a34c753fSRafael Auler First = false;
481a34c753fSRafael Auler continue;
482a34c753fSRafael Auler }
483a34c753fSRafael Auler if (BB->isEntryPoint()) {
484a34c753fSRafael Auler uint64_t EntryAddress = BB->getOffset() + BF.getAddress();
485a34c753fSRafael Auler // Look for branch data associated with this entry point
486a34c753fSRafael Auler if (BinaryData *BD = BC.getBinaryDataAtAddress(EntryAddress)) {
487a34c753fSRafael Auler if (FuncBranchData *Data = getBranchDataForSymbols(BD->getSymbols())) {
488a34c753fSRafael Auler FBD->appendFrom(*Data, BB->getOffset());
489a34c753fSRafael Auler Data->Used = true;
490a34c753fSRafael Auler Updated = true;
491a34c753fSRafael Auler }
492a34c753fSRafael Auler }
493a34c753fSRafael Auler }
494a34c753fSRafael Auler }
495a34c753fSRafael Auler
496a34c753fSRafael Auler return Updated;
497a34c753fSRafael Auler }
498a34c753fSRafael Auler
evaluateProfileData(BinaryFunction & BF,const FuncBranchData & BranchData) const499a34c753fSRafael Auler float DataReader::evaluateProfileData(BinaryFunction &BF,
500a34c753fSRafael Auler const FuncBranchData &BranchData) const {
501a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext();
502a34c753fSRafael Auler
503a34c753fSRafael Auler // Until we define a minimal profile, we consider an empty branch data to be
504a34c753fSRafael Auler // a valid profile. It could happen to a function without branches when we
505a34c753fSRafael Auler // still have an EntryData for the execution count.
506def464aaSAmir Ayupov if (BranchData.Data.empty())
507a34c753fSRafael Auler return 1.0f;
508a34c753fSRafael Auler
509a34c753fSRafael Auler uint64_t NumMatchedBranches = 0;
510a34c753fSRafael Auler for (const BranchInfo &BI : BranchData.Data) {
511a34c753fSRafael Auler bool IsValid = false;
512a34c753fSRafael Auler if (BI.From.Name == BI.To.Name) {
513a34c753fSRafael Auler // Try to record information with 0 count.
514a34c753fSRafael Auler IsValid = recordBranch(BF, BI.From.Offset, BI.To.Offset, 0);
515a34c753fSRafael Auler } else if (collectedInBoltedBinary()) {
516a34c753fSRafael Auler // We can't check branch source for collections in bolted binaries because
517a34c753fSRafael Auler // the source of the branch may be mapped to the first instruction in a BB
518a34c753fSRafael Auler // instead of the original branch (which may not exist in the source bin).
519a34c753fSRafael Auler IsValid = true;
520a34c753fSRafael Auler } else {
521a34c753fSRafael Auler // The branch has to originate from this function.
522a34c753fSRafael Auler // Check for calls, tail calls, rets and indirect branches.
523a34c753fSRafael Auler // When matching profiling info, we did not reach the stage
524a34c753fSRafael Auler // when we identify tail calls, so they are still represented
525a34c753fSRafael Auler // by regular branch instructions and we need isBranch() here.
526a34c753fSRafael Auler MCInst *Instr = BF.getInstructionAtOffset(BI.From.Offset);
527a34c753fSRafael Auler // If it's a prefix - skip it.
528a34c753fSRafael Auler if (Instr && BC.MIB->isPrefix(*Instr))
529a34c753fSRafael Auler Instr = BF.getInstructionAtOffset(BI.From.Offset + 1);
53040c2e0faSMaksim Panchenko if (Instr && (BC.MIB->isCall(*Instr) || BC.MIB->isBranch(*Instr) ||
531def464aaSAmir Ayupov BC.MIB->isReturn(*Instr)))
532a34c753fSRafael Auler IsValid = true;
533a34c753fSRafael Auler }
534a34c753fSRafael Auler
535a34c753fSRafael Auler if (IsValid) {
536a34c753fSRafael Auler ++NumMatchedBranches;
537a34c753fSRafael Auler continue;
538a34c753fSRafael Auler }
539a34c753fSRafael Auler
540a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "\tinvalid branch in " << BF << " : 0x"
541a34c753fSRafael Auler << Twine::utohexstr(BI.From.Offset) << " -> ";
542a34c753fSRafael Auler if (BI.From.Name == BI.To.Name) dbgs()
543a34c753fSRafael Auler << "0x" << Twine::utohexstr(BI.To.Offset) << '\n';
544a34c753fSRafael Auler else dbgs() << "<outbounds>\n";);
545a34c753fSRafael Auler }
546a34c753fSRafael Auler
547a34c753fSRafael Auler const float MatchRatio = (float)NumMatchedBranches / BranchData.Data.size();
548def464aaSAmir Ayupov if (opts::Verbosity >= 2 && NumMatchedBranches < BranchData.Data.size())
549a34c753fSRafael Auler errs() << "BOLT-WARNING: profile branches match only "
550a34c753fSRafael Auler << format("%.1f%%", MatchRatio * 100.0f) << " ("
551a34c753fSRafael Auler << NumMatchedBranches << '/' << BranchData.Data.size()
552a34c753fSRafael Auler << ") for function " << BF << '\n';
553a34c753fSRafael Auler
554a34c753fSRafael Auler return MatchRatio;
555a34c753fSRafael Auler }
556a34c753fSRafael Auler
readSampleData(BinaryFunction & BF)557a34c753fSRafael Auler void DataReader::readSampleData(BinaryFunction &BF) {
558a34c753fSRafael Auler FuncSampleData *SampleDataOrErr = getFuncSampleData(BF.getNames());
559a34c753fSRafael Auler if (!SampleDataOrErr)
560a34c753fSRafael Auler return;
561a34c753fSRafael Auler
562a34c753fSRafael Auler // Basic samples mode territory (without LBR info)
563a34c753fSRafael Auler // First step is to assign BB execution count based on samples from perf
564a34c753fSRafael Auler BF.ProfileMatchRatio = 1.0f;
565a34c753fSRafael Auler BF.removeTagsFromProfile();
566a34c753fSRafael Auler bool NormalizeByInsnCount = usesEvent("cycles") || usesEvent("instructions");
567a34c753fSRafael Auler bool NormalizeByCalls = usesEvent("branches");
568a34c753fSRafael Auler static bool NagUser = true;
569a34c753fSRafael Auler if (NagUser) {
570a34c753fSRafael Auler outs()
571a34c753fSRafael Auler << "BOLT-INFO: operating with basic samples profiling data (no LBR).\n";
572def464aaSAmir Ayupov if (NormalizeByInsnCount)
573a34c753fSRafael Auler outs() << "BOLT-INFO: normalizing samples by instruction count.\n";
574def464aaSAmir Ayupov else if (NormalizeByCalls)
575a34c753fSRafael Auler outs() << "BOLT-INFO: normalizing samples by branches.\n";
576def464aaSAmir Ayupov
577a34c753fSRafael Auler NagUser = false;
578a34c753fSRafael Auler }
579a34c753fSRafael Auler uint64_t LastOffset = BF.getSize();
580a34c753fSRafael Auler uint64_t TotalEntryCount = 0;
581f40d25ddSAmir Ayupov for (BinaryFunction::BasicBlockOffset &BBOffset :
582f40d25ddSAmir Ayupov llvm::reverse(BF.BasicBlockOffsets)) {
583f40d25ddSAmir Ayupov uint64_t CurOffset = BBOffset.first;
584a34c753fSRafael Auler // Always work with samples multiplied by 1000 to avoid losing them if we
585a34c753fSRafael Auler // later need to normalize numbers
586a34c753fSRafael Auler uint64_t NumSamples =
587a34c753fSRafael Auler SampleDataOrErr->getSamples(CurOffset, LastOffset) * 1000;
588f40d25ddSAmir Ayupov if (NormalizeByInsnCount && BBOffset.second->getNumNonPseudos()) {
589f40d25ddSAmir Ayupov NumSamples /= BBOffset.second->getNumNonPseudos();
590def464aaSAmir Ayupov } else if (NormalizeByCalls) {
591f40d25ddSAmir Ayupov uint32_t NumCalls = BBOffset.second->getNumCalls();
592a34c753fSRafael Auler NumSamples /= NumCalls + 1;
593a34c753fSRafael Auler }
594f40d25ddSAmir Ayupov BBOffset.second->setExecutionCount(NumSamples);
595f40d25ddSAmir Ayupov if (BBOffset.second->isEntryPoint())
596a34c753fSRafael Auler TotalEntryCount += NumSamples;
597a34c753fSRafael Auler LastOffset = CurOffset;
598a34c753fSRafael Auler }
599a34c753fSRafael Auler
600a34c753fSRafael Auler BF.ExecutionCount = TotalEntryCount;
601a34c753fSRafael Auler }
602a34c753fSRafael Auler
convertBranchData(BinaryFunction & BF) const603a34c753fSRafael Auler void DataReader::convertBranchData(BinaryFunction &BF) const {
604a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext();
605a34c753fSRafael Auler
606a34c753fSRafael Auler if (BF.empty())
607a34c753fSRafael Auler return;
608a34c753fSRafael Auler
609a34c753fSRafael Auler FuncBranchData *FBD = getBranchData(BF);
610a34c753fSRafael Auler if (!FBD)
611a34c753fSRafael Auler return;
612a34c753fSRafael Auler
613a34c753fSRafael Auler // Profile information for calls.
614a34c753fSRafael Auler //
615a34c753fSRafael Auler // There are 3 cases that we annotate differently:
616a34c753fSRafael Auler // 1) Conditional tail calls that could be mispredicted.
617a34c753fSRafael Auler // 2) Indirect calls to multiple destinations with mispredictions.
618a34c753fSRafael Auler // Before we validate CFG we have to handle indirect branches here too.
619a34c753fSRafael Auler // 3) Regular direct calls. The count could be different from containing
620a34c753fSRafael Auler // basic block count. Keep this data in case we find it useful.
621a34c753fSRafael Auler //
622a34c753fSRafael Auler for (BranchInfo &BI : FBD->Data) {
623a34c753fSRafael Auler // Ignore internal branches.
624a34c753fSRafael Auler if (BI.To.IsSymbol && BI.To.Name == BI.From.Name && BI.To.Offset != 0)
625a34c753fSRafael Auler continue;
626a34c753fSRafael Auler
627a34c753fSRafael Auler MCInst *Instr = BF.getInstructionAtOffset(BI.From.Offset);
628a34c753fSRafael Auler if (!Instr ||
629a34c753fSRafael Auler (!BC.MIB->isCall(*Instr) && !BC.MIB->isIndirectBranch(*Instr)))
630a34c753fSRafael Auler continue;
631a34c753fSRafael Auler
632a34c753fSRafael Auler auto setOrUpdateAnnotation = [&](StringRef Name, uint64_t Count) {
633def464aaSAmir Ayupov if (opts::Verbosity >= 1 && BC.MIB->hasAnnotation(*Instr, Name))
634a34c753fSRafael Auler errs() << "BOLT-WARNING: duplicate " << Name << " info for offset 0x"
63540c2e0faSMaksim Panchenko << Twine::utohexstr(BI.From.Offset) << " in function " << BF
63640c2e0faSMaksim Panchenko << '\n';
637a34c753fSRafael Auler auto &Value = BC.MIB->getOrCreateAnnotationAs<uint64_t>(*Instr, Name);
638a34c753fSRafael Auler Value += Count;
639a34c753fSRafael Auler };
640a34c753fSRafael Auler
641a34c753fSRafael Auler if (BC.MIB->isIndirectCall(*Instr) || BC.MIB->isIndirectBranch(*Instr)) {
642a34c753fSRafael Auler IndirectCallSiteProfile &CSP =
643a34c753fSRafael Auler BC.MIB->getOrCreateAnnotationAs<IndirectCallSiteProfile>(
644a34c753fSRafael Auler *Instr, "CallProfile");
645a34c753fSRafael Auler MCSymbol *CalleeSymbol = nullptr;
646a34c753fSRafael Auler if (BI.To.IsSymbol) {
647def464aaSAmir Ayupov if (BinaryData *BD = BC.getBinaryDataByName(BI.To.Name))
648a34c753fSRafael Auler CalleeSymbol = BD->getSymbol();
649a34c753fSRafael Auler }
650a34c753fSRafael Auler CSP.emplace_back(CalleeSymbol, BI.Branches, BI.Mispreds);
651a34c753fSRafael Auler } else if (BC.MIB->getConditionalTailCall(*Instr)) {
652a34c753fSRafael Auler setOrUpdateAnnotation("CTCTakenCount", BI.Branches);
653a34c753fSRafael Auler setOrUpdateAnnotation("CTCMispredCount", BI.Mispreds);
654a34c753fSRafael Auler } else {
655a34c753fSRafael Auler setOrUpdateAnnotation("Count", BI.Branches);
656a34c753fSRafael Auler }
657a34c753fSRafael Auler }
658a34c753fSRafael Auler }
659a34c753fSRafael Auler
recordBranch(BinaryFunction & BF,uint64_t From,uint64_t To,uint64_t Count,uint64_t Mispreds) const66040c2e0faSMaksim Panchenko bool DataReader::recordBranch(BinaryFunction &BF, uint64_t From, uint64_t To,
661a34c753fSRafael Auler uint64_t Count, uint64_t Mispreds) const {
662a34c753fSRafael Auler BinaryContext &BC = BF.getBinaryContext();
663a34c753fSRafael Auler
664a34c753fSRafael Auler BinaryBasicBlock *FromBB = BF.getBasicBlockContainingOffset(From);
665d5c03defSFabian Parzefall const BinaryBasicBlock *ToBB = BF.getBasicBlockContainingOffset(To);
666a34c753fSRafael Auler
667a34c753fSRafael Auler if (!FromBB || !ToBB) {
668a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "failed to get block for recorded branch\n");
669a34c753fSRafael Auler return false;
670a34c753fSRafael Auler }
671a34c753fSRafael Auler
672a34c753fSRafael Auler // Could be bad LBR data; ignore the branch. In the case of data collected
673a34c753fSRafael Auler // in binaries optimized by BOLT, a source BB may be mapped to two output
674a34c753fSRafael Auler // BBs as a result of optimizations. In that case, a branch between these
675a34c753fSRafael Auler // two will be recorded as a branch from A going to A in the source address
676a34c753fSRafael Auler // space. Keep processing.
677def464aaSAmir Ayupov if (From == To)
678a34c753fSRafael Auler return true;
679a34c753fSRafael Auler
680a34c753fSRafael Auler // Return from a tail call.
681ccb99dd1SMaksim Panchenko if (FromBB->succ_size() == 0)
682a34c753fSRafael Auler return true;
683a34c753fSRafael Auler
684a34c753fSRafael Auler // Very rarely we will see ignored branches. Do a linear check.
685def464aaSAmir Ayupov for (std::pair<uint32_t, uint32_t> &Branch : BF.IgnoredBranches)
68640c2e0faSMaksim Panchenko if (Branch ==
68740c2e0faSMaksim Panchenko std::make_pair(static_cast<uint32_t>(From), static_cast<uint32_t>(To)))
688a34c753fSRafael Auler return true;
689a34c753fSRafael Auler
69008f56926SVladislav Khmelevsky bool OffsetMatches = !!(To == ToBB->getOffset());
69108f56926SVladislav Khmelevsky if (!OffsetMatches) {
69208f56926SVladislav Khmelevsky // Skip the nops to support old .fdata
69308f56926SVladislav Khmelevsky uint64_t Offset = ToBB->getOffset();
694d5c03defSFabian Parzefall for (const MCInst &Instr : *ToBB) {
69508f56926SVladislav Khmelevsky if (!BC.MIB->isNoop(Instr))
69608f56926SVladislav Khmelevsky break;
69708f56926SVladislav Khmelevsky
6982db9b6a9SMaksim Panchenko if (std::optional<uint32_t> Size = BC.MIB->getSize(Instr))
6992db9b6a9SMaksim Panchenko Offset += *Size;
70008f56926SVladislav Khmelevsky }
70108f56926SVladislav Khmelevsky
70208f56926SVladislav Khmelevsky if (To == Offset)
70308f56926SVladislav Khmelevsky OffsetMatches = true;
70408f56926SVladislav Khmelevsky }
70508f56926SVladislav Khmelevsky
70608f56926SVladislav Khmelevsky if (!OffsetMatches) {
707a34c753fSRafael Auler // "To" could be referring to nop instructions in between 2 basic blocks.
708a34c753fSRafael Auler // While building the CFG we make sure these nops are attributed to the
709a34c753fSRafael Auler // previous basic block, thus we check if the destination belongs to the
710a34c753fSRafael Auler // gap past the last instruction.
711a34c753fSRafael Auler const MCInst *LastInstr = ToBB->getLastNonPseudoInstr();
712a34c753fSRafael Auler if (LastInstr) {
713a34c753fSRafael Auler const uint32_t LastInstrOffset =
714a9cd49d5SAmir Ayupov BC.MIB->getOffsetWithDefault(*LastInstr, 0);
715a34c753fSRafael Auler
716a34c753fSRafael Auler // With old .fdata we are getting FT branches for "jcc,jmp" sequences.
717def464aaSAmir Ayupov if (To == LastInstrOffset && BC.MIB->isUnconditionalBranch(*LastInstr))
718a34c753fSRafael Auler return true;
719a34c753fSRafael Auler
720a34c753fSRafael Auler if (To <= LastInstrOffset) {
721a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "branch recorded into the middle of the block"
722a34c753fSRafael Auler << " in " << BF << " : " << From << " -> " << To
723a34c753fSRafael Auler << '\n');
724a34c753fSRafael Auler return false;
725a34c753fSRafael Auler }
726a34c753fSRafael Auler }
727a34c753fSRafael Auler
728a34c753fSRafael Auler // The real destination is the layout successor of the detected ToBB.
7298477bc67SFabian Parzefall if (ToBB == BF.getLayout().block_back())
730a34c753fSRafael Auler return false;
731d5c03defSFabian Parzefall const BinaryBasicBlock *NextBB =
732d5c03defSFabian Parzefall BF.getLayout().getBlock(ToBB->getIndex() + 1);
733a34c753fSRafael Auler assert((NextBB && NextBB->getOffset() > ToBB->getOffset()) && "bad layout");
734a34c753fSRafael Auler ToBB = NextBB;
735a34c753fSRafael Auler }
736a34c753fSRafael Auler
737a34c753fSRafael Auler // If there's no corresponding instruction for 'From', we have probably
738a34c753fSRafael Auler // discarded it as a FT from __builtin_unreachable.
739a34c753fSRafael Auler MCInst *FromInstruction = BF.getInstructionAtOffset(From);
740a34c753fSRafael Auler if (!FromInstruction) {
741a34c753fSRafael Auler // If the data was collected in a bolted binary, the From addresses may be
742a34c753fSRafael Auler // translated to the first instruction of the source BB if BOLT inserted
743a34c753fSRafael Auler // a new branch that did not exist in the source (we can't map it to the
744a34c753fSRafael Auler // source instruction, so we map it to the first instr of source BB).
745a34c753fSRafael Auler // We do not keep offsets for random instructions. So the check above will
746a34c753fSRafael Auler // evaluate to true if the first instr is not a branch (call/jmp/ret/etc)
747a34c753fSRafael Auler if (collectedInBoltedBinary()) {
748a34c753fSRafael Auler if (FromBB->getInputOffset() != From) {
749a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "offset " << From << " does not match a BB in "
750a34c753fSRafael Auler << BF << '\n');
751a34c753fSRafael Auler return false;
752a34c753fSRafael Auler }
753a34c753fSRafael Auler FromInstruction = nullptr;
754a34c753fSRafael Auler } else {
755a34c753fSRafael Auler LLVM_DEBUG(dbgs() << "no instruction for offset " << From << " in " << BF
756a34c753fSRafael Auler << '\n');
757a34c753fSRafael Auler return false;
758a34c753fSRafael Auler }
759a34c753fSRafael Auler }
760a34c753fSRafael Auler
761a34c753fSRafael Auler if (!FromBB->getSuccessor(ToBB->getLabel())) {
762a34c753fSRafael Auler // Check if this is a recursive call or a return from a recursive call.
763a34c753fSRafael Auler if (FromInstruction && ToBB->isEntryPoint() &&
764a34c753fSRafael Auler (BC.MIB->isCall(*FromInstruction) ||
765a34c753fSRafael Auler BC.MIB->isIndirectBranch(*FromInstruction))) {
766a34c753fSRafael Auler // Execution count is already accounted for.
767a34c753fSRafael Auler return true;
768a34c753fSRafael Auler }
769a34c753fSRafael Auler // For data collected in a bolted binary, we may have created two output BBs
770a34c753fSRafael Auler // that map to one original block. Branches between these two blocks will
77140c2e0faSMaksim Panchenko // appear here as one BB jumping to itself, even though it has no loop
77240c2e0faSMaksim Panchenko // edges. Ignore these.
773a34c753fSRafael Auler if (collectedInBoltedBinary() && FromBB == ToBB)
774a34c753fSRafael Auler return true;
775a34c753fSRafael Auler
776*b06f97b0SAmir Ayupov // Allow passthrough blocks.
777ccb99dd1SMaksim Panchenko BinaryBasicBlock *FTSuccessor = FromBB->getConditionalSuccessor(false);
778ccb99dd1SMaksim Panchenko if (FTSuccessor && FTSuccessor->succ_size() == 1 &&
779ccb99dd1SMaksim Panchenko FTSuccessor->getSuccessor(ToBB->getLabel())) {
780ccb99dd1SMaksim Panchenko BinaryBasicBlock::BinaryBranchInfo &FTBI =
781ccb99dd1SMaksim Panchenko FTSuccessor->getBranchInfo(*ToBB);
782ccb99dd1SMaksim Panchenko FTBI.Count += Count;
783ccb99dd1SMaksim Panchenko if (Count)
784ccb99dd1SMaksim Panchenko FTBI.MispredictedCount += Mispreds;
785ccb99dd1SMaksim Panchenko ToBB = FTSuccessor;
786ccb99dd1SMaksim Panchenko } else {
787ffef4fe0SAmir Ayupov LLVM_DEBUG(dbgs() << "invalid branch in " << BF
788ffef4fe0SAmir Ayupov << formatv(": {0:x} -> {1:x}\n", From, To));
789a34c753fSRafael Auler return false;
790a34c753fSRafael Auler }
791ccb99dd1SMaksim Panchenko }
792a34c753fSRafael Auler
793a34c753fSRafael Auler BinaryBasicBlock::BinaryBranchInfo &BI = FromBB->getBranchInfo(*ToBB);
794a34c753fSRafael Auler BI.Count += Count;
795a34c753fSRafael Auler // Only update mispredicted count if it the count was real.
796a34c753fSRafael Auler if (Count) {
797a34c753fSRafael Auler BI.MispredictedCount += Mispreds;
798a34c753fSRafael Auler }
799a34c753fSRafael Auler
800a34c753fSRafael Auler return true;
801a34c753fSRafael Auler }
802a34c753fSRafael Auler
reportError(StringRef ErrorMsg)803a34c753fSRafael Auler void DataReader::reportError(StringRef ErrorMsg) {
804a34c753fSRafael Auler Diag << "Error reading BOLT data input file: line " << Line << ", column "
805a34c753fSRafael Auler << Col << ": " << ErrorMsg << '\n';
806a34c753fSRafael Auler }
807a34c753fSRafael Auler
expectAndConsumeFS()808a34c753fSRafael Auler bool DataReader::expectAndConsumeFS() {
809a34c753fSRafael Auler if (ParsingBuf[0] != FieldSeparator) {
810a34c753fSRafael Auler reportError("expected field separator");
811a34c753fSRafael Auler return false;
812a34c753fSRafael Auler }
813a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
814a34c753fSRafael Auler Col += 1;
815a34c753fSRafael Auler return true;
816a34c753fSRafael Auler }
817a34c753fSRafael Auler
consumeAllRemainingFS()818a34c753fSRafael Auler void DataReader::consumeAllRemainingFS() {
819a34c753fSRafael Auler while (ParsingBuf[0] == FieldSeparator) {
820a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
821a34c753fSRafael Auler Col += 1;
822a34c753fSRafael Auler }
823a34c753fSRafael Auler }
824a34c753fSRafael Auler
checkAndConsumeNewLine()825a34c753fSRafael Auler bool DataReader::checkAndConsumeNewLine() {
826a34c753fSRafael Auler if (ParsingBuf[0] != '\n')
827a34c753fSRafael Auler return false;
828a34c753fSRafael Auler
829a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
830a34c753fSRafael Auler Col = 0;
831a34c753fSRafael Auler Line += 1;
832a34c753fSRafael Auler return true;
833a34c753fSRafael Auler }
834a34c753fSRafael Auler
parseString(char EndChar,bool EndNl)835a34c753fSRafael Auler ErrorOr<StringRef> DataReader::parseString(char EndChar, bool EndNl) {
836a34c753fSRafael Auler if (EndChar == '\\') {
837a34c753fSRafael Auler reportError("EndChar could not be backslash");
838a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
839a34c753fSRafael Auler }
840a34c753fSRafael Auler
841a34c753fSRafael Auler std::string EndChars(1, EndChar);
842a34c753fSRafael Auler EndChars.push_back('\\');
843a34c753fSRafael Auler if (EndNl)
844a34c753fSRafael Auler EndChars.push_back('\n');
845a34c753fSRafael Auler
846a34c753fSRafael Auler size_t StringEnd = 0;
847a34c753fSRafael Auler do {
848a34c753fSRafael Auler StringEnd = ParsingBuf.find_first_of(EndChars, StringEnd);
849a34c753fSRafael Auler if (StringEnd == StringRef::npos ||
850a34c753fSRafael Auler (StringEnd == 0 && ParsingBuf[StringEnd] != '\\')) {
851a34c753fSRafael Auler reportError("malformed field");
852a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
853a34c753fSRafael Auler }
854a34c753fSRafael Auler
855a34c753fSRafael Auler if (ParsingBuf[StringEnd] != '\\')
856a34c753fSRafael Auler break;
857a34c753fSRafael Auler
858a34c753fSRafael Auler StringEnd += 2;
8591bf531a5SKazu Hirata } while (true);
860a34c753fSRafael Auler
861a34c753fSRafael Auler StringRef Str = ParsingBuf.substr(0, StringEnd);
862a34c753fSRafael Auler
863a34c753fSRafael Auler // If EndNl was set and nl was found instead of EndChar, do not consume the
864a34c753fSRafael Auler // new line.
86540c2e0faSMaksim Panchenko bool EndNlInsteadOfEndChar = ParsingBuf[StringEnd] == '\n' && EndChar != '\n';
866a34c753fSRafael Auler unsigned End = EndNlInsteadOfEndChar ? StringEnd : StringEnd + 1;
867a34c753fSRafael Auler
868a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(End);
869a34c753fSRafael Auler if (EndChar == '\n') {
870a34c753fSRafael Auler Col = 0;
871a34c753fSRafael Auler Line += 1;
872a34c753fSRafael Auler } else {
873a34c753fSRafael Auler Col += End;
874a34c753fSRafael Auler }
875a34c753fSRafael Auler return Str;
876a34c753fSRafael Auler }
877a34c753fSRafael Auler
parseNumberField(char EndChar,bool EndNl)878a34c753fSRafael Auler ErrorOr<int64_t> DataReader::parseNumberField(char EndChar, bool EndNl) {
879a34c753fSRafael Auler ErrorOr<StringRef> NumStrRes = parseString(EndChar, EndNl);
880a34c753fSRafael Auler if (std::error_code EC = NumStrRes.getError())
881a34c753fSRafael Auler return EC;
882a34c753fSRafael Auler StringRef NumStr = NumStrRes.get();
883a34c753fSRafael Auler int64_t Num;
884a34c753fSRafael Auler if (NumStr.getAsInteger(10, Num)) {
885a34c753fSRafael Auler reportError("expected decimal number");
886a34c753fSRafael Auler Diag << "Found: " << NumStr << "\n";
887a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
888a34c753fSRafael Auler }
889a34c753fSRafael Auler return Num;
890a34c753fSRafael Auler }
891a34c753fSRafael Auler
parseHexField(char EndChar,bool EndNl)892a34c753fSRafael Auler ErrorOr<uint64_t> DataReader::parseHexField(char EndChar, bool EndNl) {
893a34c753fSRafael Auler ErrorOr<StringRef> NumStrRes = parseString(EndChar, EndNl);
894a34c753fSRafael Auler if (std::error_code EC = NumStrRes.getError())
895a34c753fSRafael Auler return EC;
896a34c753fSRafael Auler StringRef NumStr = NumStrRes.get();
897a34c753fSRafael Auler uint64_t Num;
898a34c753fSRafael Auler if (NumStr.getAsInteger(16, Num)) {
899a34c753fSRafael Auler reportError("expected hexidecimal number");
900a34c753fSRafael Auler Diag << "Found: " << NumStr << "\n";
901a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
902a34c753fSRafael Auler }
903a34c753fSRafael Auler return Num;
904a34c753fSRafael Auler }
905a34c753fSRafael Auler
parseLocation(char EndChar,bool EndNl,bool ExpectMemLoc)90640c2e0faSMaksim Panchenko ErrorOr<Location> DataReader::parseLocation(char EndChar, bool EndNl,
907a34c753fSRafael Auler bool ExpectMemLoc) {
908a34c753fSRafael Auler // Read whether the location of the branch should be DSO or a symbol
909a34c753fSRafael Auler // 0 means it is a DSO. 1 means it is a global symbol. 2 means it is a local
910a34c753fSRafael Auler // symbol.
911a34c753fSRafael Auler // The symbol flag is also used to tag memory load events by adding 3 to the
912a34c753fSRafael Auler // base values, i.e. 3 not a symbol, 4 global symbol and 5 local symbol.
91340c2e0faSMaksim Panchenko if (!ExpectMemLoc && ParsingBuf[0] != '0' && ParsingBuf[0] != '1' &&
91440c2e0faSMaksim Panchenko ParsingBuf[0] != '2') {
915a34c753fSRafael Auler reportError("expected 0, 1 or 2");
916a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
917a34c753fSRafael Auler }
918a34c753fSRafael Auler
91940c2e0faSMaksim Panchenko if (ExpectMemLoc && ParsingBuf[0] != '3' && ParsingBuf[0] != '4' &&
92040c2e0faSMaksim Panchenko ParsingBuf[0] != '5') {
921a34c753fSRafael Auler reportError("expected 3, 4 or 5");
922a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
923a34c753fSRafael Auler }
924a34c753fSRafael Auler
925a34c753fSRafael Auler bool IsSymbol =
926a34c753fSRafael Auler (!ExpectMemLoc && (ParsingBuf[0] == '1' || ParsingBuf[0] == '2')) ||
927a34c753fSRafael Auler (ExpectMemLoc && (ParsingBuf[0] == '4' || ParsingBuf[0] == '5'));
928a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
929a34c753fSRafael Auler Col += 1;
930a34c753fSRafael Auler
931a34c753fSRafael Auler if (!expectAndConsumeFS())
932a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
933a34c753fSRafael Auler consumeAllRemainingFS();
934a34c753fSRafael Auler
935a34c753fSRafael Auler // Read the string containing the symbol or the DSO name
936a34c753fSRafael Auler ErrorOr<StringRef> NameRes = parseString(FieldSeparator);
937a34c753fSRafael Auler if (std::error_code EC = NameRes.getError())
938a34c753fSRafael Auler return EC;
939a34c753fSRafael Auler StringRef Name = NameRes.get();
940a34c753fSRafael Auler consumeAllRemainingFS();
941a34c753fSRafael Auler
942a34c753fSRafael Auler // Read the offset
943a34c753fSRafael Auler ErrorOr<uint64_t> Offset = parseHexField(EndChar, EndNl);
944a34c753fSRafael Auler if (std::error_code EC = Offset.getError())
945a34c753fSRafael Auler return EC;
946a34c753fSRafael Auler
947a34c753fSRafael Auler return Location(IsSymbol, Name, Offset.get());
948a34c753fSRafael Auler }
949a34c753fSRafael Auler
parseBranchInfo()950a34c753fSRafael Auler ErrorOr<BranchInfo> DataReader::parseBranchInfo() {
951a34c753fSRafael Auler ErrorOr<Location> Res = parseLocation(FieldSeparator);
952a34c753fSRafael Auler if (std::error_code EC = Res.getError())
953a34c753fSRafael Auler return EC;
954a34c753fSRafael Auler Location From = Res.get();
955a34c753fSRafael Auler
956a34c753fSRafael Auler consumeAllRemainingFS();
957a34c753fSRafael Auler Res = parseLocation(FieldSeparator);
958a34c753fSRafael Auler if (std::error_code EC = Res.getError())
959a34c753fSRafael Auler return EC;
960a34c753fSRafael Auler Location To = Res.get();
961a34c753fSRafael Auler
962a34c753fSRafael Auler consumeAllRemainingFS();
963a34c753fSRafael Auler ErrorOr<int64_t> MRes = parseNumberField(FieldSeparator);
964a34c753fSRafael Auler if (std::error_code EC = MRes.getError())
965a34c753fSRafael Auler return EC;
966a34c753fSRafael Auler int64_t NumMispreds = MRes.get();
967a34c753fSRafael Auler
968a34c753fSRafael Auler consumeAllRemainingFS();
969a34c753fSRafael Auler ErrorOr<int64_t> BRes = parseNumberField(FieldSeparator, /* EndNl = */ true);
970a34c753fSRafael Auler if (std::error_code EC = BRes.getError())
971a34c753fSRafael Auler return EC;
972a34c753fSRafael Auler int64_t NumBranches = BRes.get();
973a34c753fSRafael Auler
974a34c753fSRafael Auler consumeAllRemainingFS();
975a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
976a34c753fSRafael Auler reportError("expected end of line");
977a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
978a34c753fSRafael Auler }
979a34c753fSRafael Auler
980a34c753fSRafael Auler return BranchInfo(std::move(From), std::move(To), NumMispreds, NumBranches);
981a34c753fSRafael Auler }
982a34c753fSRafael Auler
parseMemInfo()983a34c753fSRafael Auler ErrorOr<MemInfo> DataReader::parseMemInfo() {
984a34c753fSRafael Auler ErrorOr<Location> Res = parseMemLocation(FieldSeparator);
985a34c753fSRafael Auler if (std::error_code EC = Res.getError())
986a34c753fSRafael Auler return EC;
987a34c753fSRafael Auler Location Offset = Res.get();
988a34c753fSRafael Auler
989a34c753fSRafael Auler consumeAllRemainingFS();
990a34c753fSRafael Auler Res = parseMemLocation(FieldSeparator);
991a34c753fSRafael Auler if (std::error_code EC = Res.getError())
992a34c753fSRafael Auler return EC;
993a34c753fSRafael Auler Location Addr = Res.get();
994a34c753fSRafael Auler
995a34c753fSRafael Auler consumeAllRemainingFS();
996a34c753fSRafael Auler ErrorOr<int64_t> CountRes = parseNumberField(FieldSeparator, true);
997a34c753fSRafael Auler if (std::error_code EC = CountRes.getError())
998a34c753fSRafael Auler return EC;
999a34c753fSRafael Auler
1000a34c753fSRafael Auler consumeAllRemainingFS();
1001a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1002a34c753fSRafael Auler reportError("expected end of line");
1003a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1004a34c753fSRafael Auler }
1005a34c753fSRafael Auler
1006a34c753fSRafael Auler return MemInfo(Offset, Addr, CountRes.get());
1007a34c753fSRafael Auler }
1008a34c753fSRafael Auler
parseSampleInfo()1009a34c753fSRafael Auler ErrorOr<SampleInfo> DataReader::parseSampleInfo() {
1010a34c753fSRafael Auler ErrorOr<Location> Res = parseLocation(FieldSeparator);
1011a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1012a34c753fSRafael Auler return EC;
1013a34c753fSRafael Auler Location Address = Res.get();
1014a34c753fSRafael Auler
1015a34c753fSRafael Auler consumeAllRemainingFS();
1016a34c753fSRafael Auler ErrorOr<int64_t> BRes = parseNumberField(FieldSeparator, /* EndNl = */ true);
1017a34c753fSRafael Auler if (std::error_code EC = BRes.getError())
1018a34c753fSRafael Auler return EC;
1019a34c753fSRafael Auler int64_t Occurrences = BRes.get();
1020a34c753fSRafael Auler
1021a34c753fSRafael Auler consumeAllRemainingFS();
1022a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1023a34c753fSRafael Auler reportError("expected end of line");
1024a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1025a34c753fSRafael Auler }
1026a34c753fSRafael Auler
1027a34c753fSRafael Auler return SampleInfo(std::move(Address), Occurrences);
1028a34c753fSRafael Auler }
1029a34c753fSRafael Auler
maybeParseNoLBRFlag()1030a34c753fSRafael Auler ErrorOr<bool> DataReader::maybeParseNoLBRFlag() {
1031a34c753fSRafael Auler if (ParsingBuf.size() < 6 || ParsingBuf.substr(0, 6) != "no_lbr")
1032a34c753fSRafael Auler return false;
1033a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(6);
1034a34c753fSRafael Auler Col += 6;
1035a34c753fSRafael Auler
1036a34c753fSRafael Auler if (ParsingBuf.size() > 0 && ParsingBuf[0] == ' ')
1037a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(1);
1038a34c753fSRafael Auler
1039a34c753fSRafael Auler while (ParsingBuf.size() > 0 && ParsingBuf[0] != '\n') {
1040a34c753fSRafael Auler ErrorOr<StringRef> EventName = parseString(' ', true);
1041a34c753fSRafael Auler if (!EventName)
1042a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1043a34c753fSRafael Auler EventNames.insert(EventName.get());
1044a34c753fSRafael Auler }
1045a34c753fSRafael Auler
1046a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1047a34c753fSRafael Auler reportError("malformed no_lbr line");
1048a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1049a34c753fSRafael Auler }
1050a34c753fSRafael Auler return true;
1051a34c753fSRafael Auler }
1052a34c753fSRafael Auler
maybeParseBATFlag()1053a34c753fSRafael Auler ErrorOr<bool> DataReader::maybeParseBATFlag() {
1054a34c753fSRafael Auler if (ParsingBuf.size() < 16 || ParsingBuf.substr(0, 16) != "boltedcollection")
1055a34c753fSRafael Auler return false;
1056a34c753fSRafael Auler ParsingBuf = ParsingBuf.drop_front(16);
1057a34c753fSRafael Auler Col += 16;
1058a34c753fSRafael Auler
1059a34c753fSRafael Auler if (!checkAndConsumeNewLine()) {
1060a34c753fSRafael Auler reportError("malformed boltedcollection line");
1061a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1062a34c753fSRafael Auler }
1063a34c753fSRafael Auler return true;
1064a34c753fSRafael Auler }
1065a34c753fSRafael Auler
hasBranchData()1066a34c753fSRafael Auler bool DataReader::hasBranchData() {
1067a34c753fSRafael Auler if (ParsingBuf.size() == 0)
1068a34c753fSRafael Auler return false;
1069a34c753fSRafael Auler
1070a34c753fSRafael Auler if (ParsingBuf[0] == '0' || ParsingBuf[0] == '1' || ParsingBuf[0] == '2')
1071a34c753fSRafael Auler return true;
1072a34c753fSRafael Auler return false;
1073a34c753fSRafael Auler }
1074a34c753fSRafael Auler
hasMemData()1075a34c753fSRafael Auler bool DataReader::hasMemData() {
1076a34c753fSRafael Auler if (ParsingBuf.size() == 0)
1077a34c753fSRafael Auler return false;
1078a34c753fSRafael Auler
1079a34c753fSRafael Auler if (ParsingBuf[0] == '3' || ParsingBuf[0] == '4' || ParsingBuf[0] == '5')
1080a34c753fSRafael Auler return true;
1081a34c753fSRafael Auler return false;
1082a34c753fSRafael Auler }
1083a34c753fSRafael Auler
parseInNoLBRMode()1084a34c753fSRafael Auler std::error_code DataReader::parseInNoLBRMode() {
1085a34c753fSRafael Auler auto GetOrCreateFuncEntry = [&](StringRef Name) {
1086a34c753fSRafael Auler auto I = NamesToSamples.find(Name);
1087a34c753fSRafael Auler if (I == NamesToSamples.end()) {
1088a34c753fSRafael Auler bool Success;
1089a34c753fSRafael Auler std::tie(I, Success) = NamesToSamples.insert(std::make_pair(
1090a34c753fSRafael Auler Name, FuncSampleData(Name, FuncSampleData::ContainerTy())));
1091a34c753fSRafael Auler
1092a34c753fSRafael Auler assert(Success && "unexpected result of insert");
1093a34c753fSRafael Auler }
1094a34c753fSRafael Auler return I;
1095a34c753fSRafael Auler };
1096a34c753fSRafael Auler
1097a34c753fSRafael Auler auto GetOrCreateFuncMemEntry = [&](StringRef Name) {
1098a34c753fSRafael Auler auto I = NamesToMemEvents.find(Name);
1099a34c753fSRafael Auler if (I == NamesToMemEvents.end()) {
1100a34c753fSRafael Auler bool Success;
1101a34c753fSRafael Auler std::tie(I, Success) = NamesToMemEvents.insert(
1102a34c753fSRafael Auler std::make_pair(Name, FuncMemData(Name, FuncMemData::ContainerTy())));
1103a34c753fSRafael Auler assert(Success && "unexpected result of insert");
1104a34c753fSRafael Auler }
1105a34c753fSRafael Auler return I;
1106a34c753fSRafael Auler };
1107a34c753fSRafael Auler
1108a34c753fSRafael Auler while (hasBranchData()) {
1109a34c753fSRafael Auler ErrorOr<SampleInfo> Res = parseSampleInfo();
1110a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1111a34c753fSRafael Auler return EC;
1112a34c753fSRafael Auler
1113a34c753fSRafael Auler SampleInfo SI = Res.get();
1114a34c753fSRafael Auler
1115a34c753fSRafael Auler // Ignore samples not involving known locations
1116a34c753fSRafael Auler if (!SI.Loc.IsSymbol)
1117a34c753fSRafael Auler continue;
1118a34c753fSRafael Auler
111973b89e3fSMaksim Panchenko auto I = GetOrCreateFuncEntry(SI.Loc.Name);
112073b89e3fSMaksim Panchenko I->second.Data.emplace_back(std::move(SI));
1121a34c753fSRafael Auler }
1122a34c753fSRafael Auler
1123a34c753fSRafael Auler while (hasMemData()) {
1124a34c753fSRafael Auler ErrorOr<MemInfo> Res = parseMemInfo();
1125a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1126a34c753fSRafael Auler return EC;
1127a34c753fSRafael Auler
1128a34c753fSRafael Auler MemInfo MI = Res.get();
1129a34c753fSRafael Auler
1130a34c753fSRafael Auler // Ignore memory events not involving known pc.
1131a34c753fSRafael Auler if (!MI.Offset.IsSymbol)
1132a34c753fSRafael Auler continue;
1133a34c753fSRafael Auler
113473b89e3fSMaksim Panchenko auto I = GetOrCreateFuncMemEntry(MI.Offset.Name);
113573b89e3fSMaksim Panchenko I->second.Data.emplace_back(std::move(MI));
1136a34c753fSRafael Auler }
1137a34c753fSRafael Auler
113873b89e3fSMaksim Panchenko for (auto &FuncSamples : NamesToSamples)
1139d2c87699SAmir Ayupov llvm::stable_sort(FuncSamples.second.Data);
1140a34c753fSRafael Auler
114173b89e3fSMaksim Panchenko for (auto &MemEvents : NamesToMemEvents)
1142d2c87699SAmir Ayupov llvm::stable_sort(MemEvents.second.Data);
1143a34c753fSRafael Auler
1144a34c753fSRafael Auler return std::error_code();
1145a34c753fSRafael Auler }
1146a34c753fSRafael Auler
parse()1147a34c753fSRafael Auler std::error_code DataReader::parse() {
1148a34c753fSRafael Auler auto GetOrCreateFuncEntry = [&](StringRef Name) {
1149a34c753fSRafael Auler auto I = NamesToBranches.find(Name);
1150a34c753fSRafael Auler if (I == NamesToBranches.end()) {
1151a34c753fSRafael Auler bool Success;
1152a34c753fSRafael Auler std::tie(I, Success) = NamesToBranches.insert(std::make_pair(
1153a34c753fSRafael Auler Name, FuncBranchData(Name, FuncBranchData::ContainerTy(),
1154a34c753fSRafael Auler FuncBranchData::ContainerTy())));
1155a34c753fSRafael Auler assert(Success && "unexpected result of insert");
1156a34c753fSRafael Auler }
1157a34c753fSRafael Auler return I;
1158a34c753fSRafael Auler };
1159a34c753fSRafael Auler
1160a34c753fSRafael Auler auto GetOrCreateFuncMemEntry = [&](StringRef Name) {
1161a34c753fSRafael Auler auto I = NamesToMemEvents.find(Name);
1162a34c753fSRafael Auler if (I == NamesToMemEvents.end()) {
1163a34c753fSRafael Auler bool Success;
1164a34c753fSRafael Auler std::tie(I, Success) = NamesToMemEvents.insert(
1165a34c753fSRafael Auler std::make_pair(Name, FuncMemData(Name, FuncMemData::ContainerTy())));
1166a34c753fSRafael Auler assert(Success && "unexpected result of insert");
1167a34c753fSRafael Auler }
1168a34c753fSRafael Auler return I;
1169a34c753fSRafael Auler };
1170a34c753fSRafael Auler
1171a34c753fSRafael Auler Col = 0;
1172a34c753fSRafael Auler Line = 1;
1173a34c753fSRafael Auler ErrorOr<bool> FlagOrErr = maybeParseNoLBRFlag();
1174a34c753fSRafael Auler if (!FlagOrErr)
1175a34c753fSRafael Auler return FlagOrErr.getError();
1176a34c753fSRafael Auler NoLBRMode = *FlagOrErr;
1177a34c753fSRafael Auler
1178a34c753fSRafael Auler ErrorOr<bool> BATFlagOrErr = maybeParseBATFlag();
1179a34c753fSRafael Auler if (!BATFlagOrErr)
1180a34c753fSRafael Auler return BATFlagOrErr.getError();
1181a34c753fSRafael Auler BATMode = *BATFlagOrErr;
1182a34c753fSRafael Auler
1183a34c753fSRafael Auler if (!hasBranchData() && !hasMemData()) {
1184a34c753fSRafael Auler Diag << "ERROR: no valid profile data found\n";
1185a34c753fSRafael Auler return make_error_code(llvm::errc::io_error);
1186a34c753fSRafael Auler }
1187a34c753fSRafael Auler
1188a34c753fSRafael Auler if (NoLBRMode)
1189a34c753fSRafael Auler return parseInNoLBRMode();
1190a34c753fSRafael Auler
1191a34c753fSRafael Auler while (hasBranchData()) {
1192a34c753fSRafael Auler ErrorOr<BranchInfo> Res = parseBranchInfo();
1193a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1194a34c753fSRafael Auler return EC;
1195a34c753fSRafael Auler
1196a34c753fSRafael Auler BranchInfo BI = Res.get();
1197a34c753fSRafael Auler
1198a34c753fSRafael Auler // Ignore branches not involving known location.
1199a34c753fSRafael Auler if (!BI.From.IsSymbol && !BI.To.IsSymbol)
1200a34c753fSRafael Auler continue;
1201a34c753fSRafael Auler
120273b89e3fSMaksim Panchenko auto I = GetOrCreateFuncEntry(BI.From.Name);
120373b89e3fSMaksim Panchenko I->second.Data.emplace_back(std::move(BI));
1204a34c753fSRafael Auler
1205a34c753fSRafael Auler // Add entry data for branches to another function or branches
1206a34c753fSRafael Auler // to entry points (including recursive calls)
1207f841ca0cSKazu Hirata if (BI.To.IsSymbol && (BI.From.Name != BI.To.Name || BI.To.Offset == 0)) {
1208a34c753fSRafael Auler I = GetOrCreateFuncEntry(BI.To.Name);
120973b89e3fSMaksim Panchenko I->second.EntryData.emplace_back(std::move(BI));
1210a34c753fSRafael Auler }
1211a34c753fSRafael Auler
1212a34c753fSRafael Auler // If destination is the function start - update execution count.
1213a34c753fSRafael Auler // NB: the data is skewed since we cannot tell tail recursion from
1214a34c753fSRafael Auler // branches to the function start.
1215a34c753fSRafael Auler if (BI.To.IsSymbol && BI.To.Offset == 0) {
1216a34c753fSRafael Auler I = GetOrCreateFuncEntry(BI.To.Name);
121773b89e3fSMaksim Panchenko I->second.ExecutionCount += BI.Branches;
1218a34c753fSRafael Auler }
1219a34c753fSRafael Auler }
1220a34c753fSRafael Auler
1221a34c753fSRafael Auler while (hasMemData()) {
1222a34c753fSRafael Auler ErrorOr<MemInfo> Res = parseMemInfo();
1223a34c753fSRafael Auler if (std::error_code EC = Res.getError())
1224a34c753fSRafael Auler return EC;
1225a34c753fSRafael Auler
1226a34c753fSRafael Auler MemInfo MI = Res.get();
1227a34c753fSRafael Auler
1228a34c753fSRafael Auler // Ignore memory events not involving known pc.
1229a34c753fSRafael Auler if (!MI.Offset.IsSymbol)
1230a34c753fSRafael Auler continue;
1231a34c753fSRafael Auler
123273b89e3fSMaksim Panchenko auto I = GetOrCreateFuncMemEntry(MI.Offset.Name);
123373b89e3fSMaksim Panchenko I->second.Data.emplace_back(std::move(MI));
1234a34c753fSRafael Auler }
1235a34c753fSRafael Auler
123673b89e3fSMaksim Panchenko for (auto &FuncBranches : NamesToBranches)
1237d2c87699SAmir Ayupov llvm::stable_sort(FuncBranches.second.Data);
1238a34c753fSRafael Auler
123973b89e3fSMaksim Panchenko for (auto &MemEvents : NamesToMemEvents)
1240d2c87699SAmir Ayupov llvm::stable_sort(MemEvents.second.Data);
1241a34c753fSRafael Auler
1242a34c753fSRafael Auler return std::error_code();
1243a34c753fSRafael Auler }
1244a34c753fSRafael Auler
buildLTONameMaps()1245a34c753fSRafael Auler void DataReader::buildLTONameMaps() {
124673b89e3fSMaksim Panchenko for (auto &FuncData : NamesToBranches) {
124773b89e3fSMaksim Panchenko const StringRef FuncName = FuncData.first;
124815d1e517SAmir Ayupov const std::optional<StringRef> CommonName = getLTOCommonName(FuncName);
1249a34c753fSRafael Auler if (CommonName)
125073b89e3fSMaksim Panchenko LTOCommonNameMap[*CommonName].push_back(&FuncData.second);
1251a34c753fSRafael Auler }
1252a34c753fSRafael Auler
125373b89e3fSMaksim Panchenko for (auto &FuncData : NamesToMemEvents) {
125473b89e3fSMaksim Panchenko const StringRef FuncName = FuncData.first;
125515d1e517SAmir Ayupov const std::optional<StringRef> CommonName = getLTOCommonName(FuncName);
1256a34c753fSRafael Auler if (CommonName)
125773b89e3fSMaksim Panchenko LTOCommonNameMemMap[*CommonName].push_back(&FuncData.second);
1258a34c753fSRafael Auler }
1259a34c753fSRafael Auler }
1260a34c753fSRafael Auler
1261a34c753fSRafael Auler template <typename MapTy>
126273b89e3fSMaksim Panchenko static typename MapTy::mapped_type *
fetchMapEntry(MapTy & Map,const std::vector<MCSymbol * > & Symbols)1263a34c753fSRafael Auler fetchMapEntry(MapTy &Map, const std::vector<MCSymbol *> &Symbols) {
1264a34c753fSRafael Auler // Do a reverse order iteration since the name in profile has a higher chance
1265a34c753fSRafael Auler // of matching a name at the end of the list.
1266f40d25ddSAmir Ayupov for (const MCSymbol *Symbol : llvm::reverse(Symbols)) {
1267f40d25ddSAmir Ayupov auto I = Map.find(normalizeName(Symbol->getName()));
1268a34c753fSRafael Auler if (I != Map.end())
126973b89e3fSMaksim Panchenko return &I->second;
1270a34c753fSRafael Auler }
1271a34c753fSRafael Auler return nullptr;
1272a34c753fSRafael Auler }
1273a34c753fSRafael Auler
1274a34c753fSRafael Auler template <typename MapTy>
127573b89e3fSMaksim Panchenko static typename MapTy::mapped_type *
fetchMapEntry(MapTy & Map,const std::vector<StringRef> & FuncNames)1276a34c753fSRafael Auler fetchMapEntry(MapTy &Map, const std::vector<StringRef> &FuncNames) {
1277a34c753fSRafael Auler // Do a reverse order iteration since the name in profile has a higher chance
1278a34c753fSRafael Auler // of matching a name at the end of the list.
1279f40d25ddSAmir Ayupov for (StringRef Name : llvm::reverse(FuncNames)) {
1280f40d25ddSAmir Ayupov auto I = Map.find(normalizeName(Name));
1281a34c753fSRafael Auler if (I != Map.end())
128273b89e3fSMaksim Panchenko return &I->second;
1283a34c753fSRafael Auler }
1284a34c753fSRafael Auler return nullptr;
1285a34c753fSRafael Auler }
1286a34c753fSRafael Auler
1287a34c753fSRafael Auler template <typename MapTy>
128873b89e3fSMaksim Panchenko static std::vector<typename MapTy::mapped_type *>
fetchMapEntriesRegex(MapTy & Map,const StringMap<std::vector<typename MapTy::mapped_type * >> & LTOCommonNameMap,const std::vector<StringRef> & FuncNames)128973b89e3fSMaksim Panchenko fetchMapEntriesRegex(MapTy &Map,
129073b89e3fSMaksim Panchenko const StringMap<std::vector<typename MapTy::mapped_type *>>
129140c2e0faSMaksim Panchenko <OCommonNameMap,
1292a34c753fSRafael Auler const std::vector<StringRef> &FuncNames) {
129373b89e3fSMaksim Panchenko std::vector<typename MapTy::mapped_type *> AllData;
1294a34c753fSRafael Auler // Do a reverse order iteration since the name in profile has a higher chance
1295a34c753fSRafael Auler // of matching a name at the end of the list.
1296f40d25ddSAmir Ayupov for (StringRef FuncName : llvm::reverse(FuncNames)) {
1297f40d25ddSAmir Ayupov std::string Name = normalizeName(FuncName);
129815d1e517SAmir Ayupov const std::optional<StringRef> LTOCommonName = getLTOCommonName(Name);
1299a34c753fSRafael Auler if (LTOCommonName) {
1300a34c753fSRafael Auler auto I = LTOCommonNameMap.find(*LTOCommonName);
1301a34c753fSRafael Auler if (I != LTOCommonNameMap.end()) {
130273b89e3fSMaksim Panchenko const std::vector<typename MapTy::mapped_type *> &CommonData =
130373b89e3fSMaksim Panchenko I->second;
1304a34c753fSRafael Auler AllData.insert(AllData.end(), CommonData.begin(), CommonData.end());
1305a34c753fSRafael Auler }
1306a34c753fSRafael Auler } else {
1307a34c753fSRafael Auler auto I = Map.find(Name);
1308def464aaSAmir Ayupov if (I != Map.end())
130973b89e3fSMaksim Panchenko return {&I->second};
1310a34c753fSRafael Auler }
1311a34c753fSRafael Auler }
1312a34c753fSRafael Auler return AllData;
1313a34c753fSRafael Auler }
1314a34c753fSRafael Auler
mayHaveProfileData(const BinaryFunction & Function)1315a34c753fSRafael Auler bool DataReader::mayHaveProfileData(const BinaryFunction &Function) {
1316a34c753fSRafael Auler if (getBranchData(Function) || getMemData(Function))
1317a34c753fSRafael Auler return true;
1318a34c753fSRafael Auler
13196b05a62aSAmir Ayupov if (getFuncSampleData(Function.getNames()) ||
13206b05a62aSAmir Ayupov getBranchDataForNames(Function.getNames()) ||
1321a34c753fSRafael Auler getMemDataForNames(Function.getNames()))
1322a34c753fSRafael Auler return true;
1323a34c753fSRafael Auler
1324a34c753fSRafael Auler if (!hasVolatileName(Function))
1325a34c753fSRafael Auler return false;
1326a34c753fSRafael Auler
1327a34c753fSRafael Auler const std::vector<FuncBranchData *> AllBranchData =
1328a34c753fSRafael Auler getBranchDataForNamesRegex(Function.getNames());
1329a34c753fSRafael Auler if (!AllBranchData.empty())
1330a34c753fSRafael Auler return true;
1331a34c753fSRafael Auler
1332a34c753fSRafael Auler const std::vector<FuncMemData *> AllMemData =
1333a34c753fSRafael Auler getMemDataForNamesRegex(Function.getNames());
1334a34c753fSRafael Auler if (!AllMemData.empty())
1335a34c753fSRafael Auler return true;
1336a34c753fSRafael Auler
1337a34c753fSRafael Auler return false;
1338a34c753fSRafael Auler }
1339a34c753fSRafael Auler
1340a34c753fSRafael Auler FuncBranchData *
getBranchDataForNames(const std::vector<StringRef> & FuncNames)1341a34c753fSRafael Auler DataReader::getBranchDataForNames(const std::vector<StringRef> &FuncNames) {
1342a34c753fSRafael Auler return fetchMapEntry<NamesToBranchesMapTy>(NamesToBranches, FuncNames);
1343a34c753fSRafael Auler }
1344a34c753fSRafael Auler
1345a34c753fSRafael Auler FuncBranchData *
getBranchDataForSymbols(const std::vector<MCSymbol * > & Symbols)1346a34c753fSRafael Auler DataReader::getBranchDataForSymbols(const std::vector<MCSymbol *> &Symbols) {
1347a34c753fSRafael Auler return fetchMapEntry<NamesToBranchesMapTy>(NamesToBranches, Symbols);
1348a34c753fSRafael Auler }
1349a34c753fSRafael Auler
1350a34c753fSRafael Auler FuncMemData *
getMemDataForNames(const std::vector<StringRef> & FuncNames)1351a34c753fSRafael Auler DataReader::getMemDataForNames(const std::vector<StringRef> &FuncNames) {
1352a34c753fSRafael Auler return fetchMapEntry<NamesToMemEventsMapTy>(NamesToMemEvents, FuncNames);
1353a34c753fSRafael Auler }
1354a34c753fSRafael Auler
1355a34c753fSRafael Auler FuncSampleData *
getFuncSampleData(const std::vector<StringRef> & FuncNames)1356a34c753fSRafael Auler DataReader::getFuncSampleData(const std::vector<StringRef> &FuncNames) {
1357a34c753fSRafael Auler return fetchMapEntry<NamesToSamplesMapTy>(NamesToSamples, FuncNames);
1358a34c753fSRafael Auler }
1359a34c753fSRafael Auler
getBranchDataForNamesRegex(const std::vector<StringRef> & FuncNames)136040c2e0faSMaksim Panchenko std::vector<FuncBranchData *> DataReader::getBranchDataForNamesRegex(
1361a34c753fSRafael Auler const std::vector<StringRef> &FuncNames) {
1362a34c753fSRafael Auler return fetchMapEntriesRegex(NamesToBranches, LTOCommonNameMap, FuncNames);
1363a34c753fSRafael Auler }
1364a34c753fSRafael Auler
1365a34c753fSRafael Auler std::vector<FuncMemData *>
getMemDataForNamesRegex(const std::vector<StringRef> & FuncNames)1366a34c753fSRafael Auler DataReader::getMemDataForNamesRegex(const std::vector<StringRef> &FuncNames) {
1367a34c753fSRafael Auler return fetchMapEntriesRegex(NamesToMemEvents, LTOCommonNameMemMap, FuncNames);
1368a34c753fSRafael Auler }
1369a34c753fSRafael Auler
hasLocalsWithFileName() const1370a34c753fSRafael Auler bool DataReader::hasLocalsWithFileName() const {
137173b89e3fSMaksim Panchenko for (const auto &Func : NamesToBranches) {
137273b89e3fSMaksim Panchenko const StringRef &FuncName = Func.first;
1373a34c753fSRafael Auler if (FuncName.count('/') == 2 && FuncName[0] != '/')
1374a34c753fSRafael Auler return true;
1375a34c753fSRafael Auler }
1376a34c753fSRafael Auler return false;
1377a34c753fSRafael Auler }
1378a34c753fSRafael Auler
dump() const1379a34c753fSRafael Auler void DataReader::dump() const {
138073b89e3fSMaksim Panchenko for (const auto &KV : NamesToBranches) {
138173b89e3fSMaksim Panchenko const StringRef Name = KV.first;
138273b89e3fSMaksim Panchenko const FuncBranchData &FBD = KV.second;
138373b89e3fSMaksim Panchenko Diag << Name << " branches:\n";
138473b89e3fSMaksim Panchenko for (const BranchInfo &BI : FBD.Data)
1385a34c753fSRafael Auler Diag << BI.From.Name << " " << BI.From.Offset << " " << BI.To.Name << " "
1386a34c753fSRafael Auler << BI.To.Offset << " " << BI.Mispreds << " " << BI.Branches << "\n";
138773b89e3fSMaksim Panchenko Diag << Name << " entry points:\n";
138873b89e3fSMaksim Panchenko for (const BranchInfo &BI : FBD.EntryData)
1389a34c753fSRafael Auler Diag << BI.From.Name << " " << BI.From.Offset << " " << BI.To.Name << " "
1390a34c753fSRafael Auler << BI.To.Offset << " " << BI.Mispreds << " " << BI.Branches << "\n";
1391a34c753fSRafael Auler }
1392a34c753fSRafael Auler
1393a34c753fSRafael Auler for (auto I = EventNames.begin(), E = EventNames.end(); I != E; ++I) {
1394a34c753fSRafael Auler StringRef Event = I->getKey();
1395a34c753fSRafael Auler Diag << "Data was collected with event: " << Event << "\n";
1396a34c753fSRafael Auler }
139773b89e3fSMaksim Panchenko for (const auto &KV : NamesToSamples) {
139873b89e3fSMaksim Panchenko const StringRef Name = KV.first;
139973b89e3fSMaksim Panchenko const FuncSampleData &FSD = KV.second;
140073b89e3fSMaksim Panchenko Diag << Name << " samples:\n";
140173b89e3fSMaksim Panchenko for (const SampleInfo &SI : FSD.Data)
140240c2e0faSMaksim Panchenko Diag << SI.Loc.Name << " " << SI.Loc.Offset << " " << SI.Hits << "\n";
1403a34c753fSRafael Auler }
1404a34c753fSRafael Auler
140573b89e3fSMaksim Panchenko for (const auto &KV : NamesToMemEvents) {
140673b89e3fSMaksim Panchenko const StringRef Name = KV.first;
140773b89e3fSMaksim Panchenko const FuncMemData &FMD = KV.second;
140873b89e3fSMaksim Panchenko Diag << "Memory events for " << Name;
1409a34c753fSRafael Auler Location LastOffset(0);
141073b89e3fSMaksim Panchenko for (const MemInfo &MI : FMD.Data) {
1411def464aaSAmir Ayupov if (MI.Offset == LastOffset)
1412a34c753fSRafael Auler Diag << ", " << MI.Addr << "/" << MI.Count;
1413def464aaSAmir Ayupov else
1414a34c753fSRafael Auler Diag << "\n" << MI.Offset << ": " << MI.Addr << "/" << MI.Count;
1415a34c753fSRafael Auler LastOffset = MI.Offset;
1416a34c753fSRafael Auler }
1417a34c753fSRafael Auler Diag << "\n";
1418a34c753fSRafael Auler }
1419a34c753fSRafael Auler }
1420a34c753fSRafael Auler
1421a34c753fSRafael Auler } // namespace bolt
1422a34c753fSRafael Auler } // namespace llvm
1423