xref: /llvm-project/bolt/lib/Profile/YAMLProfileWriter.cpp (revision 061b40896470f6f1840d340fe52deb761026d3ef)
1 //===- bolt/Profile/YAMLProfileWriter.cpp - YAML profile serializer -------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 #include "bolt/Profile/YAMLProfileWriter.h"
10 #include "bolt/Core/BinaryBasicBlock.h"
11 #include "bolt/Core/BinaryFunction.h"
12 #include "bolt/Profile/ProfileReaderBase.h"
13 #include "bolt/Rewrite/RewriteInstance.h"
14 #include "llvm/Support/CommandLine.h"
15 #include "llvm/Support/FileSystem.h"
16 #include "llvm/Support/raw_ostream.h"
17 
18 #undef  DEBUG_TYPE
19 #define DEBUG_TYPE "bolt-prof"
20 
21 namespace opts {
22 extern llvm::cl::opt<bool> ProfileUseDFS;
23 } // namespace opts
24 
25 namespace llvm {
26 namespace bolt {
27 
28 yaml::bolt::BinaryFunctionProfile
29 YAMLProfileWriter::convert(const BinaryFunction &BF, bool UseDFS) {
30   yaml::bolt::BinaryFunctionProfile YamlBF;
31   const BinaryContext &BC = BF.getBinaryContext();
32 
33   const uint16_t LBRProfile = BF.getProfileFlags() & BinaryFunction::PF_LBR;
34 
35   // Prepare function and block hashes
36   BF.computeHash(UseDFS);
37   BF.computeBlockHashes();
38 
39   YamlBF.Name = BF.getPrintName();
40   YamlBF.Id = BF.getFunctionNumber();
41   YamlBF.Hash = BF.getHash();
42   YamlBF.NumBasicBlocks = BF.size();
43   YamlBF.ExecCount = BF.getKnownExecutionCount();
44 
45   BinaryFunction::BasicBlockOrderType Order;
46   llvm::copy(UseDFS ? BF.dfs() : BF.getLayout().blocks(),
47              std::back_inserter(Order));
48 
49   for (const BinaryBasicBlock *BB : Order) {
50     yaml::bolt::BinaryBasicBlockProfile YamlBB;
51     YamlBB.Index = BB->getLayoutIndex();
52     YamlBB.NumInstructions = BB->getNumNonPseudos();
53     YamlBB.Hash = BB->getHash();
54 
55     if (!LBRProfile) {
56       YamlBB.EventCount = BB->getKnownExecutionCount();
57       if (YamlBB.EventCount)
58         YamlBF.Blocks.emplace_back(YamlBB);
59       continue;
60     }
61 
62     YamlBB.ExecCount = BB->getKnownExecutionCount();
63 
64     for (const MCInst &Instr : *BB) {
65       if (!BC.MIB->isCall(Instr) && !BC.MIB->isIndirectBranch(Instr))
66         continue;
67 
68       SmallVector<std::pair<StringRef, yaml::bolt::CallSiteInfo>> CSTargets;
69       yaml::bolt::CallSiteInfo CSI;
70       std::optional<uint32_t> Offset = BC.MIB->getOffset(Instr);
71       if (!Offset || *Offset < BB->getInputOffset())
72         continue;
73       CSI.Offset = *Offset - BB->getInputOffset();
74 
75       if (BC.MIB->isIndirectCall(Instr) || BC.MIB->isIndirectBranch(Instr)) {
76         const auto ICSP = BC.MIB->tryGetAnnotationAs<IndirectCallSiteProfile>(
77             Instr, "CallProfile");
78         if (!ICSP)
79           continue;
80         for (const IndirectCallProfile &CSP : ICSP.get()) {
81           StringRef TargetName = "";
82           CSI.DestId = 0; // designated for unknown functions
83           CSI.EntryDiscriminator = 0;
84           if (CSP.Symbol) {
85             const BinaryFunction *Callee = BC.getFunctionForSymbol(CSP.Symbol);
86             if (Callee) {
87               CSI.DestId = Callee->getFunctionNumber();
88               TargetName = Callee->getOneName();
89             }
90           }
91           CSI.Count = CSP.Count;
92           CSI.Mispreds = CSP.Mispreds;
93           CSTargets.emplace_back(TargetName, CSI);
94         }
95       } else { // direct call or a tail call
96         uint64_t EntryID = 0;
97         CSI.DestId = 0;
98         StringRef TargetName = "";
99         const MCSymbol *CalleeSymbol = BC.MIB->getTargetSymbol(Instr);
100         const BinaryFunction *const Callee =
101             BC.getFunctionForSymbol(CalleeSymbol, &EntryID);
102         if (Callee) {
103           CSI.DestId = Callee->getFunctionNumber();
104           CSI.EntryDiscriminator = EntryID;
105           TargetName = Callee->getOneName();
106         }
107 
108         if (BC.MIB->getConditionalTailCall(Instr)) {
109           auto CTCCount =
110               BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCTakenCount");
111           if (CTCCount) {
112             CSI.Count = *CTCCount;
113             auto CTCMispreds =
114                 BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "CTCMispredCount");
115             if (CTCMispreds)
116               CSI.Mispreds = *CTCMispreds;
117           }
118         } else {
119           auto Count = BC.MIB->tryGetAnnotationAs<uint64_t>(Instr, "Count");
120           if (Count)
121             CSI.Count = *Count;
122         }
123 
124         if (CSI.Count)
125           CSTargets.emplace_back(TargetName, CSI);
126       }
127       // Sort targets in a similar way to getBranchData, see Location::operator<
128       llvm::sort(CSTargets, [](const auto &RHS, const auto &LHS) {
129         if (RHS.first != LHS.first)
130           return RHS.first < LHS.first;
131         return RHS.second.Offset < LHS.second.Offset;
132       });
133       for (auto &KV : CSTargets)
134         YamlBB.CallSites.push_back(KV.second);
135     }
136 
137     // Skip printing if there's no profile data for non-entry basic block.
138     // Include landing pads with non-zero execution count.
139     if (YamlBB.CallSites.empty() && !BB->isEntryPoint() &&
140         !(BB->isLandingPad() && BB->getKnownExecutionCount() != 0)) {
141       // Include blocks having successors or predecessors with positive counts.
142       uint64_t SuccessorExecCount = 0;
143       for (const BinaryBasicBlock::BinaryBranchInfo &BranchInfo :
144            BB->branch_info())
145         SuccessorExecCount += BranchInfo.Count;
146       uint64_t PredecessorExecCount = 0;
147       for (auto Pred : BB->predecessors())
148         PredecessorExecCount += Pred->getBranchInfo(*BB).Count;
149       if (!SuccessorExecCount && !PredecessorExecCount)
150         continue;
151     }
152 
153     auto BranchInfo = BB->branch_info_begin();
154     for (const BinaryBasicBlock *Successor : BB->successors()) {
155       yaml::bolt::SuccessorInfo YamlSI;
156       YamlSI.Index = Successor->getLayoutIndex();
157       YamlSI.Count = BranchInfo->Count;
158       YamlSI.Mispreds = BranchInfo->MispredictedCount;
159 
160       YamlBB.Successors.emplace_back(YamlSI);
161 
162       ++BranchInfo;
163     }
164 
165     YamlBF.Blocks.emplace_back(YamlBB);
166   }
167   return YamlBF;
168 }
169 
170 std::error_code YAMLProfileWriter::writeProfile(const RewriteInstance &RI) {
171   const BinaryContext &BC = RI.getBinaryContext();
172   const auto &Functions = BC.getBinaryFunctions();
173 
174   std::error_code EC;
175   OS = std::make_unique<raw_fd_ostream>(Filename, EC, sys::fs::OF_None);
176   if (EC) {
177     errs() << "BOLT-WARNING: " << EC.message() << " : unable to open "
178            << Filename << " for output.\n";
179     return EC;
180   }
181 
182   yaml::bolt::BinaryProfile BP;
183 
184   // Fill out the header info.
185   BP.Header.Version = 1;
186   BP.Header.FileName = std::string(BC.getFilename());
187   std::optional<StringRef> BuildID = BC.getFileBuildID();
188   BP.Header.Id = BuildID ? std::string(*BuildID) : "<unknown>";
189   BP.Header.Origin = std::string(RI.getProfileReader()->getReaderName());
190   BP.Header.IsDFSOrder = opts::ProfileUseDFS;
191   BP.Header.HashFunction = HashFunction::Default;
192 
193   StringSet<> EventNames = RI.getProfileReader()->getEventNames();
194   if (!EventNames.empty()) {
195     std::string Sep;
196     for (const StringMapEntry<std::nullopt_t> &EventEntry : EventNames) {
197       BP.Header.EventNames += Sep + EventEntry.first().str();
198       Sep = ",";
199     }
200   }
201 
202   // Make sure the profile is consistent across all functions.
203   uint16_t ProfileFlags = BinaryFunction::PF_NONE;
204   for (const auto &BFI : Functions) {
205     const BinaryFunction &BF = BFI.second;
206     if (BF.hasProfile() && !BF.empty()) {
207       assert(BF.getProfileFlags() != BinaryFunction::PF_NONE);
208       if (ProfileFlags == BinaryFunction::PF_NONE)
209         ProfileFlags = BF.getProfileFlags();
210 
211       assert(BF.getProfileFlags() == ProfileFlags &&
212              "expected consistent profile flags across all functions");
213     }
214   }
215   BP.Header.Flags = ProfileFlags;
216 
217   // Add all function objects.
218   for (const auto &BFI : Functions) {
219     const BinaryFunction &BF = BFI.second;
220     if (BF.hasProfile()) {
221       if (!BF.hasValidProfile() && !RI.getProfileReader()->isTrustedSource())
222         continue;
223 
224       BP.Functions.emplace_back(convert(BF, opts::ProfileUseDFS));
225     }
226   }
227 
228   // Write the profile.
229   yaml::Output Out(*OS, nullptr, 0);
230   Out << BP;
231 
232   return std::error_code();
233 }
234 
235 } // namespace bolt
236 } // namespace llvm
237