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
18using namespace llvm;
19using namespace llvm::remarks;
20
21BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
22    BitstreamRemarkContainerType ContainerType)
23    : Bitstream(Encoded), ContainerType(ContainerType) {}
24
25static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
26  append_range(R, Str);
27}
28
29static 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
37static 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
48void 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
64void 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
75void 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
84void 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
94void 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
108void 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
118void 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
125void 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
197void 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
234void 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
270void 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
320void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
321  OS.write(Encoded.data(), Encoded.size());
322  Encoded.clear();
323}
324
325StringRef BitstreamRemarkSerializerHelper::getBuffer() {
326  return StringRef(Encoded.data(), Encoded.size());
327}
328
329BitstreamRemarkSerializer::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
340BitstreamRemarkSerializer::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
350void 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
371std::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
384void BitstreamMetaSerializer::emit() {
385  Helper->setupBlockInfo();
386  Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
387                        ExternalFilename);
388  Helper->flushToStream(OS);
389}
390