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 | |
22 | namespace opts { |
23 | extern llvm::cl::opt<bool> ProfileUseDFS; |
24 | } // namespace opts |
25 | |
26 | namespace llvm { |
27 | namespace bolt { |
28 | |
29 | const 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 | |
50 | yaml::bolt::BinaryFunctionProfile |
51 | YAMLProfileWriter::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 | |
177 | std::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 | |