summaryrefslogtreecommitdiff
path: root/lib/Remarks
diff options
context:
space:
mode:
Diffstat (limited to 'lib/Remarks')
-rw-r--r--lib/Remarks/Remark.cpp132
-rw-r--r--lib/Remarks/RemarkFormat.cpp30
-rw-r--r--lib/Remarks/RemarkParser.cpp119
-rw-r--r--lib/Remarks/RemarkStringTable.cpp48
-rw-r--r--lib/Remarks/YAMLRemarkParser.cpp327
-rw-r--r--lib/Remarks/YAMLRemarkParser.h96
-rw-r--r--lib/Remarks/YAMLRemarkSerializer.cpp167
7 files changed, 919 insertions, 0 deletions
diff --git a/lib/Remarks/Remark.cpp b/lib/Remarks/Remark.cpp
new file mode 100644
index 000000000000..401ac514b011
--- /dev/null
+++ b/lib/Remarks/Remark.cpp
@@ -0,0 +1,132 @@
+//===- 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" void LLVMRemarkEntryDispose(LLVMRemarkEntryRef Remark) {
+ delete unwrap(Remark);
+}
+
+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/lib/Remarks/RemarkFormat.cpp b/lib/Remarks/RemarkFormat.cpp
new file mode 100644
index 000000000000..bcd0f753ff64
--- /dev/null
+++ b/lib/Remarks/RemarkFormat.cpp
@@ -0,0 +1,30 @@
+//===- RemarkFormat.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 utilities to handle the different remark formats.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/ADT/StringSwitch.h"
+
+using namespace llvm;
+using namespace llvm::remarks;
+
+Expected<Format> llvm::remarks::parseFormat(StringRef FormatStr) {
+ auto Result = StringSwitch<Format>(FormatStr)
+ .Cases("", "yaml", Format::YAML)
+ .Default(Format::Unknown);
+
+ if (Result == Format::Unknown)
+ return createStringError(std::make_error_code(std::errc::invalid_argument),
+ "Unknown remark serializer format: '%s'",
+ FormatStr.data());
+
+ return Result;
+}
diff --git a/lib/Remarks/RemarkParser.cpp b/lib/Remarks/RemarkParser.cpp
new file mode 100644
index 000000000000..f67464073bd1
--- /dev/null
+++ b/lib/Remarks/RemarkParser.cpp
@@ -0,0 +1,119 @@
+//===- RemarkParser.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 "llvm/Remarks/RemarkParser.h"
+#include "YAMLRemarkParser.h"
+#include "llvm-c/Remarks.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/CBindingWrapping.h"
+
+using namespace llvm;
+using namespace llvm::remarks;
+
+char EndOfFileError::ID = 0;
+
+ParsedStringTable::ParsedStringTable(StringRef InBuffer) : Buffer(InBuffer) {
+ while (!InBuffer.empty()) {
+ // Strings are separated by '\0' bytes.
+ std::pair<StringRef, StringRef> Split = InBuffer.split('\0');
+ // We only store the offset from the beginning of the buffer.
+ Offsets.push_back(Split.first.data() - Buffer.data());
+ InBuffer = Split.second;
+ }
+}
+
+Expected<StringRef> ParsedStringTable::operator[](size_t Index) const {
+ if (Index >= Offsets.size())
+ return createStringError(
+ std::make_error_code(std::errc::invalid_argument),
+ "String with index %u is out of bounds (size = %u).", Index,
+ Offsets.size());
+
+ size_t Offset = Offsets[Index];
+ // If it's the last offset, we can't use the next offset to know the size of
+ // the string.
+ size_t NextOffset =
+ (Index == Offsets.size() - 1) ? Buffer.size() : Offsets[Index + 1];
+ return StringRef(Buffer.data() + Offset, NextOffset - Offset - 1);
+}
+
+Expected<std::unique_ptr<Parser>>
+llvm::remarks::createRemarkParser(Format ParserFormat, StringRef Buf,
+ Optional<const ParsedStringTable *> StrTab) {
+ switch (ParserFormat) {
+ case Format::YAML:
+ return llvm::make_unique<YAMLRemarkParser>(Buf, StrTab);
+ case Format::Unknown:
+ return createStringError(std::make_error_code(std::errc::invalid_argument),
+ "Unknown remark parser format.");
+ }
+ llvm_unreachable("unknown format");
+}
+
+// Wrapper that holds the state needed to interact with the C API.
+struct CParser {
+ std::unique_ptr<Parser> TheParser;
+ Optional<std::string> Err;
+
+ CParser(Format ParserFormat, StringRef Buf,
+ Optional<const ParsedStringTable *> StrTab = None)
+ : TheParser(cantFail(createRemarkParser(ParserFormat, Buf, StrTab))) {}
+
+ void handleError(Error E) { Err.emplace(toString(std::move(E))); }
+ bool hasError() const { return Err.hasValue(); }
+ const char *getMessage() const { return Err ? Err->c_str() : nullptr; };
+};
+
+// Create wrappers for C Binding types (see CBindingWrapping.h).
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(CParser, LLVMRemarkParserRef)
+
+extern "C" LLVMRemarkParserRef LLVMRemarkParserCreateYAML(const void *Buf,
+ uint64_t Size) {
+ return wrap(new CParser(Format::YAML,
+ StringRef(static_cast<const char *>(Buf), Size)));
+}
+
+extern "C" LLVMRemarkEntryRef
+LLVMRemarkParserGetNext(LLVMRemarkParserRef Parser) {
+ CParser &TheCParser = *unwrap(Parser);
+ remarks::Parser &TheParser = *TheCParser.TheParser;
+
+ Expected<std::unique_ptr<Remark>> MaybeRemark = TheParser.next();
+ if (Error E = MaybeRemark.takeError()) {
+ if (E.isA<EndOfFileError>()) {
+ consumeError(std::move(E));
+ return nullptr;
+ }
+
+ // Handle the error. Allow it to be checked through HasError and
+ // GetErrorMessage.
+ TheCParser.handleError(std::move(E));
+ return nullptr;
+ }
+
+ // Valid remark.
+ return wrap(MaybeRemark->release());
+}
+
+extern "C" LLVMBool LLVMRemarkParserHasError(LLVMRemarkParserRef Parser) {
+ return unwrap(Parser)->hasError();
+}
+
+extern "C" const char *
+LLVMRemarkParserGetErrorMessage(LLVMRemarkParserRef Parser) {
+ return unwrap(Parser)->getMessage();
+}
+
+extern "C" void LLVMRemarkParserDispose(LLVMRemarkParserRef Parser) {
+ delete unwrap(Parser);
+}
diff --git a/lib/Remarks/RemarkStringTable.cpp b/lib/Remarks/RemarkStringTable.cpp
new file mode 100644
index 000000000000..984aa5b33b48
--- /dev/null
+++ b/lib/Remarks/RemarkStringTable.cpp
@@ -0,0 +1,48 @@
+//===- RemarkStringTable.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 string table used at remark generation.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Remarks/RemarkStringTable.h"
+#include "llvm/Support/EndianStream.h"
+#include "llvm/Support/Error.h"
+#include <vector>
+
+using namespace llvm;
+using namespace llvm::remarks;
+
+std::pair<unsigned, StringRef> StringTable::add(StringRef Str) {
+ size_t NextID = StrTab.size();
+ auto KV = StrTab.insert({Str, NextID});
+ // If it's a new string, add it to the final size.
+ if (KV.second)
+ SerializedSize += KV.first->first().size() + 1; // +1 for the '\0'
+ // Can be either NextID or the previous ID if the string is already there.
+ return {KV.first->second, KV.first->first()};
+}
+
+void StringTable::serialize(raw_ostream &OS) const {
+ // Emit the number of strings.
+ uint64_t StrTabSize = SerializedSize;
+ support::endian::write(OS, StrTabSize, support::little);
+ // Emit the sequence of strings.
+ for (StringRef Str : serialize()) {
+ OS << Str;
+ // Explicitly emit a '\0'.
+ OS.write('\0');
+ }
+}
+
+std::vector<StringRef> StringTable::serialize() const {
+ std::vector<StringRef> Strings{StrTab.size()};
+ for (const auto &KV : StrTab)
+ Strings[KV.second] = KV.first();
+ return Strings;
+}
diff --git a/lib/Remarks/YAMLRemarkParser.cpp b/lib/Remarks/YAMLRemarkParser.cpp
new file mode 100644
index 000000000000..ed78b7ba5d95
--- /dev/null
+++ b/lib/Remarks/YAMLRemarkParser.cpp
@@ -0,0 +1,327 @@
+//===- 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;
+
+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;
+}
+
+YAMLRemarkParser::YAMLRemarkParser(StringRef Buf,
+ Optional<const ParsedStringTable *> StrTab)
+ : Parser{Format::YAML}, StrTab(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 = llvm::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;
+ if (!StrTab) {
+ Result = Value->getRawValue();
+ } else {
+ // 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;
+}
+
+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);
+}
diff --git a/lib/Remarks/YAMLRemarkParser.h b/lib/Remarks/YAMLRemarkParser.h
new file mode 100644
index 000000000000..cea76e63e75c
--- /dev/null
+++ b/lib/Remarks/YAMLRemarkParser.h
@@ -0,0 +1,96 @@
+//===-- 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 "llvm/ADT/Optional.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkParser.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 {
+
+class YAMLParseError : public ErrorInfo<YAMLParseError> {
+public:
+ static char ID;
+
+ YAMLParseError(StringRef Message, SourceMgr &SM, yaml::Stream &Stream,
+ yaml::Node &Node);
+
+ YAMLParseError(StringRef Message) : Message(Message) {}
+
+ void log(raw_ostream &OS) const override { OS << Message; }
+ std::error_code convertToErrorCode() const override {
+ return inconvertibleErrorCode();
+ }
+
+private:
+ std::string Message;
+};
+
+/// Regular YAML to Remark parser.
+struct YAMLRemarkParser : public Parser {
+ /// The string table used for parsing strings.
+ Optional<const ParsedStringTable *> StrTab;
+ /// Last error message that can come from the YAML parser diagnostics.
+ /// We need this for catching errors in the constructor.
+ std::string LastErrorMessage;
+ /// Source manager for better error messages.
+ SourceMgr SM;
+ /// Stream for yaml parsing.
+ yaml::Stream Stream;
+ /// Iterator in the YAML stream.
+ yaml::document_iterator YAMLIt;
+
+ YAMLRemarkParser(StringRef Buf,
+ Optional<const ParsedStringTable *> StrTab = None);
+
+ Expected<std::unique_ptr<Remark>> next() override;
+
+ static bool classof(const Parser *P) {
+ return P->ParserFormat == Format::YAML;
+ }
+
+private:
+ /// Create a YAMLParseError error from an existing error generated by the YAML
+ /// parser.
+ /// If there is no error, this returns Success.
+ Error error();
+ /// Create a YAMLParseError error referencing a specific node.
+ Error error(StringRef Message, yaml::Node &Node);
+ /// Parse a YAML remark to a remarks::Remark object.
+ Expected<std::unique_ptr<Remark>> parseRemark(yaml::Document &Remark);
+ /// Parse the type of a remark to an enum type.
+ Expected<Type> parseType(yaml::MappingNode &Node);
+ /// Parse one key to a string.
+ Expected<StringRef> parseKey(yaml::KeyValueNode &Node);
+ /// Parse one value to a string.
+ Expected<StringRef> parseStr(yaml::KeyValueNode &Node);
+ /// Parse one value to an unsigned.
+ Expected<unsigned> parseUnsigned(yaml::KeyValueNode &Node);
+ /// Parse a debug location.
+ Expected<RemarkLocation> parseDebugLoc(yaml::KeyValueNode &Node);
+ /// Parse an argument.
+ Expected<Argument> parseArg(yaml::Node &Node);
+};
+} // end namespace remarks
+} // end namespace llvm
+
+#endif /* LLVM_REMARKS_YAML_REMARK_PARSER_H */
diff --git a/lib/Remarks/YAMLRemarkSerializer.cpp b/lib/Remarks/YAMLRemarkSerializer.cpp
new file mode 100644
index 000000000000..d64ae8e12ab0
--- /dev/null
+++ b/lib/Remarks/YAMLRemarkSerializer.cpp
@@ -0,0 +1,167 @@
+//===- YAMLRemarkSerializer.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 the implementation of the YAML remark serializer using
+// LLVM's YAMLTraits.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Remarks/RemarkSerializer.h"
+#include "llvm/Support/CommandLine.h"
+
+using namespace llvm;
+using namespace llvm::remarks;
+
+cl::opt<bool> RemarksYAMLStringTable(
+ "remarks-yaml-string-table", cl::init(false), cl::Hidden,
+ cl::desc("Enable the usage of a string table with YAML remarks."));
+
+// Use the same keys whether we use a string table or not (respectively, T is an
+// unsigned or a StringRef).
+template <typename T>
+static void mapRemarkHeader(yaml::IO &io, T PassName, T RemarkName,
+ Optional<RemarkLocation> RL, T FunctionName,
+ Optional<uint64_t> Hotness,
+ ArrayRef<Argument> Args) {
+ io.mapRequired("Pass", PassName);
+ io.mapRequired("Name", RemarkName);
+ io.mapOptional("DebugLoc", RL);
+ io.mapRequired("Function", FunctionName);
+ io.mapOptional("Hotness", Hotness);
+ io.mapOptional("Args", Args);
+}
+
+namespace llvm {
+namespace yaml {
+
+template <> struct MappingTraits<remarks::Remark *> {
+ static void mapping(IO &io, remarks::Remark *&Remark) {
+ assert(io.outputting() && "input not yet implemented");
+
+ if (io.mapTag("!Passed", (Remark->RemarkType == Type::Passed)))
+ ;
+ else if (io.mapTag("!Missed", (Remark->RemarkType == Type::Missed)))
+ ;
+ else if (io.mapTag("!Analysis", (Remark->RemarkType == Type::Analysis)))
+ ;
+ else if (io.mapTag("!AnalysisFPCommute",
+ (Remark->RemarkType == Type::AnalysisFPCommute)))
+ ;
+ else if (io.mapTag("!AnalysisAliasing",
+ (Remark->RemarkType == Type::AnalysisAliasing)))
+ ;
+ else if (io.mapTag("!Failure", (Remark->RemarkType == Type::Failure)))
+ ;
+ else
+ llvm_unreachable("Unknown remark type");
+
+ if (Optional<StringTable> &StrTab =
+ reinterpret_cast<YAMLSerializer *>(io.getContext())->StrTab) {
+ unsigned PassID = StrTab->add(Remark->PassName).first;
+ unsigned NameID = StrTab->add(Remark->RemarkName).first;
+ unsigned FunctionID = StrTab->add(Remark->FunctionName).first;
+ mapRemarkHeader(io, PassID, NameID, Remark->Loc, FunctionID,
+ Remark->Hotness, Remark->Args);
+ } else {
+ mapRemarkHeader(io, Remark->PassName, Remark->RemarkName, Remark->Loc,
+ Remark->FunctionName, Remark->Hotness, Remark->Args);
+ }
+ }
+};
+
+template <> struct MappingTraits<RemarkLocation> {
+ static void mapping(IO &io, RemarkLocation &RL) {
+ assert(io.outputting() && "input not yet implemented");
+
+ StringRef File = RL.SourceFilePath;
+ unsigned Line = RL.SourceLine;
+ unsigned Col = RL.SourceColumn;
+
+ if (Optional<StringTable> &StrTab =
+ reinterpret_cast<YAMLSerializer *>(io.getContext())->StrTab) {
+ unsigned FileID = StrTab->add(File).first;
+ io.mapRequired("File", FileID);
+ } else {
+ io.mapRequired("File", File);
+ }
+
+ io.mapRequired("Line", Line);
+ io.mapRequired("Column", Col);
+ }
+
+ static const bool flow = true;
+};
+
+/// Helper struct for multiline string block literals. Use this type to preserve
+/// newlines in strings.
+struct StringBlockVal {
+ StringRef Value;
+ StringBlockVal(const std::string &Value) : Value(Value) {}
+};
+
+template <> struct BlockScalarTraits<StringBlockVal> {
+ static void output(const StringBlockVal &S, void *Ctx, raw_ostream &OS) {
+ return ScalarTraits<StringRef>::output(S.Value, Ctx, OS);
+ }
+
+ static StringRef input(StringRef Scalar, void *Ctx, StringBlockVal &S) {
+ return ScalarTraits<StringRef>::input(Scalar, Ctx, S.Value);
+ }
+};
+
+/// ArrayRef is not really compatible with the YAMLTraits. Everything should be
+/// immutable in an ArrayRef, while the SequenceTraits expect a mutable version
+/// for inputting, but we're only using the outputting capabilities here.
+/// This is a hack, but still nicer than having to manually call the YAMLIO
+/// internal methods.
+/// Keep this in this file so that it doesn't get misused from YAMLTraits.h.
+template <typename T> struct SequenceTraits<ArrayRef<T>> {
+ static size_t size(IO &io, ArrayRef<T> &seq) { return seq.size(); }
+ static Argument &element(IO &io, ArrayRef<T> &seq, size_t index) {
+ assert(io.outputting() && "input not yet implemented");
+ // The assert above should make this "safer" to satisfy the YAMLTraits.
+ return const_cast<T &>(seq[index]);
+ }
+};
+
+/// Implement this as a mapping for now to get proper quotation for the value.
+template <> struct MappingTraits<Argument> {
+ static void mapping(IO &io, Argument &A) {
+ assert(io.outputting() && "input not yet implemented");
+
+ if (Optional<StringTable> &StrTab =
+ reinterpret_cast<YAMLSerializer *>(io.getContext())->StrTab) {
+ auto ValueID = StrTab->add(A.Val).first;
+ io.mapRequired(A.Key.data(), ValueID);
+ } else if (StringRef(A.Val).count('\n') > 1) {
+ StringBlockVal S(A.Val);
+ io.mapRequired(A.Key.data(), S);
+ } else {
+ io.mapRequired(A.Key.data(), A.Val);
+ }
+ io.mapOptional("DebugLoc", A.Loc);
+ }
+};
+
+} // end namespace yaml
+} // end namespace llvm
+
+LLVM_YAML_IS_SEQUENCE_VECTOR(Argument)
+
+YAMLSerializer::YAMLSerializer(raw_ostream &OS, UseStringTable UseStringTable)
+ : Serializer(OS), YAMLOutput(OS, reinterpret_cast<void *>(this)) {
+ if (UseStringTable == remarks::UseStringTable::Yes || RemarksYAMLStringTable)
+ StrTab.emplace();
+}
+
+void YAMLSerializer::emit(const Remark &Remark) {
+ // Again, YAMLTraits expect a non-const object for inputting, but we're not
+ // using that here.
+ auto R = const_cast<remarks::Remark *>(&Remark);
+ YAMLOutput << R;
+}