diff options
Diffstat (limited to 'llvm/tools')
| -rw-r--r-- | llvm/tools/llvm-profdata/llvm-profdata.cpp | 7 | ||||
| -rw-r--r-- | llvm/tools/llvm-remarkutil/CMakeLists.txt | 1 | ||||
| -rw-r--r-- | llvm/tools/llvm-remarkutil/RemarkCounter.cpp | 6 | ||||
| -rw-r--r-- | llvm/tools/llvm-remarkutil/RemarkSummary.cpp | 254 | ||||
| -rw-r--r-- | llvm/tools/llvm-remarkutil/RemarkUtilHelpers.h | 51 |
5 files changed, 316 insertions, 3 deletions
diff --git a/llvm/tools/llvm-profdata/llvm-profdata.cpp b/llvm/tools/llvm-profdata/llvm-profdata.cpp index a356bcd..9b853e2 100644 --- a/llvm/tools/llvm-profdata/llvm-profdata.cpp +++ b/llvm/tools/llvm-profdata/llvm-profdata.cpp @@ -10,6 +10,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/ScopeExit.h" #include "llvm/ADT/SmallSet.h" #include "llvm/ADT/SmallVector.h" #include "llvm/ADT/StringRef.h" @@ -778,6 +779,12 @@ loadInput(const WeightedFile &Input, SymbolRemapper *Remapper, // we have more non-fatal errors from InstrProfReader in the future. How // should this interact with different -failure-mode? std::optional<std::pair<Error, std::string>> ReaderWarning; + auto ReaderWarningScope = llvm::make_scope_exit([&] { + // If we hit a different error we may still have an error in ReaderWarning. + // Consume it now to avoid an assert + if (ReaderWarning) + consumeError(std::move(ReaderWarning->first)); + }); auto Warn = [&](Error E) { if (ReaderWarning) { consumeError(std::move(E)); diff --git a/llvm/tools/llvm-remarkutil/CMakeLists.txt b/llvm/tools/llvm-remarkutil/CMakeLists.txt index c6e9334..3f0a436 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 2e842c8..4e429b7 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 0000000..124bd51 --- /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 different 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 different 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 73867fe..39e7b42 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 |
