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

source code of bolt/lib/Profile/YAMLProfileWriter.cpp