llvm-project/llvm/lib/Remarks/BitstreamRemarkParser.h
Tobias Stadler 45f1440d9a
Reland "[Remarks] BitstreamRemarkParser: Refactor error handling" (#158667)
Reland #156511 after fixing a build failure not caught by clang. The
default implementation of `parseRecord` is currently unused. Apparently,
clang doesn't type check uninstantiated methods in class templates. To
avoid this footgun, we `= delete` the impl for now.

Original message:

In preparation of larger changes to the bitstream remark format,
refactor the error handling code in the BitstreamRemarkParser.

Main change: move the various static helper methods into the parser
helper classes, so we don't need to pass around as many args. Calling
`error(...)` inside the helper classes now automatically prepends the
current block being parsed to the error message.

NFCI (except for error messages on invalid bitstream files).

Pull Request: https://github.com/llvm/llvm-project/pull/158667
2025-09-16 14:17:35 +02:00

257 lines
8.2 KiB
C++

//===-- BitstreamRemarkParser.h - Parser for Bitstream remarks --*- C++/-*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
//
// This file provides the impementation of the Bitstream remark parser.
//
//===----------------------------------------------------------------------===//
#ifndef LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H
#define LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H
#include "llvm/ADT/StringRef.h"
#include "llvm/Bitstream/BitstreamReader.h"
#include "llvm/Remarks/BitstreamRemarkContainer.h"
#include "llvm/Remarks/Remark.h"
#include "llvm/Remarks/RemarkFormat.h"
#include "llvm/Remarks/RemarkParser.h"
#include "llvm/Remarks/RemarkStringTable.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/FormatVariadic.h"
#include <cstdint>
#include <memory>
#include <optional>
namespace llvm {
namespace remarks {
class BitstreamBlockParserHelperBase {
protected:
BitstreamCursor &Stream;
StringRef BlockName;
unsigned BlockID;
public:
BitstreamBlockParserHelperBase(BitstreamCursor &Stream, unsigned BlockID,
StringRef BlockName)
: Stream(Stream), BlockName(BlockName), BlockID(BlockID) {}
template <typename... Ts> Error error(char const *Fmt, const Ts &...Vals) {
std::string Buffer;
raw_string_ostream OS(Buffer);
OS << "Error while parsing " << BlockName << " block: ";
OS << formatv(Fmt, Vals...);
return make_error<StringError>(
std::move(Buffer),
std::make_error_code(std::errc::illegal_byte_sequence));
}
Error expectBlock();
protected:
Error enterBlock();
Error unknownRecord(unsigned AbbrevID);
Error unexpectedRecord(StringRef RecordName);
Error malformedRecord(StringRef RecordName);
Error unexpectedBlock(unsigned Code);
};
template <typename Derived>
class BitstreamBlockParserHelper : public BitstreamBlockParserHelperBase {
protected:
using BitstreamBlockParserHelperBase::BitstreamBlockParserHelperBase;
Derived &derived() { return *static_cast<Derived *>(this); }
/// Parse a record and fill in the fields in the parser.
/// The subclass must statically override this method.
Error parseRecord(unsigned Code) = delete;
/// Parse a subblock and fill in the fields in the parser.
/// The subclass can statically override this method.
Error parseSubBlock(unsigned Code) { return unexpectedBlock(Code); }
public:
/// Enter, parse, and leave this bitstream block. This expects the
/// BitstreamCursor to be right after the SubBlock entry (i.e. after calling
/// expectBlock).
Error parseBlock() {
if (Error E = enterBlock())
return E;
// Stop when there is nothing to read anymore or when we encounter an
// END_BLOCK.
while (true) {
Expected<BitstreamEntry> Next = Stream.advance();
if (!Next)
return Next.takeError();
switch (Next->Kind) {
case BitstreamEntry::SubBlock:
if (Error E = derived().parseSubBlock(Next->ID))
return E;
continue;
case BitstreamEntry::EndBlock:
return Error::success();
case BitstreamEntry::Record:
if (Error E = derived().parseRecord(Next->ID))
return E;
continue;
case BitstreamEntry::Error:
return error("Unexpected end of bitstream.");
}
llvm_unreachable("Unexpected BitstreamEntry");
}
}
};
/// Helper to parse a META_BLOCK for a bitstream remark container.
class BitstreamMetaParserHelper
: public BitstreamBlockParserHelper<BitstreamMetaParserHelper> {
friend class BitstreamBlockParserHelper;
public:
struct ContainerInfo {
uint64_t Version;
uint64_t Type;
};
/// The parsed content: depending on the container type, some fields might
/// be empty.
std::optional<ContainerInfo> Container;
std::optional<uint64_t> RemarkVersion;
std::optional<StringRef> ExternalFilePath;
std::optional<StringRef> StrTabBuf;
BitstreamMetaParserHelper(BitstreamCursor &Stream)
: BitstreamBlockParserHelper(Stream, META_BLOCK_ID, MetaBlockName) {}
protected:
Error parseRecord(unsigned Code);
};
/// Helper to parse a REMARK_BLOCK for a bitstream remark container.
class BitstreamRemarkParserHelper
: public BitstreamBlockParserHelper<BitstreamRemarkParserHelper> {
friend class BitstreamBlockParserHelper;
protected:
SmallVector<uint64_t, 5> Record;
StringRef RecordBlob;
unsigned RecordID;
public:
struct RemarkLoc {
uint64_t SourceFileNameIdx;
uint64_t SourceLine;
uint64_t SourceColumn;
};
struct Argument {
std::optional<uint64_t> KeyIdx;
std::optional<uint64_t> ValueIdx;
std::optional<RemarkLoc> Loc;
Argument(std::optional<uint64_t> KeyIdx, std::optional<uint64_t> ValueIdx)
: KeyIdx(KeyIdx), ValueIdx(ValueIdx) {}
};
/// The parsed content: depending on the remark, some fields might be empty.
std::optional<uint8_t> Type;
std::optional<uint64_t> RemarkNameIdx;
std::optional<uint64_t> PassNameIdx;
std::optional<uint64_t> FunctionNameIdx;
std::optional<uint64_t> Hotness;
std::optional<RemarkLoc> Loc;
SmallVector<Argument, 8> Args;
BitstreamRemarkParserHelper(BitstreamCursor &Stream)
: BitstreamBlockParserHelper(Stream, REMARK_BLOCK_ID, RemarkBlockName) {}
/// Clear helper state and parse next remark block.
Error parseNext();
protected:
Error parseRecord(unsigned Code);
Error handleRecord();
};
/// Helper to parse any bitstream remark container.
struct BitstreamParserHelper {
/// The Bitstream reader.
BitstreamCursor Stream;
/// The block info block.
BitstreamBlockInfo BlockInfo;
/// Start parsing at \p Buffer.
BitstreamParserHelper(StringRef Buffer);
/// Parse and validate the magic number.
Error expectMagic();
/// Advance to the meta block
Error advanceToMetaBlock();
/// Parse the block info block containing all the abbrevs.
/// This needs to be called before calling any other parsing function.
Error parseBlockInfoBlock();
/// Return true if the parser reached the end of the stream.
bool atEndOfStream() { return Stream.AtEndOfStream(); }
};
/// Parses and holds the state of the latest parsed remark.
struct BitstreamRemarkParser : public RemarkParser {
/// The buffer to parse.
BitstreamParserHelper ParserHelper;
/// The string table used for parsing strings.
std::optional<ParsedStringTable> StrTab;
/// Temporary remark buffer used when the remarks are stored separately.
std::unique_ptr<MemoryBuffer> TmpRemarkBuffer;
/// The common metadata used to decide how to parse the buffer.
/// This is filled when parsing the metadata block.
uint64_t ContainerVersion = 0;
uint64_t RemarkVersion = 0;
BitstreamRemarkContainerType ContainerType =
BitstreamRemarkContainerType::Standalone;
/// Wether the parser is ready to parse remarks.
bool ReadyToParseRemarks = false;
/// Create a parser that expects to find a string table embedded in the
/// stream.
explicit BitstreamRemarkParser(StringRef Buf);
Expected<std::unique_ptr<Remark>> next() override;
static bool classof(const RemarkParser *P) {
return P->ParserFormat == Format::Bitstream;
}
/// Parse and process the metadata of the buffer.
Error parseMeta();
/// Parse a Bitstream remark.
Expected<std::unique_ptr<Remark>> parseRemark();
private:
Error processCommonMeta(BitstreamMetaParserHelper &Helper);
Error processStandaloneMeta(BitstreamMetaParserHelper &Helper);
Error processSeparateRemarksFileMeta(BitstreamMetaParserHelper &Helper);
Error processSeparateRemarksMetaMeta(BitstreamMetaParserHelper &Helper);
Error processExternalFilePath(BitstreamMetaParserHelper &Helper);
Error processStrTab(BitstreamMetaParserHelper &Helper);
Error processRemarkVersion(BitstreamMetaParserHelper &Helper);
Expected<std::unique_ptr<Remark>>
processRemark(BitstreamRemarkParserHelper &Helper);
};
Expected<std::unique_ptr<BitstreamRemarkParser>> createBitstreamParserFromMeta(
StringRef Buf,
std::optional<StringRef> ExternalFilePrependPath = std::nullopt);
} // end namespace remarks
} // end namespace llvm
#endif /* LLVM_LIB_REMARKS_BITSTREAM_REMARK_PARSER_H */