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