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
BitstreamRemarkSerializerHelper(BitstreamRemarkContainerType ContainerType)21 BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
22 BitstreamRemarkContainerType ContainerType)
23 : Bitstream(Encoded), ContainerType(ContainerType) {}
24
push(SmallVectorImpl<uint64_t> & R,StringRef Str)25 static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
26 append_range(R, Str);
27 }
28
setRecordName(unsigned RecordID,BitstreamWriter & Bitstream,SmallVectorImpl<uint64_t> & R,StringRef Str)29 static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream,
30 SmallVectorImpl<uint64_t> &R, StringRef Str) {
31 R.clear();
32 R.push_back(RecordID);
33 push(R, Str);
34 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, R);
35 }
36
initBlock(unsigned BlockID,BitstreamWriter & Bitstream,SmallVectorImpl<uint64_t> & R,StringRef Str)37 static void initBlock(unsigned BlockID, BitstreamWriter &Bitstream,
38 SmallVectorImpl<uint64_t> &R, StringRef Str) {
39 R.clear();
40 R.push_back(BlockID);
41 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, R);
42
43 R.clear();
44 push(R, Str);
45 Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, R);
46 }
47
setupMetaBlockInfo()48 void BitstreamRemarkSerializerHelper::setupMetaBlockInfo() {
49 // Setup the metadata block.
50 initBlock(META_BLOCK_ID, Bitstream, R, MetaBlockName);
51
52 // The container information.
53 setRecordName(RECORD_META_CONTAINER_INFO, Bitstream, R,
54 MetaContainerInfoName);
55
56 auto Abbrev = std::make_shared<BitCodeAbbrev>();
57 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_CONTAINER_INFO));
58 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
59 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Type.
60 RecordMetaContainerInfoAbbrevID =
61 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
62 }
63
setupMetaRemarkVersion()64 void BitstreamRemarkSerializerHelper::setupMetaRemarkVersion() {
65 setRecordName(RECORD_META_REMARK_VERSION, Bitstream, R,
66 MetaRemarkVersionName);
67
68 auto Abbrev = std::make_shared<BitCodeAbbrev>();
69 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_REMARK_VERSION));
70 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
71 RecordMetaRemarkVersionAbbrevID =
72 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
73 }
74
emitMetaRemarkVersion(uint64_t RemarkVersion)75 void BitstreamRemarkSerializerHelper::emitMetaRemarkVersion(
76 uint64_t RemarkVersion) {
77 // The remark version is emitted only if we emit remarks.
78 R.clear();
79 R.push_back(RECORD_META_REMARK_VERSION);
80 R.push_back(RemarkVersion);
81 Bitstream.EmitRecordWithAbbrev(RecordMetaRemarkVersionAbbrevID, R);
82 }
83
setupMetaStrTab()84 void BitstreamRemarkSerializerHelper::setupMetaStrTab() {
85 setRecordName(RECORD_META_STRTAB, Bitstream, R, MetaStrTabName);
86
87 auto Abbrev = std::make_shared<BitCodeAbbrev>();
88 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_STRTAB));
89 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Raw table.
90 RecordMetaStrTabAbbrevID =
91 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
92 }
93
emitMetaStrTab(const StringTable & StrTab)94 void 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(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(RecordMetaStrTabAbbrevID, R, Blob);
106 }
107
setupMetaExternalFile()108 void BitstreamRemarkSerializerHelper::setupMetaExternalFile() {
109 setRecordName(RECORD_META_EXTERNAL_FILE, Bitstream, R, MetaExternalFileName);
110
111 auto Abbrev = std::make_shared<BitCodeAbbrev>();
112 Abbrev->Add(BitCodeAbbrevOp(RECORD_META_EXTERNAL_FILE));
113 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename.
114 RecordMetaExternalFileAbbrevID =
115 Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
116 }
117
emitMetaExternalFile(StringRef Filename)118 void BitstreamRemarkSerializerHelper::emitMetaExternalFile(StringRef Filename) {
119 // The external file is emitted only if we emit the separate metadata.
120 R.clear();
121 R.push_back(RECORD_META_EXTERNAL_FILE);
122 Bitstream.EmitRecordWithBlob(RecordMetaExternalFileAbbrevID, R, Filename);
123 }
124
setupRemarkBlockInfo()125 void BitstreamRemarkSerializerHelper::setupRemarkBlockInfo() {
126 // Setup the remark block.
127 initBlock(REMARK_BLOCK_ID, Bitstream, R, RemarkBlockName);
128
129 // The header of a remark.
130 {
131 setRecordName(RECORD_REMARK_HEADER, Bitstream, R, RemarkHeaderName);
132
133 auto Abbrev = std::make_shared<BitCodeAbbrev>();
134 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HEADER));
135 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Type
136 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Remark Name
137 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Pass name
138 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Function name
139 RecordRemarkHeaderAbbrevID =
140 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
141 }
142
143 // The location of a remark.
144 {
145 setRecordName(RECORD_REMARK_DEBUG_LOC, Bitstream, R, RemarkDebugLocName);
146
147 auto Abbrev = std::make_shared<BitCodeAbbrev>();
148 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_DEBUG_LOC));
149 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
150 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
151 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
152 RecordRemarkDebugLocAbbrevID =
153 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
154 }
155
156 // The hotness of a remark.
157 {
158 setRecordName(RECORD_REMARK_HOTNESS, Bitstream, R, RemarkHotnessName);
159
160 auto Abbrev = std::make_shared<BitCodeAbbrev>();
161 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HOTNESS));
162 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Hotness
163 RecordRemarkHotnessAbbrevID =
164 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
165 }
166
167 // An argument entry with a debug location attached.
168 {
169 setRecordName(RECORD_REMARK_ARG_WITH_DEBUGLOC, Bitstream, R,
170 RemarkArgWithDebugLocName);
171
172 auto Abbrev = std::make_shared<BitCodeAbbrev>();
173 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITH_DEBUGLOC));
174 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
175 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
176 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
177 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
178 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
179 RecordRemarkArgWithDebugLocAbbrevID =
180 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
181 }
182
183 // An argument entry with no debug location attached.
184 {
185 setRecordName(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, Bitstream, R,
186 RemarkArgWithoutDebugLocName);
187
188 auto Abbrev = std::make_shared<BitCodeAbbrev>();
189 Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC));
190 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
191 Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
192 RecordRemarkArgWithoutDebugLocAbbrevID =
193 Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
194 }
195 }
196
setupBlockInfo()197 void BitstreamRemarkSerializerHelper::setupBlockInfo() {
198 // Emit magic number.
199 for (const char C : ContainerMagic)
200 Bitstream.Emit(static_cast<unsigned>(C), 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
emitMetaBlock(uint64_t ContainerVersion,std::optional<uint64_t> RemarkVersion,std::optional<const StringTable * > StrTab,std::optional<StringRef> Filename)234 void 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(META_BLOCK_ID, 3);
240
241 // The container version and type.
242 R.clear();
243 R.push_back(RECORD_META_CONTAINER_INFO);
244 R.push_back(ContainerVersion);
245 R.push_back(static_cast<uint64_t>(ContainerType));
246 Bitstream.EmitRecordWithAbbrev(RecordMetaContainerInfoAbbrevID, R);
247
248 switch (ContainerType) {
249 case BitstreamRemarkContainerType::SeparateRemarksMeta:
250 assert(StrTab != std::nullopt && *StrTab != nullptr);
251 emitMetaStrTab(**StrTab);
252 assert(Filename != std::nullopt);
253 emitMetaExternalFile(*Filename);
254 break;
255 case BitstreamRemarkContainerType::SeparateRemarksFile:
256 assert(RemarkVersion != std::nullopt);
257 emitMetaRemarkVersion(*RemarkVersion);
258 break;
259 case BitstreamRemarkContainerType::Standalone:
260 assert(RemarkVersion != std::nullopt);
261 emitMetaRemarkVersion(*RemarkVersion);
262 assert(StrTab != std::nullopt && *StrTab != nullptr);
263 emitMetaStrTab(**StrTab);
264 break;
265 }
266
267 Bitstream.ExitBlock();
268 }
269
emitRemarkBlock(const Remark & Remark,StringTable & StrTab)270 void BitstreamRemarkSerializerHelper::emitRemarkBlock(const Remark &Remark,
271 StringTable &StrTab) {
272 Bitstream.EnterSubblock(REMARK_BLOCK_ID, 4);
273
274 R.clear();
275 R.push_back(RECORD_REMARK_HEADER);
276 R.push_back(static_cast<uint64_t>(Remark.RemarkType));
277 R.push_back(StrTab.add(Remark.RemarkName).first);
278 R.push_back(StrTab.add(Remark.PassName).first);
279 R.push_back(StrTab.add(Remark.FunctionName).first);
280 Bitstream.EmitRecordWithAbbrev(RecordRemarkHeaderAbbrevID, R);
281
282 if (const std::optional<RemarkLocation> &Loc = Remark.Loc) {
283 R.clear();
284 R.push_back(RECORD_REMARK_DEBUG_LOC);
285 R.push_back(StrTab.add(Loc->SourceFilePath).first);
286 R.push_back(Loc->SourceLine);
287 R.push_back(Loc->SourceColumn);
288 Bitstream.EmitRecordWithAbbrev(RecordRemarkDebugLocAbbrevID, R);
289 }
290
291 if (std::optional<uint64_t> Hotness = Remark.Hotness) {
292 R.clear();
293 R.push_back(RECORD_REMARK_HOTNESS);
294 R.push_back(*Hotness);
295 Bitstream.EmitRecordWithAbbrev(RecordRemarkHotnessAbbrevID, R);
296 }
297
298 for (const Argument &Arg : Remark.Args) {
299 R.clear();
300 unsigned Key = StrTab.add(Arg.Key).first;
301 unsigned Val = StrTab.add(Arg.Val).first;
302 bool HasDebugLoc = Arg.Loc != std::nullopt;
303 R.push_back(HasDebugLoc ? RECORD_REMARK_ARG_WITH_DEBUGLOC
304 : RECORD_REMARK_ARG_WITHOUT_DEBUGLOC);
305 R.push_back(Key);
306 R.push_back(Val);
307 if (HasDebugLoc) {
308 R.push_back(StrTab.add(Arg.Loc->SourceFilePath).first);
309 R.push_back(Arg.Loc->SourceLine);
310 R.push_back(Arg.Loc->SourceColumn);
311 }
312 Bitstream.EmitRecordWithAbbrev(HasDebugLoc
313 ? RecordRemarkArgWithDebugLocAbbrevID
314 : RecordRemarkArgWithoutDebugLocAbbrevID,
315 R);
316 }
317 Bitstream.ExitBlock();
318 }
319
flushToStream(raw_ostream & OS)320 void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
321 OS.write(Encoded.data(), Encoded.size());
322 Encoded.clear();
323 }
324
getBuffer()325 StringRef BitstreamRemarkSerializerHelper::getBuffer() {
326 return StringRef(Encoded.data(), Encoded.size());
327 }
328
BitstreamRemarkSerializer(raw_ostream & OS,SerializerMode Mode)329 BitstreamRemarkSerializer::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
BitstreamRemarkSerializer(raw_ostream & OS,SerializerMode Mode,StringTable StrTabIn)340 BitstreamRemarkSerializer::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
emit(const Remark & Remark)350 void 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);
367
368 Helper.flushToStream(OS);
369 }
370
metaSerializer(raw_ostream & OS,std::optional<StringRef> ExternalFilename)371 std::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 OS,
379 IsStandalone ? BitstreamRemarkContainerType::Standalone
380 : BitstreamRemarkContainerType::SeparateRemarksMeta,
381 &*StrTab, ExternalFilename);
382 }
383
emit()384 void BitstreamMetaSerializer::emit() {
385 Helper->setupBlockInfo();
386 Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
387 ExternalFilename);
388 Helper->flushToStream(OS);
389 }
390