[llvm] 128214f - [llvm-remarkutil] Introduce summary tool (#160549)
via llvm-commits
llvm-commits at lists.llvm.org
Mon Oct 27 09:54:42 PDT 2025
Author: Tobias Stadler
Date: 2025-10-27T09:54:38-07:00
New Revision: 128214f3b2a4b470a4b45f9b2eece7a439d795d7
URL: https://github.com/llvm/llvm-project/commit/128214f3b2a4b470a4b45f9b2eece7a439d795d7
DIFF: https://github.com/llvm/llvm-project/commit/128214f3b2a4b470a4b45f9b2eece7a439d795d7.diff
LOG: [llvm-remarkutil] Introduce summary tool (#160549)
This tool provides a harness for implementing different strategies that
summarize many remarks (possibly from multiple translation units) into
new summary remarks. The remark summaries can then be viewed using tools
like `opt-viewer`.
The first summary strategy is `--inline-callees`, which generates
remarks that summarize the per-callee inline statistics for functions
that appear in inling remarks. This is useful for troubleshooting
inlining issues/regressions on large codebases.
Pull Request: https://github.com/llvm/llvm-project/pull/160549
Added:
llvm/test/tools/llvm-remarkutil/summary/Inputs/inline.yaml
llvm/test/tools/llvm-remarkutil/summary/inline.test
llvm/tools/llvm-remarkutil/RemarkSummary.cpp
Modified:
llvm/include/llvm/Remarks/Remark.h
llvm/lib/Remarks/Remark.cpp
llvm/test/tools/llvm-remarkutil/broken-bitstream-remark-magic.test
llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
llvm/test/tools/llvm-remarkutil/empty-file.test
llvm/tools/llvm-remarkutil/CMakeLists.txt
llvm/tools/llvm-remarkutil/RemarkCounter.cpp
llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
Removed:
################################################################################
diff --git a/llvm/include/llvm/Remarks/Remark.h b/llvm/include/llvm/Remarks/Remark.h
index 8c8ca769c7d16..663af6302d6ff 100644
--- a/llvm/include/llvm/Remarks/Remark.h
+++ b/llvm/include/llvm/Remarks/Remark.h
@@ -51,12 +51,21 @@ struct Argument {
// If set, the debug location corresponding to the value.
std::optional<RemarkLocation> Loc;
+ Argument() = default;
+ Argument(StringRef Key, StringRef Val) : Key(Key), Val(Val) {}
+
/// Implement operator<< on Argument.
LLVM_ABI void print(raw_ostream &OS) const;
- /// Return the value of argument as int.
- LLVM_ABI std::optional<int> getValAsInt() const;
- /// Check if the argument value can be parsed as int.
- LLVM_ABI bool isValInt() const;
+
+ /// Return the value of argument as an integer of type T.
+ template <typename T>
+ std::optional<T> getValAsInt(unsigned Radix = 10) const {
+ StringRef Str = Val;
+ T Res;
+ if (Str.consumeInteger<T>(Radix, Res) || !Str.empty())
+ return std::nullopt;
+ return Res;
+ }
};
// Create wrappers for C Binding types (see CBindingWrapping.h).
@@ -127,6 +136,10 @@ struct Remark {
/// Return a message composed from the arguments as a string.
LLVM_ABI std::string getArgsAsMsg() const;
+ /// Return the first argument with the specified key or nullptr if no such
+ /// argument was found.
+ LLVM_ABI Argument *getArgByKey(StringRef Key);
+
/// Clone this remark to explicitly ask for a copy.
Remark clone() const { return *this; }
diff --git a/llvm/lib/Remarks/Remark.cpp b/llvm/lib/Remarks/Remark.cpp
index 0e98cad8e9045..09f24e93255e0 100644
--- a/llvm/lib/Remarks/Remark.cpp
+++ b/llvm/lib/Remarks/Remark.cpp
@@ -13,6 +13,7 @@
#include "llvm/Remarks/Remark.h"
#include "llvm/ADT/APInt.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
#include <optional>
using namespace llvm;
@@ -26,16 +27,13 @@ std::string Remark::getArgsAsMsg() const {
return Str;
}
-/// Returns the value of a specified key parsed from StringRef.
-std::optional<int> Argument::getValAsInt() const {
- APInt KeyVal;
- if (Val.getAsInteger(10, KeyVal))
- return std::nullopt;
- return KeyVal.getSExtValue();
+Argument *Remark::getArgByKey(StringRef Key) {
+ auto *It = find_if(Args, [&](auto &Arg) { return Arg.Key == Key; });
+ if (It == Args.end())
+ return nullptr;
+ return &*It;
}
-bool Argument::isValInt() const { return getValAsInt().has_value(); }
-
void RemarkLocation::print(raw_ostream &OS) const {
OS << "{ "
<< "File: " << SourceFilePath << ", Line: " << SourceLine
diff --git a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark-magic.test b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark-magic.test
index c21dbd72a2a18..9d64201cc071e 100644
--- a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark-magic.test
+++ b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark-magic.test
@@ -3,5 +3,6 @@ RUN: not llvm-remarkutil instruction-mix %p/Inputs/broken-remark-magic.bitstream
RUN: not llvm-remarkutil annotation-count --annotation-type=remark %p/Inputs/broken-remark-magic.bitstream -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil count %p/Inputs/broken-remark-magic.bitstream -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil filter %p/Inputs/broken-remark-magic.bitstream -o - 2>&1 | FileCheck %s
+RUN: not llvm-remarkutil summary %p/Inputs/broken-remark-magic.bitstream -o - 2>&1 | FileCheck %s
CHECK: error: Automatic detection of remark format failed. Unknown magic number: '1234'
diff --git a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
index 339f082d4825b..0a668131c801c 100644
--- a/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
+++ b/llvm/test/tools/llvm-remarkutil/broken-bitstream-remark.test
@@ -3,5 +3,6 @@ RUN: not llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/broken-r
RUN: not llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remark %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil count --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil filter --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+RUN: not llvm-remarkutil summary --parser=bitstream %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
CHECK: error: Unknown magic number: expecting RMRK, got --- .
diff --git a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
index 9da3de4034b0f..76b2d5610d8cd 100644
--- a/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
+++ b/llvm/test/tools/llvm-remarkutil/broken-yaml-remark.test
@@ -4,5 +4,6 @@ RUN: not llvm-remarkutil instruction-mix --parser=yaml %p/Inputs/broken-remark -
RUN: not llvm-remarkutil annotation-count --parser=yaml --annotation-type=remark %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil count --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
RUN: not llvm-remarkutil filter --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
+RUN: not llvm-remarkutil summary --parser=yaml %p/Inputs/broken-remark -o - 2>&1 | FileCheck %s
CHECK: error: Type, Pass, Name or Function missing
diff --git a/llvm/test/tools/llvm-remarkutil/empty-file.test b/llvm/test/tools/llvm-remarkutil/empty-file.test
index 9b2b000e9c24b..53f04f36226a5 100644
--- a/llvm/test/tools/llvm-remarkutil/empty-file.test
+++ b/llvm/test/tools/llvm-remarkutil/empty-file.test
@@ -4,18 +4,21 @@ RUN: not llvm-remarkutil instruction-mix --parser=yaml %p/Inputs/empty-file -o -
RUN: not llvm-remarkutil annotation-count --parser=yaml --annotation-type=remark %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: not llvm-remarkutil count --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: not llvm-remarkutil filter --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
+RUN: not llvm-remarkutil summary --parser=yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --check-prefix=YAMLPARSER
RUN: llvm-remarkutil bitstream2yaml %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=BITSTREAM2YAML
RUN: llvm-remarkutil instruction-count --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=SIZEBITSTREAM
RUN: llvm-remarkutil instruction-mix --parser=bitstream %p/Inputs/empty-file --report_style=csv -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=MIXBITSTREAM
RUN: llvm-remarkutil annotation-count --parser=bitstream --annotation-type=remark %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=ANNOTATIONBITSTREAM
RUN: llvm-remarkutil count --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=COUNTBITSTREAM
-RUN: llvm-remarkutil filter --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=FILTERBITSTREAM
+RUN: llvm-remarkutil filter --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=EMPTYBITSTREAM
+RUN: llvm-remarkutil summary --parser=bitstream %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=EMPTYBITSTREAM
; Parser format auto-detection should treat empty files as bitstream files
RUN: llvm-remarkutil instruction-count %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=SIZEBITSTREAM
RUN: llvm-remarkutil instruction-mix %p/Inputs/empty-file --report_style=csv -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=MIXBITSTREAM
RUN: llvm-remarkutil annotation-count --annotation-type=remark %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=ANNOTATIONBITSTREAM
RUN: llvm-remarkutil count %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=COUNTBITSTREAM
-RUN: llvm-remarkutil filter %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=FILTERBITSTREAM
+RUN: llvm-remarkutil filter %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=EMPTYBITSTREAM
+RUN: llvm-remarkutil summary %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allow-empty --check-prefix=EMPTYBITSTREAM
; YAMLPARSER: error: document root is not of mapping type.
@@ -34,4 +37,4 @@ RUN: llvm-remarkutil filter %p/Inputs/empty-file -o - 2>&1 | FileCheck %s --allo
; MIXBITSTREAM-LABEL: Instruction,Count
; MIXBITSTREAM-EMPTY:
-; FILTERBITSTREAM-NOT: {{.}}
+; EMPTYBITSTREAM-NOT: {{.}}
diff --git a/llvm/test/tools/llvm-remarkutil/summary/Inputs/inline.yaml b/llvm/test/tools/llvm-remarkutil/summary/Inputs/inline.yaml
new file mode 100644
index 0000000000000..efb8cd6ecf5a9
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/summary/Inputs/inline.yaml
@@ -0,0 +1,50 @@
+--- !Missed
+Pass: inline
+Name: TooCostly
+DebugLoc: { File: 'foo.cpp', Line: 21, Column: 6 }
+Function: fooCaller
+Args:
+ - Callee: fooCallee
+ DebugLoc: { File: 'foo.cpp', Line: 10, Column: 0 }
+ - Caller: fooCaller
+ DebugLoc: { File: 'foo.cpp', Line: 20, Column: 0 }
+ - Cost: '125'
+ - Threshold: '100'
+...
+--- !Passed
+Pass: inline
+Name: Inlined
+DebugLoc: { File: 'foo.cpp', Line: 21, Column: 6 }
+Function: fooCaller2
+Args:
+ - Callee: fooCallee
+ DebugLoc: { File: 'foo.cpp', Line: 10, Column: 0 }
+ - Caller: fooCaller
+ DebugLoc: { File: 'foo.cpp', Line: 20, Column: 0 }
+ - Cost: '-15'
+ - Threshold: '100'
+ - Line: '1'
+ - Column: '6'
+...
+--- !Passed
+Pass: inline
+Name: AlwaysInline
+DebugLoc: { File: 'bar.cpp', Line: 23, Column: 10 }
+Function: barCaller
+Args:
+ - Callee: barCallee
+ DebugLoc: { File: 'bar.cpp', Line: 5, Column: 0 }
+ - Caller: barCaller
+ DebugLoc: { File: 'bar.cpp', Line: 22, Column: 0 }
+ - Reason: always inline attribute
+ - Line: '23'
+ - Column: '10'
+...
+--- !Missed
+Pass: inline
+Name: NoDefinition
+Function: bazCaller
+Args:
+ - Callee: bazCallee
+ - Caller: bazCaller
+...
diff --git a/llvm/test/tools/llvm-remarkutil/summary/inline.test b/llvm/test/tools/llvm-remarkutil/summary/inline.test
new file mode 100644
index 0000000000000..57473186e63e3
--- /dev/null
+++ b/llvm/test/tools/llvm-remarkutil/summary/inline.test
@@ -0,0 +1,54 @@
+RUN: llvm-remarkutil summary --inline-callees %p/Inputs/inline.yaml | FileCheck -strict-whitespace %s
+
+; CHECK: --- !Analysis
+; CHECK-NEXT: Pass: inline
+; CHECK-NEXT: Name: Summary
+; CHECK-NEXT: DebugLoc: { File: bar.cpp, Line: 5, Column: 0 }
+; CHECK-NEXT: Function: barCallee
+; CHECK-NEXT: Args:
+; CHECK-NEXT: - String: 'Incoming Calls ('
+; CHECK-NEXT: - String: AlwaysInline
+; CHECK-NEXT: - String: ': '
+; CHECK-NEXT: - AlwaysInline: '1'
+; CHECK-NEXT: - String: ')'
+; CHECK-NEXT: ...
+; CHECK-NEXT: --- !Analysis
+; CHECK-NEXT: Pass: inline
+; CHECK-NEXT: Name: Summary
+; CHECK-NEXT: Function: bazCallee
+; CHECK-NEXT: Args:
+; CHECK-NEXT: - String: 'Incoming Calls ('
+; CHECK-NEXT: - String: NoDefinition
+; CHECK-NEXT: - String: ': '
+; CHECK-NEXT: - NoDefinition: '1'
+; CHECK-NEXT: - String: ')'
+; CHECK-NEXT: ...
+; CHECK-NEXT: --- !Analysis
+; CHECK-NEXT: Pass: inline
+; CHECK-NEXT: Name: Summary
+; CHECK-NEXT: DebugLoc: { File: foo.cpp, Line: 10, Column: 0 }
+; CHECK-NEXT: Function: fooCallee
+; CHECK-NEXT: Args:
+; CHECK-NEXT: - String: 'Incoming Calls ('
+; CHECK-NEXT: - String: Inlined
+; CHECK-NEXT: - String: ': '
+; CHECK-NEXT: - Inlined: '1'
+; CHECK-NEXT: - String: ', '
+; CHECK-NEXT: - String: TooCostly
+; CHECK-NEXT: - String: ': '
+; CHECK-NEXT: - TooCostly: '1'
+; CHECK-NEXT: - String: ')'
+; CHECK-NEXT: - String: "\nLeast profitable (cost="
+; CHECK-NEXT: - LeastProfitCost: '125'
+; CHECK-NEXT: DebugLoc: { File: foo.cpp, Line: 21, Column: 6 }
+; CHECK-NEXT: - String: ', threshold='
+; CHECK-NEXT: - LeastProfitThreshold: '100'
+; CHECK-NEXT: - String: ')'
+; CHECK-NEXT: - String: "\nMost profitable (cost="
+; CHECK-NEXT: - MostProfitCost: '-15'
+; CHECK-NEXT: DebugLoc: { File: foo.cpp, Line: 21, Column: 6 }
+; CHECK-NEXT: - String: ', threshold='
+; CHECK-NEXT: - MostProfitThreshold: '100'
+; CHECK-NEXT: - String: ')'
+; CHECK-NEXT: ...
+; CHECK-NOT: {{.}}
diff --git a/llvm/tools/llvm-remarkutil/CMakeLists.txt b/llvm/tools/llvm-remarkutil/CMakeLists.txt
index c6e9334d87c04..3f0a4360266e1 100644
--- a/llvm/tools/llvm-remarkutil/CMakeLists.txt
+++ b/llvm/tools/llvm-remarkutil/CMakeLists.txt
@@ -11,6 +11,7 @@ add_llvm_tool(llvm-remarkutil
RemarkFilter.cpp
RemarkInstructionMix.cpp
RemarkSizeDiff.cpp
+ RemarkSummary.cpp
RemarkUtil.cpp
RemarkUtilHelpers.cpp
RemarkUtilRegistry.cpp
diff --git a/llvm/tools/llvm-remarkutil/RemarkCounter.cpp b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
index 2e842c8c2d72e..4e429b75e3c2d 100644
--- a/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
+++ b/llvm/tools/llvm-remarkutil/RemarkCounter.cpp
@@ -70,11 +70,11 @@ static cl::opt<GroupBy> GroupByOpt(
/// integer value or 0 if it is has no integer value.
static unsigned getValForKey(StringRef Key, const Remark &Remark) {
auto *RemarkArg = find_if(Remark.Args, [&Key](const Argument &Arg) {
- return Arg.Key == Key && Arg.isValInt();
+ return Arg.Key == Key && Arg.getValAsInt<unsigned>();
});
if (RemarkArg == Remark.Args.end())
return 0;
- return *RemarkArg->getValAsInt();
+ return *RemarkArg->getValAsInt<unsigned>();
}
Error ArgumentCounter::getAllMatchingArgumentsInRemark(
@@ -91,7 +91,7 @@ Error ArgumentCounter::getAllMatchingArgumentsInRemark(
continue;
for (auto &Key : Arguments) {
for (Argument Arg : Remark.Args)
- if (Key.match(Arg.Key) && Arg.isValInt())
+ if (Key.match(Arg.Key) && Arg.getValAsInt<unsigned>())
ArgumentSetIdxMap.insert({Arg.Key, ArgumentSetIdxMap.size()});
}
}
diff --git a/llvm/tools/llvm-remarkutil/RemarkSummary.cpp b/llvm/tools/llvm-remarkutil/RemarkSummary.cpp
new file mode 100644
index 0000000000000..124bd51720d17
--- /dev/null
+++ b/llvm/tools/llvm-remarkutil/RemarkSummary.cpp
@@ -0,0 +1,254 @@
+//===- RemarkSummary.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
+//
+//===----------------------------------------------------------------------===//
+//
+// Specialized tool to summarize remarks
+//
+//===----------------------------------------------------------------------===//
+
+#include "RemarkUtilHelpers.h"
+#include "RemarkUtilRegistry.h"
+
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/StringRef.h"
+#include "llvm/Support/CommandLine.h"
+#include "llvm/Support/Debug.h"
+#include "llvm/Support/Error.h"
+#include "llvm/Support/Regex.h"
+#include "llvm/Support/WithColor.h"
+#include <memory>
+
+using namespace llvm;
+using namespace remarks;
+using namespace llvm::remarkutil;
+
+namespace summary {
+
+static cl::SubCommand
+ SummarySub("summary", "Summarize remarks using
diff erent strategies.");
+
+INPUT_FORMAT_COMMAND_LINE_OPTIONS(SummarySub)
+OUTPUT_FORMAT_COMMAND_LINE_OPTIONS(SummarySub)
+INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SummarySub)
+
+static cl::OptionCategory SummaryStrategyCat("Strategy options");
+
+enum class KeepMode { None, Used, All };
+
+static cl::opt<KeepMode> KeepInputOpt(
+ "keep", cl::desc("Keep input remarks in output"), cl::init(KeepMode::None),
+ cl::values(clEnumValN(KeepMode::None, "none",
+ "Don't keep input remarks (default)"),
+ clEnumValN(KeepMode::Used, "used",
+ "Keep only remarks used for summary"),
+ clEnumValN(KeepMode::All, "all", "Keep all input remarks")),
+ cl::sub(SummarySub));
+
+static cl::opt<bool>
+ IgnoreMalformedOpt("ignore-malformed",
+ cl::desc("Ignore remarks that fail to process"),
+ cl::init(false), cl::Hidden, cl::sub(SummarySub));
+
+// Use one cl::opt per Strategy, because future strategies might need to take
+// per-strategy parameters.
+static cl::opt<bool> EnableInlineSummaryOpt(
+ "inline-callees", cl::desc("Summarize per-callee inling statistics"),
+ cl::cat(SummaryStrategyCat), cl::init(false), cl::sub(SummarySub));
+
+/// An interface to implement
diff erent strategies for creating remark
+/// summaries. Override this class to develop new strategies.
+class SummaryStrategy {
+public:
+ virtual ~SummaryStrategy() = default;
+
+ /// Strategy should return true if it wants to process the remark \p R.
+ virtual bool filter(Remark &R) = 0;
+
+ /// Hook to process the remark \p R (i.e. collect the necessary data for
+ /// producing summary remarks). This will only be called with remarks
+ /// accepted by filter(). Can return an error if \p R is malformed or
+ /// unexpected.
+ virtual Error process(Remark &R) = 0;
+
+ /// Hook to emit new remarks based on the collected data.
+ virtual void emit(RemarkSerializer &Serializer) = 0;
+};
+
+/// Check if any summary strategy options are explicitly enabled.
+static bool isAnyStrategyRequested() {
+ StringMap<cl::Option *> Opts = cl::getRegisteredOptions(SummarySub);
+ for (auto &[_, Opt] : Opts) {
+ if (!is_contained(Opt->Categories, &SummaryStrategyCat))
+ continue;
+ if (!Opt->getNumOccurrences())
+ continue;
+ return true;
+ }
+ return false;
+}
+
+class InlineCalleeSummary : public SummaryStrategy {
+ struct CallsiteCost {
+ int Cost = 0;
+ int Threshold = 0;
+ std::optional<RemarkLocation> Loc;
+
+ int getProfit() const { return Threshold - Cost; }
+
+ friend bool operator==(const CallsiteCost &A, const CallsiteCost &B) {
+ return A.Cost == B.Cost && A.Threshold == B.Threshold && A.Loc == B.Loc;
+ }
+
+ friend bool operator!=(const CallsiteCost &A, const CallsiteCost &B) {
+ return !(A == B);
+ }
+ };
+
+ struct CalleeSummary {
+ SmallDenseMap<StringRef, size_t> Stats;
+ std::optional<RemarkLocation> Loc;
+ std::optional<CallsiteCost> LeastProfit;
+ std::optional<CallsiteCost> MostProfit;
+
+ void updateCost(CallsiteCost NewCost) {
+ if (!LeastProfit || NewCost.getProfit() < LeastProfit->getProfit())
+ LeastProfit = NewCost;
+ if (!MostProfit || NewCost.getProfit() > MostProfit->getProfit())
+ MostProfit = NewCost;
+ }
+ };
+
+ DenseMap<StringRef, CalleeSummary> Callees;
+
+ Error malformed() { return createStringError("Malformed inline remark."); }
+
+ bool filter(Remark &R) override {
+ return R.PassName == "inline" && R.RemarkName != "Summary";
+ }
+
+ Error process(Remark &R) override {
+ auto *CalleeArg = R.getArgByKey("Callee");
+ if (!CalleeArg)
+ return Error::success();
+ auto &Callee = Callees[CalleeArg->Val];
+ ++Callee.Stats[R.RemarkName];
+ if (!Callee.Loc)
+ Callee.Loc = CalleeArg->Loc;
+
+ Argument *CostArg = R.getArgByKey("Cost");
+ Argument *ThresholdArg = R.getArgByKey("Threshold");
+ if (!CostArg || !ThresholdArg)
+ return Error::success();
+ auto CostVal = CostArg->getValAsInt<int>();
+ auto ThresholdVal = ThresholdArg->getValAsInt<int>();
+ if (!CostVal || !ThresholdVal)
+ return malformed();
+ Callee.updateCost({*CostVal, *ThresholdVal, R.Loc});
+ return Error::success();
+ }
+
+ void emit(RemarkSerializer &Serializer) override {
+ SmallVector<StringRef> SortedKeys(Callees.keys());
+ llvm::sort(SortedKeys);
+ for (StringRef K : SortedKeys) {
+ auto &V = Callees[K];
+ RemarkBuilder RB(Type::Analysis, "inline", "Summary", K);
+ if (V.Stats.empty())
+ continue;
+ RB.R.Loc = V.Loc;
+ RB << "Incoming Calls (";
+ SmallVector<StringRef> StatKeys(V.Stats.keys());
+ llvm::sort(StatKeys);
+ bool First = true;
+ for (StringRef StatK : StatKeys) {
+ if (!First)
+ RB << ", ";
+ RB << StatK << ": " << NV(StatK, V.Stats[StatK]);
+ First = false;
+ }
+ RB << ")";
+ if (V.LeastProfit && V.MostProfit != V.LeastProfit) {
+ RB << "\nLeast profitable (cost="
+ << NV("LeastProfitCost", V.LeastProfit->Cost, V.LeastProfit->Loc)
+ << ", threshold="
+ << NV("LeastProfitThreshold", V.LeastProfit->Threshold) << ")";
+ }
+ if (V.MostProfit) {
+ RB << "\nMost profitable (cost="
+ << NV("MostProfitCost", V.MostProfit->Cost, V.MostProfit->Loc)
+ << ", threshold="
+ << NV("MostProfitThreshold", V.MostProfit->Threshold) << ")";
+ }
+ Serializer.emit(RB.R);
+ }
+ }
+};
+
+static Error trySummary() {
+ auto MaybeBuf = getInputMemoryBuffer(InputFileName);
+ if (!MaybeBuf)
+ return MaybeBuf.takeError();
+ auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());
+ if (!MaybeParser)
+ return MaybeParser.takeError();
+ auto &Parser = **MaybeParser;
+
+ Format SerializerFormat =
+ getSerializerFormat(OutputFileName, OutputFormat, Parser.ParserFormat);
+
+ auto MaybeOF = getOutputFileForRemarks(OutputFileName, SerializerFormat);
+ if (!MaybeOF)
+ return MaybeOF.takeError();
+ auto OF = std::move(*MaybeOF);
+
+ auto MaybeSerializer = createRemarkSerializer(SerializerFormat, OF->os());
+ if (!MaybeSerializer)
+ return MaybeSerializer.takeError();
+ auto &Serializer = **MaybeSerializer;
+
+ bool UseDefaultStrategies = !isAnyStrategyRequested();
+ SmallVector<std::unique_ptr<SummaryStrategy>> Strategies;
+ if (EnableInlineSummaryOpt || UseDefaultStrategies)
+ Strategies.push_back(std::make_unique<InlineCalleeSummary>());
+
+ auto MaybeRemark = Parser.next();
+ for (; MaybeRemark; MaybeRemark = Parser.next()) {
+ Remark &Remark = **MaybeRemark;
+ bool UsedRemark = false;
+ for (auto &Strategy : Strategies) {
+ if (!Strategy->filter(Remark))
+ continue;
+ UsedRemark = true;
+ if (auto E = Strategy->process(Remark)) {
+ if (IgnoreMalformedOpt) {
+ WithColor::warning() << "Ignored error: " << E << "\n";
+ consumeError(std::move(E));
+ continue;
+ }
+ return E;
+ }
+ }
+ if (KeepInputOpt == KeepMode::All ||
+ (KeepInputOpt == KeepMode::Used && UsedRemark))
+ Serializer.emit(Remark);
+ }
+
+ auto E = MaybeRemark.takeError();
+ if (!E.isA<EndOfFileError>())
+ return E;
+ consumeError(std::move(E));
+
+ for (auto &Strategy : Strategies)
+ Strategy->emit(Serializer);
+
+ OF->keep();
+ return Error::success();
+}
+
+static CommandRegistration SummaryReg(&SummarySub, trySummary);
+
+} // namespace summary
diff --git a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
index 73867fe35f06c..39e7b423c4dc0 100644
--- a/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
+++ b/llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h
@@ -9,6 +9,7 @@
// Helpers for remark utilites
//
//===----------------------------------------------------------------------===//
+#include "llvm/ADT/StringExtras.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/Remarks/Remark.h"
#include "llvm/Remarks/RemarkFormat.h"
@@ -19,6 +20,7 @@
#include "llvm/Support/FileSystem.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/Regex.h"
+#include "llvm/Support/StringSaver.h"
#include "llvm/Support/ToolOutputFile.h"
// Keep input + output help + names consistent across the various modes via a
@@ -205,5 +207,54 @@ struct Filters {
bool filterRemark(const Remark &Remark);
};
+/// Helper to construct Remarks using an API similar to DiagnosticInfo.
+/// Once this is more fully featured, consider implementing DiagnosticInfo using
+/// RemarkBuilder.
+class RemarkBuilder {
+ BumpPtrAllocator Alloc;
+ UniqueStringSaver Strs;
+
+public:
+ Remark R;
+ struct Argument {
+ std::string Key;
+ std::string Val;
+ std::optional<RemarkLocation> Loc;
+ Argument(StringRef Key, StringRef Val,
+ std::optional<RemarkLocation> Loc = std::nullopt)
+ : Key(Key), Val(Val), Loc(Loc) {}
+ Argument(StringRef Key, int Val,
+ std::optional<RemarkLocation> Loc = std::nullopt)
+ : Key(Key), Val(itostr(Val)), Loc(Loc) {}
+ };
+
+ RemarkBuilder(Type RemarkType, StringRef PassName, StringRef RemarkName,
+ StringRef FunctionName)
+ : Strs(Alloc) {
+ R.RemarkType = RemarkType;
+ R.PassName = Strs.save(PassName);
+ R.RemarkName = Strs.save(RemarkName);
+ R.FunctionName = Strs.save(FunctionName);
+ }
+
+ RemarkBuilder &operator<<(Argument &&Arg) {
+ auto &RArg = R.Args.emplace_back(Strs.save(Arg.Key), Strs.save(Arg.Val));
+ RArg.Loc = Arg.Loc;
+ return *this;
+ }
+
+ RemarkBuilder &operator<<(const char *Str) {
+ R.Args.emplace_back("String", Str);
+ return *this;
+ }
+
+ RemarkBuilder &operator<<(StringRef Str) {
+ R.Args.emplace_back("String", Strs.save(Str));
+ return *this;
+ }
+};
+
+using NV = RemarkBuilder::Argument;
+
} // namespace remarks
} // namespace llvm
More information about the llvm-commits
mailing list