| //===- 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/Support/MemoryBuffer.h" |
| #include "llvm/Support/Path.h" |
| |
| 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(); |
| } |
| |
| 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<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, Optional<ParsedStringTable> StrTab, |
| 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 (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 (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, |
| 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, |
| 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( |
| 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 == None) |
| 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); |
| } |