1//===-- BitstreamRemarkParser.h - Parser for Bitstream remarks --*- C++/-*-===//
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 impementation of the Bitstream remark parser.
10//
11//===----------------------------------------------------------------------===//
12
13#ifndef LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H
14#define LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H
15
16#include "llvm/ADT/StringRef.h"
17#include "llvm/Bitstream/BitstreamReader.h"
18#include "llvm/Remarks/BitstreamRemarkContainer.h"
19#include "llvm/Remarks/Remark.h"
20#include "llvm/Remarks/RemarkFormat.h"
21#include "llvm/Remarks/RemarkParser.h"
22#include "llvm/Remarks/RemarkStringTable.h"
23#include "llvm/Support/Error.h"
24#include "llvm/Support/FormatVariadic.h"
25#include <cstdint>
26#include <memory>
27#include <optional>
28
29namespace llvm {
30namespace remarks {
31
32class BitstreamBlockParserHelperBase {
33protected:
34 BitstreamCursor &Stream;
35
36 StringRef BlockName;
37 unsigned BlockID;
38
39public:
40 BitstreamBlockParserHelperBase(BitstreamCursor &Stream, unsigned BlockID,
41 StringRef BlockName)
42 : Stream(Stream), BlockName(BlockName), BlockID(BlockID) {}
43
44 template <typename... Ts> Error error(char const *Fmt, const Ts &...Vals) {
45 std::string Buffer;
46 raw_string_ostream OS(Buffer);
47 OS << "Error while parsing " << BlockName << " block: ";
48 OS << formatv(Fmt, Vals...);
49 return make_error<StringError>(
50 Args: std::move(Buffer),
51 Args: std::make_error_code(e: std::errc::illegal_byte_sequence));
52 }
53
54 Error expectBlock();
55
56protected:
57 Error enterBlock();
58
59 Error unknownRecord(unsigned AbbrevID);
60 Error unexpectedRecord(StringRef RecordName);
61 Error malformedRecord(StringRef RecordName);
62 Error unexpectedBlock(unsigned Code);
63};
64
65template <typename Derived>
66class BitstreamBlockParserHelper : public BitstreamBlockParserHelperBase {
67protected:
68 using BitstreamBlockParserHelperBase::BitstreamBlockParserHelperBase;
69 Derived &derived() { return *static_cast<Derived *>(this); }
70
71 /// Parse a record and fill in the fields in the parser.
72 /// The subclass must statically override this method.
73 Error parseRecord(unsigned Code) = delete;
74
75 /// Parse a subblock and fill in the fields in the parser.
76 /// The subclass can statically override this method.
77 Error parseSubBlock(unsigned Code) { return unexpectedBlock(Code); }
78
79public:
80 /// Enter, parse, and leave this bitstream block. This expects the
81 /// BitstreamCursor to be right after the SubBlock entry (i.e. after calling
82 /// expectBlock).
83 Error parseBlock() {
84 if (Error E = enterBlock())
85 return E;
86
87 // Stop when there is nothing to read anymore or when we encounter an
88 // END_BLOCK.
89 while (true) {
90 Expected<BitstreamEntry> Next = Stream.advance();
91 if (!Next)
92 return Next.takeError();
93 switch (Next->Kind) {
94 case BitstreamEntry::SubBlock:
95 if (Error E = derived().parseSubBlock(Next->ID))
96 return E;
97 continue;
98 case BitstreamEntry::EndBlock:
99 return Error::success();
100 case BitstreamEntry::Record:
101 if (Error E = derived().parseRecord(Next->ID))
102 return E;
103 continue;
104 case BitstreamEntry::Error:
105 return error("Unexpected end of bitstream.");
106 }
107 llvm_unreachable("Unexpected BitstreamEntry");
108 }
109 }
110};
111
112/// Helper to parse a META_BLOCK for a bitstream remark container.
113class BitstreamMetaParserHelper
114 : public BitstreamBlockParserHelper<BitstreamMetaParserHelper> {
115 friend class BitstreamBlockParserHelper<BitstreamMetaParserHelper>;
116
117public:
118 struct ContainerInfo {
119 uint64_t Version;
120 uint64_t Type;
121 };
122
123 /// The parsed content: depending on the container type, some fields might
124 /// be empty.
125 std::optional<ContainerInfo> Container;
126 std::optional<uint64_t> RemarkVersion;
127 std::optional<StringRef> ExternalFilePath;
128 std::optional<StringRef> StrTabBuf;
129
130 BitstreamMetaParserHelper(BitstreamCursor &Stream)
131 : BitstreamBlockParserHelper(Stream, META_BLOCK_ID, MetaBlockName) {}
132
133protected:
134 Error parseRecord(unsigned Code);
135};
136
137/// Helper to parse a REMARK_BLOCK for a bitstream remark container.
138class BitstreamRemarkParserHelper
139 : public BitstreamBlockParserHelper<BitstreamRemarkParserHelper> {
140 friend class BitstreamBlockParserHelper<BitstreamRemarkParserHelper>;
141
142protected:
143 SmallVector<uint64_t, 5> Record;
144 StringRef RecordBlob;
145 unsigned RecordID;
146
147public:
148 struct RemarkLoc {
149 uint64_t SourceFileNameIdx;
150 uint64_t SourceLine;
151 uint64_t SourceColumn;
152 };
153
154 struct Argument {
155 std::optional<uint64_t> KeyIdx;
156 std::optional<uint64_t> ValueIdx;
157 std::optional<RemarkLoc> Loc;
158
159 Argument(std::optional<uint64_t> KeyIdx, std::optional<uint64_t> ValueIdx)
160 : KeyIdx(KeyIdx), ValueIdx(ValueIdx) {}
161 };
162
163 /// The parsed content: depending on the remark, some fields might be empty.
164 std::optional<uint8_t> Type;
165 std::optional<uint64_t> RemarkNameIdx;
166 std::optional<uint64_t> PassNameIdx;
167 std::optional<uint64_t> FunctionNameIdx;
168 std::optional<uint64_t> Hotness;
169 std::optional<RemarkLoc> Loc;
170
171 SmallVector<Argument, 8> Args;
172
173 BitstreamRemarkParserHelper(BitstreamCursor &Stream)
174 : BitstreamBlockParserHelper(Stream, REMARK_BLOCK_ID, RemarkBlockName) {}
175
176 /// Clear helper state and parse next remark block.
177 Error parseNext();
178
179protected:
180 Error parseRecord(unsigned Code);
181 Error handleRecord();
182};
183
184/// Helper to parse any bitstream remark container.
185struct BitstreamParserHelper {
186 /// The Bitstream reader.
187 BitstreamCursor Stream;
188 /// The block info block.
189 BitstreamBlockInfo BlockInfo;
190
191 /// Helper to parse the metadata blocks in this bitstream.
192 BitstreamMetaParserHelper MetaHelper;
193 /// Helper to parse the remark blocks in this bitstream. Only needed
194 /// for ContainerType RemarksFile.
195 std::optional<BitstreamRemarkParserHelper> RemarksHelper;
196 /// The position of the first remark block we encounter after
197 /// the initial metadata block.
198 std::optional<uint64_t> RemarkStartBitPos;
199
200 /// Start parsing at \p Buffer.
201 BitstreamParserHelper(StringRef Buffer)
202 : Stream(Buffer), MetaHelper(Stream), RemarksHelper(Stream) {}
203
204 /// Parse and validate the magic number.
205 Error expectMagic();
206 /// Parse the block info block containing all the abbrevs.
207 /// This needs to be called before calling any other parsing function.
208 Error parseBlockInfoBlock();
209
210 /// Parse all metadata blocks in the file. This populates the meta helper.
211 Error parseMeta();
212 /// Parse the next remark. This populates the remark helper data.
213 Error parseRemark();
214};
215
216/// Parses and holds the state of the latest parsed remark.
217struct BitstreamRemarkParser : public RemarkParser {
218 /// The buffer to parse.
219 std::optional<BitstreamParserHelper> ParserHelper;
220 /// The string table used for parsing strings.
221 std::optional<ParsedStringTable> StrTab;
222 /// Temporary remark buffer used when the remarks are stored separately.
223 std::unique_ptr<MemoryBuffer> TmpRemarkBuffer;
224 /// Whether the metadata has already been parsed, so we can continue parsing
225 /// remarks.
226 bool IsMetaReady = false;
227 /// The common metadata used to decide how to parse the buffer.
228 /// This is filled when parsing the metadata block.
229 uint64_t ContainerVersion = 0;
230 uint64_t RemarkVersion = 0;
231 BitstreamRemarkContainerType ContainerType =
232 BitstreamRemarkContainerType::RemarksFile;
233
234 /// Create a parser that expects to find a string table embedded in the
235 /// stream.
236 explicit BitstreamRemarkParser(StringRef Buf);
237
238 Expected<std::unique_ptr<Remark>> next() override;
239
240 static bool classof(const RemarkParser *P) {
241 return P->ParserFormat == Format::Bitstream;
242 }
243
244 /// Parse and process the metadata of the buffer.
245 Error parseMeta();
246
247private:
248 Error processCommonMeta();
249 Error processFileContainerMeta();
250 Error processExternalFilePath();
251
252 Expected<std::unique_ptr<Remark>> processRemark();
253
254 Error processStrTab();
255 Error processRemarkVersion();
256};
257
258Expected<std::unique_ptr<BitstreamRemarkParser>> createBitstreamParserFromMeta(
259 StringRef Buf,
260 std::optional<StringRef> ExternalFilePrependPath = std::nullopt);
261
262} // end namespace remarks
263} // end namespace llvm
264
265#endif /* LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H */
266