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