BitstreamRemarkSerializer.cpp revision 360784
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
16using namespace llvm;
17using namespace llvm::remarks;
18
19BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
20    BitstreamRemarkContainerType ContainerType)
21    : Encoded(), R(), Bitstream(Encoded), ContainerType(ContainerType) {}
22
23static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
24  for (const char C : Str)
25    R.push_back(C);
26}
27
28static 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
36static 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
47void 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
63void 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
74void 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
83void 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
93void 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
107void 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
117void 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
124void 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
196void 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
233void 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
268void 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
318void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
319  OS.write(Encoded.data(), Encoded.size());
320  Encoded.clear();
321}
322
323StringRef BitstreamRemarkSerializerHelper::getBuffer() {
324  return StringRef(Encoded.data(), Encoded.size());
325}
326
327BitstreamRemarkSerializer::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
338BitstreamRemarkSerializer::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
348void 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
368std::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
381void BitstreamMetaSerializer::emit() {
382  Helper->setupBlockInfo();
383  Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
384                        ExternalFilename);
385  Helper->flushToStream(OS);
386}
387