387 lines
14 KiB
C++
387 lines
14 KiB
C++
|
//===- BitstreamRemarkSerializer.cpp --------------------------------------===//
|
||
|
//
|
||
|
// 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 implementation of the LLVM bitstream remark serializer
|
||
|
// using LLVM's bitstream writer.
|
||
|
//
|
||
|
//===----------------------------------------------------------------------===//
|
||
|
|
||
|
#include "llvm/Remarks/BitstreamRemarkSerializer.h"
|
||
|
|
||
|
using namespace llvm;
|
||
|
using namespace llvm::remarks;
|
||
|
|
||
|
BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper(
|
||
|
BitstreamRemarkContainerType ContainerType)
|
||
|
: Encoded(), R(), Bitstream(Encoded), ContainerType(ContainerType) {}
|
||
|
|
||
|
static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) {
|
||
|
for (const char C : Str)
|
||
|
R.push_back(C);
|
||
|
}
|
||
|
|
||
|
static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream,
|
||
|
SmallVectorImpl<uint64_t> &R, StringRef Str) {
|
||
|
R.clear();
|
||
|
R.push_back(RecordID);
|
||
|
push(R, Str);
|
||
|
Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETRECORDNAME, R);
|
||
|
}
|
||
|
|
||
|
static void initBlock(unsigned BlockID, BitstreamWriter &Bitstream,
|
||
|
SmallVectorImpl<uint64_t> &R, StringRef Str) {
|
||
|
R.clear();
|
||
|
R.push_back(BlockID);
|
||
|
Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_SETBID, R);
|
||
|
|
||
|
R.clear();
|
||
|
push(R, Str);
|
||
|
Bitstream.EmitRecord(bitc::BLOCKINFO_CODE_BLOCKNAME, R);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupMetaBlockInfo() {
|
||
|
// Setup the metadata block.
|
||
|
initBlock(META_BLOCK_ID, Bitstream, R, MetaBlockName);
|
||
|
|
||
|
// The container information.
|
||
|
setRecordName(RECORD_META_CONTAINER_INFO, Bitstream, R,
|
||
|
MetaContainerInfoName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_META_CONTAINER_INFO));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 2)); // Type.
|
||
|
RecordMetaContainerInfoAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupMetaRemarkVersion() {
|
||
|
setRecordName(RECORD_META_REMARK_VERSION, Bitstream, R,
|
||
|
MetaRemarkVersionName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_META_REMARK_VERSION));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Version.
|
||
|
RecordMetaRemarkVersionAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::emitMetaRemarkVersion(
|
||
|
uint64_t RemarkVersion) {
|
||
|
// The remark version is emitted only if we emit remarks.
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_META_REMARK_VERSION);
|
||
|
R.push_back(RemarkVersion);
|
||
|
Bitstream.EmitRecordWithAbbrev(RecordMetaRemarkVersionAbbrevID, R);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupMetaStrTab() {
|
||
|
setRecordName(RECORD_META_STRTAB, Bitstream, R, MetaStrTabName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_META_STRTAB));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Raw table.
|
||
|
RecordMetaStrTabAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::emitMetaStrTab(
|
||
|
const StringTable &StrTab) {
|
||
|
// The string table is not emitted if we emit remarks separately.
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_META_STRTAB);
|
||
|
|
||
|
// Serialize to a blob.
|
||
|
std::string Buf;
|
||
|
raw_string_ostream OS(Buf);
|
||
|
StrTab.serialize(OS);
|
||
|
StringRef Blob = OS.str();
|
||
|
Bitstream.EmitRecordWithBlob(RecordMetaStrTabAbbrevID, R, Blob);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupMetaExternalFile() {
|
||
|
setRecordName(RECORD_META_EXTERNAL_FILE, Bitstream, R, MetaExternalFileName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_META_EXTERNAL_FILE));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Blob)); // Filename.
|
||
|
RecordMetaExternalFileAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(META_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::emitMetaExternalFile(StringRef Filename) {
|
||
|
// The external file is emitted only if we emit the separate metadata.
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_META_EXTERNAL_FILE);
|
||
|
Bitstream.EmitRecordWithBlob(RecordMetaExternalFileAbbrevID, R, Filename);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupRemarkBlockInfo() {
|
||
|
// Setup the remark block.
|
||
|
initBlock(REMARK_BLOCK_ID, Bitstream, R, RemarkBlockName);
|
||
|
|
||
|
// The header of a remark.
|
||
|
{
|
||
|
setRecordName(RECORD_REMARK_HEADER, Bitstream, R, RemarkHeaderName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HEADER));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 3)); // Type
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Remark Name
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 6)); // Pass name
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Function name
|
||
|
RecordRemarkHeaderAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
// The location of a remark.
|
||
|
{
|
||
|
setRecordName(RECORD_REMARK_DEBUG_LOC, Bitstream, R, RemarkDebugLocName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_DEBUG_LOC));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
|
||
|
RecordRemarkDebugLocAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
// The hotness of a remark.
|
||
|
{
|
||
|
setRecordName(RECORD_REMARK_HOTNESS, Bitstream, R, RemarkHotnessName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_HOTNESS));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 8)); // Hotness
|
||
|
RecordRemarkHotnessAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
// An argument entry with a debug location attached.
|
||
|
{
|
||
|
setRecordName(RECORD_REMARK_ARG_WITH_DEBUGLOC, Bitstream, R,
|
||
|
RemarkArgWithDebugLocName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITH_DEBUGLOC));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // File
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Line
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::Fixed, 32)); // Column
|
||
|
RecordRemarkArgWithDebugLocAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
|
||
|
// An argument entry with no debug location attached.
|
||
|
{
|
||
|
setRecordName(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC, Bitstream, R,
|
||
|
RemarkArgWithoutDebugLocName);
|
||
|
|
||
|
auto Abbrev = std::make_shared<BitCodeAbbrev>();
|
||
|
Abbrev->Add(BitCodeAbbrevOp(RECORD_REMARK_ARG_WITHOUT_DEBUGLOC));
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Key
|
||
|
Abbrev->Add(BitCodeAbbrevOp(BitCodeAbbrevOp::VBR, 7)); // Value
|
||
|
RecordRemarkArgWithoutDebugLocAbbrevID =
|
||
|
Bitstream.EmitBlockInfoAbbrev(REMARK_BLOCK_ID, Abbrev);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::setupBlockInfo() {
|
||
|
// Emit magic number.
|
||
|
for (const char C : ContainerMagic)
|
||
|
Bitstream.Emit(static_cast<unsigned>(C), 8);
|
||
|
|
||
|
Bitstream.EnterBlockInfoBlock();
|
||
|
|
||
|
// Setup the main metadata. Depending on the container type, we'll setup the
|
||
|
// required records next.
|
||
|
setupMetaBlockInfo();
|
||
|
|
||
|
switch (ContainerType) {
|
||
|
case BitstreamRemarkContainerType::SeparateRemarksMeta:
|
||
|
// Needs a string table that the separate remark file is using.
|
||
|
setupMetaStrTab();
|
||
|
// Needs to know where the external remarks file is.
|
||
|
setupMetaExternalFile();
|
||
|
break;
|
||
|
case BitstreamRemarkContainerType::SeparateRemarksFile:
|
||
|
// Contains remarks: emit the version.
|
||
|
setupMetaRemarkVersion();
|
||
|
// Contains remarks: emit the remark abbrevs.
|
||
|
setupRemarkBlockInfo();
|
||
|
break;
|
||
|
case BitstreamRemarkContainerType::Standalone:
|
||
|
// Contains remarks: emit the version.
|
||
|
setupMetaRemarkVersion();
|
||
|
// Needs a string table.
|
||
|
setupMetaStrTab();
|
||
|
// Contains remarks: emit the remark abbrevs.
|
||
|
setupRemarkBlockInfo();
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Bitstream.ExitBlock();
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::emitMetaBlock(
|
||
|
uint64_t ContainerVersion, Optional<uint64_t> RemarkVersion,
|
||
|
Optional<const StringTable *> StrTab, Optional<StringRef> Filename) {
|
||
|
// Emit the meta block
|
||
|
Bitstream.EnterSubblock(META_BLOCK_ID, 3);
|
||
|
|
||
|
// The container version and type.
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_META_CONTAINER_INFO);
|
||
|
R.push_back(ContainerVersion);
|
||
|
R.push_back(static_cast<uint64_t>(ContainerType));
|
||
|
Bitstream.EmitRecordWithAbbrev(RecordMetaContainerInfoAbbrevID, R);
|
||
|
|
||
|
switch (ContainerType) {
|
||
|
case BitstreamRemarkContainerType::SeparateRemarksMeta:
|
||
|
assert(StrTab != None && *StrTab != nullptr);
|
||
|
emitMetaStrTab(**StrTab);
|
||
|
assert(Filename != None);
|
||
|
emitMetaExternalFile(*Filename);
|
||
|
break;
|
||
|
case BitstreamRemarkContainerType::SeparateRemarksFile:
|
||
|
assert(RemarkVersion != None);
|
||
|
emitMetaRemarkVersion(*RemarkVersion);
|
||
|
break;
|
||
|
case BitstreamRemarkContainerType::Standalone:
|
||
|
assert(RemarkVersion != None);
|
||
|
emitMetaRemarkVersion(*RemarkVersion);
|
||
|
assert(StrTab != None && *StrTab != nullptr);
|
||
|
emitMetaStrTab(**StrTab);
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
Bitstream.ExitBlock();
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::emitRemarkBlock(const Remark &Remark,
|
||
|
StringTable &StrTab) {
|
||
|
Bitstream.EnterSubblock(REMARK_BLOCK_ID, 4);
|
||
|
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_REMARK_HEADER);
|
||
|
R.push_back(static_cast<uint64_t>(Remark.RemarkType));
|
||
|
R.push_back(StrTab.add(Remark.RemarkName).first);
|
||
|
R.push_back(StrTab.add(Remark.PassName).first);
|
||
|
R.push_back(StrTab.add(Remark.FunctionName).first);
|
||
|
Bitstream.EmitRecordWithAbbrev(RecordRemarkHeaderAbbrevID, R);
|
||
|
|
||
|
if (const Optional<RemarkLocation> &Loc = Remark.Loc) {
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_REMARK_DEBUG_LOC);
|
||
|
R.push_back(StrTab.add(Loc->SourceFilePath).first);
|
||
|
R.push_back(Loc->SourceLine);
|
||
|
R.push_back(Loc->SourceColumn);
|
||
|
Bitstream.EmitRecordWithAbbrev(RecordRemarkDebugLocAbbrevID, R);
|
||
|
}
|
||
|
|
||
|
if (Optional<uint64_t> Hotness = Remark.Hotness) {
|
||
|
R.clear();
|
||
|
R.push_back(RECORD_REMARK_HOTNESS);
|
||
|
R.push_back(*Hotness);
|
||
|
Bitstream.EmitRecordWithAbbrev(RecordRemarkHotnessAbbrevID, R);
|
||
|
}
|
||
|
|
||
|
for (const Argument &Arg : Remark.Args) {
|
||
|
R.clear();
|
||
|
unsigned Key = StrTab.add(Arg.Key).first;
|
||
|
unsigned Val = StrTab.add(Arg.Val).first;
|
||
|
bool HasDebugLoc = Arg.Loc != None;
|
||
|
R.push_back(HasDebugLoc ? RECORD_REMARK_ARG_WITH_DEBUGLOC
|
||
|
: RECORD_REMARK_ARG_WITHOUT_DEBUGLOC);
|
||
|
R.push_back(Key);
|
||
|
R.push_back(Val);
|
||
|
if (HasDebugLoc) {
|
||
|
R.push_back(StrTab.add(Arg.Loc->SourceFilePath).first);
|
||
|
R.push_back(Arg.Loc->SourceLine);
|
||
|
R.push_back(Arg.Loc->SourceColumn);
|
||
|
}
|
||
|
Bitstream.EmitRecordWithAbbrev(HasDebugLoc
|
||
|
? RecordRemarkArgWithDebugLocAbbrevID
|
||
|
: RecordRemarkArgWithoutDebugLocAbbrevID,
|
||
|
R);
|
||
|
}
|
||
|
Bitstream.ExitBlock();
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializerHelper::flushToStream(raw_ostream &OS) {
|
||
|
OS.write(Encoded.data(), Encoded.size());
|
||
|
Encoded.clear();
|
||
|
}
|
||
|
|
||
|
StringRef BitstreamRemarkSerializerHelper::getBuffer() {
|
||
|
return StringRef(Encoded.data(), Encoded.size());
|
||
|
}
|
||
|
|
||
|
BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS,
|
||
|
SerializerMode Mode)
|
||
|
: RemarkSerializer(Format::Bitstream, OS, Mode),
|
||
|
Helper(BitstreamRemarkContainerType::SeparateRemarksFile) {
|
||
|
assert(Mode == SerializerMode::Separate &&
|
||
|
"For SerializerMode::Standalone, a pre-filled string table needs to "
|
||
|
"be provided.");
|
||
|
// We always use a string table with bitstream.
|
||
|
StrTab.emplace();
|
||
|
}
|
||
|
|
||
|
BitstreamRemarkSerializer::BitstreamRemarkSerializer(raw_ostream &OS,
|
||
|
SerializerMode Mode,
|
||
|
StringTable StrTabIn)
|
||
|
: RemarkSerializer(Format::Bitstream, OS, Mode),
|
||
|
Helper(Mode == SerializerMode::Separate
|
||
|
? BitstreamRemarkContainerType::SeparateRemarksFile
|
||
|
: BitstreamRemarkContainerType::Standalone) {
|
||
|
StrTab = std::move(StrTabIn);
|
||
|
}
|
||
|
|
||
|
void BitstreamRemarkSerializer::emit(const Remark &Remark) {
|
||
|
if (!DidSetUp) {
|
||
|
// Emit the metadata that is embedded in the remark file.
|
||
|
// If we're in standalone mode, serialize the string table as well.
|
||
|
bool IsStandalone =
|
||
|
Helper.ContainerType == BitstreamRemarkContainerType::Standalone;
|
||
|
BitstreamMetaSerializer MetaSerializer(
|
||
|
OS, Helper,
|
||
|
IsStandalone ? &*StrTab : Optional<const StringTable *>(None));
|
||
|
MetaSerializer.emit();
|
||
|
DidSetUp = true;
|
||
|
}
|
||
|
|
||
|
assert(DidSetUp &&
|
||
|
"The Block info block and the meta block were not emitted yet.");
|
||
|
Helper.emitRemarkBlock(Remark, *StrTab);
|
||
|
|
||
|
Helper.flushToStream(OS);
|
||
|
}
|
||
|
|
||
|
std::unique_ptr<MetaSerializer> BitstreamRemarkSerializer::metaSerializer(
|
||
|
raw_ostream &OS, Optional<StringRef> ExternalFilename) {
|
||
|
assert(Helper.ContainerType !=
|
||
|
BitstreamRemarkContainerType::SeparateRemarksMeta);
|
||
|
bool IsStandalone =
|
||
|
Helper.ContainerType == BitstreamRemarkContainerType::Standalone;
|
||
|
return std::make_unique<BitstreamMetaSerializer>(
|
||
|
OS,
|
||
|
IsStandalone ? BitstreamRemarkContainerType::Standalone
|
||
|
: BitstreamRemarkContainerType::SeparateRemarksMeta,
|
||
|
&*StrTab, ExternalFilename);
|
||
|
}
|
||
|
|
||
|
void BitstreamMetaSerializer::emit() {
|
||
|
Helper->setupBlockInfo();
|
||
|
Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab,
|
||
|
ExternalFilename);
|
||
|
Helper->flushToStream(OS);
|
||
|
}
|