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::BitstreamRemarkSerializerHelper(
22     BitstreamRemarkContainerType ContainerType)
23     : Bitstream(Encoded), ContainerType(ContainerType) {}
24 
25 static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
26   append_range(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(RecordID);
33   push(R, Str);
34   Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, 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(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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
320 void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
321   OS.write(Encoded.data(), Encoded.size());
322   Encoded.clear();
323 }
324 
325 StringRef BitstreamRemarkSerializerHelper::getBuffer() {
326   return StringRef(Encoded.data(), Encoded.size());
327 }
328 
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 
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 
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 
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 
384 void BitstreamMetaSerializer::emit() {
385   Helper->setupBlockInfo();
386   Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
387                         ExternalFilename);
388   Helper->flushToStream(OS);
389 }
390