1 //===- bolt/Profile/YAMLProfileWriter.cpp - YAML profile serializer -------===//
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
7 //===----------------------------------------------------------------------===//
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/CommandLine.h"
16 #include "llvm/Support/FileSystem.h"
17 #include "llvm/Support/raw_ostream.h"
20 #define DEBUG_TYPE "bolt-prof"
23 extern llvm::cl::opt
<bool> ProfileUseDFS
;
30 void convert(const BinaryFunction
&BF
,
31 yaml::bolt::BinaryFunctionProfile
&YamlBF
) {
32 const BinaryContext
&BC
= BF
.getBinaryContext();
34 const uint16_t LBRProfile
= BF
.getProfileFlags() & BinaryFunction::PF_LBR
;
36 // Prepare function and block hashes
37 BF
.computeHash(opts::ProfileUseDFS
);
38 BF
.computeBlockHashes();
40 YamlBF
.Name
= BF
.getPrintName();
41 YamlBF
.Id
= BF
.getFunctionNumber();
42 YamlBF
.Hash
= BF
.getHash();
43 YamlBF
.NumBasicBlocks
= BF
.size();
44 YamlBF
.ExecCount
= BF
.getKnownExecutionCount();
46 BinaryFunction::BasicBlockOrderType Order
;
47 llvm::copy(opts::ProfileUseDFS
? BF
.dfs() : BF
.getLayout().blocks(),
48 std::back_inserter(Order
));
50 for (const BinaryBasicBlock
*BB
: Order
) {
51 yaml::bolt::BinaryBasicBlockProfile YamlBB
;
52 YamlBB
.Index
= BB
->getLayoutIndex();
53 YamlBB
.NumInstructions
= BB
->getNumNonPseudos();
54 YamlBB
.Hash
= BB
->getHash();
57 YamlBB
.EventCount
= BB
->getKnownExecutionCount();
58 if (YamlBB
.EventCount
)
59 YamlBF
.Blocks
.emplace_back(YamlBB
);
63 YamlBB
.ExecCount
= BB
->getKnownExecutionCount();
65 for (const MCInst
&Instr
: *BB
) {
66 if (!BC
.MIB
->isCall(Instr
) && !BC
.MIB
->isIndirectBranch(Instr
))
69 SmallVector
<std::pair
<StringRef
, yaml::bolt::CallSiteInfo
>> CSTargets
;
70 yaml::bolt::CallSiteInfo CSI
;
71 std::optional
<uint32_t> Offset
= BC
.MIB
->getOffset(Instr
);
72 if (!Offset
|| *Offset
< BB
->getInputOffset())
74 CSI
.Offset
= *Offset
- BB
->getInputOffset();
76 if (BC
.MIB
->isIndirectCall(Instr
) || BC
.MIB
->isIndirectBranch(Instr
)) {
77 const auto ICSP
= BC
.MIB
->tryGetAnnotationAs
<IndirectCallSiteProfile
>(
78 Instr
, "CallProfile");
81 for (const IndirectCallProfile
&CSP
: ICSP
.get()) {
82 StringRef TargetName
= "";
83 CSI
.DestId
= 0; // designated for unknown functions
84 CSI
.EntryDiscriminator
= 0;
86 const BinaryFunction
*Callee
= BC
.getFunctionForSymbol(CSP
.Symbol
);
88 CSI
.DestId
= Callee
->getFunctionNumber();
89 TargetName
= Callee
->getOneName();
92 CSI
.Count
= CSP
.Count
;
93 CSI
.Mispreds
= CSP
.Mispreds
;
94 CSTargets
.emplace_back(TargetName
, CSI
);
96 } else { // direct call or a tail call
99 StringRef TargetName
= "";
100 const MCSymbol
*CalleeSymbol
= BC
.MIB
->getTargetSymbol(Instr
);
101 const BinaryFunction
*const Callee
=
102 BC
.getFunctionForSymbol(CalleeSymbol
, &EntryID
);
104 CSI
.DestId
= Callee
->getFunctionNumber();
105 CSI
.EntryDiscriminator
= EntryID
;
106 TargetName
= Callee
->getOneName();
109 if (BC
.MIB
->getConditionalTailCall(Instr
)) {
111 BC
.MIB
->tryGetAnnotationAs
<uint64_t>(Instr
, "CTCTakenCount");
113 CSI
.Count
= *CTCCount
;
115 BC
.MIB
->tryGetAnnotationAs
<uint64_t>(Instr
, "CTCMispredCount");
117 CSI
.Mispreds
= *CTCMispreds
;
120 auto Count
= BC
.MIB
->tryGetAnnotationAs
<uint64_t>(Instr
, "Count");
126 CSTargets
.emplace_back(TargetName
, CSI
);
128 // Sort targets in a similar way to getBranchData, see Location::operator<
129 llvm::sort(CSTargets
, [](const auto &RHS
, const auto &LHS
) {
130 if (RHS
.first
!= LHS
.first
)
131 return RHS
.first
< LHS
.first
;
132 return RHS
.second
.Offset
< LHS
.second
.Offset
;
134 for (auto &KV
: CSTargets
)
135 YamlBB
.CallSites
.push_back(KV
.second
);
138 // Skip printing if there's no profile data for non-entry basic block.
139 // Include landing pads with non-zero execution count.
140 if (YamlBB
.CallSites
.empty() && !BB
->isEntryPoint() &&
141 !(BB
->isLandingPad() && BB
->getKnownExecutionCount() != 0)) {
142 // Include blocks having successors or predecessors with positive counts.
143 uint64_t SuccessorExecCount
= 0;
144 for (const BinaryBasicBlock::BinaryBranchInfo
&BranchInfo
:
146 SuccessorExecCount
+= BranchInfo
.Count
;
147 uint64_t PredecessorExecCount
= 0;
148 for (auto Pred
: BB
->predecessors())
149 PredecessorExecCount
+= Pred
->getBranchInfo(*BB
).Count
;
150 if (!SuccessorExecCount
&& !PredecessorExecCount
)
154 auto BranchInfo
= BB
->branch_info_begin();
155 for (const BinaryBasicBlock
*Successor
: BB
->successors()) {
156 yaml::bolt::SuccessorInfo YamlSI
;
157 YamlSI
.Index
= Successor
->getLayoutIndex();
158 YamlSI
.Count
= BranchInfo
->Count
;
159 YamlSI
.Mispreds
= BranchInfo
->MispredictedCount
;
161 YamlBB
.Successors
.emplace_back(YamlSI
);
166 YamlBF
.Blocks
.emplace_back(YamlBB
);
169 } // end anonymous namespace
171 std::error_code
YAMLProfileWriter::writeProfile(const RewriteInstance
&RI
) {
172 const BinaryContext
&BC
= RI
.getBinaryContext();
173 const auto &Functions
= BC
.getBinaryFunctions();
176 OS
= std::make_unique
<raw_fd_ostream
>(Filename
, EC
, sys::fs::OF_None
);
178 errs() << "BOLT-WARNING: " << EC
.message() << " : unable to open "
179 << Filename
<< " for output.\n";
183 yaml::bolt::BinaryProfile BP
;
185 // Fill out the header info.
186 BP
.Header
.Version
= 1;
187 BP
.Header
.FileName
= std::string(BC
.getFilename());
188 std::optional
<StringRef
> BuildID
= BC
.getFileBuildID();
189 BP
.Header
.Id
= BuildID
? std::string(*BuildID
) : "<unknown>";
190 BP
.Header
.Origin
= std::string(RI
.getProfileReader()->getReaderName());
191 BP
.Header
.IsDFSOrder
= opts::ProfileUseDFS
;
193 StringSet
<> EventNames
= RI
.getProfileReader()->getEventNames();
194 if (!EventNames
.empty()) {
196 for (const StringMapEntry
<std::nullopt_t
> &EventEntry
: EventNames
) {
197 BP
.Header
.EventNames
+= Sep
+ EventEntry
.first().str();
202 // Make sure the profile is consistent across all functions.
203 uint16_t ProfileFlags
= BinaryFunction::PF_NONE
;
204 for (const auto &BFI
: Functions
) {
205 const BinaryFunction
&BF
= BFI
.second
;
206 if (BF
.hasProfile() && !BF
.empty()) {
207 assert(BF
.getProfileFlags() != BinaryFunction::PF_NONE
);
208 if (ProfileFlags
== BinaryFunction::PF_NONE
)
209 ProfileFlags
= BF
.getProfileFlags();
211 assert(BF
.getProfileFlags() == ProfileFlags
&&
212 "expected consistent profile flags across all functions");
215 BP
.Header
.Flags
= ProfileFlags
;
217 // Add all function objects.
218 for (const auto &BFI
: Functions
) {
219 const BinaryFunction
&BF
= BFI
.second
;
220 if (BF
.hasProfile()) {
221 if (!BF
.hasValidProfile() && !RI
.getProfileReader()->isTrustedSource())
224 yaml::bolt::BinaryFunctionProfile YamlBF
;
226 BP
.Functions
.emplace_back(YamlBF
);
230 // Write the profile.
231 yaml::Output
Out(*OS
, nullptr, 0);
234 return std::error_code();