1//===----------------------------------------------------------------------===//
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/// \file
10/// This file defines the YAMLIO mappings for the format-agnostic BB address
11/// map YAML types declared in BBAddrMapYAML.h.
12///
13//===----------------------------------------------------------------------===//
14
15#include "llvm/ObjectYAML/BBAddrMapYAML.h"
16#include "llvm/Object/BBAddrMap.h"
17#include "llvm/ObjectYAML/ContiguousBlobAccumulator.h"
18#include "llvm/Support/Errc.h"
19#include "llvm/Support/WithColor.h"
20
21namespace llvm {
22namespace yaml {
23
24void MappingTraits<BBAddrMapYAML::BBAddrMapEntry>::mapping(
25 IO &IO, BBAddrMapYAML::BBAddrMapEntry &E) {
26 assert(IO.getContext() && "The IO context is not initialized");
27 IO.mapRequired(Key: "Version", Val&: E.Version);
28 IO.mapOptional(Key: "Feature", Val&: E.Feature, Default: Hex16(0));
29 IO.mapOptional(Key: "FuncGUID", Val&: E.FuncGUID);
30 IO.mapOptional(Key: "NumBBRanges", Val&: E.NumBBRanges);
31 IO.mapOptional(Key: "BBRanges", Val&: E.BBRanges);
32}
33
34void MappingTraits<BBAddrMapYAML::BBAddrMapEntry::BBRangeEntry>::mapping(
35 IO &IO, BBAddrMapYAML::BBAddrMapEntry::BBRangeEntry &E) {
36 IO.mapOptional(Key: "BaseAddress", Val&: E.BaseAddress, Default: Hex64(0));
37 IO.mapOptional(Key: "NumBlocks", Val&: E.NumBlocks);
38 IO.mapOptional(Key: "BBEntries", Val&: E.BBEntries);
39}
40
41void MappingTraits<BBAddrMapYAML::BBAddrMapEntry::BBEntry>::mapping(
42 IO &IO, BBAddrMapYAML::BBAddrMapEntry::BBEntry &E) {
43 assert(IO.getContext() && "The IO context is not initialized");
44 IO.mapOptional(Key: "ID", Val&: E.ID);
45 IO.mapRequired(Key: "AddressOffset", Val&: E.AddressOffset);
46 IO.mapRequired(Key: "Size", Val&: E.Size);
47 IO.mapRequired(Key: "Metadata", Val&: E.Metadata);
48 IO.mapOptional(Key: "CallsiteEndOffsets", Val&: E.CallsiteEndOffsets);
49 IO.mapOptional(Key: "Hash", Val&: E.Hash);
50}
51
52void MappingTraits<BBAddrMapYAML::PGOAnalysisMapEntry>::mapping(
53 IO &IO, BBAddrMapYAML::PGOAnalysisMapEntry &E) {
54 assert(IO.getContext() && "The IO context is not initialized");
55 IO.mapOptional(Key: "FuncEntryCount", Val&: E.FuncEntryCount);
56 IO.mapOptional(Key: "PGOBBEntries", Val&: E.PGOBBEntries);
57}
58
59void MappingTraits<BBAddrMapYAML::PGOAnalysisMapEntry::PGOBBEntry>::mapping(
60 IO &IO, BBAddrMapYAML::PGOAnalysisMapEntry::PGOBBEntry &E) {
61 assert(IO.getContext() && "The IO context is not initialized");
62 IO.mapOptional(Key: "BBFreq", Val&: E.BBFreq);
63 IO.mapOptional(Key: "PostLinkBBFreq", Val&: E.PostLinkBBFreq);
64 IO.mapOptional(Key: "Successors", Val&: E.Successors);
65}
66
67void MappingTraits<
68 BBAddrMapYAML::PGOAnalysisMapEntry::PGOBBEntry::SuccessorEntry>::
69 mapping(IO &IO,
70 BBAddrMapYAML::PGOAnalysisMapEntry::PGOBBEntry::SuccessorEntry &E) {
71 assert(IO.getContext() && "The IO context is not initialized");
72 IO.mapRequired(Key: "ID", Val&: E.ID);
73 IO.mapRequired(Key: "BrProb", Val&: E.BrProb);
74 IO.mapOptional(Key: "PostLinkBrFreq", Val&: E.PostLinkBrFreq);
75}
76
77} // end namespace yaml
78
79namespace BBAddrMapYAML {
80
81void encodePayload(ArrayRef<BBAddrMapEntry> Entries,
82 const std::vector<PGOAnalysisMapEntry> *PGOAnalyses,
83 yaml::ContiguousBlobAccumulator &CBA,
84 llvm::endianness Endian, unsigned AddressSize) {
85 assert((AddressSize == 4 || AddressSize == 8) && "invalid address size");
86 for (const auto &[Idx, E] : llvm::enumerate(First&: Entries)) {
87 // Write version and feature values.
88 if (E.Version > 6)
89 WithColor::warning() << "unsupported BB address map version: "
90 << static_cast<int>(E.Version)
91 << "; encoding using the most recent version";
92 CBA.write(C: E.Version);
93 if (E.Version < 5)
94 CBA.write(C: static_cast<uint8_t>(E.Feature));
95 else
96 CBA.write<uint16_t>(Val: E.Feature, E: Endian);
97 auto FeatureOrErr = llvm::object::BBAddrMap::Features::decode(Val: E.Feature);
98 if (!FeatureOrErr) {
99 // Invalid feature: warn and skip the entry.
100 WithColor::warning() << toString(E: FeatureOrErr.takeError());
101 continue;
102 }
103 // Write the function GUID. This is emitted when either the feature is
104 // enabled or 'FuncGUID' is specified, which allows testing mismatches.
105 if (!FeatureOrErr->FuncGUID && E.FuncGUID.has_value())
106 WithColor::warning() << "feature value(" << E.Feature
107 << ") does not support function GUID.";
108 if (FeatureOrErr->FuncGUID || E.FuncGUID.has_value())
109 CBA.write<uint64_t>(Val: E.FuncGUID.value_or(u: llvm::yaml::Hex64(0)), E: Endian);
110 bool MultiBBRangeFeatureEnabled = FeatureOrErr->MultiBBRange;
111 bool MultiBBRange =
112 MultiBBRangeFeatureEnabled ||
113 (E.NumBBRanges.has_value() && E.NumBBRanges.value() != 1) ||
114 (E.BBRanges && E.BBRanges->size() != 1);
115 if (MultiBBRange && !MultiBBRangeFeatureEnabled)
116 WithColor::warning() << "feature value(" << E.Feature
117 << ") does not support multiple BB ranges.";
118 if (MultiBBRange) {
119 // Write the number of basic block ranges, which is overridden by the
120 // 'NumBBRanges' field when specified.
121 uint64_t NumBBRanges =
122 E.NumBBRanges.value_or(u: E.BBRanges ? E.BBRanges->size() : 0);
123 CBA.writeULEB128(Val: NumBBRanges);
124 }
125 if (!E.BBRanges)
126 continue;
127 uint64_t TotalNumBlocks = 0;
128 bool EmitCallsiteEndOffsets =
129 FeatureOrErr->CallsiteEndOffsets || E.hasAnyCallsiteEndOffsets();
130 for (const BBAddrMapEntry::BBRangeEntry &BBR : *E.BBRanges) {
131 // Write the pointer-sized base address of the range.
132 if (AddressSize == 8)
133 CBA.write<uint64_t>(Val: BBR.BaseAddress, E: Endian);
134 else
135 CBA.write<uint32_t>(Val: static_cast<uint32_t>(BBR.BaseAddress), E: Endian);
136 // Write number of BBEntries (number of basic blocks in this basic block
137 // range). This is overridden by the 'NumBlocks' YAML field when
138 // specified.
139 uint64_t NumBlocks =
140 BBR.NumBlocks.value_or(u: BBR.BBEntries ? BBR.BBEntries->size() : 0);
141 CBA.writeULEB128(Val: NumBlocks);
142 // Write all BBEntries in this BBRange.
143 if (!BBR.BBEntries || FeatureOrErr->OmitBBEntries)
144 continue;
145 for (const BBAddrMapEntry::BBEntry &BBE : *BBR.BBEntries) {
146 ++TotalNumBlocks;
147 if (E.Version > 1)
148 CBA.writeULEB128(Val: BBE.ID);
149 CBA.writeULEB128(Val: BBE.AddressOffset);
150 if (EmitCallsiteEndOffsets) {
151 size_t NumCallsiteEndOffsets =
152 BBE.CallsiteEndOffsets ? BBE.CallsiteEndOffsets->size() : 0;
153 CBA.writeULEB128(Val: NumCallsiteEndOffsets);
154 if (BBE.CallsiteEndOffsets) {
155 for (uint32_t Offset : *BBE.CallsiteEndOffsets)
156 CBA.writeULEB128(Val: Offset);
157 }
158 }
159 CBA.writeULEB128(Val: BBE.Size);
160 CBA.writeULEB128(Val: BBE.Metadata);
161 if (FeatureOrErr->BBHash || BBE.Hash.has_value()) {
162 uint64_t Hash =
163 BBE.Hash.has_value() ? BBE.Hash.value() : llvm::yaml::Hex64(0);
164 CBA.write<uint64_t>(Val: Hash, E: Endian);
165 }
166 }
167 }
168 if (!PGOAnalyses)
169 continue;
170 const PGOAnalysisMapEntry &PGOEntry = PGOAnalyses->at(n: Idx);
171
172 if (PGOEntry.FuncEntryCount)
173 CBA.writeULEB128(Val: *PGOEntry.FuncEntryCount);
174
175 if (!PGOEntry.PGOBBEntries)
176 continue;
177
178 const auto &PGOBBEntries = PGOEntry.PGOBBEntries.value();
179 if (TotalNumBlocks != PGOBBEntries.size()) {
180 WithColor::warning() << "PGOBBEntries must be the same length as "
181 "BBEntries in the BB address map.\n"
182 << "Mismatch on function with address: "
183 << E.getFunctionAddress();
184 continue;
185 }
186
187 for (const auto &PGOBBE : PGOBBEntries) {
188 if (PGOBBE.BBFreq)
189 CBA.writeULEB128(Val: *PGOBBE.BBFreq);
190 if (FeatureOrErr->PostLinkCfg || PGOBBE.PostLinkBBFreq.has_value())
191 CBA.writeULEB128(Val: PGOBBE.PostLinkBBFreq.value_or(u: 0));
192 if (PGOBBE.Successors) {
193 CBA.writeULEB128(Val: PGOBBE.Successors->size());
194 for (const auto &[ID, BrProb, PostLinkBrFreq] : *PGOBBE.Successors) {
195 CBA.writeULEB128(Val: ID);
196 CBA.writeULEB128(Val: BrProb);
197 if (FeatureOrErr->PostLinkCfg || PostLinkBrFreq.has_value())
198 CBA.writeULEB128(Val: PostLinkBrFreq.value_or(u: 0));
199 }
200 }
201 }
202 }
203}
204
205Error decodePayload(
206 ArrayRef<uint8_t> Content, llvm::endianness Endian, unsigned AddressSize,
207 std::optional<std::vector<BBAddrMapEntry>> &Entries,
208 std::optional<std::vector<PGOAnalysisMapEntry>> &PGOAnalyses) {
209 assert((AddressSize == 4 || AddressSize == 8) && "invalid address size");
210 DataExtractor Data(Content, Endian == llvm::endianness::little);
211
212 std::vector<BBAddrMapEntry> DecodedEntries;
213 bool HasAnyPGOAnalysisMapEntry = false;
214 std::vector<PGOAnalysisMapEntry> DecodedPGOAnalyses;
215 DataExtractor::Cursor Cur(0);
216 uint8_t Version = 0;
217 uint16_t Feature = 0;
218 uint64_t Address = 0;
219 while (Cur && Cur.tell() < Content.size()) {
220 Version = Data.getU8(C&: Cur);
221 if (Cur && Version > 6)
222 return createStringError(
223 EC: errc::invalid_argument,
224 S: "invalid SHT_LLVM_BB_ADDR_MAP section version: " +
225 Twine(static_cast<int>(Version)));
226 Feature = Version < 5 ? Data.getU8(C&: Cur) : Data.getU16(C&: Cur);
227 uint64_t NumBBRanges = 1;
228 uint64_t NumBlocks = 0;
229 uint32_t TotalNumBlocks = 0;
230 auto FeatureOrErr = llvm::object::BBAddrMap::Features::decode(Val: Feature);
231 if (!FeatureOrErr)
232 return FeatureOrErr.takeError();
233 std::optional<llvm::yaml::Hex64> FuncGUID;
234 if (FeatureOrErr->FuncGUID)
235 FuncGUID = Data.getU64(C&: Cur);
236 if (FeatureOrErr->MultiBBRange) {
237 NumBBRanges = Data.getULEB128(C&: Cur);
238 } else {
239 Address = Data.getUnsigned(C&: Cur, Size: AddressSize);
240 NumBlocks = Data.getULEB128(C&: Cur);
241 }
242 std::vector<BBAddrMapEntry::BBRangeEntry> BBRanges;
243 uint64_t BaseAddress = 0;
244 for (uint64_t BBRangeN = 0; Cur && BBRangeN != NumBBRanges; ++BBRangeN) {
245 if (FeatureOrErr->MultiBBRange) {
246 BaseAddress = Data.getUnsigned(C&: Cur, Size: AddressSize);
247 NumBlocks = Data.getULEB128(C&: Cur);
248 } else {
249 BaseAddress = Address;
250 }
251
252 std::vector<BBAddrMapEntry::BBEntry> BBEntries;
253 // Read the specified number of BB entries, or until decoding fails.
254 for (uint64_t BlockIndex = 0; Cur && BlockIndex < NumBlocks;
255 ++BlockIndex) {
256 uint32_t ID = Version >= 2 ? Data.getULEB128(C&: Cur) : BlockIndex;
257 uint64_t Offset = Data.getULEB128(C&: Cur);
258 std::optional<std::vector<llvm::yaml::Hex64>> CallsiteEndOffsets;
259 if (FeatureOrErr->CallsiteEndOffsets) {
260 uint32_t NumCallsites = Data.getULEB128(C&: Cur);
261 CallsiteEndOffsets = std::vector<llvm::yaml::Hex64>(NumCallsites, 0);
262 for (uint32_t CallsiteIndex = 0; Cur && CallsiteIndex < NumCallsites;
263 ++CallsiteIndex) {
264 (*CallsiteEndOffsets)[CallsiteIndex] = Data.getULEB128(C&: Cur);
265 }
266 }
267 uint64_t Size = Data.getULEB128(C&: Cur);
268 uint64_t Metadata = Data.getULEB128(C&: Cur);
269 std::optional<llvm::yaml::Hex64> Hash;
270 if (FeatureOrErr->BBHash)
271 Hash = Data.getU64(C&: Cur);
272 BBEntries.push_back(
273 x: {.ID: ID, .AddressOffset: Offset, .Size: Size, .Metadata: Metadata, .CallsiteEndOffsets: std::move(CallsiteEndOffsets), .Hash: Hash});
274 }
275 TotalNumBlocks += BBEntries.size();
276 BBRanges.push_back(x: {.BaseAddress: BaseAddress, /*NumBlocks=*/{}, .BBEntries: BBEntries});
277 }
278 DecodedEntries.push_back(
279 x: {.Version: Version, .Feature: Feature, .FuncGUID: FuncGUID, /*NumBBRanges=*/{}, .BBRanges: std::move(BBRanges)});
280
281 PGOAnalysisMapEntry &PGOAnalysis = DecodedPGOAnalyses.emplace_back();
282 if (FeatureOrErr->hasPGOAnalysis()) {
283 HasAnyPGOAnalysisMapEntry = true;
284
285 if (FeatureOrErr->FuncEntryCount)
286 PGOAnalysis.FuncEntryCount = Data.getULEB128(C&: Cur);
287
288 if (FeatureOrErr->hasPGOAnalysisBBData()) {
289 auto &PGOBBEntries = PGOAnalysis.PGOBBEntries.emplace();
290 for (uint64_t BlockIndex = 0; Cur && BlockIndex < TotalNumBlocks;
291 ++BlockIndex) {
292 auto &PGOBBEntry = PGOBBEntries.emplace_back();
293 if (FeatureOrErr->BBFreq) {
294 PGOBBEntry.BBFreq = Data.getULEB128(C&: Cur);
295 if (FeatureOrErr->PostLinkCfg)
296 PGOBBEntry.PostLinkBBFreq = Data.getULEB128(C&: Cur);
297 if (!Cur)
298 break;
299 }
300
301 if (FeatureOrErr->BrProb) {
302 auto &SuccEntries = PGOBBEntry.Successors.emplace();
303 uint64_t SuccCount = Data.getULEB128(C&: Cur);
304 for (uint64_t SuccIdx = 0; Cur && SuccIdx < SuccCount; ++SuccIdx) {
305 uint32_t ID = Data.getULEB128(C&: Cur);
306 uint32_t BrProb = Data.getULEB128(C&: Cur);
307 std::optional<uint32_t> PostLinkBrFreq;
308 if (FeatureOrErr->PostLinkCfg)
309 PostLinkBrFreq = Data.getULEB128(C&: Cur);
310 SuccEntries.push_back(x: {.ID: ID, .BrProb: BrProb, .PostLinkBrFreq: PostLinkBrFreq});
311 }
312 }
313 }
314 }
315 }
316 }
317
318 if (!Cur) {
319 consumeError(Err: Cur.takeError());
320 return Error::success();
321 }
322 Entries = std::move(DecodedEntries);
323 if (HasAnyPGOAnalysisMapEntry)
324 PGOAnalyses = std::move(DecodedPGOAnalyses);
325 return Error::success();
326}
327
328} // end namespace BBAddrMapYAML
329} // end namespace llvm
330