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/ADT/ScopeExit.h"
16#include "llvm/Remarks/Remark.h"
17#include <cassert>
18#include <optional>
19
20using namespace llvm;
21using namespace llvm::remarks;
22
23BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
24 BitstreamRemarkContainerType ContainerType, raw_ostream &OS)
25 : Bitstream(OS), ContainerType(ContainerType) {}
26
27static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream,
28 SmallVectorImpl<uint64_t> &R, StringRef Str) {
29 R.clear();
30 R.push_back(Elt: RecordID);
31 append_range(C&: R, R&: Str);
32 Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_SETRECORDNAME, Vals: R);
33}
34
35static void initBlock(unsigned BlockID, BitstreamWriter &Bitstream,
36 SmallVectorImpl<uint64_t> &R, StringRef Str) {
37 R.clear();
38 R.push_back(Elt: BlockID);
39 Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_SETBID, Vals: R);
40
41 R.clear();
42 append_range(C&: R, R&: Str);
43 Bitstream.EmitRecord(Code: bitc::BLOCKINFO_CODE_BLOCKNAME, Vals: R);
44}
45
46void BitstreamRemarkSerializerHelper::setupMetaBlockInfo() {
47 // Setup the metadata block.
48 initBlock(BlockID: META_BLOCK_ID, Bitstream, R, Str: MetaBlockName);
49
50 // The container information.
51 setRecordName(RecordID: RECORD_META_CONTAINER_INFO, Bitstream, R,
52 Str: MetaContainerInfoName);
53
54 auto Abbrev = std::make_shared<BitCodeAbbrev>();
55 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_CONTAINER_INFO));
56 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
57 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Type.
58 RecordMetaContainerInfoAbbrevID =
59 Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev);
60}
61
62void BitstreamRemarkSerializerHelper::setupMetaRemarkVersion() {
63 setRecordName(RecordID: RECORD_META_REMARK_VERSION, Bitstream, R,
64 Str: MetaRemarkVersionName);
65
66 auto Abbrev = std::make_shared<BitCodeAbbrev>();
67 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_REMARK_VERSION));
68 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
69 RecordMetaRemarkVersionAbbrevID =
70 Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev);
71}
72
73void BitstreamRemarkSerializerHelper::emitMetaRemarkVersion(
74 uint64_t RemarkVersion) {
75 // The remark version is emitted only if we emit remarks.
76 R.clear();
77 R.push_back(Elt: RECORD_META_REMARK_VERSION);
78 R.push_back(Elt: RemarkVersion);
79 Bitstream.EmitRecordWithAbbrev(Abbrev: RecordMetaRemarkVersionAbbrevID, Vals: R);
80}
81
82void BitstreamRemarkSerializerHelper::setupMetaStrTab() {
83 setRecordName(RecordID: RECORD_META_STRTAB, Bitstream, R, Str: MetaStrTabName);
84
85 auto Abbrev = std::make_shared<BitCodeAbbrev>();
86 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_STRTAB));
87 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Raw table.
88 RecordMetaStrTabAbbrevID =
89 Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev);
90}
91
92void 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(Elt: 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(Abbrev: RecordMetaStrTabAbbrevID, Vals: R, Blob);
104}
105
106void BitstreamRemarkSerializerHelper::setupMetaExternalFile() {
107 setRecordName(RecordID: RECORD_META_EXTERNAL_FILE, Bitstream, R, Str: MetaExternalFileName);
108
109 auto Abbrev = std::make_shared<BitCodeAbbrev>();
110 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_META_EXTERNAL_FILE));
111 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename.
112 RecordMetaExternalFileAbbrevID =
113 Bitstream.EmitBlockInfoAbbrev(BlockID: META_BLOCK_ID, Abbv: Abbrev);
114}
115
116void BitstreamRemarkSerializerHelper::emitMetaExternalFile(StringRef Filename) {
117 // The external file is emitted only if we emit the separate metadata.
118 R.clear();
119 R.push_back(Elt: RECORD_META_EXTERNAL_FILE);
120 Bitstream.EmitRecordWithBlob(Abbrev: RecordMetaExternalFileAbbrevID, Vals: R, Blob: Filename);
121}
122
123void BitstreamRemarkSerializerHelper::setupRemarkBlockInfo() {
124 // Setup the remark block.
125 initBlock(BlockID: REMARK_BLOCK_ID, Bitstream, R, Str: RemarkBlockName);
126
127 // The header of a remark.
128 {
129 setRecordName(RecordID: RECORD_REMARK_HEADER, Bitstream, R, Str: RemarkHeaderName);
130
131 auto Abbrev = std::make_shared<BitCodeAbbrev>();
132 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_HEADER));
133 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Type
134 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Remark Name
135 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Pass name
136 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Function name
137 RecordRemarkHeaderAbbrevID =
138 Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev);
139 }
140
141 // The location of a remark.
142 {
143 setRecordName(RecordID: RECORD_REMARK_DEBUG_LOC, Bitstream, R, Str: RemarkDebugLocName);
144
145 auto Abbrev = std::make_shared<BitCodeAbbrev>();
146 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_DEBUG_LOC));
147 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
148 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
149 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
150 RecordRemarkDebugLocAbbrevID =
151 Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev);
152 }
153
154 // The hotness of a remark.
155 {
156 setRecordName(RecordID: RECORD_REMARK_HOTNESS, Bitstream, R, Str: RemarkHotnessName);
157
158 auto Abbrev = std::make_shared<BitCodeAbbrev>();
159 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_HOTNESS));
160 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Hotness
161 RecordRemarkHotnessAbbrevID =
162 Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev);
163 }
164
165 // An argument entry with a debug location attached.
166 {
167 setRecordName(RecordID: RECORD_REMARK_ARG_WITH_DEBUGLOC, Bitstream, R,
168 Str: RemarkArgWithDebugLocName);
169
170 auto Abbrev = std::make_shared<BitCodeAbbrev>();
171 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_ARG_WITH_DEBUGLOC));
172 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
173 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
174 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
175 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
176 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
177 RecordRemarkArgWithDebugLocAbbrevID =
178 Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev);
179 }
180
181 // An argument entry with no debug location attached.
182 {
183 setRecordName(RecordID: RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, Bitstream, R,
184 Str: RemarkArgWithoutDebugLocName);
185
186 auto Abbrev = std::make_shared<BitCodeAbbrev>();
187 Abbrev->Add(OpInfo: BitCodeAbbrevOp(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC));
188 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
189 Abbrev->Add(OpInfo: BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
190 RecordRemarkArgWithoutDebugLocAbbrevID =
191 Bitstream.EmitBlockInfoAbbrev(BlockID: REMARK_BLOCK_ID, Abbv: Abbrev);
192 }
193}
194
195void BitstreamRemarkSerializerHelper::setupBlockInfo() {
196 // Emit magic number.
197 for (const char C : ContainerMagic)
198 Bitstream.Emit(Val: static_cast<unsigned>(C), NumBits: 8);
199
200 Bitstream.EnterBlockInfoBlock();
201 llvm::scope_exit ExitBlock([&] { Bitstream.ExitBlock(); });
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::RemarksFileExternal:
209 // Needs to know where the external remarks file is.
210 setupMetaExternalFile();
211 return;
212 case BitstreamRemarkContainerType::RemarksFile:
213 // Contains remarks: emit the version.
214 setupMetaRemarkVersion();
215 // Needs a string table.
216 setupMetaStrTab();
217 // Contains remarks: emit the remark abbrevs.
218 setupRemarkBlockInfo();
219 return;
220 }
221 llvm_unreachable("Unexpected BitstreamRemarkContainerType");
222}
223
224void BitstreamRemarkSerializerHelper::emitMetaBlock(
225 std::optional<StringRef> Filename) {
226 // Emit the meta block
227 Bitstream.EnterSubblock(BlockID: META_BLOCK_ID, CodeLen: 3);
228 llvm::scope_exit ExitBlock([&] { Bitstream.ExitBlock(); });
229
230 // The container version and type.
231 R.clear();
232 R.push_back(Elt: RECORD_META_CONTAINER_INFO);
233 R.push_back(Elt: CurrentContainerVersion);
234 R.push_back(Elt: static_cast<uint64_t>(ContainerType));
235 Bitstream.EmitRecordWithAbbrev(Abbrev: RecordMetaContainerInfoAbbrevID, Vals: R);
236
237 switch (ContainerType) {
238 case BitstreamRemarkContainerType::RemarksFileExternal:
239 assert(Filename != std::nullopt);
240 emitMetaExternalFile(Filename: *Filename);
241 return;
242 case BitstreamRemarkContainerType::RemarksFile:
243 emitMetaRemarkVersion(RemarkVersion: CurrentRemarkVersion);
244 return;
245 }
246 llvm_unreachable("Unexpected BitstreamRemarkContainerType");
247}
248
249void BitstreamRemarkSerializerHelper::emitLateMetaBlock(
250 const StringTable &StrTab) {
251 // Emit the late meta block (after all remarks are serialized)
252 Bitstream.EnterSubblock(BlockID: META_BLOCK_ID, CodeLen: 3);
253 emitMetaStrTab(StrTab);
254 Bitstream.ExitBlock();
255}
256
257void BitstreamRemarkSerializerHelper::emitRemark(const Remark &Remark,
258 StringTable &StrTab) {
259 Bitstream.EnterSubblock(BlockID: REMARK_BLOCK_ID, CodeLen: 4);
260
261 R.clear();
262 R.push_back(Elt: RECORD_REMARK_HEADER);
263 R.push_back(Elt: static_cast<uint64_t>(Remark.RemarkType));
264 R.push_back(Elt: StrTab.add(Str: Remark.RemarkName).first);
265 R.push_back(Elt: StrTab.add(Str: Remark.PassName).first);
266 R.push_back(Elt: StrTab.add(Str: Remark.FunctionName).first);
267 Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkHeaderAbbrevID, Vals: R);
268
269 if (const std::optional<RemarkLocation> &Loc = Remark.Loc) {
270 R.clear();
271 R.push_back(Elt: RECORD_REMARK_DEBUG_LOC);
272 R.push_back(Elt: StrTab.add(Str: Loc->SourceFilePath).first);
273 R.push_back(Elt: Loc->SourceLine);
274 R.push_back(Elt: Loc->SourceColumn);
275 Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkDebugLocAbbrevID, Vals: R);
276 }
277
278 if (std::optional<uint64_t> Hotness = Remark.Hotness) {
279 R.clear();
280 R.push_back(Elt: RECORD_REMARK_HOTNESS);
281 R.push_back(Elt: *Hotness);
282 Bitstream.EmitRecordWithAbbrev(Abbrev: RecordRemarkHotnessAbbrevID, Vals: R);
283 }
284
285 for (const Argument &Arg : Remark.Args) {
286 R.clear();
287 unsigned Key = StrTab.add(Str: Arg.Key).first;
288 unsigned Val = StrTab.add(Str: Arg.Val).first;
289 bool HasDebugLoc = Arg.Loc != std::nullopt;
290 R.push_back(Elt: HasDebugLoc ? RECORD_REMARK_ARG_WITH_DEBUGLOC
291 : RECORD_REMARK_ARG_WITHOUT_DEBUGLOC);
292 R.push_back(Elt: Key);
293 R.push_back(Elt: Val);
294 if (HasDebugLoc) {
295 R.push_back(Elt: StrTab.add(Str: Arg.Loc->SourceFilePath).first);
296 R.push_back(Elt: Arg.Loc->SourceLine);
297 R.push_back(Elt: Arg.Loc->SourceColumn);
298 }
299 Bitstream.EmitRecordWithAbbrev(Abbrev: HasDebugLoc
300 ? RecordRemarkArgWithDebugLocAbbrevID
301 : RecordRemarkArgWithoutDebugLocAbbrevID,
302 Vals: R);
303 }
304 Bitstream.ExitBlock();
305}
306
307BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS)
308 : RemarkSerializer(Format::Bitstream, OS) {
309 StrTab.emplace();
310}
311
312BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS,
313 StringTable StrTabIn)
314 : RemarkSerializer(Format::Bitstream, OS) {
315 StrTab = std::move(StrTabIn);
316}
317
318BitstreamRemarkSerializer::~BitstreamRemarkSerializer() { finalize(); }
319
320void BitstreamRemarkSerializer::setup() {
321 if (Helper)
322 return;
323 Helper.emplace(args: BitstreamRemarkContainerType::RemarksFile, args&: OS);
324 Helper->setupBlockInfo();
325 Helper->emitMetaBlock();
326}
327
328void BitstreamRemarkSerializer::finalize() {
329 if (!Helper)
330 return;
331 Helper->emitLateMetaBlock(StrTab: *StrTab);
332 Helper = std::nullopt;
333}
334
335void BitstreamRemarkSerializer::emit(const Remark &Remark) {
336 setup();
337 Helper->emitRemark(Remark, StrTab&: *StrTab);
338}
339
340std::unique_ptr<MetaSerializer>
341BitstreamRemarkSerializer::metaSerializer(raw_ostream &OS,
342 StringRef ExternalFilename) {
343 return std::make_unique<BitstreamMetaSerializer>(
344 args&: OS, args: BitstreamRemarkContainerType::RemarksFileExternal, args&: ExternalFilename);
345}
346
347void BitstreamMetaSerializer::emit() {
348 assert(Helper && "BitstreamMetaSerializer emitted multiple times");
349 Helper->setupBlockInfo();
350 Helper->emitMetaBlock(Filename: ExternalFilename);
351 Helper = std::nullopt;
352}
353