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);
 | |
| }
 |