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