diff options
author | orivej <orivej@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
---|---|---|
committer | Daniil Cherednik <dcherednik@yandex-team.ru> | 2022-02-10 16:44:49 +0300 |
commit | 718c552901d703c502ccbefdfc3c9028d608b947 (patch) | |
tree | 46534a98bbefcd7b1f3faa5b52c138ab27db75b7 /contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp | |
parent | e9656aae26e0358d5378e5b63dcac5c8dbe0e4d0 (diff) | |
download | ydb-718c552901d703c502ccbefdfc3c9028d608b947.tar.gz |
Restoring authorship annotation for <orivej@yandex-team.ru>. Commit 1 of 2.
Diffstat (limited to 'contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp')
-rw-r--r-- | contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp | 906 |
1 files changed, 453 insertions, 453 deletions
diff --git a/contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp b/contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp index 3d9996c931..5b9efdfac7 100644 --- a/contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp +++ b/contrib/libs/llvm12/lib/Remarks/YAMLRemarkParser.cpp @@ -1,453 +1,453 @@ -//===- YAMLRemarkParser.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 "YAMLRemarkParser.h" -#include "llvm/ADT/StringSwitch.h" -#include "llvm/Support/Endian.h" -#include "llvm/Support/Path.h" - -using namespace llvm; -using namespace llvm::remarks; - -char YAMLParseError::ID = 0; - -static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { - assert(Ctx && "Expected non-null Ctx in diagnostic handler."); - std::string &Message = *static_cast<std::string *>(Ctx); - assert(Message.empty() && "Expected an empty string."); - raw_string_ostream OS(Message); - Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false, - /*ShowKindLabels*/ true); - OS << '\n'; - OS.flush(); -} - -YAMLParseError::YAMLParseError(StringRef Msg, SourceMgr &SM, - yaml::Stream &Stream, yaml::Node &Node) { - // 1) Set up a diagnostic handler to avoid errors being printed out to - // stderr. - // 2) Use the stream to print the error with the associated node. - // 3) The stream will use the source manager to print the error, which will - // call the diagnostic handler. - // 4) The diagnostic handler will stream the error directly into this object's - // Message member, which is used when logging is asked for. - auto OldDiagHandler = SM.getDiagHandler(); - auto OldDiagCtx = SM.getDiagContext(); - SM.setDiagHandler(handleDiagnostic, &Message); - Stream.printError(&Node, Twine(Msg) + Twine('\n')); - // Restore the old handlers. - SM.setDiagHandler(OldDiagHandler, OldDiagCtx); -} - -static SourceMgr setupSM(std::string &LastErrorMessage) { - SourceMgr SM; - SM.setDiagHandler(handleDiagnostic, &LastErrorMessage); - return SM; -} - -// Parse the magic number. This function returns true if this represents remark -// metadata, false otherwise. -static Expected<bool> parseMagic(StringRef &Buf) { - if (!Buf.consume_front(remarks::Magic)) - return false; - - if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1))) - return createStringError(std::errc::illegal_byte_sequence, - "Expecting \\0 after magic number."); - return true; -} - -static Expected<uint64_t> parseVersion(StringRef &Buf) { - if (Buf.size() < sizeof(uint64_t)) - return createStringError(std::errc::illegal_byte_sequence, - "Expecting version number."); - - uint64_t Version = - support::endian::read<uint64_t, support::little, support::unaligned>( - Buf.data()); - if (Version != remarks::CurrentRemarkVersion) - return createStringError(std::errc::illegal_byte_sequence, - "Mismatching remark version. Got %" PRId64 - ", expected %" PRId64 ".", - Version, remarks::CurrentRemarkVersion); - Buf = Buf.drop_front(sizeof(uint64_t)); - return Version; -} - -static Expected<uint64_t> parseStrTabSize(StringRef &Buf) { - if (Buf.size() < sizeof(uint64_t)) - return createStringError(std::errc::illegal_byte_sequence, - "Expecting string table size."); - uint64_t StrTabSize = - support::endian::read<uint64_t, support::little, support::unaligned>( - Buf.data()); - Buf = Buf.drop_front(sizeof(uint64_t)); - return StrTabSize; -} - -static Expected<ParsedStringTable> parseStrTab(StringRef &Buf, - uint64_t StrTabSize) { - if (Buf.size() < StrTabSize) - return createStringError(std::errc::illegal_byte_sequence, - "Expecting string table."); - - // Attach the string table to the parser. - ParsedStringTable Result(StringRef(Buf.data(), StrTabSize)); - Buf = Buf.drop_front(StrTabSize); - return Expected<ParsedStringTable>(std::move(Result)); -} - -Expected<std::unique_ptr<YAMLRemarkParser>> -remarks::createYAMLParserFromMeta(StringRef Buf, - Optional<ParsedStringTable> StrTab, - Optional<StringRef> ExternalFilePrependPath) { - // We now have a magic number. The metadata has to be correct. - Expected<bool> isMeta = parseMagic(Buf); - if (!isMeta) - return isMeta.takeError(); - // If it's not recognized as metadata, roll back. - std::unique_ptr<MemoryBuffer> SeparateBuf; - if (*isMeta) { - Expected<uint64_t> Version = parseVersion(Buf); - if (!Version) - return Version.takeError(); - - Expected<uint64_t> StrTabSize = parseStrTabSize(Buf); - if (!StrTabSize) - return StrTabSize.takeError(); - - // If the size of string table is not 0, try to build one. - if (*StrTabSize != 0) { - if (StrTab) - return createStringError(std::errc::illegal_byte_sequence, - "String table already provided."); - Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, *StrTabSize); - if (!MaybeStrTab) - return MaybeStrTab.takeError(); - StrTab = std::move(*MaybeStrTab); - } - // If it starts with "---", there is no external file. - if (!Buf.startswith("---")) { - // At this point, we expect Buf to contain the external file path. - StringRef ExternalFilePath = Buf; - SmallString<80> FullPath; - if (ExternalFilePrependPath) - FullPath = *ExternalFilePrependPath; - sys::path::append(FullPath, ExternalFilePath); - - // Try to open the file and start parsing from there. - ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = - MemoryBuffer::getFile(FullPath); - if (std::error_code EC = BufferOrErr.getError()) - return createFileError(FullPath, EC); - - // Keep the buffer alive. - SeparateBuf = std::move(*BufferOrErr); - Buf = SeparateBuf->getBuffer(); - } - } - - std::unique_ptr<YAMLRemarkParser> Result = - StrTab - ? std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(*StrTab)) - : std::make_unique<YAMLRemarkParser>(Buf); - if (SeparateBuf) - Result->SeparateBuf = std::move(SeparateBuf); - return std::move(Result); -} - -YAMLRemarkParser::YAMLRemarkParser(StringRef Buf) - : YAMLRemarkParser(Buf, None) {} - -YAMLRemarkParser::YAMLRemarkParser(StringRef Buf, - Optional<ParsedStringTable> StrTab) - : RemarkParser{Format::YAML}, StrTab(std::move(StrTab)), LastErrorMessage(), - SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {} - -Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) { - return make_error<YAMLParseError>(Message, SM, Stream, Node); -} - -Error YAMLRemarkParser::error() { - if (LastErrorMessage.empty()) - return Error::success(); - Error E = make_error<YAMLParseError>(LastErrorMessage); - LastErrorMessage.clear(); - return E; -} - -Expected<std::unique_ptr<Remark>> -YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) { - if (Error E = error()) - return std::move(E); - - yaml::Node *YAMLRoot = RemarkEntry.getRoot(); - if (!YAMLRoot) { - return createStringError(std::make_error_code(std::errc::invalid_argument), - "not a valid YAML file."); - } - - auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot); - if (!Root) - return error("document root is not of mapping type.", *YAMLRoot); - - std::unique_ptr<Remark> Result = std::make_unique<Remark>(); - Remark &TheRemark = *Result; - - // First, the type. It needs special handling since is not part of the - // key-value stream. - Expected<Type> T = parseType(*Root); - if (!T) - return T.takeError(); - else - TheRemark.RemarkType = *T; - - // Then, parse the fields, one by one. - for (yaml::KeyValueNode &RemarkField : *Root) { - Expected<StringRef> MaybeKey = parseKey(RemarkField); - if (!MaybeKey) - return MaybeKey.takeError(); - StringRef KeyName = *MaybeKey; - - if (KeyName == "Pass") { - if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) - TheRemark.PassName = *MaybeStr; - else - return MaybeStr.takeError(); - } else if (KeyName == "Name") { - if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) - TheRemark.RemarkName = *MaybeStr; - else - return MaybeStr.takeError(); - } else if (KeyName == "Function") { - if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) - TheRemark.FunctionName = *MaybeStr; - else - return MaybeStr.takeError(); - } else if (KeyName == "Hotness") { - if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField)) - TheRemark.Hotness = *MaybeU; - else - return MaybeU.takeError(); - } else if (KeyName == "DebugLoc") { - if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField)) - TheRemark.Loc = *MaybeLoc; - else - return MaybeLoc.takeError(); - } else if (KeyName == "Args") { - auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); - if (!Args) - return error("wrong value type for key.", RemarkField); - - for (yaml::Node &Arg : *Args) { - if (Expected<Argument> MaybeArg = parseArg(Arg)) - TheRemark.Args.push_back(*MaybeArg); - else - return MaybeArg.takeError(); - } - } else { - return error("unknown key.", RemarkField); - } - } - - // Check if any of the mandatory fields are missing. - if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() || - TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty()) - return error("Type, Pass, Name or Function missing.", - *RemarkEntry.getRoot()); - - return std::move(Result); -} - -Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) { - auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) - .Case("!Passed", remarks::Type::Passed) - .Case("!Missed", remarks::Type::Missed) - .Case("!Analysis", remarks::Type::Analysis) - .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute) - .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing) - .Case("!Failure", remarks::Type::Failure) - .Default(remarks::Type::Unknown); - if (Type == remarks::Type::Unknown) - return error("expected a remark tag.", Node); - return Type; -} - -Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) { - if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey())) - return Key->getRawValue(); - - return error("key is not a string.", Node); -} - -Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) { - auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); - if (!Value) - return error("expected a value of scalar type.", Node); - StringRef Result = Value->getRawValue(); - - if (Result.front() == '\'') - Result = Result.drop_front(); - - if (Result.back() == '\'') - Result = Result.drop_back(); - - return Result; -} - -Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) { - SmallVector<char, 4> Tmp; - auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); - if (!Value) - return error("expected a value of scalar type.", Node); - unsigned UnsignedValue = 0; - if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) - return error("expected a value of integer type.", *Value); - return UnsignedValue; -} - -Expected<RemarkLocation> -YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) { - auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); - if (!DebugLoc) - return error("expected a value of mapping type.", Node); - - Optional<StringRef> File; - Optional<unsigned> Line; - Optional<unsigned> Column; - - for (yaml::KeyValueNode &DLNode : *DebugLoc) { - Expected<StringRef> MaybeKey = parseKey(DLNode); - if (!MaybeKey) - return MaybeKey.takeError(); - StringRef KeyName = *MaybeKey; - - if (KeyName == "File") { - if (Expected<StringRef> MaybeStr = parseStr(DLNode)) - File = *MaybeStr; - else - return MaybeStr.takeError(); - } else if (KeyName == "Column") { - if (Expected<unsigned> MaybeU = parseUnsigned(DLNode)) - Column = *MaybeU; - else - return MaybeU.takeError(); - } else if (KeyName == "Line") { - if (Expected<unsigned> MaybeU = parseUnsigned(DLNode)) - Line = *MaybeU; - else - return MaybeU.takeError(); - } else { - return error("unknown entry in DebugLoc map.", DLNode); - } - } - - // If any of the debug loc fields is missing, return an error. - if (!File || !Line || !Column) - return error("DebugLoc node incomplete.", Node); - - return RemarkLocation{*File, *Line, *Column}; -} - -Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) { - auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); - if (!ArgMap) - return error("expected a value of mapping type.", Node); - - Optional<StringRef> KeyStr; - Optional<StringRef> ValueStr; - Optional<RemarkLocation> Loc; - - for (yaml::KeyValueNode &ArgEntry : *ArgMap) { - Expected<StringRef> MaybeKey = parseKey(ArgEntry); - if (!MaybeKey) - return MaybeKey.takeError(); - StringRef KeyName = *MaybeKey; - - // Try to parse debug locs. - if (KeyName == "DebugLoc") { - // Can't have multiple DebugLoc entries per argument. - if (Loc) - return error("only one DebugLoc entry is allowed per argument.", - ArgEntry); - - if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) { - Loc = *MaybeLoc; - continue; - } else - return MaybeLoc.takeError(); - } - - // If we already have a string, error out. - if (ValueStr) - return error("only one string entry is allowed per argument.", ArgEntry); - - // Try to parse the value. - if (Expected<StringRef> MaybeStr = parseStr(ArgEntry)) - ValueStr = *MaybeStr; - else - return MaybeStr.takeError(); - - // Keep the key from the string. - KeyStr = KeyName; - } - - if (!KeyStr) - return error("argument key is missing.", *ArgMap); - if (!ValueStr) - return error("argument value is missing.", *ArgMap); - - return Argument{*KeyStr, *ValueStr, Loc}; -} - -Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() { - if (YAMLIt == Stream.end()) - return make_error<EndOfFileError>(); - - Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*YAMLIt); - if (!MaybeResult) { - // Avoid garbage input, set the iterator to the end. - YAMLIt = Stream.end(); - return MaybeResult.takeError(); - } - - ++YAMLIt; - - return std::move(*MaybeResult); -} - -Expected<StringRef> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode &Node) { - auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); - if (!Value) - return error("expected a value of scalar type.", Node); - StringRef Result; - // If we have a string table, parse it as an unsigned. - unsigned StrID = 0; - if (Expected<unsigned> MaybeStrID = parseUnsigned(Node)) - StrID = *MaybeStrID; - else - return MaybeStrID.takeError(); - - if (Expected<StringRef> Str = (*StrTab)[StrID]) - Result = *Str; - else - return Str.takeError(); - - if (Result.front() == '\'') - Result = Result.drop_front(); - - if (Result.back() == '\'') - Result = Result.drop_back(); - - return Result; -} +//===- YAMLRemarkParser.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 "YAMLRemarkParser.h" +#include "llvm/ADT/StringSwitch.h" +#include "llvm/Support/Endian.h" +#include "llvm/Support/Path.h" + +using namespace llvm; +using namespace llvm::remarks; + +char YAMLParseError::ID = 0; + +static void handleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { + assert(Ctx && "Expected non-null Ctx in diagnostic handler."); + std::string &Message = *static_cast<std::string *>(Ctx); + assert(Message.empty() && "Expected an empty string."); + raw_string_ostream OS(Message); + Diag.print(/*ProgName=*/nullptr, OS, /*ShowColors*/ false, + /*ShowKindLabels*/ true); + OS << '\n'; + OS.flush(); +} + +YAMLParseError::YAMLParseError(StringRef Msg, SourceMgr &SM, + yaml::Stream &Stream, yaml::Node &Node) { + // 1) Set up a diagnostic handler to avoid errors being printed out to + // stderr. + // 2) Use the stream to print the error with the associated node. + // 3) The stream will use the source manager to print the error, which will + // call the diagnostic handler. + // 4) The diagnostic handler will stream the error directly into this object's + // Message member, which is used when logging is asked for. + auto OldDiagHandler = SM.getDiagHandler(); + auto OldDiagCtx = SM.getDiagContext(); + SM.setDiagHandler(handleDiagnostic, &Message); + Stream.printError(&Node, Twine(Msg) + Twine('\n')); + // Restore the old handlers. + SM.setDiagHandler(OldDiagHandler, OldDiagCtx); +} + +static SourceMgr setupSM(std::string &LastErrorMessage) { + SourceMgr SM; + SM.setDiagHandler(handleDiagnostic, &LastErrorMessage); + return SM; +} + +// Parse the magic number. This function returns true if this represents remark +// metadata, false otherwise. +static Expected<bool> parseMagic(StringRef &Buf) { + if (!Buf.consume_front(remarks::Magic)) + return false; + + if (Buf.size() < 1 || !Buf.consume_front(StringRef("\0", 1))) + return createStringError(std::errc::illegal_byte_sequence, + "Expecting \\0 after magic number."); + return true; +} + +static Expected<uint64_t> parseVersion(StringRef &Buf) { + if (Buf.size() < sizeof(uint64_t)) + return createStringError(std::errc::illegal_byte_sequence, + "Expecting version number."); + + uint64_t Version = + support::endian::read<uint64_t, support::little, support::unaligned>( + Buf.data()); + if (Version != remarks::CurrentRemarkVersion) + return createStringError(std::errc::illegal_byte_sequence, + "Mismatching remark version. Got %" PRId64 + ", expected %" PRId64 ".", + Version, remarks::CurrentRemarkVersion); + Buf = Buf.drop_front(sizeof(uint64_t)); + return Version; +} + +static Expected<uint64_t> parseStrTabSize(StringRef &Buf) { + if (Buf.size() < sizeof(uint64_t)) + return createStringError(std::errc::illegal_byte_sequence, + "Expecting string table size."); + uint64_t StrTabSize = + support::endian::read<uint64_t, support::little, support::unaligned>( + Buf.data()); + Buf = Buf.drop_front(sizeof(uint64_t)); + return StrTabSize; +} + +static Expected<ParsedStringTable> parseStrTab(StringRef &Buf, + uint64_t StrTabSize) { + if (Buf.size() < StrTabSize) + return createStringError(std::errc::illegal_byte_sequence, + "Expecting string table."); + + // Attach the string table to the parser. + ParsedStringTable Result(StringRef(Buf.data(), StrTabSize)); + Buf = Buf.drop_front(StrTabSize); + return Expected<ParsedStringTable>(std::move(Result)); +} + +Expected<std::unique_ptr<YAMLRemarkParser>> +remarks::createYAMLParserFromMeta(StringRef Buf, + Optional<ParsedStringTable> StrTab, + Optional<StringRef> ExternalFilePrependPath) { + // We now have a magic number. The metadata has to be correct. + Expected<bool> isMeta = parseMagic(Buf); + if (!isMeta) + return isMeta.takeError(); + // If it's not recognized as metadata, roll back. + std::unique_ptr<MemoryBuffer> SeparateBuf; + if (*isMeta) { + Expected<uint64_t> Version = parseVersion(Buf); + if (!Version) + return Version.takeError(); + + Expected<uint64_t> StrTabSize = parseStrTabSize(Buf); + if (!StrTabSize) + return StrTabSize.takeError(); + + // If the size of string table is not 0, try to build one. + if (*StrTabSize != 0) { + if (StrTab) + return createStringError(std::errc::illegal_byte_sequence, + "String table already provided."); + Expected<ParsedStringTable> MaybeStrTab = parseStrTab(Buf, *StrTabSize); + if (!MaybeStrTab) + return MaybeStrTab.takeError(); + StrTab = std::move(*MaybeStrTab); + } + // If it starts with "---", there is no external file. + if (!Buf.startswith("---")) { + // At this point, we expect Buf to contain the external file path. + StringRef ExternalFilePath = Buf; + SmallString<80> FullPath; + if (ExternalFilePrependPath) + FullPath = *ExternalFilePrependPath; + sys::path::append(FullPath, ExternalFilePath); + + // Try to open the file and start parsing from there. + ErrorOr<std::unique_ptr<MemoryBuffer>> BufferOrErr = + MemoryBuffer::getFile(FullPath); + if (std::error_code EC = BufferOrErr.getError()) + return createFileError(FullPath, EC); + + // Keep the buffer alive. + SeparateBuf = std::move(*BufferOrErr); + Buf = SeparateBuf->getBuffer(); + } + } + + std::unique_ptr<YAMLRemarkParser> Result = + StrTab + ? std::make_unique<YAMLStrTabRemarkParser>(Buf, std::move(*StrTab)) + : std::make_unique<YAMLRemarkParser>(Buf); + if (SeparateBuf) + Result->SeparateBuf = std::move(SeparateBuf); + return std::move(Result); +} + +YAMLRemarkParser::YAMLRemarkParser(StringRef Buf) + : YAMLRemarkParser(Buf, None) {} + +YAMLRemarkParser::YAMLRemarkParser(StringRef Buf, + Optional<ParsedStringTable> StrTab) + : RemarkParser{Format::YAML}, StrTab(std::move(StrTab)), LastErrorMessage(), + SM(setupSM(LastErrorMessage)), Stream(Buf, SM), YAMLIt(Stream.begin()) {} + +Error YAMLRemarkParser::error(StringRef Message, yaml::Node &Node) { + return make_error<YAMLParseError>(Message, SM, Stream, Node); +} + +Error YAMLRemarkParser::error() { + if (LastErrorMessage.empty()) + return Error::success(); + Error E = make_error<YAMLParseError>(LastErrorMessage); + LastErrorMessage.clear(); + return E; +} + +Expected<std::unique_ptr<Remark>> +YAMLRemarkParser::parseRemark(yaml::Document &RemarkEntry) { + if (Error E = error()) + return std::move(E); + + yaml::Node *YAMLRoot = RemarkEntry.getRoot(); + if (!YAMLRoot) { + return createStringError(std::make_error_code(std::errc::invalid_argument), + "not a valid YAML file."); + } + + auto *Root = dyn_cast<yaml::MappingNode>(YAMLRoot); + if (!Root) + return error("document root is not of mapping type.", *YAMLRoot); + + std::unique_ptr<Remark> Result = std::make_unique<Remark>(); + Remark &TheRemark = *Result; + + // First, the type. It needs special handling since is not part of the + // key-value stream. + Expected<Type> T = parseType(*Root); + if (!T) + return T.takeError(); + else + TheRemark.RemarkType = *T; + + // Then, parse the fields, one by one. + for (yaml::KeyValueNode &RemarkField : *Root) { + Expected<StringRef> MaybeKey = parseKey(RemarkField); + if (!MaybeKey) + return MaybeKey.takeError(); + StringRef KeyName = *MaybeKey; + + if (KeyName == "Pass") { + if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) + TheRemark.PassName = *MaybeStr; + else + return MaybeStr.takeError(); + } else if (KeyName == "Name") { + if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) + TheRemark.RemarkName = *MaybeStr; + else + return MaybeStr.takeError(); + } else if (KeyName == "Function") { + if (Expected<StringRef> MaybeStr = parseStr(RemarkField)) + TheRemark.FunctionName = *MaybeStr; + else + return MaybeStr.takeError(); + } else if (KeyName == "Hotness") { + if (Expected<unsigned> MaybeU = parseUnsigned(RemarkField)) + TheRemark.Hotness = *MaybeU; + else + return MaybeU.takeError(); + } else if (KeyName == "DebugLoc") { + if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(RemarkField)) + TheRemark.Loc = *MaybeLoc; + else + return MaybeLoc.takeError(); + } else if (KeyName == "Args") { + auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); + if (!Args) + return error("wrong value type for key.", RemarkField); + + for (yaml::Node &Arg : *Args) { + if (Expected<Argument> MaybeArg = parseArg(Arg)) + TheRemark.Args.push_back(*MaybeArg); + else + return MaybeArg.takeError(); + } + } else { + return error("unknown key.", RemarkField); + } + } + + // Check if any of the mandatory fields are missing. + if (TheRemark.RemarkType == Type::Unknown || TheRemark.PassName.empty() || + TheRemark.RemarkName.empty() || TheRemark.FunctionName.empty()) + return error("Type, Pass, Name or Function missing.", + *RemarkEntry.getRoot()); + + return std::move(Result); +} + +Expected<Type> YAMLRemarkParser::parseType(yaml::MappingNode &Node) { + auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) + .Case("!Passed", remarks::Type::Passed) + .Case("!Missed", remarks::Type::Missed) + .Case("!Analysis", remarks::Type::Analysis) + .Case("!AnalysisFPCommute", remarks::Type::AnalysisFPCommute) + .Case("!AnalysisAliasing", remarks::Type::AnalysisAliasing) + .Case("!Failure", remarks::Type::Failure) + .Default(remarks::Type::Unknown); + if (Type == remarks::Type::Unknown) + return error("expected a remark tag.", Node); + return Type; +} + +Expected<StringRef> YAMLRemarkParser::parseKey(yaml::KeyValueNode &Node) { + if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey())) + return Key->getRawValue(); + + return error("key is not a string.", Node); +} + +Expected<StringRef> YAMLRemarkParser::parseStr(yaml::KeyValueNode &Node) { + auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); + if (!Value) + return error("expected a value of scalar type.", Node); + StringRef Result = Value->getRawValue(); + + if (Result.front() == '\'') + Result = Result.drop_front(); + + if (Result.back() == '\'') + Result = Result.drop_back(); + + return Result; +} + +Expected<unsigned> YAMLRemarkParser::parseUnsigned(yaml::KeyValueNode &Node) { + SmallVector<char, 4> Tmp; + auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); + if (!Value) + return error("expected a value of scalar type.", Node); + unsigned UnsignedValue = 0; + if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) + return error("expected a value of integer type.", *Value); + return UnsignedValue; +} + +Expected<RemarkLocation> +YAMLRemarkParser::parseDebugLoc(yaml::KeyValueNode &Node) { + auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); + if (!DebugLoc) + return error("expected a value of mapping type.", Node); + + Optional<StringRef> File; + Optional<unsigned> Line; + Optional<unsigned> Column; + + for (yaml::KeyValueNode &DLNode : *DebugLoc) { + Expected<StringRef> MaybeKey = parseKey(DLNode); + if (!MaybeKey) + return MaybeKey.takeError(); + StringRef KeyName = *MaybeKey; + + if (KeyName == "File") { + if (Expected<StringRef> MaybeStr = parseStr(DLNode)) + File = *MaybeStr; + else + return MaybeStr.takeError(); + } else if (KeyName == "Column") { + if (Expected<unsigned> MaybeU = parseUnsigned(DLNode)) + Column = *MaybeU; + else + return MaybeU.takeError(); + } else if (KeyName == "Line") { + if (Expected<unsigned> MaybeU = parseUnsigned(DLNode)) + Line = *MaybeU; + else + return MaybeU.takeError(); + } else { + return error("unknown entry in DebugLoc map.", DLNode); + } + } + + // If any of the debug loc fields is missing, return an error. + if (!File || !Line || !Column) + return error("DebugLoc node incomplete.", Node); + + return RemarkLocation{*File, *Line, *Column}; +} + +Expected<Argument> YAMLRemarkParser::parseArg(yaml::Node &Node) { + auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); + if (!ArgMap) + return error("expected a value of mapping type.", Node); + + Optional<StringRef> KeyStr; + Optional<StringRef> ValueStr; + Optional<RemarkLocation> Loc; + + for (yaml::KeyValueNode &ArgEntry : *ArgMap) { + Expected<StringRef> MaybeKey = parseKey(ArgEntry); + if (!MaybeKey) + return MaybeKey.takeError(); + StringRef KeyName = *MaybeKey; + + // Try to parse debug locs. + if (KeyName == "DebugLoc") { + // Can't have multiple DebugLoc entries per argument. + if (Loc) + return error("only one DebugLoc entry is allowed per argument.", + ArgEntry); + + if (Expected<RemarkLocation> MaybeLoc = parseDebugLoc(ArgEntry)) { + Loc = *MaybeLoc; + continue; + } else + return MaybeLoc.takeError(); + } + + // If we already have a string, error out. + if (ValueStr) + return error("only one string entry is allowed per argument.", ArgEntry); + + // Try to parse the value. + if (Expected<StringRef> MaybeStr = parseStr(ArgEntry)) + ValueStr = *MaybeStr; + else + return MaybeStr.takeError(); + + // Keep the key from the string. + KeyStr = KeyName; + } + + if (!KeyStr) + return error("argument key is missing.", *ArgMap); + if (!ValueStr) + return error("argument value is missing.", *ArgMap); + + return Argument{*KeyStr, *ValueStr, Loc}; +} + +Expected<std::unique_ptr<Remark>> YAMLRemarkParser::next() { + if (YAMLIt == Stream.end()) + return make_error<EndOfFileError>(); + + Expected<std::unique_ptr<Remark>> MaybeResult = parseRemark(*YAMLIt); + if (!MaybeResult) { + // Avoid garbage input, set the iterator to the end. + YAMLIt = Stream.end(); + return MaybeResult.takeError(); + } + + ++YAMLIt; + + return std::move(*MaybeResult); +} + +Expected<StringRef> YAMLStrTabRemarkParser::parseStr(yaml::KeyValueNode &Node) { + auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); + if (!Value) + return error("expected a value of scalar type.", Node); + StringRef Result; + // If we have a string table, parse it as an unsigned. + unsigned StrID = 0; + if (Expected<unsigned> MaybeStrID = parseUnsigned(Node)) + StrID = *MaybeStrID; + else + return MaybeStrID.takeError(); + + if (Expected<StringRef> Str = (*StrTab)[StrID]) + Result = *Str; + else + return Str.takeError(); + + if (Result.front() == '\'') + Result = Result.drop_front(); + + if (Result.back() == '\'') + Result = Result.drop_back(); + + return Result; +} |