[llvm] 30d8e08 - [RemarkUtil] Refactor remarkutil tool to use a command registry.

Zain Jaffal via llvm-commits llvm-commits at lists.llvm.org
Fri Jul 28 01:21:47 PDT 2023


Author: Zain Jaffal
Date: 2023-07-28T09:21:29+01:00
New Revision: 30d8e0837d68a11430cc4c0f24cd743e14219a7d

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

LOG: [RemarkUtil] Refactor remarkutil tool to use a command registry.

In preperation to move all remark utilities into one tool. We use
command registry to breakdown each utility into a separate file.

For now we have 3 utilities for remarks
1. Convert: which is responsible for converting yaml remarks to
   bitstream and vice-versa
2. Count: Analyse remarks and report count. This currently only supports
   asm-remarks and annotation-summary remarks.
3. Diff remarks: Currently we only have a diff for size remarks using
   `llvm-remark-size-diff`

The first two utilites have been simplified and seperated into two
files. The following commit will move `llvm-remark-size-diff` and fold
it to be inside `llvm-remarkutil` as a subcommand

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

Added: 
    llvm/tools/llvm-remarkutil/RemarkConvert.cpp
    llvm/tools/llvm-remarkutil/RemarkCount.cpp
    llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp
    llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
    llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp
    llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h

Modified: 
    llvm/tools/llvm-remarkutil/CMakeLists.txt
    llvm/tools/llvm-remarkutil/RemarkUtil.cpp

Removed: 
    


################################################################################
diff  --git a/llvm/tools/llvm-remarkutil/CMakeLists.txt b/llvm/tools/llvm-remarkutil/CMakeLists.txt
index bf044d248fb785..85c920dfd3b838 100644
--- a/llvm/tools/llvm-remarkutil/CMakeLists.txt
+++ b/llvm/tools/llvm-remarkutil/CMakeLists.txt
@@ -5,5 +5,9 @@ set(LLVM_LINK_COMPONENTS
   )
 
 add_llvm_tool(llvm-remarkutil
+  RemarkConvert.cpp
+  RemarkCount.cpp
   RemarkUtil.cpp
+  RemarkUtilHelpers.cpp
+  RemarkUtilRegistry.cpp
   )

diff  --git a/llvm/tools/llvm-remarkutil/RemarkConvert.cpp b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp
new file mode 100644
index 00000000000000..35d8dcd99b4a9c
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkConvert.cpp
@@ -0,0 +1,143 @@
+//===- RemarkConvert.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Convert remarks from bitstream to yaml and the other way around.
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemarkUtilHelpers.h"
+#include "RemarkUtilRegistry.h"
+
+using namespace llvm;
+using namespace remarks;
+using namespace llvm::remarkutil;
+
+extern ExitOnError ExitOnErr;
+static cl::SubCommand
+    YAML2Bitstream("yaml2bitstream",
+                   "Convert YAML remarks to bitstream remarks");
+static cl::SubCommand
+    Bitstream2YAML("bitstream2yaml",
+                   "Convert bitstream remarks to YAML remarks");
+
+namespace yaml2bitstream {
+/// Remark format to parse.
+static constexpr Format InputFormat = Format::YAML;
+/// Remark format to output.
+static constexpr Format OutputFormat = Format::Bitstream;
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(YAML2Bitstream)
+} // namespace yaml2bitstream
+
+namespace bitstream2yaml {
+/// Remark format to parse.
+static constexpr Format InputFormat = Format::Bitstream;
+/// Remark format to output.
+static constexpr Format OutputFormat = Format::YAML;
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(Bitstream2YAML)
+} // namespace bitstream2yaml
+
+namespace yaml2bitstream {
+/// Parses all remarks in the input YAML file.
+/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file.
+/// \p [out] StrTab - A string table populated for later remark serialization.
+/// \returns Error::success() if all remarks were successfully parsed, and an
+/// Error otherwise.
+static Error
+tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
+                            StringTable &StrTab) {
+  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+  if (!MaybeBuf)
+    return MaybeBuf.takeError();
+  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
+  if (!MaybeParser)
+    return MaybeParser.takeError();
+  auto &Parser = **MaybeParser;
+  auto MaybeRemark = Parser.next();
+  for (; MaybeRemark; MaybeRemark = Parser.next()) {
+    StrTab.internalize(**MaybeRemark);
+    ParsedRemarks.push_back(std::move(*MaybeRemark));
+  }
+  auto E = MaybeRemark.takeError();
+  if (!E.isA<EndOfFileError>())
+    return E;
+  consumeError(std::move(E));
+  return Error::success();
+}
+
+/// Reserialize a list of parsed YAML remarks into bitstream remarks.
+/// \p ParsedRemarks - A list of remarks.
+/// \p StrTab - The string table for the remarks.
+/// \returns Error::success() on success.
+static Error tryReserializeYAML2Bitstream(
+    const std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
+    StringTable &StrTab) {
+  auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
+  if (!MaybeOF)
+    return MaybeOF.takeError();
+  auto OF = std::move(*MaybeOF);
+  auto MaybeSerializer = createRemarkSerializer(
+      OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab));
+  if (!MaybeSerializer)
+    return MaybeSerializer.takeError();
+  auto Serializer = std::move(*MaybeSerializer);
+  for (const auto &Remark : ParsedRemarks)
+    Serializer->emit(*Remark);
+  OF->keep();
+  return Error::success();
+}
+
+/// Parse YAML remarks and reserialize as bitstream remarks.
+/// \returns Error::success() on success, and an Error otherwise.
+static Error tryYAML2Bitstream() {
+  StringTable StrTab;
+  std::vector<std::unique_ptr<Remark>> ParsedRemarks;
+  ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab));
+  return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab);
+}
+} // namespace yaml2bitstream
+
+namespace bitstream2yaml {
+/// Parse bitstream remarks and reserialize as YAML remarks.
+/// \returns An Error if reserialization fails, or Error::success() on success.
+static Error tryBitstream2YAML() {
+  // Create the serializer.
+  auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
+  if (!MaybeOF)
+    return MaybeOF.takeError();
+  auto OF = std::move(*MaybeOF);
+  auto MaybeSerializer = createRemarkSerializer(
+      OutputFormat, SerializerMode::Standalone, OF->os());
+  if (!MaybeSerializer)
+    return MaybeSerializer.takeError();
+
+  // Create the parser.
+  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+  if (!MaybeBuf)
+    return MaybeBuf.takeError();
+  auto Serializer = std::move(*MaybeSerializer);
+  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
+  if (!MaybeParser)
+    return MaybeParser.takeError();
+  auto &Parser = **MaybeParser;
+
+  // Parse + reserialize all remarks.
+  auto MaybeRemark = Parser.next();
+  for (; MaybeRemark; MaybeRemark = Parser.next())
+    Serializer->emit(**MaybeRemark);
+  auto E = MaybeRemark.takeError();
+  if (!E.isA<EndOfFileError>())
+    return E;
+  consumeError(std::move(E));
+  return Error::success();
+}
+} // namespace bitstream2yaml
+
+static CommandRegistration Bitstream2YamlReg(&Bitstream2YAML,
+                                             bitstream2yaml::tryBitstream2YAML);
+static CommandRegistration Yaml2Bitstream(&YAML2Bitstream,
+                                          yaml2bitstream::tryYAML2Bitstream);

diff  --git a/llvm/tools/llvm-remarkutil/RemarkCount.cpp b/llvm/tools/llvm-remarkutil/RemarkCount.cpp
new file mode 100644
index 00000000000000..d08f47ff352796
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkCount.cpp
@@ -0,0 +1,159 @@
+//===- RemarkCount.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Count remarks using `instruction-count` for asm-printer remarks and
+// `annotation-count` for annotation-remarks
+//
+//===----------------------------------------------------------------------===//
+#include "RemarkUtilHelpers.h"
+#include "RemarkUtilRegistry.h"
+
+using namespace llvm;
+using namespace remarks;
+using namespace llvm::remarkutil;
+
+static cl::SubCommand InstructionCount(
+    "instruction-count",
+    "Function instruction count information (requires asm-printer remarks)");
+static cl::SubCommand
+    AnnotationCount("annotation-count",
+                    "Collect count information from annotation remarks (uses "
+                    "AnnotationRemarksPass)");
+
+namespace instructioncount {
+INPUT_FORMAT_COMMAND_LINE_OPTIONS(InstructionCount)
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(InstructionCount)
+DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(InstructionCount)
+} // namespace instructioncount
+
+namespace annotationcount {
+INPUT_FORMAT_COMMAND_LINE_OPTIONS(AnnotationCount)
+static cl::opt<std::string> AnnotationTypeToCollect(
+    "annotation-type", cl::desc("annotation-type remark to collect count for"),
+    cl::sub(AnnotationCount));
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(AnnotationCount)
+DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(AnnotationCount)
+} // namespace annotationcount
+
+static bool shouldSkipRemark(bool UseDebugLoc, Remark &Remark) {
+  return UseDebugLoc && !Remark.Loc.has_value();
+}
+
+namespace instructioncount {
+/// Outputs all instruction count remarks in the file as a CSV.
+/// \returns Error::success() on success, and an Error otherwise.
+static Error tryInstructionCount() {
+  // Create the output buffer.
+  auto MaybeOF = getOutputFileWithFlags(OutputFileName,
+                                        /*Flags = */ sys::fs::OF_TextWithCRLF);
+  if (!MaybeOF)
+    return MaybeOF.takeError();
+  auto OF = std::move(*MaybeOF);
+  // Create a parser for the user-specified input format.
+  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+  if (!MaybeBuf)
+    return MaybeBuf.takeError();
+  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
+  if (!MaybeParser)
+    return MaybeParser.takeError();
+  // Emit CSV header.
+  if (UseDebugLoc)
+    OF->os() << "Source,";
+  OF->os() << "Function,InstructionCount\n";
+  // Parse all remarks. Whenever we see an instruction count remark, output
+  // the file name and the number of instructions.
+  auto &Parser = **MaybeParser;
+  auto MaybeRemark = Parser.next();
+  for (; MaybeRemark; MaybeRemark = Parser.next()) {
+    auto &Remark = **MaybeRemark;
+    if (Remark.RemarkName != "InstructionCount")
+      continue;
+    if (shouldSkipRemark(UseDebugLoc, Remark))
+      continue;
+    auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) {
+      return Arg.Key == "NumInstructions";
+    });
+    assert(InstrCountArg != Remark.Args.end() &&
+           "Expected instruction count remarks to have a NumInstructions key?");
+    if (UseDebugLoc) {
+      std::string Loc = Remark.Loc->SourceFilePath.str() + ":" +
+                        std::to_string(Remark.Loc->SourceLine) + +":" +
+                        std::to_string(Remark.Loc->SourceColumn);
+      OF->os() << Loc << ",";
+    }
+    OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n";
+  }
+  auto E = MaybeRemark.takeError();
+  if (!E.isA<EndOfFileError>())
+    return E;
+  consumeError(std::move(E));
+  OF->keep();
+  return Error::success();
+}
+} // namespace instructioncount
+
+namespace annotationcount {
+static Error tryAnnotationCount() {
+  // Create the output buffer.
+  auto MaybeOF = getOutputFileWithFlags(OutputFileName,
+                                        /*Flags = */ sys::fs::OF_TextWithCRLF);
+  if (!MaybeOF)
+    return MaybeOF.takeError();
+  auto OF = std::move(*MaybeOF);
+  // Create a parser for the user-specified input format.
+  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+  if (!MaybeBuf)
+    return MaybeBuf.takeError();
+  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
+  if (!MaybeParser)
+    return MaybeParser.takeError();
+  // Emit CSV header.
+  if (UseDebugLoc)
+    OF->os() << "Source,";
+  OF->os() << "Function,Count\n";
+  // Parse all remarks. When we see the specified remark collect the count
+  // information.
+  auto &Parser = **MaybeParser;
+  auto MaybeRemark = Parser.next();
+  for (; MaybeRemark; MaybeRemark = Parser.next()) {
+    auto &Remark = **MaybeRemark;
+    if (Remark.RemarkName != "AnnotationSummary")
+      continue;
+    if (shouldSkipRemark(UseDebugLoc, Remark))
+      continue;
+    auto *RemarkNameArg = find_if(Remark.Args, [](const Argument &Arg) {
+      return Arg.Key == "type" && Arg.Val == AnnotationTypeToCollect;
+    });
+    if (RemarkNameArg == Remark.Args.end())
+      continue;
+    auto *CountArg = find_if(
+        Remark.Args, [](const Argument &Arg) { return Arg.Key == "count"; });
+    assert(CountArg != Remark.Args.end() &&
+           "Expected annotation-type remark to have a count key?");
+    if (UseDebugLoc) {
+      std::string Loc = Remark.Loc->SourceFilePath.str() + ":" +
+                        std::to_string(Remark.Loc->SourceLine) + +":" +
+                        std::to_string(Remark.Loc->SourceColumn);
+      OF->os() << Loc << ",";
+    }
+    OF->os() << Remark.FunctionName << "," << CountArg->Val << "\n";
+  }
+  auto E = MaybeRemark.takeError();
+  if (!E.isA<EndOfFileError>())
+    return E;
+  consumeError(std::move(E));
+  OF->keep();
+  return Error::success();
+}
+} // namespace annotationcount
+
+static CommandRegistration
+    InstructionCountReg(&InstructionCount,
+                        instructioncount::tryInstructionCount);
+static CommandRegistration Yaml2Bitstream(&AnnotationCount,
+                                          annotationcount::tryAnnotationCount);

diff  --git a/llvm/tools/llvm-remarkutil/RemarkUtil.cpp b/llvm/tools/llvm-remarkutil/RemarkUtil.cpp
index 14af5d2842cf80..8ab3fbba4311d1 100644
--- a/llvm/tools/llvm-remarkutil/RemarkUtil.cpp
+++ b/llvm/tools/llvm-remarkutil/RemarkUtil.cpp
@@ -1,4 +1,4 @@
-//===--------- llvm-remarkutil/RemarkUtil.cpp -----------===//
+//===--------- llvm-remarkutil/RemarkUtil.cpp -----------------------------===//
 //
 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
 // See https://llvm.org/LICENSE.txt for license information.
@@ -8,370 +8,34 @@
 /// Utility for remark files.
 //===----------------------------------------------------------------------===//
 
-#include "llvm-c/Remarks.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/Remarks/Remark.h"
-#include "llvm/Remarks/RemarkFormat.h"
-#include "llvm/Remarks/RemarkParser.h"
-#include "llvm/Remarks/YAMLRemarkSerializer.h"
-#include "llvm/Support/CommandLine.h"
-#include "llvm/Support/Compiler.h"
-#include "llvm/Support/Error.h"
-#include "llvm/Support/FileSystem.h"
+#include "RemarkUtilRegistry.h"
 #include "llvm/Support/InitLLVM.h"
-#include "llvm/Support/MemoryBuffer.h"
-#include "llvm/Support/ToolOutputFile.h"
-#include "llvm/Support/WithColor.h"
 
 using namespace llvm;
-using namespace remarks;
-
-static ExitOnError ExitOnErr;
-static cl::OptionCategory RemarkUtilCategory("llvm-remarkutil options");
-namespace subopts {
-static cl::SubCommand
-    YAML2Bitstream("yaml2bitstream",
-                   "Convert YAML remarks to bitstream remarks");
-static cl::SubCommand
-    Bitstream2YAML("bitstream2yaml",
-                   "Convert bitstream remarks to YAML remarks");
-static cl::SubCommand InstructionCount(
-    "instruction-count",
-    "Function instruction count information (requires asm-printer remarks)");
-static cl::SubCommand
-    AnnotationCount("annotation-count",
-                    "Collect count information from annotation remarks (uses "
-                    "AnnotationRemarksPass)");
-} // namespace subopts
-
-// Keep input + output help + names consistent across the various modes via a
-// hideous macro.
-#define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT)                              \
-  static cl::opt<std::string> InputFileName(                                   \
-      cl::Positional, cl::cat(RemarkUtilCategory), cl::init("-"),              \
-      cl::desc("<input file>"), cl::sub(SUBOPT));                              \
-  static cl::opt<std::string> OutputFileName(                                  \
-      "o", cl::init("-"), cl::cat(RemarkUtilCategory), cl::desc("Output"),     \
-      cl::value_desc("filename"), cl::sub(SUBOPT));
-
-// Keep Input format and names consistent accross the modes via a macro.
-#define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT)                              \
-  static cl::opt<Format> InputFormat(                                          \
-      "parser", cl::desc("Input remark format to parse"),                      \
-      cl::values(clEnumValN(Format::YAML, "yaml", "YAML"),                     \
-                 clEnumValN(Format::Bitstream, "bitstream", "Bitstream")),     \
-      cl::sub(SUBOPT));
-
-#define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT)                            \
-  static cl::opt<bool> UseDebugLoc(                                            \
-      "use-debug-loc",                                                         \
-      cl::desc(                                                                \
-          "Add debug loc information when generating tables for "              \
-          "functions. The loc is represented as (path:line number:column "     \
-          "number)"),                                                          \
-      cl::init(false), cl::sub(SUBOPT));
-namespace yaml2bitstream {
-/// Remark format to parse.
-static constexpr Format InputFormat = Format::YAML;
-/// Remark format to output.
-static constexpr Format OutputFormat = Format::Bitstream;
-INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::YAML2Bitstream)
-} // namespace yaml2bitstream
-
-namespace bitstream2yaml {
-/// Remark format to parse.
-static constexpr Format InputFormat = Format::Bitstream;
-/// Remark format to output.
-static constexpr Format OutputFormat = Format::YAML;
-INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::Bitstream2YAML)
-} // namespace bitstream2yaml
-
-namespace instructioncount {
-INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::InstructionCount)
-INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::InstructionCount)
-DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::InstructionCount)
-} // namespace instructioncount
-
-namespace annotationcount {
-INPUT_FORMAT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount)
-static cl::opt<std::string> AnnotationTypeToCollect(
-    "annotation-type", cl::desc("annotation-type remark to collect count for"),
-    cl::sub(subopts::AnnotationCount));
-INPUT_OUTPUT_COMMAND_LINE_OPTIONS(subopts::AnnotationCount)
-DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(subopts::AnnotationCount)
-} // namespace annotationcount
-
-/// \returns A MemoryBuffer for the input file on success, and an Error
-/// otherwise.
-static Expected<std::unique_ptr<MemoryBuffer>>
-getInputMemoryBuffer(StringRef InputFileName) {
-  auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName);
-  if (auto ErrorCode = MaybeBuf.getError())
-    return createStringError(ErrorCode,
-                             Twine("Cannot open file '" + InputFileName +
-                                   "': " + ErrorCode.message()));
-  return std::move(*MaybeBuf);
-}
-
-/// \returns A ToolOutputFile which can be used for outputting the results of
-/// some tool mode.
-/// \p OutputFileName is the desired destination.
-/// \p Flags controls whether or not the file is opened for writing in text
-/// mode, as a binary, etc. See sys::fs::OpenFlags for more detail.
-static Expected<std::unique_ptr<ToolOutputFile>>
-getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) {
-  if (OutputFileName == "")
-    OutputFileName = "-";
-  std::error_code ErrorCode;
-  auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags);
-  if (ErrorCode)
-    return errorCodeToError(ErrorCode);
-  return std::move(OF);
-}
-
-/// \returns A ToolOutputFile which can be used for writing remarks on success,
-/// and an Error otherwise.
-/// \p OutputFileName is the desired destination.
-/// \p OutputFormat
-static Expected<std::unique_ptr<ToolOutputFile>>
-getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) {
-  assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) &&
-         "Expected one of YAML or Bitstream!");
-  return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML
-                                                    ? sys::fs::OF_TextWithCRLF
-                                                    : sys::fs::OF_None);
-}
-
-static bool shouldSkipRemark(bool UseDebugLoc, Remark &Remark) {
-  return UseDebugLoc && !Remark.Loc.has_value();
-}
-
-namespace yaml2bitstream {
-/// Parses all remarks in the input YAML file.
-/// \p [out] ParsedRemarks - Filled with remarks parsed from the input file.
-/// \p [out] StrTab - A string table populated for later remark serialization.
-/// \returns Error::success() if all remarks were successfully parsed, and an
-/// Error otherwise.
-static Error
-tryParseRemarksFromYAMLFile(std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
-                            StringTable &StrTab) {
-  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
-  if (!MaybeBuf)
-    return MaybeBuf.takeError();
-  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
-  if (!MaybeParser)
-    return MaybeParser.takeError();
-  auto &Parser = **MaybeParser;
-  auto MaybeRemark = Parser.next();
-  for (; MaybeRemark; MaybeRemark = Parser.next()) {
-    StrTab.internalize(**MaybeRemark);
-    ParsedRemarks.push_back(std::move(*MaybeRemark));
-  }
-  auto E = MaybeRemark.takeError();
-  if (!E.isA<EndOfFileError>())
-    return E;
-  consumeError(std::move(E));
-  return Error::success();
-}
-
-/// Reserialize a list of parsed YAML remarks into bitstream remarks.
-/// \p ParsedRemarks - A list of remarks.
-/// \p StrTab - The string table for the remarks.
-/// \returns Error::success() on success.
-static Error tryReserializeYAML2Bitstream(
-    const std::vector<std::unique_ptr<Remark>> &ParsedRemarks,
-    StringTable &StrTab) {
-  auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
-  if (!MaybeOF)
-    return MaybeOF.takeError();
-  auto OF = std::move(*MaybeOF);
-  auto MaybeSerializer = createRemarkSerializer(
-      OutputFormat, SerializerMode::Standalone, OF->os(), std::move(StrTab));
-  if (!MaybeSerializer)
-    return MaybeSerializer.takeError();
-  auto Serializer = std::move(*MaybeSerializer);
-  for (const auto &Remark : ParsedRemarks)
-    Serializer->emit(*Remark);
-  OF->keep();
-  return Error::success();
-}
-
-/// Parse YAML remarks and reserialize as bitstream remarks.
-/// \returns Error::success() on success, and an Error otherwise.
-static Error tryYAML2Bitstream() {
-  StringTable StrTab;
-  std::vector<std::unique_ptr<Remark>> ParsedRemarks;
-  ExitOnErr(tryParseRemarksFromYAMLFile(ParsedRemarks, StrTab));
-  return tryReserializeYAML2Bitstream(ParsedRemarks, StrTab);
-}
-} // namespace yaml2bitstream
-
-namespace bitstream2yaml {
-/// Parse bitstream remarks and reserialize as YAML remarks.
-/// \returns An Error if reserialization fails, or Error::success() on success.
-static Error tryBitstream2YAML() {
-  // Create the serializer.
-  auto MaybeOF = getOutputFileForRemarks(OutputFileName, OutputFormat);
-  if (!MaybeOF)
-    return MaybeOF.takeError();
-  auto OF = std::move(*MaybeOF);
-  auto MaybeSerializer = createRemarkSerializer(
-      OutputFormat, SerializerMode::Standalone, OF->os());
-  if (!MaybeSerializer)
-    return MaybeSerializer.takeError();
-
-  // Create the parser.
-  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
-  if (!MaybeBuf)
-    return MaybeBuf.takeError();
-  auto Serializer = std::move(*MaybeSerializer);
-  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
-  if (!MaybeParser)
-    return MaybeParser.takeError();
-  auto &Parser = **MaybeParser;
-
-  // Parse + reserialize all remarks.
-  auto MaybeRemark = Parser.next();
-  for (; MaybeRemark; MaybeRemark = Parser.next())
-    Serializer->emit(**MaybeRemark);
-  auto E = MaybeRemark.takeError();
-  if (!E.isA<EndOfFileError>())
-    return E;
-  consumeError(std::move(E));
-  return Error::success();
-}
-} // namespace bitstream2yaml
-
-namespace instructioncount {
-/// Outputs all instruction count remarks in the file as a CSV.
-/// \returns Error::success() on success, and an Error otherwise.
-static Error tryInstructionCount() {
-  // Create the output buffer.
-  auto MaybeOF = getOutputFileWithFlags(OutputFileName,
-                                        /*Flags = */ sys::fs::OF_TextWithCRLF);
-  if (!MaybeOF)
-    return MaybeOF.takeError();
-  auto OF = std::move(*MaybeOF);
-  // Create a parser for the user-specified input format.
-  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
-  if (!MaybeBuf)
-    return MaybeBuf.takeError();
-  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
-  if (!MaybeParser)
-    return MaybeParser.takeError();
-  // Emit CSV header.
-  if (UseDebugLoc)
-    OF->os() << "Source,";
-  OF->os() << "Function,InstructionCount\n";
-  // Parse all remarks. Whenever we see an instruction count remark, output
-  // the file name and the number of instructions.
-  auto &Parser = **MaybeParser;
-  auto MaybeRemark = Parser.next();
-  for (; MaybeRemark; MaybeRemark = Parser.next()) {
-    auto &Remark = **MaybeRemark;
-    if (Remark.RemarkName != "InstructionCount")
-      continue;
-    if (shouldSkipRemark(UseDebugLoc, Remark))
-      continue;
-    auto *InstrCountArg = find_if(Remark.Args, [](const Argument &Arg) {
-      return Arg.Key == "NumInstructions";
-    });
-    assert(InstrCountArg != Remark.Args.end() &&
-           "Expected instruction count remarks to have a NumInstructions key?");
-    if (UseDebugLoc) {
-      std::string Loc = Remark.Loc->SourceFilePath.str() + ":" +
-                        std::to_string(Remark.Loc->SourceLine) + +":" +
-                        std::to_string(Remark.Loc->SourceColumn);
-      OF->os() << Loc << ",";
-    }
-    OF->os() << Remark.FunctionName << "," << InstrCountArg->Val << "\n";
-  }
-  auto E = MaybeRemark.takeError();
-  if (!E.isA<EndOfFileError>())
-    return E;
-  consumeError(std::move(E));
-  OF->keep();
-  return Error::success();
-}
-} // namespace instructioncount
-
-namespace annotationcount {
-static Error tryAnnotationCount() {
-  // Create the output buffer.
-  auto MaybeOF = getOutputFileWithFlags(OutputFileName,
-                                        /*Flags = */ sys::fs::OF_TextWithCRLF);
-  if (!MaybeOF)
-    return MaybeOF.takeError();
-  auto OF = std::move(*MaybeOF);
-  // Create a parser for the user-specified input format.
-  auto MaybeBuf = getInputMemoryBuffer(InputFileName);
-  if (!MaybeBuf)
-    return MaybeBuf.takeError();
-  auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
-  if (!MaybeParser)
-    return MaybeParser.takeError();
-  // Emit CSV header.
-  if (UseDebugLoc)
-    OF->os() << "Source,";
-  OF->os() << "Function,Count\n";
-  // Parse all remarks. When we see the specified remark collect the count
-  // information.
-  auto &Parser = **MaybeParser;
-  auto MaybeRemark = Parser.next();
-  for (; MaybeRemark; MaybeRemark = Parser.next()) {
-    auto &Remark = **MaybeRemark;
-    if (Remark.RemarkName != "AnnotationSummary")
-      continue;
-    if (shouldSkipRemark(UseDebugLoc, Remark))
-      continue;
-    auto *RemarkNameArg = find_if(Remark.Args, [](const Argument &Arg) {
-      return Arg.Key == "type" && Arg.Val == AnnotationTypeToCollect;
-    });
-    if (RemarkNameArg == Remark.Args.end())
-      continue;
-    auto *CountArg = find_if(
-        Remark.Args, [](const Argument &Arg) { return Arg.Key == "count"; });
-    assert(CountArg != Remark.Args.end() &&
-           "Expected annotation-type remark to have a count key?");
-    if (UseDebugLoc) {
-      std::string Loc = Remark.Loc->SourceFilePath.str() + ":" +
-                        std::to_string(Remark.Loc->SourceLine) + +":" +
-                        std::to_string(Remark.Loc->SourceColumn);
-      OF->os() << Loc << ",";
+using namespace llvm::remarkutil;
+ExitOnError ExitOnErr;
+
+static Error handleSubOptions() {
+  for (auto *SC : cl::getRegisteredSubcommands()) {
+    if (*SC) {
+      // If no subcommand was provided, we need to explicitly check if this is
+      // the top-level subcommand.
+      if (SC == &cl::SubCommand::getTopLevel())
+        break;
+      if (auto C = dispatch(SC)) {
+        return C();
+      }
     }
-    OF->os() << Remark.FunctionName << "," << CountArg->Val << "\n";
   }
-  auto E = MaybeRemark.takeError();
-  if (!E.isA<EndOfFileError>())
-    return E;
-  consumeError(std::move(E));
-  OF->keep();
-  return Error::success();
-}
-
-} // namespace annotationcount
-/// Handle user-specified suboptions (e.g. yaml2bitstream, bitstream2yaml).
-/// \returns An Error if the specified suboption fails or if no suboption was
-/// specified. Otherwise, Error::success().
-static Error handleSuboptions() {
-  if (subopts::Bitstream2YAML)
-    return bitstream2yaml::tryBitstream2YAML();
-  if (subopts::YAML2Bitstream)
-    return yaml2bitstream::tryYAML2Bitstream();
-  if (subopts::InstructionCount)
-    return instructioncount::tryInstructionCount();
-  if (subopts::AnnotationCount)
-    return annotationcount::tryAnnotationCount();
 
   return make_error<StringError>(
       "Please specify a subcommand. (See -help for options)",
       inconvertibleErrorCode());
 }
 
-int main(int argc, const char **argv) {
+int main(int argc, char *argv[]) {
   InitLLVM X(argc, argv);
-  cl::HideUnrelatedOptions(RemarkUtilCategory);
   cl::ParseCommandLineOptions(argc, argv, "Remark file utilities\n");
   ExitOnErr.setBanner(std::string(argv[0]) + ": error: ");
-  ExitOnErr(handleSuboptions());
+  ExitOnErr(handleSubOptions());
 }

diff  --git a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp
new file mode 100644
index 00000000000000..c0357161c4f984
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.cpp
@@ -0,0 +1,57 @@
+//===- RemarkUtilHelpers.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers for remark utilites
+//
+//===----------------------------------------------------------------------===//
+#include "RemarkUtilHelpers.h"
+
+namespace llvm {
+namespace remarks {
+/// \returns A MemoryBuffer for the input file on success, and an Error
+/// otherwise.
+Expected<std::unique_ptr<MemoryBuffer>>
+getInputMemoryBuffer(StringRef InputFileName) {
+  auto MaybeBuf = MemoryBuffer::getFileOrSTDIN(InputFileName);
+  if (auto ErrorCode = MaybeBuf.getError())
+    return createStringError(ErrorCode,
+                             Twine("Cannot open file '" + InputFileName +
+                                   "': " + ErrorCode.message()));
+  return std::move(*MaybeBuf);
+}
+
+/// \returns A ToolOutputFile which can be used for outputting the results of
+/// some tool mode.
+/// \p OutputFileName is the desired destination.
+/// \p Flags controls whether or not the file is opened for writing in text
+/// mode, as a binary, etc. See sys::fs::OpenFlags for more detail.
+Expected<std::unique_ptr<ToolOutputFile>>
+getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags) {
+  if (OutputFileName == "")
+    OutputFileName = "-";
+  std::error_code ErrorCode;
+  auto OF = std::make_unique<ToolOutputFile>(OutputFileName, ErrorCode, Flags);
+  if (ErrorCode)
+    return errorCodeToError(ErrorCode);
+  return std::move(OF);
+}
+
+/// \returns A ToolOutputFile which can be used for writing remarks on success,
+/// and an Error otherwise.
+/// \p OutputFileName is the desired destination.
+/// \p OutputFormat
+Expected<std::unique_ptr<ToolOutputFile>>
+getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat) {
+  assert((OutputFormat == Format::YAML || OutputFormat == Format::Bitstream) &&
+         "Expected one of YAML or Bitstream!");
+  return getOutputFileWithFlags(OutputFileName, OutputFormat == Format::YAML
+                                                    ? sys::fs::OF_TextWithCRLF
+                                                    : sys::fs::OF_None);
+}
+} // namespace remarks
+} // namespace llvm

diff  --git a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
new file mode 100644
index 00000000000000..5d2335224d4c2f
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
@@ -0,0 +1,59 @@
+//===- RemarkUtilHelpers.h ------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Helpers for remark utilites
+//
+//===----------------------------------------------------------------------===//
+#include "llvm-c/Remarks.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Remarks/Remark.h"
+#include "llvm/Remarks/RemarkFormat.h"
+#include "llvm/Remarks/RemarkParser.h"
+#include "llvm/Remarks/YAMLRemarkSerializer.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/FileSystem.h"
+#include "llvm/Support/MemoryBuffer.h"
+#include "llvm/Support/ToolOutputFile.h"
+
+// Keep input + output help + names consistent across the various modes via a
+// hideous macro.
+#define INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SUBOPT)                              \
+  static cl::opt<std::string> InputFileName(cl::Positional, cl::init("-"),     \
+                                            cl::desc("<input file>"),          \
+                                            cl::sub(SUBOPT));                  \
+  static cl::opt<std::string> OutputFileName(                                  \
+      "o", cl::init("-"), cl::desc("Output"), cl::value_desc("filename"),      \
+      cl::sub(SUBOPT));
+
+// Keep Input format and names consistent accross the modes via a macro.
+#define INPUT_FORMAT_COMMAND_LINE_OPTIONS(SUBOPT)                              \
+  static cl::opt<Format> InputFormat(                                          \
+      "parser", cl::desc("Input remark format to parse"),                      \
+      cl::values(clEnumValN(Format::YAML, "yaml", "YAML"),                     \
+                 clEnumValN(Format::Bitstream, "bitstream", "Bitstream")),     \
+      cl::sub(SUBOPT));
+
+#define DEBUG_LOC_INFO_COMMAND_LINE_OPTIONS(SUBOPT)                            \
+  static cl::opt<bool> UseDebugLoc(                                            \
+      "use-debug-loc",                                                         \
+      cl::desc(                                                                \
+          "Add debug loc information when generating tables for "              \
+          "functions. The loc is represented as (path:line number:column "     \
+          "number)"),                                                          \
+      cl::init(false), cl::sub(SUBOPT));
+
+namespace llvm {
+namespace remarks {
+Expected<std::unique_ptr<MemoryBuffer>>
+getInputMemoryBuffer(StringRef InputFileName);
+Expected<std::unique_ptr<ToolOutputFile>>
+getOutputFileWithFlags(StringRef OutputFileName, sys::fs::OpenFlags Flags);
+Expected<std::unique_ptr<ToolOutputFile>>
+getOutputFileForRemarks(StringRef OutputFileName, Format OutputFormat);
+} // namespace remarks
+} // namespace llvm

diff  --git a/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp
new file mode 100644
index 00000000000000..244a16d51805b6
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.cpp
@@ -0,0 +1,41 @@
+//===- RemarkUtilRegistry.cpp: Implement a command registry. --------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implement a simple subcommand registry.
+//
+//===----------------------------------------------------------------------===//
+#include "RemarkUtilRegistry.h"
+#include <unordered_map>
+
+namespace llvm {
+namespace remarkutil {
+
+using HandlerType = std::function<Error()>;
+
+static std::unordered_map<cl::SubCommand *, HandlerType> &getCommands() {
+  static std::unordered_map<cl::SubCommand *, HandlerType> Commands;
+  return Commands;
+}
+
+CommandRegistration::CommandRegistration(cl::SubCommand *SC,
+                                         HandlerType Command) {
+  assert(getCommands().count(SC) == 0 &&
+         "Attempting to overwrite a command handler");
+  assert(Command && "Attempting to register an empty std::function<Error()>");
+  getCommands()[SC] = Command;
+}
+
+HandlerType dispatch(cl::SubCommand *SC) {
+  auto It = getCommands().find(SC);
+  assert(It != getCommands().end() &&
+         "Attempting to dispatch on un-registered SubCommand.");
+  return It->second;
+}
+
+} // namespace remarkutil
+} // namespace llvm

diff  --git a/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h
new file mode 100644
index 00000000000000..2dd3a53cb047d9
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkUtilRegistry.h
@@ -0,0 +1,40 @@
+//===- RemarkUtilRegistry.h: Implement a command registry. ----------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+//
+// Implement a simple subcommand registry.
+//
+//===----------------------------------------------------------------------===//
+#ifndef TOOLS_LLVM_REMARKUTIL_REGISTRY_H
+#define TOOLS_LLVM_REMARKUTIL_REGISTRY_H
+
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Error.h"
+
+namespace llvm {
+namespace remarkutil {
+
+// Use |CommandRegistration| as a global initialiser that registers a function
+// and associates it with |SC|. This requires that a command has not been
+// registered to a given |SC|.
+//
+// Usage:
+//
+//   // At namespace scope.
+//   static CommandRegistration Unused(&MySubCommand, [] { ... });
+//
+struct CommandRegistration {
+  CommandRegistration(cl::SubCommand *SC, std::function<Error()> Command);
+};
+
+// Requires that |SC| is not null and has an associated function to it.
+std::function<Error()> dispatch(cl::SubCommand *SC);
+
+} // namespace remarkutil
+} // namespace llvm
+
+#endif // TOOLS_LLVM_REMARKUTIL_REGISTRY_H


        


More information about the llvm-commits mailing list