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