[llvm] r344162 - [OptRemarks] Add library for parsing optimization remarks

Francis Visoiu Mistrih via llvm-commits llvm-commits at lists.llvm.org
Wed Oct 10 10:58:09 PDT 2018


Author: thegameg
Date: Wed Oct 10 10:58:09 2018
New Revision: 344162

URL: http://llvm.org/viewvc/llvm-project?rev=344162&view=rev
Log:
[OptRemarks] Add library for parsing optimization remarks

Add a library that parses optimization remarks (currently YAML, so based
on the YAMLParser).

The goal is to be able to provide tools a remark parser that is not
completely dependent on YAML, in case we decide to change the format
later.

It exposes a C API which takes a handler that is called with the remark
structure.

It adds a libLLVMOptRemark.a static library, and it's used in-tree by
the llvm-opt-report tool (from which the parser has been mostly moved
out).

Differential Revision: https://reviews.llvm.org/D52776

Added:
    llvm/trunk/include/llvm-c/OptRemarks.h
    llvm/trunk/lib/OptRemarks/
    llvm/trunk/lib/OptRemarks/CMakeLists.txt
    llvm/trunk/lib/OptRemarks/LLVMBuild.txt
      - copied, changed from r344161, llvm/trunk/lib/LLVMBuild.txt
    llvm/trunk/lib/OptRemarks/OptRemarksParser.cpp
    llvm/trunk/unittests/OptRemarks/
    llvm/trunk/unittests/OptRemarks/CMakeLists.txt
    llvm/trunk/unittests/OptRemarks/OptRemarksParsingTest.cpp
Modified:
    llvm/trunk/lib/CMakeLists.txt
    llvm/trunk/lib/LLVMBuild.txt
    llvm/trunk/tools/llvm-opt-report/CMakeLists.txt
    llvm/trunk/tools/llvm-opt-report/OptReport.cpp
    llvm/trunk/unittests/CMakeLists.txt

Added: llvm/trunk/include/llvm-c/OptRemarks.h
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/include/llvm-c/OptRemarks.h?rev=344162&view=auto
==============================================================================
--- llvm/trunk/include/llvm-c/OptRemarks.h (added)
+++ llvm/trunk/include/llvm-c/OptRemarks.h Wed Oct 10 10:58:09 2018
@@ -0,0 +1,197 @@
+/*===-- llvm-c/OptRemarks.h - OptRemarks Public C Interface -------*- C -*-===*\
+|*                                                                            *|
+|*                     The LLVM Compiler Infrastructure                       *|
+|*                                                                            *|
+|* This file is distributed under the University of Illinois Open Source      *|
+|* License. See LICENSE.TXT for details.                                      *|
+|*                                                                            *|
+|*===----------------------------------------------------------------------===*|
+|*                                                                            *|
+|* This header provides a public interface to an opt-remark library.          *|
+|* LLVM provides an implementation of this interface.                         *|
+|*                                                                            *|
+\*===----------------------------------------------------------------------===*/
+
+#ifndef LLVM_C_OPT_REMARKS_H
+#define LLVM_C_OPT_REMARKS_H
+
+#include "llvm-c/Core.h"
+#include "llvm-c/Types.h"
+#ifdef __cplusplus
+#include <cstddef>
+extern "C" {
+#else
+#include <stddef.h>
+#endif /* !defined(__cplusplus) */
+
+/**
+ * @defgroup LLVMCOPTREMARKS OptRemarks
+ * @ingroup LLVMC
+ *
+ * @{
+ */
+
+#define OPT_REMARKS_API_VERSION 0
+
+/**
+ * String containing a buffer and a length. The buffer is not guaranteed to be
+ * zero-terminated.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+typedef struct {
+  const char *Str;
+  uint32_t Len;
+} LLVMOptRemarkStringRef;
+
+/**
+ * DebugLoc containing File, Line and Column.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+typedef struct {
+  // File:
+  LLVMOptRemarkStringRef SourceFile;
+  // Line:
+  uint32_t SourceLineNumber;
+  // Column:
+  uint32_t SourceColumnNumber;
+} LLVMOptRemarkDebugLoc;
+
+/**
+ * Element of the "Args" list. The key might give more information about what
+ * are the semantics of the value, e.g. "Callee" will tell you that the value
+ * is a symbol that names a function.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+typedef struct {
+  // e.g. "Callee"
+  LLVMOptRemarkStringRef Key;
+  // e.g. "malloc"
+  LLVMOptRemarkStringRef Value;
+
+  // "DebugLoc": Optional
+  LLVMOptRemarkDebugLoc DebugLoc;
+} LLVMOptRemarkArg;
+
+/**
+ * One remark entry.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+typedef struct {
+  // e.g. !Missed, !Passed
+  LLVMOptRemarkStringRef RemarkType;
+  // "Pass": Required
+  LLVMOptRemarkStringRef PassName;
+  // "Name": Required
+  LLVMOptRemarkStringRef RemarkName;
+  // "Function": Required
+  LLVMOptRemarkStringRef FunctionName;
+
+  // "DebugLoc": Optional
+  LLVMOptRemarkDebugLoc DebugLoc;
+  // "Hotness": Optional
+  uint32_t Hotness;
+  // "Args": Optional. It is an array of `num_args` elements.
+  uint32_t NumArgs;
+  LLVMOptRemarkArg *Args;
+} LLVMOptRemarkEntry;
+
+typedef struct LLVMOptRemarkOpaqueParser *LLVMOptRemarkParserRef;
+
+/**
+ * Creates a remark parser that can be used to read and parse the buffer located
+ * in \p Buf of size \p Size.
+ *
+ * \p Buf cannot be NULL.
+ *
+ * This function should be paired with LLVMOptRemarkParserDispose() to avoid
+ * leaking resources.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+extern LLVMOptRemarkParserRef LLVMOptRemarkParserCreate(const void *Buf,
+                                                        uint64_t Size);
+
+/**
+ * Returns the next remark in the file.
+ *
+ * The value pointed to by the return value is invalidated by the next call to
+ * LLVMOptRemarkParserGetNext().
+ *
+ * If the parser reaches the end of the buffer, the return value will be NULL.
+ *
+ * In the case of an error, the return value will be NULL, and:
+ *
+ * 1) LLVMOptRemarkParserHasError() will return `1`.
+ *
+ * 2) LLVMOptRemarkParserGetErrorMessage() will return a descriptive error
+ *    message.
+ *
+ * An error may occur if:
+ *
+ * 1) An argument is invalid.
+ *
+ * 2) There is a YAML parsing error. This type of error aborts parsing
+ *    immediately and returns `1`. It can occur on malformed YAML.
+ *
+ * 3) Remark parsing error. If this type of error occurs, the parser won't call
+ *    the handler and will continue to the next one. It can occur on malformed
+ *    remarks, like missing or extra fields in the file.
+ *
+ * Here is a quick example of the usage:
+ *
+ * ```
+ *  LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, Size);
+ *  LLVMOptRemarkEntry *Remark = NULL;
+ *  while ((Remark == LLVMOptRemarkParserGetNext(Parser))) {
+ *    // use Remark
+ *  }
+ *  bool HasError = LLVMOptRemarkParserHasError(Parser);
+ *  LLVMOptRemarkParserDispose(Parser);
+ * ```
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+extern LLVMOptRemarkEntry *
+LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser);
+
+/**
+ * Returns `1` if the parser encountered an error while parsing the buffer.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+extern LLVMBool LLVMOptRemarkParserHasError(LLVMOptRemarkParserRef Parser);
+
+/**
+ * Returns a null-terminated string containing an error message.
+ *
+ * In case of no error, the result is `NULL`.
+ *
+ * The memory of the string is bound to the lifetime of \p Parser. If
+ * LLVMOptRemarkParserDispose() is called, the memory of the string will be
+ * released.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+extern const char *
+LLVMOptRemarkParserGetErrorMessage(LLVMOptRemarkParserRef Parser);
+
+/**
+ * Releases all the resources used by \p Parser.
+ *
+ * \since OPT_REMARKS_API_VERSION=0
+ */
+extern void LLVMOptRemarkParserDispose(LLVMOptRemarkParserRef Parser);
+
+/**
+ * @} // endgoup LLVMCOPTREMARKS
+ */
+
+#ifdef __cplusplus
+}
+#endif /* !defined(__cplusplus) */
+
+#endif /* LLVM_C_OPT_REMARKS_H */

Modified: llvm/trunk/lib/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/CMakeLists.txt?rev=344162&r1=344161&r2=344162&view=diff
==============================================================================
--- llvm/trunk/lib/CMakeLists.txt (original)
+++ llvm/trunk/lib/CMakeLists.txt Wed Oct 10 10:58:09 2018
@@ -15,6 +15,7 @@ add_subdirectory(MC)
 add_subdirectory(Object)
 add_subdirectory(ObjectYAML)
 add_subdirectory(Option)
+add_subdirectory(OptRemarks)
 add_subdirectory(DebugInfo)
 add_subdirectory(ExecutionEngine)
 add_subdirectory(Target)

Modified: llvm/trunk/lib/LLVMBuild.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/LLVMBuild.txt?rev=344162&r1=344161&r2=344162&view=diff
==============================================================================
--- llvm/trunk/lib/LLVMBuild.txt (original)
+++ llvm/trunk/lib/LLVMBuild.txt Wed Oct 10 10:58:09 2018
@@ -35,6 +35,7 @@ subdirectories =
  BinaryFormat
  ObjectYAML
  Option
+ OptRemarks
  Passes
  ProfileData
  Support

Added: llvm/trunk/lib/OptRemarks/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/OptRemarks/CMakeLists.txt?rev=344162&view=auto
==============================================================================
--- llvm/trunk/lib/OptRemarks/CMakeLists.txt (added)
+++ llvm/trunk/lib/OptRemarks/CMakeLists.txt Wed Oct 10 10:58:09 2018
@@ -0,0 +1,3 @@
+add_llvm_library(LLVMOptRemarks
+  OptRemarksParser.cpp
+)

Copied: llvm/trunk/lib/OptRemarks/LLVMBuild.txt (from r344161, llvm/trunk/lib/LLVMBuild.txt)
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/OptRemarks/LLVMBuild.txt?p2=llvm/trunk/lib/OptRemarks/LLVMBuild.txt&p1=llvm/trunk/lib/LLVMBuild.txt&r1=344161&r2=344162&rev=344162&view=diff
==============================================================================
--- llvm/trunk/lib/LLVMBuild.txt (original)
+++ llvm/trunk/lib/OptRemarks/LLVMBuild.txt Wed Oct 10 10:58:09 2018
@@ -1,4 +1,4 @@
-;===- ./lib/LLVMBuild.txt --------------------------------------*- Conf -*--===;
+;===- ./lib/OptRemarks/LLVMBuild.txt ---------------------------*- Conf -*--===;
 ;
 ;                     The LLVM Compiler Infrastructure
 ;
@@ -15,38 +15,8 @@
 ;
 ;===------------------------------------------------------------------------===;
 
-[common]
-subdirectories =
- Analysis
- AsmParser
- Bitcode
- CodeGen
- DebugInfo
- Demangle
- ExecutionEngine
- FuzzMutate
- LineEditor
- Linker
- IR
- IRReader
- LTO
- MC
- Object
- BinaryFormat
- ObjectYAML
- Option
- Passes
- ProfileData
- Support
- TableGen
- Target
- Testing
- ToolDrivers
- Transforms
- WindowsManifest
- XRay
-
 [component_0]
-type = Group
-name = Libraries
-parent = $ROOT
+type = Library
+name = OptRemarks
+parent = Libraries
+required_libraries = Support

Added: llvm/trunk/lib/OptRemarks/OptRemarksParser.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/lib/OptRemarks/OptRemarksParser.cpp?rev=344162&view=auto
==============================================================================
--- llvm/trunk/lib/OptRemarks/OptRemarksParser.cpp (added)
+++ llvm/trunk/lib/OptRemarks/OptRemarksParser.cpp Wed Oct 10 10:58:09 2018
@@ -0,0 +1,368 @@
+//===- OptRemarksParser.cpp -----------------------------------------------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is dual licensed under the MIT and the University of Illinois Open
+// Source Licenses. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file provides utility methods used by clients that want to use the
+// parser for optimization remarks in LLVM.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/OptRemarks.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/Support/SourceMgr.h"
+#include "llvm/Support/YAMLTraits.h"
+
+using namespace llvm;
+
+namespace {
+struct RemarkParser {
+  /// 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<LLVMOptRemarkEntry> LastRemark;
+  /// Temporary parsing buffer for the arguments.
+  SmallVector<LLVMOptRemarkArg, 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<LLVMOptRemarkArg> *Args;
+    StringRef Type;
+    StringRef Pass;
+    StringRef Name;
+    StringRef Function;
+    /// Optional.
+    Optional<StringRef> File;
+    Optional<unsigned> Line;
+    Optional<unsigned> Column;
+    Optional<unsigned> Hotness;
+
+    ParseState(SmallVectorImpl<LLVMOptRemarkArg> &Args) : Args(&Args) {}
+    /// Use Args only as a **temporary** buffer.
+    ~ParseState() { Args->clear(); }
+  };
+
+  ParseState State;
+
+  /// Set to `true` if we had any errors during parsing.
+  bool HadAnyErrors = false;
+
+  RemarkParser(StringRef Buf)
+      : SM(), Stream(Buf, SM), ErrorString(), ErrorStream(ErrorString),
+        DI(Stream.begin()), LastRemark(), TmpArgs(), State(TmpArgs) {
+    SM.setDiagHandler(RemarkParser::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<LLVMOptRemarkArg> &TmpArgs, yaml::Node &Node);
+
+  /// Handle a diagnostic from the YAML stream. Records the error in the
+  /// RemarkParser class.
+  static void HandleDiagnostic(const SMDiagnostic &Diag, void *Ctx) {
+    assert(Ctx && "Expected non-null Ctx in diagnostic handler.");
+    auto *Parser = static_cast<RemarkParser *>(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 LLVMOptRemarkStringRef toOptRemarkStr(StringRef Str) {
+  return {Str.data(), static_cast<uint32_t>(Str.size())};
+}
+
+Error RemarkParser::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 RemarkParser::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 RemarkParser::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 RemarkParser::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 RemarkParser::parseArg(SmallVectorImpl<LLVMOptRemarkArg> &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);
+
+    // Try to parse a string.
+    if (Error E = parseValue(ValueStr, ArgEntry))
+      return E;
+
+    // Keep the key from the string.
+    KeyStr = KeyName;
+  }
+
+  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(LLVMOptRemarkArg{
+      toOptRemarkStr(KeyStr), toOptRemarkStr(ValueStr),
+      LLVMOptRemarkDebugLoc{toOptRemarkStr(File.getValueOr(StringRef())),
+                            Line.getValueOr(0), Column.getValueOr(0)}});
+
+  return Error::success();
+}
+
+Error RemarkParser::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 = LLVMOptRemarkEntry{
+      toOptRemarkStr(State.Type),
+      toOptRemarkStr(State.Pass),
+      toOptRemarkStr(State.Name),
+      toOptRemarkStr(State.Function),
+      LLVMOptRemarkDebugLoc{toOptRemarkStr(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();
+}
+} // namespace
+
+// Create wrappers for C Binding types (see CBindingWrapping.h).
+DEFINE_SIMPLE_CONVERSION_FUNCTIONS(RemarkParser, LLVMOptRemarkParserRef);
+
+extern "C" LLVMOptRemarkParserRef LLVMOptRemarkParserCreate(const void *Buf,
+                                                            uint64_t Size) {
+  return wrap(
+      new RemarkParser(StringRef(static_cast<const char *>(Buf), Size)));
+}
+
+extern "C" LLVMOptRemarkEntry *
+LLVMOptRemarkParserGetNext(LLVMOptRemarkParserRef Parser) {
+  RemarkParser &TheParser = *unwrap(Parser);
+  // Check for EOF.
+  if (TheParser.HadAnyErrors || TheParser.DI == TheParser.Stream.end())
+    return nullptr;
+
+  // 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;
+    });
+    return nullptr;
+  }
+
+  // Move on.
+  ++TheParser.DI;
+
+  // Return the just-parsed remark.
+  if (Optional<LLVMOptRemarkEntry> &Entry = TheParser.LastRemark)
+    return &*Entry;
+  return nullptr;
+}
+
+extern "C" LLVMBool LLVMOptRemarkParserHasError(LLVMOptRemarkParserRef Parser) {
+  return unwrap(Parser)->HadAnyErrors;
+}
+
+extern "C" const char *
+LLVMOptRemarkParserGetErrorMessage(LLVMOptRemarkParserRef Parser) {
+  return unwrap(Parser)->ErrorStream.str().c_str();
+}
+
+extern "C" void LLVMOptRemarkParserDispose(LLVMOptRemarkParserRef Parser) {
+  delete unwrap(Parser);
+}

Modified: llvm/trunk/tools/llvm-opt-report/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-report/CMakeLists.txt?rev=344162&r1=344161&r2=344162&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-opt-report/CMakeLists.txt (original)
+++ llvm/trunk/tools/llvm-opt-report/CMakeLists.txt Wed Oct 10 10:58:09 2018
@@ -1,4 +1,4 @@
-set(LLVM_LINK_COMPONENTS Core Demangle Object Support)
+set(LLVM_LINK_COMPONENTS Core Demangle Object OptRemarks Support)
 
 add_llvm_tool(llvm-opt-report
   OptReport.cpp

Modified: llvm/trunk/tools/llvm-opt-report/OptReport.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/tools/llvm-opt-report/OptReport.cpp?rev=344162&r1=344161&r2=344162&view=diff
==============================================================================
--- llvm/trunk/tools/llvm-opt-report/OptReport.cpp (original)
+++ llvm/trunk/tools/llvm-opt-report/OptReport.cpp Wed Oct 10 10:58:09 2018
@@ -28,6 +28,7 @@
 #include "llvm/Support/WithColor.h"
 #include "llvm/Support/YAMLTraits.h"
 #include "llvm/Support/raw_ostream.h"
+#include "llvm-c/OptRemarks.h"
 #include <cstdlib>
 #include <map>
 #include <set>
@@ -142,104 +143,44 @@ typedef std::map<std::string, std::map<i
           OptReportLocationInfo>>>> LocationInfoTy;
 } // anonymous namespace
 
-static void collectLocationInfo(yaml::Stream &Stream,
-                                LocationInfoTy &LocationInfo) {
-  SmallVector<char, 8> Tmp;
-
-  // Note: We're using the YAML parser here directly, instead of using the
-  // YAMLTraits implementation, because the YAMLTraits implementation does not
-  // support a way to handle only a subset of the input keys (it will error out
-  // if there is an input key that you don't map to your class), and
-  // furthermore, it does not provide a way to handle the Args sequence of
-  // key/value pairs, where the order must be captured and the 'String' key
-  // might be repeated.
-  for (auto &Doc : Stream) {
-    auto *Root = dyn_cast<yaml::MappingNode>(Doc.getRoot());
-    if (!Root)
-      continue;
+static bool readLocationInfo(LocationInfoTy &LocationInfo) {
+  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
+      MemoryBuffer::getFile(InputFileName.c_str());
+  if (std::error_code EC = Buf.getError()) {
+    WithColor::error() << "Can't open file " << InputFileName << ": "
+                       << EC.message() << "\n";
+    return false;
+  }
 
-    bool Transformed = Root->getRawTag() == "!Passed";
-    std::string Pass, File, Function;
-    int Line = 0, Column = 1;
+  StringRef Buffer = (*Buf)->getBuffer();
+  LLVMOptRemarkParserRef Parser =
+      LLVMOptRemarkParserCreate(Buffer.data(), Buffer.size());
+
+  LLVMOptRemarkEntry *Remark = nullptr;
+  while ((Remark = LLVMOptRemarkParserGetNext(Parser))) {
+    bool Transformed =
+        StringRef(Remark->RemarkType.Str, Remark->RemarkType.Len) == "!Passed";
+    StringRef Pass(Remark->PassName.Str, Remark->PassName.Len);
+    StringRef File(Remark->DebugLoc.SourceFile.Str,
+                   Remark->DebugLoc.SourceFile.Len);
+    StringRef Function(Remark->FunctionName.Str, Remark->FunctionName.Len);
+    uint32_t Line = Remark->DebugLoc.SourceLineNumber;
+    uint32_t Column = Remark->DebugLoc.SourceColumnNumber;
+    ArrayRef<LLVMOptRemarkArg> Args(Remark->Args, Remark->NumArgs);
 
     int VectorizationFactor = 1;
     int InterleaveCount = 1;
     int UnrollCount = 1;
 
-    for (auto &RootChild : *Root) {
-      auto *Key = dyn_cast<yaml::ScalarNode>(RootChild.getKey());
-      if (!Key)
-        continue;
-      StringRef KeyName = Key->getValue(Tmp);
-      if (KeyName == "Pass") {
-        auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
-        if (!Value)
-          continue;
-        Pass = Value->getValue(Tmp);
-      } else if (KeyName == "Function") {
-        auto *Value = dyn_cast<yaml::ScalarNode>(RootChild.getValue());
-        if (!Value)
-          continue;
-        Function = Value->getValue(Tmp);
-      } else if (KeyName == "DebugLoc") {
-        auto *DebugLoc = dyn_cast<yaml::MappingNode>(RootChild.getValue());
-        if (!DebugLoc)
-          continue;
-
-        for (auto &DLChild : *DebugLoc) {
-          auto *DLKey = dyn_cast<yaml::ScalarNode>(DLChild.getKey());
-          if (!DLKey)
-            continue;
-          StringRef DLKeyName = DLKey->getValue(Tmp);
-          if (DLKeyName == "File") {
-            auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
-            if (!Value)
-              continue;
-            File = Value->getValue(Tmp);
-          } else if (DLKeyName == "Line") {
-            auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
-            if (!Value)
-              continue;
-            Value->getValue(Tmp).getAsInteger(10, Line);
-          } else if (DLKeyName == "Column") {
-            auto *Value = dyn_cast<yaml::ScalarNode>(DLChild.getValue());
-            if (!Value)
-              continue;
-            Value->getValue(Tmp).getAsInteger(10, Column);
-          }
-        }
-      } else if (KeyName == "Args") {
-        auto *Args = dyn_cast<yaml::SequenceNode>(RootChild.getValue());
-        if (!Args)
-          continue;
-        for (auto &ArgChild : *Args) {
-          auto *ArgMap = dyn_cast<yaml::MappingNode>(&ArgChild);
-          if (!ArgMap)
-            continue;
-          for (auto &ArgKV : *ArgMap) {
-            auto *ArgKey = dyn_cast<yaml::ScalarNode>(ArgKV.getKey());
-            if (!ArgKey)
-              continue;
-            StringRef ArgKeyName = ArgKey->getValue(Tmp);
-            if (ArgKeyName == "VectorizationFactor") {
-              auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
-              if (!Value)
-                continue;
-              Value->getValue(Tmp).getAsInteger(10, VectorizationFactor);
-            } else if (ArgKeyName == "InterleaveCount") {
-              auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
-              if (!Value)
-                continue;
-              Value->getValue(Tmp).getAsInteger(10, InterleaveCount);
-            } else if (ArgKeyName == "UnrollCount") {
-              auto *Value = dyn_cast<yaml::ScalarNode>(ArgKV.getValue());
-              if (!Value)
-                continue;
-              Value->getValue(Tmp).getAsInteger(10, UnrollCount);
-            }
-          }
-        }
-      }
+    for (const LLVMOptRemarkArg &Arg : Args) {
+      StringRef ArgKeyName(Arg.Key.Str, Arg.Key.Len);
+      StringRef ArgValue(Arg.Value.Str, Arg.Value.Len);
+      if (ArgKeyName == "VectorizationFactor")
+        ArgValue.getAsInteger(10, VectorizationFactor);
+      else if (ArgKeyName == "InterleaveCount")
+        ArgValue.getAsInteger(10, InterleaveCount);
+      else if (ArgKeyName == "UnrollCount")
+        ArgValue.getAsInteger(10, UnrollCount);
     }
 
     if (Line < 1 || File.empty())
@@ -268,22 +209,13 @@ static void collectLocationInfo(yaml::St
       UpdateLLII(LI.Vectorized);
     }
   }
-}
-
-static bool readLocationInfo(LocationInfoTy &LocationInfo) {
-  ErrorOr<std::unique_ptr<MemoryBuffer>> Buf =
-      MemoryBuffer::getFileOrSTDIN(InputFileName);
-  if (std::error_code EC = Buf.getError()) {
-    WithColor::error() << "Can't open file " << InputFileName << ": "
-                       << EC.message() << "\n";
-    return false;
-  }
 
-  SourceMgr SM;
-  yaml::Stream Stream(Buf.get()->getBuffer(), SM);
-  collectLocationInfo(Stream, LocationInfo);
+  bool HasError = LLVMOptRemarkParserHasError(Parser);
+  if (HasError)
+    WithColor::error() << LLVMOptRemarkParserGetErrorMessage(Parser) << "\n";
 
-  return true;
+  LLVMOptRemarkParserDispose(Parser);
+  return !HasError;
 }
 
 static bool writeReport(LocationInfoTy &LocationInfo) {

Modified: llvm/trunk/unittests/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/CMakeLists.txt?rev=344162&r1=344161&r2=344162&view=diff
==============================================================================
--- llvm/trunk/unittests/CMakeLists.txt (original)
+++ llvm/trunk/unittests/CMakeLists.txt Wed Oct 10 10:58:09 2018
@@ -26,6 +26,7 @@ add_subdirectory(MI)
 add_subdirectory(Object)
 add_subdirectory(ObjectYAML)
 add_subdirectory(Option)
+add_subdirectory(OptRemarks)
 add_subdirectory(Passes)
 add_subdirectory(ProfileData)
 add_subdirectory(Support)

Added: llvm/trunk/unittests/OptRemarks/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/OptRemarks/CMakeLists.txt?rev=344162&view=auto
==============================================================================
--- llvm/trunk/unittests/OptRemarks/CMakeLists.txt (added)
+++ llvm/trunk/unittests/OptRemarks/CMakeLists.txt Wed Oct 10 10:58:09 2018
@@ -0,0 +1,8 @@
+set(LLVM_LINK_COMPONENTS
+  OptRemarks
+  Support
+  )
+
+add_llvm_unittest(OptRemarksTests
+  OptRemarksParsingTest.cpp
+  )

Added: llvm/trunk/unittests/OptRemarks/OptRemarksParsingTest.cpp
URL: http://llvm.org/viewvc/llvm-project/llvm/trunk/unittests/OptRemarks/OptRemarksParsingTest.cpp?rev=344162&view=auto
==============================================================================
--- llvm/trunk/unittests/OptRemarks/OptRemarksParsingTest.cpp (added)
+++ llvm/trunk/unittests/OptRemarks/OptRemarksParsingTest.cpp Wed Oct 10 10:58:09 2018
@@ -0,0 +1,433 @@
+//===- unittest/Support/OptRemarksParsingTest.cpp - OptTable tests --------===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm-c/OptRemarks.h"
+#include "gtest/gtest.h"
+
+using namespace llvm;
+
+template <size_t N> bool tryParse(const char (&Buf)[N]) {
+  LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1);
+  LLVMOptRemarkEntry *Remark = nullptr;
+  while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) {
+    EXPECT_TRUE(Remark == nullptr); // Only one remark per test.
+    Remark = NewRemark;
+  }
+  EXPECT_TRUE(Remark != nullptr); // We need *exactly* one remark per test.
+  bool HasError = LLVMOptRemarkParserHasError(Parser);
+  LLVMOptRemarkParserDispose(Parser);
+  return !HasError;
+}
+
+template <size_t N>
+bool parseExpectError(const char (&Buf)[N], const char *Error) {
+  LLVMOptRemarkParserRef Parser = LLVMOptRemarkParserCreate(Buf, N - 1);
+  LLVMOptRemarkEntry *Remark = nullptr;
+  while (LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser)) {
+    EXPECT_FALSE(NewRemark);
+  }
+  EXPECT_TRUE(Remark == nullptr); // We are parsing only one malformed remark.
+  EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser));
+  bool MatchesError =
+      StringRef(LLVMOptRemarkParserGetErrorMessage(Parser)).contains(Error);
+  LLVMOptRemarkParserDispose(Parser);
+
+  return MatchesError;
+}
+
+TEST(OptRemarks, OptRemarksParsingEmpty) {
+  StringRef Buf = R"YAML(
+)YAML";
+  LLVMOptRemarkParserRef Parser =
+      LLVMOptRemarkParserCreate(Buf.data(), Buf.size());
+  LLVMOptRemarkEntry *NewRemark = LLVMOptRemarkParserGetNext(Parser);
+  EXPECT_TRUE(NewRemark == nullptr); // No remark expected.
+  EXPECT_TRUE(LLVMOptRemarkParserHasError(Parser));
+  EXPECT_TRUE(StringRef(LLVMOptRemarkParserGetErrorMessage(Parser))
+                  .contains("document root is not of mapping type."));
+  LLVMOptRemarkParserDispose(Parser);
+}
+
+TEST(OptRemarks, OptRemarksParsingGood) {
+  EXPECT_TRUE(tryParse(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+DebugLoc: { File: file.c, Line: 3, Column: 12 }
+Function: foo
+Args:
+  - Callee: bar
+  - String: ' will not be inlined into '
+  - Caller: foo
+    DebugLoc: { File: file.c, Line: 2, Column: 0 }
+  - String: ' because its definition is unavailable'
+)YAML"));
+
+  // No debug loc should also pass.
+  EXPECT_TRUE(tryParse(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args:
+  - Callee: bar
+  - String: ' will not be inlined into '
+  - Caller: foo
+    DebugLoc: { File: file.c, Line: 2, Column: 0 }
+  - String: ' because its definition is unavailable'
+)YAML"));
+
+  // No args is also ok.
+  EXPECT_TRUE(tryParse(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+DebugLoc: { File: file.c, Line: 3, Column: 12 }
+Function: foo
+)YAML"));
+
+  // Different order.
+  EXPECT_TRUE(tryParse(R"YAML(
+--- !Missed
+DebugLoc: { Line: 3, Column: 12, File: file.c }
+Function: foo
+Name: NoDefinition
+Args:
+  - Callee: bar
+  - String: ' will not be inlined into '
+  - Caller: foo
+    DebugLoc: { File: file.c, Line: 2, Column: 0 }
+  - String: ' because its definition is unavailable'
+Pass: inline
+)YAML"));
+}
+
+// Mandatory common part of a remark.
+#define COMMON_REMARK "\nPass: inline\nName: NoDefinition\nFunction: foo\n"
+// Test all the types.
+TEST(OptRemarks, OptRemarksParsingTypes) {
+  // Type: Passed
+  EXPECT_TRUE(tryParse("--- !Passed" COMMON_REMARK));
+  // Type: Missed
+  EXPECT_TRUE(tryParse("--- !Missed" COMMON_REMARK));
+  // Type: Analysis
+  EXPECT_TRUE(tryParse("--- !Analysis" COMMON_REMARK));
+  // Type: AnalysisFPCompute
+  EXPECT_TRUE(tryParse("--- !AnalysisFPCompute" COMMON_REMARK));
+  // Type: AnalysisAliasing
+  EXPECT_TRUE(tryParse("--- !AnalysisAliasing" COMMON_REMARK));
+  // Type: Failure
+  EXPECT_TRUE(tryParse("--- !Failure" COMMON_REMARK));
+}
+#undef COMMON_REMARK
+
+TEST(OptRemarks, OptRemarksParsingMissingFields) {
+  // No type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+---
+Pass: inline
+Name: NoDefinition
+Function: foo
+)YAML",
+                               "error: Type, Pass, Name or Function missing."));
+  // No pass.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Name: NoDefinition
+Function: foo
+)YAML",
+                               "error: Type, Pass, Name or Function missing."));
+  // No name.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Function: foo
+)YAML",
+                               "error: Type, Pass, Name or Function missing."));
+  // No function.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+)YAML",
+                               "error: Type, Pass, Name or Function missing."));
+  // Debug loc but no file.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { Line: 3, Column: 12 }
+)YAML",
+                               "DebugLoc node incomplete."));
+  // Debug loc but no line.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Column: 12 }
+)YAML",
+                               "DebugLoc node incomplete."));
+  // Debug loc but no column.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Line: 3 }
+)YAML",
+                               "DebugLoc node incomplete."));
+}
+
+TEST(OptRemarks, OptRemarksParsingWrongTypes) {
+  // Wrong debug loc type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: foo
+)YAML",
+                               "expected a value of mapping type."));
+  // Wrong line type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Line: b, Column: 12 }
+)YAML",
+                               "expected a value of integer type."));
+  // Wrong column type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Line: 3, Column: c }
+)YAML",
+                               "expected a value of integer type."));
+  // Wrong args type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args: foo
+)YAML",
+                               "wrong value type for key."));
+  // Wrong key type.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+{ A: a }: inline
+Name: NoDefinition
+Function: foo
+)YAML",
+                               "key is not a string."));
+  // Debug loc with unknown entry.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Column: 12, Unknown: 12 }
+)YAML",
+                               "unknown entry in DebugLoc map."));
+  // Unknown entry.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Unknown: inline
+)YAML",
+                               "unknown key."));
+  // Not a scalar.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: { File: a, Line: 1, Column: 2 }
+Name: NoDefinition
+Function: foo
+)YAML",
+                               "expected a value of scalar type."));
+  // Not a string file in debug loc.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: { a: b }, Column: 12, Line: 12 }
+)YAML",
+                               "expected a value of scalar type."));
+  // Not a integer column in debug loc.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Column: { a: b }, Line: 12 }
+)YAML",
+                               "expected a value of scalar type."));
+  // Not a integer line in debug loc.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Column: 12, Line: { a: b } }
+)YAML",
+                               "expected a value of scalar type."));
+  // Not a mapping type value for args.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+DebugLoc: { File: file.c, Column: 12, Line: { a: b } }
+)YAML",
+                               "expected a value of scalar type."));
+}
+
+TEST(OptRemarks, OptRemarksParsingWrongArgs) {
+  // Multiple debug locs per arg.
+  EXPECT_TRUE(
+      parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args:
+  - Str: string
+    DebugLoc: { File: a, Line: 1, Column: 2 }
+    DebugLoc: { File: a, Line: 1, Column: 2 }
+)YAML",
+                       "only one DebugLoc entry is allowed per argument."));
+  // Multiple strings per arg.
+  EXPECT_TRUE(
+      parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args:
+  - Str: string
+    Str2: string
+    DebugLoc: { File: a, Line: 1, Column: 2 }
+)YAML",
+                       "only one string entry is allowed per argument."));
+  // No arg value.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args:
+  - Callee: ''
+  - DebugLoc: { File: a, Line: 1, Column: 2 }
+)YAML",
+                               "argument value is missing."));
+  // No arg value.
+  EXPECT_TRUE(parseExpectError(R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: foo
+Args:
+  - DebugLoc: { File: a, Line: 1, Column: 2 }
+)YAML",
+                               "argument key is missing."));
+
+}
+
+TEST(OptRemarks, OptRemarksGoodStruct) {
+  StringRef Buf = R"YAML(
+--- !Missed
+Pass: inline
+Name: NoDefinition
+DebugLoc: { File: file.c, Line: 3, Column: 12 }
+Function: foo
+Args:
+  - Callee: bar
+  - String: ' will not be inlined into '
+  - Caller: foo
+    DebugLoc: { File: file.c, Line: 2, Column: 0 }
+  - String: ' because its definition is unavailable'
+)YAML";
+
+  LLVMOptRemarkParserRef Parser =
+      LLVMOptRemarkParserCreate(Buf.data(), Buf.size());
+  LLVMOptRemarkEntry *Remark = LLVMOptRemarkParserGetNext(Parser);
+  EXPECT_FALSE(Remark == nullptr);
+  EXPECT_EQ(StringRef(Remark->RemarkType.Str, 7), "!Missed");
+  EXPECT_EQ(Remark->RemarkType.Len, 7U);
+  EXPECT_EQ(StringRef(Remark->PassName.Str, 6), "inline");
+  EXPECT_EQ(Remark->PassName.Len, 6U);
+  EXPECT_EQ(StringRef(Remark->RemarkName.Str, 12), "NoDefinition");
+  EXPECT_EQ(Remark->RemarkName.Len, 12U);
+  EXPECT_EQ(StringRef(Remark->FunctionName.Str, 3), "foo");
+  EXPECT_EQ(Remark->FunctionName.Len, 3U);
+  EXPECT_EQ(StringRef(Remark->DebugLoc.SourceFile.Str, 6), "file.c");
+  EXPECT_EQ(Remark->DebugLoc.SourceFile.Len, 6U);
+  EXPECT_EQ(Remark->DebugLoc.SourceLineNumber, 3U);
+  EXPECT_EQ(Remark->DebugLoc.SourceColumnNumber, 12U);
+  EXPECT_EQ(Remark->Hotness, 0U);
+  EXPECT_EQ(Remark->NumArgs, 4U);
+  // Arg 0
+  {
+    LLVMOptRemarkArg &Arg = Remark->Args[0];
+    EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Callee");
+    EXPECT_EQ(Arg.Key.Len, 6U);
+    EXPECT_EQ(StringRef(Arg.Value.Str, 3), "bar");
+    EXPECT_EQ(Arg.Value.Len, 3U);
+    EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
+    EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
+  }
+  // Arg 1
+  {
+    LLVMOptRemarkArg &Arg = Remark->Args[1];
+    EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String");
+    EXPECT_EQ(Arg.Key.Len, 6U);
+    EXPECT_EQ(StringRef(Arg.Value.Str, 26), " will not be inlined into ");
+    EXPECT_EQ(Arg.Value.Len, 26U);
+    EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
+    EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
+  }
+  // Arg 2
+  {
+    LLVMOptRemarkArg &Arg = Remark->Args[2];
+    EXPECT_EQ(StringRef(Arg.Key.Str, 6), "Caller");
+    EXPECT_EQ(Arg.Key.Len, 6U);
+    EXPECT_EQ(StringRef(Arg.Value.Str, 3), "foo");
+    EXPECT_EQ(Arg.Value.Len, 3U);
+    EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 6), "file.c");
+    EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 6U);
+    EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 2U);
+    EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
+  }
+  // Arg 3
+  {
+    LLVMOptRemarkArg &Arg = Remark->Args[3];
+    EXPECT_EQ(StringRef(Arg.Key.Str, 6), "String");
+    EXPECT_EQ(Arg.Key.Len, 6U);
+    EXPECT_EQ(StringRef(Arg.Value.Str, 38),
+              " because its definition is unavailable");
+    EXPECT_EQ(Arg.Value.Len, 38U);
+    EXPECT_EQ(StringRef(Arg.DebugLoc.SourceFile.Str, 0), "");
+    EXPECT_EQ(Arg.DebugLoc.SourceFile.Len, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceLineNumber, 0U);
+    EXPECT_EQ(Arg.DebugLoc.SourceColumnNumber, 0U);
+  }
+
+  EXPECT_EQ(LLVMOptRemarkParserGetNext(Parser), nullptr);
+
+  EXPECT_FALSE(LLVMOptRemarkParserHasError(Parser));
+  LLVMOptRemarkParserDispose(Parser);
+}




More information about the llvm-commits mailing list