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