diff options
Diffstat (limited to 'llvm/lib')
-rw-r--r-- | llvm/lib/Remarks/CMakeLists.txt | 2 | ||||
-rw-r--r-- | llvm/lib/Remarks/Remark.cpp | 128 | ||||
-rw-r--r-- | llvm/lib/Remarks/RemarkParser.cpp | 383 | ||||
-rw-r--r-- | llvm/lib/Remarks/RemarkParserImpl.h | 29 | ||||
-rw-r--r-- | llvm/lib/Remarks/YAMLRemarkParser.cpp | 261 | ||||
-rw-r--r-- | llvm/lib/Remarks/YAMLRemarkParser.h | 136 |
6 files changed, 622 insertions, 317 deletions
diff --git a/llvm/lib/Remarks/CMakeLists.txt b/llvm/lib/Remarks/CMakeLists.txt index 83713d66a60..2ab7e8476a1 100644 --- a/llvm/lib/Remarks/CMakeLists.txt +++ b/llvm/lib/Remarks/CMakeLists.txt @@ -1,3 +1,5 @@ add_llvm_library(LLVMRemarks + Remark.cpp RemarkParser.cpp + YAMLRemarkParser.cpp ) diff --git a/llvm/lib/Remarks/Remark.cpp b/llvm/lib/Remarks/Remark.cpp new file mode 100644 index 00000000000..b4be19f47a5 --- /dev/null +++ b/llvm/lib/Remarks/Remark.cpp @@ -0,0 +1,128 @@ +//===- Remark.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 +// +//===----------------------------------------------------------------------===// +// +// Implementation of the Remark type and the C API. +// +//===----------------------------------------------------------------------===// + +#include "llvm/Remarks/Remark.h" +#include "llvm-c/Remarks.h" +#include "llvm/Support/CBindingWrapping.h" +#include "llvm/Support/raw_ostream.h" + +using namespace llvm; +using namespace llvm::remarks; + +std::string Remark::getArgsAsMsg() const { + std::string Str; + raw_string_ostream OS(Str); + for (const Argument &Arg : Args) + OS << Arg.Val; + return OS.str(); +} + +// Create wrappers for C Binding types (see CBindingWrapping.h). +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(StringRef, LLVMRemarkStringRef) + +extern "C" const char *LLVMRemarkStringGetData(LLVMRemarkStringRef String) { + return unwrap(String)->data(); +} + +extern "C" uint32_t LLVMRemarkStringGetLen(LLVMRemarkStringRef String) { + return unwrap(String)->size(); +} + +extern "C" LLVMRemarkStringRef +LLVMRemarkDebugLocGetSourceFilePath(LLVMRemarkDebugLocRef DL) { + return wrap(&unwrap(DL)->SourceFilePath); +} + +extern "C" uint32_t LLVMRemarkDebugLocGetSourceLine(LLVMRemarkDebugLocRef DL) { + return unwrap(DL)->SourceLine; +} + +extern "C" uint32_t +LLVMRemarkDebugLocGetSourceColumn(LLVMRemarkDebugLocRef DL) { + return unwrap(DL)->SourceColumn; +} + +extern "C" LLVMRemarkStringRef LLVMRemarkArgGetKey(LLVMRemarkArgRef Arg) { + return wrap(&unwrap(Arg)->Key); +} + +extern "C" LLVMRemarkStringRef LLVMRemarkArgGetValue(LLVMRemarkArgRef Arg) { + return wrap(&unwrap(Arg)->Val); +} + +extern "C" LLVMRemarkDebugLocRef +LLVMRemarkArgGetDebugLoc(LLVMRemarkArgRef Arg) { + if (const Optional<RemarkLocation> &Loc = unwrap(Arg)->Loc) + return wrap(&*Loc); + return nullptr; +} + +extern "C" LLVMRemarkType LLVMRemarkEntryGetType(LLVMRemarkEntryRef Remark) { + // Assume here that the enums can be converted both ways. + return static_cast<LLVMRemarkType>(unwrap(Remark)->RemarkType); +} + +extern "C" LLVMRemarkStringRef +LLVMRemarkEntryGetPassName(LLVMRemarkEntryRef Remark) { + return wrap(&unwrap(Remark)->PassName); +} + +extern "C" LLVMRemarkStringRef +LLVMRemarkEntryGetRemarkName(LLVMRemarkEntryRef Remark) { + return wrap(&unwrap(Remark)->RemarkName); +} + +extern "C" LLVMRemarkStringRef +LLVMRemarkEntryGetFunctionName(LLVMRemarkEntryRef Remark) { + return wrap(&unwrap(Remark)->FunctionName); +} + +extern "C" LLVMRemarkDebugLocRef +LLVMRemarkEntryGetDebugLoc(LLVMRemarkEntryRef Remark) { + if (const Optional<RemarkLocation> &Loc = unwrap(Remark)->Loc) + return wrap(&*Loc); + return nullptr; +} + +extern "C" uint64_t LLVMRemarkEntryGetHotness(LLVMRemarkEntryRef Remark) { + if (const Optional<uint64_t> &Hotness = unwrap(Remark)->Hotness) + return *Hotness; + return 0; +} + +extern "C" uint32_t LLVMRemarkEntryGetNumArgs(LLVMRemarkEntryRef Remark) { + return unwrap(Remark)->Args.size(); +} + +extern "C" LLVMRemarkArgRef +LLVMRemarkEntryGetFirstArg(LLVMRemarkEntryRef Remark) { + ArrayRef<Argument> Args = unwrap(Remark)->Args; + // No arguments to iterate on. + if (Args.empty()) + return NULL; + return reinterpret_cast<LLVMRemarkArgRef>( + const_cast<Argument *>(Args.begin())); +} + +extern "C" LLVMRemarkArgRef +LLVMRemarkEntryGetNextArg(LLVMRemarkArgRef ArgIt, LLVMRemarkEntryRef Remark) { + // No more arguments to iterate on. + if (ArgIt == NULL) + return NULL; + + auto It = (ArrayRef<Argument>::const_iterator)ArgIt; + auto Next = std::next(It); + if (Next == unwrap(Remark)->Args.end()) + return NULL; + + return reinterpret_cast<LLVMRemarkArgRef>(const_cast<Argument *>(Next)); +} diff --git a/llvm/lib/Remarks/RemarkParser.cpp b/llvm/lib/Remarks/RemarkParser.cpp index 204b8002884..a303b36c0ad 100644 --- a/llvm/lib/Remarks/RemarkParser.cpp +++ b/llvm/lib/Remarks/RemarkParser.cpp @@ -11,355 +11,104 @@ // //===----------------------------------------------------------------------===// +#include "llvm/Remarks/RemarkParser.h" +#include "YAMLRemarkParser.h" #include "llvm-c/Remarks.h" #include "llvm/ADT/STLExtras.h" -#include "llvm/Support/SourceMgr.h" -#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/CBindingWrapping.h" using namespace llvm; +using namespace llvm::remarks; -namespace { -struct YAMLRemarkParser { - /// Source manager for better error messages. - SourceMgr SM; - /// Stream for yaml parsing. - yaml::Stream Stream; - /// Storage for the error stream. - std::string ErrorString; - /// The error stream. - raw_string_ostream ErrorStream; - /// Iterator in the YAML stream. - yaml::document_iterator DI; - /// The parsed remark (if any). - Optional<LLVMRemarkEntry> LastRemark; - /// Temporary parsing buffer for the arguments. - SmallVector<LLVMRemarkArg, 8> TmpArgs; - /// The state used by the parser to parse a remark entry. Invalidated with - /// every call to `parseYAMLElement`. - struct ParseState { - /// Temporary parsing buffer for the arguments. - SmallVectorImpl<LLVMRemarkArg> *Args; - StringRef Type; - StringRef Pass; - StringRef Name; - StringRef Function; - /// Optional. - Optional<StringRef> File; - Optional<unsigned> Line; - Optional<unsigned> Column; - Optional<unsigned> Hotness; +Parser::Parser(StringRef Buf) : Impl(llvm::make_unique<YAMLParserImpl>(Buf)) {} - ParseState(SmallVectorImpl<LLVMRemarkArg> &Args) : Args(&Args) {} - /// Use Args only as a **temporary** buffer. - ~ParseState() { Args->clear(); } - }; +Parser::~Parser() = default; - ParseState State; - - /// Set to `true` if we had any errors during parsing. - bool HadAnyErrors = false; - - YAMLRemarkParser(StringRef Buf) - : SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString), - DI(Stream.begin()), LastRemark(), TmpArgs(), State(TmpArgs) { - SM.setDiagHandler(YAMLRemarkParser::HandleDiagnostic, this); - } - - /// Parse a YAML element. - Error parseYAMLElement(yaml::Document &Remark); - -private: - /// Parse one key to a string. - /// otherwise. - Error parseKey(StringRef &Result, yaml::KeyValueNode &Node); - /// Parse one value to a string. - Error parseValue(StringRef &Result, yaml::KeyValueNode &Node); - /// Parse one value to an unsigned. - Error parseValue(Optional<unsigned> &Result, yaml::KeyValueNode &Node); - /// Parse a debug location. - Error parseDebugLoc(Optional<StringRef> &File, Optional<unsigned> &Line, - Optional<unsigned> &Column, yaml::KeyValueNode &Node); - /// Parse an argument. - Error parseArg(SmallVectorImpl<LLVMRemarkArg> &TmpArgs, yaml::Node &Node); - - /// Handle a diagnostic from the YAML stream. Records the error in the - /// YAMLRemarkParser class. - static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { - assert(Ctx && "Expected non-null Ctx in diagnostic handler."); - auto *Parser = static_cast<YAMLRemarkParser *>(Ctx); - Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false, - /*ShowKindLabels*/ true); - } -}; - -class ParseError : public ErrorInfo<ParseError> { -public: - static char ID; - - ParseError(StringRef Message, yaml::Node &Node) - : Message(Message), Node(Node) {} - - void log(raw_ostream &OS) const override { OS << Message; } - std::error_code convertToErrorCode() const override { - return inconvertibleErrorCode(); - } - - StringRef getMessage() const { return Message; } - yaml::Node &getNode() const { return Node; } - -private: - StringRef Message; // No need to hold a full copy of the buffer. - yaml::Node &Node; -}; - -char ParseError::ID = 0; - -static LLVMRemarkStringRef toRemarkStr(StringRef Str) { - return {Str.data(), static_cast<uint32_t>(Str.size())}; -} - -Error YAMLRemarkParser::parseKey(StringRef &Result, yaml::KeyValueNode &Node) { - auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey()); - if (!Key) - return make_error<ParseError>("key is not a string.", Node); - - Result = Key->getRawValue(); - return Error::success(); -} - -Error YAMLRemarkParser::parseValue(StringRef &Result, - yaml::KeyValueNode &Node) { - auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); - if (!Value) - return make_error<ParseError>("expected a value of scalar type.", Node); - Result = Value->getRawValue(); - - if (Result.front() == '\'') - Result = Result.drop_front(); - - if (Result.back() == '\'') - Result = Result.drop_back(); - - return Error::success(); -} - -Error YAMLRemarkParser::parseValue(Optional<unsigned> &Result, - yaml::KeyValueNode &Node) { - SmallVector<char, 4> Tmp; - auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); - if (!Value) - return make_error<ParseError>("expected a value of scalar type.", Node); - unsigned UnsignedValue = 0; - if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) - return make_error<ParseError>("expected a value of integer type.", *Value); - Result = UnsignedValue; - return Error::success(); -} - -Error YAMLRemarkParser::parseDebugLoc(Optional<StringRef> &File, - Optional<unsigned> &Line, - Optional<unsigned> &Column, - yaml::KeyValueNode &Node) { - auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); - if (!DebugLoc) - return make_error<ParseError>("expected a value of mapping type.", Node); - - for (yaml::KeyValueNode &DLNode : *DebugLoc) { - StringRef KeyName; - if (Error E = parseKey(KeyName, DLNode)) - return E; - if (KeyName == "File") { - File = StringRef(); // Set the optional to contain a default constructed - // value, to be passed to the parsing function. - if (Error E = parseValue(*File, DLNode)) - return E; - } else if (KeyName == "Column") { - if (Error E = parseValue(Column, DLNode)) - return E; - } else if (KeyName == "Line") { - if (Error E = parseValue(Line, DLNode)) - return E; - } else { - return make_error<ParseError>("unknown entry in DebugLoc map.", DLNode); - } - } - - // If any of the debug loc fields is missing, return an error. - if (!File || !Line || !Column) - return make_error<ParseError>("DebugLoc node incomplete.", Node); - - return Error::success(); -} - -Error YAMLRemarkParser::parseArg(SmallVectorImpl<LLVMRemarkArg> &Args, - yaml::Node &Node) { - auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); - if (!ArgMap) - return make_error<ParseError>("expected a value of mapping type.", Node); - - StringRef ValueStr; - StringRef KeyStr; - Optional<StringRef> File; - Optional<unsigned> Line; - Optional<unsigned> Column; - - for (yaml::KeyValueNode &ArgEntry : *ArgMap) { - StringRef KeyName; - if (Error E = parseKey(KeyName, ArgEntry)) - return E; - - // Try to parse debug locs. - if (KeyName == "DebugLoc") { - // Can't have multiple DebugLoc entries per argument. - if (File || Line || Column) - return make_error<ParseError>( - "only one DebugLoc entry is allowed per argument.", ArgEntry); - - if (Error E = parseDebugLoc(File, Line, Column, ArgEntry)) - return E; - continue; - } - - // If we already have a string, error out. - if (!ValueStr.empty()) - return make_error<ParseError>( - "only one string entry is allowed per argument.", ArgEntry); +static Expected<const Remark *> getNextYAML(YAMLParserImpl &Impl) { + YAMLRemarkParser &YAMLParser = Impl.YAMLParser; + // Check for EOF. + if (Impl.YAMLIt == Impl.YAMLParser.Stream.end()) + return nullptr; - // Try to parse a string. - if (Error E = parseValue(ValueStr, ArgEntry)) - return E; + auto CurrentIt = Impl.YAMLIt; - // Keep the key from the string. - KeyStr = KeyName; + // Try to parse an entry. + if (Error E = YAMLParser.parseYAMLElement(*CurrentIt)) { + // Set the iterator to the end, in case the user calls getNext again. + Impl.YAMLIt = Impl.YAMLParser.Stream.end(); + return std::move(E); } - if (KeyStr.empty()) - return make_error<ParseError>("argument key is missing.", *ArgMap); - if (ValueStr.empty()) - return make_error<ParseError>("argument value is missing.", *ArgMap); - - Args.push_back(LLVMRemarkArg{ - toRemarkStr(KeyStr), toRemarkStr(ValueStr), - LLVMRemarkDebugLoc{toRemarkStr(File.getValueOr(StringRef())), - Line.getValueOr(0), Column.getValueOr(0)}}); + // Move on. + ++Impl.YAMLIt; - return Error::success(); + // Return the just-parsed remark. + if (const Optional<YAMLRemarkParser::ParseState> &State = YAMLParser.State) + return &State->Remark; + else + return createStringError(std::make_error_code(std::errc::invalid_argument), + "unexpected error while parsing."); } -Error YAMLRemarkParser::parseYAMLElement(yaml::Document &Remark) { - // Parsing a new remark, clear the previous one. - LastRemark = None; - State = ParseState(TmpArgs); - - auto *Root = dyn_cast<yaml::MappingNode>(Remark.getRoot()); - if (!Root) - return make_error<ParseError>("document root is not of mapping type.", - *Remark.getRoot()); - - State.Type = Root->getRawTag(); - - for (yaml::KeyValueNode &RemarkField : *Root) { - StringRef KeyName; - if (Error E = parseKey(KeyName, RemarkField)) - return E; - - if (KeyName == "Pass") { - if (Error E = parseValue(State.Pass, RemarkField)) - return E; - } else if (KeyName == "Name") { - if (Error E = parseValue(State.Name, RemarkField)) - return E; - } else if (KeyName == "Function") { - if (Error E = parseValue(State.Function, RemarkField)) - return E; - } else if (KeyName == "Hotness") { - if (Error E = parseValue(State.Hotness, RemarkField)) - return E; - } else if (KeyName == "DebugLoc") { - if (Error E = - parseDebugLoc(State.File, State.Line, State.Column, RemarkField)) - return E; - } else if (KeyName == "Args") { - auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); - if (!Args) - return make_error<ParseError>("wrong value type for key.", RemarkField); - - for (yaml::Node &Arg : *Args) - if (Error E = parseArg(*State.Args, Arg)) - return E; - } else { - return make_error<ParseError>("unknown key.", RemarkField); - } - } - - // If the YAML parsing failed, don't even continue parsing. We might - // encounter malformed YAML. - if (Stream.failed()) - return make_error<ParseError>("YAML parsing failed.", *Remark.getRoot()); - - // Check if any of the mandatory fields are missing. - if (State.Type.empty() || State.Pass.empty() || State.Name.empty() || - State.Function.empty()) - return make_error<ParseError>("Type, Pass, Name or Function missing.", - *Remark.getRoot()); - - LastRemark = LLVMRemarkEntry{ - toRemarkStr(State.Type), - toRemarkStr(State.Pass), - toRemarkStr(State.Name), - toRemarkStr(State.Function), - LLVMRemarkDebugLoc{toRemarkStr(State.File.getValueOr(StringRef())), - State.Line.getValueOr(0), State.Column.getValueOr(0)}, - State.Hotness.getValueOr(0), - static_cast<uint32_t>(State.Args->size()), - State.Args->data()}; - - return Error::success(); +Expected<const Remark *> Parser::getNext() const { + if (auto *Impl = dyn_cast<YAMLParserImpl>(this->Impl.get())) + return getNextYAML(*Impl); + llvm_unreachable("Get next called with an unknown parsing implementation."); } -} // namespace // Create wrappers for C Binding types (see CBindingWrapping.h). -DEFINE_SIMPLE_CONVERSION_FUNCTIONS(YAMLRemarkParser, LLVMRemarkParserRef) +DEFINE_SIMPLE_CONVERSION_FUNCTIONS(remarks::Parser, LLVMRemarkParserRef) -extern "C" LLVMRemarkParserRef LLVMRemarkParserCreate(const void *Buf, - uint64_t Size) { +extern "C" LLVMRemarkParserRef LLVMRemarkParserCreateYAML(const void *Buf, + uint64_t Size) { return wrap( - new YAMLRemarkParser(StringRef(static_cast<const char *>(Buf), Size))); + new remarks::Parser(StringRef(static_cast<const char *>(Buf), Size))); } -extern "C" LLVMRemarkEntry * -LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) { - YAMLRemarkParser &TheParser = *unwrap(Parser); - // Check for EOF. - if (TheParser.HadAnyErrors || TheParser.DI == TheParser.Stream.end()) - return nullptr; +static void handleYAMLError(remarks::YAMLParserImpl &Impl, Error E) { + handleAllErrors( + std::move(E), + [&](const YAMLParseError &PE) { + Impl.YAMLParser.Stream.printError(&PE.getNode(), + Twine(PE.getMessage()) + Twine('\n')); + Impl.HasErrors = true; + }, + [&](const StringError &PE) { PE.log(Impl.YAMLParser.ErrorStream); }); +} - // Try to parse an entry. - if (Error E = TheParser.parseYAMLElement(*TheParser.DI)) { - handleAllErrors(std::move(E), [&](const ParseError &PE) { - TheParser.Stream.printError(&PE.getNode(), - Twine(PE.getMessage()) + Twine('\n')); - TheParser.HadAnyErrors = true; - }); +extern "C" LLVMRemarkEntryRef +LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) { + remarks::Parser &TheParser = *unwrap(Parser); + + Expected<const remarks::Remark *> RemarkOrErr = TheParser.getNext(); + if (!RemarkOrErr) { + // Error during parsing. + if (auto *Impl = dyn_cast<remarks::YAMLParserImpl>(TheParser.Impl.get())) + handleYAMLError(*Impl, RemarkOrErr.takeError()); + else + llvm_unreachable("unkown parser implementation."); return nullptr; } - // Move on. - ++TheParser.DI; - - // Return the just-parsed remark. - if (Optional<LLVMRemarkEntry> &Entry = TheParser.LastRemark) - return &*Entry; - return nullptr; + if (*RemarkOrErr == nullptr) + return nullptr; + // Valid remark. + return wrap(*RemarkOrErr); } extern "C" LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser) { - return unwrap(Parser)->HadAnyErrors; + if (auto *Impl = + dyn_cast<remarks::YAMLParserImpl>(unwrap(Parser)->Impl.get())) + return Impl->HasErrors; + llvm_unreachable("unkown parser implementation."); } extern "C" const char * LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser) { - return unwrap(Parser)->ErrorStream.str().c_str(); + if (auto *Impl = + dyn_cast<remarks::YAMLParserImpl>(unwrap(Parser)->Impl.get())) + return Impl->YAMLParser.ErrorStream.str().c_str(); + llvm_unreachable("unkown parser implementation."); } extern "C" void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser) { diff --git a/llvm/lib/Remarks/RemarkParserImpl.h b/llvm/lib/Remarks/RemarkParserImpl.h new file mode 100644 index 00000000000..f812f495a17 --- /dev/null +++ b/llvm/lib/Remarks/RemarkParserImpl.h @@ -0,0 +1,29 @@ +//===-- RemarkParserImpl.h - Implementation details -------------*- C++/-*-===// +// +// 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 implementation details for the remark parser. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_REMARKS_REMARK_PARSER_IMPL_H +#define LLVM_REMARKS_REMARK_PARSER_IMPL_H + +namespace llvm { +namespace remarks { +/// This is used as a base for any parser implementation. +struct ParserImpl { + enum class Kind { YAML }; + + // The parser kind. This is used as a tag to safely cast between + // implementations. + enum Kind Kind; +}; +} // end namespace remarks +} // end namespace llvm + +#endif /* LLVM_REMARKS_REMARK_PARSER_IMPL_H */ diff --git a/llvm/lib/Remarks/YAMLRemarkParser.cpp b/llvm/lib/Remarks/YAMLRemarkParser.cpp new file mode 100644 index 00000000000..3091c80629d --- /dev/null +++ b/llvm/lib/Remarks/YAMLRemarkParser.cpp @@ -0,0 +1,261 @@ +//===- 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/Remarks/RemarkParser.h" + +using namespace llvm; +using namespace llvm::remarks; + +char YAMLParseError::ID = 0; + +Error YAMLRemarkParser::parseKey(StringRef &Result, yaml::KeyValueNode &Node) { + if (auto *Key = dyn_cast<yaml::ScalarNode>(Node.getKey())) { + Result = Key->getRawValue(); + return Error::success(); + } + + return make_error<YAMLParseError>("key is not a string.", Node); +} + +template <typename T> +Error YAMLRemarkParser::parseStr(T &Result, yaml::KeyValueNode &Node) { + auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); + if (!Value) + return make_error<YAMLParseError>("expected a value of scalar type.", Node); + StringRef Tmp = Value->getRawValue(); + + if (Tmp.front() == '\'') + Tmp = Tmp.drop_front(); + + if (Tmp.back() == '\'') + Tmp = Tmp.drop_back(); + + Result = Tmp; + + return Error::success(); +} + +template <typename T> +Error YAMLRemarkParser::parseUnsigned(T &Result, yaml::KeyValueNode &Node) { + SmallVector<char, 4> Tmp; + auto *Value = dyn_cast<yaml::ScalarNode>(Node.getValue()); + if (!Value) + return make_error<YAMLParseError>("expected a value of scalar type.", Node); + unsigned UnsignedValue = 0; + if (Value->getValue(Tmp).getAsInteger(10, UnsignedValue)) + return make_error<YAMLParseError>("expected a value of integer type.", + *Value); + Result = UnsignedValue; + return Error::success(); +} + +Error YAMLRemarkParser::parseType(Type &Result, yaml::MappingNode &Node) { + auto Type = StringSwitch<remarks::Type>(Node.getRawTag()) + .Case("!Passed", Type::Passed) + .Case("!Missed", Type::Missed) + .Case("!Analysis", Type::Analysis) + .Case("!AnalysisFPCommute", Type::AnalysisFPCommute) + .Case("!AnalysisAliasing", Type::AnalysisAliasing) + .Case("!Failure", Type::Failure) + .Default(Type::Unknown); + if (Type == Type::Unknown) + return make_error<YAMLParseError>("expected a remark tag.", Node); + Result = Type; + return Error::success(); +} + +Error YAMLRemarkParser::parseDebugLoc(Optional<RemarkLocation> &Result, + yaml::KeyValueNode &Node) { + auto *DebugLoc = dyn_cast<yaml::MappingNode>(Node.getValue()); + if (!DebugLoc) + return make_error<YAMLParseError>("expected a value of mapping type.", + Node); + + Optional<StringRef> File; + Optional<unsigned> Line; + Optional<unsigned> Column; + + for (yaml::KeyValueNode &DLNode : *DebugLoc) { + StringRef KeyName; + if (Error E = parseKey(KeyName, DLNode)) + return E; + if (KeyName == "File") { + if (Error E = parseStr(File, DLNode)) + return E; + } else if (KeyName == "Column") { + if (Error E = parseUnsigned(Column, DLNode)) + return E; + } else if (KeyName == "Line") { + if (Error E = parseUnsigned(Line, DLNode)) + return E; + } else { + return make_error<YAMLParseError>("unknown entry in DebugLoc map.", + DLNode); + } + } + + // If any of the debug loc fields is missing, return an error. + if (!File || !Line || !Column) + return make_error<YAMLParseError>("DebugLoc node incomplete.", Node); + + Result = RemarkLocation{*File, *Line, *Column}; + + return Error::success(); +} + +Error YAMLRemarkParser::parseRemarkField(yaml::KeyValueNode &RemarkField) { + + StringRef KeyName; + if (Error E = parseKey(KeyName, RemarkField)) + return E; + + if (KeyName == "Pass") { + if (Error E = parseStr(State->Remark.PassName, RemarkField)) + return E; + } else if (KeyName == "Name") { + if (Error E = parseStr(State->Remark.RemarkName, RemarkField)) + return E; + } else if (KeyName == "Function") { + if (Error E = parseStr(State->Remark.FunctionName, RemarkField)) + return E; + } else if (KeyName == "Hotness") { + State->Remark.Hotness = 0; + if (Error E = parseUnsigned(*State->Remark.Hotness, RemarkField)) + return E; + } else if (KeyName == "DebugLoc") { + if (Error E = parseDebugLoc(State->Remark.Loc, RemarkField)) + return E; + } else if (KeyName == "Args") { + auto *Args = dyn_cast<yaml::SequenceNode>(RemarkField.getValue()); + if (!Args) + return make_error<YAMLParseError>("wrong value type for key.", + RemarkField); + + for (yaml::Node &Arg : *Args) + if (Error E = parseArg(State->Args, Arg)) + return E; + + State->Remark.Args = State->Args; + } else { + return make_error<YAMLParseError>("unknown key.", RemarkField); + } + + return Error::success(); +} + +Error YAMLRemarkParser::parseArg(SmallVectorImpl<Argument> &Args, + yaml::Node &Node) { + auto *ArgMap = dyn_cast<yaml::MappingNode>(&Node); + if (!ArgMap) + return make_error<YAMLParseError>("expected a value of mapping type.", + Node); + + StringRef KeyStr; + StringRef ValueStr; + Optional<RemarkLocation> Loc; + + for (yaml::KeyValueNode &ArgEntry : *ArgMap) + if (Error E = parseArgEntry(ArgEntry, KeyStr, ValueStr, Loc)) + return E; + + if (KeyStr.empty()) + return make_error<YAMLParseError>("argument key is missing.", *ArgMap); + if (ValueStr.empty()) + return make_error<YAMLParseError>("argument value is missing.", *ArgMap); + + Args.push_back(Argument{KeyStr, ValueStr, Loc}); + + return Error::success(); +} + +Error YAMLRemarkParser::parseArgEntry(yaml::KeyValueNode &ArgEntry, + StringRef &KeyStr, StringRef &ValueStr, + Optional<RemarkLocation> &Loc) { + StringRef KeyName; + if (Error E = parseKey(KeyName, ArgEntry)) + return E; + + // Try to parse debug locs. + if (KeyName == "DebugLoc") { + // Can't have multiple DebugLoc entries per argument. + if (Loc) + return make_error<YAMLParseError>( + "only one DebugLoc entry is allowed per argument.", ArgEntry); + + if (Error E = parseDebugLoc(Loc, ArgEntry)) + return E; + return Error::success(); + } + + // If we already have a string, error out. + if (!ValueStr.empty()) + return make_error<YAMLParseError>( + "only one string entry is allowed per argument.", ArgEntry); + + // Try to parse a string. + if (Error E = parseStr(ValueStr, ArgEntry)) + return E; + + // Keep the key from the string. + KeyStr = KeyName; + return Error::success(); +} + +Error YAMLRemarkParser::parseYAMLElement(yaml::Document &Remark) { + // Parsing a new remark, clear the previous one by re-constructing the state + // in-place in the Optional. + State.emplace(TmpArgs); + + yaml::Node *YAMLRoot = Remark.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 make_error<YAMLParseError>("document root is not of mapping type.", + *YAMLRoot); + + if (Error E = parseType(State->Remark.RemarkType, *Root)) + return E; + + for (yaml::KeyValueNode &RemarkField : *Root) + if (Error E = parseRemarkField(RemarkField)) + return E; + + // If the YAML parsing failed, don't even continue parsing. We might + // encounter malformed YAML. + if (Stream.failed()) + return make_error<YAMLParseError>("YAML parsing failed.", + *Remark.getRoot()); + + // Check if any of the mandatory fields are missing. + if (State->Remark.RemarkType == Type::Unknown || + State->Remark.PassName.empty() || State->Remark.RemarkName.empty() || + State->Remark.FunctionName.empty()) + return make_error<YAMLParseError>("Type, Pass, Name or Function missing.", + *Remark.getRoot()); + + return Error::success(); +} + +/// Handle a diagnostic from the YAML stream. Records the error in the +/// YAMLRemarkParser class. +void YAMLRemarkParser::HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) { + assert(Ctx && "Expected non-null Ctx in diagnostic handler."); + auto *Parser = static_cast<YAMLRemarkParser *>(Ctx); + Diag.print(/*ProgName=*/nullptr, Parser->ErrorStream, /*ShowColors*/ false, + /*ShowKindLabels*/ true); +} diff --git a/llvm/lib/Remarks/YAMLRemarkParser.h b/llvm/lib/Remarks/YAMLRemarkParser.h new file mode 100644 index 00000000000..1eb5706bfd4 --- /dev/null +++ b/llvm/lib/Remarks/YAMLRemarkParser.h @@ -0,0 +1,136 @@ +//===-- YAMLRemarkParser.h - Parser for YAML remarks ------------*- C++/-*-===// +// +// 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 impementation of the YAML remark parser. +// +//===----------------------------------------------------------------------===// + +#ifndef LLVM_REMARKS_YAML_REMARK_PARSER_H +#define LLVM_REMARKS_YAML_REMARK_PARSER_H + +#include "RemarkParserImpl.h" +#include "llvm/ADT/Optional.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/Remarks/Remark.h" +#include "llvm/Support/Error.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/YAMLParser.h" +#include "llvm/Support/YAMLTraits.h" +#include "llvm/Support/raw_ostream.h" +#include <string> + +namespace llvm { +namespace remarks { +/// Parses and holds the state of the latest parsed remark. +struct YAMLRemarkParser { + /// Source manager for better error messages. + SourceMgr SM; + /// Stream for yaml parsing. + yaml::Stream Stream; + /// Storage for the error stream. + std::string ErrorString; + /// The error stream. + raw_string_ostream ErrorStream; + /// Temporary parsing buffer for the arguments. + SmallVector<Argument, 8> TmpArgs; + + /// The state used by the parser to parse a remark entry. Invalidated with + /// every call to `parseYAMLElement`. + struct ParseState { + /// Temporary parsing buffer for the arguments. + /// The parser itself is owning this buffer in order to reduce the number of + /// allocations. + SmallVectorImpl<Argument> &Args; + Remark Remark; + + ParseState(SmallVectorImpl<Argument> &Args) : Args(Args) {} + /// Use Args only as a **temporary** buffer. + ~ParseState() { Args.clear(); } + }; + + /// The current state of the parser. If the parsing didn't start yet, it will + /// not be containing any value. + Optional<ParseState> State; + + YAMLRemarkParser(StringRef Buf) + : SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString), + TmpArgs() { + SM.setDiagHandler(YAMLRemarkParser::HandleDiagnostic, this); + } + + /// Parse a YAML element. + Error parseYAMLElement(yaml::Document &Remark); + +private: + /// Parse one key to a string. + /// otherwise. + Error parseKey(StringRef &Result, yaml::KeyValueNode &Node); + /// Parse one value to a string. + template <typename T> Error parseStr(T &Result, yaml::KeyValueNode &Node); + /// Parse one value to an unsigned. + template <typename T> + Error parseUnsigned(T &Result, yaml::KeyValueNode &Node); + /// Parse the type of a remark to an enum type. + Error parseType(Type &Result, yaml::MappingNode &Node); + /// Parse a debug location. + Error parseDebugLoc(Optional<RemarkLocation> &Result, + yaml::KeyValueNode &Node); + /// Parse a remark field and update the parsing state. + Error parseRemarkField(yaml::KeyValueNode &RemarkField); + /// Parse an argument. + Error parseArg(SmallVectorImpl<Argument> &TmpArgs, yaml::Node &Node); + /// Parse an entry from the contents of an argument. + Error parseArgEntry(yaml::KeyValueNode &ArgEntry, StringRef &KeyStr, + StringRef &ValueStr, Optional<RemarkLocation> &Loc); + + /// Handle a diagnostic from the YAML stream. Records the error in the + /// YAMLRemarkParser class. + static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx); +}; + +class YAMLParseError : public ErrorInfo<YAMLParseError> { +public: + static char ID; + + YAMLParseError(StringRef Message, yaml::Node &Node) + : Message(Message), Node(Node) {} + + void log(raw_ostream &OS) const override { OS << Message; } + std::error_code convertToErrorCode() const override { + return inconvertibleErrorCode(); + } + + StringRef getMessage() const { return Message; } + yaml::Node &getNode() const { return Node; } + +private: + StringRef Message; // No need to hold a full copy of the buffer. + yaml::Node &Node; +}; + +/// Regular YAML to Remark parser. +struct YAMLParserImpl : public ParserImpl { + /// The object parsing the YAML. + YAMLRemarkParser YAMLParser; + /// Iterator in the YAML stream. + yaml::document_iterator YAMLIt; + /// Set to `true` if we had any errors during parsing. + bool HasErrors = false; + + YAMLParserImpl(StringRef Buf) + : ParserImpl{ParserImpl::Kind::YAML}, YAMLParser(Buf), + YAMLIt(YAMLParser.Stream.begin()), HasErrors(false) {} + + static bool classof(const ParserImpl *PI) { + return PI->Kind == ParserImpl::Kind::YAML; + } +}; +} // end namespace remarks +} // end namespace llvm + +#endif /* LLVM_REMARKS_YAML_REMARK_PARSER_H */ |