LLVM 22.0.0git
BitstreamRemarkParser.cpp
Go to the documentation of this file.
1//===- BitstreamRemarkParser.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 utility methods used by clients that want to use the
10// parser for remark diagnostics in LLVM.
11//
12//===----------------------------------------------------------------------===//
13
16#include "llvm/Support/Path.h"
17#include <optional>
18
19using namespace llvm;
20using namespace llvm::remarks;
21
22namespace {
23
24template <typename... Ts> Error error(char const *Fmt, const Ts &...Vals) {
25 std::string Buffer;
26 raw_string_ostream OS(Buffer);
27 OS << formatv(Fmt, Vals...);
29 std::move(Buffer),
30 std::make_error_code(std::errc::illegal_byte_sequence));
31}
32
33} // namespace
34
36 return error("Unknown record entry ({}).", AbbrevID);
37}
38
40 return error("Unexpected record entry ({}).", RecordName);
41}
42
44 return error("Malformed record entry ({}).", RecordName);
45}
46
48 return error("Unexpected subblock ({}).", Code);
49}
50
53 if (!Next)
54 return Next.takeError();
55 switch (Next->Kind) {
57 return Next->ID;
60 return error("Expected subblock, but got unexpected record.");
62 return error("Expected subblock, but got unexpected end of bitstream.");
63 }
64 llvm_unreachable("Unexpected BitstreamEntry");
65}
66
68 auto MaybeBlockID = expectSubBlock(Stream);
69 if (!MaybeBlockID)
70 return MaybeBlockID.takeError();
71 if (*MaybeBlockID != BlockID)
72 return error("Expected {} block, but got unexpected block ({}).", BlockName,
73 *MaybeBlockID);
74 return Error::success();
75}
76
78 if (Stream.EnterSubBlock(BlockID))
79 return error("Error while entering {} block.", BlockName);
80 return Error::success();
81}
82
84 // Note: 2 is used here because it's the max number of fields we have per
85 // record.
87 StringRef Blob;
88 Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob);
89 if (!RecordID)
90 return RecordID.takeError();
91
92 switch (*RecordID) {
94 if (Record.size() != 2)
96 Container = {Record[0], Record[1]};
97 // Error immediately if container version is outdated, so the user sees an
98 // explanation instead of a parser error.
99 if (Container->Version != CurrentContainerVersion) {
100 return ::error(
101 "Unsupported remark container version (expected: {}, read: {}). "
102 "Please upgrade/downgrade your toolchain to read this container.",
104 }
105 break;
106 }
108 if (Record.size() != 1)
111 // Error immediately if remark version is outdated, so the user sees an
112 // explanation instead of a parser error.
114 return ::error(
115 "Unsupported remark version in container (expected: {}, read: {}). "
116 "Please upgrade/downgrade your toolchain to read this container.",
118 }
119 break;
120 }
121 case RECORD_META_STRTAB: {
122 if (Record.size() != 0)
124 StrTabBuf = Blob;
125 break;
126 }
128 if (Record.size() != 0)
130 ExternalFilePath = Blob;
131 break;
132 }
133 default:
134 return unknownRecord(*RecordID);
135 }
136 return Error::success();
137}
138
140 Record.clear();
141 Expected<unsigned> MaybeRecordID =
142 Stream.readRecord(Code, Record, &RecordBlob);
143 if (!MaybeRecordID)
144 return MaybeRecordID.takeError();
145 RecordID = *MaybeRecordID;
146 return handleRecord();
147}
148
150 switch (RecordID) {
152 if (Record.size() != 4)
154 Type = Record[0];
156 PassNameIdx = Record[2];
158 break;
159 }
161 if (Record.size() != 3)
163 Loc = {Record[0], Record[1], Record[2]};
164 break;
165 }
167 if (Record.size() != 1)
169 Hotness = Record[0];
170 break;
171 }
173 if (Record.size() != 5)
175 auto &Arg = Args.emplace_back(Record[0], Record[1]);
176 Arg.Loc = {Record[2], Record[3], Record[4]};
177 break;
178 }
180 if (Record.size() != 2)
182 Args.emplace_back(Record[0], Record[1]);
183 break;
184 }
185 default:
186 return unknownRecord(RecordID);
187 }
188 return Error::success();
189}
190
192 Type.reset();
193 RemarkNameIdx.reset();
194 PassNameIdx.reset();
195 FunctionNameIdx.reset();
196 Hotness.reset();
197 Loc.reset();
198 Args.clear();
199
200 if (Error E = expectBlock())
201 return E;
202 return parseBlock();
203}
204
207
209 std::array<char, 4> Result;
210 for (unsigned I = 0; I < 4; ++I)
211 if (Expected<unsigned> R = Stream.Read(8))
212 Result[I] = *R;
213 else
214 return R.takeError();
215
216 StringRef MagicNumber{Result.data(), Result.size()};
218 return error("Unknown magic number: expecting {}, got {}.",
220 return Error::success();
221}
222
225 if (!Next)
226 return Next.takeError();
227 if (Next->Kind != BitstreamEntry::SubBlock ||
229 return error(
230 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "
231 "BLOCKINFO_BLOCK, ...].");
232
234 Stream.ReadBlockInfoBlock();
235 if (!MaybeBlockInfo)
236 return MaybeBlockInfo.takeError();
237
238 if (!*MaybeBlockInfo)
239 return error("Missing BLOCKINFO_BLOCK.");
240
241 BlockInfo = **MaybeBlockInfo;
242
243 Stream.setBlockInfo(&BlockInfo);
244 return Error::success();
245}
246
248 if (Error E = expectMagic())
249 return E;
250 if (Error E = parseBlockInfoBlock())
251 return E;
252 return Error::success();
253}
254
257 StringRef Buf, std::optional<StringRef> ExternalFilePrependPath) {
258 auto Parser = std::make_unique<BitstreamRemarkParser>(Buf);
259
260 if (ExternalFilePrependPath)
261 Parser->ExternalFilePrependPath = std::string(*ExternalFilePrependPath);
262
263 return std::move(Parser);
264}
265
267 if (ParserHelper.atEndOfStream())
269
270 if (!ReadyToParseRemarks) {
271 if (Error E = parseMeta())
272 return std::move(E);
273 ReadyToParseRemarks = true;
274 }
275
276 return parseRemark();
277}
278
280 if (Error E = ParserHelper.advanceToMetaBlock())
281 return E;
282
283 BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream);
284 if (Error E = MetaHelper.expectBlock())
285 return E;
286 if (Error E = MetaHelper.parseBlock())
287 return E;
288
289 if (Error E = processCommonMeta(MetaHelper))
290 return E;
291
292 switch (ContainerType) {
294 return processStandaloneMeta(MetaHelper);
296 return processSeparateRemarksFileMeta(MetaHelper);
298 return processSeparateRemarksMetaMeta(MetaHelper);
299 }
300 llvm_unreachable("Unknown BitstreamRemarkContainerType enum");
301}
302
303Error BitstreamRemarkParser::processCommonMeta(
305 if (!Helper.Container)
306 return Helper.error("Missing container info.");
307 auto &Container = *Helper.Container;
308 ContainerVersion = Container.Version;
309 // Always >= BitstreamRemarkContainerType::First since it's unsigned.
310 if (Container.Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last))
311 return Helper.error("Invalid container type.");
312 ContainerType = static_cast<BitstreamRemarkContainerType>(Container.Type);
313 return Error::success();
314}
315
316Error BitstreamRemarkParser::processStrTab(BitstreamMetaParserHelper &Helper) {
317 if (!Helper.StrTabBuf)
318 return Helper.error("Missing string table.");
319 // Parse and assign the string table.
320 StrTab.emplace(*Helper.StrTabBuf);
321 return Error::success();
322}
323
324Error BitstreamRemarkParser::processRemarkVersion(
326 if (!Helper.RemarkVersion)
327 return Helper.error("Missing remark version.");
329 return Error::success();
330}
331
332Error BitstreamRemarkParser::processExternalFilePath(
334 if (!Helper.ExternalFilePath)
335 return Helper.error("Missing external file path.");
336 StringRef ExternalFilePath = *Helper.ExternalFilePath;
337
338 SmallString<80> FullPath(ExternalFilePrependPath);
339 sys::path::append(FullPath, ExternalFilePath);
340
341 // External file: open the external file, parse it, check if its metadata
342 // matches the one from the separate metadata, then replace the current parser
343 // with the one parsing the remarks.
344 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =
345 MemoryBuffer::getFile(FullPath);
346 if (std::error_code EC = BufferOrErr.getError())
347 return createFileError(FullPath, EC);
348
349 TmpRemarkBuffer = std::move(*BufferOrErr);
350
351 // Don't try to parse the file if it's empty.
352 if (TmpRemarkBuffer->getBufferSize() == 0)
354
355 // Create a separate parser used for parsing the separate file.
356 ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer());
357 // Advance and check until we can parse the meta block.
358 if (Error E = ParserHelper.advanceToMetaBlock())
359 return E;
360 // Parse the meta from the separate file.
361 // Note: here we overwrite the BlockInfo with the one from the file. This will
362 // be used to parse the rest of the file.
363 BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream);
364 if (Error E = SeparateMetaHelper.expectBlock())
365 return E;
366 if (Error E = SeparateMetaHelper.parseBlock())
367 return E;
368
369 if (Error E = processCommonMeta(SeparateMetaHelper))
370 return E;
371
373 return SeparateMetaHelper.error("Wrong container type in external file.");
374
375 // Process the meta from the separate file.
376 return processSeparateRemarksFileMeta(SeparateMetaHelper);
377}
378
379Error BitstreamRemarkParser::processStandaloneMeta(
381 if (Error E = processStrTab(Helper))
382 return E;
383 return processRemarkVersion(Helper);
384}
385
386Error BitstreamRemarkParser::processSeparateRemarksFileMeta(
388 return processRemarkVersion(Helper);
389}
390
391Error BitstreamRemarkParser::processSeparateRemarksMetaMeta(
393 if (Error E = processStrTab(Helper))
394 return E;
395 return processExternalFilePath(Helper);
396}
397
399 BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream);
400 if (Error E = RemarkHelper.parseNext())
401 return std::move(E);
402
403 return processRemark(RemarkHelper);
404}
405
407BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) {
408 std::unique_ptr<Remark> Result = std::make_unique<Remark>();
409 Remark &R = *Result;
410
411 if (!StrTab)
412 return Helper.error("Missing string table.");
413
414 if (!Helper.Type)
415 return Helper.error("Missing remark type.");
416
417 // Always >= Type::First since it's unsigned.
418 if (*Helper.Type > static_cast<uint8_t>(Type::Last))
419 return Helper.error("Unknown remark type.");
420
421 R.RemarkType = static_cast<Type>(*Helper.Type);
422
423 if (!Helper.RemarkNameIdx)
424 return Helper.error("Missing remark name.");
425
426 if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx])
427 R.RemarkName = *RemarkName;
428 else
429 return RemarkName.takeError();
430
431 if (!Helper.PassNameIdx)
432 return Helper.error("Missing remark pass.");
433
435 R.PassName = *PassName;
436 else
437 return PassName.takeError();
438
439 if (!Helper.FunctionNameIdx)
440 return Helper.error("Missing remark function name.");
441
442 if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx])
443 R.FunctionName = *FunctionName;
444 else
445 return FunctionName.takeError();
446
447 if (Helper.Loc) {
448 Expected<StringRef> SourceFileName =
449 (*StrTab)[Helper.Loc->SourceFileNameIdx];
450 if (!SourceFileName)
451 return SourceFileName.takeError();
452 R.Loc.emplace();
453 R.Loc->SourceFilePath = *SourceFileName;
454 R.Loc->SourceLine = Helper.Loc->SourceLine;
455 R.Loc->SourceColumn = Helper.Loc->SourceColumn;
456 }
457
458 if (Helper.Hotness)
459 R.Hotness = *Helper.Hotness;
460
461 for (const BitstreamRemarkParserHelper::Argument &Arg : Helper.Args) {
462 if (!Arg.KeyIdx)
463 return Helper.error("Missing key in remark argument.");
464 if (!Arg.ValueIdx)
465 return Helper.error("Missing value in remark argument.");
466
467 // We have at least a key and a value, create an entry.
468 auto &RArg = R.Args.emplace_back();
469
470 if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx])
471 RArg.Key = *Key;
472 else
473 return Key.takeError();
474
476 RArg.Val = *Value;
477 else
478 return Value.takeError();
479
480 if (Arg.Loc) {
481 if (Expected<StringRef> SourceFileName =
482 (*StrTab)[Arg.Loc->SourceFileNameIdx]) {
483 RArg.Loc.emplace();
484 RArg.Loc->SourceFilePath = *SourceFileName;
485 RArg.Loc->SourceLine = Arg.Loc->SourceLine;
486 RArg.Loc->SourceColumn = Arg.Loc->SourceColumn;
487 } else
488 return SourceFileName.takeError();
489 }
490 }
491
492 return std::move(Result);
493}
static Expected< unsigned > expectSubBlock(BitstreamCursor &Stream)
static GCRegistry::Add< CoreCLRGC > E("coreclr", "CoreCLR-compatible GC")
#define I(x, y, z)
Definition MD5.cpp:58
#define error(X)
static const char PassName[]
This represents a position within a bitcode file, implemented on top of a SimpleBitstreamCursor.
Expected< BitstreamEntry > advance(unsigned Flags=0)
Advance the current bitstream, returning the next entry in the stream.
std::error_code getError() const
Definition ErrorOr.h:152
Lightweight error class with error context and mandatory checking.
Definition Error.h:159
static ErrorSuccess success()
Create a success value.
Definition Error.h:336
Tagged union holding either a T or a Error.
Definition Error.h:485
Error takeError()
Take ownership of the stored error.
Definition Error.h:612
static ErrorOr< std::unique_ptr< MemoryBuffer > > getFile(const Twine &Filename, bool IsText=false, bool RequiresNullTerminator=true, bool IsVolatile=false, std::optional< Align > Alignment=std::nullopt)
Open the specified file as a MemoryBuffer, returning a new MemoryBuffer if successful,...
This is a 'vector' (really, a variable-sized array), optimized for the case when the array is small.
StringRef - Represent a constant reference to a string, i.e.
Definition StringRef.h:55
LLVM Value Representation.
Definition Value.h:75
A raw_ostream that writes to an std::string.
Error error(char const *Fmt, const Ts &...Vals)
Helper to parse a META_BLOCK for a bitstream remark container.
std::optional< ContainerInfo > Container
The parsed content: depending on the container type, some fields might be empty.
Helper to parse a REMARK_BLOCK for a bitstream remark container.
std::optional< uint8_t > Type
The parsed content: depending on the remark, some fields might be empty.
Error parseNext()
Clear helper state and parse next remark block.
#define llvm_unreachable(msg)
Marks that the current location is not supposed to be reachable.
@ BLOCKINFO_BLOCK_ID
BLOCKINFO_BLOCK is used to define metadata about blocks, for example, standard abbrevs that should be...
constexpr StringLiteral RemarkDebugLocName("Remark debug location")
BitstreamRemarkContainerType
Type of the remark container.
@ SeparateRemarksFile
The remarks emitted separately.
@ SeparateRemarksMeta
The metadata emitted separately.
constexpr StringLiteral RemarkArgWithDebugLocName("Argument with debug location")
constexpr uint64_t CurrentContainerVersion
The current version of the remark container.
constexpr StringLiteral MetaExternalFileName("External File")
constexpr StringLiteral MetaRemarkVersionName("Remark version")
Format
The format used for serializing/deserializing remarks.
constexpr StringLiteral MetaContainerInfoName("Container info")
Expected< std::unique_ptr< BitstreamRemarkParser > > createBitstreamParserFromMeta(StringRef Buf, std::optional< StringRef > ExternalFilePrependPath=std::nullopt)
constexpr StringLiteral RemarkHeaderName("Remark header")
constexpr StringLiteral RemarkArgWithoutDebugLocName("Argument")
constexpr uint64_t CurrentRemarkVersion
The current version of the remark entry.
Definition Remark.h:29
constexpr StringLiteral ContainerMagic("RMRK")
The magic number used for identifying remark blocks.
constexpr StringLiteral MetaStrTabName("String table")
Type
The type of the remark.
Definition Remark.h:66
constexpr StringLiteral RemarkHotnessName("Remark hotness")
LLVM_ABI void append(SmallVectorImpl< char > &path, const Twine &a, const Twine &b="", const Twine &c="", const Twine &d="")
Append to path.
Definition Path.cpp:456
This is an optimization pass for GlobalISel generic memory operations.
Error createFileError(const Twine &F, Error E)
Concatenate a source file path and/or name with an Error.
Definition Error.h:1399
auto formatv(bool Validate, const char *Fmt, Ts &&...Vals)
LLVM_ATTRIBUTE_VISIBILITY_DEFAULT AnalysisKey InnerAnalysisManagerProxy< AnalysisManagerT, IRUnitT, ExtraArgTs... >::Key
Error make_error(ArgTs &&... Args)
Make a Error instance representing failure using the given error info type.
Definition Error.h:340
FunctionAddr VTableAddr Next
Definition InstrProf.h:141
BitstreamParserHelper(StringRef Buffer)
Start parsing at Buffer.
Error advanceToMetaBlock()
Advance to the meta block.
BitstreamBlockInfo BlockInfo
The block info block.
BitstreamCursor Stream
The Bitstream reader.
Error parseBlockInfoBlock()
Parse the block info block containing all the abbrevs.
Error expectMagic()
Parse and validate the magic number.
Error parseMeta()
Parse and process the metadata of the buffer.
std::optional< ParsedStringTable > StrTab
The string table used for parsing strings.
Expected< std::unique_ptr< Remark > > parseRemark()
Parse a Bitstream remark.
std::unique_ptr< MemoryBuffer > TmpRemarkBuffer
Temporary remark buffer used when the remarks are stored separately.
uint64_t ContainerVersion
The common metadata used to decide how to parse the buffer.
BitstreamParserHelper ParserHelper
The buffer to parse.
Expected< std::unique_ptr< Remark > > next() override
If no error occurs, this returns a valid Remark object.
BitstreamRemarkParser(StringRef Buf)
Create a parser that expects to find a string table embedded in the stream.
BitstreamRemarkContainerType ContainerType
bool ReadyToParseRemarks
Wether the parser is ready to parse remarks.
std::string ExternalFilePrependPath
Path to prepend when opening an external remark file.
RemarkParser(Format ParserFormat)
A remark type used for both emission and parsing.
Definition Remark.h:98