1*24f320e4SZain Jaffal //===-------------- RemarkSizeDiff.cpp ------------------------------------===//
2*24f320e4SZain Jaffal //
3*24f320e4SZain Jaffal // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4*24f320e4SZain Jaffal // See https://llvm.org/LICENSE.txt for license information.
5*24f320e4SZain Jaffal // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6*24f320e4SZain Jaffal //
7*24f320e4SZain Jaffal //===----------------------------------------------------------------------===//
8*24f320e4SZain Jaffal ///
9*24f320e4SZain Jaffal /// \file
10*24f320e4SZain Jaffal /// Diffs instruction count and stack size remarks between two remark files.
11*24f320e4SZain Jaffal ///
12*24f320e4SZain Jaffal /// This is intended for use by compiler developers who want to see how their
13*24f320e4SZain Jaffal /// changes impact program code size.
14*24f320e4SZain Jaffal ///
15*24f320e4SZain Jaffal //===----------------------------------------------------------------------===//
16*24f320e4SZain Jaffal
17*24f320e4SZain Jaffal #include "RemarkUtilHelpers.h"
18*24f320e4SZain Jaffal #include "RemarkUtilRegistry.h"
19*24f320e4SZain Jaffal #include "llvm/ADT/SmallSet.h"
20*24f320e4SZain Jaffal #include "llvm/Support/FormatVariadic.h"
21*24f320e4SZain Jaffal #include "llvm/Support/JSON.h"
22*24f320e4SZain Jaffal
23*24f320e4SZain Jaffal using namespace llvm;
24*24f320e4SZain Jaffal using namespace remarks;
25*24f320e4SZain Jaffal using namespace remarkutil;
26*24f320e4SZain Jaffal static cl::SubCommand
27*24f320e4SZain Jaffal RemarkSizeDiffUtil("size-diff",
28*24f320e4SZain Jaffal "Diff instruction count and stack size remarks "
29*24f320e4SZain Jaffal "between two remark files");
30*24f320e4SZain Jaffal enum ReportStyleOptions { human_output, json_output };
31*24f320e4SZain Jaffal static cl::opt<std::string> InputFileNameA(cl::Positional, cl::Required,
32*24f320e4SZain Jaffal cl::sub(RemarkSizeDiffUtil),
33*24f320e4SZain Jaffal cl::desc("remarks_a"));
34*24f320e4SZain Jaffal static cl::opt<std::string> InputFileNameB(cl::Positional, cl::Required,
35*24f320e4SZain Jaffal cl::sub(RemarkSizeDiffUtil),
36*24f320e4SZain Jaffal cl::desc("remarks_b"));
37*24f320e4SZain Jaffal static cl::opt<std::string> OutputFilename("o", cl::init("-"),
38*24f320e4SZain Jaffal cl::sub(RemarkSizeDiffUtil),
39*24f320e4SZain Jaffal cl::desc("Output"),
40*24f320e4SZain Jaffal cl::value_desc("file"));
41*24f320e4SZain Jaffal INPUT_FORMAT_COMMAND_LINE_OPTIONS(RemarkSizeDiffUtil)
42*24f320e4SZain Jaffal static cl::opt<ReportStyleOptions> ReportStyle(
43*24f320e4SZain Jaffal "report_style", cl::sub(RemarkSizeDiffUtil),
44*24f320e4SZain Jaffal cl::init(ReportStyleOptions::human_output),
45*24f320e4SZain Jaffal cl::desc("Choose the report output format:"),
46*24f320e4SZain Jaffal cl::values(clEnumValN(human_output, "human", "Human-readable format"),
47*24f320e4SZain Jaffal clEnumValN(json_output, "json", "JSON format")));
48*24f320e4SZain Jaffal static cl::opt<bool> PrettyPrint("pretty", cl::sub(RemarkSizeDiffUtil),
49*24f320e4SZain Jaffal cl::init(false),
50*24f320e4SZain Jaffal cl::desc("Pretty-print JSON"));
51*24f320e4SZain Jaffal
52*24f320e4SZain Jaffal /// Contains information from size remarks.
53*24f320e4SZain Jaffal // This is a little nicer to read than a std::pair.
54*24f320e4SZain Jaffal struct InstCountAndStackSize {
55*24f320e4SZain Jaffal int64_t InstCount = 0;
56*24f320e4SZain Jaffal int64_t StackSize = 0;
57*24f320e4SZain Jaffal };
58*24f320e4SZain Jaffal
59*24f320e4SZain Jaffal /// Represents which files a function appeared in.
60*24f320e4SZain Jaffal enum FilesPresent { A, B, BOTH };
61*24f320e4SZain Jaffal
62*24f320e4SZain Jaffal /// Contains the data from the remarks in file A and file B for some function.
63*24f320e4SZain Jaffal /// E.g. instruction count, stack size...
64*24f320e4SZain Jaffal struct FunctionDiff {
65*24f320e4SZain Jaffal /// Function name from the remark.
66*24f320e4SZain Jaffal std::string FuncName;
67*24f320e4SZain Jaffal // Idx 0 = A, Idx 1 = B.
68*24f320e4SZain Jaffal int64_t InstCount[2] = {0, 0};
69*24f320e4SZain Jaffal int64_t StackSize[2] = {0, 0};
70*24f320e4SZain Jaffal
71*24f320e4SZain Jaffal // Calculate diffs between the first and second files.
getInstDiffFunctionDiff72*24f320e4SZain Jaffal int64_t getInstDiff() const { return InstCount[1] - InstCount[0]; }
getStackDiffFunctionDiff73*24f320e4SZain Jaffal int64_t getStackDiff() const { return StackSize[1] - StackSize[0]; }
74*24f320e4SZain Jaffal
75*24f320e4SZain Jaffal // Accessors for the remarks from the first file.
getInstCountAFunctionDiff76*24f320e4SZain Jaffal int64_t getInstCountA() const { return InstCount[0]; }
getStackSizeAFunctionDiff77*24f320e4SZain Jaffal int64_t getStackSizeA() const { return StackSize[0]; }
78*24f320e4SZain Jaffal
79*24f320e4SZain Jaffal // Accessors for the remarks from the second file.
getInstCountBFunctionDiff80*24f320e4SZain Jaffal int64_t getInstCountB() const { return InstCount[1]; }
getStackSizeBFunctionDiff81*24f320e4SZain Jaffal int64_t getStackSizeB() const { return StackSize[1]; }
82*24f320e4SZain Jaffal
83*24f320e4SZain Jaffal /// \returns which files this function was present in.
getFilesPresentFunctionDiff84*24f320e4SZain Jaffal FilesPresent getFilesPresent() const {
85*24f320e4SZain Jaffal if (getInstCountA() == 0)
86*24f320e4SZain Jaffal return B;
87*24f320e4SZain Jaffal if (getInstCountB() == 0)
88*24f320e4SZain Jaffal return A;
89*24f320e4SZain Jaffal return BOTH;
90*24f320e4SZain Jaffal }
91*24f320e4SZain Jaffal
FunctionDiffFunctionDiff92*24f320e4SZain Jaffal FunctionDiff(StringRef FuncName, const InstCountAndStackSize &A,
93*24f320e4SZain Jaffal const InstCountAndStackSize &B)
94*24f320e4SZain Jaffal : FuncName(FuncName) {
95*24f320e4SZain Jaffal InstCount[0] = A.InstCount;
96*24f320e4SZain Jaffal InstCount[1] = B.InstCount;
97*24f320e4SZain Jaffal StackSize[0] = A.StackSize;
98*24f320e4SZain Jaffal StackSize[1] = B.StackSize;
99*24f320e4SZain Jaffal }
100*24f320e4SZain Jaffal };
101*24f320e4SZain Jaffal
102*24f320e4SZain Jaffal /// Organizes the diffs into 3 categories:
103*24f320e4SZain Jaffal /// - Functions which only appeared in the first file
104*24f320e4SZain Jaffal /// - Functions which only appeared in the second file
105*24f320e4SZain Jaffal /// - Functions which appeared in both files
106*24f320e4SZain Jaffal struct DiffsCategorizedByFilesPresent {
107*24f320e4SZain Jaffal /// Diffs for functions which only appeared in the first file.
108*24f320e4SZain Jaffal SmallVector<FunctionDiff> OnlyInA;
109*24f320e4SZain Jaffal
110*24f320e4SZain Jaffal /// Diffs for functions which only appeared in the second file.
111*24f320e4SZain Jaffal SmallVector<FunctionDiff> OnlyInB;
112*24f320e4SZain Jaffal
113*24f320e4SZain Jaffal /// Diffs for functions which appeared in both files.
114*24f320e4SZain Jaffal SmallVector<FunctionDiff> InBoth;
115*24f320e4SZain Jaffal
116*24f320e4SZain Jaffal /// Add a diff to the appropriate list.
addDiffDiffsCategorizedByFilesPresent117*24f320e4SZain Jaffal void addDiff(FunctionDiff &FD) {
118*24f320e4SZain Jaffal switch (FD.getFilesPresent()) {
119*24f320e4SZain Jaffal case A:
120*24f320e4SZain Jaffal OnlyInA.push_back(FD);
121*24f320e4SZain Jaffal break;
122*24f320e4SZain Jaffal case B:
123*24f320e4SZain Jaffal OnlyInB.push_back(FD);
124*24f320e4SZain Jaffal break;
125*24f320e4SZain Jaffal case BOTH:
126*24f320e4SZain Jaffal InBoth.push_back(FD);
127*24f320e4SZain Jaffal break;
128*24f320e4SZain Jaffal }
129*24f320e4SZain Jaffal }
130*24f320e4SZain Jaffal };
131*24f320e4SZain Jaffal
printFunctionDiff(const FunctionDiff & FD,llvm::raw_ostream & OS)132*24f320e4SZain Jaffal static void printFunctionDiff(const FunctionDiff &FD, llvm::raw_ostream &OS) {
133*24f320e4SZain Jaffal // Describe which files the function had remarks in.
134*24f320e4SZain Jaffal FilesPresent FP = FD.getFilesPresent();
135*24f320e4SZain Jaffal const std::string &FuncName = FD.FuncName;
136*24f320e4SZain Jaffal const int64_t InstDiff = FD.getInstDiff();
137*24f320e4SZain Jaffal assert(InstDiff && "Shouldn't get functions with no size change?");
138*24f320e4SZain Jaffal const int64_t StackDiff = FD.getStackDiff();
139*24f320e4SZain Jaffal // Output an indicator denoting which files the function was present in.
140*24f320e4SZain Jaffal switch (FP) {
141*24f320e4SZain Jaffal case FilesPresent::A:
142*24f320e4SZain Jaffal OS << "-- ";
143*24f320e4SZain Jaffal break;
144*24f320e4SZain Jaffal case FilesPresent::B:
145*24f320e4SZain Jaffal OS << "++ ";
146*24f320e4SZain Jaffal break;
147*24f320e4SZain Jaffal case FilesPresent::BOTH:
148*24f320e4SZain Jaffal OS << "== ";
149*24f320e4SZain Jaffal break;
150*24f320e4SZain Jaffal }
151*24f320e4SZain Jaffal // Output an indicator denoting if a function changed in size.
152*24f320e4SZain Jaffal if (InstDiff > 0)
153*24f320e4SZain Jaffal OS << "> ";
154*24f320e4SZain Jaffal else
155*24f320e4SZain Jaffal OS << "< ";
156*24f320e4SZain Jaffal OS << FuncName << ", ";
157*24f320e4SZain Jaffal OS << InstDiff << " instrs, ";
158*24f320e4SZain Jaffal OS << StackDiff << " stack B";
159*24f320e4SZain Jaffal OS << "\n";
160*24f320e4SZain Jaffal }
161*24f320e4SZain Jaffal
162*24f320e4SZain Jaffal /// Print an item in the summary section.
163*24f320e4SZain Jaffal ///
164*24f320e4SZain Jaffal /// \p TotalA - Total count of the metric in file A.
165*24f320e4SZain Jaffal /// \p TotalB - Total count of the metric in file B.
166*24f320e4SZain Jaffal /// \p Metric - Name of the metric we want to print (e.g. instruction
167*24f320e4SZain Jaffal /// count).
168*24f320e4SZain Jaffal /// \p OS - The output stream.
printSummaryItem(int64_t TotalA,int64_t TotalB,StringRef Metric,llvm::raw_ostream & OS)169*24f320e4SZain Jaffal static void printSummaryItem(int64_t TotalA, int64_t TotalB, StringRef Metric,
170*24f320e4SZain Jaffal llvm::raw_ostream &OS) {
171*24f320e4SZain Jaffal OS << " " << Metric << ": ";
172*24f320e4SZain Jaffal int64_t TotalDiff = TotalB - TotalA;
173*24f320e4SZain Jaffal if (TotalDiff == 0) {
174*24f320e4SZain Jaffal OS << "None\n";
175*24f320e4SZain Jaffal return;
176*24f320e4SZain Jaffal }
177*24f320e4SZain Jaffal OS << TotalDiff << " (" << formatv("{0:p}", TotalDiff / (double)TotalA)
178*24f320e4SZain Jaffal << ")\n";
179*24f320e4SZain Jaffal }
180*24f320e4SZain Jaffal
181*24f320e4SZain Jaffal /// Print all contents of \p Diff and a high-level summary of the differences.
printDiffsCategorizedByFilesPresent(DiffsCategorizedByFilesPresent & DiffsByFilesPresent,llvm::raw_ostream & OS)182*24f320e4SZain Jaffal static void printDiffsCategorizedByFilesPresent(
183*24f320e4SZain Jaffal DiffsCategorizedByFilesPresent &DiffsByFilesPresent,
184*24f320e4SZain Jaffal llvm::raw_ostream &OS) {
185*24f320e4SZain Jaffal int64_t InstrsA = 0;
186*24f320e4SZain Jaffal int64_t InstrsB = 0;
187*24f320e4SZain Jaffal int64_t StackA = 0;
188*24f320e4SZain Jaffal int64_t StackB = 0;
189*24f320e4SZain Jaffal // Helper lambda to sort + print a list of diffs.
190*24f320e4SZain Jaffal auto PrintDiffList = [&](SmallVector<FunctionDiff> &FunctionDiffList) {
191*24f320e4SZain Jaffal if (FunctionDiffList.empty())
192*24f320e4SZain Jaffal return;
193*24f320e4SZain Jaffal stable_sort(FunctionDiffList,
194*24f320e4SZain Jaffal [](const FunctionDiff &LHS, const FunctionDiff &RHS) {
195*24f320e4SZain Jaffal return LHS.getInstDiff() < RHS.getInstDiff();
196*24f320e4SZain Jaffal });
197*24f320e4SZain Jaffal for (const auto &FuncDiff : FunctionDiffList) {
198*24f320e4SZain Jaffal // If there is a difference in instruction count, then print out info for
199*24f320e4SZain Jaffal // the function.
200*24f320e4SZain Jaffal if (FuncDiff.getInstDiff())
201*24f320e4SZain Jaffal printFunctionDiff(FuncDiff, OS);
202*24f320e4SZain Jaffal InstrsA += FuncDiff.getInstCountA();
203*24f320e4SZain Jaffal InstrsB += FuncDiff.getInstCountB();
204*24f320e4SZain Jaffal StackA += FuncDiff.getStackSizeA();
205*24f320e4SZain Jaffal StackB += FuncDiff.getStackSizeB();
206*24f320e4SZain Jaffal }
207*24f320e4SZain Jaffal };
208*24f320e4SZain Jaffal PrintDiffList(DiffsByFilesPresent.OnlyInA);
209*24f320e4SZain Jaffal PrintDiffList(DiffsByFilesPresent.OnlyInB);
210*24f320e4SZain Jaffal PrintDiffList(DiffsByFilesPresent.InBoth);
211*24f320e4SZain Jaffal OS << "\n### Summary ###\n";
212*24f320e4SZain Jaffal OS << "Total change: \n";
213*24f320e4SZain Jaffal printSummaryItem(InstrsA, InstrsB, "instruction count", OS);
214*24f320e4SZain Jaffal printSummaryItem(StackA, StackB, "stack byte usage", OS);
215*24f320e4SZain Jaffal }
216*24f320e4SZain Jaffal
217*24f320e4SZain Jaffal /// Collects an expected integer value from a given argument index in a remark.
218*24f320e4SZain Jaffal ///
219*24f320e4SZain Jaffal /// \p Remark - The remark.
220*24f320e4SZain Jaffal /// \p ArgIdx - The index where the integer value should be found.
221*24f320e4SZain Jaffal /// \p ExpectedKeyName - The expected key name for the index
222*24f320e4SZain Jaffal /// (e.g. "InstructionCount")
223*24f320e4SZain Jaffal ///
224*24f320e4SZain Jaffal /// \returns the integer value at the index if it exists, and the key-value pair
225*24f320e4SZain Jaffal /// is what is expected. Otherwise, returns an Error.
getIntValFromKey(const remarks::Remark & Remark,unsigned ArgIdx,StringRef ExpectedKeyName)226*24f320e4SZain Jaffal static Expected<int64_t> getIntValFromKey(const remarks::Remark &Remark,
227*24f320e4SZain Jaffal unsigned ArgIdx,
228*24f320e4SZain Jaffal StringRef ExpectedKeyName) {
229*24f320e4SZain Jaffal auto KeyName = Remark.Args[ArgIdx].Key;
230*24f320e4SZain Jaffal if (KeyName != ExpectedKeyName)
231*24f320e4SZain Jaffal return createStringError(
232*24f320e4SZain Jaffal inconvertibleErrorCode(),
233*24f320e4SZain Jaffal Twine("Unexpected key at argument index " + std::to_string(ArgIdx) +
234*24f320e4SZain Jaffal ": Expected '" + ExpectedKeyName + "', got '" + KeyName + "'"));
235*24f320e4SZain Jaffal long long Val;
236*24f320e4SZain Jaffal auto ValStr = Remark.Args[ArgIdx].Val;
237*24f320e4SZain Jaffal if (getAsSignedInteger(ValStr, 0, Val))
238*24f320e4SZain Jaffal return createStringError(
239*24f320e4SZain Jaffal inconvertibleErrorCode(),
240*24f320e4SZain Jaffal Twine("Could not convert string to signed integer: " + ValStr));
241*24f320e4SZain Jaffal return static_cast<int64_t>(Val);
242*24f320e4SZain Jaffal }
243*24f320e4SZain Jaffal
244*24f320e4SZain Jaffal /// Collects relevant size information from \p Remark if it is an size-related
245*24f320e4SZain Jaffal /// remark of some kind (e.g. instruction count). Otherwise records nothing.
246*24f320e4SZain Jaffal ///
247*24f320e4SZain Jaffal /// \p Remark - The remark.
248*24f320e4SZain Jaffal /// \p FuncNameToSizeInfo - Maps function names to relevant size info.
249*24f320e4SZain Jaffal /// \p NumInstCountRemarksParsed - Keeps track of the number of instruction
250*24f320e4SZain Jaffal /// count remarks parsed. We need at least 1 in both files to produce a diff.
processRemark(const remarks::Remark & Remark,StringMap<InstCountAndStackSize> & FuncNameToSizeInfo,unsigned & NumInstCountRemarksParsed)251*24f320e4SZain Jaffal static Error processRemark(const remarks::Remark &Remark,
252*24f320e4SZain Jaffal StringMap<InstCountAndStackSize> &FuncNameToSizeInfo,
253*24f320e4SZain Jaffal unsigned &NumInstCountRemarksParsed) {
254*24f320e4SZain Jaffal const auto &RemarkName = Remark.RemarkName;
255*24f320e4SZain Jaffal const auto &PassName = Remark.PassName;
256*24f320e4SZain Jaffal // Collect remarks which contain the number of instructions in a function.
257*24f320e4SZain Jaffal if (PassName == "asm-printer" && RemarkName == "InstructionCount") {
258*24f320e4SZain Jaffal // Expecting the 0-th argument to have the key "NumInstructions" and an
259*24f320e4SZain Jaffal // integer value.
260*24f320e4SZain Jaffal auto MaybeInstCount =
261*24f320e4SZain Jaffal getIntValFromKey(Remark, /*ArgIdx = */ 0, "NumInstructions");
262*24f320e4SZain Jaffal if (!MaybeInstCount)
263*24f320e4SZain Jaffal return MaybeInstCount.takeError();
264*24f320e4SZain Jaffal FuncNameToSizeInfo[Remark.FunctionName].InstCount = *MaybeInstCount;
265*24f320e4SZain Jaffal ++NumInstCountRemarksParsed;
266*24f320e4SZain Jaffal }
267*24f320e4SZain Jaffal // Collect remarks which contain the stack size of a function.
268*24f320e4SZain Jaffal else if (PassName == "prologepilog" && RemarkName == "StackSize") {
269*24f320e4SZain Jaffal // Expecting the 0-th argument to have the key "NumStackBytes" and an
270*24f320e4SZain Jaffal // integer value.
271*24f320e4SZain Jaffal auto MaybeStackSize =
272*24f320e4SZain Jaffal getIntValFromKey(Remark, /*ArgIdx = */ 0, "NumStackBytes");
273*24f320e4SZain Jaffal if (!MaybeStackSize)
274*24f320e4SZain Jaffal return MaybeStackSize.takeError();
275*24f320e4SZain Jaffal FuncNameToSizeInfo[Remark.FunctionName].StackSize = *MaybeStackSize;
276*24f320e4SZain Jaffal }
277*24f320e4SZain Jaffal // Either we collected a remark, or it's something we don't care about. In
278*24f320e4SZain Jaffal // both cases, this is a success.
279*24f320e4SZain Jaffal return Error::success();
280*24f320e4SZain Jaffal }
281*24f320e4SZain Jaffal
282*24f320e4SZain Jaffal /// Process all of the size-related remarks in a file.
283*24f320e4SZain Jaffal ///
284*24f320e4SZain Jaffal /// \param[in] InputFileName - Name of file to read from.
285*24f320e4SZain Jaffal /// \param[in, out] FuncNameToSizeInfo - Maps function names to relevant
286*24f320e4SZain Jaffal /// size info.
readFileAndProcessRemarks(StringRef InputFileName,StringMap<InstCountAndStackSize> & FuncNameToSizeInfo)287*24f320e4SZain Jaffal static Error readFileAndProcessRemarks(
288*24f320e4SZain Jaffal StringRef InputFileName,
289*24f320e4SZain Jaffal StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) {
290*24f320e4SZain Jaffal
291*24f320e4SZain Jaffal auto MaybeBuf = getInputMemoryBuffer(InputFileName);
292*24f320e4SZain Jaffal if (!MaybeBuf)
293*24f320e4SZain Jaffal return MaybeBuf.takeError();
294*24f320e4SZain Jaffal auto MaybeParser =
295*24f320e4SZain Jaffal createRemarkParserFromMeta(InputFormat, (*MaybeBuf)->getBuffer());
296*24f320e4SZain Jaffal if (!MaybeParser)
297*24f320e4SZain Jaffal return MaybeParser.takeError();
298*24f320e4SZain Jaffal auto &Parser = **MaybeParser;
299*24f320e4SZain Jaffal auto MaybeRemark = Parser.next();
300*24f320e4SZain Jaffal unsigned NumInstCountRemarksParsed = 0;
301*24f320e4SZain Jaffal for (; MaybeRemark; MaybeRemark = Parser.next()) {
302*24f320e4SZain Jaffal if (auto E = processRemark(**MaybeRemark, FuncNameToSizeInfo,
303*24f320e4SZain Jaffal NumInstCountRemarksParsed))
304*24f320e4SZain Jaffal return E;
305*24f320e4SZain Jaffal }
306*24f320e4SZain Jaffal auto E = MaybeRemark.takeError();
307*24f320e4SZain Jaffal if (!E.isA<remarks::EndOfFileError>())
308*24f320e4SZain Jaffal return E;
309*24f320e4SZain Jaffal consumeError(std::move(E));
310*24f320e4SZain Jaffal // We need at least one instruction count remark in each file to produce a
311*24f320e4SZain Jaffal // meaningful diff.
312*24f320e4SZain Jaffal if (NumInstCountRemarksParsed == 0)
313*24f320e4SZain Jaffal return createStringError(
314*24f320e4SZain Jaffal inconvertibleErrorCode(),
315*24f320e4SZain Jaffal "File '" + InputFileName +
316*24f320e4SZain Jaffal "' did not contain any instruction-count remarks!");
317*24f320e4SZain Jaffal return Error::success();
318*24f320e4SZain Jaffal }
319*24f320e4SZain Jaffal
320*24f320e4SZain Jaffal /// Wrapper function for readFileAndProcessRemarks which handles errors.
321*24f320e4SZain Jaffal ///
322*24f320e4SZain Jaffal /// \param[in] InputFileName - Name of file to read from.
323*24f320e4SZain Jaffal /// \param[out] FuncNameToSizeInfo - Populated with information from size
324*24f320e4SZain Jaffal /// remarks in the input file.
325*24f320e4SZain Jaffal ///
326*24f320e4SZain Jaffal /// \returns true if readFileAndProcessRemarks returned no errors. False
327*24f320e4SZain Jaffal /// otherwise.
tryReadFileAndProcessRemarks(StringRef InputFileName,StringMap<InstCountAndStackSize> & FuncNameToSizeInfo)328*24f320e4SZain Jaffal static Error tryReadFileAndProcessRemarks(
329*24f320e4SZain Jaffal StringRef InputFileName,
330*24f320e4SZain Jaffal StringMap<InstCountAndStackSize> &FuncNameToSizeInfo) {
331*24f320e4SZain Jaffal if (Error E = readFileAndProcessRemarks(InputFileName, FuncNameToSizeInfo)) {
332*24f320e4SZain Jaffal return E;
333*24f320e4SZain Jaffal }
334*24f320e4SZain Jaffal return Error::success();
335*24f320e4SZain Jaffal }
336*24f320e4SZain Jaffal
337*24f320e4SZain Jaffal /// Populates \p FuncDiffs with the difference between \p
338*24f320e4SZain Jaffal /// FuncNameToSizeInfoA and \p FuncNameToSizeInfoB.
339*24f320e4SZain Jaffal ///
340*24f320e4SZain Jaffal /// \param[in] FuncNameToSizeInfoA - Size info collected from the first
341*24f320e4SZain Jaffal /// remarks file.
342*24f320e4SZain Jaffal /// \param[in] FuncNameToSizeInfoB - Size info collected from
343*24f320e4SZain Jaffal /// the second remarks file.
344*24f320e4SZain Jaffal /// \param[out] DiffsByFilesPresent - Filled with the diff between \p
345*24f320e4SZain Jaffal /// FuncNameToSizeInfoA and \p FuncNameToSizeInfoB.
346*24f320e4SZain Jaffal static void
computeDiff(const StringMap<InstCountAndStackSize> & FuncNameToSizeInfoA,const StringMap<InstCountAndStackSize> & FuncNameToSizeInfoB,DiffsCategorizedByFilesPresent & DiffsByFilesPresent)347*24f320e4SZain Jaffal computeDiff(const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoA,
348*24f320e4SZain Jaffal const StringMap<InstCountAndStackSize> &FuncNameToSizeInfoB,
349*24f320e4SZain Jaffal DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
350*24f320e4SZain Jaffal SmallSet<std::string, 10> FuncNames;
351*24f320e4SZain Jaffal for (const auto &FuncName : FuncNameToSizeInfoA.keys())
352*24f320e4SZain Jaffal FuncNames.insert(FuncName.str());
353*24f320e4SZain Jaffal for (const auto &FuncName : FuncNameToSizeInfoB.keys())
354*24f320e4SZain Jaffal FuncNames.insert(FuncName.str());
355*24f320e4SZain Jaffal for (const std::string &FuncName : FuncNames) {
356*24f320e4SZain Jaffal const auto &SizeInfoA = FuncNameToSizeInfoA.lookup(FuncName);
357*24f320e4SZain Jaffal const auto &SizeInfoB = FuncNameToSizeInfoB.lookup(FuncName);
358*24f320e4SZain Jaffal FunctionDiff FuncDiff(FuncName, SizeInfoA, SizeInfoB);
359*24f320e4SZain Jaffal DiffsByFilesPresent.addDiff(FuncDiff);
360*24f320e4SZain Jaffal }
361*24f320e4SZain Jaffal }
362*24f320e4SZain Jaffal
363*24f320e4SZain Jaffal /// Attempt to get the output stream for writing the diff.
getOutputStream()364*24f320e4SZain Jaffal static ErrorOr<std::unique_ptr<ToolOutputFile>> getOutputStream() {
365*24f320e4SZain Jaffal if (OutputFilename == "")
366*24f320e4SZain Jaffal OutputFilename = "-";
367*24f320e4SZain Jaffal std::error_code EC;
368*24f320e4SZain Jaffal auto Out = std::make_unique<ToolOutputFile>(OutputFilename, EC,
369*24f320e4SZain Jaffal sys::fs::OF_TextWithCRLF);
370*24f320e4SZain Jaffal if (!EC)
371*24f320e4SZain Jaffal return std::move(Out);
372*24f320e4SZain Jaffal return EC;
373*24f320e4SZain Jaffal }
374*24f320e4SZain Jaffal
375*24f320e4SZain Jaffal /// \return a json::Array representing all FunctionDiffs in \p FunctionDiffs.
376*24f320e4SZain Jaffal /// \p WhichFiles represents which files the functions in \p FunctionDiffs
377*24f320e4SZain Jaffal /// appeared in (A, B, or both).
378*24f320e4SZain Jaffal json::Array
getFunctionDiffListAsJSON(const SmallVector<FunctionDiff> & FunctionDiffs,const FilesPresent & WhichFiles)379*24f320e4SZain Jaffal getFunctionDiffListAsJSON(const SmallVector<FunctionDiff> &FunctionDiffs,
380*24f320e4SZain Jaffal const FilesPresent &WhichFiles) {
381*24f320e4SZain Jaffal json::Array FunctionDiffsAsJSON;
382*24f320e4SZain Jaffal int64_t InstCountA, InstCountB, StackSizeA, StackSizeB;
383*24f320e4SZain Jaffal for (auto &Diff : FunctionDiffs) {
384*24f320e4SZain Jaffal InstCountA = InstCountB = StackSizeA = StackSizeB = 0;
385*24f320e4SZain Jaffal switch (WhichFiles) {
386*24f320e4SZain Jaffal case BOTH:
387*24f320e4SZain Jaffal [[fallthrough]];
388*24f320e4SZain Jaffal case A:
389*24f320e4SZain Jaffal InstCountA = Diff.getInstCountA();
390*24f320e4SZain Jaffal StackSizeA = Diff.getStackSizeA();
391*24f320e4SZain Jaffal if (WhichFiles != BOTH)
392*24f320e4SZain Jaffal break;
393*24f320e4SZain Jaffal [[fallthrough]];
394*24f320e4SZain Jaffal case B:
395*24f320e4SZain Jaffal InstCountB = Diff.getInstCountB();
396*24f320e4SZain Jaffal StackSizeB = Diff.getStackSizeB();
397*24f320e4SZain Jaffal break;
398*24f320e4SZain Jaffal }
399*24f320e4SZain Jaffal // Each metric we care about is represented like:
400*24f320e4SZain Jaffal // "Val": [A, B]
401*24f320e4SZain Jaffal // This allows any consumer of the JSON to calculate the diff using B - A.
402*24f320e4SZain Jaffal // This is somewhat wasteful for OnlyInA and OnlyInB (we only need A or B).
403*24f320e4SZain Jaffal // However, this should make writing consuming tools easier, since the tool
404*24f320e4SZain Jaffal // writer doesn't need to think about slightly different formats in each
405*24f320e4SZain Jaffal // section.
406*24f320e4SZain Jaffal json::Object FunctionObject({{"FunctionName", Diff.FuncName},
407*24f320e4SZain Jaffal {"InstCount", {InstCountA, InstCountB}},
408*24f320e4SZain Jaffal {"StackSize", {StackSizeA, StackSizeB}}});
409*24f320e4SZain Jaffal FunctionDiffsAsJSON.push_back(std::move(FunctionObject));
410*24f320e4SZain Jaffal }
411*24f320e4SZain Jaffal return FunctionDiffsAsJSON;
412*24f320e4SZain Jaffal }
413*24f320e4SZain Jaffal
414*24f320e4SZain Jaffal /// Output all diffs in \p DiffsByFilesPresent as a JSON report. This is
415*24f320e4SZain Jaffal /// intended for consumption by external tools.
416*24f320e4SZain Jaffal ///
417*24f320e4SZain Jaffal /// \p InputFileNameA - File A used to produce the report.
418*24f320e4SZain Jaffal /// \p InputFileNameB - File B used ot produce the report.
419*24f320e4SZain Jaffal /// \p OS - Output stream.
420*24f320e4SZain Jaffal ///
421*24f320e4SZain Jaffal /// JSON output includes:
422*24f320e4SZain Jaffal /// - \p InputFileNameA and \p InputFileNameB under "Files".
423*24f320e4SZain Jaffal /// - Functions present in both files under "InBoth".
424*24f320e4SZain Jaffal /// - Functions present only in A in "OnlyInA".
425*24f320e4SZain Jaffal /// - Functions present only in B in "OnlyInB".
426*24f320e4SZain Jaffal /// - Instruction count and stack size differences for each function.
427*24f320e4SZain Jaffal ///
428*24f320e4SZain Jaffal /// Differences are represented using [count_a, count_b]. The actual difference
429*24f320e4SZain Jaffal /// can be computed via count_b - count_a.
430*24f320e4SZain Jaffal static void
outputJSONForAllDiffs(StringRef InputFileNameA,StringRef InputFileNameB,const DiffsCategorizedByFilesPresent & DiffsByFilesPresent,llvm::raw_ostream & OS)431*24f320e4SZain Jaffal outputJSONForAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB,
432*24f320e4SZain Jaffal const DiffsCategorizedByFilesPresent &DiffsByFilesPresent,
433*24f320e4SZain Jaffal llvm::raw_ostream &OS) {
434*24f320e4SZain Jaffal json::Object Output;
435*24f320e4SZain Jaffal // Include file names in the report.
436*24f320e4SZain Jaffal json::Object Files(
437*24f320e4SZain Jaffal {{"A", InputFileNameA.str()}, {"B", InputFileNameB.str()}});
438*24f320e4SZain Jaffal Output["Files"] = std::move(Files);
439*24f320e4SZain Jaffal Output["OnlyInA"] = getFunctionDiffListAsJSON(DiffsByFilesPresent.OnlyInA, A);
440*24f320e4SZain Jaffal Output["OnlyInB"] = getFunctionDiffListAsJSON(DiffsByFilesPresent.OnlyInB, B);
441*24f320e4SZain Jaffal Output["InBoth"] =
442*24f320e4SZain Jaffal getFunctionDiffListAsJSON(DiffsByFilesPresent.InBoth, BOTH);
443*24f320e4SZain Jaffal json::OStream JOS(OS, PrettyPrint ? 2 : 0);
444*24f320e4SZain Jaffal JOS.value(std::move(Output));
445*24f320e4SZain Jaffal OS << '\n';
446*24f320e4SZain Jaffal }
447*24f320e4SZain Jaffal
448*24f320e4SZain Jaffal /// Output all diffs in \p DiffsByFilesPresent using the desired output style.
449*24f320e4SZain Jaffal /// \returns Error::success() on success, and an Error otherwise.
450*24f320e4SZain Jaffal /// \p InputFileNameA - Name of input file A; may be used in the report.
451*24f320e4SZain Jaffal /// \p InputFileNameB - Name of input file B; may be used in the report.
452*24f320e4SZain Jaffal static Error
outputAllDiffs(StringRef InputFileNameA,StringRef InputFileNameB,DiffsCategorizedByFilesPresent & DiffsByFilesPresent)453*24f320e4SZain Jaffal outputAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB,
454*24f320e4SZain Jaffal DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
455*24f320e4SZain Jaffal auto MaybeOF = getOutputStream();
456*24f320e4SZain Jaffal if (std::error_code EC = MaybeOF.getError())
457*24f320e4SZain Jaffal return errorCodeToError(EC);
458*24f320e4SZain Jaffal std::unique_ptr<ToolOutputFile> OF = std::move(*MaybeOF);
459*24f320e4SZain Jaffal switch (ReportStyle) {
460*24f320e4SZain Jaffal case human_output:
461*24f320e4SZain Jaffal printDiffsCategorizedByFilesPresent(DiffsByFilesPresent, OF->os());
462*24f320e4SZain Jaffal break;
463*24f320e4SZain Jaffal case json_output:
464*24f320e4SZain Jaffal outputJSONForAllDiffs(InputFileNameA, InputFileNameB, DiffsByFilesPresent,
465*24f320e4SZain Jaffal OF->os());
466*24f320e4SZain Jaffal break;
467*24f320e4SZain Jaffal }
468*24f320e4SZain Jaffal OF->keep();
469*24f320e4SZain Jaffal return Error::success();
470*24f320e4SZain Jaffal }
471*24f320e4SZain Jaffal
472*24f320e4SZain Jaffal /// Boolean wrapper for outputDiff which handles errors.
473*24f320e4SZain Jaffal static Error
tryOutputAllDiffs(StringRef InputFileNameA,StringRef InputFileNameB,DiffsCategorizedByFilesPresent & DiffsByFilesPresent)474*24f320e4SZain Jaffal tryOutputAllDiffs(StringRef InputFileNameA, StringRef InputFileNameB,
475*24f320e4SZain Jaffal DiffsCategorizedByFilesPresent &DiffsByFilesPresent) {
476*24f320e4SZain Jaffal if (Error E =
477*24f320e4SZain Jaffal outputAllDiffs(InputFileNameA, InputFileNameB, DiffsByFilesPresent)) {
478*24f320e4SZain Jaffal return E;
479*24f320e4SZain Jaffal }
480*24f320e4SZain Jaffal return Error::success();
481*24f320e4SZain Jaffal }
482*24f320e4SZain Jaffal
trySizeSiff()483*24f320e4SZain Jaffal static Error trySizeSiff() {
484*24f320e4SZain Jaffal StringMap<InstCountAndStackSize> FuncNameToSizeInfoA;
485*24f320e4SZain Jaffal StringMap<InstCountAndStackSize> FuncNameToSizeInfoB;
486*24f320e4SZain Jaffal if (auto E =
487*24f320e4SZain Jaffal tryReadFileAndProcessRemarks(InputFileNameA, FuncNameToSizeInfoA))
488*24f320e4SZain Jaffal return E;
489*24f320e4SZain Jaffal if (auto E =
490*24f320e4SZain Jaffal tryReadFileAndProcessRemarks(InputFileNameB, FuncNameToSizeInfoB))
491*24f320e4SZain Jaffal return E;
492*24f320e4SZain Jaffal DiffsCategorizedByFilesPresent DiffsByFilesPresent;
493*24f320e4SZain Jaffal computeDiff(FuncNameToSizeInfoA, FuncNameToSizeInfoB, DiffsByFilesPresent);
494*24f320e4SZain Jaffal if (auto E = tryOutputAllDiffs(InputFileNameA, InputFileNameB,
495*24f320e4SZain Jaffal DiffsByFilesPresent))
496*24f320e4SZain Jaffal return E;
497*24f320e4SZain Jaffal return Error::success();
498*24f320e4SZain Jaffal }
499*24f320e4SZain Jaffal
500*24f320e4SZain Jaffal static CommandRegistration RemarkSizeSiffRegister(&RemarkSizeDiffUtil,
501*24f320e4SZain Jaffal trySizeSiff);