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