xref: /llvm-project/bolt/include/bolt/Profile/ProfileYAMLMapping.h (revision 9a9af0a23fc910694b6a806b7ce9cb2e7e4240ef)
1 //===- bolt/Profile/ProfileYAMLMapping.h ------------------------*- C++ -*-===//
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 // Implement mapping between binary function profile and YAML representation.
10 //
11 //===----------------------------------------------------------------------===//
12 
13 #ifndef BOLT_PROFILE_PROFILEYAMLMAPPING_H
14 #define BOLT_PROFILE_PROFILEYAMLMAPPING_H
15 
16 #include "bolt/Core/BinaryFunction.h"
17 #include "llvm/Support/YAMLTraits.h"
18 #include <vector>
19 
20 using llvm::bolt::BinaryFunction;
21 
22 namespace llvm {
23 namespace yaml {
24 
25 namespace bolt {
26 struct CallSiteInfo {
27   llvm::yaml::Hex32 Offset{0};
28   uint32_t DestId{0};
29   uint32_t EntryDiscriminator{0}; /// multiple entry discriminator
30   uint64_t Count{0};
31   uint64_t Mispreds{0};
32 
33   bool operator==(const CallSiteInfo &Other) const {
34     return Offset == Other.Offset && DestId == Other.DestId &&
35            EntryDiscriminator == Other.EntryDiscriminator;
36   }
37 
38   bool operator!=(const CallSiteInfo &Other) const { return !(*this == Other); }
39 
40   bool operator<(const CallSiteInfo &Other) const {
41     if (Offset < Other.Offset)
42       return true;
43     if (Offset > Other.Offset)
44       return false;
45 
46     if (DestId < Other.DestId)
47       return true;
48     if (DestId > Other.DestId)
49       return false;
50 
51     if (EntryDiscriminator < Other.EntryDiscriminator)
52       return true;
53 
54     return false;
55   }
56 };
57 } // end namespace bolt
58 
59 template <> struct MappingTraits<bolt::CallSiteInfo> {
60   static void mapping(IO &YamlIO, bolt::CallSiteInfo &CSI) {
61     YamlIO.mapRequired("off", CSI.Offset);
62     YamlIO.mapRequired("fid", CSI.DestId);
63     YamlIO.mapOptional("disc", CSI.EntryDiscriminator, (uint32_t)0);
64     YamlIO.mapRequired("cnt", CSI.Count);
65     YamlIO.mapOptional("mis", CSI.Mispreds, (uint64_t)0);
66   }
67 
68   static const bool flow = true;
69 };
70 
71 namespace bolt {
72 struct SuccessorInfo {
73   uint32_t Index{0};
74   uint64_t Count{0};
75   uint64_t Mispreds{0};
76 
77   bool operator==(const SuccessorInfo &Other) const {
78     return Index == Other.Index;
79   }
80   bool operator!=(const SuccessorInfo &Other) const {
81     return !(*this == Other);
82   }
83 };
84 } // end namespace bolt
85 
86 template <> struct MappingTraits<bolt::SuccessorInfo> {
87   static void mapping(IO &YamlIO, bolt::SuccessorInfo &SI) {
88     YamlIO.mapRequired("bid", SI.Index);
89     YamlIO.mapRequired("cnt", SI.Count);
90     YamlIO.mapOptional("mis", SI.Mispreds, (uint64_t)0);
91   }
92 
93   static const bool flow = true;
94 };
95 
96 namespace bolt {
97 struct PseudoProbeInfo {
98   uint32_t InlineTreeIndex = 0;
99   uint64_t BlockMask = 0;            // bitset with probe indices from 1 to 64
100   std::vector<uint64_t> BlockProbes; // block probes with indices above 64
101   std::vector<uint64_t> CallProbes;
102   std::vector<uint64_t> IndCallProbes;
103   std::vector<uint32_t> InlineTreeNodes;
104 
105   bool operator==(const PseudoProbeInfo &Other) const {
106     return InlineTreeIndex == Other.InlineTreeIndex &&
107            BlockProbes == Other.BlockProbes && CallProbes == Other.CallProbes &&
108            IndCallProbes == Other.IndCallProbes;
109   }
110 };
111 } // end namespace bolt
112 
113 template <> struct MappingTraits<bolt::PseudoProbeInfo> {
114   static void mapping(IO &YamlIO, bolt::PseudoProbeInfo &PI) {
115     YamlIO.mapOptional("blx", PI.BlockMask, 0);
116     YamlIO.mapOptional("blk", PI.BlockProbes, std::vector<uint64_t>());
117     YamlIO.mapOptional("call", PI.CallProbes, std::vector<uint64_t>());
118     YamlIO.mapOptional("icall", PI.IndCallProbes, std::vector<uint64_t>());
119     YamlIO.mapOptional("id", PI.InlineTreeIndex, 0);
120     YamlIO.mapOptional("ids", PI.InlineTreeNodes, std::vector<uint32_t>());
121   }
122 
123   static const bool flow = true;
124 };
125 } // end namespace yaml
126 } // end namespace llvm
127 
128 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::CallSiteInfo)
129 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::SuccessorInfo)
130 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::PseudoProbeInfo)
131 
132 namespace llvm {
133 namespace yaml {
134 
135 namespace bolt {
136 struct BinaryBasicBlockProfile {
137   uint32_t Index{0};
138   uint32_t NumInstructions{0};
139   llvm::yaml::Hex64 Hash{0};
140   uint64_t ExecCount{0};
141   uint64_t EventCount{0};
142   std::vector<CallSiteInfo> CallSites;
143   std::vector<SuccessorInfo> Successors;
144   std::vector<PseudoProbeInfo> PseudoProbes;
145 
146   bool operator==(const BinaryBasicBlockProfile &Other) const {
147     return Index == Other.Index;
148   }
149   bool operator!=(const BinaryBasicBlockProfile &Other) const {
150     return !(*this == Other);
151   }
152 };
153 } // end namespace bolt
154 
155 template <> struct MappingTraits<bolt::BinaryBasicBlockProfile> {
156   static void mapping(IO &YamlIO, bolt::BinaryBasicBlockProfile &BBP) {
157     YamlIO.mapRequired("bid", BBP.Index);
158     YamlIO.mapRequired("insns", BBP.NumInstructions);
159     YamlIO.mapOptional("hash", BBP.Hash, (llvm::yaml::Hex64)0);
160     YamlIO.mapOptional("exec", BBP.ExecCount, (uint64_t)0);
161     YamlIO.mapOptional("events", BBP.EventCount, (uint64_t)0);
162     YamlIO.mapOptional("calls", BBP.CallSites,
163                        std::vector<bolt::CallSiteInfo>());
164     YamlIO.mapOptional("succ", BBP.Successors,
165                        std::vector<bolt::SuccessorInfo>());
166     YamlIO.mapOptional("probes", BBP.PseudoProbes,
167                        std::vector<bolt::PseudoProbeInfo>());
168   }
169 };
170 
171 namespace bolt {
172 struct InlineTreeNode {
173   uint32_t ParentIndexDelta;
174   uint32_t CallSiteProbe;
175   // Index in PseudoProbeDesc.GUID, UINT32_MAX for same as previous (omitted)
176   uint32_t GUIDIndex;
177   // Decoded contents, ParentIndexDelta becomes absolute value.
178   uint64_t GUID;
179   uint64_t Hash;
180   bool operator==(const InlineTreeNode &) const { return false; }
181 };
182 } // end namespace bolt
183 
184 template <> struct MappingTraits<bolt::InlineTreeNode> {
185   static void mapping(IO &YamlIO, bolt::InlineTreeNode &ITI) {
186     YamlIO.mapOptional("g", ITI.GUIDIndex, UINT32_MAX);
187     YamlIO.mapOptional("p", ITI.ParentIndexDelta, 0);
188     YamlIO.mapOptional("cs", ITI.CallSiteProbe, 0);
189   }
190 
191   static const bool flow = true;
192 };
193 } // end namespace yaml
194 } // end namespace llvm
195 
196 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryBasicBlockProfile)
197 LLVM_YAML_IS_FLOW_SEQUENCE_VECTOR(llvm::yaml::bolt::InlineTreeNode)
198 
199 namespace llvm {
200 namespace yaml {
201 
202 namespace bolt {
203 struct BinaryFunctionProfile {
204   std::string Name;
205   uint32_t NumBasicBlocks{0};
206   uint32_t Id{0};
207   llvm::yaml::Hex64 Hash{0};
208   uint64_t ExecCount{0};
209   std::vector<BinaryBasicBlockProfile> Blocks;
210   std::vector<InlineTreeNode> InlineTree;
211   bool Used{false};
212 };
213 } // end namespace bolt
214 
215 template <> struct MappingTraits<bolt::BinaryFunctionProfile> {
216   static void mapping(IO &YamlIO, bolt::BinaryFunctionProfile &BFP) {
217     YamlIO.mapRequired("name", BFP.Name);
218     YamlIO.mapRequired("fid", BFP.Id);
219     YamlIO.mapRequired("hash", BFP.Hash);
220     YamlIO.mapRequired("exec", BFP.ExecCount);
221     YamlIO.mapRequired("nblocks", BFP.NumBasicBlocks);
222     YamlIO.mapOptional("blocks", BFP.Blocks,
223                        std::vector<bolt::BinaryBasicBlockProfile>());
224     YamlIO.mapOptional("inline_tree", BFP.InlineTree,
225                        std::vector<bolt::InlineTreeNode>());
226   }
227 };
228 
229 LLVM_YAML_STRONG_TYPEDEF(uint16_t, PROFILE_PF)
230 
231 template <> struct ScalarBitSetTraits<PROFILE_PF> {
232   static void bitset(IO &io, PROFILE_PF &value) {
233     io.bitSetCase(value, "lbr", BinaryFunction::PF_LBR);
234     io.bitSetCase(value, "sample", BinaryFunction::PF_SAMPLE);
235     io.bitSetCase(value, "memevent", BinaryFunction::PF_MEMEVENT);
236   }
237 };
238 
239 template <> struct ScalarEnumerationTraits<llvm::bolt::HashFunction> {
240   using HashFunction = llvm::bolt::HashFunction;
241   static void enumeration(IO &io, HashFunction &value) {
242     io.enumCase(value, "std-hash", HashFunction::StdHash);
243     io.enumCase(value, "xxh3", HashFunction::XXH3);
244   }
245 };
246 
247 namespace bolt {
248 struct BinaryProfileHeader {
249   uint32_t Version{1};
250   std::string FileName; // Name of the profiled binary.
251   std::string Id;       // BuildID.
252   PROFILE_PF Flags{BinaryFunction::PF_NONE};
253   // Type of the profile.
254   std::string Origin;     // How the profile was obtained.
255   std::string EventNames; // Events used for sample profile.
256   bool IsDFSOrder{true};  // Whether using DFS block order in function profile
257   llvm::bolt::HashFunction HashFunction; // Hash used for BB/BF hashing
258 };
259 } // end namespace bolt
260 
261 template <> struct MappingTraits<bolt::BinaryProfileHeader> {
262   static void mapping(IO &YamlIO, bolt::BinaryProfileHeader &Header) {
263     YamlIO.mapRequired("profile-version", Header.Version);
264     YamlIO.mapRequired("binary-name", Header.FileName);
265     YamlIO.mapOptional("binary-build-id", Header.Id);
266     YamlIO.mapRequired("profile-flags", Header.Flags);
267     YamlIO.mapOptional("profile-origin", Header.Origin);
268     YamlIO.mapOptional("profile-events", Header.EventNames);
269     YamlIO.mapOptional("dfs-order", Header.IsDFSOrder);
270     YamlIO.mapOptional("hash-func", Header.HashFunction,
271                        llvm::bolt::HashFunction::StdHash);
272   }
273 };
274 
275 namespace bolt {
276 struct ProfilePseudoProbeDesc {
277   std::vector<Hex64> GUID;
278   std::vector<Hex64> Hash;
279   std::vector<uint32_t> GUIDHashIdx; // Index of hash for that GUID in Hash
280 
281   bool operator==(const ProfilePseudoProbeDesc &Other) const {
282     // Only treat empty Desc as equal
283     return GUID.empty() && Other.GUID.empty() && Hash.empty() &&
284            Other.Hash.empty() && GUIDHashIdx.empty() &&
285            Other.GUIDHashIdx.empty();
286   }
287 };
288 } // end namespace bolt
289 
290 template <> struct MappingTraits<bolt::ProfilePseudoProbeDesc> {
291   static void mapping(IO &YamlIO, bolt::ProfilePseudoProbeDesc &PD) {
292     YamlIO.mapRequired("gs", PD.GUID);
293     YamlIO.mapRequired("gh", PD.GUIDHashIdx);
294     YamlIO.mapRequired("hs", PD.Hash);
295   }
296 };
297 } // end namespace yaml
298 } // end namespace llvm
299 
300 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::BinaryFunctionProfile)
301 LLVM_YAML_IS_SEQUENCE_VECTOR(llvm::yaml::bolt::ProfilePseudoProbeDesc)
302 
303 namespace llvm {
304 namespace yaml {
305 
306 namespace bolt {
307 struct BinaryProfile {
308   BinaryProfileHeader Header;
309   std::vector<BinaryFunctionProfile> Functions;
310   ProfilePseudoProbeDesc PseudoProbeDesc;
311 };
312 } // namespace bolt
313 
314 template <> struct MappingTraits<bolt::BinaryProfile> {
315   static void mapping(IO &YamlIO, bolt::BinaryProfile &BP) {
316     YamlIO.mapRequired("header", BP.Header);
317     YamlIO.mapRequired("functions", BP.Functions);
318     YamlIO.mapOptional("pseudo_probe_desc", BP.PseudoProbeDesc,
319                        bolt::ProfilePseudoProbeDesc());
320   }
321 };
322 
323 } // end namespace yaml
324 } // end namespace llvm
325 
326 #endif
327