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