diff options
Diffstat (limited to 'llvm/lib/Remarks/YAMLRemarkParser.cpp')
-rw-r--r-- | llvm/lib/Remarks/YAMLRemarkParser.cpp | 261 |
1 files changed, 261 insertions, 0 deletions
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); +} |