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 {
convert(const BinaryFunction & BF,yaml::bolt::BinaryFunctionProfile & YamlBF)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 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
writeProfile(const RewriteInstance & RI)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 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<NoneType> &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