1 | //===- BitstreamRemarkSerializer.cpp --------------------------------------===// |
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 | // This file provides the implementation of the LLVM bitstream remark serializer |
10 | // using LLVM's bitstream writer. |
11 | // |
12 | //===----------------------------------------------------------------------===// |
13 | |
14 | #include "llvm/Remarks/BitstreamRemarkSerializer.h" |
15 | #include "llvm/Remarks/Remark.h" |
16 | #include <optional> |
17 | |
18 | using namespace llvm; |
19 | using namespace llvm::remarks; |
20 | |
21 | BitstreamRemarkSerializerHelper::( |
22 | BitstreamRemarkContainerType ContainerType) |
23 | : Bitstream(Encoded), ContainerType(ContainerType) {} |
24 | |
25 | static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) { |
26 | append_range(C&: R, R&: Str); |
27 | } |
28 | |
29 | static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream, |
30 | SmallVectorImpl<uint64_t> &R, StringRef Str) { |
31 | R.clear(); |
32 | R.push_back(Elt: RecordID); |
33 | push(R, Str); |
34 | Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_SETRECORDNAME, Vals: R); |
35 | } |
36 | |
37 | static void initBlock(unsigned BlockID, BitstreamWriter &Bitstream, |
38 | SmallVectorImpl<uint64_t> &R, StringRef Str) { |
39 | R.clear(); |
40 | R.push_back(Elt: BlockID); |
41 | Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_SETBID, Vals: R); |
42 | |
43 | R.clear(); |
44 | push(R, Str); |
45 | Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_BLOCKNAME, Vals: R); |
46 | } |
47 | |
48 | void BitstreamRemarkSerializerHelper::() { |
49 | // Setup the metadata block. |
50 | initBlock(BlockID: META_BLOCK_ID, Bitstream, R, Str: MetaBlockName); |
51 | |
52 | // The container information. |
53 | setRecordName(RecordID: RECORD_META_CONTAINER_INFO, Bitstream, R, |
54 | Str: MetaContainerInfoName); |
55 | |
56 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
57 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_CONTAINER_INFO)); |
58 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version. |
59 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Type. |
60 | RecordMetaContainerInfoAbbrevID = |
61 | Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev); |
62 | } |
63 | |
64 | void BitstreamRemarkSerializerHelper::() { |
65 | setRecordName(RecordID: RECORD_META_REMARK_VERSION, Bitstream, R, |
66 | Str: MetaRemarkVersionName); |
67 | |
68 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
69 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_REMARK_VERSION)); |
70 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version. |
71 | RecordMetaRemarkVersionAbbrevID = |
72 | Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev); |
73 | } |
74 | |
75 | void BitstreamRemarkSerializerHelper::( |
76 | uint64_t ) { |
77 | // The remark version is emitted only if we emit remarks. |
78 | R.clear(); |
79 | R.push_back(Elt: RECORD_META_REMARK_VERSION); |
80 | R.push_back(Elt: RemarkVersion); |
81 | Bitstream.EmitRecordWithAbbrev(Abbrev: RecordMetaRemarkVersionAbbrevID, Vals: R); |
82 | } |
83 | |
84 | void BitstreamRemarkSerializerHelper::() { |
85 | setRecordName(RecordID: RECORD_META_STRTAB, Bitstream, R, Str: MetaStrTabName); |
86 | |
87 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
88 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_STRTAB)); |
89 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Raw table. |
90 | RecordMetaStrTabAbbrevID = |
91 | Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev); |
92 | } |
93 | |
94 | void BitstreamRemarkSerializerHelper::( |
95 | const StringTable &StrTab) { |
96 | // The string table is not emitted if we emit remarks separately. |
97 | R.clear(); |
98 | R.push_back(Elt: RECORD_META_STRTAB); |
99 | |
100 | // Serialize to a blob. |
101 | std::string Buf; |
102 | raw_string_ostream OS(Buf); |
103 | StrTab.serialize(OS); |
104 | StringRef Blob = OS.str(); |
105 | Bitstream.EmitRecordWithBlob(Abbrev: RecordMetaStrTabAbbrevID, Vals: R, Blob); |
106 | } |
107 | |
108 | void BitstreamRemarkSerializerHelper::() { |
109 | setRecordName(RecordID: RECORD_META_EXTERNAL_FILE, Bitstream, R, Str: MetaExternalFileName); |
110 | |
111 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
112 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_EXTERNAL_FILE)); |
113 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename. |
114 | RecordMetaExternalFileAbbrevID = |
115 | Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev); |
116 | } |
117 | |
118 | void BitstreamRemarkSerializerHelper::(StringRef Filename) { |
119 | // The external file is emitted only if we emit the separate metadata. |
120 | R.clear(); |
121 | R.push_back(Elt: RECORD_META_EXTERNAL_FILE); |
122 | Bitstream.EmitRecordWithBlob(Abbrev: RecordMetaExternalFileAbbrevID, Vals: R, Blob: Filename); |
123 | } |
124 | |
125 | void BitstreamRemarkSerializerHelper::() { |
126 | // Setup the remark block. |
127 | initBlock(BlockID: REMARK_BLOCK_ID, Bitstream, R, Str: RemarkBlockName); |
128 | |
129 | // The header of a remark. |
130 | { |
131 | setRecordName(RecordID: RECORD_REMARK_HEADER, Bitstream, R, Str: RemarkHeaderName); |
132 | |
133 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
134 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_HEADER)); |
135 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Type |
136 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Remark Name |
137 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Pass name |
138 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Function name |
139 | RecordRemarkHeaderAbbrevID = |
140 | Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev); |
141 | } |
142 | |
143 | // The location of a remark. |
144 | { |
145 | setRecordName(RecordID: RECORD_REMARK_DEBUG_LOC, Bitstream, R, Str: RemarkDebugLocName); |
146 | |
147 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
148 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_DEBUG_LOC)); |
149 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File |
150 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line |
151 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column |
152 | RecordRemarkDebugLocAbbrevID = |
153 | Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev); |
154 | } |
155 | |
156 | // The hotness of a remark. |
157 | { |
158 | setRecordName(RecordID: RECORD_REMARK_HOTNESS, Bitstream, R, Str: RemarkHotnessName); |
159 | |
160 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
161 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_HOTNESS)); |
162 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Hotness |
163 | RecordRemarkHotnessAbbrevID = |
164 | Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev); |
165 | } |
166 | |
167 | // An argument entry with a debug location attached. |
168 | { |
169 | setRecordName(RecordID: RECORD_REMARK_ARG_WITH_DEBUGLOC, Bitstream, R, |
170 | Str: RemarkArgWithDebugLocName); |
171 | |
172 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
173 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_ARG_WITH_DEBUGLOC)); |
174 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key |
175 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value |
176 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File |
177 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line |
178 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column |
179 | RecordRemarkArgWithDebugLocAbbrevID = |
180 | Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev); |
181 | } |
182 | |
183 | // An argument entry with no debug location attached. |
184 | { |
185 | setRecordName(RecordID: RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, Bitstream, R, |
186 | Str: RemarkArgWithoutDebugLocName); |
187 | |
188 | auto Abbrev = std::make_shared<BitCodeAbbrev>(); |
189 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC)); |
190 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key |
191 | Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value |
192 | RecordRemarkArgWithoutDebugLocAbbrevID = |
193 | Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev); |
194 | } |
195 | } |
196 | |
197 | void BitstreamRemarkSerializerHelper::() { |
198 | // Emit magic number. |
199 | for (const char C : ContainerMagic) |
200 | Bitstream.Emit(Val: static_cast<unsigned>(C), NumBits: 8); |
201 | |
202 | Bitstream.EnterBlockInfoBlock(); |
203 | |
204 | // Setup the main metadata. Depending on the container type, we'll setup the |
205 | // required records next. |
206 | setupMetaBlockInfo(); |
207 | |
208 | switch (ContainerType) { |
209 | case BitstreamRemarkContainerType::SeparateRemarksMeta: |
210 | // Needs a string table that the separate remark file is using. |
211 | setupMetaStrTab(); |
212 | // Needs to know where the external remarks file is. |
213 | setupMetaExternalFile(); |
214 | break; |
215 | case BitstreamRemarkContainerType::SeparateRemarksFile: |
216 | // Contains remarks: emit the version. |
217 | setupMetaRemarkVersion(); |
218 | // Contains remarks: emit the remark abbrevs. |
219 | setupRemarkBlockInfo(); |
220 | break; |
221 | case BitstreamRemarkContainerType::Standalone: |
222 | // Contains remarks: emit the version. |
223 | setupMetaRemarkVersion(); |
224 | // Needs a string table. |
225 | setupMetaStrTab(); |
226 | // Contains remarks: emit the remark abbrevs. |
227 | setupRemarkBlockInfo(); |
228 | break; |
229 | } |
230 | |
231 | Bitstream.ExitBlock(); |
232 | } |
233 | |
234 | void BitstreamRemarkSerializerHelper::( |
235 | uint64_t ContainerVersion, std::optional<uint64_t> , |
236 | std::optional<const StringTable *> StrTab, |
237 | std::optional<StringRef> Filename) { |
238 | // Emit the meta block |
239 | Bitstream.EnterSubblock(BlockID: META_BLOCK_ID, CodeLen: 3); |
240 | |
241 | // The container version and type. |
242 | R.clear(); |
243 | R.push_back(Elt: RECORD_META_CONTAINER_INFO); |
244 | R.push_back(Elt: ContainerVersion); |
245 | R.push_back(Elt: static_cast<uint64_t>(ContainerType)); |
246 | Bitstream.EmitRecordWithAbbrev(Abbrev: RecordMetaContainerInfoAbbrevID, Vals: R); |
247 | |
248 | switch (ContainerType) { |
249 | case BitstreamRemarkContainerType::SeparateRemarksMeta: |
250 | assert(StrTab != std::nullopt && *StrTab != nullptr); |
251 | emitMetaStrTab(StrTab: **StrTab); |
252 | assert(Filename != std::nullopt); |
253 | emitMetaExternalFile(Filename: *Filename); |
254 | break; |
255 | case BitstreamRemarkContainerType::SeparateRemarksFile: |
256 | assert(RemarkVersion != std::nullopt); |
257 | emitMetaRemarkVersion(RemarkVersion: *RemarkVersion); |
258 | break; |
259 | case BitstreamRemarkContainerType::Standalone: |
260 | assert(RemarkVersion != std::nullopt); |
261 | emitMetaRemarkVersion(RemarkVersion: *RemarkVersion); |
262 | assert(StrTab != std::nullopt && *StrTab != nullptr); |
263 | emitMetaStrTab(StrTab: **StrTab); |
264 | break; |
265 | } |
266 | |
267 | Bitstream.ExitBlock(); |
268 | } |
269 | |
270 | void BitstreamRemarkSerializerHelper::(const Remark &, |
271 | StringTable &StrTab) { |
272 | Bitstream.EnterSubblock(BlockID: REMARK_BLOCK_ID, CodeLen: 4); |
273 | |
274 | R.clear(); |
275 | R.push_back(Elt: RECORD_REMARK_HEADER); |
276 | R.push_back(Elt: static_cast<uint64_t>(Remark.RemarkType)); |
277 | R.push_back(Elt: StrTab.add(Str: Remark.RemarkName).first); |
278 | R.push_back(Elt: StrTab.add(Str: Remark.PassName).first); |
279 | R.push_back(Elt: StrTab.add(Str: Remark.FunctionName).first); |
280 | Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkHeaderAbbrevID, Vals: R); |
281 | |
282 | if (const std::optional<RemarkLocation> &Loc = Remark.Loc) { |
283 | R.clear(); |
284 | R.push_back(Elt: RECORD_REMARK_DEBUG_LOC); |
285 | R.push_back(Elt: StrTab.add(Str: Loc->SourceFilePath).first); |
286 | R.push_back(Elt: Loc->SourceLine); |
287 | R.push_back(Elt: Loc->SourceColumn); |
288 | Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkDebugLocAbbrevID, Vals: R); |
289 | } |
290 | |
291 | if (std::optional<uint64_t> Hotness = Remark.Hotness) { |
292 | R.clear(); |
293 | R.push_back(Elt: RECORD_REMARK_HOTNESS); |
294 | R.push_back(Elt: *Hotness); |
295 | Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkHotnessAbbrevID, Vals: R); |
296 | } |
297 | |
298 | for (const Argument &Arg : Remark.Args) { |
299 | R.clear(); |
300 | unsigned Key = StrTab.add(Str: Arg.Key).first; |
301 | unsigned Val = StrTab.add(Str: Arg.Val).first; |
302 | bool HasDebugLoc = Arg.Loc != std::nullopt; |
303 | R.push_back(Elt: HasDebugLoc ? RECORD_REMARK_ARG_WITH_DEBUGLOC |
304 | : RECORD_REMARK_ARG_WITHOUT_DEBUGLOC); |
305 | R.push_back(Elt: Key); |
306 | R.push_back(Elt: Val); |
307 | if (HasDebugLoc) { |
308 | R.push_back(Elt: StrTab.add(Str: Arg.Loc->SourceFilePath).first); |
309 | R.push_back(Elt: Arg.Loc->SourceLine); |
310 | R.push_back(Elt: Arg.Loc->SourceColumn); |
311 | } |
312 | Bitstream.EmitRecordWithAbbrev(Abbrev: HasDebugLoc |
313 | ? RecordRemarkArgWithDebugLocAbbrevID |
314 | : RecordRemarkArgWithoutDebugLocAbbrevID, |
315 | Vals: R); |
316 | } |
317 | Bitstream.ExitBlock(); |
318 | } |
319 | |
320 | void BitstreamRemarkSerializerHelper::(raw_ostream &OS) { |
321 | OS.write(Ptr: Encoded.data(), Size: Encoded.size()); |
322 | Encoded.clear(); |
323 | } |
324 | |
325 | StringRef BitstreamRemarkSerializerHelper::() { |
326 | return StringRef(Encoded.data(), Encoded.size()); |
327 | } |
328 | |
329 | BitstreamRemarkSerializer::(raw_ostream &OS, |
330 | SerializerMode Mode) |
331 | : RemarkSerializer(Format::Bitstream, OS, Mode), |
332 | Helper(BitstreamRemarkContainerType::SeparateRemarksFile) { |
333 | assert(Mode == SerializerMode::Separate && |
334 | "For SerializerMode::Standalone, a pre-filled string table needs to " |
335 | "be provided." ); |
336 | // We always use a string table with bitstream. |
337 | StrTab.emplace(); |
338 | } |
339 | |
340 | BitstreamRemarkSerializer::(raw_ostream &OS, |
341 | SerializerMode Mode, |
342 | StringTable StrTabIn) |
343 | : RemarkSerializer(Format::Bitstream, OS, Mode), |
344 | Helper(Mode == SerializerMode::Separate |
345 | ? BitstreamRemarkContainerType::SeparateRemarksFile |
346 | : BitstreamRemarkContainerType::Standalone) { |
347 | StrTab = std::move(StrTabIn); |
348 | } |
349 | |
350 | void BitstreamRemarkSerializer::(const Remark &) { |
351 | if (!DidSetUp) { |
352 | // Emit the metadata that is embedded in the remark file. |
353 | // If we're in standalone mode, serialize the string table as well. |
354 | bool IsStandalone = |
355 | Helper.ContainerType == BitstreamRemarkContainerType::Standalone; |
356 | BitstreamMetaSerializer MetaSerializer( |
357 | OS, Helper, |
358 | IsStandalone ? &*StrTab |
359 | : std::optional<const StringTable *>(std::nullopt)); |
360 | MetaSerializer.emit(); |
361 | DidSetUp = true; |
362 | } |
363 | |
364 | assert(DidSetUp && |
365 | "The Block info block and the meta block were not emitted yet." ); |
366 | Helper.emitRemarkBlock(Remark, StrTab&: *StrTab); |
367 | |
368 | Helper.flushToStream(OS); |
369 | } |
370 | |
371 | std::unique_ptr<MetaSerializer> BitstreamRemarkSerializer::( |
372 | raw_ostream &OS, std::optional<StringRef> ExternalFilename) { |
373 | assert(Helper.ContainerType != |
374 | BitstreamRemarkContainerType::SeparateRemarksMeta); |
375 | bool IsStandalone = |
376 | Helper.ContainerType == BitstreamRemarkContainerType::Standalone; |
377 | return std::make_unique<BitstreamMetaSerializer>( |
378 | args&: OS, |
379 | args: IsStandalone ? BitstreamRemarkContainerType::Standalone |
380 | : BitstreamRemarkContainerType::SeparateRemarksMeta, |
381 | args: &*StrTab, args&: ExternalFilename); |
382 | } |
383 | |
384 | void BitstreamMetaSerializer::() { |
385 | Helper->setupBlockInfo(); |
386 | Helper->emitMetaBlock(ContainerVersion: CurrentContainerVersion, RemarkVersion: CurrentRemarkVersion, StrTab, |
387 | Filename: ExternalFilename); |
388 | Helper->flushToStream(OS); |
389 | } |
390 | |