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 
19 BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
20     BitstreamRemarkContainerType ContainerType)
21     : Encoded(), R(), Bitstream(Encoded), ContainerType(ContainerType) {}
22 
23 static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
24   for (const char C : Str)
25     R.push_back(C);
26 }
27 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
318 void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
319   OS.write(Encoded.data(), Encoded.size());
320   Encoded.clear();
321 }
322 
323 StringRef BitstreamRemarkSerializerHelper::getBuffer() {
324   return StringRef(Encoded.data(), Encoded.size());
325 }
326 
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 
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 
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 
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 
381 void BitstreamMetaSerializer::emit() {
382   Helper->setupBlockInfo();
383   Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
384                         ExternalFilename);
385   Helper->flushToStream(OS);
386 }
387