508 lines · cpp
1//===- BitstreamRemarkParser.cpp ------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// This file provides utility methods used by clients that want to use the10// parser for remark diagnostics in LLVM.11//12//===----------------------------------------------------------------------===//13 14#include "BitstreamRemarkParser.h"15#include "llvm/Support/MemoryBuffer.h"16#include "llvm/Support/Path.h"17#include <optional>18 19using namespace llvm;20using namespace llvm::remarks;21 22namespace {23 24template <typename... Ts> Error error(char const *Fmt, const Ts &...Vals) {25 std::string Buffer;26 raw_string_ostream OS(Buffer);27 OS << formatv(Fmt, Vals...);28 return make_error<StringError>(29 std::move(Buffer),30 std::make_error_code(std::errc::illegal_byte_sequence));31}32 33} // namespace34 35Error BitstreamBlockParserHelperBase::unknownRecord(unsigned AbbrevID) {36 return error("Unknown record entry ({}).", AbbrevID);37}38 39Error BitstreamBlockParserHelperBase::unexpectedRecord(StringRef RecordName) {40 return error("Unexpected record entry ({}).", RecordName);41}42 43Error BitstreamBlockParserHelperBase::malformedRecord(StringRef RecordName) {44 return error("Malformed record entry ({}).", RecordName);45}46 47Error BitstreamBlockParserHelperBase::unexpectedBlock(unsigned Code) {48 return error("Unexpected subblock ({}).", Code);49}50 51static Expected<unsigned> expectSubBlock(BitstreamCursor &Stream) {52 Expected<BitstreamEntry> Next = Stream.advance();53 if (!Next)54 return Next.takeError();55 switch (Next->Kind) {56 case BitstreamEntry::SubBlock:57 return Next->ID;58 case BitstreamEntry::Record:59 case BitstreamEntry::EndBlock:60 return error("Expected subblock, but got unexpected record.");61 case BitstreamEntry::Error:62 return error("Expected subblock, but got unexpected end of bitstream.");63 }64 llvm_unreachable("Unexpected BitstreamEntry");65}66 67Error BitstreamBlockParserHelperBase::expectBlock() {68 auto MaybeBlockID = expectSubBlock(Stream);69 if (!MaybeBlockID)70 return MaybeBlockID.takeError();71 if (*MaybeBlockID != BlockID)72 return error("Expected {} block, but got unexpected block ({}).", BlockName,73 *MaybeBlockID);74 return Error::success();75}76 77Error BitstreamBlockParserHelperBase::enterBlock() {78 if (Stream.EnterSubBlock(BlockID))79 return error("Error while entering {} block.", BlockName);80 return Error::success();81}82 83Error BitstreamMetaParserHelper::parseRecord(unsigned Code) {84 // Note: 2 is used here because it's the max number of fields we have per85 // record.86 SmallVector<uint64_t, 2> Record;87 StringRef Blob;88 Expected<unsigned> RecordID = Stream.readRecord(Code, Record, &Blob);89 if (!RecordID)90 return RecordID.takeError();91 92 switch (*RecordID) {93 case RECORD_META_CONTAINER_INFO: {94 if (Record.size() != 2)95 return malformedRecord(MetaContainerInfoName);96 Container = {Record[0], Record[1]};97 // Error immediately if container version is outdated, so the user sees an98 // explanation instead of a parser error.99 if (Container->Version != CurrentContainerVersion) {100 return ::error(101 "Unsupported remark container version (expected: {}, read: {}). "102 "Please upgrade/downgrade your toolchain to read this container.",103 CurrentContainerVersion, Container->Version);104 }105 break;106 }107 case RECORD_META_REMARK_VERSION: {108 if (Record.size() != 1)109 return malformedRecord(MetaRemarkVersionName);110 RemarkVersion = Record[0];111 // Error immediately if remark version is outdated, so the user sees an112 // explanation instead of a parser error.113 if (*RemarkVersion != CurrentRemarkVersion) {114 return ::error(115 "Unsupported remark version in container (expected: {}, read: {}). "116 "Please upgrade/downgrade your toolchain to read this container.",117 CurrentRemarkVersion, *RemarkVersion);118 }119 break;120 }121 case RECORD_META_STRTAB: {122 if (Record.size() != 0)123 return malformedRecord(MetaStrTabName);124 StrTabBuf = Blob;125 break;126 }127 case RECORD_META_EXTERNAL_FILE: {128 if (Record.size() != 0)129 return malformedRecord(MetaExternalFileName);130 ExternalFilePath = Blob;131 break;132 }133 default:134 return unknownRecord(*RecordID);135 }136 return Error::success();137}138 139Error BitstreamRemarkParserHelper::parseRecord(unsigned Code) {140 Record.clear();141 Expected<unsigned> MaybeRecordID =142 Stream.readRecord(Code, Record, &RecordBlob);143 if (!MaybeRecordID)144 return MaybeRecordID.takeError();145 RecordID = *MaybeRecordID;146 return handleRecord();147}148 149Error BitstreamRemarkParserHelper::handleRecord() {150 switch (RecordID) {151 case RECORD_REMARK_HEADER: {152 if (Record.size() != 4)153 return malformedRecord(RemarkHeaderName);154 Type = Record[0];155 RemarkNameIdx = Record[1];156 PassNameIdx = Record[2];157 FunctionNameIdx = Record[3];158 break;159 }160 case RECORD_REMARK_DEBUG_LOC: {161 if (Record.size() != 3)162 return malformedRecord(RemarkDebugLocName);163 Loc = {Record[0], Record[1], Record[2]};164 break;165 }166 case RECORD_REMARK_HOTNESS: {167 if (Record.size() != 1)168 return malformedRecord(RemarkHotnessName);169 Hotness = Record[0];170 break;171 }172 case RECORD_REMARK_ARG_WITH_DEBUGLOC: {173 if (Record.size() != 5)174 return malformedRecord(RemarkArgWithDebugLocName);175 auto &Arg = Args.emplace_back(Record[0], Record[1]);176 Arg.Loc = {Record[2], Record[3], Record[4]};177 break;178 }179 case RECORD_REMARK_ARG_WITHOUT_DEBUGLOC: {180 if (Record.size() != 2)181 return malformedRecord(RemarkArgWithoutDebugLocName);182 Args.emplace_back(Record[0], Record[1]);183 break;184 }185 default:186 return unknownRecord(RecordID);187 }188 return Error::success();189}190 191Error BitstreamRemarkParserHelper::parseNext() {192 Type.reset();193 RemarkNameIdx.reset();194 PassNameIdx.reset();195 FunctionNameIdx.reset();196 Hotness.reset();197 Loc.reset();198 Args.clear();199 200 return parseBlock();201}202 203Error BitstreamParserHelper::expectMagic() {204 std::array<char, 4> Result;205 for (unsigned I = 0; I < 4; ++I)206 if (Expected<unsigned> R = Stream.Read(8))207 Result[I] = *R;208 else209 return R.takeError();210 211 StringRef MagicNumber{Result.data(), Result.size()};212 if (MagicNumber != remarks::ContainerMagic)213 return error("Unknown magic number: expecting {}, got {}.",214 remarks::ContainerMagic, MagicNumber);215 return Error::success();216}217 218Error BitstreamParserHelper::parseBlockInfoBlock() {219 Expected<BitstreamEntry> Next = Stream.advance();220 if (!Next)221 return Next.takeError();222 if (Next->Kind != BitstreamEntry::SubBlock ||223 Next->ID != llvm::bitc::BLOCKINFO_BLOCK_ID)224 return error(225 "Error while parsing BLOCKINFO_BLOCK: expecting [ENTER_SUBBLOCK, "226 "BLOCKINFO_BLOCK, ...].");227 228 Expected<std::optional<BitstreamBlockInfo>> MaybeBlockInfo =229 Stream.ReadBlockInfoBlock();230 if (!MaybeBlockInfo)231 return MaybeBlockInfo.takeError();232 233 if (!*MaybeBlockInfo)234 return error("Missing BLOCKINFO_BLOCK.");235 236 BlockInfo = **MaybeBlockInfo;237 238 Stream.setBlockInfo(&BlockInfo);239 return Error::success();240}241 242Error BitstreamParserHelper::parseMeta() {243 if (Error E = expectMagic())244 return E;245 if (Error E = parseBlockInfoBlock())246 return E;247 248 // Parse early meta block.249 if (Error E = MetaHelper.expectBlock())250 return E;251 if (Error E = MetaHelper.parseBlock())252 return E;253 254 // Skip all Remarks blocks.255 while (!Stream.AtEndOfStream()) {256 auto MaybeBlockID = expectSubBlock(Stream);257 if (!MaybeBlockID)258 return MaybeBlockID.takeError();259 if (*MaybeBlockID == META_BLOCK_ID)260 break;261 if (*MaybeBlockID != REMARK_BLOCK_ID)262 return error("Unexpected block between meta blocks.");263 // Remember first remark block.264 if (!RemarkStartBitPos)265 RemarkStartBitPos = Stream.GetCurrentBitNo();266 if (Error E = Stream.SkipBlock())267 return E;268 }269 270 // Late meta block is optional if there are no remarks.271 if (Stream.AtEndOfStream())272 return Error::success();273 274 // Parse late meta block.275 if (Error E = MetaHelper.parseBlock())276 return E;277 return Error::success();278}279 280Error BitstreamParserHelper::parseRemark() {281 if (RemarkStartBitPos) {282 RemarkStartBitPos.reset();283 } else {284 auto MaybeBlockID = expectSubBlock(Stream);285 if (!MaybeBlockID)286 return MaybeBlockID.takeError();287 if (*MaybeBlockID != REMARK_BLOCK_ID)288 return make_error<EndOfFileError>();289 }290 return RemarksHelper->parseNext();291}292 293Expected<std::unique_ptr<BitstreamRemarkParser>>294remarks::createBitstreamParserFromMeta(295 StringRef Buf, std::optional<StringRef> ExternalFilePrependPath) {296 auto Parser = std::make_unique<BitstreamRemarkParser>(Buf);297 298 if (ExternalFilePrependPath)299 Parser->ExternalFilePrependPath = std::string(*ExternalFilePrependPath);300 301 return std::move(Parser);302}303 304BitstreamRemarkParser::BitstreamRemarkParser(StringRef Buf)305 : RemarkParser(Format::Bitstream), ParserHelper(Buf) {}306 307Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::next() {308 if (!IsMetaReady) {309 // Container is completely empty.310 if (ParserHelper->Stream.AtEndOfStream())311 return make_error<EndOfFileError>();312 313 if (Error E = parseMeta())314 return std::move(E);315 IsMetaReady = true;316 317 // Container has meta, but no remarks blocks.318 if (!ParserHelper->RemarkStartBitPos)319 return error(320 "Container is non-empty, but does not contain any remarks blocks.");321 322 if (Error E =323 ParserHelper->Stream.JumpToBit(*ParserHelper->RemarkStartBitPos))324 return std::move(E);325 ParserHelper->RemarksHelper.emplace(ParserHelper->Stream);326 }327 328 if (Error E = ParserHelper->parseRemark())329 return std::move(E);330 return processRemark();331}332 333Error BitstreamRemarkParser::parseMeta() {334 if (Error E = ParserHelper->parseMeta())335 return E;336 if (Error E = processCommonMeta())337 return E;338 339 switch (ContainerType) {340 case BitstreamRemarkContainerType::RemarksFileExternal:341 return processExternalFilePath();342 case BitstreamRemarkContainerType::RemarksFile:343 return processFileContainerMeta();344 }345 llvm_unreachable("Unknown BitstreamRemarkContainerType enum");346}347 348Error BitstreamRemarkParser::processCommonMeta() {349 auto &Helper = ParserHelper->MetaHelper;350 if (!Helper.Container)351 return Helper.error("Missing container info.");352 auto &Container = *Helper.Container;353 ContainerVersion = Container.Version;354 // Always >= BitstreamRemarkContainerType::First since it's unsigned.355 if (Container.Type > static_cast<uint8_t>(BitstreamRemarkContainerType::Last))356 return Helper.error("Invalid container type.");357 ContainerType = static_cast<BitstreamRemarkContainerType>(Container.Type);358 return Error::success();359}360 361Error BitstreamRemarkParser::processFileContainerMeta() {362 if (Error E = processRemarkVersion())363 return E;364 if (Error E = processStrTab())365 return E;366 return Error::success();367}368 369Error BitstreamRemarkParser::processStrTab() {370 auto &Helper = ParserHelper->MetaHelper;371 if (!Helper.StrTabBuf)372 return Helper.error("Missing string table.");373 // Parse and assign the string table.374 StrTab.emplace(*Helper.StrTabBuf);375 return Error::success();376}377 378Error BitstreamRemarkParser::processRemarkVersion() {379 auto &Helper = ParserHelper->MetaHelper;380 if (!Helper.RemarkVersion)381 return Helper.error("Missing remark version.");382 RemarkVersion = *Helper.RemarkVersion;383 return Error::success();384}385 386Error BitstreamRemarkParser::processExternalFilePath() {387 auto &Helper = ParserHelper->MetaHelper;388 if (!Helper.ExternalFilePath)389 return Helper.error("Missing external file path.");390 391 SmallString<80> FullPath(ExternalFilePrependPath);392 sys::path::append(FullPath, *Helper.ExternalFilePath);393 394 // External file: open the external file, parse it, check if its metadata395 // matches the one from the separate metadata, then replace the current396 // parser with the one parsing the remarks.397 ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr =398 MemoryBuffer::getFile(FullPath);399 if (std::error_code EC = BufferOrErr.getError())400 return createFileError(FullPath, EC);401 402 TmpRemarkBuffer = std::move(*BufferOrErr);403 404 // Don't try to parse the file if it's empty.405 if (TmpRemarkBuffer->getBufferSize() == 0)406 return make_error<EndOfFileError>();407 408 // Create a separate parser used for parsing the separate file.409 ParserHelper.emplace(TmpRemarkBuffer->getBuffer());410 if (Error E = parseMeta())411 return E;412 413 if (ContainerType != BitstreamRemarkContainerType::RemarksFile)414 return ParserHelper->MetaHelper.error(415 "Wrong container type in external file.");416 417 return Error::success();418}419 420Expected<std::unique_ptr<Remark>> BitstreamRemarkParser::processRemark() {421 auto &Helper = *ParserHelper->RemarksHelper;422 std::unique_ptr<Remark> Result = std::make_unique<Remark>();423 Remark &R = *Result;424 425 if (!StrTab)426 return Helper.error("Missing string table.");427 428 if (!Helper.Type)429 return Helper.error("Missing remark type.");430 431 // Always >= Type::First since it's unsigned.432 if (*Helper.Type > static_cast<uint8_t>(Type::Last))433 return Helper.error("Unknown remark type.");434 435 R.RemarkType = static_cast<Type>(*Helper.Type);436 437 if (!Helper.RemarkNameIdx)438 return Helper.error("Missing remark name.");439 440 if (Expected<StringRef> RemarkName = (*StrTab)[*Helper.RemarkNameIdx])441 R.RemarkName = *RemarkName;442 else443 return RemarkName.takeError();444 445 if (!Helper.PassNameIdx)446 return Helper.error("Missing remark pass.");447 448 if (Expected<StringRef> PassName = (*StrTab)[*Helper.PassNameIdx])449 R.PassName = *PassName;450 else451 return PassName.takeError();452 453 if (!Helper.FunctionNameIdx)454 return Helper.error("Missing remark function name.");455 456 if (Expected<StringRef> FunctionName = (*StrTab)[*Helper.FunctionNameIdx])457 R.FunctionName = *FunctionName;458 else459 return FunctionName.takeError();460 461 if (Helper.Loc) {462 Expected<StringRef> SourceFileName =463 (*StrTab)[Helper.Loc->SourceFileNameIdx];464 if (!SourceFileName)465 return SourceFileName.takeError();466 R.Loc.emplace();467 R.Loc->SourceFilePath = *SourceFileName;468 R.Loc->SourceLine = Helper.Loc->SourceLine;469 R.Loc->SourceColumn = Helper.Loc->SourceColumn;470 }471 472 if (Helper.Hotness)473 R.Hotness = *Helper.Hotness;474 475 for (const BitstreamRemarkParserHelper::Argument &Arg : Helper.Args) {476 if (!Arg.KeyIdx)477 return Helper.error("Missing key in remark argument.");478 if (!Arg.ValueIdx)479 return Helper.error("Missing value in remark argument.");480 481 // We have at least a key and a value, create an entry.482 auto &RArg = R.Args.emplace_back();483 484 if (Expected<StringRef> Key = (*StrTab)[*Arg.KeyIdx])485 RArg.Key = *Key;486 else487 return Key.takeError();488 489 if (Expected<StringRef> Value = (*StrTab)[*Arg.ValueIdx])490 RArg.Val = *Value;491 else492 return Value.takeError();493 494 if (Arg.Loc) {495 if (Expected<StringRef> SourceFileName =496 (*StrTab)[Arg.Loc->SourceFileNameIdx]) {497 RArg.Loc.emplace();498 RArg.Loc->SourceFilePath = *SourceFileName;499 RArg.Loc->SourceLine = Arg.Loc->SourceLine;500 RArg.Loc->SourceColumn = Arg.Loc->SourceColumn;501 } else502 return SourceFileName.takeError();503 }504 }505 506 return std::move(Result);507}508