255 lines · cpp
1//===- RemarkSummary.cpp --------------------------------------------------===//2//3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.4// See https://llvm.org/LICENSE.txt for license information.5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception6//7//===----------------------------------------------------------------------===//8//9// Specialized tool to summarize remarks10//11//===----------------------------------------------------------------------===//12 13#include "RemarkUtilHelpers.h"14#include "RemarkUtilRegistry.h"15 16#include "llvm/ADT/STLExtras.h"17#include "llvm/ADT/StringRef.h"18#include "llvm/Support/CommandLine.h"19#include "llvm/Support/Debug.h"20#include "llvm/Support/Error.h"21#include "llvm/Support/Regex.h"22#include "llvm/Support/WithColor.h"23#include <memory>24 25using namespace llvm;26using namespace remarks;27using namespace llvm::remarkutil;28 29namespace summary {30 31static cl::SubCommand32 SummarySub("summary", "Summarize remarks using different strategies.");33 34INPUT_FORMAT_COMMAND_LINE_OPTIONS(SummarySub)35OUTPUT_FORMAT_COMMAND_LINE_OPTIONS(SummarySub)36INPUT_OUTPUT_COMMAND_LINE_OPTIONS(SummarySub)37 38static cl::OptionCategory SummaryStrategyCat("Strategy options");39 40enum class KeepMode { None, Used, All };41 42static cl::opt<KeepMode> KeepInputOpt(43 "keep", cl::desc("Keep input remarks in output"), cl::init(KeepMode::None),44 cl::values(clEnumValN(KeepMode::None, "none",45 "Don't keep input remarks (default)"),46 clEnumValN(KeepMode::Used, "used",47 "Keep only remarks used for summary"),48 clEnumValN(KeepMode::All, "all", "Keep all input remarks")),49 cl::sub(SummarySub));50 51static cl::opt<bool>52 IgnoreMalformedOpt("ignore-malformed",53 cl::desc("Ignore remarks that fail to process"),54 cl::init(false), cl::Hidden, cl::sub(SummarySub));55 56// Use one cl::opt per Strategy, because future strategies might need to take57// per-strategy parameters.58static cl::opt<bool> EnableInlineSummaryOpt(59 "inline-callees", cl::desc("Summarize per-callee inling statistics"),60 cl::cat(SummaryStrategyCat), cl::init(false), cl::sub(SummarySub));61 62/// An interface to implement different strategies for creating remark63/// summaries. Override this class to develop new strategies.64class SummaryStrategy {65public:66 virtual ~SummaryStrategy() = default;67 68 /// Strategy should return true if it wants to process the remark \p R.69 virtual bool filter(Remark &R) = 0;70 71 /// Hook to process the remark \p R (i.e. collect the necessary data for72 /// producing summary remarks). This will only be called with remarks73 /// accepted by filter(). Can return an error if \p R is malformed or74 /// unexpected.75 virtual Error process(Remark &R) = 0;76 77 /// Hook to emit new remarks based on the collected data.78 virtual void emit(RemarkSerializer &Serializer) = 0;79};80 81/// Check if any summary strategy options are explicitly enabled.82static bool isAnyStrategyRequested() {83 StringMap<cl::Option *> Opts = cl::getRegisteredOptions(SummarySub);84 for (auto &[_, Opt] : Opts) {85 if (!is_contained(Opt->Categories, &SummaryStrategyCat))86 continue;87 if (!Opt->getNumOccurrences())88 continue;89 return true;90 }91 return false;92}93 94class InlineCalleeSummary : public SummaryStrategy {95 struct CallsiteCost {96 int Cost = 0;97 int Threshold = 0;98 std::optional<RemarkLocation> Loc;99 100 int getProfit() const { return Threshold - Cost; }101 102 friend bool operator==(const CallsiteCost &A, const CallsiteCost &B) {103 return A.Cost == B.Cost && A.Threshold == B.Threshold && A.Loc == B.Loc;104 }105 106 friend bool operator!=(const CallsiteCost &A, const CallsiteCost &B) {107 return !(A == B);108 }109 };110 111 struct CalleeSummary {112 SmallDenseMap<StringRef, size_t> Stats;113 std::optional<RemarkLocation> Loc;114 std::optional<CallsiteCost> LeastProfit;115 std::optional<CallsiteCost> MostProfit;116 117 void updateCost(CallsiteCost NewCost) {118 if (!LeastProfit || NewCost.getProfit() < LeastProfit->getProfit())119 LeastProfit = NewCost;120 if (!MostProfit || NewCost.getProfit() > MostProfit->getProfit())121 MostProfit = NewCost;122 }123 };124 125 DenseMap<StringRef, CalleeSummary> Callees;126 127 Error malformed() { return createStringError("Malformed inline remark."); }128 129 bool filter(Remark &R) override {130 return R.PassName == "inline" && R.RemarkName != "Summary";131 }132 133 Error process(Remark &R) override {134 auto *CalleeArg = R.getArgByKey("Callee");135 if (!CalleeArg)136 return Error::success();137 auto &Callee = Callees[CalleeArg->Val];138 ++Callee.Stats[R.RemarkName];139 if (!Callee.Loc)140 Callee.Loc = CalleeArg->Loc;141 142 Argument *CostArg = R.getArgByKey("Cost");143 Argument *ThresholdArg = R.getArgByKey("Threshold");144 if (!CostArg || !ThresholdArg)145 return Error::success();146 auto CostVal = CostArg->getValAsInt<int>();147 auto ThresholdVal = ThresholdArg->getValAsInt<int>();148 if (!CostVal || !ThresholdVal)149 return malformed();150 Callee.updateCost({*CostVal, *ThresholdVal, R.Loc});151 return Error::success();152 }153 154 void emit(RemarkSerializer &Serializer) override {155 SmallVector<StringRef> SortedKeys(Callees.keys());156 llvm::sort(SortedKeys);157 for (StringRef K : SortedKeys) {158 auto &V = Callees[K];159 RemarkBuilder RB(Type::Analysis, "inline", "Summary", K);160 if (V.Stats.empty())161 continue;162 RB.R.Loc = V.Loc;163 RB << "Incoming Calls (";164 SmallVector<StringRef> StatKeys(V.Stats.keys());165 llvm::sort(StatKeys);166 bool First = true;167 for (StringRef StatK : StatKeys) {168 if (!First)169 RB << ", ";170 RB << StatK << ": " << NV(StatK, V.Stats[StatK]);171 First = false;172 }173 RB << ")";174 if (V.LeastProfit && V.MostProfit != V.LeastProfit) {175 RB << "\nLeast profitable (cost="176 << NV("LeastProfitCost", V.LeastProfit->Cost, V.LeastProfit->Loc)177 << ", threshold="178 << NV("LeastProfitThreshold", V.LeastProfit->Threshold) << ")";179 }180 if (V.MostProfit) {181 RB << "\nMost profitable (cost="182 << NV("MostProfitCost", V.MostProfit->Cost, V.MostProfit->Loc)183 << ", threshold="184 << NV("MostProfitThreshold", V.MostProfit->Threshold) << ")";185 }186 Serializer.emit(RB.R);187 }188 }189};190 191static Error trySummary() {192 auto MaybeBuf = getInputMemoryBuffer(InputFileName);193 if (!MaybeBuf)194 return MaybeBuf.takeError();195 auto MaybeParser = createRemarkParser(InputFormat, (*MaybeBuf)->getBuffer());196 if (!MaybeParser)197 return MaybeParser.takeError();198 auto &Parser = **MaybeParser;199 200 Format SerializerFormat =201 getSerializerFormat(OutputFileName, OutputFormat, Parser.ParserFormat);202 203 auto MaybeOF = getOutputFileForRemarks(OutputFileName, SerializerFormat);204 if (!MaybeOF)205 return MaybeOF.takeError();206 auto OF = std::move(*MaybeOF);207 208 auto MaybeSerializer = createRemarkSerializer(SerializerFormat, OF->os());209 if (!MaybeSerializer)210 return MaybeSerializer.takeError();211 auto &Serializer = **MaybeSerializer;212 213 bool UseDefaultStrategies = !isAnyStrategyRequested();214 SmallVector<std::unique_ptr<SummaryStrategy>> Strategies;215 if (EnableInlineSummaryOpt || UseDefaultStrategies)216 Strategies.push_back(std::make_unique<InlineCalleeSummary>());217 218 auto MaybeRemark = Parser.next();219 for (; MaybeRemark; MaybeRemark = Parser.next()) {220 Remark &Remark = **MaybeRemark;221 bool UsedRemark = false;222 for (auto &Strategy : Strategies) {223 if (!Strategy->filter(Remark))224 continue;225 UsedRemark = true;226 if (auto E = Strategy->process(Remark)) {227 if (IgnoreMalformedOpt) {228 WithColor::warning() << "Ignored error: " << E << "\n";229 consumeError(std::move(E));230 continue;231 }232 return E;233 }234 }235 if (KeepInputOpt == KeepMode::All ||236 (KeepInputOpt == KeepMode::Used && UsedRemark))237 Serializer.emit(Remark);238 }239 240 auto E = MaybeRemark.takeError();241 if (!E.isA<EndOfFileError>())242 return E;243 consumeError(std::move(E));244 245 for (auto &Strategy : Strategies)246 Strategy->emit(Serializer);247 248 OF->keep();249 return Error::success();250}251 252static CommandRegistration SummaryReg(&SummarySub, trySummary);253 254} // namespace summary255