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