summaryrefslogtreecommitdiffstats
path: root/llvm/lib
diff options
context:
space:
mode:
authorFrancis Visoiu Mistrih <francisvm@yahoo.com>2019-03-19 18:09:51 +0000
committerFrancis Visoiu Mistrih <francisvm@yahoo.com>2019-03-19 18:09:51 +0000
commit9ef60a2539b268f40052f61149aab6c2442732aa (patch)
treed7f7015d028a6118e17cc24b4995a9fd00a251e6 /llvm/lib
parentaea09858142fec87697ca4dd22c51cef410fb97f (diff)
downloadbcm5719-llvm-9ef60a2539b268f40052f61149aab6c2442732aa.tar.gz
bcm5719-llvm-9ef60a2539b268f40052f61149aab6c2442732aa.zip
[Remarks] Add a new Remark / RemarkParser abstraction
This adds a Remark class that allows us to share code when working with remarks. The C API has been updated to reflect this. Instead of the parser generating C structs, it's now using a C++ object that is used through opaque pointers in C. This gives us much more flexibility on what changes we can make to the internal state of the object and interacts much better with scenarios where the library is used through dlopen. * C API updates: * move from C structs to opaque pointers and functions * the remark type is now an enum instead of a string * unit tests updates: * use mostly the C++ API * keep one test for the C API * rename to YAMLRemarksParsingTest * a typo was fixed: AnalysisFPCompute -> AnalysisFPCommute. * a new error message was added: "expected a remark tag." * llvm-opt-report has been updated to use the C++ parser instead of the C API Differential Revision: https://reviews.llvm.org/D59049 llvm-svn: 356491
Diffstat (limited to 'llvm/lib')
-rw-r--r--llvm/lib/Remarks/CMakeLists.txt2
-rw-r--r--llvm/lib/Remarks/Remark.cpp128
-rw-r--r--llvm/lib/Remarks/RemarkParser.cpp383
-rw-r--r--llvm/lib/Remarks/RemarkParserImpl.h29
-rw-r--r--llvm/lib/Remarks/YAMLRemarkParser.cpp261
-rw-r--r--llvm/lib/Remarks/YAMLRemarkParser.h136
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 */
OpenPOWER on IntegriCloud