[llvm] Introduce llvmremark util diff command (PR #85007)

Zain Jaffal via llvm-commits llvm-commits at lists.llvm.org
Sun Apr 28 02:46:57 PDT 2024


================
@@ -0,0 +1,298 @@
+//===- RemarkDiff.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Generic tool to diff remarks based on properties
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemarkUtilHelpers.h"
+#include "RemarkUtilRegistry.h"
+#include "llvm/ADT/MapVector.h"
+#include "llvm/ADT/SetVector.h"
+#include "llvm/ADT/SmallSet.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/JSON.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/ScopedPrinter.h"
+
+namespace llvm {
+namespace remarks {
+
+enum ReportStyleOptions { human_output, json_output };
+/// copy of Argument class using std::string instead of StringRef.
+struct RemarkArgInfo {
+  std::string Key;
+  std::string Val;
+  RemarkArgInfo(StringRef Key, StringRef Val)
+      : Key(Key.str()), Val(Val.str()) {}
+  void print(raw_ostream &OS) const;
+};
+
+hash_code hash_value(const RemarkArgInfo &Arg) {
+  return hash_combine(Arg.Key, Arg.Val);
+}
+
+/// A wrapper for Remark class that can be used for generating remark diff.
+struct RemarkInfo {
+  std::string RemarkName;
+  std::string FunctionName;
+  std::string PassName;
+  Type RemarkType;
+  SmallVector<RemarkArgInfo, 4> Args;
+  RemarkInfo();
+  RemarkInfo(std::string RemarkName, std::string FunctionName,
+             std::string PassName, Type RemarkType,
+             SmallVector<RemarkArgInfo, 4> &Args)
+      : RemarkName(RemarkName), FunctionName(FunctionName), PassName(PassName),
+        RemarkType(RemarkType), Args(Args) {}
+  RemarkInfo(Remark &Remark)
+      : RemarkName(Remark.RemarkName.str()),
+        FunctionName(Remark.FunctionName.str()),
+        PassName(Remark.PassName.str()), RemarkType(Remark.RemarkType) {
+    for (const auto &Arg : Remark.Args)
+      Args.push_back({Arg.Key.str(), Arg.Val.str()});
+  }
+
+  /// Check if the remark has the same name, function name and pass name as \p
+  /// RHS
+  bool hasSameHeader(const RemarkInfo &RHS) const {
+    return RemarkName == RHS.RemarkName && FunctionName == RHS.FunctionName &&
+           PassName == RHS.PassName;
+  };
+  void print(raw_ostream &OS) const;
+  void printHeader(raw_ostream &OS) const;
+};
+
+inline bool operator<(const RemarkArgInfo &LHS, const RemarkArgInfo &RHS) {
+  return std::make_tuple(LHS.Key, LHS.Val) < std::make_tuple(RHS.Key, RHS.Val);
+}
+
+inline bool operator<(const RemarkInfo &LHS, const RemarkInfo &RHS) {
+  return std::make_tuple(LHS.RemarkType, LHS.PassName, LHS.RemarkName,
+                         LHS.FunctionName, LHS.Args) <
+         std::make_tuple(RHS.RemarkType, RHS.PassName, RHS.RemarkName,
+                         RHS.FunctionName, RHS.Args);
+}
+
+inline bool operator==(const RemarkArgInfo &LHS, const RemarkArgInfo &RHS) {
+  return LHS.Key == RHS.Key && LHS.Val == RHS.Val;
+}
+
+inline bool operator==(const RemarkInfo &LHS, const RemarkInfo &RHS) {
+  return LHS.RemarkName == RHS.RemarkName &&
+         LHS.FunctionName == RHS.FunctionName && LHS.PassName == RHS.PassName &&
+         LHS.RemarkType == RHS.RemarkType && LHS.Args == RHS.Args;
+}
+
+inline raw_ostream &operator<<(raw_ostream &OS,
+                               const RemarkArgInfo &RemarkArgInfo) {
+  RemarkArgInfo.print(OS);
+  return OS;
+}
+
+inline raw_ostream &operator<<(raw_ostream &OS, const RemarkInfo &RemarkInfo) {
+  RemarkInfo.print(OS);
+  return OS;
+}
+
+/// Represents the unique location where the remark was issued which is based on
+/// the debug information attatched to the remark. The debug location conists of
+/// the source file path, function name, line number and column number.
+struct DebugLocation {
+  std::string SourceFilePath;
+  std::string FunctionName;
+  unsigned SourceLine = 0;
+  unsigned SourceColumn = 0;
+  DebugLocation() = default;
+  DebugLocation(StringRef SourceFilePath, StringRef FunctionName,
+                unsigned SourceLine, unsigned SourceColumn)
+      : SourceFilePath(SourceFilePath.str()), FunctionName(FunctionName.str()),
+        SourceLine(SourceLine), SourceColumn(SourceColumn) {}
+  std::string toString() {
+    return "Ln: " + to_string(SourceLine) + " Col: " + to_string(SourceColumn);
+  }
+};
+
+/// Configure the verbosity of the diff by choosing to only show unique remarks
+/// from each version or only consider remarks if they differ in type or
+/// argument. The configurator handles user specified arguments passed by flags.
+struct DiffConfigurator {
+  bool AddRemarksFromA;
+  bool AddRemarksFromB;
+  bool ShowCommonRemarks;
+  bool ShowRemarkTypeDiff;
+  bool ShowArgTypeDiff;
+  DiffConfigurator(bool ShowArgDiffOnly, bool OnlyShowCommonRemarks,
+                   bool OnlyShowDifferentRemarks, bool ShowOnlyA,
+                   bool ShowOnlyB, bool ShowRemarkTypeDiffOnly) {
+    AddRemarksFromA = !OnlyShowCommonRemarks && (ShowOnlyA || !ShowOnlyB);
+    AddRemarksFromB = !OnlyShowCommonRemarks && (ShowOnlyB || !ShowOnlyA);
+    ShowCommonRemarks = !OnlyShowDifferentRemarks || OnlyShowCommonRemarks;
+    ShowRemarkTypeDiff = !ShowArgDiffOnly || ShowRemarkTypeDiffOnly;
+    ShowArgTypeDiff = !ShowRemarkTypeDiffOnly || ShowArgDiffOnly;
+  }
+};
+
+/// Represent a diff where the remark header information is the same but the
+/// differ in remark type or agruments.
+struct DiffAtRemark {
+  RemarkInfo BaseRemark;
+  std::optional<std::pair<Type, Type>> RemarkTypeDiff;
+  SmallVector<RemarkArgInfo, 4> OnlyA;
+  SmallVector<RemarkArgInfo, 4> OnlyB;
+  SmallVector<RemarkArgInfo, 4> InBoth;
+
+  /// Compute the diff between two remarks \p RA and \p RB which share the same
+  /// header and differ in remark type and arguments.
+  DiffAtRemark(RemarkInfo &RA, RemarkInfo &RB, DiffConfigurator &DiffConfig)
+      : BaseRemark(RA) {
+    if (DiffConfig.ShowArgTypeDiff) {
+      unsigned ArgIdx = 0;
+      // Loop through the remarks in RA and RB in order comparing both.
+      for (; ArgIdx < std::min(RA.Args.size(), RB.Args.size()); ArgIdx++) {
+        if (RA.Args[ArgIdx] == (RB.Args[ArgIdx]))
+          InBoth.push_back(RA.Args[ArgIdx]);
+        else {
+          OnlyA.push_back(RA.Args[ArgIdx]);
+          OnlyB.push_back(RB.Args[ArgIdx]);
+        }
+      }
+
+      // Add the remaining remarks if they exist to OnlyA or OnlyB.
+      SmallVector<RemarkArgInfo, 4> RemainingArgs =
+          RA.Args.size() > RB.Args.size() ? RA.Args : RB.Args;
+      bool IsARemaining = RA.Args.size() > RB.Args.size() ? true : false;
+      for (; ArgIdx < RemainingArgs.size(); ArgIdx++)
+        if (IsARemaining)
----------------
zjaffal wrote:

The code I have copies the rest of the remaining remarks into OnlyA or OnlyB. I guess I can move the check of `IsARemaining` outside the loop

https://github.com/llvm/llvm-project/pull/85007


More information about the llvm-commits mailing list