//===- 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 &R, StringRef Str) { append_range(R, Str); } static void setRecordName(unsigned RecordID, BitstreamWriter &Bitstream, SmallVectorImpl &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 &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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(); 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(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 RemarkVersion, Optional StrTab, Optional 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(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(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 &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 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(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 BitstreamRemarkSerializer::metaSerializer( raw_ostream &OS, Optional ExternalFilename) { assert(Helper.ContainerType != BitstreamRemarkContainerType::SeparateRemarksMeta); bool IsStandalone = Helper.ContainerType == BitstreamRemarkContainerType::Standalone; return std::make_unique( OS, IsStandalone ? BitstreamRemarkContainerType::Standalone : BitstreamRemarkContainerType::SeparateRemarksMeta, &*StrTab, ExternalFilename); } void BitstreamMetaSerializer::emit() { Helper->setupBlockInfo(); Helper->emitMetaBlock(CurrentContainerVersion, CurrentRemarkVersion, StrTab, ExternalFilename); Helper->flushToStream(OS); }