diff options
Diffstat (limited to 'lib/Remarks')
-rw-r--r-- | lib/Remarks/Remark.cpp | 132 | ||||
-rw-r--r-- | lib/Remarks/RemarkFormat.cpp | 30 | ||||
-rw-r--r-- | lib/Remarks/RemarkParser.cpp | 119 | ||||
-rw-r--r-- | lib/Remarks/RemarkStringTable.cpp | 48 | ||||
-rw-r--r-- | lib/Remarks/YAMLRemarkParser.cpp | 327 | ||||
-rw-r--r-- | lib/Remarks/YAMLRemarkParser.h | 96 | ||||
-rw-r--r-- | lib/Remarks/YAMLRemarkSerializer.cpp | 167 |
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; +} |