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