| //===- 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" | |
| #include "llvm/Remarks/Remark.h" | |
| #include <optional> | |
| using namespace llvm; | |
| using namespace llvm::remarks; | |
| BitstreamRemarkSerializerHelper::BitstreamRemarkSerializerHelper( | |
| BitstreamRemarkContainerType ContainerType) | |
| : Bitstream(Encoded), ContainerType(ContainerType) {} | |
| static void push(SmallVectorImpl<uint64_t> &R, StringRef Str) { | |
| append_range(R, Str); | |
| } | |
| 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, std::optional<uint64_t> RemarkVersion, | |
| std::optional<const StringTable *> StrTab, | |
| std::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 != std::nullopt && *StrTab != nullptr); | |
| emitMetaStrTab(**StrTab); | |
| assert(Filename != std::nullopt); | |
| emitMetaExternalFile(*Filename); | |
| break; | |
| case BitstreamRemarkContainerType::SeparateRemarksFile: | |
| assert(RemarkVersion != std::nullopt); | |
| emitMetaRemarkVersion(*RemarkVersion); | |
| break; | |
| case BitstreamRemarkContainerType::Standalone: | |
| assert(RemarkVersion != std::nullopt); | |
| emitMetaRemarkVersion(*RemarkVersion); | |
| assert(StrTab != std::nullopt && *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 std::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 (std::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 != std::nullopt; | |
| 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 | |
| : std::optional<const StringTable *>(std::nullopt)); | |
| 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, std::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); | |
| } |