| //===- BitstreamRemarkParser.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 utility methods used by clients that want to use the | |
| // parser for remark diagnostics in LLVM. | |
| // | |
| //===----------------------------------------------------------------------===// | |
| #include "llvm/Remarks/BitstreamRemarkParser.h" | |
| #include "BitstreamRemarkParser.h" | |
| #include "llvm/Remarks/Remark.h" | |
| #include "llvm/Support/MemoryBuffer.h" | |
| #include "llvm/Support/Path.h" | |
| #include <optional> | |
| using namespace llvm; | |
| using namespace llvm::remarks; | |
| static Error unknownRecord(const char *BlockName, unsigned RecordID) { | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing %s: unknown record entry (%lu).", BlockName, | |
| RecordID); | |
| } | |
| static Error malformedRecord(const char *BlockName, const char *RecordName) { | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing %s: malformed record entry (%s).", BlockName, | |
| RecordName); | |
| } | |
| BitstreamMetaParserHelper::BitstreamMetaParserHelper( | |
| BitstreamCursor &Stream, BitstreamBlockInfo &BlockInfo) | |
| : Stream(Stream), BlockInfo(BlockInfo) {} | |
| /// Parse a record and fill in the fields in the parser. | |
| static Error parseRecord(BitstreamMetaParserHelper &Parser, unsigned Code) { | |
| BitstreamCursor &Stream = Parser.Stream; | |
| // Note: 2 is used here because it's the max number of fields we have per | |
| // record. | |
| SmallVector<uint64_t, 2> Record; | |
| StringRef Blob; | |
| Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); | |
| if (!RecordID) | |
| return RecordID.takeError(); | |
| switch (*RecordID) { | |
| case RECORD_META_CONTAINER_INFO: { | |
| if (Record.size() != 2) | |
| return malformedRecord("BLOCK_META", "RECORD_META_CONTAINER_INFO"); | |
| Parser.ContainerVersion = Record[0]; | |
| Parser.ContainerType = Record[1]; | |
| break; | |
| } | |
| case RECORD_META_REMARK_VERSION: { | |
| if (Record.size() != 1) | |
| return malformedRecord("BLOCK_META", "RECORD_META_REMARK_VERSION"); | |
| Parser.RemarkVersion = Record[0]; | |
| break; | |
| } | |
| case RECORD_META_STRTAB: { | |
| if (Record.size() != 0) | |
| return malformedRecord("BLOCK_META", "RECORD_META_STRTAB"); | |
| Parser.StrTabBuf = Blob; | |
| break; | |
| } | |
| case RECORD_META_EXTERNAL_FILE: { | |
| if (Record.size() != 0) | |
| return malformedRecord("BLOCK_META", "RECORD_META_EXTERNAL_FILE"); | |
| Parser.ExternalFilePath = Blob; | |
| break; | |
| } | |
| default: | |
| return unknownRecord("BLOCK_META", *RecordID); | |
| } | |
| return Error::success(); | |
| } | |
| BitstreamRemarkParser::BitstreamRemarkParser(StringRef Buf) | |
| : RemarkParser(Format::Bitstream), ParserHelper(Buf) {} | |
| BitstreamRemarkParser::BitstreamRemarkParser(StringRef Buf, | |
| ParsedStringTable StrTab) | |
| : RemarkParser(Format::Bitstream), | |
| ParserHelper(Buf), | |
| StrTab(std::move(StrTab)) {} | |
| BitstreamRemarkParserHelper::BitstreamRemarkParserHelper( | |
| BitstreamCursor &Stream) | |
| : Stream(Stream) {} | |
| /// Parse a record and fill in the fields in the parser. | |
| static Error parseRecord(BitstreamRemarkParserHelper &Parser, unsigned Code) { | |
| BitstreamCursor &Stream = Parser.Stream; | |
| // Note: 5 is used here because it's the max number of fields we have per | |
| // record. | |
| SmallVector<uint64_t, 5> Record; | |
| StringRef Blob; | |
| Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob); | |
| if (!RecordID) | |
| return RecordID.takeError(); | |
| switch (*RecordID) { | |
| case RECORD_REMARK_HEADER: { | |
| if (Record.size() != 4) | |
| return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HEADER"); | |
| Parser.Type = Record[0]; | |
| Parser.RemarkNameIdx = Record[1]; | |
| Parser.PassNameIdx = Record[2]; | |
| Parser.FunctionNameIdx = Record[3]; | |
| break; | |
| } | |
| case RECORD_REMARK_DEBUG_LOC: { | |
| if (Record.size() != 3) | |
| return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_DEBUG_LOC"); | |
| Parser.SourceFileNameIdx = Record[0]; | |
| Parser.SourceLine = Record[1]; | |
| Parser.SourceColumn = Record[2]; | |
| break; | |
| } | |
| case RECORD_REMARK_HOTNESS: { | |
| if (Record.size() != 1) | |
| return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_HOTNESS"); | |
| Parser.Hotness = Record[0]; | |
| break; | |
| } | |
| case RECORD_REMARK_ARG_WITH_DEBUGLOC: { | |
| if (Record.size() != 5) | |
| return malformedRecord("BLOCK_REMARK", "RECORD_REMARK_ARG_WITH_DEBUGLOC"); | |
| // Create a temporary argument. Use that as a valid memory location for this | |
| // argument entry. | |
| Parser.TmpArgs.emplace_back(); | |
| Parser.TmpArgs.back().KeyIdx = Record[0]; | |
| Parser.TmpArgs.back().ValueIdx = Record[1]; | |
| Parser.TmpArgs.back().SourceFileNameIdx = Record[2]; | |
| Parser.TmpArgs.back().SourceLine = Record[3]; | |
| Parser.TmpArgs.back().SourceColumn = Record[4]; | |
| Parser.Args = | |
| ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); | |
| break; | |
| } | |
| case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC: { | |
| if (Record.size() != 2) | |
| return malformedRecord("BLOCK_REMARK", | |
| "RECORD_REMARK_ARG_WITHOUT_DEBUGLOC"); | |
| // Create a temporary argument. Use that as a valid memory location for this | |
| // argument entry. | |
| Parser.TmpArgs.emplace_back(); | |
| Parser.TmpArgs.back().KeyIdx = Record[0]; | |
| Parser.TmpArgs.back().ValueIdx = Record[1]; | |
| Parser.Args = | |
| ArrayRef<BitstreamRemarkParserHelper::Argument>(Parser.TmpArgs); | |
| break; | |
| } | |
| default: | |
| return unknownRecord("BLOCK_REMARK", *RecordID); | |
| } | |
| return Error::success(); | |
| } | |
| template <typename T> | |
| static Error parseBlock(T &ParserHelper, unsigned BlockID, | |
| const char *BlockName) { | |
| BitstreamCursor &Stream = ParserHelper.Stream; | |
| Expected<BitstreamEntry> Next = Stream.advance(); | |
| if (!Next) | |
| return Next.takeError(); | |
| if (Next->Kind != BitstreamEntry::SubBlock || Next->ID != BlockID) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing %s: expecting [ENTER_SUBBLOCK, %s, ...].", | |
| BlockName, BlockName); | |
| if (Stream.EnterSubBlock(BlockID)) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while entering %s.", BlockName); | |
| // Stop when there is nothing to read anymore or when we encounter an | |
| // END_BLOCK. | |
| while (!Stream.AtEndOfStream()) { | |
| Next = Stream.advance(); | |
| if (!Next) | |
| return Next.takeError(); | |
| switch (Next->Kind) { | |
| case BitstreamEntry::EndBlock: | |
| return Error::success(); | |
| case BitstreamEntry::Error: | |
| case BitstreamEntry::SubBlock: | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing %s: expecting records.", BlockName); | |
| case BitstreamEntry::Record: | |
| if (Error E = parseRecord(ParserHelper, Next->ID)) | |
| return E; | |
| continue; | |
| } | |
| } | |
| // If we're here, it means we didn't get an END_BLOCK yet, but we're at the | |
| // end of the stream. In this case, error. | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing %s: unterminated block.", BlockName); | |
| } | |
| Error BitstreamMetaParserHelper::parse() { | |
| return parseBlock(*this, META_BLOCK_ID, "META_BLOCK"); | |
| } | |
| Error BitstreamRemarkParserHelper::parse() { | |
| return parseBlock(*this, REMARK_BLOCK_ID, "REMARK_BLOCK"); | |
| } | |
| BitstreamParserHelper::BitstreamParserHelper(StringRef Buffer) | |
| : Stream(Buffer) {} | |
| Expected<std::array<char, 4>> BitstreamParserHelper::parseMagic() { | |
| std::array<char, 4> Result; | |
| for (unsigned i = 0; i < 4; ++i) | |
| if (Expected<unsigned> R = Stream.Read(8)) | |
| Result[i] = *R; | |
| else | |
| return R.takeError(); | |
| return Result; | |
| } | |
| Error BitstreamParserHelper::parseBlockInfoBlock() { | |
| Expected<BitstreamEntry> Next = Stream.advance(); | |
| if (!Next) | |
| return Next.takeError(); | |
| if (Next->Kind != BitstreamEntry::SubBlock || | |
| Next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, " | |
| "BLOCKINFO_BLOCK, ...]."); | |
| Expected<std::optional<BitstreamBlockInfo>> MaybeBlockInfo = | |
| Stream.ReadBlockInfoBlock(); | |
| if (!MaybeBlockInfo) | |
| return MaybeBlockInfo.takeError(); | |
| if (!*MaybeBlockInfo) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCKINFO_BLOCK."); | |
| BlockInfo = **MaybeBlockInfo; | |
| Stream.setBlockInfo(&BlockInfo); | |
| return Error::success(); | |
| } | |
| static Expected<bool> isBlock(BitstreamCursor &Stream, unsigned BlockID) { | |
| bool Result = false; | |
| uint64_t PreviousBitNo = Stream.GetCurrentBitNo(); | |
| Expected<BitstreamEntry> Next = Stream.advance(); | |
| if (!Next) | |
| return Next.takeError(); | |
| switch (Next->Kind) { | |
| case BitstreamEntry::SubBlock: | |
| // Check for the block id. | |
| Result = Next->ID == BlockID; | |
| break; | |
| case BitstreamEntry::Error: | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Unexpected error while parsing bitstream."); | |
| default: | |
| Result = false; | |
| break; | |
| } | |
| if (Error E = Stream.JumpToBit(PreviousBitNo)) | |
| return std::move(E); | |
| return Result; | |
| } | |
| Expected<bool> BitstreamParserHelper::isMetaBlock() { | |
| return isBlock(Stream, META_BLOCK_ID); | |
| } | |
| Expected<bool> BitstreamParserHelper::isRemarkBlock() { | |
| return isBlock(Stream, META_BLOCK_ID); | |
| } | |
| static Error validateMagicNumber(StringRef MagicNumber) { | |
| if (MagicNumber != remarks::ContainerMagic) | |
| return createStringError(std::make_error_code(std::errc::invalid_argument), | |
| "Unknown magic number: expecting %s, got %.4s.", | |
| remarks::ContainerMagic.data(), MagicNumber.data()); | |
| return Error::success(); | |
| } | |
| static Error advanceToMetaBlock(BitstreamParserHelper &Helper) { | |
| Expected<std::array<char, 4>> MagicNumber = Helper.parseMagic(); | |
| if (!MagicNumber) | |
| return MagicNumber.takeError(); | |
| if (Error E = validateMagicNumber( | |
| StringRef(MagicNumber->data(), MagicNumber->size()))) | |
| return E; | |
| if (Error E = Helper.parseBlockInfoBlock()) | |
| return E; | |
| Expected<bool> isMetaBlock = Helper.isMetaBlock(); | |
| if (!isMetaBlock) | |
| return isMetaBlock.takeError(); | |
| if (!*isMetaBlock) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Expecting META_BLOCK after the BLOCKINFO_BLOCK."); | |
| return Error::success(); | |
| } | |
| Expected<std::unique_ptr<BitstreamRemarkParser>> | |
| remarks::createBitstreamParserFromMeta( | |
| StringRef Buf, std::optional<ParsedStringTable> StrTab, | |
| std::optional<StringRef> ExternalFilePrependPath) { | |
| BitstreamParserHelper Helper(Buf); | |
| Expected<std::array<char, 4>> MagicNumber = Helper.parseMagic(); | |
| if (!MagicNumber) | |
| return MagicNumber.takeError(); | |
| if (Error E = validateMagicNumber( | |
| StringRef(MagicNumber->data(), MagicNumber->size()))) | |
| return std::move(E); | |
| auto Parser = | |
| StrTab ? std::make_unique<BitstreamRemarkParser>(Buf, std::move(*StrTab)) | |
| : std::make_unique<BitstreamRemarkParser>(Buf); | |
| if (ExternalFilePrependPath) | |
| Parser->ExternalFilePrependPath = std::string(*ExternalFilePrependPath); | |
| return std::move(Parser); | |
| } | |
| Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() { | |
| if (ParserHelper.atEndOfStream()) | |
| return make_error<EndOfFileError>(); | |
| if (!ReadyToParseRemarks) { | |
| if (Error E = parseMeta()) | |
| return std::move(E); | |
| ReadyToParseRemarks = true; | |
| } | |
| return parseRemark(); | |
| } | |
| Error BitstreamRemarkParser::parseMeta() { | |
| // Advance and to the meta block. | |
| if (Error E = advanceToMetaBlock(ParserHelper)) | |
| return E; | |
| BitstreamMetaParserHelper MetaHelper(ParserHelper.Stream, | |
| ParserHelper.BlockInfo); | |
| if (Error E = MetaHelper.parse()) | |
| return E; | |
| if (Error E = processCommonMeta(MetaHelper)) | |
| return E; | |
| switch (ContainerType) { | |
| case BitstreamRemarkContainerType::Standalone: | |
| return processStandaloneMeta(MetaHelper); | |
| case BitstreamRemarkContainerType::SeparateRemarksFile: | |
| return processSeparateRemarksFileMeta(MetaHelper); | |
| case BitstreamRemarkContainerType::SeparateRemarksMeta: | |
| return processSeparateRemarksMetaMeta(MetaHelper); | |
| } | |
| llvm_unreachable("Unknown BitstreamRemarkContainerType enum"); | |
| } | |
| Error BitstreamRemarkParser::processCommonMeta( | |
| BitstreamMetaParserHelper &Helper) { | |
| if (std::optional<uint64_t> Version = Helper.ContainerVersion) | |
| ContainerVersion = *Version; | |
| else | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: missing container version."); | |
| if (std::optional<uint8_t> Type = Helper.ContainerType) { | |
| // Always >= BitstreamRemarkContainerType::First since it's unsigned. | |
| if (*Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last)) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: invalid container type."); | |
| ContainerType = static_cast<BitstreamRemarkContainerType>(*Type); | |
| } else | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: missing container type."); | |
| return Error::success(); | |
| } | |
| static Error processStrTab(BitstreamRemarkParser &P, | |
| std::optional<StringRef> StrTabBuf) { | |
| if (!StrTabBuf) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: missing string table."); | |
| // Parse and assign the string table. | |
| P.StrTab.emplace(*StrTabBuf); | |
| return Error::success(); | |
| } | |
| static Error processRemarkVersion(BitstreamRemarkParser &P, | |
| std::optional<uint64_t> RemarkVersion) { | |
| if (!RemarkVersion) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: missing remark version."); | |
| P.RemarkVersion = *RemarkVersion; | |
| return Error::success(); | |
| } | |
| Error BitstreamRemarkParser::processExternalFilePath( | |
| std::optional<StringRef> ExternalFilePath) { | |
| if (!ExternalFilePath) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_META: missing external file path."); | |
| SmallString<80> FullPath(ExternalFilePrependPath); | |
| sys::path::append(FullPath, *ExternalFilePath); | |
| // External file: open the external file, parse it, check if its metadata | |
| // matches the one from the separate metadata, then replace the current parser | |
| // with the one parsing the remarks. | |
| ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = | |
| MemoryBuffer::getFile(FullPath); | |
| if (std::error_code EC = BufferOrErr.getError()) | |
| return createFileError(FullPath, EC); | |
| TmpRemarkBuffer = std::move(*BufferOrErr); | |
| // Don't try to parse the file if it's empty. | |
| if (TmpRemarkBuffer->getBufferSize() == 0) | |
| return make_error<EndOfFileError>(); | |
| // Create a separate parser used for parsing the separate file. | |
| ParserHelper = BitstreamParserHelper(TmpRemarkBuffer->getBuffer()); | |
| // Advance and check until we can parse the meta block. | |
| if (Error E = advanceToMetaBlock(ParserHelper)) | |
| return E; | |
| // Parse the meta from the separate file. | |
| // Note: here we overwrite the BlockInfo with the one from the file. This will | |
| // be used to parse the rest of the file. | |
| BitstreamMetaParserHelper SeparateMetaHelper(ParserHelper.Stream, | |
| ParserHelper.BlockInfo); | |
| if (Error E = SeparateMetaHelper.parse()) | |
| return E; | |
| uint64_t PreviousContainerVersion = ContainerVersion; | |
| if (Error E = processCommonMeta(SeparateMetaHelper)) | |
| return E; | |
| if (ContainerType != BitstreamRemarkContainerType::SeparateRemarksFile) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing external file's BLOCK_META: wrong container " | |
| "type."); | |
| if (PreviousContainerVersion != ContainerVersion) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing external file's BLOCK_META: mismatching versions: " | |
| "original meta: %lu, external file meta: %lu.", | |
| PreviousContainerVersion, ContainerVersion); | |
| // Process the meta from the separate file. | |
| return processSeparateRemarksFileMeta(SeparateMetaHelper); | |
| } | |
| Error BitstreamRemarkParser::processStandaloneMeta( | |
| BitstreamMetaParserHelper &Helper) { | |
| if (Error E = processStrTab(*this, Helper.StrTabBuf)) | |
| return E; | |
| return processRemarkVersion(*this, Helper.RemarkVersion); | |
| } | |
| Error BitstreamRemarkParser::processSeparateRemarksFileMeta( | |
| BitstreamMetaParserHelper &Helper) { | |
| return processRemarkVersion(*this, Helper.RemarkVersion); | |
| } | |
| Error BitstreamRemarkParser::processSeparateRemarksMetaMeta( | |
| BitstreamMetaParserHelper &Helper) { | |
| if (Error E = processStrTab(*this, Helper.StrTabBuf)) | |
| return E; | |
| return processExternalFilePath(Helper.ExternalFilePath); | |
| } | |
| Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::parseRemark() { | |
| BitstreamRemarkParserHelper RemarkHelper(ParserHelper.Stream); | |
| if (Error E = RemarkHelper.parse()) | |
| return std::move(E); | |
| return processRemark(RemarkHelper); | |
| } | |
| Expected<std::unique_ptr<Remark>> | |
| BitstreamRemarkParser::processRemark(BitstreamRemarkParserHelper &Helper) { | |
| std::unique_ptr<Remark> Result = std::make_unique<Remark>(); | |
| Remark &R = *Result; | |
| if (StrTab == std::nullopt) | |
| return createStringError( | |
| std::make_error_code(std::errc::invalid_argument), | |
| "Error while parsing BLOCK_REMARK: missing string table."); | |
| if (!Helper.Type) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing remark type."); | |
| // Always >= Type::First since it's unsigned. | |
| if (*Helper.Type > static_cast<uint8_t>(Type::Last)) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: unknown remark type."); | |
| R.RemarkType = static_cast<Type>(*Helper.Type); | |
| if (!Helper.RemarkNameIdx) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing remark name."); | |
| if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx]) | |
| R.RemarkName = *RemarkName; | |
| else | |
| return RemarkName.takeError(); | |
| if (!Helper.PassNameIdx) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing remark pass."); | |
| if (Expected<StringRef> PassName = (*StrTab)[*Helper.PassNameIdx]) | |
| R.PassName = *PassName; | |
| else | |
| return PassName.takeError(); | |
| if (!Helper.FunctionNameIdx) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing remark function name."); | |
| if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx]) | |
| R.FunctionName = *FunctionName; | |
| else | |
| return FunctionName.takeError(); | |
| if (Helper.SourceFileNameIdx && Helper.SourceLine && Helper.SourceColumn) { | |
| Expected<StringRef> SourceFileName = (*StrTab)[*Helper.SourceFileNameIdx]; | |
| if (!SourceFileName) | |
| return SourceFileName.takeError(); | |
| R.Loc.emplace(); | |
| R.Loc->SourceFilePath = *SourceFileName; | |
| R.Loc->SourceLine = *Helper.SourceLine; | |
| R.Loc->SourceColumn = *Helper.SourceColumn; | |
| } | |
| if (Helper.Hotness) | |
| R.Hotness = *Helper.Hotness; | |
| if (!Helper.Args) | |
| return std::move(Result); | |
| for (const BitstreamRemarkParserHelper::Argument &Arg : *Helper.Args) { | |
| if (!Arg.KeyIdx) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing key in remark argument."); | |
| if (!Arg.ValueIdx) | |
| return createStringError( | |
| std::make_error_code(std::errc::illegal_byte_sequence), | |
| "Error while parsing BLOCK_REMARK: missing value in remark " | |
| "argument."); | |
| // We have at least a key and a value, create an entry. | |
| R.Args.emplace_back(); | |
| if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx]) | |
| R.Args.back().Key = *Key; | |
| else | |
| return Key.takeError(); | |
| if (Expected<StringRef> Value = (*StrTab)[*Arg.ValueIdx]) | |
| R.Args.back().Val = *Value; | |
| else | |
| return Value.takeError(); | |
| if (Arg.SourceFileNameIdx && Arg.SourceLine && Arg.SourceColumn) { | |
| if (Expected<StringRef> SourceFileName = | |
| (*StrTab)[*Arg.SourceFileNameIdx]) { | |
| R.Args.back().Loc.emplace(); | |
| R.Args.back().Loc->SourceFilePath = *SourceFileName; | |
| R.Args.back().Loc->SourceLine = *Arg.SourceLine; | |
| R.Args.back().Loc->SourceColumn = *Arg.SourceColumn; | |
| } else | |
| return SourceFileName.takeError(); | |
| } | |
| } | |
| return std::move(Result); | |
| } |