[llvm] e72e59e - [Remarks] Add support for linking remarks

Francis Visoiu Mistrih via llvm-commits llvm-commits at lists.llvm.org
Thu Oct 31 15:28:05 PDT 2019


Author: Francis Visoiu Mistrih
Date: 2019-10-31T15:26:55-07:00
New Revision: e72e59e90287c50539cb2c0afbf197aa0101a023

URL: https://github.com/llvm/llvm-project/commit/e72e59e90287c50539cb2c0afbf197aa0101a023
DIFF: https://github.com/llvm/llvm-project/commit/e72e59e90287c50539cb2c0afbf197aa0101a023.diff

LOG: [Remarks] Add support for linking remarks

Remarks are usually emitted per-TU, and for generating a standalone
remark file that can be shipped with the linked binary we need some kind
of tool to merge everything together.

The remarks::RemarkLinker class takes care of this and:

* Deduplicates remarks
* Filters remarks with no debug location
* Merges string tables from all the entries

As an output, it provides an iterator range that can be used to
serialize the remarks to a file.

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

Added: 
    llvm/include/llvm/Remarks/RemarkLinker.h
    llvm/lib/Remarks/RemarkLinker.cpp
    llvm/unittests/Remarks/RemarksLinkingTest.cpp

Modified: 
    llvm/include/llvm/Remarks/Remark.h
    llvm/include/llvm/Remarks/RemarkFormat.h
    llvm/lib/Remarks/CMakeLists.txt
    llvm/lib/Remarks/RemarkFormat.cpp
    llvm/unittests/Remarks/CMakeLists.txt

Removed: 
    


################################################################################
diff  --git a/llvm/include/llvm/Remarks/Remark.h b/llvm/include/llvm/Remarks/Remark.h
index 1243311fb8c5..73b2680e98da 100644
--- a/llvm/include/llvm/Remarks/Remark.h
+++ b/llvm/include/llvm/Remarks/Remark.h
@@ -110,6 +110,21 @@ struct Remark {
 DEFINE_SIMPLE_CONVERSION_FUNCTIONS(Remark, LLVMRemarkEntryRef)
 
 /// Comparison operators for Remark objects and dependent objects.
+
+template <typename T>
+bool operator<(const Optional<T> &LHS, const Optional<T> &RHS) {
+  // Sorting based on optionals should result in all `None` entries to appear
+  // before the valid entries. For example, remarks with no debug location will
+  // appear first.
+  if (!LHS && !RHS)
+    return false;
+  if (!LHS && RHS)
+    return true;
+  if (LHS && !RHS)
+    return false;
+  return *LHS < *RHS;
+}
+
 inline bool operator==(const RemarkLocation &LHS, const RemarkLocation &RHS) {
   return LHS.SourceFilePath == RHS.SourceFilePath &&
          LHS.SourceLine == RHS.SourceLine &&
@@ -120,6 +135,11 @@ inline bool operator!=(const RemarkLocation &LHS, const RemarkLocation &RHS) {
   return !(LHS == RHS);
 }
 
+inline bool operator<(const RemarkLocation &LHS, const RemarkLocation &RHS) {
+  return std::make_tuple(LHS.SourceFilePath, LHS.SourceLine, LHS.SourceColumn) <
+         std::make_tuple(RHS.SourceFilePath, RHS.SourceLine, RHS.SourceColumn);
+}
+
 inline bool operator==(const Argument &LHS, const Argument &RHS) {
   return LHS.Key == RHS.Key && LHS.Val == RHS.Val && LHS.Loc == RHS.Loc;
 }
@@ -128,6 +148,11 @@ inline bool operator!=(const Argument &LHS, const Argument &RHS) {
   return !(LHS == RHS);
 }
 
+inline bool operator<(const Argument &LHS, const Argument &RHS) {
+  return std::make_tuple(LHS.Key, LHS.Val, LHS.Loc) <
+         std::make_tuple(RHS.Key, RHS.Val, RHS.Loc);
+}
+
 inline bool operator==(const Remark &LHS, const Remark &RHS) {
   return LHS.RemarkType == RHS.RemarkType && LHS.PassName == RHS.PassName &&
          LHS.RemarkName == RHS.RemarkName &&
@@ -139,6 +164,13 @@ inline bool operator!=(const Remark &LHS, const Remark &RHS) {
   return !(LHS == RHS);
 }
 
+inline bool operator<(const Remark &LHS, const Remark &RHS) {
+  return std::make_tuple(LHS.RemarkType, LHS.PassName, LHS.RemarkName,
+                         LHS.FunctionName, LHS.Loc, LHS.Hotness, LHS.Args) <
+         std::make_tuple(RHS.RemarkType, RHS.PassName, RHS.RemarkName,
+                         RHS.FunctionName, RHS.Loc, RHS.Hotness, RHS.Args);
+}
+
 } // end namespace remarks
 } // end namespace llvm
 

diff  --git a/llvm/include/llvm/Remarks/RemarkFormat.h b/llvm/include/llvm/Remarks/RemarkFormat.h
index 6dd32b226099..a432c5adf59e 100644
--- a/llvm/include/llvm/Remarks/RemarkFormat.h
+++ b/llvm/include/llvm/Remarks/RemarkFormat.h
@@ -27,6 +27,9 @@ enum class Format { Unknown, YAML, YAMLStrTab, Bitstream };
 /// Parse and validate a string for the remark format.
 Expected<Format> parseFormat(StringRef FormatStr);
 
+/// Parse and validate a magic number to a remark format.
+Expected<Format> magicToFormat(StringRef Magic);
+
 } // end namespace remarks
 } // end namespace llvm
 

diff  --git a/llvm/include/llvm/Remarks/RemarkLinker.h b/llvm/include/llvm/Remarks/RemarkLinker.h
new file mode 100644
index 000000000000..c82c73d8c94f
--- /dev/null
+++ b/llvm/include/llvm/Remarks/RemarkLinker.h
@@ -0,0 +1,100 @@
+//===-- llvm/Remarks/RemarkLinker.h -----------------------------*- 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 an interface to link together multiple remark files.
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_REMARKS_REMARK_LINKER_H
+#define LLVM_REMARKS_REMARK_LINKER_H
+
+#include "llvm/Object/ObjectFile.h"
+#include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Remarks/RemarkStringTable.h"
+#include "llvm/Support/Error.h"
+#include <memory>
+#include <set>
+
+namespace llvm {
+namespace remarks {
+
+struct RemarkLinker {
+private:
+  /// Compare through the pointers.
+  struct RemarkPtrCompare {
+    bool operator()(const std::unique_ptr<Remark> &LHS,
+                    const std::unique_ptr<Remark> &RHS) const {
+      assert(LHS && RHS && "Invalid pointers to compare.");
+      return *LHS < *RHS;
+    };
+  };
+
+  /// The main string table for the remarks.
+  /// Note: all remarks should use the strings from this string table to avoid
+  /// dangling references.
+  StringTable StrTab;
+
+  /// A set holding unique remarks.
+  /// FIXME: std::set is probably not the most appropriate data structure here.
+  /// Due to the limitation of having a move-only key, there isn't another
+  /// obvious choice for now.
+  std::set<std::unique_ptr<Remark>, RemarkPtrCompare> Remarks;
+
+  /// A path to append before the external file path found in remark metadata.
+  Optional<std::string> PrependPath;
+
+  /// Keep this remark. If it's already in the set, discard it.
+  Remark &keep(std::unique_ptr<Remark> Remark);
+
+public:
+  /// Set a path to prepend to the external file path.
+  void setExternalFilePrependPath(StringRef PrependPath);
+
+  /// Link the remarks found in \p Buffer.
+  /// If \p RemarkFormat is not provided, try to deduce it from the metadata in
+  /// \p Buffer.
+  /// \p Buffer can be either a standalone remark container or just
+  /// metadata. This takes care of uniquing and merging the remarks.
+  Error link(StringRef Buffer, Optional<Format> RemarkFormat = None);
+
+  /// Link the remarks found in \p Obj by looking for the right section and
+  /// calling the method above.
+  Error link(const object::ObjectFile &Obj,
+             Optional<Format> RemarkFormat = None);
+
+  /// Serialize the linked remarks to the stream \p OS, using the format \p
+  /// RemarkFormat.
+  /// This clears internal state such as the string table.
+  /// Note: this implies that the serialization mode is standalone.
+  Error serialize(raw_ostream &OS, Format RemarksFormat) const;
+
+  /// Check whether there are any remarks linked.
+  bool empty() const { return Remarks.empty(); }
+
+  /// Return a collection of the linked unique remarks to iterate on.
+  /// Ex:
+  /// for (const Remark &R : RL.remarks() { [...] }
+  using iterator =
+      pointee_iterator<std::set<std::unique_ptr<Remark>>::iterator>;
+
+  iterator_range<iterator> remarks() const {
+    return {Remarks.begin(), Remarks.end()};
+  }
+};
+
+/// Returns a buffer with the contents of the remarks section depending on the
+/// format of the file. If the section doesn't exist, this returns an empty
+/// optional.
+Expected<Optional<StringRef>>
+getRemarksSectionContents(const object::ObjectFile &Obj);
+
+} // end namespace remarks
+} // end namespace llvm
+
+#endif /* LLVM_REMARKS_REMARK_LINKER_H */

diff  --git a/llvm/lib/Remarks/CMakeLists.txt b/llvm/lib/Remarks/CMakeLists.txt
index 9fbb1f069711..db84d631c2ca 100644
--- a/llvm/lib/Remarks/CMakeLists.txt
+++ b/llvm/lib/Remarks/CMakeLists.txt
@@ -3,6 +3,7 @@ add_llvm_library(LLVMRemarks
   BitstreamRemarkSerializer.cpp
   Remark.cpp
   RemarkFormat.cpp
+  RemarkLinker.cpp
   RemarkParser.cpp
   RemarkSerializer.cpp
   RemarkStringTable.cpp

diff  --git a/llvm/lib/Remarks/RemarkFormat.cpp b/llvm/lib/Remarks/RemarkFormat.cpp
index f2d0331ec6a8..c7fce151eff0 100644
--- a/llvm/lib/Remarks/RemarkFormat.cpp
+++ b/llvm/lib/Remarks/RemarkFormat.cpp
@@ -12,6 +12,7 @@
 
 #include "llvm/Remarks/RemarkFormat.h"
 #include "llvm/ADT/StringSwitch.h"
+#include "llvm/Remarks/BitstreamRemarkContainer.h"
 
 using namespace llvm;
 using namespace llvm::remarks;
@@ -30,3 +31,17 @@ Expected<Format> llvm::remarks::parseFormat(StringRef FormatStr) {
 
   return Result;
 }
+
+Expected<Format> llvm::remarks::magicToFormat(StringRef Magic) {
+  auto Result =
+      StringSwitch<Format>(Magic)
+          .StartsWith("--- ", Format::YAML) // This is only an assumption.
+          .StartsWith(remarks::Magic, Format::YAMLStrTab)
+          .StartsWith(remarks::ContainerMagic, Format::Bitstream)
+          .Default(Format::Unknown);
+
+  if (Result == Format::Unknown)
+    return createStringError(std::make_error_code(std::errc::invalid_argument),
+                             "Unknown remark magic: '%s'", Magic.data());
+  return Result;
+}

diff  --git a/llvm/lib/Remarks/RemarkLinker.cpp b/llvm/lib/Remarks/RemarkLinker.cpp
new file mode 100644
index 000000000000..617ce770af66
--- /dev/null
+++ b/llvm/lib/Remarks/RemarkLinker.cpp
@@ -0,0 +1,126 @@
+//===- RemarkLinker.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 an implementation of the remark linker.
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Remarks/RemarkLinker.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Remarks/BitstreamRemarkContainer.h"
+#include "llvm/Remarks/RemarkParser.h"
+#include "llvm/Remarks/RemarkSerializer.h"
+#include "llvm/Support/Error.h"
+
+using namespace llvm;
+using namespace llvm::remarks;
+
+static Expected<StringRef>
+getRemarksSectionName(const object::ObjectFile &Obj) {
+  if (Obj.isMachO())
+    return StringRef("__remarks");
+  // ELF -> .remarks, but there is no ELF support at this point.
+  return createStringError(std::errc::illegal_byte_sequence,
+                           "Unsupported file format.");
+}
+
+Expected<Optional<StringRef>>
+llvm::remarks::getRemarksSectionContents(const object::ObjectFile &Obj) {
+  Expected<StringRef> SectionName = getRemarksSectionName(Obj);
+  if (!SectionName)
+    return SectionName.takeError();
+
+  for (const object::SectionRef &Section : Obj.sections()) {
+    Expected<StringRef> MaybeName = Section.getName();
+    if (!MaybeName)
+      return MaybeName.takeError();
+    if (*MaybeName != *SectionName)
+      continue;
+
+    if (Expected<StringRef> Contents = Section.getContents())
+      return *Contents;
+    else
+      return Contents.takeError();
+  }
+  return Optional<StringRef>{};
+}
+
+Remark &RemarkLinker::keep(std::unique_ptr<Remark> Remark) {
+  StrTab.internalize(*Remark);
+  auto Inserted = Remarks.insert(std::move(Remark));
+  return **Inserted.first;
+}
+
+void RemarkLinker::setExternalFilePrependPath(StringRef PrependPathIn) {
+  PrependPath = PrependPathIn;
+}
+
+// Discard remarks with no source location.
+static bool shouldKeepRemark(const Remark &R) { return R.Loc.hasValue(); }
+
+Error RemarkLinker::link(StringRef Buffer, Optional<Format> RemarkFormat) {
+  if (!RemarkFormat) {
+    Expected<Format> ParserFormat = magicToFormat(Buffer);
+    if (!ParserFormat)
+      return ParserFormat.takeError();
+    RemarkFormat = *ParserFormat;
+  }
+
+  Expected<std::unique_ptr<RemarkParser>> MaybeParser =
+      createRemarkParserFromMeta(
+          *RemarkFormat, Buffer, /*StrTab=*/None,
+          PrependPath ? Optional<StringRef>(StringRef(*PrependPath))
+                      : Optional<StringRef>(None));
+  if (!MaybeParser)
+    return MaybeParser.takeError();
+
+  RemarkParser &Parser = **MaybeParser;
+
+  while (true) {
+    Expected<std::unique_ptr<Remark>> Next = Parser.next();
+    if (Error E = Next.takeError()) {
+      if (E.isA<EndOfFileError>()) {
+        consumeError(std::move(E));
+        break;
+      }
+      return E;
+    }
+
+    assert(*Next != nullptr);
+
+    if (shouldKeepRemark(**Next))
+      keep(std::move(*Next));
+  }
+  return Error::success();
+}
+
+Error RemarkLinker::link(const object::ObjectFile &Obj,
+                         Optional<Format> RemarkFormat) {
+  Expected<Optional<StringRef>> SectionOrErr = getRemarksSectionContents(Obj);
+  if (!SectionOrErr)
+    return SectionOrErr.takeError();
+
+  if (Optional<StringRef> Section = *SectionOrErr)
+    return link(*Section, RemarkFormat);
+  return Error::success();
+}
+
+Error RemarkLinker::serialize(raw_ostream &OS, Format RemarksFormat) const {
+  Expected<std::unique_ptr<RemarkSerializer>> MaybeSerializer =
+      createRemarkSerializer(RemarksFormat, SerializerMode::Standalone, OS,
+                             std::move(const_cast<StringTable &>(StrTab)));
+  if (!MaybeSerializer)
+    return MaybeSerializer.takeError();
+
+  std::unique_ptr<remarks::RemarkSerializer> Serializer =
+      std::move(*MaybeSerializer);
+
+  for (const Remark &R : remarks())
+    Serializer->emit(R);
+  return Error::success();
+}

diff  --git a/llvm/unittests/Remarks/CMakeLists.txt b/llvm/unittests/Remarks/CMakeLists.txt
index 8e0fbab2c6eb..35d8290860fe 100644
--- a/llvm/unittests/Remarks/CMakeLists.txt
+++ b/llvm/unittests/Remarks/CMakeLists.txt
@@ -9,6 +9,7 @@ add_llvm_unittest(RemarksTests
   BitstreamRemarksParsingTest.cpp
   BitstreamRemarksSerializerTest.cpp
   RemarksAPITest.cpp
+  RemarksLinkingTest.cpp
   RemarksStrTabParsingTest.cpp
   YAMLRemarksParsingTest.cpp
   YAMLRemarksSerializerTest.cpp

diff  --git a/llvm/unittests/Remarks/RemarksLinkingTest.cpp b/llvm/unittests/Remarks/RemarksLinkingTest.cpp
new file mode 100644
index 000000000000..8d38c6b1cdbd
--- /dev/null
+++ b/llvm/unittests/Remarks/RemarksLinkingTest.cpp
@@ -0,0 +1,217 @@
+//===- unittest/Support/RemarksLinkingTest.cpp - Linking tests ------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#include "llvm/Bitcode/BitcodeAnalyzer.h"
+#include "llvm/Remarks/RemarkLinker.h"
+#include "llvm/Remarks/RemarkSerializer.h"
+#include "llvm/Support/raw_ostream.h"
+#include "gtest/gtest.h"
+#include <string>
+
+using namespace llvm;
+
+static void serializeAndCheck(remarks::RemarkLinker &RL,
+                              remarks::Format OutputFormat,
+                              StringRef ExpectedOutput) {
+  // 1. Create a serializer.
+  // 2. Serialize all the remarks from the linker.
+  // 3. Check that it matches the output.
+  std::string Buf;
+  raw_string_ostream OS(Buf);
+  Error E = RL.serialize(OS, OutputFormat);
+  EXPECT_FALSE(static_cast<bool>(E));
+
+  // For bitstream, run it through the analyzer.
+  if (OutputFormat == remarks::Format::Bitstream) {
+    std::string AnalyzeBuf;
+    raw_string_ostream AnalyzeOS(AnalyzeBuf);
+    BCDumpOptions O(AnalyzeOS);
+    O.ShowBinaryBlobs = true;
+    BitcodeAnalyzer BA(OS.str());
+    EXPECT_FALSE(BA.analyze(O)); // Expect no errors.
+    EXPECT_EQ(AnalyzeOS.str(), ExpectedOutput);
+  } else {
+    EXPECT_EQ(OS.str(), ExpectedOutput);
+  }
+}
+
+static void check(remarks::Format InputFormat, StringRef Input,
+                  remarks::Format OutputFormat, StringRef ExpectedOutput) {
+  remarks::RemarkLinker RL;
+  EXPECT_FALSE(RL.link(Input, InputFormat));
+  serializeAndCheck(RL, OutputFormat, ExpectedOutput);
+}
+
+static void check(remarks::Format InputFormat, StringRef Input,
+                  remarks::Format InputFormat2, StringRef Input2,
+                  remarks::Format OutputFormat, StringRef ExpectedOutput) {
+  remarks::RemarkLinker RL;
+  EXPECT_FALSE(RL.link(Input, InputFormat));
+  EXPECT_FALSE(RL.link(Input2, InputFormat2));
+  serializeAndCheck(RL, OutputFormat, ExpectedOutput);
+}
+
+TEST(Remarks, LinkingGoodYAML) {
+  // One YAML remark.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n");
+
+  // Check that we don't keep remarks without debug locations.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::YAML, "");
+
+  // Check that we deduplicate remarks.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n"
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n");
+}
+
+TEST(Remarks, LinkingGoodBitstream) {
+  // One YAML remark.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::Bitstream,
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
+        "</Meta>\n"
+        "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
+        "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
+        "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
+        "</Remark>\n");
+
+  // Check that we deduplicate remarks.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n"
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::Bitstream,
+        "<BLOCKINFO_BLOCK/>\n"
+        "<Meta BlockID=8 NumWords=12 BlockCodeSize=3>\n"
+        "  <Container info codeid=1 abbrevid=4 op0=0 op1=2/>\n"
+        "  <Remark version codeid=2 abbrevid=5 op0=0/>\n"
+        "  <String table codeid=3 abbrevid=6/> blob data = "
+        "'inline\\x00NoDefinition\\x00foo\\x00file.c\\x00'\n"
+        "</Meta>\n"
+        "<Remark BlockID=9 NumWords=4 BlockCodeSize=4>\n"
+        "  <Remark header codeid=5 abbrevid=4 op0=2 op1=1 op2=0 op3=2/>\n"
+        "  <Remark debug location codeid=6 abbrevid=5 op0=3 op1=3 op2=12/>\n"
+        "</Remark>\n");
+}
+
+TEST(Remarks, LinkingGoodStrTab) {
+  // Check that remarks from 
diff erent entries use the same strtab.
+  check(remarks::Format::YAML,
+        "--- !Missed\n"
+        "Pass:            inline\n"
+        "Name:            NoDefinition\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::YAML,
+        "--- !Passed\n"
+        "Pass:            inline\n"
+        "Name:            Ok\n"
+        "DebugLoc:        { File: file.c, Line: 3, Column: 12 }\n"
+        "Function:        foo\n"
+        "...\n",
+        remarks::Format::YAMLStrTab,
+        StringRef("REMARKS\0\0\0\0\0\0\0\0\0\x22\0\0\0\0\0\0\0"
+                  "inline\0NoDefinition\0foo\0file.c\0Ok\0"
+                  "--- !Passed\n"
+                  "Pass:            0\n"
+                  "Name:            4\n"
+                  "DebugLoc:        { File: 3, Line: 3, Column: 12 }\n"
+                  "Function:        2\n"
+                  "...\n"
+                  "--- !Missed\n"
+                  "Pass:            0\n"
+                  "Name:            1\n"
+                  "DebugLoc:        { File: 3, Line: 3, Column: 12 }\n"
+                  "Function:        2\n"
+                  "...\n",
+                  304));
+}
+
+// Check that we propagate parsing errors.
+TEST(Remarks, LinkingError) {
+  remarks::RemarkLinker RL;
+  {
+    Error E = RL.link("badyaml", remarks::Format::YAML);
+    EXPECT_TRUE(static_cast<bool>(E));
+    EXPECT_EQ(toString(std::move(E)),
+              "YAML:1:1: error: document root is not of mapping type.\n"
+              "\n"
+              "badyaml\n"
+              "^~~~~~~\n"
+              "\n");
+  }
+
+  {
+    // Check that the prepend path is propagated and fails with the full path.
+    RL.setExternalFilePrependPath("/baddir/");
+    Error E = RL.link(
+        StringRef("REMARKS\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0badfile.opt.yaml",
+                  40),
+        remarks::Format::YAMLStrTab);
+    EXPECT_TRUE(static_cast<bool>(E));
+    EXPECT_EQ(toString(std::move(E)),
+              "'/baddir/badfile.opt.yaml': No such file or directory");
+  }
+}


        


More information about the llvm-commits mailing list