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
18using namespace llvm;
19using namespace llvm::remarks;
20
21BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
22 BitstreamRemarkContainerType ContainerType)
23 : Bitstream(Encoded), ContainerType(ContainerType) {}
24
25static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
26 append_range(C&: R, R&: Str);
27}
28
29static 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
37static 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
48void BitstreamRemarkSerializerHelper::setupMetaBlockInfo() {
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
64void BitstreamRemarkSerializerHelper::setupMetaRemarkVersion() {
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
75void BitstreamRemarkSerializerHelper::emitMetaRemarkVersion(
76 uint64_t RemarkVersion) {
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
84void BitstreamRemarkSerializerHelper::setupMetaStrTab() {
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
94void BitstreamRemarkSerializerHelper::emitMetaStrTab(
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
108void BitstreamRemarkSerializerHelper::setupMetaExternalFile() {
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
118void BitstreamRemarkSerializerHelper::emitMetaExternalFile(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
125void BitstreamRemarkSerializerHelper::setupRemarkBlockInfo() {
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
197void BitstreamRemarkSerializerHelper::setupBlockInfo() {
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
234void BitstreamRemarkSerializerHelper::emitMetaBlock(
235 uint64_t ContainerVersion, std::optional<uint64_t> RemarkVersion,
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
270void BitstreamRemarkSerializerHelper::emitRemarkBlock(const Remark &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
320void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
321 OS.write(Ptr: Encoded.data(), Size: Encoded.size());
322 Encoded.clear();
323}
324
325StringRef BitstreamRemarkSerializerHelper::getBuffer() {
326 return StringRef(Encoded.data(), Encoded.size());
327}
328
329BitstreamRemarkSerializer::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
340BitstreamRemarkSerializer::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
350void BitstreamRemarkSerializer::emit(const Remark &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
371std::unique_ptr<MetaSerializer> BitstreamRemarkSerializer::metaSerializer(
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
384void BitstreamMetaSerializer::emit() {
385 Helper->setupBlockInfo();
386 Helper->emitMetaBlock(ContainerVersion: CurrentContainerVersion, RemarkVersion: CurrentRemarkVersion, StrTab,
387 Filename: ExternalFilename);
388 Helper->flushToStream(OS);
389}
390