diff options
Diffstat (limited to 'llvm/tools/llvm-remarkutil/RemarkSummary.cpp')
| -rw-r--r-- | llvm/tools/llvm-remarkutil/RemarkSummary.cpp | 254 |
1 files changed, 254 insertions, 0 deletions
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 |
